/* most free space */ static void au_mfs(struct dentry *dentry) { struct super_block *sb; struct au_branch *br; struct au_wbr_mfs *mfs; aufs_bindex_t bindex, bend; int err; unsigned long long b, bavail; struct path h_path; /* reduce the stack usage */ struct kstatfs *st; st = kmalloc(sizeof(*st), GFP_NOFS); if (unlikely(!st)) { AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM); return; } bavail = 0; sb = dentry->d_sb; mfs = &au_sbi(sb)->si_wbr_mfs; MtxMustLock(&mfs->mfs_lock); mfs->mfs_bindex = -EROFS; mfs->mfsrr_bytes = 0; bend = au_sbend(sb); for (bindex = 0; bindex <= bend; bindex++) { br = au_sbr(sb, bindex); if (au_br_rdonly(br)) continue; /* sb->s_root for NFS is unreliable */ h_path.mnt = br->br_mnt; h_path.dentry = h_path.mnt->mnt_root; err = vfs_statfs(&h_path, st); if (unlikely(err)) { AuWarn1("failed statfs, b%d, %d\n", bindex, err); continue; } /* when the available size is equal, select the lower one */ BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail) || sizeof(b) < sizeof(st->f_bsize)); b = st->f_bavail * st->f_bsize; br->br_wbr->wbr_bytes = b; if (b >= bavail) { bavail = b; mfs->mfs_bindex = bindex; mfs->mfs_jiffy = jiffies; } } mfs->mfsrr_bytes = bavail; AuDbg("b%d\n", mfs->mfs_bindex); kfree(st); }
static void do_wkq(struct au_wkinfo *wkinfo) { unsigned int idle, n; int i, idle_idx; while (1) { if (au_ftest_wkq(wkinfo->flags, WAIT)) { idle_idx = 0; idle = UINT_MAX; for (i = 0; i < aufs_nwkq; i++) { n = atomic_inc_return(&au_wkq[i].busy); if (n == 1 && !enqueue(au_wkq + i, wkinfo)) return; /* success */ if (n < idle) { idle_idx = i; idle = n; } atomic_dec(&au_wkq[i].busy); } } else idle_idx = aufs_nwkq; atomic_inc(&au_wkq[idle_idx].busy); if (!enqueue(au_wkq + idle_idx, wkinfo)) return; /* success */ /* impossible? */ AuWarn1("failed to queue_work()\n"); yield(); } }
static void au_warn_ima(void) { #ifdef CONFIG_IMA /* since it doesn't support mark_files_ro() */ AuWarn1("RW -> RO makes IMA to produce wrong message\n"); #endif }
static void au_warn_inotify(int val, char *str) { #ifdef CONFIG_AUFS_HINOTIFY if (val == AuOpt_UDBA_HNOTIFY && !strcmp(str, "inotify")) AuWarn1("udba=inotify is deprecated, use udba=notify\n"); #endif }
void au_warn_loopback(struct super_block *h_sb) { int i, new_nelem; unsigned long *a, magic; static DEFINE_SPINLOCK(spin); magic = h_sb->s_magic; spin_lock(&spin); a = au_warn_loopback_array; for (i = 0; i < au_warn_loopback_nelem && *a; i++) if (a[i] == magic) { spin_unlock(&spin); return; } /* h_sb is new to us, print it */ if (i < au_warn_loopback_nelem) { a[i] = magic; goto pr; } /* expand the array */ new_nelem = au_warn_loopback_nelem + au_warn_loopback_step; a = au_kzrealloc(au_warn_loopback_array, au_warn_loopback_nelem * sizeof(unsigned long), new_nelem * sizeof(unsigned long), GFP_ATOMIC, /*may_shrink*/0); if (a) { au_warn_loopback_nelem = new_nelem; au_warn_loopback_array = a; a[i] = magic; goto pr; } spin_unlock(&spin); AuWarn1("realloc failed, ignored\n"); return; pr: spin_unlock(&spin); pr_warn("you may want to try another patch for loopback file " "on %s(0x%lx) branch\n", au_sbtype(h_sb), magic); }
/* * test if @dentry dir can be rename source or not. * if it can, return 0 and @children is filled. * success means, * - it is a logically empty dir. * - or, it exists on writable branch and has no children including whiteouts * on the lower branch. */ static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt) { int err; unsigned int rdhash; aufs_bindex_t bstart; bstart = au_dbstart(dentry); if (bstart != btgt) { struct au_nhash whlist; SiMustAnyLock(dentry->d_sb); rdhash = au_sbi(dentry->d_sb)->si_rdhash; if (!rdhash) rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry)); err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); if (unlikely(err)) goto out; err = au_test_empty(dentry, &whlist); au_nhash_wh_free(&whlist); goto out; } if (bstart == au_dbtaildir(dentry)) return 0; /* success */ err = au_test_empty_lower(dentry); out: if (err == -ENOTEMPTY) { AuWarn1("renaming dir who has child(ren) on multiple branches," " is not supported\n"); err = -EXDEV; } return err; }
/* todo: return with unlocked? */ struct inode *au_new_inode(struct dentry *dentry, int must_new) { struct inode *inode; struct dentry *h_dentry; struct super_block *sb; ino_t h_ino, ino; int err, match; aufs_bindex_t bstart; sb = dentry->d_sb; bstart = au_dbstart(dentry); h_dentry = au_h_dptr(dentry, bstart); h_ino = h_dentry->d_inode->i_ino; err = au_xino_read(sb, bstart, h_ino, &ino); inode = ERR_PTR(err); if (unlikely(err)) goto out; new_ino: if (!ino) { ino = au_xino_new_ino(sb); if (unlikely(!ino)) { inode = ERR_PTR(-EIO); goto out; } } AuDbg("i%lu\n", (unsigned long)ino); inode = au_iget_locked(sb, ino); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out; AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); if (inode->i_state & I_NEW) { ii_write_lock_new_child(inode); err = set_inode(inode, dentry); if (!err) { unlock_new_inode(inode); goto out; /* success */ } ii_write_unlock(inode); iget_failed(inode); goto out_err; } else if (!must_new) { err = reval_inode(inode, dentry, &match); if (!err) goto out; /* success */ else if (match) goto out_iput; } if (unlikely(au_test_fs_unique_ino(h_dentry->d_inode))) AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir," " b%d, %s, %.*s, hi%lu, i%lu.\n", bstart, au_sbtype(h_dentry->d_sb), AuDLNPair(dentry), (unsigned long)h_ino, (unsigned long)ino); ino = 0; err = au_xino_write(sb, bstart, h_ino, /*ino*/0); if (!err) { iput(inode); goto new_ino; } out_iput: iput(inode); out_err: inode = ERR_PTR(err); out: return inode; }
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; }
/* todo: return with unlocked? */ struct inode *au_new_inode(struct dentry *dentry, int must_new) { struct inode *inode, *h_inode; struct dentry *h_dentry; struct super_block *sb; struct mutex *mtx; ino_t h_ino, ino; int err; aufs_bindex_t bstart; sb = dentry->d_sb; bstart = au_dbstart(dentry); h_dentry = au_h_dptr(dentry, bstart); h_inode = h_dentry->d_inode; h_ino = h_inode->i_ino; /* * stop 'race'-ing between hardlinks under different * parents. */ mtx = NULL; if (!S_ISDIR(h_inode->i_mode)) mtx = &au_sbr(sb, bstart)->br_xino.xi_nondir_mtx; new_ino: if (mtx) mutex_lock(mtx); err = au_xino_read(sb, bstart, h_ino, &ino); inode = ERR_PTR(err); if (unlikely(err)) goto out; if (!ino) { ino = au_xino_new_ino(sb); if (unlikely(!ino)) { inode = ERR_PTR(-EIO); goto out; } } AuDbg("i%lu\n", (unsigned long)ino); inode = au_iget_locked(sb, ino); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out; AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); if (inode->i_state & I_NEW) { /* verbose coding for lock class name */ if (unlikely(S_ISLNK(h_inode->i_mode))) au_rw_class(&au_ii(inode)->ii_rwsem, au_lc_key + AuLcSymlink_IIINFO); else if (unlikely(S_ISDIR(h_inode->i_mode))) au_rw_class(&au_ii(inode)->ii_rwsem, au_lc_key + AuLcDir_IIINFO); else /* likely */ au_rw_class(&au_ii(inode)->ii_rwsem, au_lc_key + AuLcNonDir_IIINFO); ii_write_lock_new_child(inode); err = set_inode(inode, dentry); if (!err) { unlock_new_inode(inode); goto out; /* success */ } /* * iget_failed() calls iput(), but we need to call * ii_write_unlock() after iget_failed(). so dirty hack for * i_count. */ atomic_inc(&inode->i_count); iget_failed(inode); ii_write_unlock(inode); au_xino_write(sb, bstart, h_ino, /*ino*/0); /* ignore this error */ goto out_iput; } else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) { /* * horrible race condition between lookup, readdir and copyup * (or something). */ if (mtx) mutex_unlock(mtx); err = reval_inode(inode, dentry); if (unlikely(err < 0)) { mtx = NULL; goto out_iput; } if (!err) { mtx = NULL; goto out; /* success */ } else if (mtx) mutex_lock(mtx); } if (unlikely(au_test_fs_unique_ino(h_dentry->d_inode))) AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir," " b%d, %s, %pd, hi%lu, i%lu.\n", bstart, au_sbtype(h_dentry->d_sb), dentry, (unsigned long)h_ino, (unsigned long)ino); ino = 0; err = au_xino_write(sb, bstart, h_ino, /*ino*/0); if (!err) { iput(inode); if (mtx) mutex_unlock(mtx); goto new_ino; } out_iput: iput(inode); inode = ERR_PTR(err); out: if (mtx) mutex_unlock(mtx); return inode; }
/* todo: return with unlocked? */ struct inode *au_new_inode(struct dentry *dentry, int must_new) { struct inode *inode, *h_inode; struct dentry *h_dentry; struct super_block *sb; struct mutex *mtx; ino_t h_ino, ino; int err, match; aufs_bindex_t bstart; sb = dentry->d_sb; bstart = au_dbstart(dentry); h_dentry = au_h_dptr(dentry, bstart); h_inode = h_dentry->d_inode; h_ino = h_inode->i_ino; /* * stop 'race'-ing between hardlinks under different * parents. */ mtx = NULL; if (!S_ISDIR(h_inode->i_mode)) mtx = &au_sbr(sb, bstart)->br_xino.xi_nondir_mtx; new_ino: if (mtx) mutex_lock(mtx); err = au_xino_read(sb, bstart, h_ino, &ino); inode = ERR_PTR(err); if (unlikely(err)) goto out; if (!ino) { ino = au_xino_new_ino(sb); if (unlikely(!ino)) { inode = ERR_PTR(-EIO); goto out; } } AuDbg("i%lu\n", (unsigned long)ino); inode = au_iget_locked(sb, ino); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out; AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); if (inode->i_state & I_NEW) { ii_write_lock_new_child(inode); err = set_inode(inode, dentry); if (!err) { unlock_new_inode(inode); goto out; /* success */ } ii_write_unlock(inode); iget_failed(inode); goto out_err; } else if (!must_new) { /* * horrible race condition between lookup, readdir and copyup * (or something). */ if (mtx) mutex_unlock(mtx); err = reval_inode(inode, dentry, &match); if (!err) { mtx = NULL; goto out; /* success */ } else if (match) { mtx = NULL; goto out_iput; } else if (mtx) mutex_lock(mtx); } if (unlikely(au_test_fs_unique_ino(h_dentry->d_inode))) AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir," " b%d, %s, %.*s, hi%lu, i%lu.\n", bstart, au_sbtype(h_dentry->d_sb), AuDLNPair(dentry), (unsigned long)h_ino, (unsigned long)ino); ino = 0; err = au_xino_write(sb, bstart, h_ino, /*ino*/0); if (!err) { iput(inode); if (mtx) mutex_unlock(mtx); goto new_ino; } out_iput: iput(inode); out_err: inode = ERR_PTR(err); out: if (mtx) mutex_unlock(mtx); return inode; }
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; }