summaryrefslogtreecommitdiff
path: root/fs/aufs/super.c
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-09-08 01:01:14 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-09-08 01:01:14 -0300
commite5fd91f1ef340da553f7a79da9540c3db711c937 (patch)
treeb11842027dc6641da63f4bcc524f8678263304a3 /fs/aufs/super.c
parent2a9b0348e685a63d97486f6749622b61e9e3292f (diff)
Linux-libre 4.2-gnu
Diffstat (limited to 'fs/aufs/super.c')
-rw-r--r--fs/aufs/super.c1004
1 files changed, 0 insertions, 1004 deletions
diff --git a/fs/aufs/super.c b/fs/aufs/super.c
deleted file mode 100644
index 6b7f38e48..000000000
--- a/fs/aufs/super.c
+++ /dev/null
@@ -1,1004 +0,0 @@
-/*
- * Copyright (C) 2005-2015 Junjiro R. 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, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * mount and super_block operations
- */
-
-#include <linux/mm.h>
-#include <linux/seq_file.h>
-#include <linux/statfs.h>
-#include <linux/vmalloc.h>
-#include "aufs.h"
-
-/*
- * super_operations
- */
-static struct inode *aufs_alloc_inode(struct super_block *sb __maybe_unused)
-{
- struct au_icntnr *c;
-
- c = au_cache_alloc_icntnr();
- if (c) {
- au_icntnr_init(c);
- c->vfs_inode.i_version = 1; /* sigen(sb); */
- c->iinfo.ii_hinode = NULL;
- return &c->vfs_inode;
- }
- return NULL;
-}
-
-static void aufs_destroy_inode_cb(struct rcu_head *head)
-{
- struct inode *inode = container_of(head, struct inode, i_rcu);
-
- INIT_HLIST_HEAD(&inode->i_dentry);
- au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode));
-}
-
-static void aufs_destroy_inode(struct inode *inode)
-{
- au_iinfo_fin(inode);
- call_rcu(&inode->i_rcu, aufs_destroy_inode_cb);
-}
-
-struct inode *au_iget_locked(struct super_block *sb, ino_t ino)
-{
- struct inode *inode;
- int err;
-
- inode = iget_locked(sb, ino);
- if (unlikely(!inode)) {
- inode = ERR_PTR(-ENOMEM);
- goto out;
- }
- if (!(inode->i_state & I_NEW))
- goto out;
-
- err = au_xigen_new(inode);
- if (!err)
- err = au_iinfo_init(inode);
- if (!err)
- inode->i_version++;
- else {
- iget_failed(inode);
- inode = ERR_PTR(err);
- }
-
-out:
- /* never return NULL */
- AuDebugOn(!inode);
- AuTraceErrPtr(inode);
- return inode;
-}
-
-/* lock free root dinfo */
-static int au_show_brs(struct seq_file *seq, struct super_block *sb)
-{
- int err;
- aufs_bindex_t bindex, bend;
- struct path path;
- struct au_hdentry *hdp;
- struct au_branch *br;
- au_br_perm_str_t perm;
-
- err = 0;
- bend = au_sbend(sb);
- hdp = au_di(sb->s_root)->di_hdentry;
- for (bindex = 0; !err && bindex <= bend; bindex++) {
- br = au_sbr(sb, bindex);
- path.mnt = au_br_mnt(br);
- path.dentry = hdp[bindex].hd_dentry;
- err = au_seq_path(seq, &path);
- if (err > 0) {
- au_optstr_br_perm(&perm, br->br_perm);
- err = seq_printf(seq, "=%s", perm.a);
- if (err == -1)
- err = -E2BIG;
- }
- if (!err && bindex != bend)
- err = seq_putc(seq, ':');
- }
-
- return err;
-}
-
-static void au_show_wbr_create(struct seq_file *m, int v,
- struct au_sbinfo *sbinfo)
-{
- const char *pat;
-
- AuRwMustAnyLock(&sbinfo->si_rwsem);
-
- seq_puts(m, ",create=");
- pat = au_optstr_wbr_create(v);
- switch (v) {
- case AuWbrCreate_TDP:
- case AuWbrCreate_RR:
- case AuWbrCreate_MFS:
- case AuWbrCreate_PMFS:
- seq_puts(m, pat);
- break;
- case AuWbrCreate_MFSV:
- seq_printf(m, /*pat*/"mfs:%lu",
- jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire)
- / MSEC_PER_SEC);
- break;
- case AuWbrCreate_PMFSV:
- seq_printf(m, /*pat*/"pmfs:%lu",
- jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire)
- / MSEC_PER_SEC);
- break;
- case AuWbrCreate_MFSRR:
- seq_printf(m, /*pat*/"mfsrr:%llu",
- sbinfo->si_wbr_mfs.mfsrr_watermark);
- break;
- case AuWbrCreate_MFSRRV:
- seq_printf(m, /*pat*/"mfsrr:%llu:%lu",
- sbinfo->si_wbr_mfs.mfsrr_watermark,
- jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire)
- / MSEC_PER_SEC);
- break;
- case AuWbrCreate_PMFSRR:
- seq_printf(m, /*pat*/"pmfsrr:%llu",
- sbinfo->si_wbr_mfs.mfsrr_watermark);
- break;
- case AuWbrCreate_PMFSRRV:
- seq_printf(m, /*pat*/"pmfsrr:%llu:%lu",
- sbinfo->si_wbr_mfs.mfsrr_watermark,
- jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire)
- / MSEC_PER_SEC);
- break;
- }
-}
-
-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_hdentry *hdp;
-
- 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;
- brid = au_xino_brid(sb);
- if (brid >= 0) {
- bindex = au_br_index(sb, brid);
- hdp = au_di(sb->s_root)->di_hdentry;
- h_root = hdp[0 + bindex].hd_dentry;
- }
- 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
-}
-
-/* seq_file will re-call me in case of too long string */
-static int aufs_show_options(struct seq_file *m, struct dentry *dentry)
-{
- int err;
- unsigned int mnt_flags, v;
- struct super_block *sb;
- struct au_sbinfo *sbinfo;
-
-#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)
-
-#define AuUInt(name, str, val) do { \
- if (val != AUFS_##name##_DEF) \
- seq_printf(m, "," #str "=%u", val); \
-} while (0)
-
- sb = dentry->d_sb;
- if (sb->s_flags & MS_POSIXACL)
- seq_puts(m, ",acl");
-
- /* lock free root dinfo */
- si_noflush_read_lock(sb);
- sbinfo = au_sbi(sb);
- seq_printf(m, ",si=%lx", sysaufs_si_id(sbinfo));
-
- mnt_flags = au_mntflags(sb);
- if (au_opt_test(mnt_flags, XINO)) {
- err = au_show_xino(m, sb);
- if (unlikely(err))
- goto out;
- } else
- seq_puts(m, ",noxino");
-
- AuBool(TRUNC_XINO, trunc_xino);
- AuStr(UDBA, udba);
- AuBool(SHWH, shwh);
- AuBool(PLINK, plink);
- AuBool(DIO, dio);
- AuBool(DIRPERM1, dirperm1);
-
- 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');
-
- AuUInt(DIRWH, dirwh, sbinfo->si_dirwh);
-
- v = jiffies_to_msecs(sbinfo->si_rdcache) / MSEC_PER_SEC;
- AuUInt(RDCACHE, rdcache, v);
-
- AuUInt(RDBLK, rdblk, sbinfo->si_rdblk);
- AuUInt(RDHASH, rdhash, sbinfo->si_rdhash);
-
- au_fhsm_show(m, sbinfo);
-
- AuBool(SUM, sum);
- /* AuBool(SUM_W, wsum); */
- AuBool(WARN_PERM, warn_perm);
- AuBool(VERBOSE, verbose);
-
-out:
- /* be sure to print "br:" last */
- if (!sysaufs_brs) {
- seq_puts(m, ",br:");
- au_show_brs(m, sb);
- }
- si_read_unlock(sb);
- return 0;
-
-#undef AuBool
-#undef AuStr
-#undef AuUInt
-}
-
-/* ---------------------------------------------------------------------- */
-
-/* sum mode which returns the summation for statfs(2) */
-
-static u64 au_add_till_max(u64 a, u64 b)
-{
- u64 old;
-
- old = a;
- a += b;
- if (old <= a)
- return a;
- return ULLONG_MAX;
-}
-
-static u64 au_mul_till_max(u64 a, long mul)
-{
- u64 old;
-
- old = a;
- a *= mul;
- if (old <= a)
- return a;
- return ULLONG_MAX;
-}
-
-static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf)
-{
- int err;
- long bsize, factor;
- u64 blocks, bfree, bavail, files, ffree;
- aufs_bindex_t bend, bindex, i;
- unsigned char shared;
- struct path h_path;
- struct super_block *h_sb;
-
- err = 0;
- bsize = LONG_MAX;
- files = 0;
- ffree = 0;
- blocks = 0;
- bfree = 0;
- bavail = 0;
- bend = au_sbend(sb);
- for (bindex = 0; bindex <= bend; bindex++) {
- h_path.mnt = au_sbr_mnt(sb, bindex);
- h_sb = h_path.mnt->mnt_sb;
- shared = 0;
- for (i = 0; !shared && i < bindex; i++)
- shared = (au_sbr_sb(sb, i) == h_sb);
- if (shared)
- continue;
-
- /* sb->s_root for NFS is unreliable */
- h_path.dentry = h_path.mnt->mnt_root;
- err = vfs_statfs(&h_path, buf);
- if (unlikely(err))
- goto out;
-
- if (bsize > buf->f_bsize) {
- /*
- * we will reduce bsize, so we have to expand blocks
- * etc. to match them again
- */
- factor = (bsize / buf->f_bsize);
- blocks = au_mul_till_max(blocks, factor);
- bfree = au_mul_till_max(bfree, factor);
- bavail = au_mul_till_max(bavail, factor);
- bsize = buf->f_bsize;
- }
-
- factor = (buf->f_bsize / bsize);
- blocks = au_add_till_max(blocks,
- au_mul_till_max(buf->f_blocks, factor));
- bfree = au_add_till_max(bfree,
- au_mul_till_max(buf->f_bfree, factor));
- bavail = au_add_till_max(bavail,
- au_mul_till_max(buf->f_bavail, factor));
- files = au_add_till_max(files, buf->f_files);
- ffree = au_add_till_max(ffree, buf->f_ffree);
- }
-
- buf->f_bsize = bsize;
- buf->f_blocks = blocks;
- buf->f_bfree = bfree;
- buf->f_bavail = bavail;
- buf->f_files = files;
- buf->f_ffree = ffree;
- buf->f_frsize = 0;
-
-out:
- return err;
-}
-
-static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf)
-{
- int err;
- struct path h_path;
- struct super_block *sb;
-
- /* lock free root dinfo */
- sb = dentry->d_sb;
- si_noflush_read_lock(sb);
- if (!au_opt_test(au_mntflags(sb), SUM)) {
- /* sb->s_root for NFS is unreliable */
- h_path.mnt = au_sbr_mnt(sb, 0);
- h_path.dentry = h_path.mnt->mnt_root;
- err = vfs_statfs(&h_path, buf);
- } else
- err = au_statfs_sum(sb, buf);
- si_read_unlock(sb);
-
- if (!err) {
- buf->f_type = AUFS_SUPER_MAGIC;
- buf->f_namelen = AUFS_MAX_NAMELEN;
- memset(&buf->f_fsid, 0, sizeof(buf->f_fsid));
- }
- /* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */
-
- return err;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static int aufs_sync_fs(struct super_block *sb, int wait)
-{
- int err, e;
- aufs_bindex_t bend, bindex;
- struct au_branch *br;
- struct super_block *h_sb;
-
- err = 0;
- si_noflush_read_lock(sb);
- bend = au_sbend(sb);
- for (bindex = 0; bindex <= bend; bindex++) {
- br = au_sbr(sb, bindex);
- if (!au_br_writable(br->br_perm))
- continue;
-
- h_sb = au_sbr_sb(sb, bindex);
- if (h_sb->s_op->sync_fs) {
- e = h_sb->s_op->sync_fs(h_sb, wait);
- if (unlikely(e && !err))
- err = e;
- /* go on even if an error happens */
- }
- }
- si_read_unlock(sb);
-
- return err;
-}
-
-/* ---------------------------------------------------------------------- */
-
-/* final actions when unmounting a file system */
-static void aufs_put_super(struct super_block *sb)
-{
- struct au_sbinfo *sbinfo;
-
- sbinfo = au_sbi(sb);
- if (!sbinfo)
- return;
-
- dbgaufs_si_fin(sbinfo);
- kobject_put(&sbinfo->si_kobj);
-}
-
-/* ---------------------------------------------------------------------- */
-
-void au_array_free(void *array)
-{
- if (array) {
- if (!is_vmalloc_addr(array))
- kfree(array);
- else
- vfree(array);
- }
-}
-
-void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg)
-{
- void *array;
- unsigned long long n, sz;
-
- array = NULL;
- n = 0;
- if (!*hint)
- goto out;
-
- if (*hint > ULLONG_MAX / sizeof(array)) {
- array = ERR_PTR(-EMFILE);
- pr_err("hint %llu\n", *hint);
- goto out;
- }
-
- sz = sizeof(array) * *hint;
- array = kzalloc(sz, GFP_NOFS);
- if (unlikely(!array))
- array = vzalloc(sz);
- if (unlikely(!array)) {
- array = ERR_PTR(-ENOMEM);
- goto out;
- }
-
- n = cb(array, *hint, arg);
- AuDebugOn(n > *hint);
-
-out:
- *hint = n;
- return array;
-}
-
-static unsigned long long au_iarray_cb(void *a,
- unsigned long long max __maybe_unused,
- void *arg)
-{
- unsigned long long n;
- struct inode **p, *inode;
- struct list_head *head;
-
- n = 0;
- p = a;
- head = arg;
- spin_lock(&inode_sb_list_lock);
- list_for_each_entry(inode, head, i_sb_list) {
- if (!is_bad_inode(inode)
- && au_ii(inode)->ii_bstart >= 0) {
- spin_lock(&inode->i_lock);
- if (atomic_read(&inode->i_count)) {
- au_igrab(inode);
- *p++ = inode;
- n++;
- AuDebugOn(n > max);
- }
- spin_unlock(&inode->i_lock);
- }
- }
- spin_unlock(&inode_sb_list_lock);
-
- return n;
-}
-
-struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max)
-{
- *max = atomic_long_read(&au_sbi(sb)->si_ninodes);
- return au_array_alloc(max, au_iarray_cb, &sb->s_inodes);
-}
-
-void au_iarray_free(struct inode **a, unsigned long long max)
-{
- unsigned long long ull;
-
- for (ull = 0; ull < max; ull++)
- iput(a[ull]);
- au_array_free(a);
-}
-
-/* ---------------------------------------------------------------------- */
-
-/*
- * refresh dentry and inode at remount time.
- */
-/* todo: consolidate with simple_reval_dpath() and au_reval_for_attr() */
-static int au_do_refresh(struct dentry *dentry, unsigned int dir_flags,
- struct dentry *parent)
-{
- int err;
-
- di_write_lock_child(dentry);
- di_read_lock_parent(parent, AuLock_IR);
- err = au_refresh_dentry(dentry, parent);
- if (!err && dir_flags)
- au_hn_reset(d_inode(dentry), dir_flags);
- di_read_unlock(parent, AuLock_IR);
- di_write_unlock(dentry);
-
- return err;
-}
-
-static int au_do_refresh_d(struct dentry *dentry, unsigned int sigen,
- struct au_sbinfo *sbinfo,
- const unsigned int dir_flags)
-{
- int err;
- struct dentry *parent;
-
- err = 0;
- parent = dget_parent(dentry);
- if (!au_digen_test(parent, sigen) && au_digen_test(dentry, sigen)) {
- if (d_really_is_positive(dentry)) {
- if (!d_is_dir(dentry))
- err = au_do_refresh(dentry, /*dir_flags*/0,
- parent);
- else {
- err = au_do_refresh(dentry, dir_flags, parent);
- if (unlikely(err))
- au_fset_si(sbinfo, FAILED_REFRESH_DIR);
- }
- } else
- err = au_do_refresh(dentry, /*dir_flags*/0, parent);
- AuDbgDentry(dentry);
- }
- dput(parent);
-
- AuTraceErr(err);
- return err;
-}
-
-static int au_refresh_d(struct super_block *sb)
-{
- int err, i, j, ndentry, e;
- unsigned int sigen;
- struct au_dcsub_pages dpages;
- struct au_dpage *dpage;
- struct dentry **dentries, *d;
- struct au_sbinfo *sbinfo;
- struct dentry *root = sb->s_root;
- const unsigned int dir_flags = au_hi_flags(d_inode(root), /*isdir*/1);
-
- err = au_dpages_init(&dpages, GFP_NOFS);
- if (unlikely(err))
- goto out;
- err = au_dcsub_pages(&dpages, root, NULL, NULL);
- if (unlikely(err))
- goto out_dpages;
-
- sigen = au_sigen(sb);
- sbinfo = au_sbi(sb);
- for (i = 0; i < dpages.ndpage; i++) {
- dpage = dpages.dpages + i;
- dentries = dpage->dentries;
- ndentry = dpage->ndentry;
- for (j = 0; j < ndentry; j++) {
- d = dentries[j];
- e = au_do_refresh_d(d, sigen, sbinfo, dir_flags);
- if (unlikely(e && !err))
- err = e;
- /* go on even err */
- }
- }
-
-out_dpages:
- au_dpages_free(&dpages);
-out:
- return err;
-}
-
-static int au_refresh_i(struct super_block *sb)
-{
- int err, e;
- unsigned int sigen;
- unsigned long long max, ull;
- struct inode *inode, **array;
-
- array = au_iarray_alloc(sb, &max);
- err = PTR_ERR(array);
- if (IS_ERR(array))
- goto out;
-
- err = 0;
- sigen = au_sigen(sb);
- for (ull = 0; ull < max; ull++) {
- inode = array[ull];
- if (unlikely(!inode))
- break;
- if (au_iigen(inode, NULL) != sigen) {
- ii_write_lock_child(inode);
- e = au_refresh_hinode_self(inode);
- ii_write_unlock(inode);
- if (unlikely(e)) {
- pr_err("error %d, i%lu\n", e, inode->i_ino);
- if (!err)
- err = e;
- /* go on even if err */
- }
- }
- }
-
- au_iarray_free(array, max);
-
-out:
- return err;
-}
-
-static void au_remount_refresh(struct super_block *sb)
-{
- int err, e;
- unsigned int udba;
- aufs_bindex_t bindex, bend;
- struct dentry *root;
- struct inode *inode;
- struct au_branch *br;
-
- au_sigen_inc(sb);
- au_fclr_si(au_sbi(sb), FAILED_REFRESH_DIR);
-
- root = sb->s_root;
- DiMustNoWaiters(root);
- inode = d_inode(root);
- IiMustNoWaiters(inode);
-
- udba = au_opt_udba(sb);
- bend = au_sbend(sb);
- for (bindex = 0; bindex <= bend; bindex++) {
- br = au_sbr(sb, bindex);
- err = au_hnotify_reset_br(udba, br, br->br_perm);
- if (unlikely(err))
- AuIOErr("hnotify failed on br %d, %d, ignored\n",
- bindex, err);
- /* go on even if err */
- }
- au_hn_reset(inode, au_hi_flags(inode, /*isdir*/1));
-
- di_write_unlock(root);
- err = au_refresh_d(sb);
- e = au_refresh_i(sb);
- if (unlikely(e && !err))
- err = e;
- /* aufs_write_lock() calls ..._child() */
- di_write_lock_child(root);
-
- au_cpup_attr_all(inode, /*force*/1);
-
- if (unlikely(err))
- AuIOErr("refresh failed, ignored, %d\n", 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;
-}
-
-static int aufs_remount_fs(struct super_block *sb, int *flags, char *data)
-{
- int err, do_dx;
- unsigned int mntflags;
- struct au_opts opts;
- struct dentry *root;
- struct inode *inode;
- struct au_sbinfo *sbinfo;
-
- err = 0;
- root = sb->s_root;
- if (!data || !*data) {
- err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
- if (!err) {
- di_write_lock_child(root);
- err = au_opts_verify(sb, *flags, /*pending*/0);
- aufs_write_unlock(root);
- }
- goto out;
- }
-
- err = -ENOMEM;
- memset(&opts, 0, sizeof(opts));
- opts.opt = (void *)__get_free_page(GFP_NOFS);
- if (unlikely(!opts.opt))
- goto out;
- opts.max_opt = PAGE_SIZE / sizeof(*opts.opt);
- opts.flags = AuOpts_REMOUNT;
- opts.sb_flags = *flags;
-
- /* parse it before aufs lock */
- err = au_opts_parse(sb, data, &opts);
- if (unlikely(err))
- goto out_opts;
-
- sbinfo = au_sbi(sb);
- inode = d_inode(root);
- mutex_lock(&inode->i_mutex);
- err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM);
- if (unlikely(err))
- goto out_mtx;
- di_write_lock_child(root);
-
- /* au_opts_remount() may return an error */
- err = au_opts_remount(sb, &opts);
- au_opts_free(&opts);
-
- if (au_ftest_opts(opts.flags, REFRESH))
- au_remount_refresh(sb);
-
- if (au_ftest_opts(opts.flags, REFRESH_DYAOP)) {
- mntflags = au_mntflags(sb);
- do_dx = !!au_opt_test(mntflags, DIO);
- au_dy_arefresh(do_dx);
- }
-
- au_fhsm_wrote_all(sb, /*force*/1); /* ?? */
- aufs_write_unlock(root);
-
-out_mtx:
- mutex_unlock(&inode->i_mutex);
-out_opts:
- free_page((unsigned long)opts.opt);
-out:
- err = cvt_err(err);
- AuTraceErr(err);
- return err;
-}
-
-static const struct super_operations aufs_sop = {
- .alloc_inode = aufs_alloc_inode,
- .destroy_inode = aufs_destroy_inode,
- /* always deleting, no clearing */
- .drop_inode = generic_delete_inode,
- .show_options = aufs_show_options,
- .statfs = aufs_statfs,
- .put_super = aufs_put_super,
- .sync_fs = aufs_sync_fs,
- .remount_fs = aufs_remount_fs
-};
-
-/* ---------------------------------------------------------------------- */
-
-static int alloc_root(struct super_block *sb)
-{
- int err;
- struct inode *inode;
- struct dentry *root;
-
- err = -ENOMEM;
- inode = au_iget_locked(sb, AUFS_ROOT_INO);
- err = PTR_ERR(inode);
- if (IS_ERR(inode))
- goto out;
-
- inode->i_op = &aufs_dir_iop;
- inode->i_fop = &aufs_dir_fop;
- inode->i_mode = S_IFDIR;
- set_nlink(inode, 2);
- unlock_new_inode(inode);
-
- root = d_make_root(inode);
- if (unlikely(!root))
- goto out;
- err = PTR_ERR(root);
- if (IS_ERR(root))
- goto out;
-
- err = au_di_init(root);
- if (!err) {
- sb->s_root = root;
- return 0; /* success */
- }
- dput(root);
-
-out:
- return err;
-}
-
-static int aufs_fill_super(struct super_block *sb, void *raw_data,
- int silent __maybe_unused)
-{
- int err;
- struct au_opts opts;
- struct dentry *root;
- struct inode *inode;
- char *arg = raw_data;
-
- if (unlikely(!arg || !*arg)) {
- err = -EINVAL;
- pr_err("no arg\n");
- goto out;
- }
-
- err = -ENOMEM;
- memset(&opts, 0, sizeof(opts));
- opts.opt = (void *)__get_free_page(GFP_NOFS);
- if (unlikely(!opts.opt))
- goto out;
- opts.max_opt = PAGE_SIZE / sizeof(*opts.opt);
- opts.sb_flags = sb->s_flags;
-
- err = au_si_alloc(sb);
- if (unlikely(err))
- goto out_opts;
-
- /* all timestamps always follow the ones on the branch */
- sb->s_flags |= MS_NOATIME | MS_NODIRATIME;
- sb->s_op = &aufs_sop;
- sb->s_d_op = &aufs_dop;
- sb->s_magic = AUFS_SUPER_MAGIC;
- sb->s_maxbytes = 0;
- sb->s_stack_depth = 1;
- au_export_init(sb);
- /* au_xattr_init(sb); */
-
- err = alloc_root(sb);
- if (unlikely(err)) {
- si_write_unlock(sb);
- goto out_info;
- }
- root = sb->s_root;
- inode = d_inode(root);
-
- /*
- * 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, arg, &opts);
- if (unlikely(err))
- goto out_root;
-
- /* lock vfs_inode first, then aufs. */
- mutex_lock(&inode->i_mutex);
- aufs_write_lock(root);
- err = au_opts_mount(sb, &opts);
- au_opts_free(&opts);
- aufs_write_unlock(root);
- mutex_unlock(&inode->i_mutex);
- if (!err)
- goto out_opts; /* success */
-
-out_root:
- dput(root);
- sb->s_root = NULL;
-out_info:
- dbgaufs_si_fin(au_sbi(sb));
- 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);
- return err;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static struct dentry *aufs_mount(struct file_system_type *fs_type, int flags,
- const char *dev_name __maybe_unused,
- void *raw_data)
-{
- struct dentry *root;
- struct super_block *sb;
-
- /* all timestamps always follow the ones on the branch */
- /* mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME; */
- root = mount_nodev(fs_type, flags, raw_data, aufs_fill_super);
- if (IS_ERR(root))
- goto out;
-
- sb = root->d_sb;
- si_write_lock(sb, !AuLock_FLUSH);
- sysaufs_brs_add(sb, 0);
- si_write_unlock(sb);
- au_sbilist_add(sb);
-
-out:
- return root;
-}
-
-static void aufs_kill_sb(struct super_block *sb)
-{
- struct au_sbinfo *sbinfo;
-
- sbinfo = au_sbi(sb);
- if (sbinfo) {
- au_sbilist_del(sb);
- aufs_write_lock(sb->s_root);
- au_fhsm_fin(sb);
- if (sbinfo->si_wbr_create_ops->fin)
- sbinfo->si_wbr_create_ops->fin(sb);
- if (au_opt_test(sbinfo->si_mntflags, UDBA_HNOTIFY)) {
- au_opt_set_udba(sbinfo->si_mntflags, UDBA_NONE);
- au_remount_refresh(sb);
- }
- if (au_opt_test(sbinfo->si_mntflags, PLINK))
- au_plink_put(sb, /*verbose*/1);
- au_xino_clr(sb);
- sbinfo->si_sb = NULL;
- aufs_write_unlock(sb->s_root);
- au_nwt_flush(&sbinfo->si_nowait);
- }
- kill_anon_super(sb);
-}
-
-struct file_system_type aufs_fs_type = {
- .name = AUFS_FSTYPE,
- /* a race between rename and others */
- .fs_flags = FS_RENAME_DOES_D_MOVE,
- .mount = aufs_mount,
- .kill_sb = aufs_kill_sb,
- /* no need to __module_get() and module_put(). */
- .owner = THIS_MODULE,
-};