static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry) { struct dentry *wh = NULL; int hn; if (!dentry || IS_ERR(dentry)) { dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry)); return -1; } /* do not call dget_parent() here */ /* note: access d_xxx without d_lock */ dpri("d%d: %.*s?/%.*s, %s, cnt %d, flags 0x%x\n", bindex, AuDLNPair(dentry->d_parent), AuDLNPair(dentry), dentry->d_sb ? au_sbtype(dentry->d_sb) : "??", d_count(dentry), dentry->d_flags); hn = -1; if (bindex >= 0 && dentry->d_inode && au_test_aufs(dentry->d_sb)) { struct au_iinfo *iinfo = au_ii(dentry->d_inode); if (iinfo) { hn = !!au_hn(iinfo->ii_hinode + bindex); wh = iinfo->ii_hinode[0 + bindex].hi_whdentry; } } do_pri_inode(bindex, dentry->d_inode, hn, wh); return 0; }
static void au_ren_rev_whtmp(int err, struct au_ren_args *a) { int rerr; a->h_path.dentry = au_lkup_one(&a->dst_dentry->d_name, a->dst_h_parent, a->br, /*nd*/NULL); rerr = PTR_ERR(a->h_path.dentry); if (IS_ERR(a->h_path.dentry)) { RevertFailure("lookup %.*s", AuDLNPair(a->dst_dentry)); return; } if (a->h_path.dentry->d_inode) { d_drop(a->h_path.dentry); dput(a->h_path.dentry); return; } rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path); d_drop(a->h_path.dentry); dput(a->h_path.dentry); if (!rerr) au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst)); else RevertFailure("rename %.*s", AuDLNPair(a->h_dst)); }
/* * for the moment, aufs supports the branch filesystem which does not support * link(2). testing on FAT which does not support i_op->setattr() fully either, * copyup failed. finally, such filesystem will not be used as the writable * branch. * * returns tri-state, see above. */ static int au_wh_init_rw(struct dentry *h_root, struct au_wbr *wbr, int do_plink, struct au_wh_base base[], struct path *h_path) { int err; struct inode *h_dir; WbrWhMustWriteLock(wbr); err = test_linkable(h_root); if (unlikely(err)) { err = 1; goto out; } /* * todo: should this create be done in /sbin/mount.aufs helper? */ err = -EEXIST; h_dir = h_root->d_inode; if (!base[AuBrWh_BASE].dentry->d_inode) { h_path->dentry = base[AuBrWh_BASE].dentry; err = vfsub_create(h_dir, h_path, WH_MASK, /*want_excl*/true); } else if (S_ISREG(base[AuBrWh_BASE].dentry->d_inode->i_mode)) err = 0; else pr_err("unknown %.*s/%.*s exists\n", AuDLNPair(h_root), AuDLNPair(base[AuBrWh_BASE].dentry)); if (unlikely(err)) goto out; h_path->dentry = base[AuBrWh_PLINK].dentry; if (do_plink) { err = au_whdir(h_dir, h_path); if (unlikely(err)) goto out; wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); } else au_wh_clean(h_dir, h_path, /*isdir*/1); wbr->wbr_whbase = dget(base[AuBrWh_BASE].dentry); h_path->dentry = base[AuBrWh_ORPH].dentry; err = au_whdir(h_dir, h_path); if (unlikely(err)) goto out; wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); out: return err; }
/* * test if the @wh_name exists under @h_parent. * @try_sio specifies the necessary of super-io. */ int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, struct au_branch *br, int try_sio) { int err; struct dentry *wh_dentry; if (!try_sio) wh_dentry = vfsub_lkup_one(wh_name, h_parent); else wh_dentry = au_sio_lkup_one(wh_name, h_parent, br); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) goto out; err = 0; if (!wh_dentry->d_inode) goto out_wh; /* success */ err = 1; if (S_ISREG(wh_dentry->d_inode->i_mode)) goto out_wh; /* success */ err = -EIO; AuIOErr("%.*s Invalid whiteout entry type 0%o.\n", AuDLNPair(wh_dentry), wh_dentry->d_inode->i_mode); out_wh: dput(wh_dentry); out: return err; }
/* * initial procedure of adding a new entry. * prepare writable branch and the parent dir, lock it, * and lookup whiteout for the new entry. */ static struct dentry* lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, struct dentry *src_dentry, struct au_pin *pin, struct au_wr_dir_args *wr_dir_args) { struct dentry *wh_dentry, *h_parent; struct super_block *sb; struct au_branch *br; int err; unsigned int udba; aufs_bindex_t bcpup; AuDbg("%.*s\n", AuDLNPair(dentry)); err = au_wr_dir(dentry, src_dentry, wr_dir_args); bcpup = err; wh_dentry = ERR_PTR(err); if (unlikely(err < 0)) goto out; sb = dentry->d_sb; udba = au_opt_udba(sb); err = au_pin(pin, dentry, bcpup, udba, AuPin_DI_LOCKED | AuPin_MNT_WRITE); wh_dentry = ERR_PTR(err); if (unlikely(err)) goto out; h_parent = au_pinned_h_parent(pin); if (udba != AuOpt_UDBA_NONE && au_dbstart(dentry) == bcpup) err = au_may_add(dentry, bcpup, h_parent, au_ftest_wrdir(wr_dir_args->flags, ISDIR)); else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) err = -ENAMETOOLONG; wh_dentry = ERR_PTR(err); if (unlikely(err)) goto out_unpin; br = au_sbr(sb, bcpup); if (dt) { struct path tmp = { .dentry = h_parent, .mnt = au_br_mnt(br) }; au_dtime_store(dt, au_pinned_parent(pin), &tmp); } wh_dentry = NULL; if (bcpup != au_dbwh(dentry)) goto out; /* success */ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br); out_unpin: if (IS_ERR(wh_dentry)) au_unpin(pin); out: return wh_dentry; }
/* cf. aufs_rmdir() */ static int au_ren_del_whtmp(struct au_ren_args *a) { int err; struct inode *dir; dir = a->dst_dir; SiMustAnyLock(dir->i_sb); if (!au_nhash_test_longer_wh(&a->whlist, a->btgt, au_sbi(dir->i_sb)->si_dirwh) || au_test_fs_remote(a->h_dst->d_sb)) { err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist); if (unlikely(err)) pr_warning("failed removing whtmp dir %.*s (%d), " "ignored.\n", AuDLNPair(a->h_dst), err); } else { au_nhash_wh_free(&a->thargs->whlist); a->thargs->whlist = a->whlist; a->whlist.nh_num = 0; au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs); dput(a->h_dst); a->thargs = NULL; } return 0; }
static int au_wbr_init(struct au_branch *br, struct super_block *sb, int perm, struct path *path) { int err; struct kstatfs kst; struct au_wbr *wbr; struct dentry *h_dentry; wbr = br->br_wbr; au_rw_init(&wbr->wbr_wh_rwsem); memset(wbr->wbr_wh, 0, sizeof(wbr->wbr_wh)); atomic_set(&wbr->wbr_wh_running, 0); wbr->wbr_bytes = 0; /* * a limit for rmdir/rename a dir * cf. AUFS_MAX_NAMELEN in include/linux/aufs_type.h */ h_dentry = path->dentry; err = vfs_statfs(h_dentry, &kst); if (unlikely(err)) goto out; err = -EINVAL; if (kst.f_namelen >= NAME_MAX) err = au_br_init_wh(sb, br, perm, h_dentry); else pr_err("%.*s(%s), unsupported namelen %ld\n", AuDLNPair(h_dentry), au_sbtype(h_dentry->d_sb), kst.f_namelen); out: return err; }
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
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); }
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); }
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 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; }
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; }
/* * if valid returns 1, otherwise 0. */ static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { int valid, err; unsigned int sigen; unsigned char do_udba; struct super_block *sb; struct inode *inode; err = -EINVAL; sb = dentry->d_sb; inode = dentry->d_inode; aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW); sigen = au_sigen(sb); if (au_digen(dentry) != sigen) { AuDebugOn(IS_ROOT(dentry)); if (inode) err = au_reval_dpath(dentry, sigen); if (unlikely(err)) goto out_dgrade; AuDebugOn(au_digen(dentry) != sigen); } if (inode && au_iigen(inode) != sigen) { AuDebugOn(IS_ROOT(dentry)); err = au_refresh_hinode(inode, dentry); if (unlikely(err)) goto out_dgrade; AuDebugOn(au_iigen(inode) != sigen); } di_downgrade_lock(dentry, AuLock_IR); AuDebugOn(au_digen(dentry) != sigen); AuDebugOn(inode && au_iigen(inode) != sigen); err = -EINVAL; do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); if (do_udba && inode) { aufs_bindex_t bstart = au_ibstart(inode); if (bstart >= 0 && au_test_higen(inode, au_h_iptr(inode, bstart))) goto out; } err = h_d_revalidate(dentry, inode, nd, do_udba); if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) /* both of real entry and whiteout found */ err = -EIO; goto out; out_dgrade: di_downgrade_lock(dentry, AuLock_IR); out: aufs_read_unlock(dentry, AuLock_IR); AuTraceErr(err); valid = !err; if (!valid) AuDbg("%.*s invalid\n", AuDLNPair(dentry)); return valid; }
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); }
static void au_ren_rev_whsrc(int err, struct au_ren_args *a) { int rerr; a->h_path.dentry = a->src_wh_dentry; rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); au_set_dbwh(a->src_dentry, a->src_bwh); if (rerr) RevertFailure("unlink %.*s", AuDLNPair(a->src_wh_dentry)); }
static void au_ren_rev_cpup(int err, struct au_ren_args *a) { int rerr; a->h_path.dentry = a->dst_h_dentry; rerr = vfsub_unlink(a->dst_h_dir, &a->h_path, /*force*/0); au_set_h_dptr(a->src_dentry, a->btgt, NULL); au_set_dbstart(a->src_dentry, a->src_bstart); if (rerr) RevertFailure("unlink %.*s", AuDLNPair(a->dst_h_dentry)); }
static void au_ren_rev_diropq(int err, struct au_ren_args *a) { int rerr; au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); rerr = au_diropq_remove(a->src_dentry, a->btgt); au_hn_imtx_unlock(a->src_hinode); au_set_dbdiropq(a->src_dentry, a->src_bdiropq); if (rerr) RevertFailure("remove diropq %.*s", AuDLNPair(a->src_dentry)); }
static int test_linkable(struct dentry *h_root) { struct inode *h_dir = h_root->d_inode; if (h_dir->i_op->link) return 0; pr_err("%.*s (%s) doesn't support link(2), use noplink and rw+nolwh\n", AuDLNPair(h_root), au_sbtype(h_root->d_sb)); return -ENOSYS; }
static void au_ren_rev_rename(int err, struct au_ren_args *a) { int rerr; a->h_path.dentry = au_lkup_one(&a->src_dentry->d_name, a->src_h_parent, a->br, /*nd*/NULL); rerr = PTR_ERR(a->h_path.dentry); if (IS_ERR(a->h_path.dentry)) { RevertFailure("au_lkup_one %.*s", AuDLNPair(a->src_dentry)); return; } rerr = vfsub_rename(a->dst_h_dir, au_h_dptr(a->src_dentry, a->btgt), a->src_h_dir, &a->h_path); d_drop(a->h_path.dentry); dput(a->h_path.dentry); /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */ if (rerr) RevertFailure("rename %.*s", AuDLNPair(a->src_dentry)); }
/* * final procedure of adding a new entry, except link(2). * remove whiteout, instantiate, copyup the parent dir's times and size * and update version. * if it failed, re-create the removed whiteout. */ static int epilog(struct inode *dir, aufs_bindex_t bindex, struct dentry *wh_dentry, struct dentry *dentry) { int err, rerr; aufs_bindex_t bwh; struct path h_path; struct inode *inode, *h_dir; struct dentry *wh; bwh = -1; if (wh_dentry) { h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ IMustLock(h_dir); AuDebugOn(au_h_iptr(dir, bindex) != h_dir); bwh = au_dbwh(dentry); h_path.dentry = wh_dentry; h_path.mnt = au_sbr_mnt(dir->i_sb, bindex); err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); if (unlikely(err)) goto out; } inode = au_new_inode(dentry, /*must_new*/1); if (!IS_ERR(inode)) { d_instantiate(dentry, inode); dir = dentry->d_parent->d_inode; /* dir inode is locked */ IMustLock(dir); if (au_ibstart(dir) == au_dbstart(dentry)) au_cpup_attr_timesizes(dir); dir->i_version++; return 0; /* success */ } err = PTR_ERR(inode); if (!wh_dentry) goto out; /* revert */ /* dir inode is locked */ wh = au_wh_create(dentry, bwh, wh_dentry->d_parent); rerr = PTR_ERR(wh); if (IS_ERR(wh)) { AuIOErr("%.*s reverting whiteout failed(%d, %d)\n", AuDLNPair(dentry), err, rerr); err = -EIO; } else dput(wh); out: return err; }
static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry) { struct dentry *wh = NULL; if (!dentry || IS_ERR(dentry)) { dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry)); return -1; } /* do not call dget_parent() here */ dpri("d%d: %.*s?/%.*s, %s, cnt %d, flags 0x%x\n", bindex, AuDLNPair(dentry->d_parent), AuDLNPair(dentry), dentry->d_sb ? au_sbtype(dentry->d_sb) : "??", atomic_read(&dentry->d_count), dentry->d_flags); if (bindex >= 0 && dentry->d_inode && au_test_aufs(dentry->d_sb)) { struct au_iinfo *iinfo = au_ii(dentry->d_inode); if (iinfo) wh = iinfo->ii_hinode[0 + bindex].hi_whdentry; } do_pri_inode(bindex, dentry->d_inode, wh); return 0; }
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; }
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); }
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; }
int do_vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, struct inode *dir, struct dentry *dentry) { int err; LKTRTrace("i%lu, %.*s, i%lu, %.*s\n", src_dir->i_ino, AuDLNPair(src_dentry), dir->i_ino, AuDLNPair(dentry)); IMustLock(dir); IMustLock(src_dir); AuDebugOn(src_dir != dir && !vfsub_is_rename_mutex_locked(dir->i_sb)); lockdep_off(); err = vfs_rename(src_dir, src_dentry, dir, dentry); lockdep_on(); if (!err) { /* dir inode is locked */ au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/ au_update_fuse_h_inode(NULL, src_dentry->d_parent); /*ignore*/ au_update_fuse_h_inode(NULL, src_dentry); /*ignore*/ } return err; }
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; }
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 vfsub_iterate_dir(struct file *file, struct dir_context *ctx) { int err; AuDbg("%.*s, ctx{%pf, %llu}\n", AuDLNPair(file->f_dentry), ctx->actor, ctx->pos); lockdep_off(); err = iterate_dir(file, ctx); lockdep_on(); if (err >= 0) vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ return err; }
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; }