static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex, struct au_branch *br) { aufs_bindex_t bend; struct au_sbinfo *sbinfo; struct dentry *root, *h_root; struct inode *inode, *h_inode; struct au_hinode *hinode; SiMustWriteLock(sb); root = sb->s_root; inode = root->d_inode; sbinfo = au_sbi(sb); bend = sbinfo->si_bend; h_root = au_h_dptr(root, bindex); hinode = au_hi(inode, bindex); h_inode = au_igrab(hinode->hi_inode); au_hiput(hinode); au_sbilist_lock(); au_br_do_del_brp(sbinfo, bindex, bend); au_br_do_del_hdp(au_di(root), bindex, bend); au_br_do_del_hip(au_ii(inode), bindex, bend); au_sbilist_unlock(); dput(h_root); iput(h_inode); au_br_do_free(br); }
static void au_ren_refresh(struct au_ren_args *a) { aufs_bindex_t bend, bindex; struct dentry *d, *h_d; struct inode *i, *h_i; struct super_block *sb; d = a->dst_dentry; d_drop(d); if (a->h_dst) /* already dget-ed by au_ren_or_cpup() */ au_set_h_dptr(d, a->btgt, a->h_dst); i = a->dst_inode; if (i) { if (!au_ftest_ren(a->flags, ISDIR)) vfsub_drop_nlink(i); else { vfsub_dead_dir(i); au_cpup_attr_timesizes(i); } au_update_dbrange(d, /*do_put_zero*/1); } else { bend = a->btgt; for (bindex = au_dbstart(d); bindex < bend; bindex++) au_set_h_dptr(d, bindex, NULL); bend = au_dbend(d); for (bindex = a->btgt + 1; bindex <= bend; bindex++) au_set_h_dptr(d, bindex, NULL); au_update_dbrange(d, /*do_put_zero*/0); } d = a->src_dentry; au_set_dbwh(d, -1); bend = au_dbend(d); for (bindex = a->btgt + 1; bindex <= bend; bindex++) { h_d = au_h_dptr(d, bindex); if (h_d) au_set_h_dptr(d, bindex, NULL); } au_set_dbend(d, a->btgt); sb = d->d_sb; i = a->src_inode; if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) return; /* success */ bend = au_ibend(i); for (bindex = a->btgt + 1; bindex <= bend; bindex++) { h_i = au_h_iptr(i, bindex); if (h_i) { au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); /* ignore this error */ au_set_h_iptr(i, bindex, NULL, 0); } } au_set_ibend(i, a->btgt); }
int au_refresh_hinode(struct inode *inode, struct dentry *dentry) { int err, e, update; unsigned int flags; umode_t mode; aufs_bindex_t bindex, bend; unsigned char isdir; struct au_hinode *p; struct au_iinfo *iinfo; err = au_ii_refresh(inode, &update); if (unlikely(err)) goto out; update = 0; iinfo = au_ii(inode); p = iinfo->ii_hinode + iinfo->ii_bstart; mode = (inode->i_mode & S_IFMT); isdir = S_ISDIR(mode); flags = au_hi_flags(inode, isdir); bend = au_dbend(dentry); for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { struct inode *h_i; struct dentry *h_d; h_d = au_h_dptr(dentry, bindex); if (!h_d || !h_d->d_inode) continue; AuDebugOn(mode != (h_d->d_inode->i_mode & S_IFMT)); if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { h_i = au_h_iptr(inode, bindex); if (h_i) { if (h_i == h_d->d_inode) continue; err = -EIO; break; } } if (bindex < iinfo->ii_bstart) iinfo->ii_bstart = bindex; if (iinfo->ii_bend < bindex) iinfo->ii_bend = bindex; au_set_h_iptr(inode, bindex, au_igrab(h_d->d_inode), flags); update = 1; } au_update_ibrange(inode, /*do_put_zero*/0); e = au_dy_irefresh(inode); if (unlikely(e && !err)) err = e; if (!err) au_refresh_hinode_attr(inode, update && isdir); out: AuTraceErr(err); return err; }
/* * simple tests for the del-entry operations. * following the checks in vfs, plus the parent-child relationship. */ int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, struct dentry *h_parent, int isdir) { int err; umode_t h_mode; struct dentry *h_dentry, *h_latest; struct inode *h_inode; h_dentry = au_h_dptr(dentry, bindex); h_inode = h_dentry->d_inode; if (dentry->d_inode) { err = -ENOENT; if (unlikely(!h_inode || !h_inode->i_nlink)) goto out; h_mode = h_inode->i_mode; if (!isdir) { err = -EISDIR; if (unlikely(S_ISDIR(h_mode))) goto out; } else if (unlikely(!S_ISDIR(h_mode))) { err = -ENOTDIR; goto out; } } else { /* rename(2) case */ err = -EIO; if (unlikely(h_inode)) goto out; } err = -ENOENT; /* expected parent dir is locked */ if (unlikely(h_parent != h_dentry->d_parent)) goto out; err = 0; /* * rmdir a dir may break the consistency on some filesystem. * let's try heavy test. */ err = -EACCES; if (unlikely(au_test_h_perm(h_parent->d_inode, MAY_EXEC | MAY_WRITE))) goto out; h_latest = au_sio_lkup_one(&dentry->d_name, h_parent, au_sbr(dentry->d_sb, bindex)); err = -EIO; if (IS_ERR(h_latest)) goto out; if (h_latest == h_dentry) err = 0; dput(h_latest); out: return err; }
static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, const unsigned char add_entry, aufs_bindex_t bcpup, aufs_bindex_t bstart) { int err; struct dentry *h_parent; struct inode *h_dir; if (add_entry) IMustLock(parent->d_inode); else di_write_lock_parent(parent); err = 0; if (!au_h_dptr(parent, bcpup)) { if (bstart < bcpup) err = au_cpdown_dirs(dentry, bcpup); else err = au_cpup_dirs(dentry, bcpup); } if (!err && add_entry) { h_parent = au_h_dptr(parent, bcpup); h_dir = h_parent->d_inode; mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); err = au_lkup_neg(dentry, bcpup); /* todo: no unlock here */ mutex_unlock(&h_dir->i_mutex); AuDbg("bcpup %d\n", bcpup); if (!err) { if (!dentry->d_inode) au_set_h_dptr(dentry, bstart, NULL); au_update_dbrange(dentry, /*do_put_zero*/0); } } if (!add_entry) di_write_unlock(parent); if (!err) err = bcpup; /* success */ AuTraceErr(err); return err; }
int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) { aufs_bindex_t bindex, bend; bend = au_dbend(dentry); for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) if (au_h_dptr(dentry, bindex) == h_dentry) return bindex; return -1; }
int au_refresh_hinode(struct inode *inode, struct dentry *dentry) { int err, update; unsigned int flags; aufs_bindex_t bindex, bend; unsigned char isdir; struct inode *first; struct au_hinode *p; struct au_iinfo *iinfo; err = au_refresh_hinode_self(inode, /*do_attr*/0); if (unlikely(err)) goto out; update = 0; iinfo = au_ii(inode); p = iinfo->ii_hinode + iinfo->ii_bstart; first = p->hi_inode; isdir = S_ISDIR(inode->i_mode); flags = au_hi_flags(inode, isdir); bend = au_dbend(dentry); for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { struct inode *h_i; struct dentry *h_d; h_d = au_h_dptr(dentry, bindex); if (!h_d || !h_d->d_inode) continue; if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { h_i = au_h_iptr(inode, bindex); if (h_i) { if (h_i == h_d->d_inode) continue; err = -EIO; break; } } if (bindex < iinfo->ii_bstart) iinfo->ii_bstart = bindex; if (iinfo->ii_bend < bindex) iinfo->ii_bend = bindex; au_set_h_iptr(inode, bindex, au_igrab(h_d->d_inode), flags); update = 1; } au_update_brange(inode, /*do_put_zero*/0); if (unlikely(err)) goto out; au_refresh_hinode_attr(inode, update && isdir); out: return err; }
static noinline_for_stack int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, struct au_cpup_reg_attr *h_src_attr) { int err, sbits; struct iattr ia; struct path h_path; struct inode *h_isrc, *h_idst; struct kstat *h_st; h_path.dentry = au_h_dptr(dst, bindex); h_idst = h_path.dentry->d_inode; h_path.mnt = au_sbr_mnt(dst->d_sb, bindex); h_isrc = h_src->d_inode; ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID | ATTR_ATIME | ATTR_MTIME | ATTR_ATIME_SET | ATTR_MTIME_SET; if (h_src_attr && h_src_attr->valid) { h_st = &h_src_attr->st; ia.ia_uid = h_st->uid; ia.ia_gid = h_st->gid; ia.ia_atime = h_st->atime; ia.ia_mtime = h_st->mtime; if (h_idst->i_mode != h_st->mode && !S_ISLNK(h_idst->i_mode)) { ia.ia_valid |= ATTR_MODE; ia.ia_mode = h_st->mode; } sbits = !!(h_st->mode & (S_ISUID | S_ISGID)); au_cpup_attr_flags(h_idst, h_src_attr->iflags); } else { ia.ia_uid = h_isrc->i_uid; ia.ia_gid = h_isrc->i_gid; ia.ia_atime = h_isrc->i_atime; ia.ia_mtime = h_isrc->i_mtime; if (h_idst->i_mode != h_isrc->i_mode && !S_ISLNK(h_idst->i_mode)) { ia.ia_valid |= ATTR_MODE; ia.ia_mode = h_isrc->i_mode; } sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); au_cpup_attr_flags(h_idst, h_isrc->i_flags); } err = vfsub_notify_change(&h_path, &ia); /* is this nfs only? */ if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) { ia.ia_valid = ATTR_FORCE | ATTR_MODE; ia.ia_mode = h_isrc->i_mode; err = vfsub_notify_change(&h_path, &ia); } return err; }
static int au_ren_lock(struct au_ren_args *a) { int err; unsigned int udba; err = 0; a->src_h_parent = au_h_dptr(a->src_parent, a->btgt); a->src_hdir = au_hi(a->src_dir, a->btgt); a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); a->dst_hdir = au_hi(a->dst_dir, a->btgt); a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, a->dst_h_parent, a->dst_hdir); udba = au_opt_udba(a->src_dentry->d_sb); if (unlikely(a->src_hdir->hi_inode != a->src_h_parent->d_inode || a->dst_hdir->hi_inode != a->dst_h_parent->d_inode)) err = au_busy_or_stale(); if (!err && au_dbstart(a->src_dentry) == a->btgt) err = au_h_verify(a->src_h_dentry, udba, a->src_h_parent->d_inode, a->src_h_parent, a->br); if (!err && au_dbstart(a->dst_dentry) == a->btgt) err = au_h_verify(a->dst_h_dentry, udba, a->dst_h_parent->d_inode, a->dst_h_parent, a->br); if (!err) { err = mnt_want_write(a->br->br_mnt); if (unlikely(err)) goto out_unlock; au_fset_ren(a->flags, MNT_WRITE); goto out; /* success */ } err = au_busy_or_stale(); out_unlock: au_ren_unlock(a); out: return err; }
/* side effect: sets whlist and h_dentry */ static int au_ren_may_dir(struct au_ren_args *a) { int err; unsigned int rdhash; struct dentry *d; d = a->dst_dentry; SiMustAnyLock(d->d_sb); err = 0; if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) { rdhash = au_sbi(d->d_sb)->si_rdhash; if (!rdhash) rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS); if (unlikely(err)) goto out; au_set_dbstart(d, a->dst_bstart); err = may_rename_dstdir(d, &a->whlist); au_set_dbstart(d, a->btgt); } a->dst_h_dentry = au_h_dptr(d, au_dbstart(d)); if (unlikely(err)) goto out; d = a->src_dentry; a->src_h_dentry = au_h_dptr(d, au_dbstart(d)); if (au_ftest_ren(a->flags, ISDIR)) { err = may_rename_srcdir(d, a->btgt); if (unlikely(err)) { au_nhash_wh_free(&a->whlist); a->whlist.nh_num = 0; } } out: return err; }
/* * unlink the topmost h_dentry * Note: the target file MAY be modified by UDBA between this mutex_unlock() and * mutex_lock() in vfs_unlink(). in this case, such changes may be lost. */ static int au_do_unlink(const unsigned char dmsg, struct au_mvd_args *a) { int err; struct path h_path; h_path.mnt = au_sbr_mnt(a->sb, a->mvd_bsrc); h_path.dentry = au_h_dptr(a->dentry, a->mvd_bsrc); err = vfsub_unlink(a->mvd_h_src_dir, &h_path, /*force*/0); if (unlikely(err)) AU_MVD_PR(dmsg, "unlink failed\n"); AuTraceErr(err); return err; }
int au_reopen_nondir(struct file *file) { int err; struct dentry *dentry; aufs_bindex_t bstart, bindex, bend; struct file *hidden_file, *h_file_tmp; dentry = file->f_dentry; LKTRTrace("%.*s\n", DLNPair(dentry)); DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode) || !au_h_dptr(dentry)->d_inode); bstart = dbstart(dentry); h_file_tmp = NULL; if (fbstart(file) == bstart) { hidden_file = au_h_fptr(file); if (file->f_mode == hidden_file->f_mode) return 0; /* success */ h_file_tmp = hidden_file; get_file(h_file_tmp); set_h_fptr(file, bstart, NULL); } DEBUG_ON(fbstart(file) < bstart || ftofi(file)->fi_hfile[0 + bstart].hf_file); hidden_file = hidden_open(dentry, bstart, file->f_flags & ~O_TRUNC); //if (LktrCond) {fput(hidden_file); br_put(stobr(dentry->d_sb, bstart)); //hidden_file = ERR_PTR(-1);} err = PTR_ERR(hidden_file); if (IS_ERR(hidden_file)) goto out; // close all? err = 0; //cpup_file_flags(hidden_file, file); set_fbstart(file, bstart); set_h_fptr(file, bstart, hidden_file); memcpy(&hidden_file->f_ra, &file->f_ra, sizeof(file->f_ra)); //?? /* close lower files */ bend = fbend(file); for (bindex = bstart + 1; bindex <= bend; bindex++) set_h_fptr(file, bindex, NULL); set_fbend(file, bstart); out: if (h_file_tmp) fput(h_file_tmp); TraceErr(err); return err; }
/* make the parent dir on bdst */ static int au_do_mkdir(const unsigned char dmsg, struct au_mvd_args *a) { int err; err = 0; a->mvd_hdir_src = au_hi(a->dir, a->mvd_bsrc); a->mvd_hdir_dst = au_hi(a->dir, a->mvd_bdst); a->mvd_h_src_parent = au_h_dptr(a->parent, a->mvd_bsrc); a->mvd_h_dst_parent = NULL; if (au_dbend(a->parent) >= a->mvd_bdst) a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); if (!a->mvd_h_dst_parent) { err = au_cpdown_dirs(a->dentry, a->mvd_bdst); if (unlikely(err)) { AU_MVD_PR(dmsg, "cpdown_dirs failed\n"); goto out; } a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); } out: AuTraceErr(err); return err; }
static noinline_for_stack int au_do_h_d_reval(struct dentry *h_dentry, struct nameidata *nd, struct dentry *dentry, aufs_bindex_t bindex) { int err, valid; int (*reval)(struct dentry *, struct nameidata *); err = 0; reval = NULL; if (h_dentry->d_op) reval = h_dentry->d_op->d_revalidate; if (!reval) goto out; AuDbg("b%d\n", bindex); if (au_test_fs_null_nd(h_dentry->d_sb)) /* it may return tri-state */ valid = reval(h_dentry, NULL); else { struct nameidata h_nd; int locked; struct dentry *parent; au_h_nd(&h_nd, nd); parent = nd->path.dentry; locked = (nd && nd->path.dentry != dentry); if (locked) di_read_lock_parent(parent, AuLock_IR); BUG_ON(bindex > au_dbend(parent)); h_nd.path.dentry = au_h_dptr(parent, bindex); BUG_ON(!h_nd.path.dentry); h_nd.path.mnt = au_sbr(parent->d_sb, bindex)->br_mnt; path_get(&h_nd.path); valid = reval(h_dentry, &h_nd); path_put(&h_nd.path); if (locked) di_read_unlock(parent, AuLock_IR); } if (unlikely(valid < 0)) err = valid; else if (!valid) err = -EINVAL; out: AuTraceErr(err); return err; }
static int do_open_dir(struct file *file, int flags, struct file *h_file) { int err; aufs_bindex_t bindex, btail; struct dentry *dentry, *h_dentry; struct vfsmount *mnt; FiMustWriteLock(file); AuDebugOn(h_file); err = 0; mnt = file->f_path.mnt; dentry = file->f_path.dentry; file->f_version = d_inode(dentry)->i_version; bindex = au_dbtop(dentry); au_set_fbtop(file, bindex); btail = au_dbtaildir(dentry); au_set_fbbot_dir(file, btail); for (; !err && bindex <= btail; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (!h_dentry) continue; err = vfsub_test_mntns(mnt, h_dentry->d_sb); if (unlikely(err)) break; h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); 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_fbtop(file); bindex <= btail; bindex++) au_set_h_fptr(file, bindex, NULL); au_set_fbtop(file, -1); au_set_fbbot_dir(file, -1); 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; }
void au_update_dbend(struct dentry *dentry) { aufs_bindex_t bindex, bstart; struct dentry *h_dentry; bstart = au_dbstart(dentry); for (bindex = au_dbend(dentry); bindex >= bstart; bindex--) { h_dentry = au_h_dptr(dentry, bindex); if (!h_dentry) continue; if (h_dentry->d_inode) { au_set_dbend(dentry, bindex); return; } au_set_h_dptr(dentry, bindex, NULL); } }
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; }
/* * when we have to copyup the renaming entry, do it with the rename-target name * in order to minimize the cost (the later actual rename is unnecessary). * otherwise rename it on the target branch. */ static int au_ren_or_cpup(struct au_ren_args *a) { int err; struct dentry *d; d = a->src_dentry; if (au_dbstart(d) == a->btgt) { a->h_path.dentry = a->dst_h_dentry; if (au_ftest_ren(a->flags, DIROPQ) && au_dbdiropq(d) == a->btgt) au_fclr_ren(a->flags, DIROPQ); AuDebugOn(au_dbstart(d) != a->btgt); err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), a->dst_h_dir, &a->h_path); } else { struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex; struct file *h_file; au_fset_ren(a->flags, CPUP); mutex_lock_nested(h_mtx, AuLsc_I_CHILD); au_set_dbstart(d, a->btgt); au_set_h_dptr(d, a->btgt, dget(a->dst_h_dentry)); h_file = au_h_open_pre(d, a->src_bstart); if (IS_ERR(h_file)) { err = PTR_ERR(h_file); h_file = NULL; } else err = au_sio_cpup_single(d, a->btgt, a->src_bstart, -1, !AuCpup_DTIME, a->dst_parent); mutex_unlock(h_mtx); au_h_open_post(d, a->src_bstart, h_file); if (!err) { d = a->dst_dentry; au_set_h_dptr(d, a->btgt, NULL); au_update_dbstart(d); } else { au_set_h_dptr(d, a->btgt, NULL); au_set_dbstart(d, a->src_bstart); } } if (!err && a->h_dst) /* it will be set to dinfo later */ dget(a->h_dst); return err; }
/* * extended version of au_h_dptr(). * returns a hashed and positive (or linkable) h_dentry in bindex, NULL, or * error. */ struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex) { struct dentry *h_dentry; struct inode *inode, *h_inode; inode = dentry->d_inode; AuDebugOn(!inode); h_dentry = NULL; if (au_dbstart(dentry) <= bindex && bindex <= au_dbend(dentry)) h_dentry = au_h_dptr(dentry, bindex); if (h_dentry && !au_d_linkable(h_dentry)) { dget(h_dentry); goto out; /* success */ } AuDebugOn(bindex < au_ibstart(inode)); AuDebugOn(au_ibend(inode) < bindex); h_inode = au_h_iptr(inode, bindex); h_dentry = d_find_alias(h_inode); if (h_dentry) { if (!IS_ERR(h_dentry)) { if (!au_d_linkable(h_dentry)) goto out; /* success */ dput(h_dentry); } else goto out; } if (au_opt_test(au_mntflags(dentry->d_sb), PLINK)) { h_dentry = au_plink_lkup(inode, bindex); AuDebugOn(!h_dentry); if (!IS_ERR(h_dentry)) { if (!au_d_hashed_positive(h_dentry)) goto out; /* success */ dput(h_dentry); h_dentry = NULL; } } out: AuDbgDentry(h_dentry); return h_dentry; }
static struct dentry * au_h_dget_any(struct dentry *dentry, aufs_bindex_t *bindex) { struct dentry *h_dentry; struct inode *inode, *h_inode; struct super_block *sb; aufs_bindex_t ib, db; /* must be positive dentry */ inode = dentry->d_inode; LKTRTrace("%.*s, i%lu\n", AuDLNPair(dentry), inode->i_ino); sb = dentry->d_sb; db = au_dbstart(dentry); ib = au_ibstart(inode); if (db == ib) { *bindex = db; h_dentry = dget(au_h_dptr(dentry, db)); if (h_dentry) goto out; /* success */ } *bindex = ib; h_inode = au_h_iptr(inode, ib); h_dentry = d_find_alias(h_inode); if (h_dentry) goto out; /* success */ #if 0 if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(sb, inode)) { h_dentry = au_plink_lkup(sb, ib, inode); if (IS_ERR(h_dentry)) goto out; AuDebugOn(!h_dentry->d_inode); goto out; /* success */ } #endif h_dentry = dget(au_hi_wh(inode, ib)); out: AuTraceErrPtr(h_dentry); return h_dentry; }
struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, int force_wr) { struct file *h_file; struct dentry *h_dentry; h_dentry = au_h_dptr(dentry, bindex); AuDebugOn(!h_dentry); AuDebugOn(!h_dentry->d_inode); h_file = NULL; if (au_test_hfsplus(h_dentry->d_sb) && S_ISREG(h_dentry->d_inode->i_mode)) h_file = au_h_open(dentry, bindex, O_RDONLY | O_NOATIME | O_LARGEFILE, /*file*/NULL, force_wr); return h_file; }
/* * successful returns with iinfo write_locked * minus: errno * zero: success, matched * plus: no error, but unmatched */ static int reval_inode(struct inode *inode, struct dentry *dentry) { int err; unsigned int gen; struct au_iigen iigen; aufs_bindex_t bindex, bend; struct inode *h_inode, *h_dinode; /* * before this function, if aufs got any iinfo lock, it must be only * one, the parent dir. * it can happen by UDBA and the obsoleted inode number. */ err = -EIO; if (unlikely(inode->i_ino == parent_ino(dentry))) goto out; err = 1; ii_write_lock_new_child(inode); h_dinode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode; bend = au_ibend(inode); for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { h_inode = au_h_iptr(inode, bindex); if (!h_inode || h_inode != h_dinode) continue; err = 0; gen = au_iigen(inode, &iigen); if (gen == au_digen(dentry) && !au_ig_ftest(iigen.ig_flags, HALF_REFRESHED)) break; /* fully refresh inode using dentry */ err = au_refresh_hinode(inode, dentry); if (!err) au_update_iigen(inode, /*half*/0); break; } if (unlikely(err)) ii_write_unlock(inode); out: return err; }
/* * simple tests for the adding inode operations. * following the checks in vfs, plus the parent-child relationship. */ int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, struct dentry *h_parent, int isdir) { int err; umode_t h_mode; struct dentry *h_dentry; struct inode *h_inode; err = -ENAMETOOLONG; if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) goto out; h_dentry = au_h_dptr(dentry, bindex); h_inode = h_dentry->d_inode; if (!dentry->d_inode) { err = -EEXIST; if (unlikely(h_inode)) goto out; } else { /* rename(2) case */ err = -EIO; if (unlikely(!h_inode || !h_inode->i_nlink)) goto out; h_mode = h_inode->i_mode; if (!isdir) { err = -EISDIR; if (unlikely(S_ISDIR(h_mode))) goto out; } else if (unlikely(!S_ISDIR(h_mode))) { err = -ENOTDIR; goto out; } } err = 0; /* expected parent dir is locked */ if (unlikely(h_parent != h_dentry->d_parent)) err = -EIO; out: AuTraceErr(err); return err; }
static noinline_for_stack int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, struct au_hinode *hdir, struct vfsub_args *vargs) { int err, sbits; struct dentry *h_dst; struct iattr ia; struct inode *h_isrc, *h_idst; h_dst = au_h_dptr(dst, bindex); LKTRTrace("%.*s\n", AuDLNPair(h_dst)); h_idst = h_dst->d_inode; /* todo? IMustLock(h_idst); */ h_isrc = h_src->d_inode; /* todo? IMustLock(h_isrc); */ ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_ATIME | ATTR_MTIME | ATTR_ATIME_SET | ATTR_MTIME_SET; ia.ia_mode = h_isrc->i_mode; ia.ia_uid = h_isrc->i_uid; ia.ia_gid = h_isrc->i_gid; ia.ia_atime = h_isrc->i_atime; ia.ia_mtime = h_isrc->i_mtime; sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); au_cpup_attr_flags(h_idst, h_isrc); vfsub_args_reinit(vargs); vfsub_ign_hinode(vargs, IN_ATTRIB, hdir); err = vfsub_notify_change(h_dst, &ia, vargs); /* is this nfs only? */ if (!err && sbits && au_test_nfs(h_dst->d_sb)) { ia.ia_valid = ATTR_FORCE | ATTR_MODE; ia.ia_mode = h_isrc->i_mode; vfsub_args_reinit(vargs); vfsub_ign_hinode(vargs, IN_ATTRIB, hdir); err = vfsub_notify_change(h_dst, &ia, vargs); } AuTraceErr(err); return err; }
/* * the lifetime of branch is independent from the entry under sysfs. * sysfs handles the lifetime of the entry, and never call ->show() after it is * unlinked. */ static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, aufs_bindex_t bindex) { struct path path; struct dentry *root; struct au_branch *br; AuDbg("b%d\n", bindex); root = sb->s_root; di_read_lock_parent(root, !AuLock_IR); br = au_sbr(sb, bindex); path.mnt = br->br_mnt; path.dentry = au_h_dptr(root, bindex); au_seq_path(seq, &path); di_read_unlock(root, !AuLock_IR); seq_printf(seq, "=%s\n", au_optstr_br_perm(br->br_perm)); return 0; }
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; }
loff_t au_dir_size(struct file *file, struct dentry *dentry) { loff_t sz; aufs_bindex_t bindex, bbot; struct file *h_file; struct dentry *h_dentry; sz = 0; if (file) { AuDebugOn(!d_is_dir(file->f_path.dentry)); bbot = au_fbbot_dir(file); for (bindex = au_fbtop(file); bindex <= bbot && sz < KMALLOC_MAX_SIZE; bindex++) { h_file = au_hf_dir(file, bindex); if (h_file && file_inode(h_file)) sz += vfsub_f_size_read(h_file); } } else { AuDebugOn(!dentry); AuDebugOn(!d_is_dir(dentry)); bbot = au_dbtaildir(dentry); for (bindex = au_dbtop(dentry); bindex <= bbot && sz < KMALLOC_MAX_SIZE; bindex++) { h_dentry = au_h_dptr(dentry, bindex); if (h_dentry && d_is_positive(h_dentry)) sz += i_size_read(d_inode(h_dentry)); } } 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 au_opt_xino(struct super_block *sb, struct au_opt *opt, struct au_opt_xino **opt_xino, struct au_opts *opts) { int err; aufs_bindex_t bend, bindex; struct dentry *root, *parent, *h_root; err = 0; switch (opt->type) { case Opt_xino: err = au_xino_set(sb, &opt->xino, !!au_ftest_opts(opts->flags, REMOUNT)); if (unlikely(err)) break; *opt_xino = &opt->xino; au_xino_brid_set(sb, -1); /* safe d_parent access */ parent = opt->xino.file->f_dentry->d_parent; root = sb->s_root; bend = au_sbend(sb); for (bindex = 0; bindex <= bend; bindex++) { h_root = au_h_dptr(root, bindex); if (h_root == parent) { au_xino_brid_set(sb, au_sbr_id(sb, bindex)); break; } } break; case Opt_noxino: au_xino_clr(sb); au_xino_brid_set(sb, -1); *opt_xino = (void *)-1; break; } return err; }
/* * the lifetime of branch is independent from the entry under sysfs. * sysfs handles the lifetime of the entry, and never call ->show() after it is * unlinked. */ static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, aufs_bindex_t bindex, int idx) { int err; struct path path; struct dentry *root; struct au_branch *br; char *perm; AuDbg("b%d\n", bindex); err = 0; root = sb->s_root; di_read_lock_parent(root, !AuLock_IR); br = au_sbr(sb, bindex); switch (idx) { case AuBrSysfs_BR: path.mnt = au_br_mnt(br); path.dentry = au_h_dptr(root, bindex); au_seq_path(seq, &path); di_read_unlock(root, !AuLock_IR); perm = au_optstr_br_perm(br->br_perm); if (perm) { err = seq_printf(seq, "=%s\n", perm); kfree(perm); if (err == -1) err = -E2BIG; } else err = -ENOMEM; break; case AuBrSysfs_BRID: err = seq_printf(seq, "%d\n", br->br_id); di_read_unlock(root, !AuLock_IR); if (err == -1) err = -E2BIG; break; } return err; }