diff options
Diffstat (limited to 'fs/aufs/export.c')
-rw-r--r-- | fs/aufs/export.c | 819 |
1 files changed, 0 insertions, 819 deletions
diff --git a/fs/aufs/export.c b/fs/aufs/export.c deleted file mode 100644 index 7f6fec61f..000000000 --- a/fs/aufs/export.c +++ /dev/null @@ -1,819 +0,0 @@ -/* - * Copyright (C) 2005-2016 Junjiro R. Okajima - */ - -/* - * export via nfs - */ - -#include <linux/exportfs.h> -#include <linux/fs_struct.h> -#include <linux/namei.h> -#include <linux/nsproxy.h> -#include <linux/random.h> -#include <linux/writeback.h> -#include "../fs/mount.h" -#include "aufs.h" - -union conv { -#ifdef CONFIG_AUFS_INO_T_64 - __u32 a[2]; -#else - __u32 a[1]; -#endif - ino_t ino; -}; - -static ino_t decode_ino(__u32 *a) -{ - union conv u; - - BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a)); - u.a[0] = a[0]; -#ifdef CONFIG_AUFS_INO_T_64 - u.a[1] = a[1]; -#endif - return u.ino; -} - -static void encode_ino(__u32 *a, ino_t ino) -{ - union conv u; - - u.ino = ino; - a[0] = u.a[0]; -#ifdef CONFIG_AUFS_INO_T_64 - a[1] = u.a[1]; -#endif -} - -/* NFS file handle */ -enum { - Fh_br_id, - Fh_sigen, -#ifdef CONFIG_AUFS_INO_T_64 - /* support 64bit inode number */ - Fh_ino1, - Fh_ino2, - Fh_dir_ino1, - Fh_dir_ino2, -#else - Fh_ino1, - Fh_dir_ino1, -#endif - Fh_igen, - Fh_h_type, - Fh_tail, - - Fh_ino = Fh_ino1, - Fh_dir_ino = Fh_dir_ino1 -}; - -static int au_test_anon(struct dentry *dentry) -{ - /* note: read d_flags without d_lock */ - return !!(dentry->d_flags & DCACHE_DISCONNECTED); -} - -int au_test_nfsd(void) -{ - int ret; - struct task_struct *tsk = current; - char comm[sizeof(tsk->comm)]; - - ret = 0; - if (tsk->flags & PF_KTHREAD) { - get_task_comm(comm, tsk); - ret = !strcmp(comm, "nfsd"); - } - - return ret; -} - -/* ---------------------------------------------------------------------- */ -/* inode generation external table */ - -void au_xigen_inc(struct inode *inode) -{ - loff_t pos; - ssize_t sz; - __u32 igen; - struct super_block *sb; - struct au_sbinfo *sbinfo; - - sb = inode->i_sb; - AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); - - sbinfo = au_sbi(sb); - pos = inode->i_ino; - pos *= sizeof(igen); - igen = inode->i_generation + 1; - sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen, - sizeof(igen), &pos); - if (sz == sizeof(igen)) - return; /* success */ - - if (unlikely(sz >= 0)) - AuIOErr("xigen error (%zd)\n", sz); -} - -int au_xigen_new(struct inode *inode) -{ - int err; - loff_t pos; - ssize_t sz; - struct super_block *sb; - struct au_sbinfo *sbinfo; - struct file *file; - - err = 0; - /* todo: dirty, at mount time */ - if (inode->i_ino == AUFS_ROOT_INO) - goto out; - sb = inode->i_sb; - SiMustAnyLock(sb); - if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) - goto out; - - err = -EFBIG; - pos = inode->i_ino; - if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) { - AuIOErr1("too large i%lld\n", pos); - goto out; - } - pos *= sizeof(inode->i_generation); - - err = 0; - sbinfo = au_sbi(sb); - file = sbinfo->si_xigen; - BUG_ON(!file); - - if (vfsub_f_size_read(file) - < pos + sizeof(inode->i_generation)) { - inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next); - sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation, - sizeof(inode->i_generation), &pos); - } else - sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation, - sizeof(inode->i_generation), &pos); - if (sz == sizeof(inode->i_generation)) - goto out; /* success */ - - err = sz; - if (unlikely(sz >= 0)) { - err = -EIO; - AuIOErr("xigen error (%zd)\n", sz); - } - -out: - return err; -} - -int au_xigen_set(struct super_block *sb, struct file *base) -{ - int err; - struct au_sbinfo *sbinfo; - struct file *file; - - SiMustWriteLock(sb); - - sbinfo = au_sbi(sb); - file = au_xino_create2(base, sbinfo->si_xigen); - err = PTR_ERR(file); - if (IS_ERR(file)) - goto out; - err = 0; - if (sbinfo->si_xigen) - fput(sbinfo->si_xigen); - sbinfo->si_xigen = file; - -out: - return err; -} - -void au_xigen_clr(struct super_block *sb) -{ - struct au_sbinfo *sbinfo; - - SiMustWriteLock(sb); - - sbinfo = au_sbi(sb); - if (sbinfo->si_xigen) { - fput(sbinfo->si_xigen); - sbinfo->si_xigen = NULL; - } -} - -/* ---------------------------------------------------------------------- */ - -static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, - ino_t dir_ino) -{ - struct dentry *dentry, *d; - struct inode *inode; - unsigned int sigen; - - dentry = NULL; - inode = ilookup(sb, ino); - if (!inode) - goto out; - - dentry = ERR_PTR(-ESTALE); - sigen = au_sigen(sb); - if (unlikely(is_bad_inode(inode) - || IS_DEADDIR(inode) - || sigen != au_iigen(inode, NULL))) - goto out_iput; - - dentry = NULL; - if (!dir_ino || S_ISDIR(inode->i_mode)) - dentry = d_find_alias(inode); - else { - spin_lock(&inode->i_lock); - hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { - spin_lock(&d->d_lock); - if (!au_test_anon(d) - && d_inode(d->d_parent)->i_ino == dir_ino) { - dentry = dget_dlock(d); - spin_unlock(&d->d_lock); - break; - } - spin_unlock(&d->d_lock); - } - spin_unlock(&inode->i_lock); - } - if (unlikely(dentry && au_digen_test(dentry, sigen))) { - /* need to refresh */ - dput(dentry); - dentry = NULL; - } - -out_iput: - iput(inode); -out: - AuTraceErrPtr(dentry); - return dentry; -} - -/* ---------------------------------------------------------------------- */ - -/* todo: dirty? */ -/* if exportfs_decode_fh() passed vfsmount*, we could be happy */ - -struct au_compare_mnt_args { - /* input */ - struct super_block *sb; - - /* output */ - struct vfsmount *mnt; -}; - -static int au_compare_mnt(struct vfsmount *mnt, void *arg) -{ - struct au_compare_mnt_args *a = arg; - - if (mnt->mnt_sb != a->sb) - return 0; - a->mnt = mntget(mnt); - return 1; -} - -static struct vfsmount *au_mnt_get(struct super_block *sb) -{ - int err; - struct path root; - struct au_compare_mnt_args args = { - .sb = sb - }; - - get_fs_root(current->fs, &root); - rcu_read_lock(); - err = iterate_mounts(au_compare_mnt, &args, root.mnt); - rcu_read_unlock(); - path_put(&root); - AuDebugOn(!err); - AuDebugOn(!args.mnt); - return args.mnt; -} - -struct au_nfsd_si_lock { - unsigned int sigen; - aufs_bindex_t bindex, br_id; - unsigned char force_lock; -}; - -static int si_nfsd_read_lock(struct super_block *sb, - struct au_nfsd_si_lock *nsi_lock) -{ - int err; - aufs_bindex_t bindex; - - si_read_lock(sb, AuLock_FLUSH); - - /* branch id may be wrapped around */ - err = 0; - bindex = au_br_index(sb, nsi_lock->br_id); - if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb)) - goto out; /* success */ - - err = -ESTALE; - bindex = -1; - if (!nsi_lock->force_lock) - si_read_unlock(sb); - -out: - nsi_lock->bindex = bindex; - return err; -} - -struct find_name_by_ino { - struct dir_context ctx; - int called, found; - ino_t ino; - char *name; - int namelen; -}; - -static int -find_name_by_ino(struct dir_context *ctx, const char *name, int namelen, - loff_t offset, u64 ino, unsigned int d_type) -{ - struct find_name_by_ino *a = container_of(ctx, struct find_name_by_ino, - ctx); - - a->called++; - if (a->ino != ino) - return 0; - - memcpy(a->name, name, namelen); - a->namelen = namelen; - a->found = 1; - return 1; -} - -static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino, - struct au_nfsd_si_lock *nsi_lock) -{ - struct dentry *dentry, *parent; - struct file *file; - struct inode *dir; - struct find_name_by_ino arg = { - .ctx = { - .actor = find_name_by_ino - } - }; - int err; - - parent = path->dentry; - if (nsi_lock) - si_read_unlock(parent->d_sb); - file = vfsub_dentry_open(path, au_dir_roflags); - dentry = (void *)file; - if (IS_ERR(file)) - goto out; - - dentry = ERR_PTR(-ENOMEM); - arg.name = (void *)__get_free_page(GFP_NOFS); - if (unlikely(!arg.name)) - goto out_file; - arg.ino = ino; - arg.found = 0; - do { - arg.called = 0; - /* smp_mb(); */ - err = vfsub_iterate_dir(file, &arg.ctx); - } while (!err && !arg.found && arg.called); - dentry = ERR_PTR(err); - if (unlikely(err)) - goto out_name; - /* instead of ENOENT */ - dentry = ERR_PTR(-ESTALE); - if (!arg.found) - goto out_name; - - /* do not call vfsub_lkup_one() */ - dir = d_inode(parent); - mutex_lock(&dir->i_mutex); - dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen); - mutex_unlock(&dir->i_mutex); - AuTraceErrPtr(dentry); - if (IS_ERR(dentry)) - goto out_name; - AuDebugOn(au_test_anon(dentry)); - if (unlikely(d_really_is_negative(dentry))) { - dput(dentry); - dentry = ERR_PTR(-ENOENT); - } - -out_name: - free_page((unsigned long)arg.name); -out_file: - fput(file); -out: - if (unlikely(nsi_lock - && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0)) - if (!IS_ERR(dentry)) { - dput(dentry); - dentry = ERR_PTR(-ESTALE); - } - AuTraceErrPtr(dentry); - return dentry; -} - -static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino, - ino_t dir_ino, - struct au_nfsd_si_lock *nsi_lock) -{ - struct dentry *dentry; - struct path path; - - if (dir_ino != AUFS_ROOT_INO) { - path.dentry = decode_by_ino(sb, dir_ino, 0); - dentry = path.dentry; - if (!path.dentry || IS_ERR(path.dentry)) - goto out; - AuDebugOn(au_test_anon(path.dentry)); - } else - path.dentry = dget(sb->s_root); - - path.mnt = au_mnt_get(sb); - dentry = au_lkup_by_ino(&path, ino, nsi_lock); - path_put(&path); - -out: - AuTraceErrPtr(dentry); - return dentry; -} - -/* ---------------------------------------------------------------------- */ - -static int h_acceptable(void *expv, struct dentry *dentry) -{ - return 1; -} - -static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath, - char *buf, int len, struct super_block *sb) -{ - char *p; - int n; - struct path path; - - p = d_path(h_rootpath, buf, len); - if (IS_ERR(p)) - goto out; - n = strlen(p); - - path.mnt = h_rootpath->mnt; - path.dentry = h_parent; - p = d_path(&path, buf, len); - if (IS_ERR(p)) - goto out; - if (n != 1) - p += n; - - path.mnt = au_mnt_get(sb); - path.dentry = sb->s_root; - p = d_path(&path, buf, len - strlen(p)); - mntput(path.mnt); - if (IS_ERR(p)) - goto out; - if (n != 1) - p[strlen(p)] = '/'; - -out: - AuTraceErrPtr(p); - return p; -} - -static -struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh, - int fh_len, struct au_nfsd_si_lock *nsi_lock) -{ - struct dentry *dentry, *h_parent, *root; - struct super_block *h_sb; - char *pathname, *p; - struct vfsmount *h_mnt; - struct au_branch *br; - int err; - struct path path; - - br = au_sbr(sb, nsi_lock->bindex); - h_mnt = au_br_mnt(br); - h_sb = h_mnt->mnt_sb; - /* todo: call lower fh_to_dentry()? fh_to_parent()? */ - h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), - fh_len - Fh_tail, fh[Fh_h_type], - h_acceptable, /*context*/NULL); - dentry = h_parent; - if (unlikely(!h_parent || IS_ERR(h_parent))) { - AuWarn1("%s decode_fh failed, %ld\n", - au_sbtype(h_sb), PTR_ERR(h_parent)); - goto out; - } - dentry = NULL; - if (unlikely(au_test_anon(h_parent))) { - AuWarn1("%s decode_fh returned a disconnected dentry\n", - au_sbtype(h_sb)); - goto out_h_parent; - } - - dentry = ERR_PTR(-ENOMEM); - pathname = (void *)__get_free_page(GFP_NOFS); - if (unlikely(!pathname)) - goto out_h_parent; - - root = sb->s_root; - path.mnt = h_mnt; - di_read_lock_parent(root, !AuLock_IR); - path.dentry = au_h_dptr(root, nsi_lock->bindex); - di_read_unlock(root, !AuLock_IR); - p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); - dentry = (void *)p; - if (IS_ERR(p)) - goto out_pathname; - - si_read_unlock(sb); - err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); - dentry = ERR_PTR(err); - if (unlikely(err)) - goto out_relock; - - dentry = ERR_PTR(-ENOENT); - AuDebugOn(au_test_anon(path.dentry)); - if (unlikely(d_really_is_negative(path.dentry))) - goto out_path; - - if (ino != d_inode(path.dentry)->i_ino) - dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL); - else - dentry = dget(path.dentry); - -out_path: - path_put(&path); -out_relock: - if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0)) - if (!IS_ERR(dentry)) { - dput(dentry); - dentry = ERR_PTR(-ESTALE); - } -out_pathname: - free_page((unsigned long)pathname); -out_h_parent: - dput(h_parent); -out: - AuTraceErrPtr(dentry); - return dentry; -} - -/* ---------------------------------------------------------------------- */ - -static struct dentry * -aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, - int fh_type) -{ - struct dentry *dentry; - __u32 *fh = fid->raw; - struct au_branch *br; - ino_t ino, dir_ino; - struct au_nfsd_si_lock nsi_lock = { - .force_lock = 0 - }; - - dentry = ERR_PTR(-ESTALE); - /* it should never happen, but the file handle is unreliable */ - if (unlikely(fh_len < Fh_tail)) - goto out; - nsi_lock.sigen = fh[Fh_sigen]; - nsi_lock.br_id = fh[Fh_br_id]; - - /* branch id may be wrapped around */ - br = NULL; - if (unlikely(si_nfsd_read_lock(sb, &nsi_lock))) - goto out; - nsi_lock.force_lock = 1; - - /* is this inode still cached? */ - ino = decode_ino(fh + Fh_ino); - /* it should never happen */ - if (unlikely(ino == AUFS_ROOT_INO)) - goto out_unlock; - - dir_ino = decode_ino(fh + Fh_dir_ino); - dentry = decode_by_ino(sb, ino, dir_ino); - if (IS_ERR(dentry)) - goto out_unlock; - if (dentry) - goto accept; - - /* is the parent dir cached? */ - br = au_sbr(sb, nsi_lock.bindex); - atomic_inc(&br->br_count); - dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); - if (IS_ERR(dentry)) - goto out_unlock; - if (dentry) - goto accept; - - /* lookup path */ - dentry = decode_by_path(sb, ino, fh, fh_len, &nsi_lock); - if (IS_ERR(dentry)) - goto out_unlock; - if (unlikely(!dentry)) - /* todo?: make it ESTALE */ - goto out_unlock; - -accept: - if (!au_digen_test(dentry, au_sigen(sb)) - && d_inode(dentry)->i_generation == fh[Fh_igen]) - goto out_unlock; /* success */ - - dput(dentry); - dentry = ERR_PTR(-ESTALE); -out_unlock: - if (br) - atomic_dec(&br->br_count); - si_read_unlock(sb); -out: - AuTraceErrPtr(dentry); - return dentry; -} - -#if 0 /* reserved for future use */ -/* support subtreecheck option */ -static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid, - int fh_len, int fh_type) -{ - struct dentry *parent; - __u32 *fh = fid->raw; - ino_t dir_ino; - - dir_ino = decode_ino(fh + Fh_dir_ino); - parent = decode_by_ino(sb, dir_ino, 0); - if (IS_ERR(parent)) - goto out; - if (!parent) - parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]), - dir_ino, fh, fh_len); - -out: - AuTraceErrPtr(parent); - return parent; -} -#endif - -/* ---------------------------------------------------------------------- */ - -static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len, - struct inode *dir) -{ - int err; - aufs_bindex_t bindex; - struct super_block *sb, *h_sb; - struct dentry *dentry, *parent, *h_parent; - struct inode *h_dir; - struct au_branch *br; - - err = -ENOSPC; - if (unlikely(*max_len <= Fh_tail)) { - AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); - goto out; - } - - err = FILEID_ROOT; - if (inode->i_ino == AUFS_ROOT_INO) { - AuDebugOn(inode->i_ino != AUFS_ROOT_INO); - goto out; - } - - h_parent = NULL; - sb = inode->i_sb; - err = si_read_lock(sb, AuLock_FLUSH); - if (unlikely(err)) - goto out; - -#ifdef CONFIG_AUFS_DEBUG - if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) - AuWarn1("NFS-exporting requires xino\n"); -#endif - err = -EIO; - parent = NULL; - ii_read_lock_child(inode); - bindex = au_ibstart(inode); - if (!dir) { - dentry = d_find_any_alias(inode); - if (unlikely(!dentry)) - goto out_unlock; - AuDebugOn(au_test_anon(dentry)); - parent = dget_parent(dentry); - dput(dentry); - if (unlikely(!parent)) - goto out_unlock; - if (d_really_is_positive(parent)) - dir = d_inode(parent); - } - - ii_read_lock_parent(dir); - h_dir = au_h_iptr(dir, bindex); - ii_read_unlock(dir); - if (unlikely(!h_dir)) - goto out_parent; - h_parent = d_find_any_alias(h_dir); - if (unlikely(!h_parent)) - goto out_hparent; - - err = -EPERM; - br = au_sbr(sb, bindex); - h_sb = au_br_sb(br); - if (unlikely(!h_sb->s_export_op)) { - AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); - goto out_hparent; - } - - fh[Fh_br_id] = br->br_id; - fh[Fh_sigen] = au_sigen(sb); - encode_ino(fh + Fh_ino, inode->i_ino); - encode_ino(fh + Fh_dir_ino, dir->i_ino); - fh[Fh_igen] = inode->i_generation; - - *max_len -= Fh_tail; - fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail), - max_len, - /*connectable or subtreecheck*/0); - err = fh[Fh_h_type]; - *max_len += Fh_tail; - /* todo: macros? */ - if (err != FILEID_INVALID) - err = 99; - else - AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); - -out_hparent: - dput(h_parent); -out_parent: - dput(parent); -out_unlock: - ii_read_unlock(inode); - si_read_unlock(sb); -out: - if (unlikely(err < 0)) - err = FILEID_INVALID; - return err; -} - -/* ---------------------------------------------------------------------- */ - -static int aufs_commit_metadata(struct inode *inode) -{ - int err; - aufs_bindex_t bindex; - struct super_block *sb; - struct inode *h_inode; - int (*f)(struct inode *inode); - - sb = inode->i_sb; - si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); - ii_write_lock_child(inode); - bindex = au_ibstart(inode); - AuDebugOn(bindex < 0); - h_inode = au_h_iptr(inode, bindex); - - f = h_inode->i_sb->s_export_op->commit_metadata; - if (f) - err = f(h_inode); - else { - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = 0 /* metadata only */ - }; - - err = sync_inode(h_inode, &wbc); - } - - au_cpup_attr_timesizes(inode); - ii_write_unlock(inode); - si_read_unlock(sb); - return err; -} - -/* ---------------------------------------------------------------------- */ - -static struct export_operations aufs_export_op = { - .fh_to_dentry = aufs_fh_to_dentry, - /* .fh_to_parent = aufs_fh_to_parent, */ - .encode_fh = aufs_encode_fh, - .commit_metadata = aufs_commit_metadata -}; - -void au_export_init(struct super_block *sb) -{ - struct au_sbinfo *sbinfo; - __u32 u; - - sb->s_export_op = &aufs_export_op; - sbinfo = au_sbi(sb); - sbinfo->si_xigen = NULL; - get_random_bytes(&u, sizeof(u)); - BUILD_BUG_ON(sizeof(u) != sizeof(int)); - atomic_set(&sbinfo->si_xigen_next, u); -} |