/* * decide the branch and the parent dir where we will create a new entry. * returns new bindex or an error. * copyup the parent dir if needed. */ int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, struct au_wr_dir_args *args) { int err; aufs_bindex_t bcpup, bstart, src_bstart; const unsigned char add_entry = !!au_ftest_wrdir(args->flags, ADD_ENTRY); struct super_block *sb; struct dentry *parent; struct au_sbinfo *sbinfo; sb = dentry->d_sb; sbinfo = au_sbi(sb); parent = dget_parent(dentry); bstart = au_dbstart(dentry); bcpup = bstart; if (args->force_btgt < 0) { if (src_dentry) { src_bstart = au_dbstart(src_dentry); if (src_bstart < bstart) bcpup = src_bstart; } else if (add_entry) { err = AuWbrCreate(sbinfo, dentry, au_ftest_wrdir(args->flags, ISDIR)); bcpup = err; } if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) { if (add_entry) err = AuWbrCopyup(sbinfo, dentry); else { if (!IS_ROOT(dentry)) { di_read_lock_parent(parent, !AuLock_IR); err = AuWbrCopyup(sbinfo, dentry); di_read_unlock(parent, !AuLock_IR); } else err = AuWbrCopyup(sbinfo, dentry); } bcpup = err; if (unlikely(err < 0)) goto out; } } else { bcpup = args->force_btgt; AuDebugOn(au_test_ro(sb, bcpup, dentry->d_inode)); } AuDbg("bstart %d, bcpup %d\n", bstart, bcpup); err = bcpup; if (bcpup == bstart) goto out; /* success */ else if (bstart < bcpup) au_update_dbrange(dentry, /*do_put_zero*/1); /* copyup the new parent into the branch we process */ err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart); out: dput(parent); return err; }
/* * decide if a new whiteout for @dentry is necessary or not. * when it is necessary, prepare the parent dir for the upper branch whose * branch index is @bcpup for creation. the actual creation of the whiteout will * be done by caller. * return value: * 0: wh is unnecessary * plus: wh is necessary * minus: error */ int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) { int need_wh, err; aufs_bindex_t bstart; struct super_block *sb; sb = dentry->d_sb; bstart = au_dbstart(dentry); if (*bcpup < 0) { *bcpup = bstart; if (au_test_ro(sb, bstart, dentry->d_inode)) { err = AuWbrCopyup(au_sbi(sb), dentry); *bcpup = err; if (unlikely(err < 0)) goto out; } } else AuDebugOn(bstart < *bcpup || au_test_ro(sb, *bcpup, dentry->d_inode)); AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart); if (*bcpup != bstart) { err = au_cpup_dirs(dentry, *bcpup); if (unlikely(err)) goto out; need_wh = 1; } else { aufs_bindex_t old_bend, new_bend, bdiropq = -1; old_bend = au_dbend(dentry); if (isdir) { bdiropq = au_dbdiropq(dentry); au_set_dbdiropq(dentry, -1); } need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0, /*nd*/NULL); err = need_wh; if (isdir) au_set_dbdiropq(dentry, bdiropq); if (unlikely(err < 0)) goto out; new_bend = au_dbend(dentry); if (!need_wh && old_bend != new_bend) { au_set_h_dptr(dentry, new_bend, NULL); au_set_dbend(dentry, old_bend); } } AuDbg("need_wh %d\n", need_wh); err = need_wh; out: return err; }
/* * decide if a new whiteout for @dentry is necessary or not. * when it is necessary, prepare the parent dir for the upper branch whose * branch index is @bcpup for creation. the actual creation of the whiteout will * be done by caller. * return value: * 0: wh is unnecessary * plus: wh is necessary * minus: error */ int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) { int need_wh, err; aufs_bindex_t bstart; struct super_block *sb; sb = dentry->d_sb; bstart = au_dbstart(dentry); if (*bcpup < 0) { *bcpup = bstart; if (au_test_ro(sb, bstart, dentry->d_inode)) { err = AuWbrCopyup(au_sbi(sb), dentry); *bcpup = err; if (unlikely(err < 0)) goto out; } } else AuDebugOn(bstart < *bcpup || au_test_ro(sb, *bcpup, dentry->d_inode)); AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart); if (*bcpup != bstart) { err = au_cpup_dirs(dentry, *bcpup); if (unlikely(err)) goto out; need_wh = 1; } else { struct au_dinfo *dinfo, *tmp; need_wh = -ENOMEM; dinfo = au_di(dentry); tmp = au_di_alloc(sb, AuLsc_DI_TMP); if (tmp) { au_di_cp(tmp, dinfo); au_di_swap(tmp, dinfo); /* returns the number of positive dentries */ need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0, /*nd*/NULL); au_di_swap(tmp, dinfo); au_rw_write_unlock(&tmp->di_rwsem); au_di_free(tmp); } } AuDbg("need_wh %d\n", need_wh); err = need_wh; out: return err; }
int au_do_open_nondir(struct file *file, int flags) { int err; aufs_bindex_t bindex; struct file *h_file; struct dentry *dentry; struct au_finfo *finfo; FiMustWriteLock(file); err = 0; dentry = file->f_dentry; finfo = au_fi(file); memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); finfo->fi_hvmop = NULL; bindex = au_dbstart(dentry); /* O_TRUNC is processed already */ BUG_ON(au_test_ro(dentry->d_sb, bindex, dentry->d_inode) && (flags & O_TRUNC)); h_file = au_h_open(dentry, bindex, flags, file); if (IS_ERR(h_file)) err = PTR_ERR(h_file); else { au_set_fbstart(file, bindex); au_set_h_fptr(file, bindex, h_file); au_update_figen(file); /* todo: necessary? */ /* file->f_ra = h_file->f_ra; */ } return err; }
int au_do_open_nondir(struct file *file, int flags) { int err; aufs_bindex_t bindex; struct file *h_file; struct dentry *dentry; struct au_finfo *finfo; FiMustWriteLock(file); dentry = file->f_dentry; err = au_d_alive(dentry); if (unlikely(err)) goto out; finfo = au_fi(file); memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); finfo->fi_hvmop = NULL; bindex = au_dbstart(dentry); /* * O_TRUNC is processed already. * But it is legal the target file has its size, since someone else may * give it while we are opening. */ BUG_ON(0 && au_test_ro(dentry->d_sb, bindex, dentry->d_inode) && (flags & O_TRUNC)); h_file = au_h_open(dentry, bindex, flags, file); if (IS_ERR(h_file)) err = PTR_ERR(h_file); else { au_set_fbstart(file, bindex); au_set_h_fptr(file, bindex, h_file); au_update_figen(file); /* todo: necessary? */ /* file->f_ra = h_file->f_ra; */ } out: return err; }
/* common functions to regular file and dir */ struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, struct file *file) { struct file *h_file; struct dentry *h_dentry; struct inode *h_inode; struct super_block *sb; struct au_branch *br; struct path h_path; int err, exec_flag; /* a race condition can happen between open and unlink/rmdir */ h_file = ERR_PTR(-ENOENT); h_dentry = au_h_dptr(dentry, bindex); if (au_test_nfsd() && !h_dentry) goto out; h_inode = h_dentry->d_inode; if (au_test_nfsd() && !h_inode) goto out; spin_lock(&h_dentry->d_lock); err = (!d_unhashed(dentry) && d_unlinked(h_dentry)) || !h_inode /* || !dentry->d_inode->i_nlink */ ; spin_unlock(&h_dentry->d_lock); if (unlikely(err)) goto out; sb = dentry->d_sb; br = au_sbr(sb, bindex); h_file = ERR_PTR(-EACCES); exec_flag = flags & __FMODE_EXEC; if (exec_flag && (br->br_mnt->mnt_flags & MNT_NOEXEC)) goto out; /* drop flags for writing */ if (au_test_ro(sb, bindex, dentry->d_inode)) flags = au_file_roflags(flags); flags &= ~O_CREAT; atomic_inc(&br->br_count); h_path.dentry = h_dentry; h_path.mnt = br->br_mnt; if (!au_special_file(h_inode->i_mode)) h_file = vfsub_dentry_open(&h_path, flags); else { /* this block depends upon the configuration */ di_read_unlock(dentry, AuLock_IR); fi_write_unlock(file); si_read_unlock(sb); h_file = vfsub_dentry_open(&h_path, flags); si_noflush_read_lock(sb); fi_write_lock(file); di_read_lock_child(dentry, AuLock_IR); } if (IS_ERR(h_file)) goto out_br; if (exec_flag) { err = deny_write_access(h_file); if (unlikely(err)) { fput(h_file); h_file = ERR_PTR(err); goto out_br; } } fsnotify_open(h_file); goto out; /* success */ out_br: atomic_dec(&br->br_count); out: return h_file; }
static int au_file_refresh_by_inode(struct file *file, int *need_reopen) { int err; aufs_bindex_t bstart; struct au_pin pin; struct au_finfo *finfo; struct dentry *dentry, *parent, *hi_wh; struct inode *inode; struct super_block *sb; FiMustWriteLock(file); err = 0; finfo = au_fi(file); dentry = file->f_dentry; sb = dentry->d_sb; inode = dentry->d_inode; bstart = au_ibstart(inode); if (bstart == finfo->fi_btop || IS_ROOT(dentry)) goto out; parent = dget_parent(dentry); if (au_test_ro(sb, bstart, inode)) { di_read_lock_parent(parent, !AuLock_IR); err = AuWbrCopyup(au_sbi(sb), dentry); bstart = err; di_read_unlock(parent, !AuLock_IR); if (unlikely(err < 0)) goto out_parent; err = 0; } di_read_lock_parent(parent, AuLock_IR); hi_wh = au_hi_wh(inode, bstart); if (!S_ISDIR(inode->i_mode) && au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(inode) && !d_unhashed(dentry)) { err = au_test_and_cpup_dirs(dentry, bstart); if (unlikely(err)) goto out_unlock; /* always superio. */ err = au_pin(&pin, dentry, bstart, AuOpt_UDBA_NONE, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (!err) err = au_sio_cpup_simple(dentry, bstart, -1, AuCpup_DTIME); au_unpin(&pin); } else if (hi_wh) { /* already copied-up after unlink */ err = au_reopen_wh(file, bstart, hi_wh); *need_reopen = 0; } out_unlock: di_read_unlock(parent, AuLock_IR); out_parent: dput(parent); 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; }
}, { .bindex = bdst, .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, .file = NULL, .label = &&out_src_file, .label_file = &&out_dst_file } }; LKTRTrace("dentry %.*s, bdst %d, bsrc %d, len %lld\n", AuDLNPair(dentry), bdst, bsrc, len); AuDebugOn(bsrc <= bdst); AuDebugOn(!len); sb = dentry->d_sb; AuDebugOn(au_test_ro(sb, bdst, dentry->d_inode)); /* bsrc branch can be ro/rw. */ h = hidden; for (i = 0; i < 2; i++, h++) { h->dentry = au_h_dptr(dentry, h->bindex); AuDebugOn(!h->dentry); h_inode = h->dentry->d_inode; AuDebugOn(!h_inode || !S_ISREG(h_inode->i_mode)); h->file = au_h_open(dentry, h->bindex, h->flags, /*file*/NULL); err = PTR_ERR(h->file); if (IS_ERR(h->file)) goto *h->label; err = -EINVAL; if (unlikely(!h->file->f_op)) goto *h->label_file;
/* * 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; }
static int au_file_refresh_by_inode(struct file *file, int *need_reopen) { int err; struct au_pin pin; struct au_finfo *finfo; struct dentry *parent, *hi_wh; struct inode *inode; struct super_block *sb; struct au_cp_generic cpg = { .dentry = file->f_dentry, .bdst = -1, .bsrc = -1, .len = -1, .pin = &pin, .flags = AuCpup_DTIME }; FiMustWriteLock(file); err = 0; finfo = au_fi(file); sb = cpg.dentry->d_sb; inode = cpg.dentry->d_inode; cpg.bdst = au_ibstart(inode); if (cpg.bdst == finfo->fi_btop || IS_ROOT(cpg.dentry)) goto out; parent = dget_parent(cpg.dentry); if (au_test_ro(sb, cpg.bdst, inode)) { di_read_lock_parent(parent, !AuLock_IR); err = AuWbrCopyup(au_sbi(sb), cpg.dentry); cpg.bdst = err; di_read_unlock(parent, !AuLock_IR); if (unlikely(err < 0)) goto out_parent; err = 0; } di_read_lock_parent(parent, AuLock_IR); hi_wh = au_hi_wh(inode, cpg.bdst); if (!S_ISDIR(inode->i_mode) && au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(inode) && !d_unhashed(cpg.dentry) && cpg.bdst < au_dbstart(cpg.dentry)) { err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst); if (unlikely(err)) goto out_unlock; /* always superio. */ err = au_pin(&pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (!err) { err = au_sio_cpup_simple(&cpg); au_unpin(&pin); } } else if (hi_wh) { /* already copied-up after unlink */ err = au_reopen_wh(file, cpg.bdst, hi_wh); *need_reopen = 0; } out_unlock: di_read_unlock(parent, AuLock_IR); out_parent: dput(parent); out: return err; } static void au_do_refresh_dir(struct file *file) { aufs_bindex_t bindex, bend, new_bindex, brid; struct au_hfile *p, tmp, *q; struct au_finfo *finfo; struct super_block *sb; struct au_fidir *fidir; FiMustWriteLock(file); sb = file->f_dentry->d_sb; finfo = au_fi(file); fidir = finfo->fi_hdir; AuDebugOn(!fidir); p = fidir->fd_hfile + finfo->fi_btop; brid = p->hf_br->br_id; bend = fidir->fd_bbot; for (bindex = finfo->fi_btop; bindex <= bend; bindex++, p++) { if (!p->hf_file) continue; new_bindex = au_br_index(sb, p->hf_br->br_id); if (new_bindex == bindex) continue; if (new_bindex < 0) { au_set_h_fptr(file, bindex, NULL); continue; } /* swap two lower inode, and loop again */ q = fidir->fd_hfile + new_bindex; tmp = *q; *q = *p; *p = tmp; if (tmp.hf_file) { bindex--; p--; } } p = fidir->fd_hfile; if (!au_test_mmapped(file) && !d_unlinked(file->f_dentry)) { bend = au_sbend(sb); for (finfo->fi_btop = 0; finfo->fi_btop <= bend; finfo->fi_btop++, p++) if (p->hf_file) { if (file_inode(p->hf_file)) break; else au_hfput(p, file); } } else { bend = au_br_index(sb, brid); for (finfo->fi_btop = 0; finfo->fi_btop < bend; finfo->fi_btop++, p++) if (p->hf_file) au_hfput(p, file); bend = au_sbend(sb); } p = fidir->fd_hfile + bend; for (fidir->fd_bbot = bend; fidir->fd_bbot >= finfo->fi_btop; fidir->fd_bbot--, p--) if (p->hf_file) { if (file_inode(p->hf_file)) break; else au_hfput(p, file); } AuDebugOn(fidir->fd_bbot < finfo->fi_btop); }
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; }
/* common functions to regular file and dir */ struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, struct file *file, int force_wr) { struct file *h_file; struct dentry *h_dentry; struct inode *h_inode; struct super_block *sb; struct au_branch *br; struct path h_path; int err, exec_flag; /* a race condition can happen between open and unlink/rmdir */ h_file = ERR_PTR(-ENOENT); h_dentry = au_h_dptr(dentry, bindex); if (au_test_nfsd() && !h_dentry) goto out; h_inode = h_dentry->d_inode; if (au_test_nfsd() && !h_inode) goto out; spin_lock(&h_dentry->d_lock); err = (!d_unhashed(dentry) && d_unlinked(h_dentry)) || !h_inode /* || !dentry->d_inode->i_nlink */ ; spin_unlock(&h_dentry->d_lock); if (unlikely(err)) goto out; sb = dentry->d_sb; br = au_sbr(sb, bindex); h_file = ERR_PTR(-EACCES); exec_flag = flags & __FMODE_EXEC; if (exec_flag && (au_br_mnt(br)->mnt_flags & MNT_NOEXEC)) goto out; /* drop flags for writing */ if (au_test_ro(sb, bindex, dentry->d_inode)) { if (force_wr && !(flags & O_WRONLY)) force_wr = 0; flags = au_file_roflags(flags); if (force_wr) { h_file = ERR_PTR(-EROFS); flags = au_file_roflags(flags); if (unlikely(vfsub_native_ro(h_inode) || IS_APPEND(h_inode))) goto out; flags &= ~O_ACCMODE; flags |= O_WRONLY; } } flags &= ~O_CREAT; atomic_inc(&br->br_count); h_path.dentry = h_dentry; h_path.mnt = au_br_mnt(br); h_file = vfsub_dentry_open(&h_path, flags); if (IS_ERR(h_file)) goto out_br; if (exec_flag) { err = deny_write_access(h_file); if (unlikely(err)) { fput(h_file); h_file = ERR_PTR(err); goto out_br; } } fsnotify_open(h_file); goto out; /* success */ out_br: atomic_dec(&br->br_count); out: return h_file; }