[PATCH 10/67] aufs mount and super_block operations
From: hooanon05
Date: Fri May 16 2008 - 11:04:23 EST
From: Junjiro Okajima <hooanon05@xxxxxxxxxxx>
initial commit
aufs mount and super_block operations
Signed-off-by: Junjiro Okajima <hooanon05@xxxxxxxxxxx>
---
fs/aufs/super.c | 891 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 891 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/super.c b/fs/aufs/super.c
new file mode 100644
index 0000000..491f75f
--- /dev/null
+++ b/fs/aufs/super.c
@@ -0,0 +1,891 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * mount and super_block operations
+ *
+ * $Id: super.c,v 1.5 2008/05/12 00:28:35 sfjro Exp $
+ */
+
+#include <linux/module.h>
+#include <linux/buffer_head.h>
+#include <linux/seq_file.h>
+#include <linux/smp_lock.h>
+#include <linux/statfs.h>
+
+#include "aufs.h"
+
+/*
+ * super_operations
+ */
+static struct inode *aufs_alloc_inode(struct super_block *sb)
+{
+ struct aufs_icntnr *c;
+
+ AuTraceEnter();
+
+ c = au_cache_alloc_icntnr();
+ //if (LktrCond) {au_cache_free_icntnr(c); c = NULL;}
+ if (c) {
+ inode_init_once(&c->vfs_inode);
+ c->vfs_inode.i_version = 1; //sigen(sb);
+ c->iinfo.ii_hinode = NULL;
+ return &c->vfs_inode;
+ }
+ return NULL;
+}
+
+static void aufs_destroy_inode(struct inode *inode)
+{
+ LKTRTrace("i%lu\n", inode->i_ino);
+ au_iinfo_fin(inode);
+ au_cache_free_icntnr(container_of(inode, struct aufs_icntnr,
+ vfs_inode));
+}
+
+struct inode *au_iget_locked(struct super_block *sb, ino_t ino)
+{
+ struct inode *inode;
+ int err;
+#if 0
+ static struct backing_dev_info bdi = {
+ .ra_pages = 0, /* No readahead */
+ .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK
+ };
+#endif
+
+ LKTRTrace("i%lu\n", ino);
+
+ inode = iget_locked(sb, ino);
+ if (unlikely(!inode)) {
+ inode = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+ AuDebugOn(IS_ERR(inode));
+ if (unlikely(!(inode->i_state & I_NEW)))
+ goto out;
+
+ err = au_iinfo_init(inode);
+ //if (LktrCond) err = -1;
+ if (!err) {
+ inode->i_version++;
+ inode->i_op = &aufs_iop;
+ inode->i_fop = &aufs_file_fop;
+ inode->i_mapping->a_ops = &aufs_aop;
+ //inode->i_mapping->backing_dev_info = &bdi;
+ } else {
+ iget_failed(inode);
+ inode = ERR_PTR(err);
+ }
+
+ out:
+ /* never return NULL */
+ AuDebugOn(!inode);
+ AuTraceErrPtr(inode);
+ return inode;
+}
+
+static int au_show_brs(struct seq_file *seq, struct super_block *sb)
+{
+ int err;
+ aufs_bindex_t bindex, bend;
+ struct dentry *root;
+ struct path path;
+
+ AuTraceEnter();
+
+ err = 0;
+ root = sb->s_root;
+ bend = au_sbend(sb);
+ for (bindex = 0; !err && bindex <= bend; bindex++) {
+ path.mnt = au_sbr_mnt(sb, bindex);
+ path.dentry = au_h_dptr(root, bindex);
+ err = seq_path(seq, &path, au_esc_chars);
+ if (err > 0)
+ err = seq_printf
+ (seq, "=%s",
+ au_optstr_br_perm(au_sbr_perm(sb, bindex)));
+ if (!err && bindex != bend)
+ err = seq_putc(seq, ':');
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+static void au_show_wbr_create(struct seq_file *m, int v,
+ struct au_sbinfo *sbinfo)
+{
+ const char *pat;
+
+ AuDebugOn(v == AuWbrCreate_Def);
+
+ seq_printf(m, ",create=");
+ pat = au_optstr_wbr_create(v);
+ switch (v) {
+ case AuWbrCreate_TDP:
+ case AuWbrCreate_RR:
+ case AuWbrCreate_MFS:
+ case AuWbrCreate_PMFS:
+ seq_printf(m, pat);
+ break;
+ case AuWbrCreate_MFSV:
+ seq_printf(m, /*pat*/"mfs:%lu",
+ sbinfo->si_wbr_mfs.mfs_expire / HZ);
+ break;
+ case AuWbrCreate_PMFSV:
+ seq_printf(m, /*pat*/"pmfs:%lu",
+ sbinfo->si_wbr_mfs.mfs_expire / HZ);
+ break;
+ case AuWbrCreate_MFSRR:
+ seq_printf(m, /*pat*/"mfsrr:%Lu",
+ sbinfo->si_wbr_mfs.mfsrr_watermark);
+ break;
+ case AuWbrCreate_MFSRRV:
+ seq_printf(m, /*pat*/"mfsrr:%Lu:%lu",
+ sbinfo->si_wbr_mfs.mfsrr_watermark,
+ sbinfo->si_wbr_mfs.mfs_expire / HZ);
+ break;
+ }
+}
+
+/* seq_file will re-call me in case of too long string */
+static int aufs_show_options(struct seq_file *m, struct vfsmount *mnt)
+{
+ int err, n;
+ struct super_block *sb;
+ struct au_sbinfo *sbinfo;
+ struct dentry *root;
+ struct file *xino;
+ unsigned int mnt_flags, v;
+ struct path path;
+
+ //au_debug_on();
+ AuTraceEnter();
+
+ sb = mnt->mnt_sb;
+ root = sb->s_root;
+ if (!sysaufs_brs)
+ aufs_read_lock(root, !AuLock_IR);
+ else
+ si_noflush_read_lock(sb);
+ sbinfo = au_sbi(sb);
+ seq_printf(m, ",si=%p", sbinfo);
+ mnt_flags = au_mntflags(sb);
+ if (au_opt_test(mnt_flags, XINO)) {
+ seq_puts(m, ",xino=");
+ xino = sbinfo->si_xib;
+ path.mnt = xino->f_vfsmnt;
+ path.dentry = xino->f_dentry;
+ err = seq_path(m, &path, au_esc_chars);
+ if (unlikely(err <= 0))
+ goto out;
+ err = 0;
+#define Deleted "\\040(deleted)"
+ m->count -= sizeof(Deleted) - 1;
+ AuDebugOn(memcmp(m->buf + m->count, Deleted,
+ sizeof(Deleted) - 1));
+#undef Deleted
+ } else
+ seq_puts(m, ",noxino");
+
+#define AuBool(name, str) do { \
+ v = au_opt_test(mnt_flags, name); \
+ if (v != au_opt_test(AuOpt_Def, name)) \
+ seq_printf(m, ",%s" #str, v ? "" : "no"); \
+} while (0)
+
+#define AuStr(name, str) do { \
+ v = mnt_flags & AuOptMask_##name; \
+ if (v != (AuOpt_Def & AuOptMask_##name)) \
+ seq_printf(m, "," #str "=%s", au_optstr_##str(v)); \
+} while (0)
+
+#ifdef CONFIG_AUFS_COMPAT
+#define AuStr_BrOpt "dirs="
+#else
+#define AuStr_BrOpt "br:"
+#endif
+
+ AuBool(TRUNC_XINO, trunc_xino);
+ AuBool(DIRPERM1, dirperm1);
+ AuBool(SHWH, shwh);
+ AuBool(PLINK, plink);
+ AuStr(UDBA, udba);
+
+ v = sbinfo->si_wbr_create;
+ if (v != AuWbrCreate_Def)
+ au_show_wbr_create(m, v, sbinfo);
+
+ v = sbinfo->si_wbr_copyup;
+ if (v != AuWbrCopyup_Def)
+ seq_printf(m, ",cpup=%s", au_optstr_wbr_copyup(v));
+
+ v = au_opt_test(mnt_flags, ALWAYS_DIROPQ);
+ if (v != au_opt_test(AuOpt_Def, ALWAYS_DIROPQ))
+ seq_printf(m, ",diropq=%c", v ? 'a' : 'w');
+ AuBool(REFROF, refrof);
+ AuBool(DLGT, dlgt);
+ AuBool(WARN_PERM, warn_perm);
+ AuBool(VERBOSE, verbose);
+
+ n = sbinfo->si_dirwh;
+ if (n != AUFS_DIRWH_DEF)
+ seq_printf(m, ",dirwh=%d", n);
+ n = sbinfo->si_rdcache / HZ;
+ if (n != AUFS_RDCACHE_DEF)
+ seq_printf(m, ",rdcache=%d", n);
+
+ AuStr(COO, coo);
+
+ out:
+ if (!sysaufs_brs) {
+ seq_puts(m, "," AuStr_BrOpt);
+ au_show_brs(m, sb);
+ aufs_read_unlock(root, !AuLock_IR);
+ } else
+ si_read_unlock(sb);
+ //au_debug_off();
+ return 0;
+
+#undef AuBool
+#undef AuStr
+#undef AuStr_BrOpt
+}
+
+static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ int err;
+
+ AuTraceEnter();
+
+ aufs_read_lock(dentry->d_sb->s_root, 0);
+ err = vfsub_statfs(au_h_dptr(dentry->d_sb->s_root, 0), buf,
+ !!au_opt_test_dlgt(au_mntflags(dentry->d_sb)));
+ //if (LktrCond) err = -1;
+ aufs_read_unlock(dentry->d_sb->s_root, 0);
+ if (!err) {
+ buf->f_type = AUFS_SUPER_MAGIC;
+ buf->f_namelen -= AUFS_WH_PFX_LEN;
+ //todo: support uuid?
+ memset(&buf->f_fsid, 0, sizeof(buf->f_fsid));
+ }
+ /* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */
+
+ AuTraceErr(err);
+ return err;
+}
+
+static void au_fsync_br(struct super_block *sb)
+{
+#ifdef CONFIG_AUFS_FSYNC_SUPER_PATCH
+ aufs_bindex_t bend, bindex;
+ int brperm;
+ struct super_block *h_sb;
+
+ AuTraceEnter();
+
+ si_write_lock(sb);
+ bend = au_sbend(sb);
+ for (bindex = 0; bindex < bend; bindex++) {
+ brperm = au_sbr_perm(sb, bindex);
+ if (brperm == AuBr_RR || brperm == AuBr_RRWH)
+ continue;
+ h_sb = au_sbr_sb(sb, bindex);
+ if (bdev_read_only(h_sb->s_bdev))
+ continue;
+
+ lockdep_off();
+ down_write(&h_sb->s_umount);
+ shrink_dcache_sb(h_sb);
+ fsync_super(h_sb);
+ up_write(&h_sb->s_umount);
+ lockdep_on();
+ }
+ si_write_unlock(sb);
+#endif
+}
+
+static void aufs_umount_begin(struct vfsmount *mnt, int flags)
+{
+ struct super_block *sb = mnt->mnt_sb;
+ struct au_sbinfo *sbinfo;
+
+ AuTraceEnter();
+ //todo: dont trust BKL.
+ AuDebugOn(!kernel_locked());
+
+ sbinfo = au_sbi(sb);
+ if (unlikely(!sbinfo))
+ return;
+
+ au_fsync_br(sb);
+
+ si_write_lock(sb);
+ if (au_opt_test(au_mntflags(sb), PLINK))
+ au_plink_put(sb);
+#if 0 // remove
+ if (unlikely(au_opt_test(au_mntflags(sb), UDBA_INOTIFY)))
+ shrink_dcache_sb(sb);
+#endif
+ spin_lock(&sbinfo->si_mntcache_lock);
+ sbinfo->si_mntcache = NULL;
+ spin_unlock(&sbinfo->si_mntcache_lock);
+#if 0
+ if (sbinfo->si_wbr_create_ops->fin)
+ sbinfo->si_wbr_create_ops->fin(sb);
+#endif
+ si_write_unlock(sb);
+}
+
+/* final actions when unmounting a file system */
+static void aufs_put_super(struct super_block *sb)
+{
+ struct au_sbinfo *sbinfo;
+
+ AuTraceEnter();
+
+ sbinfo = au_sbi(sb);
+ if (unlikely(!sbinfo))
+ return;
+ kobject_put(&sbinfo->si_kobj);
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * refresh dentry and inode at remount time.
+ */
+static int do_refresh(struct dentry *dentry, mode_t type,
+ unsigned int dir_flags)
+{
+ int err;
+ struct dentry *parent;
+ struct inode *inode;
+
+ LKTRTrace("%.*s, 0%o\n", AuDLNPair(dentry), type);
+ inode = dentry->d_inode;
+ AuDebugOn(!inode);
+
+ di_write_lock_child(dentry);
+ parent = dget_parent(dentry);
+ di_read_lock_parent(parent, AuLock_IR);
+ /* returns a number of positive dentries */
+ err = au_refresh_hdentry(dentry, type);
+ //err = -1;
+ if (err >= 0) {
+ err = au_refresh_hinode(inode, dentry);
+ //err = -1;
+ if (!err && type == S_IFDIR)
+ au_reset_hinotify(inode, dir_flags);
+ }
+ if (unlikely(err))
+ AuErr("unrecoverable error %d, %.*s\n", err, AuDLNPair(dentry));
+ di_read_unlock(parent, AuLock_IR);
+ dput(parent);
+ di_write_unlock(dentry);
+
+ AuTraceErr(err);
+ return err;
+}
+
+static int test_dir(struct dentry *dentry, void *arg)
+{
+ return S_ISDIR(dentry->d_inode->i_mode);
+}
+
+//todo: merge with refresh_nondir()
+static int refresh_dir(struct dentry *root, au_gen_t sgen)
+{
+ int err, i, j, ndentry, e;
+ const unsigned int flags = au_hi_flags(root->d_inode, /*isdir*/1);
+ struct au_dcsub_pages dpages;
+ struct au_dpage *dpage;
+ struct dentry **dentries;
+ struct inode *inode;
+
+ LKTRTrace("sgen %d\n", sgen);
+ SiMustWriteLock(root->d_sb);
+ //todo: dont trust BKL.
+ AuDebugOn(au_digen(root) != sgen || !kernel_locked());
+
+ err = 0;
+ list_for_each_entry(inode, &root->d_sb->s_inodes, i_sb_list)
+ if (S_ISDIR(inode->i_mode) && au_iigen(inode) != sgen) {
+ ii_write_lock_child(inode);
+ e = au_refresh_hinode_self(inode);
+ //e = -1;
+ ii_write_unlock(inode);
+ if (unlikely(e)) {
+ LKTRTrace("e %d, i%lu\n", e, inode->i_ino);
+ if (!err)
+ err = e;
+ /* go on even if err */
+ }
+ }
+
+ e = au_dpages_init(&dpages, GFP_TEMPORARY);
+ if (unlikely(e)) {
+ if (!err)
+ err = e;
+ goto out;
+ }
+ e = au_dcsub_pages(&dpages, root, test_dir, NULL);
+ if (unlikely(e)) {
+ if (!err)
+ err = e;
+ goto out_dpages;
+ }
+
+ for (i = 0; !e && i < dpages.ndpage; i++) {
+ dpage = dpages.dpages + i;
+ dentries = dpage->dentries;
+ ndentry = dpage->ndentry;
+ for (j = 0; !e && j < ndentry; j++) {
+ struct dentry *d;
+ d = dentries[j];
+#ifdef CONFIG_AUFS_DEBUG
+ {
+ struct dentry *parent;
+ parent = dget_parent(d);
+ AuDebugOn(!S_ISDIR(d->d_inode->i_mode)
+ || IS_ROOT(d)
+ || au_digen(parent) != sgen);
+ dput(parent);
+ }
+#endif
+ if (au_digen(d) != sgen) {
+ e = do_refresh(d, S_IFDIR, flags);
+ //e = -1;
+ if (unlikely(e && !err))
+ err = e;
+ /* break on err */
+ }
+ }
+ }
+
+ out_dpages:
+ au_dpages_free(&dpages);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static int test_nondir(struct dentry *dentry, void *arg)
+{
+ return !S_ISDIR(dentry->d_inode->i_mode);
+}
+
+static int refresh_nondir(struct dentry *root, au_gen_t sgen, int do_dentry)
+{
+ int err, i, j, ndentry, e;
+ struct au_dcsub_pages dpages;
+ struct au_dpage *dpage;
+ struct dentry **dentries;
+ struct inode *inode;
+
+ LKTRTrace("sgen %d\n", sgen);
+ SiMustWriteLock(root->d_sb);
+ //todo: dont trust BKL.
+ AuDebugOn(au_digen(root) != sgen || !kernel_locked());
+
+ err = 0;
+ list_for_each_entry(inode, &root->d_sb->s_inodes, i_sb_list)
+ if (!S_ISDIR(inode->i_mode) && au_iigen(inode) != sgen) {
+ ii_write_lock_child(inode);
+ e = au_refresh_hinode_self(inode);
+ //e = -1;
+ ii_write_unlock(inode);
+ if (unlikely(e)) {
+ LKTRTrace("e %d, i%lu\n", e, inode->i_ino);
+ if (!err)
+ err = e;
+ /* go on even if err */
+ }
+ }
+
+ if (!do_dentry)
+ goto out;
+
+ e = au_dpages_init(&dpages, GFP_TEMPORARY);
+ if (unlikely(e)) {
+ if (!err)
+ err = e;
+ goto out;
+ }
+ e = au_dcsub_pages(&dpages, root, test_nondir, NULL);
+ if (unlikely(e)) {
+ if (!err)
+ err = e;
+ goto out_dpages;
+ }
+
+ for (i = 0; i < dpages.ndpage; i++) {
+ dpage = dpages.dpages + i;
+ dentries = dpage->dentries;
+ ndentry = dpage->ndentry;
+ for (j = 0; j < ndentry; j++) {
+ struct dentry *d;
+ d = dentries[j];
+#ifdef CONFIG_AUFS_DEBUG
+ {
+ struct dentry *parent;
+ parent = dget_parent(d);
+ AuDebugOn(S_ISDIR(d->d_inode->i_mode)
+ || au_digen(parent) != sgen);
+ dput(parent);
+ }
+#endif
+ inode = d->d_inode;
+ if (inode && au_digen(d) != sgen) {
+ e = do_refresh(d, inode->i_mode & S_IFMT, 0);
+ //e = -1;
+ if (unlikely(e && !err))
+ err = e;
+ /* go on even err */
+ }
+ }
+ }
+
+ out_dpages:
+ au_dpages_free(&dpages);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* stop extra interpretation of errno in mount(8), and strange error messages */
+static int cvt_err(int err)
+{
+ AuTraceErr(err);
+
+ switch (err) {
+ case -ENOENT:
+ case -ENOTDIR:
+ case -EEXIST:
+ case -EIO:
+ err = -EINVAL;
+ }
+ return err;
+}
+
+/* protected by s_umount */
+static int aufs_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+ int err;
+ struct dentry *root;
+ struct inode *inode;
+ struct au_opts opts;
+ unsigned int dlgt;
+ struct au_sbinfo *sbinfo;
+
+ //au_debug_on();
+ LKTRTrace("flags 0x%x, data %s, len %lu\n",
+ *flags, data ? data : "NULL",
+ (unsigned long)(data ? strlen(data) : 0));
+
+ au_fsync_br(sb);
+ err = 0;
+ if (!data || !*data)
+ goto out; /* success */
+
+ err = -ENOMEM;
+ memset(&opts, 0, sizeof(opts));
+ opts.opt = (void *)__get_free_page(GFP_TEMPORARY);
+ //if (LktrCond) {free_page((unsigned long)opts.opt); opts.opt = NULL;}
+ if (unlikely(!opts.opt))
+ goto out;
+ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt);
+ opts.flags = AuOpts_REMOUNT;
+
+ /* parse it before aufs lock */
+ err = au_opts_parse(sb, *flags, data, &opts);
+ //if (LktrCond) {au_free_opts(&opts); err = -1;}
+ if (unlikely(err))
+ goto out_opts;
+
+ sbinfo = au_sbi(sb);
+ root = sb->s_root;
+ inode = root->d_inode;
+ mutex_lock(&inode->i_mutex);
+ aufs_write_lock(root);
+
+ //DbgSleep(3);
+
+ /* au_do_opts() may return an error */
+ err = au_opts_remount(sb, &opts);
+ //if (LktrCond) err = -1;
+ au_opts_free(&opts);
+
+ if (au_ftest_opts(opts.flags, REFRESH_DIR)
+ || au_ftest_opts(opts.flags, REFRESH_NONDIR)) {
+ int rerr;
+ au_gen_t sigen;
+
+ dlgt = !!au_opt_test_dlgt(sbinfo->si_mntflags);
+ au_opt_clr(sbinfo->si_mntflags, DLGT);
+ au_sigen_inc(sb);
+ au_reset_hinotify(inode, au_hi_flags(inode, /*isdir*/1));
+ sigen = au_sigen(sb);
+ au_fclr_si(sbinfo, FAILED_REFRESH_DIRS);
+
+ DiMustNoWaiters(root);
+ IiMustNoWaiters(root->d_inode);
+ di_write_unlock(root);
+
+ rerr = refresh_dir(root, sigen);
+ if (unlikely(rerr)) {
+ au_fset_si(sbinfo, FAILED_REFRESH_DIRS);
+ AuWarn("Refreshing directories failed, ignores (%d)\n",
+ rerr);
+ }
+
+ if (unlikely(au_ftest_opts(opts.flags, REFRESH_NONDIR))) {
+ //au_debug_on();
+ rerr = refresh_nondir(root, sigen, !rerr);
+ if (unlikely(rerr))
+ AuWarn("Refreshing non-directories failed,"
+ " ignores (%d)\n", rerr);
+ //au_debug_off();
+ }
+
+ /* aufs_write_lock() calls ..._child() */
+ di_write_lock_child(root);
+
+ au_cpup_attr_all(inode);
+ if (unlikely(dlgt))
+ au_opt_set(sbinfo->si_mntflags, DLGT);
+ }
+
+ aufs_write_unlock(root);
+ mutex_unlock(&inode->i_mutex);
+
+ out_opts:
+ free_page((unsigned long)opts.opt);
+ out:
+ err = cvt_err(err);
+ AuTraceErr(err);
+ //au_debug_off();
+ return err;
+}
+
+static struct super_operations aufs_sop = {
+ .alloc_inode = aufs_alloc_inode,
+ .destroy_inode = aufs_destroy_inode,
+ //.dirty_inode = aufs_dirty_inode,
+ //.write_inode = aufs_write_inode,
+ //void (*put_inode) (struct inode *);
+ .drop_inode = generic_delete_inode,
+ //.delete_inode = aufs_delete_inode,
+ //.clear_inode = aufs_clear_inode,
+
+ .show_options = aufs_show_options,
+ .statfs = aufs_statfs,
+
+ .put_super = aufs_put_super,
+ //void (*write_super) (struct super_block *);
+ //int (*sync_fs)(struct super_block *sb, int wait);
+ //void (*write_super_lockfs) (struct super_block *);
+ //void (*unlockfs) (struct super_block *);
+ .remount_fs = aufs_remount_fs,
+ /* depends upon umount flags. also use put_super() (< 2.6.18) */
+ .umount_begin = aufs_umount_begin
+};
+
+/* ---------------------------------------------------------------------- */
+
+static int alloc_root(struct super_block *sb)
+{
+ int err;
+ struct inode *inode;
+ struct dentry *root;
+
+ AuTraceEnter();
+
+ err = -ENOMEM;
+ inode = au_iget_locked(sb, AUFS_ROOT_INO);
+ //if (LktrCond) {iput(inode); inode = NULL;}
+ err = PTR_ERR(inode);
+ if (IS_ERR(inode))
+ goto out;
+ unlock_new_inode(inode);
+ inode->i_mode = S_IFDIR;
+ root = d_alloc_root(inode);
+ //if (LktrCond) {igrab(inode); dput(root); root = NULL;}
+ if (unlikely(!root))
+ goto out_iput;
+ err = PTR_ERR(root);
+ if (IS_ERR(root))
+ goto out_iput;
+
+ err = au_alloc_dinfo(root);
+ //if (LktrCond){au_rw_write_unlock(&au_di(root)->di_rwsem);err=-1;}
+ if (!err) {
+ sb->s_root = root;
+ return 0; /* success */
+ }
+ dput(root);
+ goto out; /* do not iput */
+
+ out_iput:
+ iget_failed(inode);
+ iput(inode);
+ out:
+ AuTraceErr(err);
+ return err;
+
+}
+
+static int aufs_fill_super(struct super_block *sb, void *raw_data, int silent)
+{
+ int err;
+ struct dentry *root;
+ struct inode *inode;
+ struct au_opts opts;
+ char *arg = raw_data;
+
+ //au_debug_on();
+ if (unlikely(!arg || !*arg)) {
+ err = -EINVAL;
+ AuErr("no arg\n");
+ goto out;
+ }
+ LKTRTrace("%s, silent %d\n", arg, silent);
+
+ err = -ENOMEM;
+ memset(&opts, 0, sizeof(opts));
+ opts.opt = (void *)__get_free_page(GFP_TEMPORARY);
+ //if (LktrCond) {free_page((unsigned long)opts.opt); opts.opt = NULL;}
+ if (unlikely(!opts.opt))
+ goto out;
+ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt);
+
+ err = au_si_alloc(sb);
+ //if (LktrCond) {si_write_unlock(sb);free_sbinfo(sb);err=-1;}
+ if (unlikely(err))
+ goto out_opts;
+ SiMustWriteLock(sb);
+ /* all timestamps always follow the ones on the branch */
+ sb->s_flags |= MS_NOATIME | MS_NODIRATIME;
+ sb->s_op = &aufs_sop;
+ sb->s_magic = AUFS_SUPER_MAGIC;
+ au_init_export_op(sb);
+
+ err = alloc_root(sb);
+ //if (LktrCond) {au_rw_write_unlock(&au_di(sb->s_root)->di_rwsem);
+ //dput(sb->s_root);sb->s_root=NULL;err=-1;}
+ if (unlikely(err)) {
+ AuDebugOn(sb->s_root);
+ si_write_unlock(sb);
+ goto out_info;
+ }
+ root = sb->s_root;
+ DiMustWriteLock(root);
+ inode = root->d_inode;
+ inode->i_nlink = 2;
+
+ /*
+ * actually we can parse options regardless aufs lock here.
+ * but at remount time, parsing must be done before aufs lock.
+ * so we follow the same rule.
+ */
+ ii_write_lock_parent(inode);
+ aufs_write_unlock(root);
+ err = au_opts_parse(sb, sb->s_flags, arg, &opts);
+ //if (LktrCond) {au_opts_free(&opts); err = -1;}
+ if (unlikely(err))
+ goto out_root;
+
+ /* lock vfs_inode first, then aufs. */
+ mutex_lock(&inode->i_mutex);
+ inode->i_op = &aufs_dir_iop;
+ inode->i_fop = &aufs_dir_fop;
+ aufs_write_lock(root);
+
+ sb->s_maxbytes = 0;
+ err = au_opts_mount(sb, &opts);
+ //if (LktrCond) err = -1;
+ au_opts_free(&opts);
+ if (unlikely(err))
+ goto out_unlock;
+ AuDebugOn(!sb->s_maxbytes);
+
+ //AuDbgDentry(root);
+ aufs_write_unlock(root);
+ mutex_unlock(&inode->i_mutex);
+ //AuDbgSb(sb);
+ goto out_opts; /* success */
+
+ out_unlock:
+ aufs_write_unlock(root);
+ mutex_unlock(&inode->i_mutex);
+ out_root:
+ dput(root);
+ sb->s_root = NULL;
+ out_info:
+ au_fset_si(au_sbi(sb), FAILED_INIT);
+ kobject_put(&au_sbi(sb)->si_kobj);
+ sb->s_fs_info = NULL;
+ out_opts:
+ free_page((unsigned long)opts.opt);
+ out:
+ AuTraceErr(err);
+ err = cvt_err(err);
+ AuTraceErr(err);
+ //au_debug_off();
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int aufs_get_sb(struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *raw_data,
+ struct vfsmount *mnt)
+{
+ int err;
+
+ /* all timestamps always follow the ones on the branch */
+ /* mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME; */
+ err = get_sb_nodev(fs_type, flags, raw_data, aufs_fill_super, mnt);
+ if (!err) {
+ struct super_block *sb = mnt->mnt_sb;
+ struct au_sbinfo *sbinfo = au_sbi(sb);
+
+ /* no get/put */
+ spin_lock_init(&sbinfo->si_mntcache_lock);
+ sbinfo->si_mntcache = mnt;
+
+ au_sbilist_lock();
+ au_sbilist_add(sbinfo);
+ si_write_lock(sb);
+ sysaufs_brs_add(sb, 0);
+ si_write_unlock(sb);
+ au_sbilist_unlock();
+ }
+ return err;
+}
+
+struct file_system_type aufs_fs_type = {
+ .name = AUFS_FSTYPE,
+ .fs_flags = FS_REVAL_DOT, /* for UDBA and NFS branch */
+ .get_sb = aufs_get_sb,
+ .kill_sb = generic_shutdown_super,
+ //.kill_sb = kill_anon_super,
+ /* no need to __module_get() and module_put(). */
+ .owner = THIS_MODULE,
+};
--
1.4.4.4
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/