int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) { int err; unsigned int sigen; struct super_block *sb; sb = d1->d_sb; err = si_read_lock(sb, flags); if (unlikely(err)) goto out; di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR)); if (au_ftest_lock(flags, GEN)) { sigen = au_sigen(sb); err = au_digen_test(d1, sigen); AuDebugOn(!err && au_dbrange_test(d1)); if (!err) { err = au_digen_test(d2, sigen); AuDebugOn(!err && au_dbrange_test(d2)); } if (unlikely(err)) aufs_read_and_write_unlock2(d1, d2); } out: return err; }
static ssize_t aufs_aio_read_sp(struct kiocb *kio, const struct iovec *iov, unsigned long nv, loff_t pos) { ssize_t err; aufs_bindex_t bstart; unsigned char wbr; struct file *file, *h_file; struct super_block *sb; file = kio->ki_filp; sb = file->f_dentry->d_sb; si_read_lock(sb, AuLock_FLUSH); fi_read_lock(file); bstart = au_fbstart(file); h_file = au_hf_top(file); fi_read_unlock(file); wbr = !!au_br_writable(au_sbr(sb, bstart)->br_perm); si_read_unlock(sb); /* do not change the file in kio */ AuDebugOn(!h_file->f_op || !h_file->f_op->aio_read); err = h_file->f_op->aio_read(kio, iov, nv, pos); if (err > 0 && wbr) file_accessed(h_file); return err; }
/* dentry and super_block lock. call at entry point */ int aufs_read_lock(struct dentry *dentry, int flags) { int err; struct super_block *sb; sb = dentry->d_sb; err = si_read_lock(sb, flags); if (unlikely(err)) goto out; if (au_ftest_lock(flags, DW)) di_write_lock_child(dentry); else di_read_lock_child(dentry, flags); if (au_ftest_lock(flags, GEN)) { err = au_digen_test(dentry, au_sigen(sb)); AuDebugOn(!err && au_dbrange_test(dentry)); if (unlikely(err)) aufs_read_unlock(dentry, flags); } out: return err; }
/* dentry and super_block lock. call at entry point */ void aufs_read_lock(struct dentry *dentry, int flags) { si_read_lock(dentry->d_sb); if (flags & AUFS_D_WLOCK) di_write_lock_child(dentry); else di_read_lock_child(dentry, flags); }
/* dentry and super_block lock. call at entry point */ void aufs_read_lock(struct dentry *dentry, int flags) { si_read_lock(dentry->d_sb, flags); if (au_ftest_lock(flags, DW)) di_write_lock_child(dentry); else di_read_lock_child(dentry, flags); }
static int au_ibusy(struct super_block *sb, struct aufs_ibusy __user *arg) { int err; aufs_bindex_t bstart, bend; struct aufs_ibusy ibusy; struct inode *inode, *h_inode; err = -EPERM; if (unlikely(!capable(CAP_SYS_ADMIN))) goto out; err = copy_from_user(&ibusy, arg, sizeof(ibusy)); if (!err) err = !access_ok(VERIFY_WRITE, &arg->h_ino, sizeof(arg->h_ino)); if (unlikely(err)) { err = -EFAULT; AuTraceErr(err); goto out; } err = -EINVAL; si_read_lock(sb, AuLock_FLUSH); if (unlikely(ibusy.bindex < 0 || ibusy.bindex > au_sbend(sb))) goto out_unlock; err = 0; ibusy.h_ino = 0; /* invalid */ inode = ilookup(sb, ibusy.ino); if (!inode || inode->i_ino == AUFS_ROOT_INO || is_bad_inode(inode)) goto out_unlock; ii_read_lock_child(inode); bstart = au_ibstart(inode); bend = au_ibend(inode); if (bstart <= ibusy.bindex && ibusy.bindex <= bend) { h_inode = au_h_iptr(inode, ibusy.bindex); if (h_inode && au_test_ibusy(inode, bstart, bend)) ibusy.h_ino = h_inode->i_ino; } ii_read_unlock(inode); iput(inode); out_unlock: si_read_unlock(sb); if (!err) { err = __put_user(ibusy.h_ino, &arg->h_ino); if (unlikely(err)) { err = -EFAULT; AuTraceErr(err); } } out: return err; }
static int aufs_commit_metadata(struct inode *inode) { int err; aufs_bindex_t bindex; struct super_block *sb; struct inode *h_inode; int (*f)(struct inode *inode); sb = inode->i_sb; si_read_lock(sb, AuLock_FLUSH); ii_write_lock_child(inode); bindex = au_ibstart(inode); AuDebugOn(bindex < 0); h_inode = au_h_iptr(inode, bindex); f = h_inode->i_sb->s_export_op->commit_metadata; if (f) err = f(h_inode); else { struct writeback_control wbc = { .sync_mode = WB_SYNC_ALL, .nr_to_write = 0 /* metadata only */ }; err = sync_inode(h_inode, &wbc); } au_cpup_attr_timesizes(inode); ii_write_unlock(inode); si_read_unlock(sb); return err; } /* ---------------------------------------------------------------------- */ static struct export_operations aufs_export_op = { .fh_to_dentry = aufs_fh_to_dentry, /* .fh_to_parent = aufs_fh_to_parent, */ .encode_fh = aufs_encode_fh, .commit_metadata = aufs_commit_metadata }; void au_export_init(struct super_block *sb) { struct au_sbinfo *sbinfo; __u32 u; sb->s_export_op = &aufs_export_op; sbinfo = au_sbi(sb); sbinfo->si_xigen = NULL; get_random_bytes(&u, sizeof(u)); BUILD_BUG_ON(sizeof(u) != sizeof(int)); atomic_set(&sbinfo->si_xigen_next, u); }
static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct dentry *ret, *parent; struct inode *inode; struct super_block *sb; int err, npositive; IMustLock(dir); sb = dir->i_sb; si_read_lock(sb, AuLock_FLUSH); ret = ERR_PTR(-ENAMETOOLONG); if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) goto out; err = au_di_init(dentry); ret = ERR_PTR(err); if (unlikely(err)) goto out; parent = dentry->d_parent; /* dir inode is locked */ di_read_lock_parent(parent, AuLock_IR); npositive = au_lkup_dentry(dentry, au_dbstart(parent), /*type*/0, nd); di_read_unlock(parent, AuLock_IR); err = npositive; ret = ERR_PTR(err); if (unlikely(err < 0)) goto out_unlock; inode = NULL; if (npositive) { inode = au_new_inode(dentry, /*must_new*/0); ret = (void *)inode; } if (IS_ERR(inode)) goto out_unlock; ret = d_splice_alias(inode, dentry); if (unlikely(IS_ERR(ret) && inode)) ii_write_unlock(inode); out_unlock: di_write_unlock(dentry); out: si_read_unlock(sb); return ret; }
static int au_rdu_ino(struct file *file, struct aufs_rdu *rdu) { int err; ino_t ino; unsigned long long nent; union au_rdu_ent_ul *u; struct au_rdu_ent ent; struct super_block *sb; err = 0; nent = rdu->nent; u = &rdu->ent; sb = file->f_dentry->d_sb; si_read_lock(sb, AuLock_FLUSH); while (nent-- > 0) { err = copy_from_user(&ent, u->e, sizeof(ent)); if (!err) err = !access_ok(VERIFY_WRITE, &u->e->ino, sizeof(ino)); if (unlikely(err)) { err = -EFAULT; AuTraceErr(err); break; } /* AuDbg("b%d, i%llu\n", ent.bindex, ent.ino); */ if (!ent.wh) err = au_ino(sb, ent.bindex, ent.ino, ent.type, &ino); else err = au_wh_ino(sb, ent.bindex, ent.ino, ent.type, &ino); if (unlikely(err)) { AuTraceErr(err); break; } err = __put_user(ino, &u->e->ino); if (unlikely(err)) { err = -EFAULT; AuTraceErr(err); break; } u->ul += au_rdu_len(ent.nlen); } si_read_unlock(sb); return err; }
static aufs_bindex_t si_nfsd_read_lock(struct super_block *sb, struct au_nfsd_si_lock *nsi_lock) { aufs_bindex_t bindex; si_read_lock(sb, AuLock_FLUSH); /* branch id may be wrapped around */ bindex = au_br_index(sb, nsi_lock->br_id); if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb)) goto out; /* success */ if (!nsi_lock->force_lock) si_read_unlock(sb); bindex = -1; out: return bindex; }
int au_do_open(struct file *file, int (*open)(struct file *file, int flags)) { int err; struct dentry *dentry; struct super_block *sb; dentry = file->f_dentry; sb = dentry->d_sb; si_read_lock(sb, AuLock_FLUSH); err = au_finfo_init(file); if (unlikely(err)) goto out; di_read_lock_child(dentry, AuLock_IR); err = open(file, vfsub_file_flags(file)); di_read_unlock(dentry, AuLock_IR); fi_write_unlock(file); if (unlikely(err)) au_finfo_fin(file); out: si_read_unlock(sb); return err; }
static int aufs_permission(struct inode *inode, int mask) { int err; aufs_bindex_t bindex, bend; const unsigned char isdir = !!S_ISDIR(inode->i_mode), write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); struct inode *h_inode; struct super_block *sb; struct au_branch *br; /* todo: support rcu-walk? */ if (mask & MAY_NOT_BLOCK) return -ECHILD; sb = inode->i_sb; si_read_lock(sb, AuLock_FLUSH); ii_read_lock_child(inode); #if 0 err = au_iigen_test(inode, au_sigen(sb)); if (unlikely(err)) goto out; #endif if (!isdir || write_mask) { err = au_busy_or_stale(); h_inode = au_h_iptr(inode, au_ibstart(inode)); if (unlikely(!h_inode || (h_inode->i_mode & S_IFMT) != (inode->i_mode & S_IFMT))) goto out; err = 0; bindex = au_ibstart(inode); br = au_sbr(sb, bindex); err = h_permission(h_inode, mask, br->br_mnt, br->br_perm); if (write_mask && !err && !special_file(h_inode->i_mode)) { /* test whether the upper writable branch exists */ err = -EROFS; for (; bindex >= 0; bindex--) if (!au_br_rdonly(au_sbr(sb, bindex))) { err = 0; break; } } goto out; } /* non-write to dir */ err = 0; bend = au_ibend(inode); for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) { h_inode = au_h_iptr(inode, bindex); if (h_inode) { err = au_busy_or_stale(); if (unlikely(!S_ISDIR(h_inode->i_mode))) break; br = au_sbr(sb, bindex); err = h_permission(h_inode, mask, br->br_mnt, br->br_perm); } } out: ii_read_unlock(inode); si_read_unlock(sb); return err; }
static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct dentry *ret, *parent; struct inode *inode; struct super_block *sb; int err, npositive; IMustLock(dir); /* todo: support rcu-walk? */ ret = ERR_PTR(-ECHILD); if (flags & LOOKUP_RCU) goto out; ret = ERR_PTR(-ENAMETOOLONG); if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) goto out; sb = dir->i_sb; err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ret = ERR_PTR(err); if (unlikely(err)) goto out; err = au_di_init(dentry); ret = ERR_PTR(err); if (unlikely(err)) goto out_si; inode = NULL; npositive = 0; /* suppress a warning */ parent = dentry->d_parent; /* dir inode is locked */ di_read_lock_parent(parent, AuLock_IR); err = au_alive_dir(parent); if (!err) err = au_digen_test(parent, au_sigen(sb)); if (!err) { npositive = au_lkup_dentry(dentry, au_dbstart(parent), /*type*/0); err = npositive; } di_read_unlock(parent, AuLock_IR); ret = ERR_PTR(err); if (unlikely(err < 0)) goto out_unlock; if (npositive) { inode = au_new_inode(dentry, /*must_new*/0); if (IS_ERR(inode)) { ret = (void *)inode; inode = NULL; goto out_unlock; } } if (inode) atomic_inc(&inode->i_count); ret = d_splice_alias(inode, dentry); #if 0 if (unlikely(d_need_lookup(dentry))) { spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_NEED_LOOKUP; spin_unlock(&dentry->d_lock); } else #endif if (inode) { if (!IS_ERR(ret)) { iput(inode); if (ret && ret != dentry) ii_write_unlock(inode); } else { ii_write_unlock(inode); iput(inode); inode = NULL; } } out_unlock: di_write_unlock(dentry); if (inode) { /* verbose coding for lock class name */ if (unlikely(S_ISLNK(inode->i_mode))) au_rw_class(&au_di(dentry)->di_rwsem, au_lc_key + AuLcSymlink_DIINFO); else if (unlikely(S_ISDIR(inode->i_mode))) au_rw_class(&au_di(dentry)->di_rwsem, au_lc_key + AuLcDir_DIINFO); else /* likely */ au_rw_class(&au_di(dentry)->di_rwsem, au_lc_key + AuLcNonDir_DIINFO); } out_si: si_read_unlock(sb); out: return ret; }
static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct dentry *ret, *parent; struct inode *inode; struct super_block *sb; int err, npositive, lc_idx; IMustLock(dir); sb = dir->i_sb; err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ret = ERR_PTR(err); if (unlikely(err)) goto out; ret = ERR_PTR(-ENAMETOOLONG); if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) goto out_si; err = au_di_init(dentry); ret = ERR_PTR(err); if (unlikely(err)) goto out_si; inode = NULL; npositive = 0; /* suppress a warning */ parent = dentry->d_parent; /* dir inode is locked */ di_read_lock_parent(parent, AuLock_IR); err = au_alive_dir(parent); if (!err) err = au_digen_test(parent, au_sigen(sb)); if (!err) { npositive = au_lkup_dentry(dentry, au_dbstart(parent), /*type*/0, nd); err = npositive; } di_read_unlock(parent, AuLock_IR); ret = ERR_PTR(err); if (unlikely(err < 0)) goto out_unlock; if (npositive) { inode = au_new_inode(dentry, /*must_new*/0); ret = (void *)inode; } if (IS_ERR(inode)) { inode = NULL; goto out_unlock; } ret = d_splice_alias(inode, dentry); if (unlikely(IS_ERR(ret) && inode)) { ii_write_unlock(inode); lc_idx = AuLcNonDir_IIINFO; if (S_ISLNK(inode->i_mode)) lc_idx = AuLcSymlink_IIINFO; else if (S_ISDIR(inode->i_mode)) lc_idx = AuLcDir_IIINFO; au_rw_class(&au_ii(inode)->ii_rwsem, au_lc_key + lc_idx); iput(inode); } out_unlock: di_write_unlock(dentry); if (unlikely(IS_ERR(ret) && inode)) { lc_idx = AuLcNonDir_DIINFO; if (S_ISLNK(inode->i_mode)) lc_idx = AuLcSymlink_DIINFO; else if (S_ISDIR(inode->i_mode)) lc_idx = AuLcDir_DIINFO; au_rw_class(&au_di(dentry)->di_rwsem, au_lc_key + lc_idx); } out_si: si_read_unlock(sb); out: return ret; }
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; }
void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) { si_read_lock(d1->d_sb, flags); di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR)); }
void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int isdir) { AuDebugOn(d1 == d2 || d1->d_sb != d2->d_sb); si_read_lock(d1->d_sb); di_write_lock2_child(d1, d2, isdir); }
static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct dentry *ret, *parent; struct inode *inode; struct super_block *sb; int err, npositive; IMustLock(dir); /* todo: support rcu-walk? */ ret = ERR_PTR(-ECHILD); if (flags & LOOKUP_RCU) goto out; ret = ERR_PTR(-ENAMETOOLONG); if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) goto out; sb = dir->i_sb; err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ret = ERR_PTR(err); if (unlikely(err)) goto out; err = au_di_init(dentry); ret = ERR_PTR(err); if (unlikely(err)) goto out_si; inode = NULL; npositive = 0; /* suppress a warning */ parent = dentry->d_parent; /* dir inode is locked */ di_read_lock_parent(parent, AuLock_IR); err = au_alive_dir(parent); if (!err) err = au_digen_test(parent, au_sigen(sb)); if (!err) { /* regardless LOOKUP_CREATE, always ALLOW_NEG */ npositive = au_lkup_dentry(dentry, au_dbtop(parent), AuLkup_ALLOW_NEG); err = npositive; } di_read_unlock(parent, AuLock_IR); ret = ERR_PTR(err); if (unlikely(err < 0)) goto out_unlock; if (npositive) { inode = au_new_inode(dentry, /*must_new*/0); if (IS_ERR(inode)) { ret = (void *)inode; inode = NULL; goto out_unlock; } } if (inode) atomic_inc(&inode->i_count); ret = d_splice_alias(inode, dentry); #if 0 if (unlikely(d_need_lookup(dentry))) { spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_NEED_LOOKUP; spin_unlock(&dentry->d_lock); } else #endif if (inode) { if (!IS_ERR(ret)) { iput(inode); if (ret && ret != dentry) ii_write_unlock(inode); } else { ii_write_unlock(inode); iput(inode); inode = NULL; } } out_unlock: di_write_unlock(dentry); out_si: si_read_unlock(sb); out: return ret; }
int au_do_open(struct inode *inode, struct file *file, int (*open)(struct file *file, int flags)) { int err, coo; struct dentry *dentry; struct super_block *sb; aufs_bindex_t bstart; struct inode *h_dir, *dir; dentry = file->f_dentry; LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(dentry)); sb = dentry->d_sb; si_read_lock(sb); coo = 0; #if 0 switch (au_flag_test_coo(sb)) { case AuFlag_COO_LEAF: coo = !S_ISDIR(inode->i_mode); break; case AuFlag_COO_ALL: coo = 1; break; } #endif err = au_init_finfo(file); //if (LktrCond) {fi_write_unlock(file); fin_finfo(file); err = -1;} if (unlikely(err)) goto out; if (!coo) { di_read_lock_child(dentry, AUFS_I_RLOCK); bstart = dbstart(dentry); } else { di_write_lock_child(dentry); bstart = dbstart(dentry); if (test_ro(sb, bstart, dentry->d_inode)) { err = do_coo(dentry, bstart); if (err) { di_write_unlock(dentry); goto out_finfo; } bstart = dbstart(dentry); } di_downgrade_lock(dentry, AUFS_I_RLOCK); } // todo: remove this extra locks dir = dentry->d_parent->d_inode; if (!IS_ROOT(dentry)) ii_read_lock_parent(dir); h_dir = au_h_iptr_i(dir, bstart); hdir_lock(h_dir, dir, bstart); err = open(file, file->f_flags); //if (LktrCond) err = -1; hdir_unlock(h_dir, dir, bstart); if (!IS_ROOT(dentry)) ii_read_unlock(dir); di_read_unlock(dentry, AUFS_I_RLOCK); out_finfo: fi_write_unlock(file); if (unlikely(err)) au_fin_finfo(file); //DbgFile(file); out: si_read_unlock(sb); TraceErr(err); return err; }