/* 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); }
/* common function to regular file and dir */ int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), int wlock) { int err; unsigned int sigen, figen; aufs_bindex_t bstart; unsigned char pseudo_link; struct dentry *dentry; struct inode *inode; err = 0; dentry = file->f_dentry; inode = dentry->d_inode; AuDebugOn(au_special_file(inode->i_mode)); sigen = au_sigen(dentry->d_sb); fi_write_lock(file); figen = au_figen(file); di_write_lock_child(dentry); bstart = au_dbstart(dentry); pseudo_link = (bstart != au_ibstart(inode)); if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) { if (!wlock) { di_downgrade_lock(dentry, AuLock_IR); fi_downgrade_lock(file); } goto out; /* success */ } AuDbg("sigen %d, figen %d\n", sigen, figen); if (sigen != au_digen(dentry) || sigen != au_iigen(inode)) { err = au_reval_dpath(dentry, sigen); if (unlikely(err < 0)) goto out; AuDebugOn(au_digen(dentry) != sigen || au_iigen(inode) != sigen); } err = refresh_file(file, reopen); if (!err) { if (!wlock) { di_downgrade_lock(dentry, AuLock_IR); fi_downgrade_lock(file); } } else { di_write_unlock(dentry); fi_write_unlock(file); } out: return err; }
int au_reval_dpath(struct dentry *dentry, unsigned int sigen) { int err; struct dentry *d, *parent; struct inode *inode; if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIRS)) return simple_reval_dpath(dentry, sigen); /* slow loop, keep it simple and stupid */ /* cf: au_cpup_dirs() */ err = 0; parent = NULL; while (au_digen(dentry) != sigen || au_iigen(dentry->d_inode) != sigen) { d = dentry; while (1) { dput(parent); parent = dget_parent(d); if (au_digen(parent) == sigen && au_iigen(parent->d_inode) == sigen) break; d = parent; } inode = d->d_inode; if (d != dentry) di_write_lock_child(d); /* someone might update our dentry while we were sleeping */ if (au_digen(d) != sigen || au_iigen(d->d_inode) != sigen) { di_read_lock_parent(parent, AuLock_IR); /* returns a number of positive dentries */ err = au_refresh_hdentry(d, inode->i_mode & S_IFMT); if (err >= 0) err = au_refresh_hinode(inode, d); di_read_unlock(parent, AuLock_IR); } if (d != dentry) di_write_unlock(d); dput(parent); if (unlikely(err)) break; } return err; }
static int test_children_busy(struct dentry *root, aufs_bindex_t bindex, const unsigned int verbose) { int err; unsigned int sigen; sigen = au_sigen(root->d_sb); DiMustNoWaiters(root); IiMustNoWaiters(root->d_inode); di_write_unlock(root); err = test_dentry_busy(root, bindex, sigen, verbose); if (!err) err = test_inode_busy(root->d_sb, bindex, sigen, verbose); di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */ return err; }
void aufs_write_lock(struct dentry *dentry) { si_write_lock(dentry->d_sb, AuLock_FLUSH | AuLock_NOPLMW); di_write_lock_child(dentry); }
/* * test if the branch is deletable or not. */ static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex, unsigned int sigen, const unsigned int verbose) { int err, i, j, ndentry; aufs_bindex_t bstart, bend; struct au_dcsub_pages dpages; struct au_dpage *dpage; struct dentry *d; err = au_dpages_init(&dpages, GFP_NOFS); if (unlikely(err)) goto out; err = au_dcsub_pages(&dpages, root, NULL, NULL); if (unlikely(err)) goto out_dpages; for (i = 0; !err && i < dpages.ndpage; i++) { dpage = dpages.dpages + i; ndentry = dpage->ndentry; for (j = 0; !err && j < ndentry; j++) { d = dpage->dentries[j]; AuDebugOn(!atomic_read(&d->d_count)); if (!au_digen_test(d, sigen)) { di_read_lock_child(d, AuLock_IR); if (unlikely(au_dbrange_test(d))) { di_read_unlock(d, AuLock_IR); continue; } } else { di_write_lock_child(d); if (unlikely(au_dbrange_test(d))) { di_write_unlock(d); continue; } err = au_reval_dpath(d, sigen); if (!err) di_downgrade_lock(d, AuLock_IR); else { di_write_unlock(d); break; } } /* AuDbgDentry(d); */ bstart = au_dbstart(d); bend = au_dbend(d); if (bstart <= bindex && bindex <= bend && au_h_dptr(d, bindex) && au_test_dbusy(d, bstart, bend)) { err = -EBUSY; AuVerbose(verbose, "busy %.*s\n", AuDLNPair(d)); AuDbgDentry(d); } di_read_unlock(d, AuLock_IR); } } out_dpages: au_dpages_free(&dpages); out: return err; }
/* * prepare the @file for writing. */ int au_ready_to_write(struct file *file, loff_t len) { int err; struct dentry *dentry, *parent, *hidden_dentry, *hidden_parent; struct inode *hidden_inode, *hidden_dir, *inode, *dir; struct super_block *sb; aufs_bindex_t bstart, bcpup; dentry = file->f_dentry; LKTRTrace("%.*s, len %Ld\n", DLNPair(dentry), len); FiMustWriteLock(file); sb = dentry->d_sb; bstart = fbstart(file); DEBUG_ON(ftobr(file, bstart) != stobr(sb, bstart)); inode = dentry->d_inode; ii_read_lock_child(inode); LKTRTrace("rdonly %d, bstart %d\n", test_ro(sb, bstart, inode), bstart); err = test_ro(sb, bstart, inode); ii_read_unlock(inode); if (!err && (au_h_fptr(file)->f_mode & FMODE_WRITE)) return 0; /* need to cpup */ parent = dentry->d_parent; // dget_parent() di_write_lock_child(dentry); di_write_lock_parent(parent); bcpup = err = find_rw_parent_br(dentry, bstart); //bcpup = err = find_rw_br(sb, bstart); if (unlikely(err < 0)) goto out_unlock; err = 0; hidden_parent = au_h_dptr_i(parent, bcpup); if (!hidden_parent) { err = cpup_dirs(dentry, bcpup, NULL); //if (LktrCond) err = -1; if (unlikely(err)) goto out_unlock; hidden_parent = au_h_dptr_i(parent, bcpup); } hidden_dir = hidden_parent->d_inode; hidden_dentry = au_h_fptr(file)->f_dentry; hidden_inode = hidden_dentry->d_inode; dir = parent->d_inode; hdir_lock(hidden_dir, dir, bcpup); hi_lock_child(hidden_inode); if (d_unhashed(dentry) || d_unhashed(hidden_dentry) /* || !hidden_inode->i_nlink */) { if (!au_test_perm(hidden_dir, MAY_EXEC | MAY_WRITE, need_dlgt(sb))) err = cpup_wh_file(file, bcpup, len); else { struct cpup_wh_file_args args = { .errp = &err, .file = file, .bdst = bcpup, .len = len }; au_wkq_wait(call_cpup_wh_file, &args, /*dlgt*/0); } //if (LktrCond) err = -1; TraceErr(err); } else { if (!au_h_dptr_i(dentry, bcpup))
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; }
void aufs_write_lock(struct dentry *dentry) { //au_wkq_wait_nwtask(); si_write_lock(dentry->d_sb); di_write_lock_child(dentry); }
void aufs_write_lock(struct dentry *dentry) { si_write_lock(dentry->d_sb); di_write_lock_child(dentry); }
static int au_cmoo(struct dentry *dentry) { int err, cmoo; unsigned int udba; struct path h_path; struct au_pin pin; struct au_cp_generic cpg = { .dentry = dentry, .bdst = -1, .bsrc = -1, .len = -1, .pin = &pin, .flags = AuCpup_DTIME | AuCpup_HOPEN }; struct inode *inode, *delegated; struct super_block *sb; struct au_sbinfo *sbinfo; struct au_fhsm *fhsm; pid_t pid; struct au_branch *br; struct dentry *parent; struct au_hinode *hdir; DiMustWriteLock(dentry); inode = dentry->d_inode; IiMustWriteLock(inode); err = 0; if (IS_ROOT(dentry)) goto out; cpg.bsrc = au_dbstart(dentry); if (!cpg.bsrc) goto out; sb = dentry->d_sb; sbinfo = au_sbi(sb); fhsm = &sbinfo->si_fhsm; pid = au_fhsm_pid(fhsm); if (pid && (current->pid == pid || current->real_parent->pid == pid)) goto out; br = au_sbr(sb, cpg.bsrc); cmoo = au_br_cmoo(br->br_perm); if (!cmoo) goto out; if (!S_ISREG(inode->i_mode)) cmoo &= AuBrAttr_COO_ALL; if (!cmoo) goto out; parent = dget_parent(dentry); di_write_lock_parent(parent); err = au_wbr_do_copyup_bu(dentry, cpg.bsrc - 1); cpg.bdst = err; if (unlikely(err < 0)) { err = 0; /* there is no upper writable branch */ goto out_dgrade; } AuDbg("bsrc %d, bdst %d\n", cpg.bsrc, cpg.bdst); /* do not respect the coo attrib for the target branch */ err = au_cpup_dirs(dentry, cpg.bdst); if (unlikely(err)) goto out_dgrade; di_downgrade_lock(parent, AuLock_IR); udba = au_opt_udba(sb); err = au_pin(&pin, dentry, cpg.bdst, udba, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (unlikely(err)) goto out_parent; err = au_sio_cpup_simple(&cpg); au_unpin(&pin); if (unlikely(err)) goto out_parent; if (!(cmoo & AuBrWAttr_MOO)) goto out_parent; /* success */ err = au_pin(&pin, dentry, cpg.bsrc, udba, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (unlikely(err)) goto out_parent; h_path.mnt = au_br_mnt(br); h_path.dentry = au_h_dptr(dentry, cpg.bsrc); hdir = au_hi(parent->d_inode, cpg.bsrc); delegated = NULL; err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, /*force*/1); au_unpin(&pin); /* todo: keep h_dentry or not? */ if (unlikely(err == -EWOULDBLOCK)) { pr_warn("cannot retry for NFSv4 delegation" " for an internal unlink\n"); iput(delegated); } if (unlikely(err)) { pr_err("unlink %pd after coo failed (%d), ignored\n", dentry, err); err = 0; } goto out_parent; /* success */ out_dgrade: di_downgrade_lock(parent, AuLock_IR); out_parent: di_read_unlock(parent, AuLock_IR); dput(parent); out: AuTraceErr(err); return err; } int au_do_open(struct file *file, int (*open)(struct file *file, int flags), struct au_fidir *fidir) { int err; struct dentry *dentry; struct au_finfo *finfo; err = au_finfo_init(file, fidir); if (unlikely(err)) goto out; dentry = file->f_path.dentry; di_write_lock_child(dentry); err = au_cmoo(dentry); di_downgrade_lock(dentry, AuLock_IR); if (!err) err = open(file, vfsub_file_flags(file)); di_read_unlock(dentry, AuLock_IR); finfo = au_fi(file); if (!err) { finfo->fi_file = file; au_sphl_add(&finfo->fi_hlist, &au_sbi(file->f_path.dentry->d_sb)->si_files); } fi_write_unlock(file); if (unlikely(err)) { finfo->fi_hdir = NULL; au_finfo_fin(file); } out: return err; }