/* * 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 au_test_h_perm_sio(struct inode *h_inode, int mask) { if (au_test_nfs(h_inode->i_sb) && (mask & MAY_WRITE) && S_ISDIR(h_inode->i_mode)) mask |= MAY_READ; /* force permission check */ return au_test_h_perm(h_inode, mask); }
/* * simple tests for the adding inode operations. * following the checks in vfs, plus the parent-child relationship. */ int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, struct dentry *h_parent, int isdir, struct au_ndx *ndx) { int err, exist; struct dentry *h_dentry; struct inode *h_inode; umode_t h_mode; LKTRTrace("%.*s/%.*s, b%d, dir %d\n", AuDLNPair(h_parent), AuDLNPair(dentry), bindex, isdir); exist = !!dentry->d_inode; h_dentry = au_h_dptr(dentry, bindex); h_inode = h_dentry->d_inode; if (!exist) { err = -EEXIST; if (unlikely(h_inode)) goto out; } else { /* rename(2) case */ err = -EIO; if (unlikely(!h_inode || !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; } } err = -EIO; /* expected parent dir is locked */ if (unlikely(h_parent != h_dentry->d_parent)) goto out; err = 0; if (au_opt_test(au_mntflags(dentry->d_sb), UDBA_INOTIFY)) { struct dentry *h_latest; struct qstr *qstr = &dentry->d_name; err = -EACCES; if (unlikely(au_test_h_perm (h_parent->d_inode, MAY_EXEC | MAY_WRITE, au_ftest_ndx(ndx->flags, DLGT)))) goto out; h_latest = au_sio_lkup_one(qstr->name, h_parent, qstr->len, ndx); err = PTR_ERR(h_latest); if (IS_ERR(h_latest)) goto out; err = -EIO; dput(h_latest); /* fuse d_revalidate always return 0 for negative dentries */ if (h_latest == h_dentry || au_test_fuse(h_dentry->d_sb)) err = 0; } out: AuTraceErr(err); return err; }