/* || bindex > au_sbend(dentry->d_sb) */); d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry; AuDebugOn(d && (atomic_read(&d->d_count) <= 0)); return d; } aufs_bindex_t au_dbtail(struct dentry *dentry) { aufs_bindex_t bend, bwh; bend = au_dbend(dentry); if (0 <= bend) { bwh = au_dbwh(dentry); if (!bwh) return bwh; if (0 < bwh && bwh < bend) return bwh - 1; } return bend; } aufs_bindex_t au_dbtaildir(struct dentry *dentry) { aufs_bindex_t bend, bopq; AuDebugOn(dentry->d_inode && dentry->d_inode->i_mode && !S_ISDIR(dentry->d_inode->i_mode)); bend = au_dbtail(dentry); if (0 <= bend) { bopq = au_dbdiropq(dentry); AuDebugOn(bend < bopq); if (0 <= bopq && bopq < bend) bend = bopq; } return bend; } #if 0 /* reserved for future use */ aufs_bindex_t au_dbtail_generic(struct dentry *dentry) { struct inode *inode; inode = dentry->d_inode; if (inode && S_ISDIR(inode->i_mode)) return au_dbtaildir(dentry); else return au_dbtail(dentry); }
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(!file->f_dentry); AuDebugOn(!file->f_dentry->d_inode); AuDebugOn(!S_ISDIR(file->f_dentry->d_inode->i_mode)); 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 && h_file->f_dentry && h_file->f_dentry->d_inode) sz += i_size_read(h_file->f_dentry->d_inode); } } else { AuDebugOn(!dentry); AuDebugOn(!dentry->d_inode); AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode)); 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 && h_dentry->d_inode) sz += i_size_read(h_dentry->d_inode); } } 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; }
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_dbtop(dentry); au_set_fbtop(file, bindex); btail = au_dbtaildir(dentry); au_set_fbbot_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_fbtop(file); bindex <= btail; bindex++) au_set_h_fptr(file, bindex, NULL); au_set_fbtop(file, -1); au_set_fbbot_dir(file, -1); return err; }
static int do_open_dir(struct file *file, int flags) { int err; aufs_bindex_t bindex, btail; struct dentry *dentry, *h_dentry; struct file *h_file; FiMustWriteLock(file); dentry = file->f_dentry; err = au_alive_dir(dentry); if (unlikely(err)) goto out; file->f_version = dentry->d_inode->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; h_file = au_h_open(dentry, bindex, flags, file); 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); out: return err; }
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_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); 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; }
loff_t au_dir_size(struct file *file, struct dentry *dentry) { loff_t sz; aufs_bindex_t bindex, bbot; struct file *h_file; struct dentry *h_dentry; sz = 0; if (file) { AuDebugOn(!d_is_dir(file->f_path.dentry)); bbot = au_fbbot_dir(file); for (bindex = au_fbtop(file); bindex <= bbot && 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)); bbot = au_dbtaildir(dentry); for (bindex = au_dbtop(dentry); bindex <= bbot && 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; }
/* * test if @dentry dir can be rename source or not. * if it can, return 0 and @children is filled. * success means, * - it is a logically empty dir. * - or, it exists on writable branch and has no children including whiteouts * on the lower branch. */ static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt) { int err; unsigned int rdhash; aufs_bindex_t bstart; bstart = au_dbstart(dentry); if (bstart != btgt) { struct au_nhash whlist; 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; err = au_test_empty(dentry, &whlist); au_nhash_wh_free(&whlist); goto out; } if (bstart == au_dbtaildir(dentry)) return 0; /* success */ err = au_test_empty_lower(dentry); out: if (err == -ENOTEMPTY) { AuWarn1("renaming dir who has child(ren) on multiple branches," " is not supported\n"); err = -EXDEV; } return err; }
static int set_inode(struct inode *inode, struct dentry *dentry) { int err; unsigned int flags; umode_t mode; aufs_bindex_t bindex, bstart, btail; unsigned char isdir; struct dentry *h_dentry; struct inode *h_inode; struct au_iinfo *iinfo; IiMustWriteLock(inode); err = 0; isdir = 0; bstart = au_dbstart(dentry); h_inode = au_h_dptr(dentry, bstart)->d_inode; mode = h_inode->i_mode; switch (mode & S_IFMT) { case S_IFREG: btail = au_dbtail(dentry); inode->i_op = &aufs_iop; inode->i_fop = &aufs_file_fop; inode->i_mapping->a_ops = &aufs_aop; break; case S_IFDIR: isdir = 1; btail = au_dbtaildir(dentry); inode->i_op = &aufs_dir_iop; inode->i_fop = &aufs_dir_fop; break; case S_IFLNK: btail = au_dbtail(dentry); inode->i_op = &aufs_symlink_iop; break; case S_IFBLK: case S_IFCHR: case S_IFIFO: case S_IFSOCK: btail = au_dbtail(dentry); inode->i_op = &aufs_iop; au_init_special_fop(inode, mode, h_inode->i_rdev); break; default: AuIOErr("Unknown file type 0%o\n", mode); err = -EIO; goto out; } /* do not set inotify for whiteouted dirs (SHWH mode) */ flags = au_hi_flags(inode, isdir); if (au_opt_test(au_mntflags(dentry->d_sb), SHWH) && au_ftest_hi(flags, HINOTIFY) && dentry->d_name.len > AUFS_WH_PFX_LEN && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) au_fclr_hi(flags, HINOTIFY); iinfo = au_ii(inode); iinfo->ii_bstart = bstart; iinfo->ii_bend = btail; for (bindex = bstart; bindex <= btail; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (h_dentry) au_set_h_iptr(inode, bindex, au_igrab(h_dentry->d_inode), flags); } au_cpup_attr_all(inode, /*force*/1); out: return err; }
/* todo: remove this */ static int h_d_revalidate(struct dentry *dentry, struct inode *inode, struct nameidata *nd, int do_udba) { int err; umode_t mode, h_mode; aufs_bindex_t bindex, btail, bstart, ibs, ibe; unsigned char plus, unhashed, is_root, h_plus; struct inode *h_inode, *h_cached_inode; struct dentry *h_dentry; struct qstr *name, *h_name; err = 0; plus = 0; mode = 0; ibs = -1; ibe = -1; unhashed = !!d_unhashed(dentry); is_root = !!IS_ROOT(dentry); name = &dentry->d_name; /* * Theoretically, REVAL test should be unnecessary in case of INOTIFY. * But inotify doesn't fire some necessary events, * IN_ATTRIB for atime/nlink/pageio * IN_DELETE for NFS dentry * Let's do REVAL test too. */ if (do_udba && inode) { mode = (inode->i_mode & S_IFMT); plus = (inode->i_nlink > 0); ibs = au_ibstart(inode); ibe = au_ibend(inode); } bstart = au_dbstart(dentry); btail = bstart; if (inode && S_ISDIR(inode->i_mode)) btail = au_dbtaildir(dentry); for (bindex = bstart; bindex <= btail; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (!h_dentry) continue; AuDbg("b%d, %.*s\n", bindex, AuDLNPair(h_dentry)); h_name = &h_dentry->d_name; if (unlikely(do_udba && !is_root && (unhashed != !!d_unhashed(h_dentry) || name->len != h_name->len || memcmp(name->name, h_name->name, name->len)) )) { AuDbg("unhash 0x%x 0x%x, %.*s %.*s\n", unhashed, d_unhashed(h_dentry), AuDLNPair(dentry), AuDLNPair(h_dentry)); goto err; } err = au_do_h_d_reval(h_dentry, nd, dentry, bindex); if (unlikely(err)) /* do not goto err, to keep the errno */ break; /* todo: plink too? */ if (!do_udba) continue; /* UDBA tests */ h_inode = h_dentry->d_inode; if (unlikely(!!inode != !!h_inode)) goto err; h_plus = plus; h_mode = mode; h_cached_inode = h_inode; if (h_inode) { h_mode = (h_inode->i_mode & S_IFMT); h_plus = (h_inode->i_nlink > 0); } if (inode && ibs <= bindex && bindex <= ibe) h_cached_inode = au_h_iptr(inode, bindex); if (unlikely(plus != h_plus || mode != h_mode || h_cached_inode != h_inode)) goto err; continue; err: err = -EINVAL; break; } return err; }
/* * returns the number of lower positive dentries, * otherwise an error. * can be called at unlinking with @type is zero. */ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type, struct nameidata *nd) { int npositive, err; aufs_bindex_t bindex, btail, bdiropq; unsigned char isdir; struct qstr whname; struct au_do_lookup_args args = { .flags = 0, .type = type, .nd = nd }; const struct qstr *name = &dentry->d_name; struct dentry *parent; struct inode *inode; err = au_test_shwh(dentry->d_sb, name); if (unlikely(err)) goto out; err = au_wh_name_alloc(&whname, name); if (unlikely(err)) goto out; inode = dentry->d_inode; isdir = !!(inode && S_ISDIR(inode->i_mode)); if (!type) au_fset_lkup(args.flags, ALLOW_NEG); npositive = 0; parent = dget_parent(dentry); btail = au_dbtaildir(parent); for (bindex = bstart; bindex <= btail; bindex++) { struct dentry *h_parent, *h_dentry; struct inode *h_inode, *h_dir; h_dentry = au_h_dptr(dentry, bindex); if (h_dentry) { if (h_dentry->d_inode) npositive++; if (type != S_IFDIR) break; continue; } h_parent = au_h_dptr(parent, bindex); if (!h_parent) continue; h_dir = h_parent->d_inode; if (!h_dir || !S_ISDIR(h_dir->i_mode)) continue; mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, &args); mutex_unlock(&h_dir->i_mutex); err = PTR_ERR(h_dentry); if (IS_ERR(h_dentry)) goto out_parent; au_fclr_lkup(args.flags, ALLOW_NEG); if (au_dbwh(dentry) >= 0) break; if (!h_dentry) continue; h_inode = h_dentry->d_inode; if (!h_inode) continue; npositive++; if (!args.type) args.type = h_inode->i_mode & S_IFMT; if (args.type != S_IFDIR) break; else if (isdir) { /* the type of lower may be different */ bdiropq = au_dbdiropq(dentry); if (bdiropq >= 0 && bdiropq <= bindex) break; } } if (npositive) { AuLabel(positive); au_update_dbstart(dentry); } err = npositive; if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) && au_dbstart(dentry) < 0)) { err = -EIO; AuIOErr("both of real entry and whiteout found, %.*s, err %d\n", AuDLNPair(dentry), err); } out_parent: dput(parent); kfree(whname.name); out: return err; } struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent, struct au_branch *br) { struct dentry *dentry; int wkq_err; if (!au_test_h_perm_sio(parent->d_inode, MAY_EXEC)) dentry = au_lkup_one(name, parent, br, /*nd*/NULL); else { struct au_lkup_one_args args = { .errp = &dentry, .name = name, .h_parent = parent, .br = br, .nd = NULL }; wkq_err = au_wkq_wait(au_call_lkup_one, &args); if (unlikely(wkq_err)) dentry = ERR_PTR(wkq_err); } return dentry; } /* * lookup @dentry on @bindex which should be negative. */ int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex) { int err; struct dentry *parent, *h_parent, *h_dentry; parent = dget_parent(dentry); h_parent = au_h_dptr(parent, bindex); h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent, au_sbr(dentry->d_sb, bindex)); err = PTR_ERR(h_dentry); if (IS_ERR(h_dentry)) goto out; if (unlikely(h_dentry->d_inode)) { err = -EIO; AuIOErr("%.*s should be negative on b%d.\n", AuDLNPair(h_dentry), bindex); dput(h_dentry); goto out; } err = 0; if (bindex < au_dbstart(dentry)) au_set_dbstart(dentry, bindex); if (au_dbend(dentry) < bindex) au_set_dbend(dentry, bindex); au_set_h_dptr(dentry, bindex, h_dentry); out: dput(parent); return err; }
/* most free space */ static void au_mfs(struct dentry *dentry, struct dentry *parent) { struct super_block *sb; struct au_branch *br; struct au_wbr_mfs *mfs; struct dentry *h_parent; aufs_bindex_t bindex, bend; int err; unsigned long long b, bavail; struct path h_path; /* reduce the stack usage */ struct kstatfs *st; st = kmalloc(sizeof(*st), GFP_NOFS); if (unlikely(!st)) { AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM); return; } bavail = 0; sb = dentry->d_sb; mfs = &au_sbi(sb)->si_wbr_mfs; MtxMustLock(&mfs->mfs_lock); mfs->mfs_bindex = -EROFS; mfs->mfsrr_bytes = 0; if (!parent) { bindex = 0; bend = au_sbend(sb); } else { bindex = au_dbstart(parent); bend = au_dbtaildir(parent); } for (; bindex <= bend; bindex++) { if (parent) { h_parent = au_h_dptr(parent, bindex); if (!h_parent || !h_parent->d_inode) continue; } br = au_sbr(sb, bindex); if (au_br_rdonly(br)) continue; /* sb->s_root for NFS is unreliable */ h_path.mnt = au_br_mnt(br); h_path.dentry = h_path.mnt->mnt_root; err = vfs_statfs(&h_path, st); if (unlikely(err)) { AuWarn1("failed statfs, b%d, %d\n", bindex, err); continue; } /* when the available size is equal, select the lower one */ BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail) || sizeof(b) < sizeof(st->f_bsize)); b = st->f_bavail * st->f_bsize; br->br_wbr->wbr_bytes = b; if (b >= bavail) { bavail = b; mfs->mfs_bindex = bindex; mfs->mfs_jiffy = jiffies; } } mfs->mfsrr_bytes = bavail; AuDbg("b%d\n", mfs->mfs_bindex); kfree(st); }
/* todo: remove this */ static int h_d_revalidate(struct dentry *dentry, struct inode *inode, unsigned int flags, int do_udba) { int err; umode_t mode, h_mode; aufs_bindex_t bindex, btail, bstart, ibs, ibe; unsigned char plus, unhashed, is_root, h_plus, h_nfs, tmpfile; struct inode *h_inode, *h_cached_inode; struct dentry *h_dentry; struct qstr *name, *h_name; err = 0; plus = 0; mode = 0; ibs = -1; ibe = -1; unhashed = !!d_unhashed(dentry); is_root = !!IS_ROOT(dentry); name = &dentry->d_name; tmpfile = au_di(dentry)->di_tmpfile; /* * Theoretically, REVAL test should be unnecessary in case of * {FS,I}NOTIFY. * But {fs,i}notify doesn't fire some necessary events, * IN_ATTRIB for atime/nlink/pageio * Let's do REVAL test too. */ if (do_udba && inode) { mode = (inode->i_mode & S_IFMT); plus = (inode->i_nlink > 0); ibs = au_ibstart(inode); ibe = au_ibend(inode); } bstart = au_dbstart(dentry); btail = bstart; if (inode && S_ISDIR(inode->i_mode)) btail = au_dbtaildir(dentry); for (bindex = bstart; bindex <= btail; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (!h_dentry) continue; AuDbg("b%d, %pd\n", bindex, h_dentry); h_nfs = !!au_test_nfs(h_dentry->d_sb); spin_lock(&h_dentry->d_lock); h_name = &h_dentry->d_name; if (unlikely(do_udba && !is_root && ((!h_nfs && (unhashed != !!d_unhashed(h_dentry) || (!tmpfile && !au_qstreq(name, h_name)) )) || (h_nfs && !(flags & LOOKUP_OPEN) && (h_dentry->d_flags & DCACHE_NFSFS_RENAMED))) )) { int h_unhashed; h_unhashed = d_unhashed(h_dentry); spin_unlock(&h_dentry->d_lock); AuDbg("unhash 0x%x 0x%x, %pd %pd\n", unhashed, h_unhashed, dentry, h_dentry); goto err; } spin_unlock(&h_dentry->d_lock); err = au_do_h_d_reval(h_dentry, flags, dentry, bindex); if (unlikely(err)) /* do not goto err, to keep the errno */ break; /* todo: plink too? */ if (!do_udba) continue; /* UDBA tests */ if (unlikely(!!inode != d_is_positive(h_dentry))) goto err; h_inode = NULL; if (d_is_positive(h_dentry)) h_inode = d_inode(h_dentry); h_plus = plus; h_mode = mode; h_cached_inode = h_inode; if (h_inode) { h_mode = (h_inode->i_mode & S_IFMT); h_plus = (h_inode->i_nlink > 0); } if (inode && ibs <= bindex && bindex <= ibe) h_cached_inode = au_h_iptr(inode, bindex); if (!h_nfs) { if (unlikely(plus != h_plus && !tmpfile)) goto err; } else { if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED) && !is_root && !IS_ROOT(h_dentry) && unhashed != d_unhashed(h_dentry))) goto err; } if (unlikely(mode != h_mode || h_cached_inode != h_inode)) goto err; continue; err: err = -EINVAL; break; } AuTraceErr(err); return err; }
/* * returns the number of lower positive dentries, * otherwise an error. * can be called at unlinking with @type is zero. */ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type) { int npositive, err; aufs_bindex_t bindex, btail, bdiropq; unsigned char isdir, dirperm1; struct qstr whname; struct au_do_lookup_args args = { .flags = 0, .type = type }; const struct qstr *name = &dentry->d_name; struct dentry *parent; struct super_block *sb; sb = dentry->d_sb; err = au_test_shwh(sb, name); if (unlikely(err)) goto out; err = au_wh_name_alloc(&whname, name); if (unlikely(err)) goto out; isdir = !!d_is_dir(dentry); if (!type) au_fset_lkup(args.flags, ALLOW_NEG); dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1); npositive = 0; parent = dget_parent(dentry); btail = au_dbtaildir(parent); for (bindex = bstart; bindex <= btail; bindex++) { struct dentry *h_parent, *h_dentry; struct inode *h_inode, *h_dir; h_dentry = au_h_dptr(dentry, bindex); if (h_dentry) { if (d_is_positive(h_dentry)) npositive++; if (type != S_IFDIR) break; continue; } h_parent = au_h_dptr(parent, bindex); if (!h_parent || !d_is_dir(h_parent)) continue; h_dir = d_inode(h_parent); mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, &args); mutex_unlock(&h_dir->i_mutex); err = PTR_ERR(h_dentry); if (IS_ERR(h_dentry)) goto out_parent; if (h_dentry) au_fclr_lkup(args.flags, ALLOW_NEG); if (dirperm1) au_fset_lkup(args.flags, IGNORE_PERM); if (au_dbwh(dentry) >= 0) break; if (!h_dentry) continue; if (d_is_negative(h_dentry)) continue; h_inode = d_inode(h_dentry); npositive++; if (!args.type) args.type = h_inode->i_mode & S_IFMT; if (args.type != S_IFDIR) break; else if (isdir) { /* the type of lower may be different */ bdiropq = au_dbdiropq(dentry); if (bdiropq >= 0 && bdiropq <= bindex) break; } } if (npositive) { AuLabel(positive); au_update_dbstart(dentry); } err = npositive; if (unlikely(!au_opt_test(au_mntflags(sb), UDBA_NONE) && au_dbstart(dentry) < 0)) { err = -EIO; AuIOErr("both of real entry and whiteout found, %pd, err %d\n", dentry, err); } out_parent: dput(parent); kfree(whname.name); out: return err; } struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent) { struct dentry *dentry; int wkq_err; if (!au_test_h_perm_sio(d_inode(parent), MAY_EXEC)) dentry = vfsub_lkup_one(name, parent); else { struct vfsub_lkup_one_args args = { .errp = &dentry, .name = name, .parent = parent }; wkq_err = au_wkq_wait(vfsub_call_lkup_one, &args); if (unlikely(wkq_err)) dentry = ERR_PTR(wkq_err); } return dentry; } /* * lookup @dentry on @bindex which should be negative. */ int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh) { int err; struct dentry *parent, *h_parent, *h_dentry; struct au_branch *br; parent = dget_parent(dentry); h_parent = au_h_dptr(parent, bindex); br = au_sbr(dentry->d_sb, bindex); if (wh) h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); else h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); err = PTR_ERR(h_dentry); if (IS_ERR(h_dentry)) goto out; if (unlikely(d_is_positive(h_dentry))) { err = -EIO; AuIOErr("%pd should be negative on b%d.\n", h_dentry, bindex); dput(h_dentry); goto out; } err = 0; if (bindex < au_dbstart(dentry)) au_set_dbstart(dentry, bindex); if (au_dbend(dentry) < bindex) au_set_dbend(dentry, bindex); au_set_h_dptr(dentry, bindex, h_dentry); out: dput(parent); return err; }
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; ino_t ino, dir_ino; aufs_bindex_t bindex; 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 */ bindex = si_nfsd_read_lock(sb, &nsi_lock); if (unlikely(bindex < 0)) 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; 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? */ 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, bindex, 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 (dentry->d_inode->i_generation == fh[Fh_igen]) goto out_unlock; /* success */ dput(dentry); dentry = ERR_PTR(-ESTALE); out_unlock: 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 dentry *dentry, __u32 *fh, int *max_len, int connectable) { int err; aufs_bindex_t bindex, bend; struct super_block *sb, *h_sb; struct inode *inode; struct dentry *parent, *h_parent; struct au_branch *br; AuDebugOn(au_test_anon(dentry)); parent = NULL; err = -ENOSPC; if (unlikely(*max_len <= Fh_tail)) { AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); goto out; } err = FILEID_ROOT; if (IS_ROOT(dentry)) { AuDebugOn(dentry->d_inode->i_ino != AUFS_ROOT_INO); goto out; } err = -EIO; h_parent = NULL; sb = dentry->d_sb; aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR); parent = dget_parent(dentry); di_read_lock_parent(parent, !AuLock_IR); inode = dentry->d_inode; AuDebugOn(!inode); #ifdef CONFIG_AUFS_DEBUG if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) AuWarn1("NFS-exporting requires xino\n"); #endif bend = au_dbtaildir(parent); for (bindex = au_dbstart(parent); bindex <= bend; bindex++) { h_parent = au_h_dptr(parent, bindex); if (h_parent) { dget(h_parent); break; } } if (unlikely(!h_parent)) goto out_unlock; err = -EPERM; br = au_sbr(sb, bindex); h_sb = br->br_mnt->mnt_sb; if (unlikely(!h_sb->s_export_op)) { AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); goto out_dput; } 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, parent->d_inode->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 != 255) err = 99; else AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); out_dput: dput(h_parent); out_unlock: di_read_unlock(parent, !AuLock_IR); dput(parent); aufs_read_unlock(dentry, AuLock_IR); out: if (unlikely(err < 0)) err = 255; return err; }
static struct dentry * aufs_decode_fh(struct super_block *sb, __u32 *fh, int fh_len, int fh_type, int (*acceptable)(void *context, struct dentry *de), void *context) { struct dentry *dentry; ino_t ino, dir_ino; aufs_bindex_t bindex; struct au_nfsd_si_lock nsi_lock = { .sigen = fh[Fh_sigen], .br_id = fh[Fh_br_id], .force_lock = 0 }; LKTRTrace("%d, fh{br_id %u, sigen %u, i%u, diri%u, g%u}\n", fh_type, fh[Fh_br_id], fh[Fh_sigen], fh[Fh_ino], fh[Fh_dir_ino], fh[Fh_igen]); AuDebugOn(fh_len < Fh_tail); dentry = ERR_PTR(-ESTALE); /* branch id may be wrapped around */ bindex = si_nfsd_read_lock(sb, &nsi_lock); if (unlikely(bindex < 0)) goto out; nsi_lock.force_lock = 1; /* is this inode still cached? */ ino = decode_ino(fh + Fh_ino); AuDebugOn(ino == AUFS_ROOT_INO); 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? */ 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, bindex, ino, fh, fh_len, &nsi_lock); if (IS_ERR(dentry)) goto out_unlock; if (unlikely(!dentry)) goto out_unlock; accept: LKTRLabel(accept); if (dentry->d_inode->i_generation == fh[Fh_igen] && acceptable(context, dentry)) goto out_unlock; /* success */ LKTRLabel(stale); dput(dentry); dentry = ERR_PTR(-ESTALE); out_unlock: LKTRLabel(out_unlock); si_read_unlock(sb); out: LKTRLabel(out); if (0 && IS_ERR(dentry)) dentry = ERR_PTR(-ESTALE); AuTraceErrPtr(dentry); return dentry; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) static struct dentry * aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { return aufs_decode_fh(sb, fid->raw, fh_len, fh_type, h_acceptable, /*context*/NULL); } #endif /* KERNEL_VERSION */ /* ---------------------------------------------------------------------- */ static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, int connectable) { int err; aufs_bindex_t bindex, bend; struct super_block *sb, *h_sb; struct inode *inode; struct dentry *parent, *h_parent; struct au_branch *br; LKTRTrace("%.*s, max %d, conn %d\n", AuDLNPair(dentry), *max_len, connectable); AuDebugOn(au_test_anon(dentry)); parent = NULL; err = -ENOSPC; if (unlikely(*max_len <= Fh_tail)) { AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); goto out; } err = 0; //FILEID_ROOT; if (IS_ROOT(dentry)) { AuDebugOn(dentry->d_inode->i_ino != AUFS_ROOT_INO); goto out; } err = -EIO; h_parent = NULL; sb = dentry->d_sb; aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR); parent = dget_parent(dentry); di_read_lock_parent(parent, !AuLock_IR); inode = dentry->d_inode; AuDebugOn(!inode); #ifdef CONFIG_AUFS_DEBUG { unsigned int mnt_flags = au_mntflags(sb); if (unlikely(!au_opt_test_xino(mnt_flags))) AuWarn1("NFS-exporting requires xino\n"); if (unlikely(0 && !au_opt_test(mnt_flags, UDBA_INOTIFY))) AuWarn1("udba=inotify is recommended " "for NFS-exporting\n"); } #endif bend = au_dbtaildir(parent); for (bindex = au_dbstart(parent); bindex <= bend; bindex++) { h_parent = au_h_dptr(parent, bindex); if (h_parent) { dget(h_parent); break; } } if (unlikely(!h_parent)) goto out_unlock; LKTRTrace("b%d\n", bindex); err = -EPERM; br = au_sbr(sb, bindex); h_sb = br->br_mnt->mnt_sb; if (unlikely(!h_sb->s_export_op)) { AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); goto out_dput; } 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, parent->d_inode->i_ino); fh[Fh_igen] = inode->i_generation; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) /* it should be set at exporting time */ if (unlikely(!h_sb->s_export_op->find_exported_dentry)) { AuWarn("set default find_exported_dentry for %s\n", au_sbtype(h_sb)); h_sb->s_export_op->find_exported_dentry = find_exported_dentry; } #endif *max_len -= Fh_tail; fh[Fh_h_type] = au_call_encode_fh(h_parent, fh + Fh_tail, max_len, /*connectable or subtreecheck*/0); err = fh[Fh_h_type]; *max_len += Fh_tail; /* todo: macros? */ if (err != 255) err = 99; else AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); out_dput: dput(h_parent); out_unlock: di_read_unlock(parent, !AuLock_IR); dput(parent); aufs_read_unlock(dentry, AuLock_IR); out: AuTraceErr(err); if (unlikely(err < 0)) err = 255; return err; }