int iterate_dir(struct file *file, struct dir_context *ctx) { struct inode *inode = file_inode(file); int res = -ENOTDIR; if (!file->f_op->iterate) goto out; res = security_file_permission(file, MAY_READ); if (res) goto out; res = mutex_lock_killable(&inode->i_mutex); if (res) goto out; res = -ENOENT; if (!IS_DEADDIR(inode)) { ctx->pos = file->f_pos; res = file->f_op->iterate(file, ctx); file->f_pos = ctx->pos; fsnotify_access(file); file_accessed(file); } mutex_unlock(&inode->i_mutex); out: return res; }
static int coda_readdir(struct file *coda_file, void *buf, filldir_t filldir) { struct coda_file_info *cfi; struct file *host_file; int ret; cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; if (!host_file->f_op) return -ENOTDIR; if (host_file->f_op->readdir) { struct inode *host_inode = host_file->f_path.dentry->d_inode; mutex_lock(&host_inode->i_mutex); host_file->f_pos = coda_file->f_pos; ret = -ENOENT; if (!IS_DEADDIR(host_inode)) { ret = host_file->f_op->readdir(host_file, buf, filldir); file_accessed(host_file); } coda_file->f_pos = host_file->f_pos; mutex_unlock(&host_inode->i_mutex); } else ret = coda_venus_readdir(coda_file, buf, filldir); return ret; }
int vfs_readdir64(struct file *file, filldir64_t filler, void *buf) { struct file_operations_ext *fxops; struct inode *inode = file->f_dentry->d_inode; int res = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) goto out; res = -ENOSYS; if (!IS_INO64(inode)) goto out; res = security_file_permission(file, MAY_READ); if (res) goto out; fxops = (struct file_operations_ext *) file->f_op; down(&inode->i_sem); res = -ENOENT; if (!IS_DEADDIR(inode)) { res = fxops->readdir64(file, buf, filler); file_accessed(file); } up(&inode->i_sem); out: return res; }
/* copy of may_create in fs/namei.c() */ static inline int btrfs_may_create(struct inode *dir, struct dentry *child) { if (child->d_inode) return -EEXIST; if (IS_DEADDIR(dir)) return -ENOENT; return inode_permission(dir, MAY_WRITE | MAY_EXEC); }
static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, ino_t dir_ino) { struct dentry *dentry, *d; struct inode *inode; au_gen_t sigen; LKTRTrace("i%lu, diri%lu\n", (unsigned long)ino, (unsigned long)dir_ino); 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))) goto out_iput; dentry = NULL; if (!dir_ino || S_ISDIR(inode->i_mode)) dentry = d_find_alias(inode); else { spin_lock(&dcache_lock); list_for_each_entry(d, &inode->i_dentry, d_alias) if (!au_test_anon(d) && d->d_parent->d_inode->i_ino == dir_ino) { dentry = dget_locked(d); break; } spin_unlock(&dcache_lock); } if (unlikely(dentry && sigen != au_digen(dentry))) { dput(dentry); dentry = ERR_PTR(-ESTALE); } out_iput: iput(inode); out: AuTraceErrPtr(dentry); return dentry; }
int vfs_readdir(struct file *file, filldir_t filler, void *buf) { struct inode *inode = file->f_dentry->d_inode; int res = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) goto out; res = security_file_permission(file, MAY_READ); if (res) goto out; down(&inode->i_sem); res = -ENOENT; if (!IS_DEADDIR(inode)) { res = file->f_op->readdir(file, buf, filler); } up(&inode->i_sem); out: return res; }
/* file operations for directories */ int coda_readdir(struct file *coda_file, void *dirent, filldir_t filldir) { struct dentry *coda_dentry = coda_file->f_path.dentry; struct coda_file_info *cfi; struct file *host_file; struct inode *host_inode; int ret; cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; coda_vfs_stat.readdir++; host_inode = host_file->f_path.dentry->d_inode; mutex_lock(&host_inode->i_mutex); host_file->f_pos = coda_file->f_pos; if (!host_file->f_op->readdir) { /* Venus: we must read Venus dirents from the file */ ret = coda_venus_readdir(host_file, filldir, dirent, coda_dentry); } else { /* potemkin case: we were handed a directory inode. */ /* Yuk, we can't call vfs_readdir because we are already * holding the inode semaphore. */ ret = -ENOTDIR; if (!host_file->f_op || !host_file->f_op->readdir) goto out; ret = -ENOENT; if (!IS_DEADDIR(host_inode)) { ret = host_file->f_op->readdir(host_file, filldir, dirent); file_accessed(host_file); } } out: coda_file->f_pos = host_file->f_pos; mutex_unlock(&host_inode->i_mutex); return ret; }
int iterate_dir(struct file *file, struct dir_context *ctx) { struct inode *inode = file_inode(file); bool shared = false; int res = -ENOTDIR; if (file->f_op->iterate_shared) shared = true; else if (!file->f_op->iterate) goto out; res = security_file_permission(file, MAY_READ); if (res) goto out; if (shared) res = down_read_killable(&inode->i_rwsem); else res = down_write_killable(&inode->i_rwsem); if (res) goto out; res = -ENOENT; if (!IS_DEADDIR(inode)) { ctx->pos = file->f_pos; if (shared) res = file->f_op->iterate_shared(file, ctx); else res = file->f_op->iterate(file, ctx); file->f_pos = ctx->pos; fsnotify_access(file); file_accessed(file); } if (shared) inode_unlock_shared(inode); else inode_unlock(inode); out: return res; }
/* file operations for directories */ static int coda_readdir(struct file *coda_file, void *buf, filldir_t filldir) { struct coda_file_info *cfi; struct file *host_file; int ret; cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; if (!host_file->f_op) return -ENOTDIR; if (host_file->f_op->readdir) { /* potemkin case: we were handed a directory inode. * We can't use vfs_readdir because we have to keep the file * position in sync between the coda_file and the host_file. * and as such we need grab the inode mutex. */ struct inode *host_inode = host_file->f_path.dentry->d_inode; mutex_lock(&host_inode->i_mutex); host_file->f_pos = coda_file->f_pos; ret = -ENOENT; if (!IS_DEADDIR(host_inode)) { ret = host_file->f_op->readdir(host_file, buf, filldir); file_accessed(host_file); } coda_file->f_pos = host_file->f_pos; mutex_unlock(&host_inode->i_mutex); } else /* Venus: we must read Venus dirents from a file */ ret = coda_venus_readdir(coda_file, buf, filldir); return ret; }
int vfs_readdir(struct file *file, filldir_t filler, void *buf) { struct inode *inode = file->f_path.dentry->d_inode; int res = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) goto out; res = security_file_permission(file, MAY_READ); if (res) goto out; mutex_lock(&inode->i_mutex); res = -ENOENT; if (!IS_DEADDIR(inode)) { res = file->f_op->readdir(file, buf, filler); #ifndef CONFIG_FS_ALL_NOATIME file_accessed(file); #endif } mutex_unlock(&inode->i_mutex); out: return res; }
/* file operations for directories */ static int coda_readdir(struct file *coda_file, struct dir_context *ctx) { struct coda_file_info *cfi; struct file *host_file; int ret; cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); host_file = cfi->cfi_container; if (host_file->f_op->iterate) { struct inode *host_inode = file_inode(host_file); mutex_lock(&host_inode->i_mutex); ret = -ENOENT; if (!IS_DEADDIR(host_inode)) { ret = host_file->f_op->iterate(host_file, ctx); file_accessed(host_file); } mutex_unlock(&host_inode->i_mutex); return ret; } /* Venus: we must read Venus dirents from a file */ return coda_venus_readdir(coda_file, ctx); }
int vfs_readdir(struct file *file, filldir_t filler, void *buf) { struct inode *inode = file_inode(file); int res = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) goto out; res = security_file_permission(file, MAY_READ); if (res) goto out; res = mutex_lock_killable(&inode->i_mutex); if (res) goto out; res = -ENOENT; if (!IS_DEADDIR(inode)) { res = file->f_op->readdir(file, buf, filler); file_accessed(file); } mutex_unlock(&inode->i_mutex); out: return res; }
int ncp_notify_change(struct dentry *dentry, struct iattr *attr) { struct inode *inode = d_inode(dentry); int result = 0; __le32 info_mask; struct nw_modify_dos_info info; struct ncp_server *server; result = -EIO; server = NCP_SERVER(inode); if (!server) /* How this could happen? */ goto out; result = -EPERM; if (IS_DEADDIR(d_inode(dentry))) goto out; /* ageing the dentry to force validation */ ncp_age_dentry(server, dentry); result = inode_change_ok(inode, attr); if (result < 0) goto out; result = -EPERM; if ((attr->ia_valid & ATTR_UID) && !uid_eq(attr->ia_uid, server->m.uid)) goto out; if ((attr->ia_valid & ATTR_GID) && !gid_eq(attr->ia_gid, server->m.gid)) goto out; if (((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXUGO)))) goto out; info_mask = 0; memset(&info, 0, sizeof(info)); #if 1 if ((attr->ia_valid & ATTR_MODE) != 0) { umode_t newmode = attr->ia_mode; info_mask |= DM_ATTRIBUTES; if (S_ISDIR(inode->i_mode)) { newmode &= server->m.dir_mode; } else { #ifdef CONFIG_NCPFS_EXTRAS if (server->m.flags & NCP_MOUNT_EXTRAS) { /* any non-default execute bit set */ if (newmode & ~server->m.file_mode & S_IXUGO) info.attributes |= aSHARED | aSYSTEM; /* read for group/world and not in default file_mode */ else if (newmode & ~server->m.file_mode & S_IRUGO) info.attributes |= aSHARED; } else #endif newmode &= server->m.file_mode; } if (newmode & S_IWUGO) info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); else info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); #ifdef CONFIG_NCPFS_NFS_NS if (ncp_is_nfs_extras(server, NCP_FINFO(inode)->volNumber)) { result = ncp_modify_nfs_info(server, NCP_FINFO(inode)->volNumber, NCP_FINFO(inode)->dirEntNum, attr->ia_mode, 0); if (result != 0) goto out; info.attributes &= ~(aSHARED | aSYSTEM); { /* mark partial success */ struct iattr tmpattr; tmpattr.ia_valid = ATTR_MODE; tmpattr.ia_mode = attr->ia_mode; setattr_copy(inode, &tmpattr); mark_inode_dirty(inode); } } #endif } #endif /* Do SIZE before attributes, otherwise mtime together with size does not work... */ if ((attr->ia_valid & ATTR_SIZE) != 0) { int written; ncp_dbg(1, "trying to change size to %llu\n", attr->ia_size); if ((result = ncp_make_open(inode, O_WRONLY)) < 0) { result = -EACCES; goto out; } ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, attr->ia_size, 0, "", &written); /* According to ndir, the changes only take effect after closing the file */ ncp_inode_close(inode); result = ncp_make_closed(inode); if (result) goto out; if (attr->ia_size != i_size_read(inode)) { truncate_setsize(inode, attr->ia_size); mark_inode_dirty(inode); } } if ((attr->ia_valid & ATTR_CTIME) != 0) { info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE); ncp_date_unix2dos(attr->ia_ctime.tv_sec, &info.creationTime, &info.creationDate); } if ((attr->ia_valid & ATTR_MTIME) != 0) { info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE); ncp_date_unix2dos(attr->ia_mtime.tv_sec, &info.modifyTime, &info.modifyDate); } if ((attr->ia_valid & ATTR_ATIME) != 0) { __le16 dummy; info_mask |= (DM_LAST_ACCESS_DATE); ncp_date_unix2dos(attr->ia_atime.tv_sec, &dummy, &info.lastAccessDate); } if (info_mask != 0) { result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode), inode, info_mask, &info); if (result != 0) { if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) { /* NetWare seems not to allow this. I do not know why. So, just tell the user everything went fine. This is a terrible hack, but I do not know how to do this correctly. */ result = 0; } else goto out; } #ifdef CONFIG_NCPFS_STRONG if ((!result) && (info_mask & DM_ATTRIBUTES)) NCP_FINFO(inode)->nwattr = info.attributes; #endif } if (result) goto out; setattr_copy(inode, attr); mark_inode_dirty(inode); out: if (result > 0) result = -EACCES; return result; }
int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, struct inode *_dst_dir, struct dentry *_dst_dentry) { int err, flags; /* reduce stack space */ struct au_ren_args *a; AuDbg("%.*s, %.*s\n", AuDLNPair(_src_dentry), AuDLNPair(_dst_dentry)); IMustLock(_src_dir); IMustLock(_dst_dir); err = -ENOMEM; BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); a = kzalloc(sizeof(*a), GFP_NOFS); if (unlikely(!a)) goto out; a->src_dir = _src_dir; a->src_dentry = _src_dentry; a->src_inode = a->src_dentry->d_inode; a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ a->dst_dir = _dst_dir; a->dst_dentry = _dst_dentry; a->dst_inode = a->dst_dentry->d_inode; a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ if (a->dst_inode) { IMustLock(a->dst_inode); au_igrab(a->dst_inode); } err = -ENOTDIR; flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; if (S_ISDIR(a->src_inode->i_mode)) { au_fset_ren(a->flags, ISDIR); if (unlikely(a->dst_inode && !S_ISDIR(a->dst_inode->i_mode))) goto out_free; err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, AuLock_DIR | flags); } else err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, flags); if (unlikely(err)) goto out_free; err = au_d_hashed_positive(a->src_dentry); if (unlikely(err)) goto out_unlock; err = -ENOENT; if (a->dst_inode) { /* * If it is a dir, VFS unhash dst_dentry before this * function. It means we cannot rely upon d_unhashed(). */ if (unlikely(!a->dst_inode->i_nlink)) goto out_unlock; if (!S_ISDIR(a->dst_inode->i_mode)) { err = au_d_hashed_positive(a->dst_dentry); if (unlikely(err)) goto out_unlock; } else if (unlikely(IS_DEADDIR(a->dst_inode))) goto out_unlock; } else if (unlikely(d_unhashed(a->dst_dentry))) goto out_unlock; au_fset_ren(a->flags, ISSAMEDIR); /* temporary */ di_write_lock_parent(a->dst_parent); /* which branch we process */ err = au_ren_wbr(a); if (unlikely(err < 0)) goto out_parent; a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); a->h_path.mnt = a->br->br_mnt; /* are they available to be renamed */ err = au_ren_may_dir(a); if (unlikely(err)) goto out_children; /* prepare the writable parent dir on the same branch */ if (a->dst_bstart == a->btgt) { au_fset_ren(a->flags, WHDST); } else { err = au_cpup_dirs(a->dst_dentry, a->btgt); if (unlikely(err)) goto out_children; } if (a->src_dir != a->dst_dir) { /* * this temporary unlock is safe, * because both dir->i_mutex are locked. */ di_write_unlock(a->dst_parent); di_write_lock_parent(a->src_parent); err = au_wr_dir_need_wh(a->src_dentry, au_ftest_ren(a->flags, ISDIR), &a->btgt); di_write_unlock(a->src_parent); di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1); au_fclr_ren(a->flags, ISSAMEDIR); } else err = au_wr_dir_need_wh(a->src_dentry, au_ftest_ren(a->flags, ISDIR), &a->btgt); if (unlikely(err < 0)) goto out_children; if (err) au_fset_ren(a->flags, WHSRC); /* lock them all */ err = au_ren_lock(a); if (unlikely(err)) goto out_children; if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) err = au_may_ren(a); else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) err = -ENAMETOOLONG; if (unlikely(err)) goto out_hdir; /* store timestamps to be revertible */ au_ren_dt(a); /* here we go */ err = do_rename(a); if (unlikely(err)) goto out_dt; /* update dir attributes */ au_ren_refresh_dir(a); /* dput/iput all lower dentries */ au_ren_refresh(a); goto out_hdir; /* success */ out_dt: au_ren_rev_dt(err, a); out_hdir: au_ren_unlock(a); out_children: au_nhash_wh_free(&a->whlist); if (err && a->dst_inode && a->dst_bstart != a->btgt) { AuDbg("bstart %d, btgt %d\n", a->dst_bstart, a->btgt); au_set_h_dptr(a->dst_dentry, a->btgt, NULL); au_set_dbstart(a->dst_dentry, a->dst_bstart); } out_parent: if (!err) d_move(a->src_dentry, a->dst_dentry); else { au_update_dbstart(a->dst_dentry); if (!a->dst_inode) d_drop(a->dst_dentry); } if (au_ftest_ren(a->flags, ISSAMEDIR)) di_write_unlock(a->dst_parent); else di_write_unlock2(a->src_parent, a->dst_parent); out_unlock: aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); out_free: iput(a->dst_inode); if (a->thargs) au_whtmp_rmdir_free(a->thargs); kfree(a); out: AuTraceErr(err); return err; }
/* todo: return with unlocked? */ struct inode *au_new_inode(struct dentry *dentry, int must_new) { struct inode *inode, *h_inode; struct dentry *h_dentry; struct super_block *sb; struct mutex *mtx; ino_t h_ino, ino; int err; aufs_bindex_t bstart; sb = dentry->d_sb; bstart = au_dbstart(dentry); h_dentry = au_h_dptr(dentry, bstart); h_inode = h_dentry->d_inode; h_ino = h_inode->i_ino; /* * stop 'race'-ing between hardlinks under different * parents. */ mtx = NULL; if (!S_ISDIR(h_inode->i_mode)) mtx = &au_sbr(sb, bstart)->br_xino.xi_nondir_mtx; new_ino: if (mtx) mutex_lock(mtx); err = au_xino_read(sb, bstart, h_ino, &ino); inode = ERR_PTR(err); if (unlikely(err)) goto out; if (!ino) { ino = au_xino_new_ino(sb); if (unlikely(!ino)) { inode = ERR_PTR(-EIO); goto out; } } AuDbg("i%lu\n", (unsigned long)ino); inode = au_iget_locked(sb, ino); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out; AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); if (inode->i_state & I_NEW) { /* verbose coding for lock class name */ if (unlikely(S_ISLNK(h_inode->i_mode))) au_rw_class(&au_ii(inode)->ii_rwsem, au_lc_key + AuLcSymlink_IIINFO); else if (unlikely(S_ISDIR(h_inode->i_mode))) au_rw_class(&au_ii(inode)->ii_rwsem, au_lc_key + AuLcDir_IIINFO); else /* likely */ au_rw_class(&au_ii(inode)->ii_rwsem, au_lc_key + AuLcNonDir_IIINFO); ii_write_lock_new_child(inode); err = set_inode(inode, dentry); if (!err) { unlock_new_inode(inode); goto out; /* success */ } /* * iget_failed() calls iput(), but we need to call * ii_write_unlock() after iget_failed(). so dirty hack for * i_count. */ atomic_inc(&inode->i_count); iget_failed(inode); ii_write_unlock(inode); au_xino_write(sb, bstart, h_ino, /*ino*/0); /* ignore this error */ goto out_iput; } else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) { /* * horrible race condition between lookup, readdir and copyup * (or something). */ if (mtx) mutex_unlock(mtx); err = reval_inode(inode, dentry); if (unlikely(err < 0)) { mtx = NULL; goto out_iput; } if (!err) { mtx = NULL; goto out; /* success */ } else if (mtx) mutex_lock(mtx); } if (unlikely(au_test_fs_unique_ino(h_dentry->d_inode))) AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir," " b%d, %s, %pd, hi%lu, i%lu.\n", bstart, au_sbtype(h_dentry->d_sb), dentry, (unsigned long)h_ino, (unsigned long)ino); ino = 0; err = au_xino_write(sb, bstart, h_ino, /*ino*/0); if (!err) { iput(inode); if (mtx) mutex_unlock(mtx); goto new_ino; } out_iput: iput(inode); inode = ERR_PTR(err); out: if (mtx) mutex_unlock(mtx); return inode; }
static int au_rdu(struct file *file, struct aufs_rdu *rdu) { int err; aufs_bindex_t bend; struct au_rdu_arg arg; struct dentry *dentry; struct inode *inode; struct file *h_file; struct au_rdu_cookie *cookie = &rdu->cookie; err = !access_ok(VERIFY_WRITE, rdu->ent.e, rdu->sz); if (unlikely(err)) { err = -EFAULT; AuTraceErr(err); goto out; } rdu->rent = 0; rdu->tail = rdu->ent; rdu->full = 0; arg.rdu = rdu; arg.ent = rdu->ent; arg.end = arg.ent.ul; arg.end += rdu->sz; err = -ENOTDIR; if (unlikely(!file->f_op || !file->f_op->readdir)) goto out; err = security_file_permission(file, MAY_READ); AuTraceErr(err); if (unlikely(err)) goto out; dentry = file->f_dentry; inode = dentry->d_inode; #if 1 mutex_lock(&inode->i_mutex); #else err = mutex_lock_killable(&inode->i_mutex); AuTraceErr(err); if (unlikely(err)) goto out; #endif err = -ENOENT; if (unlikely(IS_DEADDIR(inode))) goto out_mtx; arg.sb = inode->i_sb; si_read_lock(arg.sb, AuLock_FLUSH); fi_read_lock(file); err = -EAGAIN; if (unlikely(au_ftest_rdu(cookie->flags, CONT) && cookie->generation != au_figen(file))) goto out_unlock; err = 0; if (!rdu->blk) { rdu->blk = au_sbi(arg.sb)->si_rdblk; if (!rdu->blk) rdu->blk = au_dir_size(file, /*dentry*/NULL); } bend = au_fbstart(file); if (cookie->bindex < bend) cookie->bindex = bend; bend = au_fbend_dir(file); /* AuDbg("b%d, b%d\n", cookie->bindex, bend); */ for (; !err && cookie->bindex <= bend; cookie->bindex++, cookie->h_pos = 0) { h_file = au_hf_dir(file, cookie->bindex); if (!h_file) continue; au_fclr_rdu(cookie->flags, FULL); err = au_rdu_do(h_file, &arg); AuTraceErr(err); if (unlikely(au_ftest_rdu(cookie->flags, FULL) || err)) break; } AuDbg("rent %llu\n", rdu->rent); if (!err && !au_ftest_rdu(cookie->flags, CONT)) { rdu->shwh = !!au_opt_test(au_sbi(arg.sb)->si_mntflags, SHWH); au_fset_rdu(cookie->flags, CONT); cookie->generation = au_figen(file); } ii_read_lock_child(inode); fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibstart(inode))); ii_read_unlock(inode); out_unlock: fi_read_unlock(file); si_read_unlock(arg.sb); out_mtx: mutex_unlock(&inode->i_mutex); out: AuTraceErr(err); return err; }
/* * if valid returns 1, otherwise 0. */ static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { int valid, err; unsigned int sigen; unsigned char do_udba; struct super_block *sb; struct inode *inode; valid = 0; if (unlikely(!au_di(dentry))) goto out; valid = 1; sb = dentry->d_sb; inode = dentry->d_inode; /* * todo: very ugly * i_mutex of parent dir may be held, * but we should not return 'invalid' due to busy. */ err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); if (unlikely(err)) { valid = err; AuTraceErr(err); goto out; } if (unlikely(au_dbrange_test(dentry))) { err = -EINVAL; AuTraceErr(err); goto out_dgrade; } sigen = au_sigen(sb); if (au_digen_test(dentry, sigen)) { AuDebugOn(IS_ROOT(dentry)); err = au_reval_dpath(dentry, sigen); if (unlikely(err)) { AuTraceErr(err); goto out_dgrade; } } di_downgrade_lock(dentry, AuLock_IR); err = -EINVAL; if (inode && (IS_DEADDIR(inode) || !inode->i_nlink)) goto out_inval; do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); if (do_udba && inode) { aufs_bindex_t bstart = au_ibstart(inode); struct inode *h_inode; if (bstart >= 0) { h_inode = au_h_iptr(inode, bstart); if (h_inode && au_test_higen(inode, h_inode)) goto out_inval; } } err = h_d_revalidate(dentry, inode, nd, do_udba); if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { err = -EIO; AuDbg("both of real entry and whiteout found, %.*s, err %d\n", AuDLNPair(dentry), err); } goto out_inval; out_dgrade: di_downgrade_lock(dentry, AuLock_IR); out_inval: aufs_read_unlock(dentry, AuLock_IR); AuTraceErr(err); valid = !err; out: if (!valid) { AuDbg("%.*s invalid, %d\n", AuDLNPair(dentry), valid); d_drop(dentry); } return valid; }
/* * if valid returns 1, otherwise 0. */ static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags) { int valid, err; unsigned int sigen; unsigned char do_udba; struct super_block *sb; struct inode *inode; /* todo: support rcu-walk? */ if (flags & LOOKUP_RCU) return -ECHILD; valid = 0; if (unlikely(!au_di(dentry))) goto out; valid = 1; sb = dentry->d_sb; /* * todo: very ugly * i_mutex of parent dir may be held, * but we should not return 'invalid' due to busy. */ err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); if (unlikely(err)) { valid = err; AuTraceErr(err); goto out; } inode = NULL; if (d_really_is_positive(dentry)) inode = d_inode(dentry); if (unlikely(inode && is_bad_inode(inode))) { err = -EINVAL; AuTraceErr(err); goto out_dgrade; } if (unlikely(au_dbrange_test(dentry))) { err = -EINVAL; AuTraceErr(err); goto out_dgrade; } sigen = au_sigen(sb); if (au_digen_test(dentry, sigen)) { AuDebugOn(IS_ROOT(dentry)); err = au_reval_dpath(dentry, sigen); if (unlikely(err)) { AuTraceErr(err); goto out_dgrade; } } di_downgrade_lock(dentry, AuLock_IR); err = -EINVAL; if (!(flags & (LOOKUP_OPEN | LOOKUP_EMPTY)) && inode && !(inode->i_state && I_LINKABLE) && (IS_DEADDIR(inode) || !inode->i_nlink)) goto out_inval; do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); if (do_udba && inode) { aufs_bindex_t bstart = au_ibstart(inode); struct inode *h_inode; if (bstart >= 0) { h_inode = au_h_iptr(inode, bstart); if (h_inode && au_test_higen(inode, h_inode)) goto out_inval; } } err = h_d_revalidate(dentry, inode, flags, do_udba); if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { err = -EIO; AuDbg("both of real entry and whiteout found, %p, err %d\n", dentry, err); } goto out_inval; out_dgrade: di_downgrade_lock(dentry, AuLock_IR); out_inval: aufs_read_unlock(dentry, AuLock_IR); AuTraceErr(err); valid = !err; out: if (!valid) { AuDbg("%pd invalid, %d\n", dentry, valid); d_drop(dentry); } return valid; }
int aufs_rmdir(struct inode *dir, struct dentry *dentry) { int err, rmdir_later; aufs_bindex_t bwh, bindex, bstart; struct au_dtime dt; struct au_pin pin; struct inode *inode; struct dentry *parent, *wh_dentry, *h_dentry; struct au_whtmp_rmdir *args; IMustLock(dir); err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); if (unlikely(err)) goto out; /* VFS already unhashes it */ inode = dentry->d_inode; err = -ENOENT; if (unlikely(!inode || !inode->i_nlink || IS_DEADDIR(inode))) goto out_unlock; IMustLock(inode); err = -ENOTDIR; if (unlikely(!S_ISDIR(inode->i_mode))) goto out_unlock; /* possible? */ err = -ENOMEM; args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); if (unlikely(!args)) goto out_unlock; parent = dentry->d_parent; /* dir inode is locked */ di_write_lock_parent(parent); err = au_test_empty(dentry, &args->whlist); if (unlikely(err)) goto out_parent; bstart = au_dbstart(dentry); bwh = au_dbwh(dentry); bindex = -1; wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &dt, &pin); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) goto out_parent; h_dentry = au_h_dptr(dentry, bstart); dget(h_dentry); rmdir_later = 0; if (bindex == bstart) { err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir); if (err > 0) { rmdir_later = err; err = 0; } } else { /* stop monitoring */ au_hn_free(au_hi(inode, bstart)); /* dir inode is locked */ IMustLock(wh_dentry->d_parent->d_inode); err = 0; } if (!err) { vfsub_dead_dir(inode); au_set_dbdiropq(dentry, -1); epilog(dir, dentry, bindex); if (rmdir_later) { au_whtmp_kick_rmdir(dir, bstart, h_dentry, args); args = NULL; } goto out_unpin; /* success */ } /* revert */ AuLabel(revert); if (wh_dentry) { int rerr; rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &dt); if (rerr) err = rerr; } out_unpin: au_unpin(&pin); dput(wh_dentry); dput(h_dentry); out_parent: di_write_unlock(parent); if (args) au_whtmp_rmdir_free(args); out_unlock: aufs_read_unlock(dentry, AuLock_DW); out: AuTraceErr(err); return err; }