static ssize_t aufs_aio_write_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 super_block *sb; struct file *file, *h_file; 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_write); err = h_file->f_op->aio_write(kio, iov, nv, pos); if (err > 0 && wbr) file_update_time(h_file); return err; }
/* * after branch manipulating, refresh the file. */ static int refresh_file(struct file *file, int (*reopen)(struct file *file)) { int err, need_reopen; struct dentry *dentry; aufs_bindex_t bend, bindex; dentry = file->f_dentry; err = au_fi_realloc(au_fi(file), au_sbend(dentry->d_sb) + 1); if (unlikely(err)) goto out; au_do_refresh_file(file); err = 0; need_reopen = 1; if (!au_test_mmapped(file)) err = au_file_refresh_by_inode(file, &need_reopen); if (!err && need_reopen && !d_unhashed(dentry)) err = reopen(file); if (!err) { au_update_figen(file); return 0; /* success */ } /* error, close all lower files */ bend = au_fbend(file); for (bindex = au_fbstart(file); bindex <= bend; bindex++) au_set_h_fptr(file, bindex, NULL); out: return err; }
int au_reopen_nondir(struct file *file) { int err; aufs_bindex_t bstart, bindex, bend; struct dentry *dentry; struct file *h_file, *h_file_tmp; dentry = file->f_dentry; AuDebugOn(au_special_file(dentry->d_inode->i_mode)); bstart = au_dbstart(dentry); h_file_tmp = NULL; if (au_fbstart(file) == bstart) { h_file = au_h_fptr(file, bstart); if (file->f_mode == h_file->f_mode) return 0; /* success */ h_file_tmp = h_file; get_file(h_file_tmp); au_set_h_fptr(file, bstart, NULL); } AuDebugOn(au_fbstart(file) < bstart || au_fi(file)->fi_hfile[0 + bstart].hf_file); h_file = au_h_open(dentry, bstart, vfsub_file_flags(file) & ~O_TRUNC, file); err = PTR_ERR(h_file); if (IS_ERR(h_file)) goto out; /* todo: close all? */ err = 0; au_set_fbstart(file, bstart); au_set_h_fptr(file, bstart, h_file); au_update_figen(file); /* todo: necessary? */ /* file->f_ra = h_file->f_ra; */ /* close lower files */ bend = au_fbend(file); for (bindex = bstart + 1; bindex <= bend; bindex++) au_set_h_fptr(file, bindex, NULL); au_set_fbend(file, bstart); out: if (h_file_tmp) fput(h_file_tmp); return err; }
int au_reopen_nondir(struct file *file) { int err; aufs_bindex_t bstart; struct dentry *dentry; struct file *h_file, *h_file_tmp; dentry = file->f_dentry; AuDebugOn(au_special_file(dentry->d_inode->i_mode)); bstart = au_dbstart(dentry); h_file_tmp = NULL; if (au_fbstart(file) == bstart) { h_file = au_hf_top(file); if (file->f_mode == h_file->f_mode) return 0; /* success */ h_file_tmp = h_file; get_file(h_file_tmp); au_set_h_fptr(file, bstart, NULL); } AuDebugOn(au_fi(file)->fi_hdir); /* * it can happen * file exists on both of rw and ro * open --> dbstart and fbstart are both 0 * prepend a branch as rw, "rw" become ro * remove rw/file * delete the top branch, "rw" becomes rw again * --> dbstart is 1, fbstart is still 0 * write --> fbstart is 0 but dbstart is 1 */ /* AuDebugOn(au_fbstart(file) < bstart); */ h_file = au_h_open(dentry, bstart, vfsub_file_flags(file) & ~O_TRUNC, file, /*force_wr*/0); err = PTR_ERR(h_file); if (IS_ERR(h_file)) { if (h_file_tmp) { atomic_inc(&au_sbr(dentry->d_sb, bstart)->br_count); au_set_h_fptr(file, bstart, h_file_tmp); h_file_tmp = NULL; } goto out; /* todo: close all? */ } err = 0; au_set_fbstart(file, bstart); au_set_h_fptr(file, bstart, h_file); au_update_figen(file); /* todo: necessary? */ /* file->f_ra = h_file->f_ra; */ out: if (h_file_tmp) fput(h_file_tmp); return err; }
/* 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_reopen_nondir(struct file *file) { int err; aufs_bindex_t bstart; struct dentry *dentry; struct file *h_file, *h_file_tmp; dentry = file->f_dentry; AuDebugOn(au_special_file(dentry->d_inode->i_mode)); bstart = au_dbstart(dentry); h_file_tmp = NULL; if (au_fbstart(file) == bstart) { h_file = au_hf_top(file); if (file->f_mode == h_file->f_mode) return 0; /* success */ h_file_tmp = h_file; get_file(h_file_tmp); au_set_h_fptr(file, bstart, NULL); } AuDebugOn(au_fi(file)->fi_hdir); AuDebugOn(au_fbstart(file) < bstart); h_file = au_h_open(dentry, bstart, file->f_flags & ~O_TRUNC, file); err = PTR_ERR(h_file); if (IS_ERR(h_file)) goto out; /* todo: close all? */ err = 0; au_set_fbstart(file, bstart); au_set_h_fptr(file, bstart, h_file); au_update_figen(file); /* todo: necessary? */ /* file->f_ra = h_file->f_ra; */ out: if (h_file_tmp) fput(h_file_tmp); return err; }
static int aufs_release_sp(struct inode *inode, struct file *file) { int err; struct file *h_file; fi_read_lock(file); h_file = au_h_fptr(file, au_fbstart(file)); fi_read_unlock(file); /* close this fifo in aufs */ err = h_file->f_op->release(inode, file); /* ignore */ aufs_release_nondir(inode, file); /* ignore */ return err; }
loff_t au_dir_size(struct file *file, struct dentry *dentry) { loff_t sz; aufs_bindex_t bindex, bend; struct file *h_file; struct dentry *h_dentry; sz = 0; if (file) { AuDebugOn(!file->f_dentry); AuDebugOn(!file->f_dentry->d_inode); AuDebugOn(!S_ISDIR(file->f_dentry->d_inode->i_mode)); bend = au_fbend_dir(file); for (bindex = au_fbstart(file); bindex <= bend && sz < KMALLOC_MAX_SIZE; bindex++) { h_file = au_hf_dir(file, bindex); if (h_file && h_file->f_dentry && h_file->f_dentry->d_inode) sz += i_size_read(h_file->f_dentry->d_inode); } } else { AuDebugOn(!dentry); AuDebugOn(!dentry->d_inode); AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode)); bend = au_dbtaildir(dentry); for (bindex = au_dbstart(dentry); bindex <= bend && sz < KMALLOC_MAX_SIZE; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (h_dentry && h_dentry->d_inode) sz += i_size_read(h_dentry->d_inode); } } 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; }
static int do_open_dir(struct file *file, int flags) { int err; aufs_bindex_t bindex, btail; struct dentry *dentry, *h_dentry; struct file *h_file; FiMustWriteLock(file); dentry = file->f_dentry; err = au_alive_dir(dentry); if (unlikely(err)) goto out; file->f_version = dentry->d_inode->i_version; bindex = au_dbstart(dentry); au_set_fbstart(file, bindex); btail = au_dbtaildir(dentry); au_set_fbend_dir(file, btail); for (; !err && bindex <= btail; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (!h_dentry) continue; h_file = au_h_open(dentry, bindex, flags, file); if (IS_ERR(h_file)) { err = PTR_ERR(h_file); break; } au_set_h_fptr(file, bindex, h_file); } au_update_figen(file); /* todo: necessary? */ /* file->f_ra = h_file->f_ra; */ if (!err) return 0; /* success */ /* close all */ for (bindex = au_fbstart(file); bindex <= btail; bindex++) au_set_h_fptr(file, bindex, NULL); au_set_fbstart(file, -1); au_set_fbend_dir(file, -1); out: return err; }
/* * after branch manipulating, refresh the file. */ static int refresh_file(struct file *file, int (*reopen)(struct file *file)) { int err, need_reopen; aufs_bindex_t bend, bindex; struct dentry *dentry; struct au_finfo *finfo; struct au_hfile *hfile; dentry = file->f_dentry; finfo = au_fi(file); if (!finfo->fi_hdir) { hfile = &finfo->fi_htop; AuDebugOn(!hfile->hf_file); bindex = au_br_index(dentry->d_sb, hfile->hf_br->br_id); AuDebugOn(bindex < 0); if (bindex != finfo->fi_btop) au_set_fbstart(file, bindex); } else { err = au_fidir_realloc(finfo, au_sbend(dentry->d_sb) + 1); if (unlikely(err)) goto out; au_do_refresh_dir(file); } err = 0; need_reopen = 1; if (!au_test_mmapped(file)) err = au_file_refresh_by_inode(file, &need_reopen); if (!err && need_reopen && !d_unlinked(dentry)) err = reopen(file); if (!err) { au_update_figen(file); goto out; /* success */ } /* error, close all lower files */ if (finfo->fi_hdir) { bend = au_fbend_dir(file); for (bindex = au_fbstart(file); bindex <= bend; bindex++) au_set_h_fptr(file, bindex, NULL); } out: return err; }
static int reopen_dir(struct file *file) { int err; unsigned int flags; aufs_bindex_t bindex, btail, bstart; struct dentry *dentry, *h_dentry; struct file *h_file; /* open all lower dirs */ dentry = file->f_dentry; bstart = au_dbstart(dentry); for (bindex = au_fbstart(file); bindex < bstart; bindex++) au_set_h_fptr(file, bindex, NULL); au_set_fbstart(file, bstart); btail = au_dbtaildir(dentry); for (bindex = au_fbend_dir(file); btail < bindex; bindex--) au_set_h_fptr(file, bindex, NULL); au_set_fbend_dir(file, btail); flags = vfsub_file_flags(file); for (bindex = bstart; bindex <= btail; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (!h_dentry) continue; h_file = au_hf_dir(file, bindex); if (h_file) continue; h_file = au_h_open(dentry, bindex, flags, file); err = PTR_ERR(h_file); if (IS_ERR(h_file)) goto out; /* close all? */ au_set_h_fptr(file, bindex, h_file); } au_update_figen(file); /* todo: necessary? */ /* file->f_ra = h_file->f_ra; */ err = 0; out: return err; }
void au_finfo_fin(struct file *file) { struct au_finfo *finfo; aufs_bindex_t bindex, bend; fi_write_lock(file); bend = au_fbend(file); bindex = au_fbstart(file); if (bindex >= 0) /* * calls fput() instead of filp_close(), * since no dnotify or lock for the lower file. */ for (; bindex <= bend; bindex++) au_set_h_fptr(file, bindex, NULL); finfo = au_fi(file); au_dbg_verify_hf(finfo); kfree(finfo->fi_hfile); fi_write_unlock(file); AuRwDestroy(&finfo->fi_rwsem); au_cache_free_finfo(finfo); }
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, struct au_do_open_args *args) { int err, no_lock = args->no_lock; struct dentry *dentry; struct au_finfo *finfo; if (!no_lock) err = au_finfo_init(file, args->fidir); else { lockdep_off(); err = au_finfo_init(file, args->fidir); lockdep_on(); } if (unlikely(err)) goto out; dentry = file->f_dentry; AuDebugOn(IS_ERR_OR_NULL(dentry)); if (!no_lock) { di_write_lock_child(dentry); err = au_cmoo(dentry); di_downgrade_lock(dentry, AuLock_IR); if (!err) err = args->open(file, vfsub_file_flags(file), NULL); di_read_unlock(dentry, AuLock_IR); } else { err = au_cmoo(dentry); if (!err) err = args->open(file, vfsub_file_flags(file), args->h_file); if (!err && au_fbstart(file) != au_dbstart(dentry)) /* * cmoo happens after h_file was opened. * need to refresh file later. */ atomic_dec(&au_fi(file)->fi_generation); } finfo = au_fi(file); if (!err) { finfo->fi_file = file; au_sphl_add(&finfo->fi_hlist, &au_sbi(file->f_dentry->d_sb)->si_files); } if (!no_lock) fi_write_unlock(file); else { lockdep_off(); fi_write_unlock(file); lockdep_on(); } if (unlikely(err)) { finfo->fi_hdir = NULL; au_finfo_fin(file); } out: return err; }
/* * prepare the @file for writing. */ int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) { int err; aufs_bindex_t bstart, bcpup, dbstart; struct dentry *dentry, *parent, *h_dentry; struct inode *h_inode, *inode; struct super_block *sb; struct file *h_file; dentry = file->f_dentry; sb = dentry->d_sb; inode = dentry->d_inode; AuDebugOn(au_special_file(inode->i_mode)); bstart = au_fbstart(file); err = au_test_ro(sb, bstart, inode); if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { err = au_pin(pin, dentry, bstart, AuOpt_UDBA_NONE, /*flags*/0); goto out; } /* need to cpup or reopen */ parent = dget_parent(dentry); di_write_lock_parent(parent); err = AuWbrCopyup(au_sbi(sb), dentry); bcpup = err; if (unlikely(err < 0)) goto out_dgrade; err = 0; if (!d_unhashed(dentry) && !au_h_dptr(parent, bcpup)) { err = au_cpup_dirs(dentry, bcpup); if (unlikely(err)) goto out_dgrade; } err = au_pin(pin, dentry, bcpup, AuOpt_UDBA_NONE, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (unlikely(err)) goto out_dgrade; h_dentry = au_hf_top(file)->f_dentry; h_inode = h_dentry->d_inode; dbstart = au_dbstart(dentry); if (dbstart <= bcpup) { h_dentry = au_h_dptr(dentry, bcpup); AuDebugOn(!h_dentry); h_inode = h_dentry->d_inode; AuDebugOn(!h_inode); bstart = bcpup; } if (dbstart <= bcpup /* just reopen */ || !d_unhashed(dentry) /* copyup and reopen */ ) { mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); h_file = au_h_open_pre(dentry, bstart); if (IS_ERR(h_file)) { err = PTR_ERR(h_file); h_file = NULL; } else { di_downgrade_lock(parent, AuLock_IR); if (dbstart > bcpup) err = au_sio_cpup_simple(dentry, bcpup, len, AuCpup_DTIME); if (!err) err = au_reopen_nondir(file); } mutex_unlock(&h_inode->i_mutex); au_h_open_post(dentry, bstart, h_file); } else { /* copyup as wh and reopen */ /* * since writable hfsplus branch is not supported, * h_open_pre/post() are unnecessary. */ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); err = au_ready_to_write_wh(file, len, bcpup); di_downgrade_lock(parent, AuLock_IR); mutex_unlock(&h_inode->i_mutex); } if (!err) { au_pin_set_parent_lflag(pin, /*lflag*/0); goto out_dput; /* success */ } au_unpin(pin); goto out_unlock; out_dgrade: di_downgrade_lock(parent, AuLock_IR); out_unlock: di_read_unlock(parent, AuLock_IR); out_dput: dput(parent); out: return err; }
static int au_ready_to_write_wh(struct file *file, loff_t len, aufs_bindex_t bcpup, struct au_pin *pin) { int err; struct inode *inode, *h_inode; struct dentry *h_dentry, *hi_wh; struct au_cp_generic cpg = { .dentry = file->f_dentry, .bdst = bcpup, .bsrc = -1, .len = len, .pin = pin }; au_update_dbstart(cpg.dentry); inode = cpg.dentry->d_inode; h_inode = NULL; if (au_dbstart(cpg.dentry) <= bcpup && au_dbend(cpg.dentry) >= bcpup) { h_dentry = au_h_dptr(cpg.dentry, bcpup); if (h_dentry) h_inode = h_dentry->d_inode; } hi_wh = au_hi_wh(inode, bcpup); if (!hi_wh && !h_inode) err = au_sio_cpup_wh(&cpg, file); else /* already copied-up after unlink */ err = au_reopen_wh(file, bcpup, hi_wh); if (!err && inode->i_nlink > 1 && au_opt_test(au_mntflags(cpg.dentry->d_sb), PLINK)) au_plink_append(inode, bcpup, au_h_dptr(cpg.dentry, bcpup)); return err; } /* * prepare the @file for writing. */ int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) { int err; aufs_bindex_t dbstart; struct dentry *parent, *h_dentry; struct inode *inode; struct super_block *sb; struct file *h_file; struct au_cp_generic cpg = { .dentry = file->f_dentry, .bdst = -1, .bsrc = -1, .len = len, .pin = pin, .flags = AuCpup_DTIME }; sb = cpg.dentry->d_sb; inode = cpg.dentry->d_inode; AuDebugOn(au_special_file(inode->i_mode)); cpg.bsrc = au_fbstart(file); err = au_test_ro(sb, cpg.bsrc, inode); if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { err = au_pin(pin, cpg.dentry, cpg.bsrc, AuOpt_UDBA_NONE, /*flags*/0); goto out; } /* need to cpup or reopen */ parent = dget_parent(cpg.dentry); di_write_lock_parent(parent); err = AuWbrCopyup(au_sbi(sb), cpg.dentry); cpg.bdst = err; if (unlikely(err < 0)) goto out_dgrade; err = 0; if (!d_unhashed(cpg.dentry) && !au_h_dptr(parent, cpg.bdst)) { err = au_cpup_dirs(cpg.dentry, cpg.bdst); if (unlikely(err)) goto out_dgrade; } err = au_pin(pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (unlikely(err)) goto out_dgrade; h_dentry = au_hf_top(file)->f_dentry; dbstart = au_dbstart(cpg.dentry); if (dbstart <= cpg.bdst) { h_dentry = au_h_dptr(cpg.dentry, cpg.bdst); AuDebugOn(!h_dentry); cpg.bsrc = cpg.bdst; } if (dbstart <= cpg.bdst /* just reopen */ || !d_unhashed(cpg.dentry) /* copyup and reopen */ ) { h_file = au_h_open_pre(cpg.dentry, cpg.bsrc, /*force_wr*/0); if (IS_ERR(h_file)) err = PTR_ERR(h_file); else { di_downgrade_lock(parent, AuLock_IR); if (dbstart > cpg.bdst) err = au_sio_cpup_simple(&cpg); if (!err) err = au_reopen_nondir(file); au_h_open_post(cpg.dentry, cpg.bsrc, h_file); } } else { /* copyup as wh and reopen */ /* * since writable hfsplus branch is not supported, * h_open_pre/post() are unnecessary. */ err = au_ready_to_write_wh(file, len, cpg.bdst, pin); di_downgrade_lock(parent, AuLock_IR); } if (!err) { au_pin_set_parent_lflag(pin, /*lflag*/0); goto out_dput; /* success */ } au_unpin(pin); goto out_unlock; out_dgrade: di_downgrade_lock(parent, AuLock_IR); out_unlock: di_read_unlock(parent, AuLock_IR); out_dput: dput(parent); out: return err; } /* ---------------------------------------------------------------------- */ int au_do_flush(struct file *file, fl_owner_t id, int (*flush)(struct file *file, fl_owner_t id)) { int err; struct super_block *sb; struct inode *inode; inode = file_inode(file); sb = inode->i_sb; si_noflush_read_lock(sb); fi_read_lock(file); ii_read_lock_child(inode); err = flush(file, id); au_cpup_attr_timesizes(inode); ii_read_unlock(inode); fi_read_unlock(file); si_read_unlock(sb); return err; }
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; }
/* * prepare the @file for writing. */ int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) { int err; aufs_bindex_t bstart, bcpup; struct dentry *dentry, *parent, *h_dentry; struct inode *h_inode, *inode; struct super_block *sb; dentry = file->f_dentry; sb = dentry->d_sb; inode = dentry->d_inode; AuDebugOn(au_special_file(inode->i_mode)); bstart = au_fbstart(file); err = au_test_ro(sb, bstart, inode); if (!err && (au_h_fptr(file, bstart)->f_mode & FMODE_WRITE)) { err = au_pin(pin, dentry, bstart, AuOpt_UDBA_NONE, /*flags*/0); goto out; } /* need to cpup */ parent = dget_parent(dentry); di_write_lock_parent(parent); err = AuWbrCopyup(au_sbi(sb), dentry); bcpup = err; if (unlikely(err < 0)) goto out_dgrade; err = 0; if (!au_h_dptr(parent, bcpup)) { err = au_cpup_dirs(dentry, bcpup); if (unlikely(err)) goto out_dgrade; } err = au_pin(pin, dentry, bcpup, AuOpt_UDBA_NONE, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (unlikely(err)) goto out_dgrade; h_dentry = au_h_fptr(file, bstart)->f_dentry; h_inode = h_dentry->d_inode; mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); if (d_unhashed(dentry) /* || d_unhashed(h_dentry) */ /* || !h_inode->i_nlink */) { err = au_ready_to_write_wh(file, len, bcpup); di_downgrade_lock(parent, AuLock_IR); } else { di_downgrade_lock(parent, AuLock_IR); if (!au_h_dptr(dentry, bcpup)) err = au_sio_cpup_simple(dentry, bcpup, len, AuCpup_DTIME); if (!err) err = au_reopen_nondir(file); } mutex_unlock(&h_inode->i_mutex); if (!err) { au_pin_set_parent_lflag(pin, /*lflag*/0); goto out_dput; /* success */ } au_unpin(pin); goto out_unlock; out_dgrade: di_downgrade_lock(parent, AuLock_IR); out_unlock: di_read_unlock(parent, AuLock_IR); out_dput: dput(parent); out: return err; }