/* * simple tests for the del-entry operations. * following the checks in vfs, plus the parent-child relationship. */ int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, struct dentry *h_parent, int isdir) { int err; umode_t h_mode; struct dentry *h_dentry, *h_latest; struct inode *h_inode; h_dentry = au_h_dptr(dentry, bindex); if (d_really_is_positive(dentry)) { err = -ENOENT; if (unlikely(d_is_negative(h_dentry))) goto out; h_inode = d_inode(h_dentry); if (unlikely(!h_inode->i_nlink)) goto out; h_mode = h_inode->i_mode; if (!isdir) { err = -EISDIR; if (unlikely(S_ISDIR(h_mode))) goto out; } else if (unlikely(!S_ISDIR(h_mode))) { err = -ENOTDIR; goto out; } } else { /* rename(2) case */ err = -EIO; if (unlikely(d_is_positive(h_dentry))) goto out; } err = -ENOENT; /* expected parent dir is locked */ if (unlikely(h_parent != h_dentry->d_parent)) goto out; err = 0; /* * rmdir a dir may break the consistency on some filesystem. * let's try heavy test. */ err = -EACCES; if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1) && au_test_h_perm(d_inode(h_parent), MAY_EXEC | MAY_WRITE))) goto out; h_latest = au_sio_lkup_one(&dentry->d_name, h_parent); err = -EIO; if (IS_ERR(h_latest)) goto out; if (h_latest == h_dentry) err = 0; dput(h_latest); out: return err; }
int vfsub_kern_path(const char *name, unsigned int flags, struct path *path) { int err; err = kern_path(name, flags, path); if (!err && d_is_positive(path->dentry)) vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ 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; }
struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, int len) { struct path path = { .mnt = NULL }; /* VFS checks it too, but by WARN_ON_ONCE() */ IMustLock(d_inode(parent)); path.dentry = lookup_one_len(name, parent, len); if (IS_ERR(path.dentry)) goto out; if (d_is_positive(path.dentry)) vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ out: AuTraceErrPtr(path.dentry); return path.dentry; }
static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent, struct au_branch *br) { int err; struct au_iattr ia; struct inode *h_inode; struct dentry *h_d; struct super_block *h_sb; err = 0; memset(&ia, -1, sizeof(ia)); h_sb = h_dentry->d_sb; h_inode = NULL; if (d_is_positive(h_dentry)) { h_inode = d_inode(h_dentry); au_iattr_save(&ia, h_inode); } else if (au_test_nfs(h_sb) || au_test_fuse(h_sb)) /* nfs d_revalidate may return 0 for negative dentry */ /* fuse d_revalidate always return 0 for negative dentry */ goto out; /* main purpose is namei.c:cached_lookup() and d_revalidate */ h_d = vfsub_lkup_one(&h_dentry->d_name, h_parent); err = PTR_ERR(h_d); if (IS_ERR(h_d)) goto out; err = 0; if (unlikely(h_d != h_dentry || d_inode(h_d) != h_inode || (h_inode && au_iattr_test(&ia, h_inode)))) err = au_busy_or_stale(); dput(h_d); out: AuTraceErr(err); return err; }
/* copy-down the file */ static int au_do_cpdown(const unsigned char dmsg, struct au_mvd_args *a) { int err; struct au_cp_generic cpg = { .dentry = a->dentry, .bdst = a->mvd_bdst, .bsrc = a->mvd_bsrc, .len = -1, .pin = &a->mvd_pin_dst, .flags = AuCpup_DTIME | AuCpup_HOPEN }; AuDbg("b%d, b%d\n", cpg.bsrc, cpg.bdst); if (a->mvdown.flags & AUFS_MVDOWN_OWLOWER) au_fset_cpup(cpg.flags, OVERWRITE); if (a->mvdown.flags & AUFS_MVDOWN_ROLOWER) au_fset_cpup(cpg.flags, RWDST); err = au_sio_cpdown_simple(&cpg); if (unlikely(err)) AU_MVD_PR(dmsg, "cpdown failed\n"); AuTraceErr(err); return err; } /* * unlink the whiteout on bdst if exist which may be created by UDBA while we * were sleeping */ static int au_do_unlink_wh(const unsigned char dmsg, struct au_mvd_args *a) { int err; struct path h_path; struct au_branch *br; struct inode *delegated; br = au_sbr(a->sb, a->mvd_bdst); h_path.dentry = au_wh_lkup(a->mvd_h_dst_parent, &a->dentry->d_name, br); err = PTR_ERR(h_path.dentry); if (IS_ERR(h_path.dentry)) { AU_MVD_PR(dmsg, "wh_lkup failed\n"); goto out; } err = 0; if (d_is_positive(h_path.dentry)) { h_path.mnt = au_br_mnt(br); delegated = NULL; err = vfsub_unlink(d_inode(a->mvd_h_dst_parent), &h_path, &delegated, /*force*/0); if (unlikely(err == -EWOULDBLOCK)) { pr_warn("cannot retry for NFSv4 delegation" " for an internal unlink\n"); iput(delegated); } if (unlikely(err)) AU_MVD_PR(dmsg, "wh_unlink failed\n"); } dput(h_path.dentry); out: AuTraceErr(err); return err; }
/* 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; }
/* * By adding a dirty branch, a cached dentry may be affected in various ways. * * a dirty branch is added * - on the top of layers * - in the middle of layers * - to the bottom of layers * * on the added branch there exists * - a whiteout * - a diropq * - a same named entry * + exist * * negative --> positive * * positive --> positive * - type is unchanged * - type is changed * + doesn't exist * * negative --> negative * * positive --> negative (rejected by au_br_del() for non-dir case) * - none */ static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo, struct au_dinfo *tmp) { int err; aufs_bindex_t bindex, bend; struct { struct dentry *dentry; struct inode *inode; mode_t mode; } orig_h, tmp_h; struct au_hdentry *hd; struct inode *inode, *h_inode; struct dentry *h_dentry; err = 0; AuDebugOn(dinfo->di_bstart < 0); orig_h.mode = 0; orig_h.dentry = dinfo->di_hdentry[dinfo->di_bstart].hd_dentry; orig_h.inode = NULL; if (d_is_positive(orig_h.dentry)) { orig_h.inode = d_inode(orig_h.dentry); orig_h.mode = orig_h.inode->i_mode & S_IFMT; } memset(&tmp_h, 0, sizeof(tmp_h)); if (tmp->di_bstart >= 0) { tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry; tmp_h.inode = NULL; if (d_is_positive(tmp_h.dentry)) { tmp_h.inode = d_inode(tmp_h.dentry); tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; } } inode = NULL; if (d_really_is_positive(dentry)) inode = d_inode(dentry); if (!orig_h.inode) { AuDbg("nagative originally\n"); if (inode) { au_hide(dentry); goto out; } AuDebugOn(inode); AuDebugOn(dinfo->di_bstart != dinfo->di_bend); AuDebugOn(dinfo->di_bdiropq != -1); if (!tmp_h.inode) { AuDbg("negative --> negative\n"); /* should have only one negative lower */ if (tmp->di_bstart >= 0 && tmp->di_bstart < dinfo->di_bstart) { AuDebugOn(tmp->di_bstart != tmp->di_bend); AuDebugOn(dinfo->di_bstart != dinfo->di_bend); au_set_h_dptr(dentry, dinfo->di_bstart, NULL); au_di_cp(dinfo, tmp); hd = tmp->di_hdentry + tmp->di_bstart; au_set_h_dptr(dentry, tmp->di_bstart, dget(hd->hd_dentry)); } au_dbg_verify_dinode(dentry); } else { AuDbg("negative --> positive\n"); /* * similar to the behaviour of creating with bypassing * aufs. * unhash it in order to force an error in the * succeeding create operation. * we should not set S_DEAD here. */ d_drop(dentry); /* au_di_swap(tmp, dinfo); */ au_dbg_verify_dinode(dentry); } } else { AuDbg("positive originally\n"); /* inode may be NULL */ AuDebugOn(inode && (inode->i_mode & S_IFMT) != orig_h.mode); if (!tmp_h.inode) { AuDbg("positive --> negative\n"); /* or bypassing aufs */ au_hide(dentry); if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_bstart) dinfo->di_bwh = tmp->di_bwh; if (inode) err = au_refresh_hinode_self(inode); au_dbg_verify_dinode(dentry); } else if (orig_h.mode == tmp_h.mode) { AuDbg("positive --> positive, same type\n"); if (!S_ISDIR(orig_h.mode) && dinfo->di_bstart > tmp->di_bstart) { /* * similar to the behaviour of removing and * creating. */ au_hide(dentry); if (inode) err = au_refresh_hinode_self(inode); au_dbg_verify_dinode(dentry); } else { /* fill empty slots */ if (dinfo->di_bstart > tmp->di_bstart) dinfo->di_bstart = tmp->di_bstart; if (dinfo->di_bend < tmp->di_bend) dinfo->di_bend = tmp->di_bend; dinfo->di_bwh = tmp->di_bwh; dinfo->di_bdiropq = tmp->di_bdiropq; hd = tmp->di_hdentry; bend = dinfo->di_bend; for (bindex = tmp->di_bstart; bindex <= bend; bindex++) { if (au_h_dptr(dentry, bindex)) continue; h_dentry = hd[bindex].hd_dentry; if (!h_dentry) continue; AuDebugOn(d_is_negative(h_dentry)); h_inode = d_inode(h_dentry); AuDebugOn(orig_h.mode != (h_inode->i_mode & S_IFMT)); au_set_h_dptr(dentry, bindex, dget(h_dentry)); } err = au_refresh_hinode(inode, dentry); au_dbg_verify_dinode(dentry); } } else { AuDbg("positive --> positive, different type\n"); /* similar to the behaviour of removing and creating */ au_hide(dentry); if (inode) err = au_refresh_hinode_self(inode); au_dbg_verify_dinode(dentry); } } out: 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; }