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; }
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; }
struct dentry *au_lkup_one(struct qstr *name, struct dentry *h_parent, struct au_branch *br, struct nameidata *nd) { struct dentry *h_dentry; int err; struct nameidata h_nd; if (au_test_fs_null_nd(h_parent->d_sb)) return vfsub_lookup_one_len(name->name, h_parent, name->len); au_h_nd(&h_nd, nd); h_nd.path.dentry = h_parent; h_nd.path.mnt = br->br_mnt; err = __lookup_one_len(name->name, &h_nd.last, NULL, name->len); h_dentry = ERR_PTR(err); if (!err) { path_get(&h_nd.path); h_dentry = vfsub_lookup_hash(&h_nd); path_put(&h_nd.path); } AuTraceErrPtr(h_dentry); return h_dentry; }
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; struct path path; p = d_path(h_rootpath, buf, len); if (IS_ERR(p)) goto out; n = strlen(p); path.mnt = h_rootpath->mnt; path.dentry = h_parent; p = d_path(&path, buf, len); if (IS_ERR(p)) goto out; if (n != 1) p += n; path.mnt = au_mnt_get(sb); path.dentry = sb->s_root; p = d_path(&path, buf, len - strlen(p)); mntput(path.mnt); if (IS_ERR(p)) goto out; if (n != 1) p[strlen(p)] = '/'; out: AuTraceErrPtr(p); return p; }
/* * returns a negative dentry whose name is unique and temporary. */ struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, struct qstr *prefix) { struct dentry *dentry; int i; char defname[NAME_MAX - AUFS_MAX_NAMELEN + DNAME_INLINE_LEN + 1], *name, *p; /* strict atomic_t is unnecessary here */ static unsigned short cnt; struct qstr qs; BUILD_BUG_ON(sizeof(cnt) * 2 > AUFS_WH_TMP_LEN); name = defname; qs.len = sizeof(defname) - DNAME_INLINE_LEN + prefix->len - 1; if (unlikely(prefix->len > DNAME_INLINE_LEN)) { dentry = ERR_PTR(-ENAMETOOLONG); if (unlikely(qs.len > NAME_MAX)) goto out; dentry = ERR_PTR(-ENOMEM); name = kmalloc(qs.len + 1, GFP_NOFS); if (unlikely(!name)) goto out; } /* doubly whiteout-ed */ memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2); p = name + AUFS_WH_PFX_LEN * 2; memcpy(p, prefix->name, prefix->len); p += prefix->len; *p++ = '.'; AuDebugOn(name + qs.len + 1 - p <= AUFS_WH_TMP_LEN); qs.name = name; for (i = 0; i < 3; i++) { sprintf(p, "%.*x", AUFS_WH_TMP_LEN, cnt++); dentry = au_sio_lkup_one(&qs, h_parent, br); if (IS_ERR(dentry) || !dentry->d_inode) goto out_name; dput(dentry); } /* pr_warn("could not get random name\n"); */ dentry = ERR_PTR(-EEXIST); AuDbg("%.*s\n", AuLNPair(&qs)); BUG(); out_name: if (name != defname) kfree(name); out: AuTraceErrPtr(dentry); return dentry; }
struct dentry *vfsub_lookup_hash(struct nameidata *nd) { struct path path = { .mnt = nd->path.mnt }; IMustLock(nd->path.dentry->d_inode); path.dentry = lookup_hash(nd); if (IS_ERR(path.dentry)) goto out; if (path.dentry->d_inode) vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ out: AuTraceErrPtr(path.dentry); return path.dentry; } /* ---------------------------------------------------------------------- */ struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, struct dentry *d2, struct au_hinode *hdir2) { struct dentry *d; lockdep_off(); d = lock_rename(d1, d2); lockdep_on(); au_hn_suspend(hdir1); if (hdir1 != hdir2) au_hn_suspend(hdir2); return d; } void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, struct dentry *d2, struct au_hinode *hdir2) { au_hn_resume(hdir1); if (hdir1 != hdir2) au_hn_resume(hdir2); lockdep_off(); unlock_rename(d1, d2); lockdep_on(); }
static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, ino_t dir_ino) { struct dentry *dentry, *d; struct inode *inode; au_gen_t sigen; LKTRTrace("i%lu, diri%lu\n", (unsigned long)ino, (unsigned long)dir_ino); dentry = NULL; inode = ilookup(sb, ino); if (!inode) goto out; dentry = ERR_PTR(-ESTALE); sigen = au_sigen(sb); if (unlikely(is_bad_inode(inode) || IS_DEADDIR(inode) || sigen != au_iigen(inode))) goto out_iput; dentry = NULL; if (!dir_ino || S_ISDIR(inode->i_mode)) dentry = d_find_alias(inode); else { spin_lock(&dcache_lock); list_for_each_entry(d, &inode->i_dentry, d_alias) if (!au_test_anon(d) && d->d_parent->d_inode->i_ino == dir_ino) { dentry = dget_locked(d); break; } spin_unlock(&dcache_lock); } if (unlikely(dentry && sigen != au_digen(dentry))) { dput(dentry); dentry = ERR_PTR(-ESTALE); } out_iput: iput(inode); out: AuTraceErrPtr(dentry); return 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 dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, int len) { struct path path = { .mnt = NULL }; IMustLock(parent->d_inode); path.dentry = lookup_one_len(name, parent, len); if (IS_ERR(path.dentry)) goto out; if (path.dentry->d_inode) vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ out: AuTraceErrPtr(path.dentry); return path.dentry; }
struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, int len) { struct path path = { .mnt = NULL }; /* VFS checks it too, but by WARN_ON_ONCE() */ IMustLock(d_inode(parent)); path.dentry = lookup_one_len(name, parent, len); if (IS_ERR(path.dentry)) goto out; if (d_is_positive(path.dentry)) vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ out: AuTraceErrPtr(path.dentry); return path.dentry; }
static 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; struct path path; if (dir_ino != AUFS_ROOT_INO) { path.dentry = decode_by_ino(sb, dir_ino, 0); dentry = path.dentry; if (!path.dentry || IS_ERR(path.dentry)) goto out; AuDebugOn(au_test_anon(path.dentry)); } else path.dentry = dget(sb->s_root); path.mnt = au_mnt_get(sb); dentry = au_lkup_by_ino(&path, ino, nsi_lock); path_put(&path); out: AuTraceErrPtr(dentry); return dentry; }
/* * initial procedure of adding a new entry. * prepare writable branch and the parent dir, lock it, * 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; int err; unsigned int mnt_flags; unsigned char pin_flags; aufs_bindex_t bstart, bcpup; struct au_ndx ndx; LKTRTrace("%.*s, src %p\n", AuDLNPair(dentry), src_dentry); bstart = au_dbstart(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; mnt_flags = au_mntflags(sb); pin_flags = AuPin_DI_LOCKED | AuPin_MNT_WRITE; if (dt && au_opt_test(mnt_flags, UDBA_INOTIFY)) au_fset_pin(pin_flags, DO_GPARENT); err = au_pin(pin, dentry, bcpup, pin_flags); wh_dentry = ERR_PTR(err); if (unlikely(err)) goto out; ndx.nfsmnt = au_nfsmnt(sb, bcpup); ndx.flags = 0; if (au_test_dlgt(mnt_flags)) au_fset_ndx(ndx.flags, DLGT); ndx.nd = NULL; /* ndx.br = NULL; */ /* ndx.nd_file = NULL; */ h_parent = au_pinned_h_parent(pin); if (!au_opt_test(mnt_flags, UDBA_NONE) && au_dbstart(dentry) == bcpup) { struct nameidata nd; if (ndx.nfsmnt) { /* todo: dirty? */ ndx.nd = &nd; ndx.br = au_sbr(sb, bcpup); memset(&nd, 0, sizeof(nd)); nd.flags = LOOKUP_CREATE; nd.intent.open.flags = O_EXCL; } err = au_may_add(dentry, bcpup, h_parent, au_ftest_wrdir(wr_dir_args->flags, ISDIR), &ndx); wh_dentry = ERR_PTR(err); if (unlikely(err)) goto out_unpin; ndx.nd = NULL; ndx.br = NULL; } if (dt) au_dtime_store(dt, au_pinned_parent(pin), h_parent, au_pinned_hdir(pin), au_pinned_hgdir(pin)); wh_dentry = NULL; if (/* bcpup != bstart || */ bcpup != au_dbwh(dentry)) goto out; /* success */ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, &ndx); out_unpin: if (IS_ERR(wh_dentry)) au_unpin(pin); out: AuTraceErrPtr(wh_dentry); return wh_dentry; }
struct dentry *vfsub_lookup_hash(struct nameidata *nd) { struct path path = { .mnt = nd->path.mnt }; IMustLock(nd->path.dentry->d_inode); path.dentry = lookup_hash(nd); if (IS_ERR(path.dentry)) goto out; if (path.dentry->d_inode) vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ out: AuTraceErrPtr(path.dentry); return path.dentry; } /* * this is "VFS:__lookup_one_len()" which was removed and merged into * VFS:lookup_one_len() by the commit. * 6a96ba5 2011-03-14 kill __lookup_one_len() * this function should always be equivalent to the corresponding part in * VFS:lookup_one_len(). */ int vfsub_name_hash(const char *name, struct qstr *this, int len) { unsigned int c; this->name = name; this->len = len; this->hash = full_name_hash(name, len); if (!len) return -EACCES; while (len--) { c = *(const unsigned char *)name++; if (c == '/' || c == '\0') return -EACCES; } return 0; } /* ---------------------------------------------------------------------- */ struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, struct dentry *d2, struct au_hinode *hdir2) { struct dentry *d; lockdep_off(); d = lock_rename(d1, d2); lockdep_on(); au_hn_suspend(hdir1); if (hdir1 != hdir2) au_hn_suspend(hdir2); return d; } void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, struct dentry *d2, struct au_hinode *hdir2) { au_hn_resume(hdir1); if (hdir1 != hdir2) au_hn_resume(hdir2); lockdep_off(); unlock_rename(d1, d2); lockdep_on(); }
static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino, struct au_nfsd_si_lock *nsi_lock) { struct dentry *dentry, *parent; struct file *file; struct inode *dir; struct find_name_by_ino arg; int err; parent = path->dentry; LKTRTrace("%.*s, i%lu\n", AuDLNPair(parent), (unsigned long)ino); if (nsi_lock) si_read_unlock(parent->d_sb); path_get(path); file = dentry_open(parent, path->mnt, au_dir_roflags); dentry = (void *)file; if (IS_ERR(file)) goto out; dentry = ERR_PTR(-ENOMEM); arg.name = __getname(); if (unlikely(!arg.name)) goto out_file; arg.ino = ino; arg.found = 0; do { arg.called = 0; /* smp_mb(); */ err = vfsub_readdir(file, find_name_by_ino, &arg, /*dlgt*/0); } while (!err && !arg.found && arg.called); dentry = ERR_PTR(err); if (unlikely(err)) goto out_name; dentry = ERR_PTR(-ENOENT); if (!arg.found) goto out_name; /* do not call au_lkup_one(), nor dlgt */ dir = parent->d_inode; vfsub_i_lock(dir); dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen); vfsub_i_unlock(dir); AuTraceErrPtr(dentry); if (IS_ERR(dentry)) goto out_name; AuDebugOn(au_test_anon(dentry)); if (unlikely(!dentry->d_inode)) { dput(dentry); dentry = ERR_PTR(-ENOENT); } out_name: __putname(arg.name); out_file: fput(file); out: if (unlikely(nsi_lock && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0)) if (!IS_ERR(dentry)) { dput(dentry); dentry = ERR_PTR(-ESTALE); } AuTraceErrPtr(dentry); return dentry; }
static struct dentry * aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { struct dentry *dentry; __u32 *fh = fid->raw; ino_t ino, dir_ino; aufs_bindex_t bindex; struct au_nfsd_si_lock nsi_lock = { .force_lock = 0 }; dentry = ERR_PTR(-ESTALE); /* it should never happen, but the file handle is unreliable */ if (unlikely(fh_len < Fh_tail)) goto out; nsi_lock.sigen = fh[Fh_sigen]; nsi_lock.br_id = fh[Fh_br_id]; /* branch id may be wrapped around */ bindex = si_nfsd_read_lock(sb, &nsi_lock); if (unlikely(bindex < 0)) goto out; nsi_lock.force_lock = 1; /* is this inode still cached? */ ino = decode_ino(fh + Fh_ino); /* it should never happen */ if (unlikely(ino == AUFS_ROOT_INO)) goto out; dir_ino = decode_ino(fh + Fh_dir_ino); dentry = decode_by_ino(sb, ino, dir_ino); if (IS_ERR(dentry)) goto out_unlock; if (dentry) goto accept; /* is the parent dir cached? */ dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); if (IS_ERR(dentry)) goto out_unlock; if (dentry) goto accept; /* lookup path */ dentry = decode_by_path(sb, bindex, ino, fh, fh_len, &nsi_lock); if (IS_ERR(dentry)) goto out_unlock; if (unlikely(!dentry)) /* todo?: make it ESTALE */ goto out_unlock; accept: if (dentry->d_inode->i_generation == fh[Fh_igen]) goto out_unlock; /* success */ dput(dentry); dentry = ERR_PTR(-ESTALE); out_unlock: si_read_unlock(sb); out: AuTraceErrPtr(dentry); return dentry; } #if 0 /* reserved for future use */ /* support subtreecheck option */ static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { struct dentry *parent; __u32 *fh = fid->raw; ino_t dir_ino; dir_ino = decode_ino(fh + Fh_dir_ino); parent = decode_by_ino(sb, dir_ino, 0); if (IS_ERR(parent)) goto out; if (!parent) parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]), dir_ino, fh, fh_len); out: AuTraceErrPtr(parent); return parent; } #endif /* ---------------------------------------------------------------------- */ static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, int connectable) { int err; aufs_bindex_t bindex, bend; struct super_block *sb, *h_sb; struct inode *inode; struct dentry *parent, *h_parent; struct au_branch *br; AuDebugOn(au_test_anon(dentry)); parent = NULL; err = -ENOSPC; if (unlikely(*max_len <= Fh_tail)) { AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); goto out; } err = FILEID_ROOT; if (IS_ROOT(dentry)) { AuDebugOn(dentry->d_inode->i_ino != AUFS_ROOT_INO); goto out; } err = -EIO; h_parent = NULL; sb = dentry->d_sb; aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR); parent = dget_parent(dentry); di_read_lock_parent(parent, !AuLock_IR); inode = dentry->d_inode; AuDebugOn(!inode); #ifdef CONFIG_AUFS_DEBUG if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) AuWarn1("NFS-exporting requires xino\n"); #endif bend = au_dbtaildir(parent); for (bindex = au_dbstart(parent); bindex <= bend; bindex++) { h_parent = au_h_dptr(parent, bindex); if (h_parent) { dget(h_parent); break; } } if (unlikely(!h_parent)) goto out_unlock; err = -EPERM; br = au_sbr(sb, bindex); h_sb = br->br_mnt->mnt_sb; if (unlikely(!h_sb->s_export_op)) { AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); goto out_dput; } fh[Fh_br_id] = br->br_id; fh[Fh_sigen] = au_sigen(sb); encode_ino(fh + Fh_ino, inode->i_ino); encode_ino(fh + Fh_dir_ino, parent->d_inode->i_ino); fh[Fh_igen] = inode->i_generation; *max_len -= Fh_tail; fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail), max_len, /*connectable or subtreecheck*/0); err = fh[Fh_h_type]; *max_len += Fh_tail; /* todo: macros? */ if (err != 255) err = 99; else AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); out_dput: dput(h_parent); out_unlock: di_read_unlock(parent, !AuLock_IR); dput(parent); aufs_read_unlock(dentry, AuLock_IR); out: if (unlikely(err < 0)) err = 255; return err; }
static struct dentry *decode_by_path(struct super_block *sb, aufs_bindex_t bindex, ino_t ino, __u32 *fh, int fh_len, struct au_nfsd_si_lock *nsi_lock) { struct dentry *dentry, *h_parent, *root; struct super_block *h_sb; char *pathname, *p; struct vfsmount *h_mnt; struct au_branch *br; int err; struct path path; br = au_sbr(sb, bindex); /* au_br_get(br); */ h_mnt = br->br_mnt; h_sb = h_mnt->mnt_sb; /* todo: call lower fh_to_dentry()? fh_to_parent()? */ h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), fh_len - Fh_tail, fh[Fh_h_type], h_acceptable, /*context*/NULL); dentry = h_parent; if (unlikely(!h_parent || IS_ERR(h_parent))) { AuWarn1("%s decode_fh failed, %ld\n", au_sbtype(h_sb), PTR_ERR(h_parent)); goto out; } dentry = NULL; if (unlikely(au_test_anon(h_parent))) { AuWarn1("%s decode_fh returned a disconnected dentry\n", au_sbtype(h_sb)); goto out_h_parent; } dentry = ERR_PTR(-ENOMEM); pathname = (void *)__get_free_page(GFP_NOFS); if (unlikely(!pathname)) goto out_h_parent; root = sb->s_root; path.mnt = h_mnt; di_read_lock_parent(root, !AuLock_IR); path.dentry = au_h_dptr(root, bindex); di_read_unlock(root, !AuLock_IR); p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); dentry = (void *)p; if (IS_ERR(p)) goto out_pathname; si_read_unlock(sb); err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); dentry = ERR_PTR(err); if (unlikely(err)) goto out_relock; dentry = ERR_PTR(-ENOENT); AuDebugOn(au_test_anon(path.dentry)); if (unlikely(!path.dentry->d_inode)) goto out_path; if (ino != path.dentry->d_inode->i_ino) dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL); else dentry = dget(path.dentry); out_path: path_put(&path); out_relock: if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0)) if (!IS_ERR(dentry)) { dput(dentry); dentry = ERR_PTR(-ESTALE); } out_pathname: free_page((unsigned long)pathname); out_h_parent: dput(h_parent); out: /* au_br_put(br); */ AuTraceErrPtr(dentry); return dentry; }
static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino, struct au_nfsd_si_lock *nsi_lock) { struct dentry *dentry, *parent; struct file *file; struct inode *dir; struct find_name_by_ino arg; int err; parent = path->dentry; if (nsi_lock) si_read_unlock(parent->d_sb); file = vfsub_dentry_open(path, au_dir_roflags); dentry = (void *)file; if (IS_ERR(file)) goto out; dentry = ERR_PTR(-ENOMEM); arg.name = __getname_gfp(GFP_NOFS); if (unlikely(!arg.name)) goto out_file; arg.ino = ino; arg.found = 0; do { arg.called = 0; /* smp_mb(); */ err = vfsub_readdir(file, find_name_by_ino, &arg); } while (!err && !arg.found && arg.called); dentry = ERR_PTR(err); if (unlikely(err)) goto out_name; dentry = ERR_PTR(-ENOENT); if (!arg.found) goto out_name; /* do not call au_lkup_one() */ dir = parent->d_inode; mutex_lock(&dir->i_mutex); dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen); mutex_unlock(&dir->i_mutex); AuTraceErrPtr(dentry); if (IS_ERR(dentry)) goto out_name; AuDebugOn(au_test_anon(dentry)); if (unlikely(!dentry->d_inode)) { dput(dentry); dentry = ERR_PTR(-ENOENT); } out_name: __putname(arg.name); out_file: fput(file); out: if (unlikely(nsi_lock && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0)) if (!IS_ERR(dentry)) { dput(dentry); dentry = ERR_PTR(-ESTALE); } AuTraceErrPtr(dentry); return dentry; }
static struct dentry * aufs_decode_fh(struct super_block *sb, __u32 *fh, int fh_len, int fh_type, int (*acceptable)(void *context, struct dentry *de), void *context) { struct dentry *dentry; ino_t ino, dir_ino; aufs_bindex_t bindex; struct au_nfsd_si_lock nsi_lock = { .sigen = fh[Fh_sigen], .br_id = fh[Fh_br_id], .force_lock = 0 }; LKTRTrace("%d, fh{br_id %u, sigen %u, i%u, diri%u, g%u}\n", fh_type, fh[Fh_br_id], fh[Fh_sigen], fh[Fh_ino], fh[Fh_dir_ino], fh[Fh_igen]); AuDebugOn(fh_len < Fh_tail); dentry = ERR_PTR(-ESTALE); /* branch id may be wrapped around */ bindex = si_nfsd_read_lock(sb, &nsi_lock); if (unlikely(bindex < 0)) goto out; nsi_lock.force_lock = 1; /* is this inode still cached? */ ino = decode_ino(fh + Fh_ino); AuDebugOn(ino == AUFS_ROOT_INO); dir_ino = decode_ino(fh + Fh_dir_ino); dentry = decode_by_ino(sb, ino, dir_ino); if (IS_ERR(dentry)) goto out_unlock; if (dentry) goto accept; /* is the parent dir cached? */ dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); if (IS_ERR(dentry)) goto out_unlock; if (dentry) goto accept; /* lookup path */ dentry = decode_by_path(sb, bindex, ino, fh, fh_len, &nsi_lock); if (IS_ERR(dentry)) goto out_unlock; if (unlikely(!dentry)) goto out_unlock; accept: LKTRLabel(accept); if (dentry->d_inode->i_generation == fh[Fh_igen] && acceptable(context, dentry)) goto out_unlock; /* success */ LKTRLabel(stale); dput(dentry); dentry = ERR_PTR(-ESTALE); out_unlock: LKTRLabel(out_unlock); si_read_unlock(sb); out: LKTRLabel(out); if (0 && IS_ERR(dentry)) dentry = ERR_PTR(-ESTALE); AuTraceErrPtr(dentry); return dentry; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) static struct dentry * aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { return aufs_decode_fh(sb, fid->raw, fh_len, fh_type, h_acceptable, /*context*/NULL); } #endif /* KERNEL_VERSION */ /* ---------------------------------------------------------------------- */ static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, int connectable) { int err; aufs_bindex_t bindex, bend; struct super_block *sb, *h_sb; struct inode *inode; struct dentry *parent, *h_parent; struct au_branch *br; LKTRTrace("%.*s, max %d, conn %d\n", AuDLNPair(dentry), *max_len, connectable); AuDebugOn(au_test_anon(dentry)); parent = NULL; err = -ENOSPC; if (unlikely(*max_len <= Fh_tail)) { AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); goto out; } err = 0; //FILEID_ROOT; if (IS_ROOT(dentry)) { AuDebugOn(dentry->d_inode->i_ino != AUFS_ROOT_INO); goto out; } err = -EIO; h_parent = NULL; sb = dentry->d_sb; aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR); parent = dget_parent(dentry); di_read_lock_parent(parent, !AuLock_IR); inode = dentry->d_inode; AuDebugOn(!inode); #ifdef CONFIG_AUFS_DEBUG { unsigned int mnt_flags = au_mntflags(sb); if (unlikely(!au_opt_test_xino(mnt_flags))) AuWarn1("NFS-exporting requires xino\n"); if (unlikely(0 && !au_opt_test(mnt_flags, UDBA_INOTIFY))) AuWarn1("udba=inotify is recommended " "for NFS-exporting\n"); } #endif bend = au_dbtaildir(parent); for (bindex = au_dbstart(parent); bindex <= bend; bindex++) { h_parent = au_h_dptr(parent, bindex); if (h_parent) { dget(h_parent); break; } } if (unlikely(!h_parent)) goto out_unlock; LKTRTrace("b%d\n", bindex); err = -EPERM; br = au_sbr(sb, bindex); h_sb = br->br_mnt->mnt_sb; if (unlikely(!h_sb->s_export_op)) { AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); goto out_dput; } fh[Fh_br_id] = br->br_id; fh[Fh_sigen] = au_sigen(sb); encode_ino(fh + Fh_ino, inode->i_ino); encode_ino(fh + Fh_dir_ino, parent->d_inode->i_ino); fh[Fh_igen] = inode->i_generation; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) /* it should be set at exporting time */ if (unlikely(!h_sb->s_export_op->find_exported_dentry)) { AuWarn("set default find_exported_dentry for %s\n", au_sbtype(h_sb)); h_sb->s_export_op->find_exported_dentry = find_exported_dentry; } #endif *max_len -= Fh_tail; fh[Fh_h_type] = au_call_encode_fh(h_parent, fh + Fh_tail, max_len, /*connectable or subtreecheck*/0); err = fh[Fh_h_type]; *max_len += Fh_tail; /* todo: macros? */ if (err != 255) err = 99; else AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); out_dput: dput(h_parent); out_unlock: di_read_unlock(parent, !AuLock_IR); dput(parent); aufs_read_unlock(dentry, AuLock_IR); out: AuTraceErr(err); if (unlikely(err < 0)) err = 255; return err; }