/* * Called from nfsd_lookup and encode_dirent. Check if we have crossed * a mount point. * Returns -EAGAIN or -ETIMEDOUT leaving *dpp and *expp unchanged, * or nfs_ok having possibly changed *dpp and *expp */ int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, struct svc_export **expp) { struct svc_export *exp = *expp, *exp2 = NULL; struct dentry *dentry = *dpp; struct vfsmount *mnt = mntget(exp->ex_path.mnt); struct dentry *mounts = dget(dentry); int err = 0; while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts)); exp2 = rqst_exp_get_by_name(rqstp, mnt, mounts); if (IS_ERR(exp2)) { if (PTR_ERR(exp2) != -ENOENT) err = PTR_ERR(exp2); dput(mounts); mntput(mnt); goto out; } if ((exp->ex_flags & NFSEXP_CROSSMOUNT) || EX_NOHIDE(exp2)) { /* successfully crossed mount point */ exp_put(exp); *expp = exp2; dput(dentry); *dpp = mounts; } else { exp_put(exp2); dput(mounts); } mntput(mnt); out: return err; }
static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd, struct list_head *mntlist) { /* stolen from afs code */ int err; mntget(newmnt); err = do_add_mount(newmnt, &nd->path, nd->path.mnt->mnt_flags | MNT_SHRINKABLE, mntlist); switch (err) { case 0: path_put(&nd->path); nd->path.mnt = newmnt; nd->path.dentry = dget(newmnt->mnt_root); schedule_delayed_work(&cifs_dfs_automount_task, cifs_dfs_mountpoint_expiry_timeout); break; case -EBUSY: /* someone else made a mount here whilst we were busy */ while (d_mountpoint(nd->path.dentry) && follow_down(&nd->path)) ; err = 0; default: mntput(newmnt); break; } return err; }
/* * nfs_follow_mountpoint - handle crossing a mountpoint on the server * @dentry - dentry of mountpoint * @nd - nameidata info * * When we encounter a mountpoint on the server, we want to set up * a mountpoint on the client too, to prevent inode numbers from * colliding, and to allow "df" to work properly. * On NFSv4, we also want to allow for the fact that different * filesystems may be migrated to different servers in a failover * situation, and that different filesystems may want to use * different security flavours. */ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) { struct vfsmount *mnt; struct nfs_server *server = NFS_SERVER(dentry->d_inode); struct dentry *parent; struct nfs_fh fh; struct nfs_fattr fattr; int err; dprintk("--> nfs_follow_mountpoint()\n"); BUG_ON(IS_ROOT(dentry)); dprintk("%s: enter\n", __FUNCTION__); dput(nd->dentry); nd->dentry = dget(dentry); /* Look it up again */ parent = dget_parent(nd->dentry); err = server->nfs_client->rpc_ops->lookup(parent->d_inode, &nd->dentry->d_name, &fh, &fattr); dput(parent); if (err != 0) goto out_err; if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) mnt = nfs_do_refmount(nd->mnt, nd->dentry); else mnt = nfs_do_submount(nd->mnt, nd->dentry, &fh, &fattr); err = PTR_ERR(mnt); if (IS_ERR(mnt)) goto out_err; mntget(mnt); err = do_add_mount(mnt, nd, nd->mnt->mnt_flags|MNT_SHRINKABLE, &nfs_automount_list); if (err < 0) { mntput(mnt); if (err == -EBUSY) goto out_follow; goto out_err; } mntput(nd->mnt); dput(nd->dentry); nd->mnt = mnt; nd->dentry = dget(mnt->mnt_root); schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); out: dprintk("%s: done, returned %d\n", __FUNCTION__, err); dprintk("<-- nfs_follow_mountpoint() = %d\n", err); return ERR_PTR(err); out_err: path_release(nd); goto out; out_follow: while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry)) ; err = 0; goto out; }
/* * Walk down the mount stack looking for an autofs mount that * has the requested mount type (ie. indirect, direct or offset). */ static int autofs_dev_ioctl_find_sbi_type(struct nameidata *nd, unsigned int type) { struct dentry *dentry; struct autofs_info *ino; unsigned int err; err = -ENOENT; /* Lookup the dentry name at the base of our mount point */ dentry = d_lookup(nd->path.dentry, &nd->last); if (!dentry) goto out; dput(nd->path.dentry); nd->path.dentry = dentry; /* And follow the mount stack looking for our autofs mount */ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { ino = autofs4_dentry_ino(nd->path.dentry); if (ino && ino->sbi->type & type) { err = 0; break; } } out: return err; }
/* * Check if the given path is a mountpoint. * * If we are supplied with the file descriptor of an autofs * mount we're looking for a specific mount. In this case * the path is considered a mountpoint if it is itself a * mountpoint or contains a mount, such as a multi-mount * without a root mount. In this case we return 1 if the * path is a mount point and the super magic of the covering * mount if there is one or 0 if it isn't a mountpoint. * * If we aren't supplied with a file descriptor then we * lookup the nameidata of the path and check if it is the * root of a mount. If a type is given we are looking for * a particular autofs mount and if we don't find a match * we return fail. If the located nameidata path is the * root of a mount we return 1 along with the super magic * of the mount or 0 otherwise. * * In both cases the the device number (as returned by * new_encode_dev()) is also returned. */ static int autofs_dev_ioctl_ismountpoint(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { struct path path; const char *name; unsigned int type; unsigned int devid, magic; int err = -ENOENT; if (param->size <= sizeof(*param)) { err = -EINVAL; goto out; } name = param->path; type = param->ismountpoint.in.type; param->ismountpoint.out.devid = devid = 0; param->ismountpoint.out.magic = magic = 0; if (!fp || param->ioctlfd == -1) { if (autofs_type_any(type)) err = kern_path(name, LOOKUP_FOLLOW, &path); else err = find_autofs_mount(name, &path, test_by_type, &type); if (err) goto out; devid = new_encode_dev(path.mnt->mnt_sb->s_dev); err = 0; if (path.dentry->d_inode && path.mnt->mnt_root == path.dentry) { err = 1; magic = path.dentry->d_inode->i_sb->s_magic; } } else { dev_t dev = sbi->sb->s_dev; err = find_autofs_mount(name, &path, test_by_dev, &dev); if (err) goto out; devid = new_encode_dev(dev); err = have_submounts(path.dentry); if (path.mnt->mnt_mountpoint != path.mnt->mnt_root) { if (follow_down(&path)) magic = path.mnt->mnt_sb->s_magic; } } param->ismountpoint.out.devid = devid; param->ismountpoint.out.magic = magic; path_put(&path); out: return err; }
static int autofs4_dir_open(struct inode *inode, struct file *file) { struct dentry *dentry = file->f_dentry; struct vfsmount *mnt = file->f_vfsmnt; struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); int status; DPRINTK("file=%p dentry=%p %.*s", file, dentry, dentry->d_name.len, dentry->d_name.name); if (autofs4_oz_mode(sbi)) goto out; if (autofs4_ispending(dentry)) { DPRINTK("dentry busy"); return -EBUSY; } if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) { struct nameidata nd; int empty; /* In case there are stale directory dentrys from a failed mount */ spin_lock(&dcache_lock); empty = list_empty(&dentry->d_subdirs); spin_unlock(&dcache_lock); if (!empty) d_invalidate(dentry); nd.flags = LOOKUP_DIRECTORY; status = (dentry->d_op->d_revalidate)(dentry, &nd); if (!status) return -ENOENT; } if (d_mountpoint(dentry)) { struct file *fp = NULL; struct vfsmount *fp_mnt = mntget(mnt); struct dentry *fp_dentry = dget(dentry); while (follow_down(&fp_mnt, &fp_dentry) && d_mountpoint(fp_dentry)); fp = dentry_open(fp_dentry, fp_mnt, file->f_flags); status = PTR_ERR(fp); if (IS_ERR(fp)) { file->private_data = NULL; return status; } file->private_data = fp; } out: return 0; }
void get_physical_root(struct path *root) { struct krg_namespace *krg_ns = find_get_krg_ns(); BUG_ON(!krg_ns); root->mnt = krg_ns->root_nsproxy.mnt_ns->root; root->dentry = root->mnt->mnt_root; path_get(root); put_krg_ns(krg_ns); while (d_mountpoint(root->dentry) && follow_down(&root->mnt, &root->dentry)) ; }
/* * follow a link from a mountpoint directory, thus causing it to be mounted */ static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd) { struct vfsmount *newmnt; int err; _enter("%p{%s},{%s:%p{%s},}", dentry, dentry->d_name.name, nd->mnt->mnt_devname, dentry, nd->dentry->d_name.name); dput(nd->dentry); nd->dentry = dget(dentry); newmnt = afs_mntpt_do_automount(nd->dentry); if (IS_ERR(newmnt)) { path_release(nd); return (void *)newmnt; } mntget(newmnt); err = do_add_mount(newmnt, nd, MNT_SHRINKABLE, &afs_vfsmounts); switch (err) { case 0: mntput(nd->mnt); dput(nd->dentry); nd->mnt = newmnt; nd->dentry = dget(newmnt->mnt_root); schedule_delayed_work(&afs_mntpt_expiry_timer, afs_mntpt_expiry_timeout * HZ); break; case -EBUSY: /* someone else made a mount here whilst we were busy */ while (d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry)) ; err = 0; default: mntput(newmnt); break; } _leave(" = %d", err); return ERR_PTR(err); }
/* Check a mount point for busyness */ static int autofs4_mount_busy(struct vfsmount *mnt, struct dentry *dentry) { struct dentry *top = dentry; int status = 1; DPRINTK("dentry %p %.*s", dentry, (int)dentry->d_name.len, dentry->d_name.name); mntget(mnt); dget(dentry); if (!follow_down(&mnt, &dentry)) goto done; if (is_autofs4_dentry(dentry)) { struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb); /* This is an autofs submount, we can't expire it */ if (autofs_type_indirect(sbi->type)) goto done; /* * Otherwise it's an offset mount and we need to check * if we can umount its mount, if there is one. */ if (!d_mountpoint(dentry)) goto done; } /* Update the expiry counter if fs is busy */ if (!may_umount_tree(mnt)) { struct autofs_info *ino = autofs4_dentry_ino(top); ino->last_used = jiffies; goto done; } status = 0; done: DPRINTK("returning = %d", status); dput(dentry); mntput(mnt); return status; }
/* * Walk down the mount stack looking for an autofs mount that * has the requested device number (aka. new_encode_dev(sb->s_dev). */ static int autofs_dev_ioctl_find_super(struct nameidata *nd, dev_t devno) { struct dentry *dentry; struct inode *inode; struct super_block *sb; dev_t s_dev; unsigned int err; err = -ENOENT; /* Lookup the dentry name at the base of our mount point */ dentry = d_lookup(nd->path.dentry, &nd->last); if (!dentry) goto out; dput(nd->path.dentry); nd->path.dentry = dentry; /* And follow the mount stack looking for our autofs mount */ while (follow_down(&nd->path.mnt, &nd->path.dentry)) { inode = nd->path.dentry->d_inode; if (!inode) break; sb = inode->i_sb; s_dev = new_encode_dev(sb->s_dev); if (devno == s_dev) { if (sb->s_magic == AUTOFS_SUPER_MAGIC) { err = 0; break; } } } out: return err; }
/* * Look up one component of a pathname. * N.B. After this call _both_ fhp and resfh need an fh_put * * If the lookup would cross a mountpoint, and the mounted filesystem * is exported to the client with NFSEXP_CROSSMNT, then the lookup is * accepted as it stands and the mounted directory is * returned. Otherwise the covered directory is returned. * NOTE: this mountpoint crossing is not supported properly by all * clients and is explicitly disallowed for NFSv3 * NeilBrown <*****@*****.**> */ int nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, int len, struct svc_fh *resfh) { struct svc_export *exp; struct dentry *dparent; struct dentry *dentry; int err; dprintk("nfsd: nfsd_lookup(fh %s, %.*s)\n", SVCFH_fmt(fhp), len,name); /* Obtain dentry and export. */ err = fh_verify(rqstp, fhp, S_IFDIR, MAY_EXEC); if (err) goto out; dparent = fhp->fh_dentry; exp = fhp->fh_export; err = nfserr_acces; /* Lookup the name, but don't follow links */ if (isdotent(name, len)) { if (len==1) dentry = dget(dparent); else { /* must be ".." */ /* checking mountpoint crossing is very different when stepping up */ if (dparent == exp->ex_dentry) { if (!EX_CROSSMNT(exp)) dentry = dget(dparent); /* .. == . just like at / */ else { struct svc_export *exp2 = NULL; struct dentry *dp; struct vfsmount *mnt = mntget(exp->ex_mnt); dentry = dget(dparent); while(follow_up(&mnt, &dentry)) ; dp = dget(dentry->d_parent); dput(dentry); dentry = dp; for ( ; exp2 == NULL && dp->d_parent != dp; dp=dp->d_parent) exp2 = exp_get(exp->ex_client, dp->d_inode->i_dev, dp->d_inode->i_ino); if (exp2==NULL) { dput(dentry); dentry = dget(dparent); } else { exp = exp2; } mntput(mnt); } } else dentry = dget(dparent->d_parent); } } else { fh_lock(fhp); dentry = lookup_one_len(name, dparent, len); err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_nfserr; /* * check if we have crossed a mount point ... */ if (d_mountpoint(dentry)) { struct svc_export *exp2 = NULL; struct vfsmount *mnt = mntget(exp->ex_mnt); struct dentry *mounts = dget(dentry); while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts)) ; exp2 = exp_get(rqstp->rq_client, mounts->d_inode->i_dev, mounts->d_inode->i_ino); if (exp2 && EX_CROSSMNT(exp2)) { /* successfully crossed mount point */ exp = exp2; dput(dentry); dentry = mounts; } else dput(mounts); mntput(mnt); } } /* * Note: we compose the file handle now, but as the * dentry may be negative, it may need to be updated. */ err = fh_compose(resfh, exp, dentry, fhp); if (!err && !dentry->d_inode) err = nfserr_noent; out: return err; out_nfserr: err = nfserrno(err); goto out; }
/* * Check if the given path is a mountpoint. * * If we are supplied with the file descriptor of an autofs * mount we're looking for a specific mount. In this case * the path is considered a mountpoint if it is itself a * mountpoint or contains a mount, such as a multi-mount * without a root mount. In this case we return 1 if the * path is a mount point and the super magic of the covering * mount if there is one or 0 if it isn't a mountpoint. * * If we aren't supplied with a file descriptor then we * lookup the nameidata of the path and check if it is the * root of a mount. If a type is given we are looking for * a particular autofs mount and if we don't find a match * we return fail. If the located nameidata path is the * root of a mount we return 1 along with the super magic * of the mount or 0 otherwise. * * In both cases the the device number (as returned by * new_encode_dev()) is also returned. */ static int autofs_dev_ioctl_ismountpoint(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param) { struct nameidata nd; const char *path; unsigned int type; int err = -ENOENT; if (param->size <= sizeof(*param)) { err = -EINVAL; goto out; } path = param->path; type = param->arg1; param->arg1 = 0; param->arg2 = 0; if (!fp || param->ioctlfd == -1) { if (type == AUTOFS_TYPE_ANY) { struct super_block *sb; err = path_lookup(path, LOOKUP_FOLLOW, &nd); if (err) goto out; sb = nd.path.dentry->d_sb; param->arg1 = new_encode_dev(sb->s_dev); } else { struct autofs_info *ino; err = path_lookup(path, LOOKUP_PARENT, &nd); if (err) goto out; err = autofs_dev_ioctl_find_sbi_type(&nd, type); if (err) goto out_release; ino = autofs4_dentry_ino(nd.path.dentry); param->arg1 = autofs4_get_dev(ino->sbi); } err = 0; if (nd.path.dentry->d_inode && nd.path.mnt->mnt_root == nd.path.dentry) { err = 1; param->arg2 = nd.path.dentry->d_inode->i_sb->s_magic; } } else { dev_t devid = new_encode_dev(sbi->sb->s_dev); err = path_lookup(path, LOOKUP_PARENT, &nd); if (err) goto out; err = autofs_dev_ioctl_find_super(&nd, devid); if (err) goto out_release; param->arg1 = autofs4_get_dev(sbi); err = have_submounts(nd.path.dentry); if (nd.path.mnt->mnt_mountpoint != nd.path.mnt->mnt_root) { if (follow_down(&nd.path.mnt, &nd.path.dentry)) { struct inode *inode = nd.path.dentry->d_inode; param->arg2 = inode->i_sb->s_magic; } } } out_release: path_put(&nd.path); out: return err; }
static void *cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) { DFS_INFO3_PARAM *referrals = NULL; unsigned int num_referrals = 0; struct cifs_sb_info *cifs_sb; struct cifsSesInfo *ses; char *full_path = NULL; int xid, i; int rc = 0; struct vfsmount *mnt = ERR_PTR(-ENOENT); cFYI(1, ("in %s", __FUNCTION__ )); BUG_ON(IS_ROOT(dentry)); xid = GetXid(); dput(nd->dentry); nd->dentry = dget(dentry); if (d_mountpoint(nd->dentry)) { goto out_follow; } if ( dentry->d_inode == NULL ) { rc = -EINVAL; goto out_err; } cifs_sb = CIFS_SB(dentry->d_inode->i_sb); ses = cifs_sb->tcon->ses; if ( !ses ) { rc = -EINVAL; goto out_err; } full_path = build_full_dfs_path_from_dentry(dentry); if ( full_path == NULL ) { rc = -ENOMEM; goto out_err; } rc = get_dfs_path(xid, ses , full_path, cifs_sb->local_nls, &num_referrals, &referrals, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); for (i = 0; i < num_referrals; i++) { cFYI(1, ("%s: ref path: %s", __FUNCTION__, referrals[i].path_name)); cFYI(1, ("%s: node path: %s", __FUNCTION__, referrals[i].node_name )); cFYI(1, ("%s: fl: %hd, serv_type: %hd, ref_flags: %hd, " "path_consumed: %hd", __FUNCTION__, referrals[i].flags, referrals[i].server_type, referrals[i].ref_flag, referrals[i].PathConsumed)); /* connect to storage node */ if (referrals[i].flags & DFSREF_STORAGE_SERVER) { int len; len = strlen(referrals[i].node_name); if (len < 2) { cERROR(1, ("%s: Net Address path too short: %s", __FUNCTION__, referrals[i].node_name )); rc = -EINVAL; goto out_err; } else { mnt = cifs_dfs_do_refmount(nd->mnt, nd->dentry, referrals[i].node_name); cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p", __FUNCTION__, referrals[i].node_name, mnt)); if ( !rc ) { /* have server so stop here & return */ break; } } } } rc = PTR_ERR(mnt); if (IS_ERR(mnt)) goto out_err; mntget(mnt); rc = do_add_mount(mnt, nd, nd->mnt->mnt_flags, &cifs_dfs_automount_list); if (rc < 0) { mntput(mnt); if (rc == -EBUSY) goto out_follow; goto out_err; } mntput(nd->mnt); dput(nd->dentry); nd->mnt = mnt; nd->dentry = dget(mnt->mnt_root); out: FreeXid(xid); free_dfs_info_array(referrals, num_referrals); cFYI(1, ("leaving %s", __FUNCTION__ )); return ERR_PTR(rc); out_err: if ( full_path ) kfree(full_path); path_release(nd); goto out; out_follow: while (d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry)) ; rc = 0; goto out; }
static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) { struct vfsmount *mnt; struct nfs_server *server = NFS_SERVER(dentry->d_inode); struct dentry *parent; struct nfs_fh *fh = NULL; struct nfs_fattr *fattr = NULL; int err; dprintk("--> nfs_follow_mountpoint()\n"); err = -ESTALE; if (IS_ROOT(dentry)) goto out_err; err = -ENOMEM; fh = nfs_alloc_fhandle(); fattr = nfs_alloc_fattr(); if (fh == NULL || fattr == NULL) goto out_err; dprintk("%s: enter\n", __func__); dput(nd->path.dentry); nd->path.dentry = dget(dentry); /* Look it up again */ parent = dget_parent(nd->path.dentry); err = server->nfs_client->rpc_ops->lookup(parent->d_inode, &nd->path.dentry->d_name, fh, fattr); dput(parent); if (err != 0) goto out_err; if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) mnt = nfs_do_refmount(nd->path.mnt, nd->path.dentry); else mnt = nfs_do_submount(nd->path.mnt, nd->path.dentry, fh, fattr); err = PTR_ERR(mnt); if (IS_ERR(mnt)) goto out_err; mntget(mnt); err = do_add_mount(mnt, &nd->path, nd->path.mnt->mnt_flags|MNT_SHRINKABLE, &nfs_automount_list); if (err < 0) { mntput(mnt); if (err == -EBUSY) goto out_follow; goto out_err; } path_put(&nd->path); nd->path.mnt = mnt; nd->path.dentry = dget(mnt->mnt_root); schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); out: nfs_free_fattr(fattr); nfs_free_fhandle(fh); dprintk("%s: done, returned %d\n", __func__, err); dprintk("<-- nfs_follow_mountpoint() = %d\n", err); return ERR_PTR(err); out_err: path_put(&nd->path); goto out; out_follow: while (d_mountpoint(nd->path.dentry) && follow_down(&nd->path)) ; err = 0; goto out; }
static void* snap_mountpoint_follow_link(struct dentry *dentry, struct nameidata *nd) { struct vfsmount *mnt = ERR_PTR(-ENOENT); vnode_t *dir_vp = NULL; char *snapname = NULL; char *zfs_fs_name = NULL; int rc = 0; vfs_t *vfsp = NULL; ASSERT(dentry->d_parent); dir_vp = LZFS_ITOV(dentry->d_parent->d_inode); vfsp = dir_vp->v_vfsp; ASSERT(vfsp); zfs_fs_name = kzalloc(MAXNAMELEN, KM_SLEEP); dput(nd->path.dentry); nd->path.dentry = dget(dentry); zfs_fs_name_fn(vfsp->vfs_data, zfs_fs_name); snapname = kzalloc(strlen(zfs_fs_name) + strlen(dentry->d_name.name) + 2, KM_SLEEP); snapname = strncpy(snapname, zfs_fs_name, strlen(zfs_fs_name) + 1); snapname = strcat(snapname, "@"); snapname = strcat(snapname, dentry->d_name.name); mnt = vfs_kern_mount(&lzfs_fs_type, 0, snapname, NULL); ((vfs_t *)mnt->mnt_sb->s_fs_info)->vfs_mntpt = dentry; mntget(mnt); rc = PTR_ERR(mnt); if (IS_ERR(mnt)) { goto out_err; } mnt->mnt_mountpoint = dentry; ASSERT(nd); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) rc = do_add_mount(mnt, nd, nd->path.mnt->mnt_flags | MNT_READONLY, NULL); #else rc = do_add_mount(mnt, &nd->path, nd->path.mnt->mnt_flags | MNT_READONLY, NULL); #endif switch (rc) { case 0: path_put(&nd->path); nd->path.mnt = mnt; nd->path.dentry = dget(mnt->mnt_root); break; case -EBUSY: nd->path.dentry = dget(mnt->mnt_root); /* someone else made a mount here whilst we were busy */ #ifdef HAVE_2ARGS_FOLLOW_DOWN /* for kernel version < 2.6.31 */ while (d_mountpoint(nd->path.dentry) && follow_down(&mnt, &nd->path.dentry)) { ; } #else while (d_mountpoint(nd->path.dentry) && follow_down(&nd->path)) { ; } #endif rc = 0; default: mntput(mnt); break; } kfree(zfs_fs_name); kfree(snapname); return ERR_PTR(rc); out_err: path_put(&nd->path); kfree(zfs_fs_name); kfree(snapname); return ERR_PTR(rc); }