Commit a3adef77 authored by J. R. Okajima's avatar J. R. Okajima
Browse files

aufs: sysfs interface



The branch path can be much longer and it is not suitable to print via
/proc/mounts as a part of mount options. Aufs can show it either
separately via sysfs or /proc/mounts (as a part of mount options).
This approach affects the lifetime of aufs objects and sbinfo contains
kobject (in another commit). Theoretically user can disable
CONFIG_SYSFS, but the lifetime management is always necessary. So
supporting sysfs is split into two files, sysaufs.c and sysfs.c.
sysaufs.c is always compiled, but sysfs.c is compiled only when
CONFIG_SYSFS is enabled.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent 8fe49c5d
What: /sys/fs/aufs/si_<id>/
Date: March 2009
Contact: J. R. Okajima <hooanon05g@gmail.com>
Description:
Under /sys/fs/aufs, a directory named si_<id> is created
per aufs mount, where <id> is a unique id generated
internally.
What: /sys/fs/aufs/si_<id>/br0, br1 ... brN
Date: March 2009
Contact: J. R. Okajima <hooanon05g@gmail.com>
Description:
It shows the abolute path of a member directory (which
is called branch) in aufs, and its permission.
What: /sys/fs/aufs/si_<id>/brid0, brid1 ... bridN
Date: July 2013
Contact: J. R. Okajima <hooanon05g@gmail.com>
Description:
It shows the id of a member directory (which is called
branch) in aufs.
What: /sys/fs/aufs/si_<id>/xi_path
Date: March 2009
Contact: J. R. Okajima <hooanon05g@gmail.com>
Description:
It shows the abolute path of XINO (External Inode Number
Bitmap, Translation Table and Generation Table) file
even if it is the default path.
When the aufs mount option 'noxino' is specified, it
will be empty. About XINO files, see the aufs manual.
......@@ -10,11 +10,12 @@ ccflags-y += -DDEBUG
ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h
obj-$(CONFIG_AUFS_FS) += aufs.o
aufs-y := module.o sbinfo.o super.o branch.o xino.o opts.o \
aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \
wkq.o vfsub.o dcsub.o \
cpup.o \
dinfo.o \
iinfo.o inode.o
# all are boolean
aufs-$(CONFIG_SYSFS) += sysfs.o
aufs-$(CONFIG_AUFS_DEBUG) += debug.o
......@@ -34,6 +34,7 @@
#include "opts.h"
#include "rwsem.h"
#include "super.h"
#include "sysaufs.h"
#include "vfsub.h"
#include "wkq.h"
......
......@@ -223,6 +223,7 @@ static int au_br_init(struct au_branch *br, struct super_block *sb,
}
}
sysaufs_br_init(br);
path_get(&br->br_path);
goto out; /* success */
......
......@@ -38,6 +38,18 @@ struct au_xino {
struct kref xi_kref;
};
/* sysfs entries */
struct au_brsysfs {
char name[16];
struct attribute attr;
};
enum {
AuBrSysfs_BR,
AuBrSysfs_BRID,
AuBrSysfs_Last
};
/* protected by superblock rwsem */
struct au_branch {
struct au_xino *br_xino;
......@@ -47,6 +59,11 @@ struct au_branch {
int br_perm;
struct path br_path;
au_lcnt_t br_count; /* in-use for other */
#ifdef CONFIG_SYSFS
/* entries under sysfs per mount-point */
struct au_brsysfs br_sysfs[AuBrSysfs_Last];
#endif
};
/* ---------------------------------------------------------------------- */
......@@ -133,6 +150,8 @@ int au_xino_init_br(struct super_block *sb, struct au_branch *br, ino_t hino,
ino_t au_xino_new_ino(struct super_block *sb);
void au_xino_delete_inode(struct inode *inode, const int unlinked);
int au_xino_path(struct seq_file *seq, struct file *file);
/* ---------------------------------------------------------------------- */
/* @idx is signed to accept -1 meaning the first file */
......
......@@ -8,6 +8,7 @@
*/
#include <linux/module.h>
#include <linux/seq_file.h>
#include "aufs.h"
/* shrinkable realloc */
......@@ -112,17 +113,51 @@ MODULE_DESCRIPTION(AUFS_NAME
" -- Advanced multi layered unification filesystem");
MODULE_VERSION(AUFS_VERSION);
/* this module parameter has no meaning when SYSFS is disabled */
int sysaufs_brs = 1;
MODULE_PARM_DESC(brs, "use <sysfs>/fs/aufs/si_*/brN");
module_param_named(brs, sysaufs_brs, int, 0444);
/* ---------------------------------------------------------------------- */
static int __init aufs_init(void)
static char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */
int au_seq_path(struct seq_file *seq, struct path *path)
{
int err;
err = seq_path(seq, path, au_esc_chars);
if (err >= 0)
err = 0;
else
err = -ENOMEM;
return err;
}
/* ---------------------------------------------------------------------- */
static int __init aufs_init(void)
{
int err, i;
char *p;
p = au_esc_chars;
for (i = 1; i <= ' '; i++)
*p++ = i;
*p++ = '\\';
*p++ = '\x7f';
*p = 0;
memset(au_cache, 0, sizeof(au_cache));
err = au_wkq_init();
sysaufs_brs_init();
err = sysaufs_init();
if (unlikely(err))
goto out;
err = au_wkq_init();
if (unlikely(err))
goto out_sysaufs;
err = au_cache_init();
if (unlikely(err))
goto out_wkq;
......@@ -133,6 +168,8 @@ static int __init aufs_init(void)
out_wkq:
au_wkq_fin();
out_sysaufs:
sysaufs_fin();
out:
return err;
}
......
......@@ -17,6 +17,14 @@
#include "dentry.h"
#include "inode.h"
struct path;
struct seq_file;
/* module parameters */
extern int sysaufs_brs;
/* ---------------------------------------------------------------------- */
void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink);
void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp,
int may_shrink);
......@@ -77,6 +85,8 @@ static inline int au_kmidx_sub(size_t sz, size_t new_sz)
#endif
}
int au_seq_path(struct seq_file *seq, struct path *path);
/* ---------------------------------------------------------------------- */
/* kmem cache */
......
......@@ -46,6 +46,10 @@ int au_si_alloc(struct super_block *sb)
if (unlikely(!sbinfo->si_branch))
goto out_sbinfo;
err = sysaufs_si_init(sbinfo);
if (unlikely(err))
goto out_br;
au_nwt_init(&sbinfo->si_nowait);
au_rw_init_wlock(&sbinfo->si_rwsem);
......@@ -58,9 +62,11 @@ int au_si_alloc(struct super_block *sb)
/* leave si_xib_last_pindex and si_xib_next_bit */
/* leave other members for sysaufs and si_mnt. */
sbinfo->si_sb = sb;
sb->s_fs_info = sbinfo;
return 0; /* success */
out_br:
au_kfree_try_rcu(sbinfo->si_branch);
out_sbinfo:
au_kfree_rcu(sbinfo);
......
......@@ -8,6 +8,7 @@
*/
#include <linux/iversion.h>
#include <linux/seq_file.h>
#include "aufs.h"
/*
......@@ -69,6 +70,84 @@ out:
return inode;
}
/* lock free root dinfo */
/* re-commit later */ __maybe_unused
static int au_show_brs(struct seq_file *seq, struct super_block *sb)
{
int err;
aufs_bindex_t bindex, bbot;
struct path path;
struct au_hdentry *hdp;
struct au_branch *br;
au_br_perm_str_t perm;
err = 0;
bbot = au_sbbot(sb);
bindex = 0;
hdp = au_hdentry(au_di(sb->s_root), bindex);
for (; !err && bindex <= bbot; bindex++, hdp++) {
br = au_sbr(sb, bindex);
path.mnt = au_br_mnt(br);
path.dentry = hdp->hd_dentry;
err = au_seq_path(seq, &path);
if (!err) {
au_optstr_br_perm(&perm, br->br_perm);
seq_printf(seq, "=%s", perm.a);
if (bindex != bbot)
seq_putc(seq, ':');
}
}
if (unlikely(err || seq_has_overflowed(seq)))
err = -E2BIG;
return err;
}
/* re-commit later */ __maybe_unused
static int au_show_xino(struct seq_file *seq, struct super_block *sb)
{
#ifdef CONFIG_SYSFS
return 0;
#else
int err;
const int len = sizeof(AUFS_XINO_FNAME) - 1;
aufs_bindex_t bindex, brid;
struct qstr *name;
struct file *f;
struct dentry *d, *h_root;
struct au_branch *br;
AuRwMustAnyLock(&sbinfo->si_rwsem);
err = 0;
f = au_sbi(sb)->si_xib;
if (!f)
goto out;
/* stop printing the default xino path on the first writable branch */
h_root = NULL;
bindex = au_xi_root(sb, f->f_path.dentry);
if (bindex >= 0) {
br = au_sbr_sb(sb, bindex);
h_root = au_br_dentry(br);
}
d = f->f_path.dentry;
name = &d->d_name;
/* safe ->d_parent because the file is unlinked */
if (d->d_parent == h_root
&& name->len == len
&& !memcmp(name->name, AUFS_XINO_FNAME, len))
goto out;
seq_puts(seq, ",xino=");
err = au_xino_path(seq, f);
out:
return err;
#endif
}
/* ---------------------------------------------------------------------- */
/* final actions when unmounting a file system */
......
......@@ -63,6 +63,9 @@ struct au_sbinfo {
* but using sysfs is majority.
*/
struct kobject si_kobj;
/* dirty, necessary for unmounting, sysfs and sysrq */
struct super_block *si_sb;
};
/* ---------------------------------------------------------------------- */
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* sysfs interface and lifetime management
* they are necessary regardless sysfs is disabled.
*/
#include <linux/random.h>
#include "aufs.h"
unsigned long sysaufs_si_mask;
struct kset *sysaufs_kset;
#define AuSiAttr(_name) { \
.attr = { .name = __stringify(_name), .mode = 0444 }, \
.show = sysaufs_si_##_name, \
}
static struct sysaufs_si_attr sysaufs_si_attr_xi_path = AuSiAttr(xi_path);
struct attribute *sysaufs_si_attrs[] = {
&sysaufs_si_attr_xi_path.attr,
NULL,
};
static const struct sysfs_ops au_sbi_ops = {
.show = sysaufs_si_show
};
static struct kobj_type au_sbi_ktype = {
.release = au_si_free,
.sysfs_ops = &au_sbi_ops,
.default_attrs = sysaufs_si_attrs
};
/* ---------------------------------------------------------------------- */
int sysaufs_si_init(struct au_sbinfo *sbinfo)
{
int err;
sbinfo->si_kobj.kset = sysaufs_kset;
/* cf. sysaufs_name() */
err = kobject_init_and_add
(&sbinfo->si_kobj, &au_sbi_ktype, /*&sysaufs_kset->kobj*/NULL,
SysaufsSiNamePrefix "%lx", sysaufs_si_id(sbinfo));
return err;
}
void sysaufs_fin(void)
{
sysfs_remove_group(&sysaufs_kset->kobj, sysaufs_attr_group);
kset_unregister(sysaufs_kset);
}
int __init sysaufs_init(void)
{
int err;
do {
get_random_bytes(&sysaufs_si_mask, sizeof(sysaufs_si_mask));
} while (!sysaufs_si_mask);
err = -EINVAL;
sysaufs_kset = kset_create_and_add(AUFS_NAME, NULL, fs_kobj);
if (unlikely(!sysaufs_kset))
goto out;
err = PTR_ERR(sysaufs_kset);
if (IS_ERR(sysaufs_kset))
goto out;
err = sysfs_create_group(&sysaufs_kset->kobj, sysaufs_attr_group);
if (unlikely(err))
kset_unregister(sysaufs_kset);
out:
return err;
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* sysfs interface and mount lifetime management
*/
#ifndef __SYSAUFS_H__
#define __SYSAUFS_H__
#ifdef __KERNEL__
#include <linux/sysfs.h>
#include "module.h"
struct super_block;
struct au_sbinfo;
struct sysaufs_si_attr {
struct attribute attr;
int (*show)(struct seq_file *seq, struct super_block *sb);
};
/* ---------------------------------------------------------------------- */
/* sysaufs.c */
extern unsigned long sysaufs_si_mask;
extern struct kset *sysaufs_kset;
extern struct attribute *sysaufs_si_attrs[];
int sysaufs_si_init(struct au_sbinfo *sbinfo);
int __init sysaufs_init(void);
void sysaufs_fin(void);
/* ---------------------------------------------------------------------- */
/* some people doesn't like to show a pointer in kernel */
static inline unsigned long sysaufs_si_id(struct au_sbinfo *sbinfo)
{
return sysaufs_si_mask ^ (unsigned long)sbinfo;
}
#define SysaufsSiNamePrefix "si_"
#define SysaufsSiNameLen (sizeof(SysaufsSiNamePrefix) + 16)
static inline void sysaufs_name(struct au_sbinfo *sbinfo, char *name)
{
snprintf(name, SysaufsSiNameLen, SysaufsSiNamePrefix "%lx",
sysaufs_si_id(sbinfo));
}
struct au_branch;
#ifdef CONFIG_SYSFS
/* sysfs.c */
extern struct attribute_group *sysaufs_attr_group;
int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb);
ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr,
char *buf);
void sysaufs_br_init(struct au_branch *br);
void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex);
#define sysaufs_brs_init() do {} while (0)
#else
#define sysaufs_attr_group NULL
AuStubInt0(sysaufs_si_xi_path, struct seq_file *seq, struct super_block *sb)
AuStub(ssize_t, sysaufs_si_show, return 0, struct kobject *kobj,
struct attribute *attr, char *buf)
AuStubVoid(sysaufs_br_init, struct au_branch *br)
AuStubVoid(sysaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex)
static inline void sysaufs_brs_init(void)
{
sysaufs_brs = 0;
}
#endif /* CONFIG_SYSFS */
#endif /* __KERNEL__ */
#endif /* __SYSAUFS_H__ */
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* sysfs interface
*/
#include <linux/seq_file.h>
#include "aufs.h"
static struct attribute *au_attr[] = {
NULL, /* need to NULL terminate the list of attributes */
};
static struct attribute_group sysaufs_attr_group_body = {
.attrs = au_attr
};
struct attribute_group *sysaufs_attr_group = &sysaufs_attr_group_body;
/* ---------------------------------------------------------------------- */
int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb)
{
int err;
SiMustAnyLock(sb);
err = 0;
if (au_opt_test(au_mntflags(sb), XINO)) {
err = au_xino_path(seq, au_sbi(sb)->si_xib);
seq_putc(seq, '\n');
}
return err;
}
/*
* the lifetime of branch is independent from the entry under sysfs.
* sysfs handles the lifetime of the entry, and never call ->show() after it is
* unlinked.
*/
static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb,
aufs_bindex_t bindex, int idx)
{
int err;
struct path path;
struct dentry *root;
struct au_branch *br;
au_br_perm_str_t perm;
AuDbg("b%d\n", bindex);
err = 0;
root = sb->s_root;
di_read_lock_parent(root, !AuLock_IR);
br = au_sbr(sb, bindex);
switch (idx) {
case AuBrSysfs_BR:
path.mnt = au_br_mnt(br);
path.dentry = au_h_dptr(root, bindex);
err = au_seq_path(seq, &path);
if (!err) {
au_optstr_br_perm(&perm, br->br_perm);
seq_printf(seq, "=%s\n", perm.a);
}
break;
case AuBrSysfs_BRID:
seq_printf(seq, "%d\n", br->br_id);
break;
}
di_read_unlock(root, !AuLock_IR);
if (unlikely(err || seq_has_overflowed(seq)))
err = -E2BIG;
return err;
}
/* ---------------------------------------------------------------------- */
static struct seq_file *au_seq(char *p, ssize_t len)
{
struct seq_file *seq;
seq = kzalloc(sizeof(*seq), GFP_NOFS);
if (seq) {
/* mutex_init(&seq.lock); */
seq->buf = p;
seq->size = len;
return seq; /* success */
}
seq = ERR_PTR(-ENOMEM);
return seq;
}
#define SysaufsBr_PREFIX "br"
#define SysaufsBrid_PREFIX "brid"
/* todo: file size may exceed PAGE_SIZE */
ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
ssize_t err;
int idx;
long l;
aufs_bindex_t bbot;
struct au_sbinfo *sbinfo;
struct super_block *sb;
struct seq_file *seq;
char *name;
struct attribute **cattr;
sbinfo = container_of(kobj, struct au_sbinfo, si_kobj);
sb = sbinfo->si_sb;
/*
* prevent a race condition between sysfs and aufs.
* for instance, sysfs_file_read() calls sysfs_get_active_two() which
* prohibits maintaining the sysfs entries.
* hew we acquire read lock after sysfs_get_active_two().
* on the other hand, the remount process may maintain the sysfs/aufs
* entries after acquiring write lock.
* it can cause a deadlock.
* simply we gave up processing read here.
*/
err = -EBUSY;
if (unlikely(!si_noflush_read_trylock(sb)))
goto out;
seq = au_seq(buf, PAGE_SIZE);
err = PTR_ERR(seq);
if (IS_ERR(seq))
goto out_unlock;
name = (void *)attr->name;
cattr = sysaufs_si_attrs;
while (*cattr) {
if (!strcmp(name, (*cattr)->name)) {
err = container_of(*cattr, struct sysaufs_si_attr, attr)
->show(seq, sb);
goto out_seq;
}
cattr++;
}
if (!strncmp(name, SysaufsBrid_PREFIX,
sizeof(SysaufsBrid_PREFIX) - 1)) {
idx = AuBrSysfs_BRID;
name += sizeof(SysaufsBrid_PREFIX) - 1;
} else if (!strncmp(name, SysaufsBr_PREFIX,
sizeof(SysaufsBr_PREFIX) - 1)) {
idx = AuBrSysfs_BR;
name += sizeof(SysaufsBr_PREFIX) - 1;
} else
BUG();
err = kstrtol(name, 10, &l);
if (!err) {
bbot = au_sbbot(sb);
if (l <= bbot)
err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l, idx);
else
err = -ENOENT;
}
out_seq:
if (!err) {
err = seq->count;
/* sysfs limit */
if (unlikely(err == PAGE_SIZE))
err = -EFBIG;
}
au_kfree_rcu(seq);
out_unlock:
si_read_unlock(sb);
out:
return err;
}
/* ---------------------------------------------------------------------- */
void sysaufs_br_init(struct au_branch *br)
{
int i;
struct au_brsysfs *br_sysfs;
struct attribute *attr;
br_sysfs = br->br_sysfs;
for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) {
attr = &br_sysfs->attr;
sysfs_attr_init(attr);
attr->name = br_sysfs->name;
attr->mode = 0444;
br_sysfs++;
}
}
void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex)
{
int err, i;
aufs_bindex_t bbot;
struct kobject *kobj;
struct au_branch *br;
struct au_brsysfs *br_sysfs;
if (!sysaufs_brs)
return;
kobj = &au_sbi(sb)->si_kobj;
bbot = au_sbbot(sb);
for (; bindex <= bbot; bindex++) {
br = au_sbr(sb, bindex);
br_sysfs = br->br_sysfs;
snprintf(br_sysfs[AuBrSysfs_BR].name, sizeof(br_sysfs->name),
SysaufsBr_PREFIX "%d", bindex);
snprintf(br_sysfs[AuBrSysfs_BRID].name, sizeof(br_sysfs->name),
SysaufsBrid_PREFIX "%d", bindex);
for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) {
err = sysfs_create_file(kobj, &br_sysfs->attr);
if (unlikely(err))
pr_warn("failed %s under sysfs(%d)\n",
br_sysfs->name, err);
br_sysfs++;
}
}
}
......@@ -24,6 +24,7 @@
#include <linux/file.h>
#include <linux/sched/signal.h>
#include <linux/seq_file.h>
#include <linux/statfs.h>
#include <linux/uaccess.h>
#include "aufs.h"
......@@ -1454,3 +1455,23 @@ void au_xino_delete_inode(struct inode *inode, const int unlinked)
err = au_xino_do_write(xwrite, file, &calc, /*ino*/0);
}
}
/* ---------------------------------------------------------------------- */
int au_xino_path(struct seq_file *seq, struct file *file)
{
int err;
err = au_seq_path(seq, &file->f_path);
if (unlikely(err))
goto out;
#define Deleted "\\040(deleted)"
seq->count -= sizeof(Deleted) - 1;
AuDebugOn(memcmp(seq->buf + seq->count, Deleted,
sizeof(Deleted) - 1));
#undef Deleted
out:
return err;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment