diff options
Diffstat (limited to 'fs/aufs/dir.c')
-rw-r--r-- | fs/aufs/dir.c | 745 |
1 files changed, 0 insertions, 745 deletions
diff --git a/fs/aufs/dir.c b/fs/aufs/dir.c deleted file mode 100644 index a994e0862..000000000 --- a/fs/aufs/dir.c +++ /dev/null @@ -1,745 +0,0 @@ -/* - * Copyright (C) 2005-2016 Junjiro R. Okajima - */ - -/* - * directory operations - */ - -#include <linux/fs_stack.h> -#include "aufs.h" - -void au_add_nlink(struct inode *dir, struct inode *h_dir) -{ - unsigned int nlink; - - AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); - - nlink = dir->i_nlink; - nlink += h_dir->i_nlink - 2; - if (h_dir->i_nlink < 2) - nlink += 2; - smp_mb(); /* for i_nlink */ - /* 0 can happen in revaliding */ - set_nlink(dir, nlink); -} - -void au_sub_nlink(struct inode *dir, struct inode *h_dir) -{ - unsigned int nlink; - - AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); - - nlink = dir->i_nlink; - nlink -= h_dir->i_nlink - 2; - if (h_dir->i_nlink < 2) - nlink -= 2; - smp_mb(); /* for i_nlink */ - /* nlink == 0 means the branch-fs is broken */ - set_nlink(dir, nlink); -} - -loff_t au_dir_size(struct file *file, struct dentry *dentry) -{ - loff_t sz; - aufs_bindex_t bindex, bend; - struct file *h_file; - struct dentry *h_dentry; - - sz = 0; - if (file) { - AuDebugOn(!d_is_dir(file->f_path.dentry)); - - bend = au_fbend_dir(file); - for (bindex = au_fbstart(file); - bindex <= bend && sz < KMALLOC_MAX_SIZE; - bindex++) { - h_file = au_hf_dir(file, bindex); - if (h_file && file_inode(h_file)) - sz += vfsub_f_size_read(h_file); - } - } else { - AuDebugOn(!dentry); - AuDebugOn(!d_is_dir(dentry)); - - bend = au_dbtaildir(dentry); - for (bindex = au_dbstart(dentry); - bindex <= bend && sz < KMALLOC_MAX_SIZE; - bindex++) { - h_dentry = au_h_dptr(dentry, bindex); - if (h_dentry && d_is_positive(h_dentry)) - sz += i_size_read(d_inode(h_dentry)); - } - } - if (sz < KMALLOC_MAX_SIZE) - sz = roundup_pow_of_two(sz); - if (sz > KMALLOC_MAX_SIZE) - sz = KMALLOC_MAX_SIZE; - else if (sz < NAME_MAX) { - BUILD_BUG_ON(AUFS_RDBLK_DEF < NAME_MAX); - sz = AUFS_RDBLK_DEF; - } - return sz; -} - -struct au_dir_ts_arg { - struct dentry *dentry; - aufs_bindex_t brid; -}; - -static void au_do_dir_ts(void *arg) -{ - struct au_dir_ts_arg *a = arg; - struct au_dtime dt; - struct path h_path; - struct inode *dir, *h_dir; - struct super_block *sb; - struct au_branch *br; - struct au_hinode *hdir; - int err; - aufs_bindex_t bstart, bindex; - - sb = a->dentry->d_sb; - if (d_really_is_negative(a->dentry)) - goto out; - /* no dir->i_mutex lock */ - aufs_read_lock(a->dentry, AuLock_DW); /* noflush */ - - dir = d_inode(a->dentry); - bstart = au_ibstart(dir); - bindex = au_br_index(sb, a->brid); - if (bindex < bstart) - goto out_unlock; - - br = au_sbr(sb, bindex); - h_path.dentry = au_h_dptr(a->dentry, bindex); - if (!h_path.dentry) - goto out_unlock; - h_path.mnt = au_br_mnt(br); - au_dtime_store(&dt, a->dentry, &h_path); - - br = au_sbr(sb, bstart); - if (!au_br_writable(br->br_perm)) - goto out_unlock; - h_path.dentry = au_h_dptr(a->dentry, bstart); - h_path.mnt = au_br_mnt(br); - err = vfsub_mnt_want_write(h_path.mnt); - if (err) - goto out_unlock; - hdir = au_hi(dir, bstart); - au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); - h_dir = au_h_iptr(dir, bstart); - if (h_dir->i_nlink - && timespec_compare(&h_dir->i_mtime, &dt.dt_mtime) < 0) { - dt.dt_h_path = h_path; - au_dtime_revert(&dt); - } - au_hn_imtx_unlock(hdir); - vfsub_mnt_drop_write(h_path.mnt); - au_cpup_attr_timesizes(dir); - -out_unlock: - aufs_read_unlock(a->dentry, AuLock_DW); -out: - dput(a->dentry); - au_nwt_done(&au_sbi(sb)->si_nowait); - kfree(arg); -} - -void au_dir_ts(struct inode *dir, aufs_bindex_t bindex) -{ - int perm, wkq_err; - aufs_bindex_t bstart; - struct au_dir_ts_arg *arg; - struct dentry *dentry; - struct super_block *sb; - - IMustLock(dir); - - dentry = d_find_any_alias(dir); - AuDebugOn(!dentry); - sb = dentry->d_sb; - bstart = au_ibstart(dir); - if (bstart == bindex) { - au_cpup_attr_timesizes(dir); - goto out; - } - - perm = au_sbr_perm(sb, bstart); - if (!au_br_writable(perm)) - goto out; - - arg = kmalloc(sizeof(*arg), GFP_NOFS); - if (!arg) - goto out; - - arg->dentry = dget(dentry); /* will be dput-ted by au_do_dir_ts() */ - arg->brid = au_sbr_id(sb, bindex); - wkq_err = au_wkq_nowait(au_do_dir_ts, arg, sb, /*flags*/0); - if (unlikely(wkq_err)) { - pr_err("wkq %d\n", wkq_err); - dput(dentry); - kfree(arg); - } - -out: - dput(dentry); -} - -/* ---------------------------------------------------------------------- */ - -static int reopen_dir(struct file *file) -{ - int err; - unsigned int flags; - aufs_bindex_t bindex, btail, bstart; - struct dentry *dentry, *h_dentry; - struct file *h_file; - - /* open all lower dirs */ - dentry = file->f_path.dentry; - bstart = au_dbstart(dentry); - for (bindex = au_fbstart(file); bindex < bstart; bindex++) - au_set_h_fptr(file, bindex, NULL); - au_set_fbstart(file, bstart); - - btail = au_dbtaildir(dentry); - for (bindex = au_fbend_dir(file); btail < bindex; bindex--) - au_set_h_fptr(file, bindex, NULL); - au_set_fbend_dir(file, btail); - - flags = vfsub_file_flags(file); - for (bindex = bstart; bindex <= btail; bindex++) { - h_dentry = au_h_dptr(dentry, bindex); - if (!h_dentry) - continue; - h_file = au_hf_dir(file, bindex); - if (h_file) - continue; - - h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out; /* close all? */ - au_set_h_fptr(file, bindex, h_file); - } - au_update_figen(file); - /* todo: necessary? */ - /* file->f_ra = h_file->f_ra; */ - err = 0; - -out: - return err; -} - -static int do_open_dir(struct file *file, int flags, struct file *h_file) -{ - int err; - aufs_bindex_t bindex, btail; - struct dentry *dentry, *h_dentry; - struct vfsmount *mnt; - - FiMustWriteLock(file); - AuDebugOn(h_file); - - err = 0; - mnt = file->f_path.mnt; - dentry = file->f_path.dentry; - file->f_version = d_inode(dentry)->i_version; - bindex = au_dbstart(dentry); - au_set_fbstart(file, bindex); - btail = au_dbtaildir(dentry); - au_set_fbend_dir(file, btail); - for (; !err && bindex <= btail; bindex++) { - h_dentry = au_h_dptr(dentry, bindex); - if (!h_dentry) - continue; - - err = vfsub_test_mntns(mnt, h_dentry->d_sb); - if (unlikely(err)) - break; - h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); - if (IS_ERR(h_file)) { - err = PTR_ERR(h_file); - break; - } - au_set_h_fptr(file, bindex, h_file); - } - au_update_figen(file); - /* todo: necessary? */ - /* file->f_ra = h_file->f_ra; */ - if (!err) - return 0; /* success */ - - /* close all */ - for (bindex = au_fbstart(file); bindex <= btail; bindex++) - au_set_h_fptr(file, bindex, NULL); - au_set_fbstart(file, -1); - au_set_fbend_dir(file, -1); - - return err; -} - -static int aufs_open_dir(struct inode *inode __maybe_unused, - struct file *file) -{ - int err; - struct super_block *sb; - struct au_fidir *fidir; - - err = -ENOMEM; - sb = file->f_path.dentry->d_sb; - si_read_lock(sb, AuLock_FLUSH); - fidir = au_fidir_alloc(sb); - if (fidir) { - struct au_do_open_args args = { - .open = do_open_dir, - .fidir = fidir - }; - err = au_do_open(file, &args); - if (unlikely(err)) - kfree(fidir); - } - si_read_unlock(sb); - return err; -} - -static int aufs_release_dir(struct inode *inode __maybe_unused, - struct file *file) -{ - struct au_vdir *vdir_cache; - struct au_finfo *finfo; - struct au_fidir *fidir; - aufs_bindex_t bindex, bend; - - finfo = au_fi(file); - fidir = finfo->fi_hdir; - if (fidir) { - au_sphl_del(&finfo->fi_hlist, - &au_sbi(file->f_path.dentry->d_sb)->si_files); - vdir_cache = fidir->fd_vdir_cache; /* lock-free */ - if (vdir_cache) - au_vdir_free(vdir_cache); - - bindex = finfo->fi_btop; - if (bindex >= 0) { - /* - * calls fput() instead of filp_close(), - * since no dnotify or lock for the lower file. - */ - bend = fidir->fd_bbot; - for (; bindex <= bend; bindex++) - au_set_h_fptr(file, bindex, NULL); - } - kfree(fidir); - finfo->fi_hdir = NULL; - } - au_finfo_fin(file); - return 0; -} - -/* ---------------------------------------------------------------------- */ - -static int au_do_flush_dir(struct file *file, fl_owner_t id) -{ - int err; - aufs_bindex_t bindex, bend; - struct file *h_file; - - err = 0; - bend = au_fbend_dir(file); - for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { - h_file = au_hf_dir(file, bindex); - if (h_file) - err = vfsub_flush(h_file, id); - } - return err; -} - -static int aufs_flush_dir(struct file *file, fl_owner_t id) -{ - return au_do_flush(file, id, au_do_flush_dir); -} - -/* ---------------------------------------------------------------------- */ - -static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync) -{ - int err; - aufs_bindex_t bend, bindex; - struct inode *inode; - struct super_block *sb; - - err = 0; - sb = dentry->d_sb; - inode = d_inode(dentry); - IMustLock(inode); - bend = au_dbend(dentry); - for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) { - struct path h_path; - - if (au_test_ro(sb, bindex, inode)) - continue; - h_path.dentry = au_h_dptr(dentry, bindex); - if (!h_path.dentry) - continue; - - h_path.mnt = au_sbr_mnt(sb, bindex); - err = vfsub_fsync(NULL, &h_path, datasync); - } - - return err; -} - -static int au_do_fsync_dir(struct file *file, int datasync) -{ - int err; - aufs_bindex_t bend, bindex; - struct file *h_file; - struct super_block *sb; - struct inode *inode; - - err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); - if (unlikely(err)) - goto out; - - inode = file_inode(file); - sb = inode->i_sb; - bend = au_fbend_dir(file); - for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { - h_file = au_hf_dir(file, bindex); - if (!h_file || au_test_ro(sb, bindex, inode)) - continue; - - err = vfsub_fsync(h_file, &h_file->f_path, datasync); - } - -out: - return err; -} - -/* - * @file may be NULL - */ -static int aufs_fsync_dir(struct file *file, loff_t start, loff_t end, - int datasync) -{ - int err; - struct dentry *dentry; - struct inode *inode; - struct super_block *sb; - struct mutex *mtx; - - err = 0; - dentry = file->f_path.dentry; - inode = d_inode(dentry); - mtx = &inode->i_mutex; - mutex_lock(mtx); - sb = dentry->d_sb; - si_noflush_read_lock(sb); - if (file) - err = au_do_fsync_dir(file, datasync); - else { - di_write_lock_child(dentry); - err = au_do_fsync_dir_no_file(dentry, datasync); - } - au_cpup_attr_timesizes(inode); - di_write_unlock(dentry); - if (file) - fi_write_unlock(file); - - si_read_unlock(sb); - mutex_unlock(mtx); - return err; -} - -/* ---------------------------------------------------------------------- */ - -static int aufs_iterate(struct file *file, struct dir_context *ctx) -{ - int err; - struct dentry *dentry; - struct inode *inode, *h_inode; - struct super_block *sb; - - AuDbg("%pD, ctx{%pf, %llu}\n", file, ctx->actor, ctx->pos); - - dentry = file->f_path.dentry; - inode = d_inode(dentry); - IMustLock(inode); - - sb = dentry->d_sb; - si_read_lock(sb, AuLock_FLUSH); - err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); - if (unlikely(err)) - goto out; - err = au_alive_dir(dentry); - if (!err) - err = au_vdir_init(file); - di_downgrade_lock(dentry, AuLock_IR); - if (unlikely(err)) - goto out_unlock; - - h_inode = au_h_iptr(inode, au_ibstart(inode)); - if (!au_test_nfsd()) { - err = au_vdir_fill_de(file, ctx); - fsstack_copy_attr_atime(inode, h_inode); - } else { - /* - * nfsd filldir may call lookup_one_len(), vfs_getattr(), - * encode_fh() and others. - */ - atomic_inc(&h_inode->i_count); - di_read_unlock(dentry, AuLock_IR); - si_read_unlock(sb); - err = au_vdir_fill_de(file, ctx); - fsstack_copy_attr_atime(inode, h_inode); - fi_write_unlock(file); - iput(h_inode); - - AuTraceErr(err); - return err; - } - -out_unlock: - di_read_unlock(dentry, AuLock_IR); - fi_write_unlock(file); -out: - si_read_unlock(sb); - return err; -} - -/* ---------------------------------------------------------------------- */ - -#define AuTestEmpty_WHONLY 1 -#define AuTestEmpty_CALLED (1 << 1) -#define AuTestEmpty_SHWH (1 << 2) -#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name) -#define au_fset_testempty(flags, name) \ - do { (flags) |= AuTestEmpty_##name; } while (0) -#define au_fclr_testempty(flags, name) \ - do { (flags) &= ~AuTestEmpty_##name; } while (0) - -#ifndef CONFIG_AUFS_SHWH -#undef AuTestEmpty_SHWH -#define AuTestEmpty_SHWH 0 -#endif - -struct test_empty_arg { - struct dir_context ctx; - struct au_nhash *whlist; - unsigned int flags; - int err; - aufs_bindex_t bindex; -}; - -static int test_empty_cb(struct dir_context *ctx, const char *__name, - int namelen, loff_t offset __maybe_unused, u64 ino, - unsigned int d_type) -{ - struct test_empty_arg *arg = container_of(ctx, struct test_empty_arg, - ctx); - char *name = (void *)__name; - - arg->err = 0; - au_fset_testempty(arg->flags, CALLED); - /* smp_mb(); */ - if (name[0] == '.' - && (namelen == 1 || (name[1] == '.' && namelen == 2))) - goto out; /* success */ - - if (namelen <= AUFS_WH_PFX_LEN - || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { - if (au_ftest_testempty(arg->flags, WHONLY) - && !au_nhash_test_known_wh(arg->whlist, name, namelen)) - arg->err = -ENOTEMPTY; - goto out; - } - - name += AUFS_WH_PFX_LEN; - namelen -= AUFS_WH_PFX_LEN; - if (!au_nhash_test_known_wh(arg->whlist, name, namelen)) - arg->err = au_nhash_append_wh - (arg->whlist, name, namelen, ino, d_type, arg->bindex, - au_ftest_testempty(arg->flags, SHWH)); - -out: - /* smp_mb(); */ - AuTraceErr(arg->err); - return arg->err; -} - -static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg) -{ - int err; - struct file *h_file; - - h_file = au_h_open(dentry, arg->bindex, - O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE, - /*file*/NULL, /*force_wr*/0); - err = PTR_ERR(h_file); - if (IS_ERR(h_file)) - goto out; - - err = 0; - if (!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) - && !file_inode(h_file)->i_nlink) - goto out_put; - - do { - arg->err = 0; - au_fclr_testempty(arg->flags, CALLED); - /* smp_mb(); */ - err = vfsub_iterate_dir(h_file, &arg->ctx); - if (err >= 0) - err = arg->err; - } while (!err && au_ftest_testempty(arg->flags, CALLED)); - -out_put: - fput(h_file); - au_sbr_put(dentry->d_sb, arg->bindex); -out: - return err; -} - -struct do_test_empty_args { - int *errp; - struct dentry *dentry; - struct test_empty_arg *arg; -}; - -static void call_do_test_empty(void *args) -{ - struct do_test_empty_args *a = args; - *a->errp = do_test_empty(a->dentry, a->arg); -} - -static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg) -{ - int err, wkq_err; - struct dentry *h_dentry; - struct inode *h_inode; - - h_dentry = au_h_dptr(dentry, arg->bindex); - h_inode = d_inode(h_dentry); - /* todo: i_mode changes anytime? */ - mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); - err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ); - mutex_unlock(&h_inode->i_mutex); - if (!err) - err = do_test_empty(dentry, arg); - else { - struct do_test_empty_args args = { - .errp = &err, - .dentry = dentry, - .arg = arg - }; - unsigned int flags = arg->flags; - - wkq_err = au_wkq_wait(call_do_test_empty, &args); - if (unlikely(wkq_err)) - err = wkq_err; - arg->flags = flags; - } - - return err; -} - -int au_test_empty_lower(struct dentry *dentry) -{ - int err; - unsigned int rdhash; - aufs_bindex_t bindex, bstart, btail; - struct au_nhash whlist; - struct test_empty_arg arg = { - .ctx = { - .actor = test_empty_cb - } - }; - int (*test_empty)(struct dentry *dentry, struct test_empty_arg *arg); - - SiMustAnyLock(dentry->d_sb); - - rdhash = au_sbi(dentry->d_sb)->si_rdhash; - if (!rdhash) - rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry)); - err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); - if (unlikely(err)) - goto out; - - arg.flags = 0; - arg.whlist = &whlist; - bstart = au_dbstart(dentry); - if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) - au_fset_testempty(arg.flags, SHWH); - test_empty = do_test_empty; - if (au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)) - test_empty = sio_test_empty; - arg.bindex = bstart; - err = test_empty(dentry, &arg); - if (unlikely(err)) - goto out_whlist; - - au_fset_testempty(arg.flags, WHONLY); - btail = au_dbtaildir(dentry); - for (bindex = bstart + 1; !err && bindex <= btail; bindex++) { - struct dentry *h_dentry; - - h_dentry = au_h_dptr(dentry, bindex); - if (h_dentry && d_is_positive(h_dentry)) { - arg.bindex = bindex; - err = test_empty(dentry, &arg); - } - } - -out_whlist: - au_nhash_wh_free(&whlist); -out: - return err; -} - -int au_test_empty(struct dentry *dentry, struct au_nhash *whlist) -{ - int err; - struct test_empty_arg arg = { - .ctx = { - .actor = test_empty_cb - } - }; - aufs_bindex_t bindex, btail; - - err = 0; - arg.whlist = whlist; - arg.flags = AuTestEmpty_WHONLY; - if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) - au_fset_testempty(arg.flags, SHWH); - btail = au_dbtaildir(dentry); - for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) { - struct dentry *h_dentry; - - h_dentry = au_h_dptr(dentry, bindex); - if (h_dentry && d_is_positive(h_dentry)) { - arg.bindex = bindex; - err = sio_test_empty(dentry, &arg); - } - } - - return err; -} - -/* ---------------------------------------------------------------------- */ - -const struct file_operations aufs_dir_fop = { - .owner = THIS_MODULE, - .llseek = default_llseek, - .read = generic_read_dir, - .iterate = aufs_iterate, - .unlocked_ioctl = aufs_ioctl_dir, -#ifdef CONFIG_COMPAT - .compat_ioctl = aufs_compat_ioctl_dir, -#endif - .open = aufs_open_dir, - .release = aufs_release_dir, - .flush = aufs_flush_dir, - .fsync = aufs_fsync_dir -}; |