static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath, char *buf, int len, struct super_block *sb) { char *p; int n; AuTraceEnter(); p = d_path(h_rootpath->dentry, h_rootpath->mnt, buf, len); if (IS_ERR(p)) goto out; n = strlen(p); p = d_path(h_parent, h_rootpath->mnt, buf, len); if (IS_ERR(p)) goto out; LKTRTrace("%s\n", p); if (n != 1) p += n; LKTRTrace("%p, %s, %ld\n", p, p, (long)(p - buf)); p = d_path(sb->s_root, au_sbi(sb)->si_mnt, buf, len - strlen(p)); if (IS_ERR(p)) goto out; if (n != 1) p[strlen(p)] = '/'; LKTRTrace("%s\n", p); out: AuTraceErrPtr(p); return p; }
static /* noinline_for_stack */ struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino, ino_t dir_ino, struct au_nfsd_si_lock *nsi_lock) { struct dentry *dentry, *parent; struct path path; LKTRTrace("i%lu, diri%lu\n", (unsigned long)ino, (unsigned long)dir_ino); parent = sb->s_root; if (dir_ino != AUFS_ROOT_INO) { parent = decode_by_ino(sb, dir_ino, 0); dentry = parent; if (!parent) goto out; if (IS_ERR(parent)) goto out; AuDebugOn(au_test_anon(parent)); } else dget(parent); path.dentry = parent; path.mnt = au_sbi(sb)->si_mnt; dentry = au_lkup_by_ino(&path, ino, nsi_lock); dput(parent); out: AuTraceErrPtr(dentry); return dentry; }
ssize_t xino_fread(au_readf_t func, struct file *file, void *buf, size_t size, loff_t *pos) { ssize_t err; mm_segment_t oldfs; LKTRTrace("%.*s, sz %zu, *pos %lld\n", AuDLNPair(file->f_dentry), size, *pos); oldfs = get_fs(); set_fs(KERNEL_DS); do { /* todo: signal_pending? */ err = func(file, (char __user *)buf, size, pos); } while (err == -EAGAIN || err == -EINTR); set_fs(oldfs); #if 0 /* reserved for future use */ if (err > 0) fsnotify_access(file->f_dentry); #endif AuTraceErr(err); return err; }
int au_xigen_inc(struct inode *inode) { int err; loff_t pos; ssize_t sz; __u32 igen; struct super_block *sb; struct au_sbinfo *sbinfo; LKTRTrace("i%lu\n", (unsigned long)inode->i_ino); err = 0; sb = inode->i_sb; if (unlikely(!au_opt_test_xino(au_mntflags(sb)))) goto out; pos = inode->i_ino; pos *= sizeof(igen); igen = inode->i_generation + 1; sbinfo = au_sbi(sb); sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen, sizeof(igen), &pos); if (sz == sizeof(igen)) goto out; /* success */ err = sz; if (unlikely(sz >= 0)) { err = -EIO; AuIOErr("xigen error (%zd)\n", sz); } out: AuTraceErr(err); return err; }
void au_dtime_revert(struct au_dtime *dt) { struct iattr attr; int err; struct au_hin_ignore ign[2]; struct vfsub_args vargs; LKTRTrace("%.*s\n", AuDLNPair(dt->dt_dentry)); attr.ia_atime = dt->dt_atime; attr.ia_mtime = dt->dt_mtime; attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET | ATTR_ATIME | ATTR_ATIME_SET; vfsub_args_init(&vargs, ign, au_test_dlgt(au_mntflags(dt->dt_dentry->d_sb)), 0); /* * IN_ATTRIB should be divided into * IN_ATTRIB_ATIME, IN_ATTRIB_MTIME ..., * and define all ORed new IN_ATTRIB macro. */ vfsub_ign_hinode(&vargs, IN_ATTRIB, dt->dt_hinode); vfsub_ign_hinode(&vargs, IN_ATTRIB, dt->dt_hdir); err = vfsub_notify_change(dt->dt_h_dentry, &attr, &vargs); if (unlikely(err)) AuWarn("restoring timestamps failed(%d). ignored\n", err); }
struct dentry *vfsub__lookup_hash(struct qstr *name, struct dentry *parent, struct nameidata *nd) { struct dentry *d; LKTRTrace("%.*s/%.*s, nd %d\n", AuDLNPair(parent), AuLNPair(name), !!nd); if (nd) LKTRTrace("nd{0x%x}\n", nd->flags); IMustLock(parent->d_inode); d = __lookup_hash(name, parent, nd); if (!IS_ERR(d)) au_update_fuse_h_inode(NULL, d); /*ignore*/ return d; }
static void call_unlink(void *args) { struct unlink_args *a = args; struct inode *h_inode; const int stop_sillyrename = (au_test_nfs(a->dentry->d_sb) && atomic_read(&a->dentry->d_count) == 1); LKTRTrace("%.*s, stop_silly %d, cnt %d\n", AuDLNPair(a->dentry), stop_sillyrename, atomic_read(&a->dentry->d_count)); if (!stop_sillyrename) dget(a->dentry); h_inode = a->dentry->d_inode; if (h_inode) atomic_inc_return(&h_inode->i_count); vfsub_ignore(a->vargs); *a->errp = do_vfsub_unlink(a->dir, a->dentry); if (unlikely(*a->errp || (a->dentry->d_flags & DCACHE_NFSFS_RENAMED))) vfsub_unignore(a->vargs); au_dbg_hin_list(a->vargs); if (!stop_sillyrename) dput(a->dentry); if (h_inode) iput(h_inode); AuTraceErr(*a->errp); }
int vfsub_sio_rmdir(struct au_hinode *hdir, struct dentry *dentry, int dlgt) { int err, do_sio, wkq_err; struct inode *dir = hdir->hi_inode; struct au_hin_ignore ign; struct vfsub_args vargs; LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry)); vfsub_args_init(&vargs, &ign, dlgt, /*force_unlink*/0); vfsub_ign_hinode(&vargs, IN_DELETE, hdir); do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE, dlgt); if (!do_sio) err = vfsub_rmdir(dir, dentry, &vargs); else { struct au_vfsub_rmdir_args args = { .errp = &err, .dir = dir, .dentry = dentry, .vargs = &vargs }; vfsub_fclr(vargs.flags, DLGT); wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args, /*dlgt*/0); if (unlikely(wkq_err)) err = wkq_err; } AuTraceErr(err); return err; }
int vfsub_sio_notify_change(struct au_hinode *hdir, struct dentry *dentry, struct iattr *ia) { int err, wkq_err; struct au_hin_ignore ign; struct vfsub_args vargs; __u32 events; struct notify_change_args args = { .errp = &err, .h_dentry = dentry, .ia = ia, .vargs = &vargs }; LKTRTrace("%.*s, 0x%x\n", AuDLNPair(dentry), ia->ia_valid); vfsub_args_init(&vargs, &ign, /*dlgt*/0, /*force_unlink*/0); events = vfsub_events_notify_change(ia); if (events) vfsub_ign_hinode(&vargs, events, hdir); wkq_err = au_wkq_wait(call_notify_change, &args, /*dlgt*/0); if (unlikely(wkq_err)) err = wkq_err; AuTraceErr(err); return err; }
void au_cpup_attr_nlink(struct inode *inode, int force) { struct inode *h_inode; struct super_block *sb; aufs_bindex_t bindex, bend; LKTRTrace("i%lu\n", inode->i_ino); /* todo? IMustLock(inode); */ AuDebugOn(!inode->i_mode); sb = inode->i_sb; bindex = au_ibstart(inode); h_inode = au_h_iptr(inode, bindex); if (!force && !S_ISDIR(h_inode->i_mode) && au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(sb, inode)) return; inode->i_nlink = h_inode->i_nlink; /* * fewer nlink makes find(1) noisy, but larger nlink doesn't. * it may includes whplink directory. */ if (S_ISDIR(h_inode->i_mode)) { bend = au_ibend(inode); for (bindex++; bindex <= bend; bindex++) { h_inode = au_h_iptr(inode, bindex); if (h_inode) au_add_nlink(inode, h_inode); } } }
ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size, loff_t *pos) { ssize_t err; LKTRTrace("%.*s, sz %zu, *pos %lld\n", AuDLNPair(file->f_dentry), size, *pos); /* todo: signal block and no wkq? */ /* * it breaks RLIMIT_FSIZE and normal user's limit, * users should care about quota and real 'filesystem full.' */ if (!au_test_wkq(current)) { int wkq_err; struct do_xino_fwrite_args args = { .errp = &err, .func = func, .file = file, .buf = buf, .size = size, .pos = pos }; wkq_err = au_wkq_wait(call_do_xino_fwrite, &args, /*dlgt*/0); if (unlikely(wkq_err)) err = wkq_err; } else
/* common functions to regular file and dir */ struct file *hidden_open(struct dentry *dentry, aufs_bindex_t bindex, int flags) { struct dentry *hidden_dentry; struct inode *hidden_inode; struct super_block *sb; struct vfsmount *hidden_mnt; struct file *hidden_file; struct aufs_branch *br; loff_t old_size; int udba; LKTRTrace("%.*s, b%d, flags 0%o\n", DLNPair(dentry), bindex, flags); DEBUG_ON(!dentry); hidden_dentry = au_h_dptr_i(dentry, bindex); DEBUG_ON(!hidden_dentry); hidden_inode = hidden_dentry->d_inode; DEBUG_ON(!hidden_inode); sb = dentry->d_sb; udba = au_flag_test(sb, AuFlag_UDBA_INOTIFY); if (unlikely(udba)) { // test here? } br = stobr(sb, bindex); br_get(br); /* drop flags for writing */ if (test_ro(sb, bindex, dentry->d_inode)) flags = au_file_roflags(flags); flags &= ~O_CREAT; spin_lock(&hidden_inode->i_lock); old_size = i_size_read(hidden_inode); spin_unlock(&hidden_inode->i_lock); //DbgSleep(3); dget(hidden_dentry); hidden_mnt = mntget(br->br_mnt); hidden_file = dentry_open(hidden_dentry, hidden_mnt, flags); //if (LktrCond) {fput(hidden_file); hidden_file = ERR_PTR(-1);} if (!IS_ERR(hidden_file)) { #if 0 // remove this if (/* old_size && */ (flags & O_TRUNC)) { au_direval_dec(dentry); if (!IS_ROOT(dentry)) au_direval_dec(dentry->d_parent); } #endif return hidden_file; } br_put(br); TraceErrPtr(hidden_file); return hidden_file; }
void di_write_unlock(struct dentry *d) { LKTRTrace("%.*s\n", AuDLNPair(d)); SiMustAnyLock(d->d_sb); if (d->d_inode) ii_write_unlock(d->d_inode); au_rw_write_unlock(&au_di(d)->di_rwsem); au_dbg_locked_di_unreg(d, AuLock_IW); }
int au_xigen_new(struct inode *inode) { int err; loff_t pos; ssize_t sz; struct super_block *sb; struct au_sbinfo *sbinfo; struct file *file; LKTRTrace("i%lu\n", inode->i_ino); err = 0; /* todo: dirty, at mount time */ if (inode->i_ino == AUFS_ROOT_INO) goto out; sb = inode->i_sb; if (unlikely(!au_opt_test_xino(au_mntflags(sb)))) goto out; err = -EFBIG; pos = inode->i_ino; if (unlikely(Au_LOFF_MAX / sizeof(inode->i_generation) - 1 < pos)) { AuIOErr1("too large i%lld\n", pos); goto out; } pos *= sizeof(inode->i_generation); err = 0; sbinfo = au_sbi(sb); file = sbinfo->si_xigen; BUG_ON(!file); if (i_size_read(file->f_dentry->d_inode) < pos + sizeof(inode->i_generation)) { inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next); sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation, sizeof(inode->i_generation), &pos); } else sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation, sizeof(inode->i_generation), &pos); if (sz == sizeof(inode->i_generation)) goto out; /* success */ err = sz; if (unlikely(sz >= 0)) { err = -EIO; AuIOErr("xigen error (%zd)\n", sz); } out: AuTraceErr(err); return err; }
struct file *vfsub_filp_open(const char *path, int oflags, int mode) { struct file *err; LKTRTrace("%s\n", path); lockdep_off(); err = filp_open(path, oflags, mode); lockdep_on(); if (!IS_ERR(err)) au_update_fuse_h_inode(err->f_vfsmnt, err->f_dentry); /*ignore*/ return err; }
void au_cpup_attr_timesizes(struct inode *inode) { struct inode *h_inode; LKTRTrace("i%lu\n", inode->i_ino); /* todo? IMustLock(inode); */ h_inode = au_h_iptr(inode, au_ibstart(inode)); AuDebugOn(!h_inode); /* todo? IMustLock(!h_inode); */ fsstack_copy_attr_times(inode, h_inode); vfsub_copy_inode_size(inode, h_inode); }
void di_write_lock(struct dentry *d, unsigned int lsc) { LKTRTrace("%.*s, %u\n", AuDLNPair(d), lsc); SiMustAnyLock(d->d_sb); /* todo: always nested? */ au_dbg_locking_di_reg(d, AuLock_IW, lsc); au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc); au_dbg_locking_di_unreg(d, AuLock_IW); au_dbg_locked_di_reg(d, AuLock_IW, lsc); if (d->d_inode) do_ii_write_lock(d->d_inode, lsc); }
struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, int len) { struct dentry *d; LKTRTrace("%.*s/%.*s\n", AuDLNPair(parent), len, name); IMustLock(parent->d_inode); d = lookup_one_len(name, parent, len); if (!IS_ERR(d)) au_update_fuse_h_inode(NULL, d); /*ignore*/ return d; }
int do_vfsub_readdir(struct file *file, filldir_t filldir, void *arg) { int err; LKTRTrace("%.*s\n", AuDLNPair(file->f_dentry)); lockdep_off(); err = vfs_readdir(file, filldir, arg); lockdep_on(); if (err >= 0) au_update_fuse_h_inode(file->f_vfsmnt, file->f_dentry); /*ignore*/ return err; }
int do_vfsub_link(struct dentry *src_dentry, struct inode *dir, struct dentry *dentry) { int err; LKTRTrace("%.*s, i%lu, %.*s\n", AuDLNPair(src_dentry), dir->i_ino, AuDLNPair(dentry)); IMustLock(dir); lockdep_off(); err = vfs_link(src_dentry, dir, dentry); lockdep_on(); if (!err) { LKTRTrace("src_i %p, dst_i %p\n", src_dentry->d_inode, dentry->d_inode); /* fuse has different memory inode for the same inumber */ au_update_fuse_h_inode(NULL, src_dentry); /*ignore*/ /* dir inode is locked */ au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/ au_update_fuse_h_inode(NULL, dentry); /*ignore*/ } return err; }
void di_read_unlock(struct dentry *d, int flags) { LKTRTrace("%.*s\n", AuDLNPair(d)); SiMustAnyLock(d->d_sb); if (d->d_inode) { if (au_ftest_lock(flags, IW)) ii_write_unlock(d->d_inode); else if (au_ftest_lock(flags, IR)) ii_read_unlock(d->d_inode); } au_rw_read_unlock(&au_di(d)->di_rwsem); au_dbg_locked_di_unreg(d, flags); }
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; }
int do_vfsub_rmdir(struct inode *dir, struct dentry *dentry) { int err; LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry)); IMustLock(dir); lockdep_off(); err = vfs_rmdir(dir, dentry); lockdep_on(); /* dir inode is locked */ if (!err) au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/ return err; }
void au_cpup_attr_changeable(struct inode *inode) { struct inode *h_inode; LKTRTrace("i%lu\n", inode->i_ino); /* todo? IMustLock(inode); */ h_inode = au_h_iptr(inode, au_ibstart(inode)); AuDebugOn(!h_inode); inode->i_mode = h_inode->i_mode; inode->i_uid = h_inode->i_uid; inode->i_gid = h_inode->i_gid; au_cpup_attr_timesizes(inode); au_cpup_attr_flags(inode, h_inode); }
long do_vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, loff_t *ppos, size_t len, unsigned int flags) { long err; LKTRTrace("%.*s, pos %lld, len %zu, 0x%x\n", AuDLNPair(out->f_dentry), *ppos, len, flags); lockdep_off(); err = vfs_splice_from(pipe, out, ppos, len, flags); lockdep_on(); if (err >= 0) au_update_fuse_h_inode(out->f_vfsmnt, out->f_dentry); /*ignore*/ return err; }
int vfsub_path_lookup(const char *name, unsigned int flags, struct nameidata *nd) { int err; LKTRTrace("%s\n", name); /* lockdep_off(); */ err = path_lookup(name, flags, nd); /* lockdep_on(); */ if (!err) au_update_fuse_h_inode(nd->mnt, nd->dentry); /*ignore*/ return err; }
int do_vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode) { int err; LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, AuDLNPair(dentry), mode); IMustLock(dir); err = vfs_mkdir(dir, dentry, mode); if (!err) { /* dir inode is locked */ au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/ au_update_fuse_h_inode(NULL, dentry); /*ignore*/ } return err; }
ssize_t do_vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { ssize_t err; LKTRTrace("%.*s, cnt %zu, pos %lld\n", AuDLNPair(file->f_dentry), count, *ppos); lockdep_off(); err = vfs_write(file, ubuf, count, ppos); lockdep_on(); if (err >= 0) au_update_fuse_h_inode(file->f_vfsmnt, file->f_dentry); /*ignore*/ return err; }
static int do_coo(struct dentry *dentry, aufs_bindex_t bstart) { int err; struct dentry *parent, *h_parent, *h_dentry; aufs_bindex_t bcpup; struct inode *h_dir, *h_inode, *dir; LKTRTrace("%.*s\n", DLNPair(dentry)); DEBUG_ON(IS_ROOT(dentry)); DiMustWriteLock(dentry); parent = dentry->d_parent; // dget_parent() di_write_lock_parent(parent); bcpup = err = find_rw_parent_br(dentry, bstart); //bcpup = err = find_rw_br(sb, bstart); if (unlikely(err < 0)) { err = 0; // stop copyup, it is not an error goto out; } err = 0; h_parent = au_h_dptr_i(parent, bcpup); if (!h_parent) { err = cpup_dirs(dentry, bcpup, NULL); if (unlikely(err)) goto out; h_parent = au_h_dptr_i(parent, bcpup); } h_dir = h_parent->d_inode; h_dentry = au_h_dptr_i(dentry, bstart); h_inode = h_dentry->d_inode; dir = parent->d_inode; hdir_lock(h_dir, dir, bcpup); hi_lock_child(h_inode); DEBUG_ON(au_h_dptr_i(dentry, bcpup)); err = sio_cpup_simple(dentry, bcpup, -1, au_flags_cpup(CPUP_DTIME, parent)); TraceErr(err); i_unlock(h_inode); hdir_unlock(h_dir, dir, bcpup); out: di_write_unlock(parent); TraceErr(err); return err; }
void di_read_lock(struct dentry *d, int flags, unsigned int lsc) { LKTRTrace("%.*s, %u\n", AuDLNPair(d), lsc); SiMustAnyLock(d->d_sb); /* todo: always nested? */ au_dbg_locking_di_reg(d, flags, lsc); au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc); au_dbg_locking_di_unreg(d, flags); au_dbg_locked_di_reg(d, flags, lsc); if (d->d_inode) { if (au_ftest_lock(flags, IW)) do_ii_write_lock(d->d_inode, lsc); else if (au_ftest_lock(flags, IR)) do_ii_read_lock(d->d_inode, lsc); } }