Exemplo n.º 1
0
/*
 * Callback supporting lookup in a GFS XATTR directory.
 */
static int
xattr_lookup_cb(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop,
    cred_t *cr, int flags, int *deflags, pathname_t *rpnp)
{
	vnode_t *pvp;
	struct pathname pn;
	int error;

	*vpp = NULL;
	*inop = 0;

	error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR, cr, NULL);

	/*
	 * Return ENOENT for EACCES requests during lookup.  Once an
	 * attribute create is attempted EACCES will be returned.
	 */
	if (error) {
		if (error == EACCES)
			return (ENOENT);
		return (error);
	}

	error = pn_get((char *)nm, UIO_SYSSPACE, &pn);
	if (error == 0) {
		error = VOP_LOOKUP(pvp, (char *)nm, vpp, &pn, flags, rootvp,
		    cr, NULL, deflags, rpnp);
		pn_free(&pn);
	}

	return (error);
}
Exemplo n.º 2
0
/*
 * readdir_xattr_casecmp: given a system attribute name, see if there
 * is a real xattr with the same normalized name.
 */
static int
readdir_xattr_casecmp(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
    int *eflags)
{
	int error;
	vnode_t *vp;
	struct pathname pn;

	*eflags = 0;

	error = pn_get(nm, UIO_SYSSPACE, &pn);
	if (error == 0) {
		error = VOP_LOOKUP(dvp, nm, &vp, &pn,
		    FIGNORECASE, rootvp, cr, ct, NULL, NULL);
		if (error == 0) {
			*eflags = ED_CASE_CONFLICT;
			VN_RELE(vp);
		} else if (error == ENOENT) {
			error = 0;
		}
		pn_free(&pn);
	}

	return (error);
}
Exemplo n.º 3
0
static int
is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir)
{
	struct match_arg marg;
	struct pathname pn;
	struct vnode *gvp;
	struct sdev_node *gdir = dir->sdev_origin;

	if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred,
	    NULL, NULL, NULL) != 0)
		return (0);

	if (gvp->v_type != VDIR) {
		VN_RELE(gvp);
		return (0);
	}

	if (pn_get(pathleft, UIO_SYSSPACE, &pn) != 0) {
		VN_RELE(gvp);
		return (0);
	}

	marg.expr = kmem_alloc(MAXNAMELEN, KM_SLEEP);
	(void) pn_getcomponent(&pn, marg.expr);
	marg.match = 0;

	walk_dir(gvp, &marg, match_name);
	VN_RELE(gvp);
	kmem_free(marg.expr, MAXNAMELEN);
	pn_free(&pn);

	return (marg.match);
}
Exemplo n.º 4
0
static int zfsfuse_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
	if(strlen(name) >= MAXNAMELEN)
		return ENAMETOOLONG;

	vfs_t *vfs = (vfs_t *) fuse_req_userdata(req);
	zfsvfs_t *zfsvfs = vfs->vfs_data;

	ZFS_ENTER(zfsvfs);

	znode_t *znode;

	int error = zfs_zget(zfsvfs, parent, &znode, B_TRUE);
	if(error) {
		ZFS_EXIT(zfsvfs);
		/* If the inode we are trying to get was recently deleted
		   dnode_hold_impl will return EEXIST instead of ENOENT */
		return error == EEXIST ? ENOENT : error;
	}

	ASSERT(znode != NULL);
	vnode_t *dvp = ZTOV(znode);
	ASSERT(dvp != NULL);

	vnode_t *vp = NULL;

	cred_t cred;
	zfsfuse_getcred(req, &cred);

	error = VOP_LOOKUP(dvp, (char *) name, &vp, NULL, 0, NULL, &cred, NULL, NULL, NULL);
	if(error)
		goto out;

	struct fuse_entry_param e = { 0 };

	e.attr_timeout = 0.0;
	e.entry_timeout = 0.0;

	if(vp == NULL)
		goto out;

	e.ino = VTOZ(vp)->z_id;
	if(e.ino == 3)
		e.ino = 1;

	e.generation = VTOZ(vp)->z_phys->zp_gen;

	error = zfsfuse_stat(vp, &e.attr, &cred);

out:
	if(vp != NULL)
		VN_RELE(vp);
	VN_RELE(dvp);
	ZFS_EXIT(zfsvfs);

	if(!error)
		fuse_reply_entry(req, &e);

	return error;
}
Exemplo n.º 5
0
STATIC struct dentry *
linvfs_get_parent(
	struct dentry		*child)
{
	int			error;
	vnode_t			*vp, *cvp;
	struct dentry		*parent;
	struct inode		*ip = NULL;
	struct dentry		dotdot;

	dotdot.d_name.name = "..";
	dotdot.d_name.len = 2;
	dotdot.d_inode = 0;

	cvp = NULL;
	vp = LINVFS_GET_VP(child->d_inode);
	VOP_LOOKUP(vp, &dotdot, &cvp, 0, NULL, NULL, error);

	if (!error) {
		ASSERT(cvp);
		ip = LINVFS_GET_IP(cvp);
		if (!ip) {
			VN_RELE(cvp);
			return ERR_PTR(-EACCES);
		}
	}
	if (error)
		return ERR_PTR(-error);
	parent = d_alloc_anon(ip);
	if (!parent) {
		VN_RELE(cvp);
		parent = ERR_PTR(-ENOMEM);
	}
	return parent;
}
Exemplo n.º 6
0
struct dentry *
vnlayer_get_parent(struct dentry *child)
{
    VNODE_T *parentvp;
    struct dentry *rdentp;
    struct lookup_ctx ctx = {0};
    CALL_DATA_T cd;
    int err;

    if (!MDKI_INOISMVFS(child->d_inode))
        return ERR_PTR(-ESTALE);

    mdki_linux_init_call_data(&cd);

    err = VOP_LOOKUP(ITOV(child->d_inode), "..", &parentvp, NULL,
                     VNODE_LF_LOOKUP, NULL, &cd, &ctx);
    mdki_linux_destroy_call_data(&cd);
    if (err == 0) {
        ASSERT(ctx.dentrypp == NULL);
        ASSERT(parentvp != NULL);
        if (!MDKI_INOISMVFS(VTOI(parentvp))) {
            rdentp = ERR_PTR(-ESTALE);
        } else { 
            rdentp = vnlayer_find_dentry(parentvp);
        }
        /* always drop vnode's refcount */
        VN_RELE(parentvp);
    } else {
        rdentp = ERR_PTR(mdki_errno_unix_to_linux(err));
    }
    return rdentp;
}
Exemplo n.º 7
0
STATIC struct dentry *
linvfs_lookup(
	struct inode	*dir,
	struct dentry	*dentry)
{
	struct inode	*ip = NULL;
	vnode_t		*vp, *cvp = NULL;
	int		error;

	if (dentry->d_name.len >= MAXNAMELEN)
		return ERR_PTR(-ENAMETOOLONG);

	vp = LINVFS_GET_VP(dir);
	VOP_LOOKUP(vp, dentry, &cvp, 0, NULL, NULL, error);
	if (!error) {
		ASSERT(cvp);
		ip = LINVFS_GET_IP(cvp);
		if (!ip) {
			VN_RELE(cvp);
			return ERR_PTR(-EACCES);
		}
	}
	if (error && (error != ENOENT))
		return ERR_PTR(-error);
	d_add(dentry, ip);	/* Negative entry goes in if ip is NULL */
	return NULL;
}
Exemplo n.º 8
0
/* ARGSUSED */
static int
zfsctl_shares_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp,
    int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
    int *direntflags, pathname_t *realpnp)
{
	zfsvfs_t *zfsvfs = dvp->v_vfsp->vfs_data;
	znode_t *dzp;
	int error;

	ZFS_ENTER(zfsvfs);

	if (gfs_lookup_dot(vpp, dvp, zfsvfs->z_ctldir, nm) == 0) {
		ZFS_EXIT(zfsvfs);
		return (0);
	}

	if (zfsvfs->z_shares_dir == 0) {
		ZFS_EXIT(zfsvfs);
		return (ENOTSUP);
	}
	if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &dzp)) == 0)
		error = VOP_LOOKUP(ZTOV(dzp), nm, vpp, pnp,
		    flags, rdir, cr, ct, direntflags, realpnp);

	VN_RELE(ZTOV(dzp));
	ZFS_EXIT(zfsvfs);

	return (error);
}
Exemplo n.º 9
0
int
nfs4_readdir_getvp(vnode_t *dvp, char *d_name, vnode_t **vpp,
		struct exportinfo **exi, struct svc_req *req,
		struct compound_state *cs, int expseudo)
{
	int error;
	int ismntpt;
	fid_t fid;
	vnode_t *vp, *pre_tvp;
	nfsstat4 status;
	struct exportinfo *newexi, *saveexi;
	cred_t *scr;

	*vpp = vp = NULL;

	if (error = VOP_LOOKUP(dvp, d_name, &vp, NULL, 0, NULL, cs->cr,
	    NULL, NULL, NULL))
		return (error);

	/*
	 * If the directory is a referral point, don't return the
	 * attrs, instead set rdattr_error to MOVED.
	 */
	if (vn_is_nfs_reparse(vp, cs->cr) && !client_is_downrev(req)) {
		VN_RELE(vp);
		DTRACE_PROBE2(nfs4serv__func__referral__moved,
		    vnode_t *, vp, char *, "nfs4_readdir_getvp");
		return (NFS4ERR_MOVED);
	}
Exemplo n.º 10
0
/* ARGSUSED */
void
acl3_getxattrdir(GETXATTRDIR3args *args, GETXATTRDIR3res *resp,
    struct exportinfo *exi, struct svc_req *req, cred_t *cr, bool_t ro)
{
	int error;
	int flags;
	vnode_t *vp, *avp;

	vp = nfs3_fhtovp(&args->fh, exi);
	if (vp == NULL) {
		resp->status = NFS3ERR_STALE;
		return;
	}

	flags = LOOKUP_XATTR;
	if (args->create)
		flags |= CREATE_XATTR_DIR;
	else {
		ulong_t val = 0;

		error = VOP_PATHCONF(vp, _PC_SATTR_EXISTS, &val, cr, NULL);
		if (!error && val == 0) {
			error = VOP_PATHCONF(vp, _PC_XATTR_EXISTS,
			    &val, cr, NULL);
			if (!error && val == 0) {
				VN_RELE(vp);
				resp->status = NFS3ERR_NOENT;
				return;
			}
		}
	}

	error = VOP_LOOKUP(vp, "", &avp, NULL, flags, NULL, cr,
	    NULL, NULL, NULL);
	if (!error && avp == vp) {	/* lookup of "" on old FS? */
		error = EINVAL;
		VN_RELE(avp);
	}
	if (!error) {
		struct vattr va;
		va.va_mask = AT_ALL;
		error = rfs4_delegated_getattr(avp, &va, 0, cr);
		if (!error) {
			vattr_to_post_op_attr(&va, &resp->resok.attr);
			error = makefh3(&resp->resok.fh, avp, exi);
		}
		VN_RELE(avp);
	}

	VN_RELE(vp);

	if (error) {
		resp->status = puterrno3(error);
		return;
	}
	resp->status = NFS3_OK;
}
Exemplo n.º 11
0
static int
zfs_replay_remove(zfsvfs_t *zfsvfs, void *data, boolean_t byteswap)
{
#ifndef TODO_OSV
	kprintf("TX_REMOVE\n");
	return EOPNOTSUPP;
#else
	lr_remove_t *lr = data;
	char *name = (char *)(lr + 1);	/* name follows lr_remove_t */
	znode_t *dzp;
	struct componentname cn;
	vnode_t *vp;
	int error;
	int vflg = 0;

	if (byteswap)
		byteswap_uint64_array(lr, sizeof (*lr));

	if ((error = zfs_zget(zfsvfs, lr->lr_doid, &dzp)) != 0)
		return (error);

	if (lr->lr_common.lrc_txtype & TX_CI)
		vflg |= FIGNORECASE;
	cn.cn_nameptr = name;
	cn.cn_namelen = strlen(name);
	cn.cn_nameiop = DELETE;
	cn.cn_flags = ISLASTCN | SAVENAME;
	cn.cn_lkflags = LK_EXCLUSIVE | LK_RETRY;
	cn.cn_cred = kcred;
	cn.cn_thread = curthread;
	vn_lock(ZTOV(dzp), LK_EXCLUSIVE | LK_RETRY);
	error = VOP_LOOKUP(ZTOV(dzp), &vp, &cn);
	if (error != 0) {
		VOP_UNLOCK(ZTOV(dzp), 0);
		goto fail;
	}

	switch ((int)lr->lr_common.lrc_txtype) {
	case TX_REMOVE:
		error = VOP_REMOVE(ZTOV(dzp), vp, &cn /*,vflg*/);
		break;
	case TX_RMDIR:
		error = VOP_RMDIR(ZTOV(dzp), vp, &cn /*,vflg*/);
		break;
	default:
		error = ENOTSUP;
	}
	vput(vp);
	VOP_UNLOCK(ZTOV(dzp), 0);

fail:
	VN_RELE(ZTOV(dzp));

	return (error);
#endif
}
Exemplo n.º 12
0
int
smb_vop_lookup_xattrdir(vnode_t *fvp, vnode_t **xattrdirvpp, int flags,
    cred_t *cr)
{
	int error;

	error = VOP_LOOKUP(fvp, "", xattrdirvpp, NULL, flags, NULL, cr,
	    &smb_ct, NULL, NULL);
	return (error);
}
Exemplo n.º 13
0
/*
 * Given a global path (from rootdir), and a vnode that is the current root,
 * return the portion of the path that is beneath the current root or NULL on
 * failure.  The path MUST be a resolved path (no '..' entries or symlinks),
 * otherwise this function will fail.
 */
static char *
localpath(char *path, struct vnode *vrootp, cred_t *cr)
{
	vnode_t *vp;
	vnode_t *cvp;
	char component[MAXNAMELEN];
	char *ret = NULL;
	pathname_t pn;

	/*
	 * We use vn_compare() instead of VN_CMP() in order to detect lofs
	 * mounts and stacked vnodes.
	 */
	if (vn_compare(vrootp, rootdir))
		return (path);

	if (pn_get(path, UIO_SYSSPACE, &pn) != 0)
		return (NULL);

	vp = rootdir;
	VN_HOLD(vp);

	if (vn_ismntpt(vp) && traverse(&vp) != 0) {
		VN_RELE(vp);
		pn_free(&pn);
		return (NULL);
	}

	while (pn_pathleft(&pn)) {
		pn_skipslash(&pn);

		if (pn_getcomponent(&pn, component) != 0)
			break;

		if (VOP_LOOKUP(vp, component, &cvp, &pn, 0, rootdir, cr,
		    NULL, NULL, NULL) != 0)
			break;
		VN_RELE(vp);
		vp = cvp;

		if (vn_ismntpt(vp) && traverse(&vp) != 0)
			break;

		if (vn_compare(vp, vrootp)) {
			ret = path + (pn.pn_path - pn.pn_buf);
			break;
		}
	}

	VN_RELE(vp);
	pn_free(&pn);

	return (ret);
}
Exemplo n.º 14
0
static int
VMBlockLookup(struct vnode *dvp,   // IN:  Directory to look in
              char *nm,            // IN:  Name of component to lookup in directory
              struct vnode **vpp,  // OUT: Pointer to vnode representing found file
              struct pathname *pnp,// IN:  Full pathname being looked up
              int flags,           // IN:  Lookup flags (see vnode.h)
              struct vnode *rdir,  // IN:  Vnode of root device
              struct cred *cr          // IN:  Credentials of caller
#if OS_VFS_VERSION >= 5
           ,  caller_context_t *ctx    // IN: Caller's context
           ,  int *direntflags         // IN:
           ,  struct pathname *rpnp    // IN:
#endif
            )
{
   struct vnode *realVp;
   VMBlockMountInfo *mip;
   int ret;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMblockLookup: entry\n");

   /* First ensure that we are looking in a directory. */
   if (dvp->v_type != VDIR) {
      return ENOTDIR;
   }

   /* Don't invoke lookup for ourself. */
   if (nm[0] == '\0' || (nm[0] == '.' && nm[1] == '\0')) {
      VN_HOLD(dvp);
      *vpp = dvp;
      return 0;
   }

   *vpp = NULL;

   /* Make sure nm exists before creating our link to it. */
   mip = VPTOMIP(dvp);
   ret = VOP_LOOKUP(mip->redirectVnode, nm, &realVp, pnp, flags, rdir, cr
#if OS_VFS_VERSION >= 5
                    , ctx, direntflags, rpnp
#endif
                   );
   if (ret) {
      return ret;
   }

   ret = VMBlockVnodeGet(vpp, realVp, nm, strlen(nm), dvp, dvp->v_vfsp, FALSE);
   if (ret) {
      VN_RELE(realVp);
      return ret;
   }

   return 0;
}
Exemplo n.º 15
0
int
RUMP_VOP_LOOKUP(struct vnode *dvp,
    struct vnode **vpp,
    struct componentname *cnp)
{
	int error;

	rump_schedule();
	error = VOP_LOOKUP(dvp, vpp, cnp);
	rump_unschedule();

	return error;
}
Exemplo n.º 16
0
/*
 * Create a directory node in a non-global dev instance.
 * Always create shadow vnode. Set sdev_origin to the corresponding
 * global directory sdev_node if it exists. This facilitates the
 * lookup operation.
 */
static int
prof_make_dir(char *name, struct sdev_node **gdirp, struct sdev_node **dirp)
{
	struct sdev_node *dir = *dirp;
	struct sdev_node *gdir = *gdirp;
	struct sdev_node *newdv;
	struct vnode *avp, *gnewdir = NULL;
	struct vattr vattr;
	int error;

	/* see if name already exists */
	rw_enter(&dir->sdev_contents, RW_READER);
	if (newdv = sdev_cache_lookup(dir, name)) {
		*dirp = newdv;
		*gdirp = newdv->sdev_origin;
		rw_exit(&dir->sdev_contents);
		SDEV_RELE(dir);
		return (0);
	}
	rw_exit(&dir->sdev_contents);

	/* find corresponding dir node in global dev */
	if (gdir) {
		error = VOP_LOOKUP(SDEVTOV(gdir), name, &gnewdir,
		    NULL, 0, NULL, kcred, NULL, NULL, NULL);
		if (error == 0) {
			*gdirp = VTOSDEV(gnewdir);
		} else { 	/* it's ok if there no global dir */
			*gdirp = NULL;
		}
	}

	/* get attribute from shadow, also create shadow dir */
	prof_getattr(dir, name, gnewdir, &vattr, &avp, NULL);

	/* create dev directory vnode */
	rw_enter(&dir->sdev_contents, RW_WRITER);
	error = prof_mknode(dir, name, &newdv, &vattr, avp, (void *)*gdirp,
	    kcred);
	rw_exit(&dir->sdev_contents);
	if (error == 0) {
		ASSERT(newdv);
		*dirp = newdv;
	}
	SDEV_RELE(dir);
	return (error);
}
Exemplo n.º 17
0
static void
prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv,
    struct vattr *vap, struct vnode **avpp, int *no_fs_perm)
{
	struct vnode *advp;

	/* get attribute from shadow, if present; else get default */
	advp = dir->sdev_attrvp;
	if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred,
	    NULL, NULL, NULL) == 0) {
		(void) VOP_GETATTR(*avpp, vap, 0, kcred, NULL);
	} else if (gdv == NULL || gdv->v_type == VDIR) {
		/* always create shadow directory */
		*vap = sdev_vattr_dir;
		if (advp && VOP_MKDIR(advp, name, &sdev_vattr_dir,
		    avpp, kcred, NULL, 0, NULL) != 0) {
			*avpp = NULLVP;
			sdcmn_err10(("prof_getattr: failed to create "
			    "shadow directory %s/%s\n", dir->sdev_path, name));
		}
	} else {
		/*
		 * get default permission from devfs
		 * Before calling devfs_get_defattr, we need to get
		 * the realvp (the dv_node). If realvp is not a dv_node,
		 * devfs_get_defattr() will return a system-wide default
		 * attr for device nodes.
		 */
		struct vnode *rvp;
		if (VOP_REALVP(gdv, &rvp, NULL) != 0)
			rvp = gdv;
		devfs_get_defattr(rvp, vap, no_fs_perm);
		*avpp = NULLVP;
	}

	/* ignore dev_t and vtype from backing store */
	if (gdv) {
		vap->va_type = gdv->v_type;
		vap->va_rdev = gdv->v_rdev;
	}
}
STATIC struct dentry *
linvfs_lookup(
	struct inode	*dir,
	struct dentry	*dentry,
	struct nameidata *nd)
{
	struct vnode	*vp = LINVFS_GET_VP(dir), *cvp;
	int		error;

	if (dentry->d_name.len >= MAXNAMELEN)
		return ERR_PTR(-ENAMETOOLONG);

	VOP_LOOKUP(vp, dentry, &cvp, 0, NULL, NULL, error);
	if (error) {
		if (unlikely(error != ENOENT))
			return ERR_PTR(-error);
		d_add(dentry, NULL);
		return NULL;
	}

	return d_splice_alias(LINVFS_GET_IP(cvp), dentry);
}
Exemplo n.º 19
0
/*
 * We have to carry on the locking protocol on the null layer vnodes
 * as we progress through the tree. We also have to enforce read-only
 * if this layer is mounted read-only.
 */
static int
null_lookup(struct vop_lookup_args *ap)
{
	struct componentname *cnp = ap->a_cnp;
	struct vnode *dvp = ap->a_dvp;
	int flags = cnp->cn_flags;
	struct vnode *vp, *ldvp, *lvp;
	int error;

	if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
		return (EROFS);
	/*
	 * Although it is possible to call null_bypass(), we'll do
	 * a direct call to reduce overhead
	 */
	ldvp = NULLVPTOLOWERVP(dvp);
	vp = lvp = NULL;
	error = VOP_LOOKUP(ldvp, &lvp, cnp);
	if (error == EJUSTRETURN && (flags & ISLASTCN) &&
	    (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
	    (cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME))
		error = EROFS;

	if ((error == 0 || error == EJUSTRETURN) && lvp != NULL) {
		if (ldvp == lvp) {
			*ap->a_vpp = dvp;
			VREF(dvp);
			vrele(lvp);
		} else {
			error = null_nodeget(dvp->v_mount, lvp, &vp);
			if (error)
				vput(lvp);
			else
				*ap->a_vpp = vp;
		}
	}
	return (error);
}
Exemplo n.º 20
0
static int
zfs_replay_rename(zfsvfs_t *zfsvfs, lr_rename_t *lr, boolean_t byteswap)
{
	char *sname = (char *)(lr + 1);	/* sname and tname follow lr_rename_t */
	char *tname = sname + strlen(sname) + 1;
	znode_t *sdzp, *tdzp;
	struct componentname scn, tcn;
	vnode_t *svp, *tvp;
	kthread_t *td = curthread;
	int error;
	int vflg = 0;

	if (byteswap)
		byteswap_uint64_array(lr, sizeof (*lr));

	if ((error = zfs_zget(zfsvfs, lr->lr_sdoid, &sdzp)) != 0)
		return (error);

	if ((error = zfs_zget(zfsvfs, lr->lr_tdoid, &tdzp)) != 0) {
		VN_RELE(ZTOV(sdzp));
		return (error);
	}

	if (lr->lr_common.lrc_txtype & TX_CI)
		vflg |= FIGNORECASE;
	svp = tvp = NULL;

	scn.cn_nameptr = sname;
	scn.cn_namelen = strlen(sname);
	scn.cn_nameiop = DELETE;
	scn.cn_flags = ISLASTCN | SAVENAME;
	scn.cn_lkflags = LK_EXCLUSIVE | LK_RETRY;
	scn.cn_cred = kcred;
	scn.cn_thread = td;
	vn_lock(ZTOV(sdzp), LK_EXCLUSIVE | LK_RETRY);
	error = VOP_LOOKUP(ZTOV(sdzp), &svp, &scn);
	VOP_UNLOCK(ZTOV(sdzp), 0);
	if (error != 0)
		goto fail;
	VOP_UNLOCK(svp, 0);

	tcn.cn_nameptr = tname;
	tcn.cn_namelen = strlen(tname);
	tcn.cn_nameiop = RENAME;
	tcn.cn_flags = ISLASTCN | SAVENAME;
	tcn.cn_lkflags = LK_EXCLUSIVE | LK_RETRY;
	tcn.cn_cred = kcred;
	tcn.cn_thread = td;
	vn_lock(ZTOV(tdzp), LK_EXCLUSIVE | LK_RETRY);
	error = VOP_LOOKUP(ZTOV(tdzp), &tvp, &tcn);
	if (error == EJUSTRETURN)
		tvp = NULL;
	else if (error != 0) {
		VOP_UNLOCK(ZTOV(tdzp), 0);
		goto fail;
	}

	error = VOP_RENAME(ZTOV(sdzp), svp, &scn, ZTOV(tdzp), tvp, &tcn /*,vflg*/);
	return (error);
fail:
	if (svp != NULL)
		vrele(svp);
	if (tvp != NULL)
		vrele(tvp);
	VN_RELE(ZTOV(tdzp));
	VN_RELE(ZTOV(sdzp));

	return (error);
}
Exemplo n.º 21
0
/*
 * This function checks the path to a new export to
 * check whether all the pathname components are
 * exported. It works by climbing the file tree one
 * component at a time via "..", crossing mountpoints
 * if necessary until an export entry is found, or the
 * system root is reached.
 *
 * If an unexported mountpoint is found, then
 * a new pseudo export is added and the pathname from
 * the mountpoint down to the export is added to the
 * visible list for the new pseudo export.  If an existing
 * pseudo export is found, then the pathname is added
 * to its visible list.
 *
 * Note that there's some tests for exportdir.
 * The exportinfo entry that's passed as a parameter
 * is that of the real export and exportdir is set
 * for this case.
 *
 * Here is an example of a possible setup:
 *
 * () - a new fs; fs mount point
 * EXPORT - a real exported node
 * PSEUDO - a pseudo node
 * vis - visible list
 * f# - security flavor#
 * (f#) - security flavor# propagated from its descendents
 * "" - covered vnode
 *
 *
 *                 /
 *                 |
 *                 (a) PSEUDO (f1,f2)
 *                 |   vis: b,b,"c","n"
 *                 |
 *                 b
 *        ---------|------------------
 *        |                          |
 *        (c) EXPORT,f1(f2)          (n) PSEUDO (f1,f2)
 *        |   vis: "e","d"           |   vis: m,m,,p,q,"o"
 *        |                          |
 *  ------------------          -------------------
 *  |        |        |         |                  |
 *  (d)      (e)      f         m EXPORT,f1(f2)    p
 *  EXPORT   EXPORT             |                  |
 *  f1       f2                 |                  |
 *           |                  |                  |
 *           j                 (o) EXPORT,f2       q EXPORT f2
 *
 */
int
treeclimb_export(struct exportinfo *exip)
{
	vnode_t *dvp, *vp;
	fid_t fid;
	int error;
	int exportdir;
	struct exportinfo *exi = NULL;
	struct exportinfo *new_exi = exip;
	struct exp_visible *visp;
	struct exp_visible *vis_head = NULL;
	struct vattr va;
	treenode_t *tree_head = NULL;

	ASSERT(RW_WRITE_HELD(&exported_lock));

	vp = exip->exi_vp;
	VN_HOLD(vp);
	exportdir = 1;

	for (;;) {

		bzero(&fid, sizeof (fid));
		fid.fid_len = MAXFIDSZ;
		error = vop_fid_pseudo(vp, &fid);
		if (error)
			break;

		if (! exportdir) {
			/*
			 * Check if this exportroot is a VROOT dir.  If so,
			 * then attach the pseudonodes.  If not, then
			 * continue .. traversal until we hit a VROOT
			 * export (pseudo or real).
			 */
			exi = checkexport4(&vp->v_vfsp->vfs_fsid, &fid, vp);
			if (exi != NULL && vp->v_flag & VROOT) {
				/*
				 * Found an export info
				 *
				 * Extend the list of visible
				 * directories whether it's a pseudo
				 * or a real export.
				 */
				more_visible(exi, tree_head);
				break;	/* and climb no further */
			}
		}

		/*
		 * If at the root of the filesystem, need
		 * to traverse across the mountpoint
		 * and continue the climb on the mounted-on
		 * filesystem.
		 */
		if (vp->v_flag & VROOT) {

			if (! exportdir) {
				/*
				 * Found the root directory of a filesystem
				 * that isn't exported.  Need to export
				 * this as a pseudo export so that an NFS v4
				 * client can do lookups in it.
				 */
				new_exi = pseudo_exportfs(vp, &fid, vis_head,
				    NULL);
				vis_head = NULL;
			}

			if (VN_CMP(vp, rootdir)) {
				/* at system root */
				/*
				 * If sharing "/", new_exi is shared exportinfo
				 * (exip). Otherwise, new_exi is exportinfo
				 * created in pseudo_exportfs() above.
				 */
				ns_root = tree_prepend_node(tree_head, 0,
				    new_exi);
				break;
			}

			vp = untraverse(vp);
			exportdir = 0;
			continue;
		}

		/*
		 * Do a getattr to obtain the nodeid (inode num)
		 * for this vnode.
		 */
		va.va_mask = AT_NODEID;
		error = VOP_GETATTR(vp, &va, 0, CRED(), NULL);
		if (error)
			break;

		/*
		 *  Add this directory fid to visible list
		 */
		visp = kmem_alloc(sizeof (*visp), KM_SLEEP);
		VN_HOLD(vp);
		visp->vis_vp = vp;
		visp->vis_fid = fid;		/* structure copy */
		visp->vis_ino = va.va_nodeid;
		visp->vis_count = 1;
		visp->vis_exported = exportdir;
		visp->vis_secinfo = NULL;
		visp->vis_seccnt = 0;
		visp->vis_next = vis_head;
		vis_head = visp;


		/*
		 * Will set treenode's pointer to exportinfo to
		 * 1. shared exportinfo (exip) - if first visit here
		 * 2. freshly allocated pseudo export (if any)
		 * 3. null otherwise
		 */
		tree_head = tree_prepend_node(tree_head, visp, new_exi);
		new_exi = NULL;

		/*
		 * Now, do a ".." to find parent dir of vp.
		 */
		error = VOP_LOOKUP(vp, "..", &dvp, NULL, 0, NULL, CRED(),
		    NULL, NULL, NULL);

		if (error == ENOTDIR && exportdir) {
			dvp = exip->exi_dvp;
			ASSERT(dvp != NULL);
			VN_HOLD(dvp);
			error = 0;
		}

		if (error)
			break;

		exportdir = 0;
		VN_RELE(vp);
		vp = dvp;
	}

	VN_RELE(vp);

	/*
	 * We can have set error due to error in:
	 * 1. vop_fid_pseudo()
	 * 2. VOP_GETATTR()
	 * 3. VOP_LOOKUP()
	 * We must free pseudo exportinfos, visibles and treenodes.
	 * Visibles are referenced from treenode_t::tree_vis and
	 * exportinfo_t::exi_visible. To avoid double freeing, only
	 * exi_visible pointer is used, via exi_rele(), for the clean-up.
	 */
	if (error) {
		/* Free unconnected visibles, if there are any. */
		if (vis_head)
			free_visible(vis_head);

		/* Connect unconnected exportinfo, if there is any. */
		if (new_exi && new_exi != exip)
			tree_head = tree_prepend_node(tree_head, 0, new_exi);

		while (tree_head) {
			treenode_t *t2 = tree_head;
			exportinfo_t *e  = tree_head->tree_exi;
			/* exip will be freed in exportfs() */
			if (e && e != exip) {
				exp_kstats_delete(e->exi_kstats);
				avl_remove(&exi_id_tree, e);
				export_unlink(e);
				exi_rele(e);
			}
			tree_head = tree_head->tree_child_first;
			kmem_free(t2, sizeof (*t2));
		}
	}

	return (error);
}
Exemplo n.º 22
0
static int
auto_lookup(
	vnode_t *dvp,
	char *nm,
	vnode_t **vpp,
	pathname_t *pnp,
	int flags,
	vnode_t *rdir,
	cred_t *cred,
	caller_context_t *ct,
	int *direntflags,
	pathname_t *realpnp)
{
	int error = 0;
	vnode_t *newvp = NULL;
	vfs_t *vfsp;
	fninfo_t *dfnip;
	fnnode_t *dfnp = NULL;
	fnnode_t *fnp = NULL;
	char *searchnm;
	int operation;		/* either AUTOFS_LOOKUP or AUTOFS_MOUNT */

	dfnip = vfstofni(dvp->v_vfsp);
	AUTOFS_DPRINT((3, "auto_lookup: dvp=%p (%s) name=%s\n",
	    (void *)dvp, dfnip->fi_map, nm));

	if (nm[0] == 0) {
		VN_HOLD(dvp);
		*vpp = dvp;
		return (0);
	}

	if (error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct))
		return (error);

	if (nm[0] == '.' && nm[1] == 0) {
		VN_HOLD(dvp);
		*vpp = dvp;
		return (0);
	}

	if (nm[0] == '.' && nm[1] == '.' && nm[2] == 0) {
		fnnode_t *pdfnp;

		pdfnp = (vntofn(dvp))->fn_parent;
		ASSERT(pdfnp != NULL);

		/*
		 * Since it is legitimate to have the VROOT flag set for the
		 * subdirectories of the indirect map in autofs filesystem,
		 * rootfnnodep is checked against fnnode of dvp instead of
		 * just checking whether VROOT flag is set in dvp
		 */

		if (pdfnp == pdfnp->fn_globals->fng_rootfnnodep) {
			vnode_t *vp;

			vfs_rlock_wait(dvp->v_vfsp);
			if (dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED) {
				vfs_unlock(dvp->v_vfsp);
				return (EIO);
			}
			vp = dvp->v_vfsp->vfs_vnodecovered;
			VN_HOLD(vp);
			vfs_unlock(dvp->v_vfsp);
			error = VOP_LOOKUP(vp, nm, vpp, pnp, flags, rdir, cred,
			    ct, direntflags, realpnp);
			VN_RELE(vp);
			return (error);
		} else {
			*vpp = fntovn(pdfnp);
			VN_HOLD(*vpp);
			return (0);
		}
	}

top:
	dfnp = vntofn(dvp);
	searchnm = nm;
	operation = 0;

	ASSERT(vn_matchops(dvp, auto_vnodeops));

	AUTOFS_DPRINT((3, "auto_lookup: dvp=%p dfnp=%p\n", (void *)dvp,
	    (void *)dfnp));

	/*
	 * If a lookup or mount of this node is in progress, wait for it
	 * to finish, and return whatever result it got.
	 */
	mutex_enter(&dfnp->fn_lock);
	if (dfnp->fn_flags & (MF_LOOKUP | MF_INPROG)) {
		mutex_exit(&dfnp->fn_lock);
		error = auto_wait4mount(dfnp);
		if (error == AUTOFS_SHUTDOWN)
			error = ENOENT;
		if (error == EAGAIN)
			goto top;
		if (error)
			return (error);
	} else
		mutex_exit(&dfnp->fn_lock);


	error = vn_vfsrlock_wait(dvp);
	if (error)
		return (error);
	vfsp = vn_mountedvfs(dvp);
	if (vfsp != NULL) {
		error = VFS_ROOT(vfsp, &newvp);
		vn_vfsunlock(dvp);
		if (!error) {
			error = VOP_LOOKUP(newvp, nm, vpp, pnp,
			    flags, rdir, cred, ct, direntflags, realpnp);
			VN_RELE(newvp);
		}
		return (error);
	}
	vn_vfsunlock(dvp);

	rw_enter(&dfnp->fn_rwlock, RW_READER);
	error = auto_search(dfnp, nm, &fnp, cred);
	if (error) {
		if (dfnip->fi_flags & MF_DIRECT) {
			/*
			 * direct map.
			 */
			if (dfnp->fn_dirents) {
				/*
				 * Mount previously triggered.
				 * 'nm' not found
				 */
				error = ENOENT;
			} else {
				/*
				 * I need to contact the daemon to trigger
				 * the mount. 'dfnp' will be the mountpoint.
				 */
				operation = AUTOFS_MOUNT;
				VN_HOLD(fntovn(dfnp));
				fnp = dfnp;
				error = 0;
			}
		} else if (dvp == dfnip->fi_rootvp) {
			/*
			 * 'dfnp' is the root of the indirect AUTOFS.
			 */
			if (rw_tryupgrade(&dfnp->fn_rwlock) == 0) {
				/*
				 * Could not acquire writer lock, release
				 * reader, and wait until available. We
				 * need to search for 'nm' again, since we
				 * had to release the lock before reacquiring
				 * it.
				 */
				rw_exit(&dfnp->fn_rwlock);
				rw_enter(&dfnp->fn_rwlock, RW_WRITER);
				error = auto_search(dfnp, nm, &fnp, cred);
			}

			ASSERT(RW_WRITE_HELD(&dfnp->fn_rwlock));
			if (error) {
				/*
				 * create node being looked-up and request
				 * mount on it.
				 */
				error = auto_enter(dfnp, nm, &fnp, kcred);
				if (!error)
					operation = AUTOFS_LOOKUP;
			}
		} else if ((dfnp->fn_dirents == NULL) &&
		    ((dvp->v_flag & VROOT) == 0) &&
		    ((fntovn(dfnp->fn_parent))->v_flag & VROOT)) {
			/*
			 * dfnp is the actual 'mountpoint' of indirect map,
			 * it is the equivalent of a direct mount,
			 * ie, /home/'user1'
			 */
			operation = AUTOFS_MOUNT;
			VN_HOLD(fntovn(dfnp));
			fnp = dfnp;
			error = 0;
			searchnm = dfnp->fn_name;
		}
	}

	if (error == EAGAIN) {
		rw_exit(&dfnp->fn_rwlock);
		goto top;
	}
	if (error) {
		rw_exit(&dfnp->fn_rwlock);
		return (error);
	}

	/*
	 * We now have the actual fnnode we're interested in.
	 * The 'MF_LOOKUP' indicates another thread is currently
	 * performing a daemon lookup of this node, therefore we
	 * wait for its completion.
	 * The 'MF_INPROG' indicates another thread is currently
	 * performing a daemon mount of this node, we wait for it
	 * to be done if we are performing a MOUNT. We don't
	 * wait for it if we are performing a LOOKUP.
	 * We can release the reader/writer lock as soon as we acquire
	 * the mutex, since the state of the lock can only change by
	 * first acquiring the mutex.
	 */
	mutex_enter(&fnp->fn_lock);
	rw_exit(&dfnp->fn_rwlock);
	if ((fnp->fn_flags & MF_LOOKUP) ||
	    ((operation == AUTOFS_MOUNT) && (fnp->fn_flags & MF_INPROG))) {
		mutex_exit(&fnp->fn_lock);
		error = auto_wait4mount(fnp);
		VN_RELE(fntovn(fnp));
		if (error == AUTOFS_SHUTDOWN)
			error = ENOENT;
		if (error && error != EAGAIN)
			return (error);
		goto top;
	}

	if (operation == 0) {
		/*
		 * got the fnnode, check for any errors
		 * on the previous operation on that node.
		 */
		error = fnp->fn_error;
		if ((error == EINTR) || (error == EAGAIN)) {
			/*
			 * previous operation on this node was
			 * not completed, do a lookup now.
			 */
			operation = AUTOFS_LOOKUP;
		} else {
			/*
			 * previous operation completed. Return
			 * a pointer to the node only if there was
			 * no error.
			 */
			mutex_exit(&fnp->fn_lock);
			if (!error)
				*vpp = fntovn(fnp);
			else
				VN_RELE(fntovn(fnp));
			return (error);
		}
	}

	/*
	 * Since I got to this point, it means I'm the one
	 * responsible for triggering the mount/look-up of this node.
	 */
	switch (operation) {
	case AUTOFS_LOOKUP:
		AUTOFS_BLOCK_OTHERS(fnp, MF_LOOKUP);
		fnp->fn_error = 0;
		mutex_exit(&fnp->fn_lock);
		error = auto_lookup_aux(fnp, searchnm, cred);
		if (!error) {
			/*
			 * Return this vnode
			 */
			*vpp = fntovn(fnp);
		} else {
			/*
			 * release our reference to this vnode
			 * and return error
			 */
			VN_RELE(fntovn(fnp));
		}
		break;
	case AUTOFS_MOUNT:
		AUTOFS_BLOCK_OTHERS(fnp, MF_INPROG);
		fnp->fn_error = 0;
		mutex_exit(&fnp->fn_lock);
		/*
		 * auto_new_mount_thread fires up a new thread which
		 * calls automountd finishing up the work
		 */
		auto_new_mount_thread(fnp, searchnm, cred);

		/*
		 * At this point, we are simply another thread
		 * waiting for the mount to complete
		 */
		error = auto_wait4mount(fnp);
		if (error == AUTOFS_SHUTDOWN)
			error = ENOENT;

		/*
		 * now release our reference to this vnode
		 */
		VN_RELE(fntovn(fnp));
		if (!error)
			goto top;
		break;
	default:
		auto_log(dfnp->fn_globals->fng_verbose,
		    dfnp->fn_globals->fng_zoneid, CE_WARN,
		    "auto_lookup: unknown operation %d",
		    operation);
	}

	AUTOFS_DPRINT((5, "auto_lookup: name=%s *vpp=%p return=%d\n",
	    nm, (void *)*vpp, error));

	return (error);
}
Exemplo n.º 23
0
static int
unionfs_lookup(void *v)
{
	struct vop_lookup_args *ap = v;
	int		iswhiteout;
	int		lockflag;
	int		error , uerror, lerror;
	u_long		nameiop;
	u_long		cnflags, cnflagsbk;
	struct unionfs_node *dunp;
	struct vnode   *dvp, *udvp, *ldvp, *vp, *uvp, *lvp, *dtmpvp;
	struct vattr	va;
	struct componentname *cnp;

	iswhiteout = 0;
	lockflag = 0;
	error = uerror = lerror = ENOENT;
	cnp = ap->a_cnp;
	nameiop = cnp->cn_nameiop;
	cnflags = cnp->cn_flags;
	dvp = ap->a_dvp;
	dunp = VTOUNIONFS(dvp);
	udvp = dunp->un_uppervp;
	ldvp = dunp->un_lowervp;
	vp = uvp = lvp = NULLVP;
	*(ap->a_vpp) = NULLVP;

	UNIONFS_INTERNAL_DEBUG("unionfs_lookup: enter: nameiop=%ld, flags=%lx, path=%s\n", nameiop, cnflags, cnp->cn_nameptr);

	if (dvp->v_type != VDIR)
		return (ENOTDIR);

	/*
	 * If read-only and op is not LOOKUP, will return EROFS.
	 */
	if ((cnflags & ISLASTCN) &&
	    (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
	    LOOKUP != nameiop)
		return (EROFS);

	/*
	 * lookup dotdot
	 */
	if (cnflags & ISDOTDOT) {
		if (LOOKUP != nameiop && udvp == NULLVP)
			return (EROFS);

		if (udvp != NULLVP) {
			dtmpvp = udvp;
			if (ldvp != NULLVP)
				VOP_UNLOCK(ldvp);
		}
		else
			dtmpvp = ldvp;

		error = VOP_LOOKUP(dtmpvp, &vp, cnp);

		if (dtmpvp == udvp && ldvp != NULLVP) {
			VOP_UNLOCK(udvp);
			vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
		}

		if (error == 0) {
			/*
			 * Exchange lock and reference from vp to
			 * dunp->un_dvp. vp is upper/lower vnode, but it
			 * will need to return the unionfs vnode.
			 */
			if (nameiop == DELETE  || nameiop == RENAME)
				VOP_UNLOCK(vp);
			vrele(vp);

			VOP_UNLOCK(dvp);
			*(ap->a_vpp) = dunp->un_dvp;
			vref(dunp->un_dvp);

			vn_lock(dunp->un_dvp, LK_EXCLUSIVE | LK_RETRY);
			vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
		} else if (error == ENOENT && nameiop != CREATE)
			cache_enter(dvp, NULLVP, cnp->cn_nameptr,
				    cnp->cn_namelen, cnp->cn_flags);

		UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", error);

		return (error);
	}

	/*
	 * lookup upper layer
	 */
	if (udvp != NULLVP) {
		uerror = VOP_LOOKUP(udvp, &uvp, cnp);

		if (uerror == 0) {
			if (udvp == uvp) {	/* is dot */
				vrele(uvp);
				*(ap->a_vpp) = dvp;
				vref(dvp);

				UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", uerror);

				return (uerror);
			}
		}

		/* check whiteout */
		if (uerror == ENOENT || uerror == EJUSTRETURN)
			if (cnp->cn_flags & ISWHITEOUT)
				iswhiteout = 1;	/* don't lookup lower */
		if (iswhiteout == 0 && ldvp != NULLVP)
			if (VOP_GETATTR(udvp, &va, cnp->cn_cred) == 0 &&
			    (va.va_flags & OPAQUE))
				iswhiteout = 1;	/* don't lookup lower */
#if 0
		UNIONFS_INTERNAL_DEBUG("unionfs_lookup: debug: whiteout=%d, path=%s\n", iswhiteout, cnp->cn_nameptr);
#endif
	}

	/*
	 * lookup lower layer
	 */
	if (ldvp != NULLVP && !(cnflags & DOWHITEOUT) && iswhiteout == 0) {
		/* always op is LOOKUP */
		cnp->cn_nameiop = LOOKUP;
		cnflagsbk = cnp->cn_flags;
		cnp->cn_flags = cnflags;

		lerror = VOP_LOOKUP(ldvp, &lvp, cnp);

		cnp->cn_nameiop = nameiop;
		if (udvp != NULLVP && (uerror == 0 || uerror == EJUSTRETURN))
			cnp->cn_flags = cnflagsbk;

		if (lerror == 0) {
			if (ldvp == lvp) {	/* is dot */
				if (uvp != NULLVP)
					vrele(uvp);	/* no need? */
				vrele(lvp);
				*(ap->a_vpp) = dvp;
				vref(dvp);

				UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", lerror);
				if (uvp != NULL)
					VOP_UNLOCK(uvp);
				return (lerror);
			}
		}
	}

	/*
	 * check lookup result
	 */
	if (uvp == NULLVP && lvp == NULLVP) {
		UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n",
		    (udvp != NULLVP ? uerror : lerror));
		return (udvp != NULLVP ? uerror : lerror);
	}

	/*
	 * check vnode type
	 */
	if (uvp != NULLVP && lvp != NULLVP && uvp->v_type != lvp->v_type) {
		vput(lvp);
		lvp = NULLVP;
	}

	/*
	 * check shadow dir
	 */
	if (uerror != 0 && uerror != EJUSTRETURN && udvp != NULLVP &&
	    lerror == 0 && lvp != NULLVP && lvp->v_type == VDIR &&
	    !(dvp->v_mount->mnt_flag & MNT_RDONLY) &&
	    (1 < cnp->cn_namelen || '.' != *(cnp->cn_nameptr))) {
		/* get unionfs vnode in order to create a new shadow dir. */
		error = unionfs_nodeget(dvp->v_mount, NULLVP, lvp, dvp, &vp,
		    cnp);
		if (error != 0)
			goto unionfs_lookup_out;
		error = unionfs_mkshadowdir(MOUNTTOUNIONFSMOUNT(dvp->v_mount),
		    udvp, VTOUNIONFS(vp), cnp);
		if (error != 0) {
			UNIONFSDEBUG("unionfs_lookup: Unable to create shadow dir.");
			vput(vp);
			goto unionfs_lookup_out;
		}
	}
	/*
	 * get unionfs vnode.
	 */
	else {
		if (uvp != NULLVP)
			error = uerror;
		else
			error = lerror;
		if (error != 0)
			goto unionfs_lookup_out;
		error = unionfs_nodeget(dvp->v_mount, uvp, lvp, dvp, &vp, cnp);
		if (error != 0) {
			UNIONFSDEBUG("unionfs_lookup: Unable to create unionfs vnode.");
			goto unionfs_lookup_out;
		}
	}

	*(ap->a_vpp) = vp;

	cache_enter(dvp, vp, cnp->cn_nameptr, cnp->cn_namelen,
		    cnp->cn_flags);

	/* XXXAD lock status on error */
unionfs_lookup_out:
	if (uvp != NULLVP)
		vrele(uvp);
	if (lvp != NULLVP)
		vrele(lvp);

	if (error == ENOENT && nameiop != CREATE)
		cache_enter(dvp, NULLVP, cnp->cn_nameptr, cnp->cn_namelen,
			    cnp->cn_flags);

	UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", error);

	return (error);
}
Exemplo n.º 24
0
static int
copen(int startfd, char *fname, int filemode, int createmode)
{
	struct pathname pn;
	vnode_t *vp, *sdvp;
	file_t *fp, *startfp;
	enum vtype type;
	int error;
	int fd, dupfd;
	vnode_t *startvp;
	proc_t *p = curproc;

	if (startfd == AT_FDCWD) {
		/*
		 * Regular open()
		 */
		startvp = NULL;
	} else {
		/*
		 * We're here via openat()
		 */
		char startchar;

		if (copyin(fname, &startchar, sizeof (char)))
			return (set_errno(EFAULT));

		/*
		 * if startchar is / then startfd is ignored
		 */
		if (startchar == '/')
			startvp = NULL;
		else {
			if ((startfp = getf(startfd)) == NULL)
				return (set_errno(EBADF));
			startvp = startfp->f_vnode;
			VN_HOLD(startvp);
			releasef(startfd);
		}
	}

	if (filemode & FXATTR) {

		/*
		 * Make sure we have a valid request.
		 * We must either have a real fd or AT_FDCWD
		 */

		if (startfd != AT_FDCWD && startvp == NULL) {
			error = EINVAL;
			goto out;
		}

		if (error = pn_get(fname, UIO_USERSPACE, &pn)) {
			goto out;
		}

		if (startfd == AT_FDCWD) {
			mutex_enter(&p->p_lock);
			startvp = PTOU(p)->u_cdir;
			VN_HOLD(startvp);
			mutex_exit(&p->p_lock);
		}

		/*
		 * Verify permission to put attributes on file
		 */

		if ((VOP_ACCESS(startvp, VREAD, 0, CRED()) != 0) &&
		    (VOP_ACCESS(startvp, VWRITE, 0, CRED()) != 0) &&
		    (VOP_ACCESS(startvp, VEXEC, 0, CRED()) != 0)) {
			error = EACCES;
			pn_free(&pn);
			goto out;
		}

		if ((startvp->v_vfsp->vfs_flag & VFS_XATTR) != 0) {
			error = VOP_LOOKUP(startvp, "", &sdvp, &pn,
			    LOOKUP_XATTR|CREATE_XATTR_DIR, rootvp, CRED());
		} else {
			error = EINVAL;
		}
		pn_free(&pn);
		if (error != 0)
			goto out;

		VN_RELE(startvp);
		startvp = sdvp;
	}

	if ((filemode & (FREAD|FWRITE)) != 0) {
		if ((filemode & (FNONBLOCK|FNDELAY)) == (FNONBLOCK|FNDELAY))
			filemode &= ~FNDELAY;
		error = falloc((vnode_t *)NULL, filemode, &fp, &fd);
		if (error == 0) {
#ifdef C2_AUDIT
			if (audit_active)
				audit_setfsat_path(1);
#endif /* C2_AUDIT */
			/*
			 * Last arg is a don't-care term if
			 * !(filemode & FCREAT).
			 */
			error = vn_openat(fname, UIO_USERSPACE, filemode,
			    (int)(createmode & MODEMASK), &vp, CRCREAT,
			    u.u_cmask, startvp);

			if (startvp != NULL)
				VN_RELE(startvp);
			if (error == 0) {
#ifdef C2_AUDIT
				if (audit_active)
					audit_copen(fd, fp, vp);
#endif /* C2_AUDIT */
				if ((vp->v_flag & VDUP) == 0) {
					fp->f_vnode = vp;
					mutex_exit(&fp->f_tlock);
					/*
					 * We must now fill in the slot
					 * falloc reserved.
					 */
					setf(fd, fp);
					return (fd);
				} else {
					/*
					 * Special handling for /dev/fd.
					 * Give up the file pointer
					 * and dup the indicated file descriptor
					 * (in v_rdev). This is ugly, but I've
					 * seen worse.
					 */
					unfalloc(fp);
					dupfd = getminor(vp->v_rdev);
					type = vp->v_type;
					mutex_enter(&vp->v_lock);
					vp->v_flag &= ~VDUP;
					mutex_exit(&vp->v_lock);
					VN_RELE(vp);
					if (type != VCHR)
						return (set_errno(EINVAL));
					if ((fp = getf(dupfd)) == NULL) {
						setf(fd, NULL);
						return (set_errno(EBADF));
					}
					mutex_enter(&fp->f_tlock);
					fp->f_count++;
					mutex_exit(&fp->f_tlock);
					setf(fd, fp);
					releasef(dupfd);
				}
				return (fd);
			} else {
				setf(fd, NULL);
				unfalloc(fp);
				return (set_errno(error));
			}
		}
	} else {
		error = EINVAL;
	}
out:
	if (startvp != NULL)
		VN_RELE(startvp);
	return (set_errno(error));
}
Exemplo n.º 25
0
/*
 * Get the "real" XATTR directory associtated with the GFS XATTR directory.
 * Note: This does NOT take any additional hold on the returned real_vp,
 * because when this lookup succeeds we save the result in xattr_realvp
 * and keep that hold until the GFS XATTR directory goes inactive.
 */
static int
xattr_dir_realdir(vnode_t *gfs_dvp, vnode_t **ret_vpp, int flags,
    cred_t *cr, caller_context_t *ct)
{
	struct pathname pn;
	char *nm = "";
	xattr_dir_t *xattr_dir;
	vnode_t *realvp;
	int error;

	*ret_vpp = NULL;

	/*
	 * Usually, we've already found the underlying XATTR directory
	 * during some previous lookup and stored it in xattr_realvp.
	 */
	mutex_enter(&gfs_dvp->v_lock);
	xattr_dir = gfs_dvp->v_data;
	realvp = xattr_dir->xattr_realvp;
	mutex_exit(&gfs_dvp->v_lock);
	if (realvp != NULL) {
		*ret_vpp = realvp;
		return (0);
	}

	/*
	 * Lookup the XATTR dir in the underlying FS, relative to our
	 * "parent", which is the real object for which this GFS XATTR
	 * directory was created.  Set the LOOKUP_HAVE_SYSATTR_DIR flag
	 * so that we don't get into an infinite loop with fop_lookup
	 * calling back to xattr_dir_lookup.
	 */
	error = pn_get(nm, UIO_SYSSPACE, &pn);
	if (error != 0)
		return (error);
	error = VOP_LOOKUP(gfs_file_parent(gfs_dvp), nm, &realvp, &pn,
	    flags | LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, ct, NULL, NULL);
	pn_free(&pn);
	if (error != 0)
		return (error);

	/*
	 * Have the real XATTR directory.  Save it -- but first
	 * check whether we lost a race doing the lookup.
	 */
	mutex_enter(&gfs_dvp->v_lock);
	xattr_dir = gfs_dvp->v_data;
	if (xattr_dir->xattr_realvp == NULL) {
		/*
		 * Note that the hold taken by the VOP_LOOKUP above is
		 * retained from here until xattr_dir_inactive.
		 */
		xattr_dir->xattr_realvp = realvp;
	} else {
		/* We lost the race. */
		VN_RELE(realvp);
		realvp = xattr_dir->xattr_realvp;
	}
	mutex_exit(&gfs_dvp->v_lock);

	*ret_vpp = realvp;
	return (0);
}
Exemplo n.º 26
0
int
xattr_dir_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
{
	int error;
	vnode_t *pvp, *dvp;
	xattr_fid_t *xfidp;
	struct pathname pn;
	char *nm;
	uint16_t orig_len;

	*vpp = NULL;

	if (fidp->fid_len < XATTR_FIDSZ)
		return (EINVAL);

	xfidp = (xattr_fid_t *)fidp;
	orig_len = fidp->fid_len;
	fidp->fid_len = xfidp->parent_len;

	error = VFS_VGET(vfsp, &pvp, fidp);
	fidp->fid_len = orig_len;
	if (error)
		return (error);

	/*
	 * Start by getting the GFS sysattr directory.	We might need
	 * to recreate it during the VOP_LOOKUP.
	 */
	nm = "";
	error = pn_get(nm, UIO_SYSSPACE, &pn);
	if (error) {
		VN_RELE(pvp);
		return (EINVAL);
	}

	error = VOP_LOOKUP(pvp, nm, &dvp, &pn, LOOKUP_XATTR|CREATE_XATTR_DIR,
	    rootvp, CRED(), NULL, NULL, NULL);
	pn_free(&pn);
	VN_RELE(pvp);
	if (error)
		return (error);

	if (xfidp->dir_offset == 0) {
		/*
		 * If we were looking for the directory, we're done.
		 */
		*vpp = dvp;
		return (0);
	}

	if (xfidp->dir_offset > XATTRDIR_NENTS) {
		VN_RELE(dvp);
		return (EINVAL);
	}

	nm = xattr_dirents[xfidp->dir_offset - 1].gfse_name;

	error = pn_get(nm, UIO_SYSSPACE, &pn);
	if (error) {
		VN_RELE(dvp);
		return (EINVAL);
	}

	error = VOP_LOOKUP(dvp, nm, vpp, &pn, 0, rootvp, CRED(), NULL,
	    NULL, NULL);

	pn_free(&pn);
	VN_RELE(dvp);

	return (error);
}
Exemplo n.º 27
0
/*
 * Get the XATTR dir for some file or directory.
 * See vnode.c: fop_lookup()
 *
 * Note this only gets the GFS XATTR directory.  We'll get the
 * real XATTR directory later, in xattr_dir_realdir.
 */
int
xattr_dir_lookup(vnode_t *dvp, vnode_t **vpp, int flags, cred_t *cr)
{
	int error = 0;

	*vpp = NULL;

	if (dvp->v_type != VDIR && dvp->v_type != VREG)
		return (EINVAL);

	mutex_enter(&dvp->v_lock);

	/*
	 * If we're already in sysattr space, don't allow creation
	 * of another level of sysattrs.
	 */
	if (dvp->v_flag & V_SYSATTR) {
		mutex_exit(&dvp->v_lock);
		return (EINVAL);
	}

	if (dvp->v_xattrdir != NULL) {
		*vpp = dvp->v_xattrdir;
		VN_HOLD(*vpp);
	} else {
		ulong_t val;
		int xattrs_allowed = dvp->v_vfsp->vfs_flag & VFS_XATTR;
		int sysattrs_allowed = 1;

		/*
		 * We have to drop the lock on dvp.  gfs_dir_create will
		 * grab it for a VN_HOLD.
		 */
		mutex_exit(&dvp->v_lock);

		/*
		 * If dvp allows xattr creation, but not sysattr
		 * creation, return the real xattr dir vp. We can't
		 * use the vfs feature mask here because _PC_SATTR_ENABLED
		 * has vnode-level granularity (e.g. .zfs).
		 */
		error = VOP_PATHCONF(dvp, _PC_SATTR_ENABLED, &val, cr, NULL);
		if (error != 0 || val == 0)
			sysattrs_allowed = 0;

		if (!xattrs_allowed && !sysattrs_allowed)
			return (EINVAL);

		if (!sysattrs_allowed) {
			struct pathname pn;
			char *nm = "";

			error = pn_get(nm, UIO_SYSSPACE, &pn);
			if (error)
				return (error);
			error = VOP_LOOKUP(dvp, nm, vpp, &pn,
			    flags|LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, NULL,
			    NULL, NULL);
			pn_free(&pn);
			return (error);
		}

		/*
		 * Note that we act as if we were given CREATE_XATTR_DIR,
		 * but only for creation of the GFS directory.
		 */
		*vpp = gfs_dir_create(
		    sizeof (xattr_dir_t), dvp, xattr_dir_ops, xattr_dirents,
		    xattrdir_do_ino, MAXNAMELEN, NULL, xattr_lookup_cb);
		mutex_enter(&dvp->v_lock);
		if (dvp->v_xattrdir != NULL) {
			/*
			 * We lost the race to create the xattr dir.
			 * Destroy this one, use the winner.  We can't
			 * just call VN_RELE(*vpp), because the vnode
			 * is only partially initialized.
			 */
			gfs_dir_t *dp = (*vpp)->v_data;

			ASSERT((*vpp)->v_count == 1);
			vn_free(*vpp);

			mutex_destroy(&dp->gfsd_lock);
			kmem_free(dp->gfsd_static,
			    dp->gfsd_nstatic * sizeof (gfs_dirent_t));
			kmem_free(dp, dp->gfsd_file.gfs_size);

			/*
			 * There is an implied VN_HOLD(dvp) here.  We should
			 * be doing a VN_RELE(dvp) to clean up the reference
			 * from *vpp, and then a VN_HOLD(dvp) for the new
			 * reference.  Instead, we just leave the count alone.
			 */

			*vpp = dvp->v_xattrdir;
			VN_HOLD(*vpp);
		} else {
			(*vpp)->v_flag |= (V_XATTRDIR|V_SYSATTR);
			dvp->v_xattrdir = *vpp;
		}
	}
	mutex_exit(&dvp->v_lock);

	return (error);
}
Exemplo n.º 28
0
/*
 * Search a pathname.
 * This is a very central and rather complicated routine.
 *
 * The pathname is pointed to by ni_cnd.cn_nameptr and is of length
 * ni_pathlen.  The starting directory is taken from ni_startdir. The
 * pathname is descended until done, or a symbolic link is encountered.
 * If the path is completed the flag ISLASTCN is set in ni_cnd.cn_flags.
 * If a symbolic link need interpretation is encountered, the flag ISSYMLINK
 * is set in ni_cnd.cn_flags.
 *
 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
 * whether the name is to be looked up, created, renamed, or deleted.
 * When CREATE, RENAME, or DELETE is specified, information usable in
 * creating, renaming, or deleting a directory entry may be calculated.
 * If flag has LOCKPARENT or'ed into it, the parent directory is returned
 * locked. If flag has WANTPARENT or'ed into it, the parent directory is
 * returned unlocked. Otherwise the parent directory is not returned. If
 * the target of the pathname exists and LOCKLEAF is or'ed into the flag
 * the target is returned locked, otherwise it is returned unlocked.
 * When creating or renaming and LOCKPARENT is specified, the target may not
 * be ".".  When deleting and LOCKPARENT is specified, the target may be ".".
 *
 * Overall outline of lookup:
 *
 * dirloop:
 *	identify next component of name at ndp->ni_ptr
 *	handle degenerate case where name is null string
 *	if .. and crossing mount points and on mounted filesys, find parent
 *	call VOP_LOOKUP routine for next component name
 *	    directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
 *	    component vnode returned in ni_vp (if it exists), locked.
 *	if result vnode is mounted on and crossing mount points,
 *	    find mounted on vnode
 *	if more components of name, do next level at dirloop
 *	return the answer in ni_vp, locked if LOCKLEAF set
 *	    if LOCKPARENT set, return locked parent in ni_dvp
 *	    if WANTPARENT set, return unlocked parent in ni_dvp
 */
int
lookup(struct nameidata *ndp)
{
	char *cp;			/* pointer into pathname argument */
	struct vnode *dp = 0;		/* the directory we are searching */
	struct vnode *tdp;		/* saved dp */
	struct mount *mp;		/* mount table entry */
	int docache;			/* == 0 do not cache last component */
	int wantparent;			/* 1 => wantparent or lockparent flag */
	int rdonly;			/* lookup read-only flag bit */
	int error = 0;
	int dpunlocked = 0;		/* dp has already been unlocked */
	int slashes;
	struct componentname *cnp = &ndp->ni_cnd;
	struct proc *p = cnp->cn_proc;
	/*
	 * Setup: break out flag bits into variables.
	 */
	wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
	docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
	if (cnp->cn_nameiop == DELETE ||
	    (wantparent && cnp->cn_nameiop != CREATE))
		docache = 0;
	rdonly = cnp->cn_flags & RDONLY;
	ndp->ni_dvp = NULL;
	cnp->cn_flags &= ~ISSYMLINK;
	dp = ndp->ni_startdir;
	ndp->ni_startdir = NULLVP;
	vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);

	/*
	 * If we have a leading string of slashes, remove them, and just make
	 * sure the current node is a directory.
	 */
	cp = cnp->cn_nameptr;
	if (*cp == '/') {
		do {
			cp++;
		} while (*cp == '/');
		ndp->ni_pathlen -= cp - cnp->cn_nameptr;
		cnp->cn_nameptr = cp;

		if (dp->v_type != VDIR) {
			error = ENOTDIR;
			goto bad;
		}

		/*
		 * If we've exhausted the path name, then just return the
		 * current node.  If the caller requested the parent node (i.e.
		 * it's a CREATE, DELETE, or RENAME), and we don't have one
		 * (because this is the root directory), then we must fail.
		 */
		if (cnp->cn_nameptr[0] == '\0') {
			if (ndp->ni_dvp == NULL && wantparent) {
				error = EISDIR;
				goto bad;
			}
			ndp->ni_vp = dp;
			cnp->cn_flags |= ISLASTCN;
			goto terminal;
		}
	}

dirloop:
	/*
	 * Search a new directory.
	 *
	 * The cn_hash value is for use by vfs_cache.
	 * The last component of the filename is left accessible via
	 * cnp->cn_nameptr for callers that need the name. Callers needing
	 * the name set the SAVENAME flag. When done, they assume
	 * responsibility for freeing the pathname buffer.
	 */
	cp = NULL;
	cnp->cn_consume = 0;
	cnp->cn_hash = hash32_stre(cnp->cn_nameptr, '/', &cp, HASHINIT);
	cnp->cn_namelen = cp - cnp->cn_nameptr;
	if (cnp->cn_namelen > NAME_MAX) {
		error = ENAMETOOLONG;
		goto bad;
	}
#ifdef NAMEI_DIAGNOSTIC
	{ char c = *cp;
	*cp = '\0';
	printf("{%s}: ", cnp->cn_nameptr);
	*cp = c; }
#endif
	ndp->ni_pathlen -= cnp->cn_namelen;
	ndp->ni_next = cp;
	/*
	 * If this component is followed by a slash, then move the pointer to
	 * the next component forward, and remember that this component must be
	 * a directory.
	 */
	if (*cp == '/') {
		do {
			cp++;
		} while (*cp == '/');
		slashes = cp - ndp->ni_next;
		ndp->ni_pathlen -= slashes;
		ndp->ni_next = cp;
		cnp->cn_flags |= REQUIREDIR;
	} else {
		slashes = 0;
		cnp->cn_flags &= ~REQUIREDIR;
	}
	/*
	 * We do special processing on the last component, whether or not it's
	 * a directory.  Cache all intervening lookups, but not the final one.
	 */
	if (*cp == '\0') {
		if (docache)
			cnp->cn_flags |= MAKEENTRY;
		else
			cnp->cn_flags &= ~MAKEENTRY;
		cnp->cn_flags |= ISLASTCN;
	} else {
		cnp->cn_flags |= MAKEENTRY;
		cnp->cn_flags &= ~ISLASTCN;
	}
	if (cnp->cn_namelen == 2 &&
	    cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
		cnp->cn_flags |= ISDOTDOT;
	else
		cnp->cn_flags &= ~ISDOTDOT;

	/*
	 * Handle "..": two special cases.
	 * 1. If at root directory (e.g. after chroot)
	 *    or at absolute root directory
	 *    then ignore it so can't get out.
	 * 2. If this vnode is the root of a mounted
	 *    filesystem, then replace it with the
	 *    vnode which was mounted on so we take the
	 *    .. in the other file system.
	 */
	if (cnp->cn_flags & ISDOTDOT) {
		for (;;) {
			if (dp == ndp->ni_rootdir || dp == rootvnode) {
				ndp->ni_dvp = dp;
				ndp->ni_vp = dp;
				vref(dp);
				goto nextname;
			}
			if ((dp->v_flag & VROOT) == 0 ||
			    (cnp->cn_flags & NOCROSSMOUNT))
				break;
			tdp = dp;
			dp = dp->v_mount->mnt_vnodecovered;
			vput(tdp);
			vref(dp);
			vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
		}
	}

	/*
	 * We now have a segment name to search for, and a directory to search.
	 */
	ndp->ni_dvp = dp;
	ndp->ni_vp = NULL;
	cnp->cn_flags &= ~PDIRUNLOCK;

	if ((error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) != 0) {
#ifdef DIAGNOSTIC
		if (ndp->ni_vp != NULL)
			panic("leaf should be empty");
#endif
#ifdef NAMEI_DIAGNOSTIC
		printf("not found\n");
#endif
		if (error != EJUSTRETURN)
			goto bad;
		/*
		 * If this was not the last component, or there were trailing
		 * slashes, then the name must exist.
		 */
		if (cnp->cn_flags & REQUIREDIR) {
			error = ENOENT;
			goto bad;
		}
		/*
		 * If creating and at end of pathname, then can consider
		 * allowing file to be created.
		 */
		if (rdonly || (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY)) {
			error = EROFS;
			goto bad;
		}
		/*
		 * We return with ni_vp NULL to indicate that the entry
		 * doesn't currently exist, leaving a pointer to the
		 * (possibly locked) directory inode in ndp->ni_dvp.
		 */
		if (cnp->cn_flags & SAVESTART) {
			ndp->ni_startdir = ndp->ni_dvp;
			vref(ndp->ni_startdir);
		}
		return (0);
	}
#ifdef NAMEI_DIAGNOSTIC
	printf("found\n");
#endif

	/*
	 * Take into account any additional components consumed by the
	 * underlying filesystem.  This will include any trailing slashes after
	 * the last component consumed.
	 */
	if (cnp->cn_consume > 0) {
		if (cnp->cn_consume >= slashes) {
			cnp->cn_flags &= ~REQUIREDIR;
		}

		ndp->ni_pathlen -= cnp->cn_consume - slashes;
		ndp->ni_next += cnp->cn_consume - slashes;
		cnp->cn_consume = 0;
		if (ndp->ni_next[0] == '\0')
			cnp->cn_flags |= ISLASTCN;
	}

	dp = ndp->ni_vp;
	/*
	 * Check to see if the vnode has been mounted on;
	 * if so find the root of the mounted file system.
	 */
	while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
	    (cnp->cn_flags & NOCROSSMOUNT) == 0) {
		if (vfs_busy(mp, VB_READ|VB_WAIT))
			continue;
		VOP_UNLOCK(dp, 0, p);
		error = VFS_ROOT(mp, &tdp);
		vfs_unbusy(mp);
		if (error) {
			dpunlocked = 1;
			goto bad2;
		}
		vrele(dp);
		ndp->ni_vp = dp = tdp;
	}

	/*
	 * Check for symbolic link.  Back up over any slashes that we skipped,
	 * as we will need them again.
	 */
	if ((dp->v_type == VLNK) && (cnp->cn_flags & (FOLLOW|REQUIREDIR))) {
		ndp->ni_pathlen += slashes;
		ndp->ni_next -= slashes;
		cnp->cn_flags |= ISSYMLINK;
		return (0);
	}

	/*
	 * Check for directory, if the component was followed by a series of
	 * slashes.
	 */
	if ((dp->v_type != VDIR) && (cnp->cn_flags & REQUIREDIR)) {
		error = ENOTDIR;
		goto bad2;
	}

nextname:
	/*
	 * Not a symbolic link.  If this was not the last component, then
	 * continue at the next component, else return.
	 */
	if (!(cnp->cn_flags & ISLASTCN)) {
		cnp->cn_nameptr = ndp->ni_next;
		vrele(ndp->ni_dvp);
		goto dirloop;
	}

terminal:
	/*
	 * Check for read-only file systems.
	 */
	if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
		/*
		 * Disallow directory write attempts on read-only
		 * file systems.
		 */
		if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
		    (wantparent &&
		    (ndp->ni_dvp->v_mount->mnt_flag & MNT_RDONLY))) {
			error = EROFS;
			goto bad2;
		}
	}
	if (ndp->ni_dvp != NULL) {
		if (cnp->cn_flags & SAVESTART) {
			ndp->ni_startdir = ndp->ni_dvp;
			vref(ndp->ni_startdir);
		}
		if (!wantparent)
			vrele(ndp->ni_dvp);
	}
	if ((cnp->cn_flags & LOCKLEAF) == 0)
		VOP_UNLOCK(dp, 0, p);
	return (0);

bad2:
	if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN) &&
	    ((cnp->cn_flags & PDIRUNLOCK) == 0))
		VOP_UNLOCK(ndp->ni_dvp, 0, p);
	vrele(ndp->ni_dvp);
bad:
	if (dpunlocked)
		vrele(dp);
	else
		vput(dp);
	ndp->ni_vp = NULL;
	return (error);
}
Exemplo n.º 29
0
/*
 * Reacquire a path name component.
 */
int
relookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp)
{
	struct proc *p = cnp->cn_proc;
	struct vnode *dp = 0;		/* the directory we are searching */
	int wantparent;			/* 1 => wantparent or lockparent flag */
	int rdonly;			/* lookup read-only flag bit */
	int error = 0;
#ifdef NAMEI_DIAGNOSTIC
	u_int32_t newhash;		/* DEBUG: check name hash */
	char *cp;			/* DEBUG: check name ptr/len */
#endif

	/*
	 * Setup: break out flag bits into variables.
	 */
	wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
	rdonly = cnp->cn_flags & RDONLY;
	cnp->cn_flags &= ~ISSYMLINK;
	dp = dvp;
	vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);

/* dirloop: */
	/*
	 * Search a new directory.
	 *
	 * The cn_hash value is for use by vfs_cache.
	 * The last component of the filename is left accessible via
	 * cnp->cn_nameptr for callers that need the name. Callers needing
	 * the name set the SAVENAME flag. When done, they assume
	 * responsibility for freeing the pathname buffer.
	 */
#ifdef NAMEI_DIAGNOSTIC
	cp = NULL;
	newhash = hash32_stre(cnp->cn_nameptr, '/', &cp, HASHINIT);
	if (newhash != cnp->cn_hash)
		panic("relookup: bad hash");
	if (cnp->cn_namelen != cp - cnp->cn_nameptr)
		panic ("relookup: bad len");
	if (*cp != 0)
		panic("relookup: not last component");
	printf("{%s}: ", cnp->cn_nameptr);
#endif

	/*
	 * Check for degenerate name (e.g. / or "")
	 * which is a way of talking about a directory,
	 * e.g. like "/." or ".".
	 */
	if (cnp->cn_nameptr[0] == '\0')
		panic("relookup: null name");

	if (cnp->cn_flags & ISDOTDOT)
		panic ("relookup: lookup on dot-dot");

	/*
	 * We now have a segment name to search for, and a directory to search.
	 */
	if ((error = VOP_LOOKUP(dp, vpp, cnp)) != 0) {
#ifdef DIAGNOSTIC
		if (*vpp != NULL)
			panic("leaf should be empty");
#endif
		if (error != EJUSTRETURN)
			goto bad;
		/*
		 * If creating and at end of pathname, then can consider
		 * allowing file to be created.
		 */
		if (rdonly || (dvp->v_mount->mnt_flag & MNT_RDONLY)) {
			error = EROFS;
			goto bad;
		}
		/* ASSERT(dvp == ndp->ni_startdir) */
		if (cnp->cn_flags & SAVESTART)
			vref(dvp);
		/*
		 * We return with ni_vp NULL to indicate that the entry
		 * doesn't currently exist, leaving a pointer to the
		 * (possibly locked) directory inode in ndp->ni_dvp.
		 */
		return (0);
	}
	dp = *vpp;

#ifdef DIAGNOSTIC
	/*
	 * Check for symbolic link
	 */
	if (dp->v_type == VLNK && (cnp->cn_flags & FOLLOW))
		panic ("relookup: symlink found.");
#endif

	/*
	 * Check for read-only file systems.
	 */
	if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) {
		/*
		 * Disallow directory write attempts on read-only
		 * file systems.
		 */
		if (rdonly || (dp->v_mount->mnt_flag & MNT_RDONLY) ||
		    (wantparent &&
		    (dvp->v_mount->mnt_flag & MNT_RDONLY))) {
			error = EROFS;
			goto bad2;
		}
	}
	/* ASSERT(dvp == ndp->ni_startdir) */
	if (cnp->cn_flags & SAVESTART)
		vref(dvp);
	if (!wantparent)
		vrele(dvp);
	if ((cnp->cn_flags & LOCKLEAF) == 0)
		VOP_UNLOCK(dp, 0, p);
	return (0);

bad2:
	if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))
		VOP_UNLOCK(dvp, 0, p);
	vrele(dvp);
bad:
	vput(dp);
	*vpp = NULL;
	return (error);
}
Exemplo n.º 30
0
/* Find parent vnode of *lvpp, return in *uvpp */
int
vfs_getcwd_scandir(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
                   char *bufp, struct proc *p)
{
    int eofflag, tries, dirbuflen, len, reclen, error = 0;
    off_t off;
    struct uio uio;
    struct iovec iov;
    char *dirbuf = NULL;
    ino_t fileno;
    struct vattr va;
    struct vnode *uvp = NULL;
    struct vnode *lvp = *lvpp;
    struct componentname cn;

    tries = 0;

    /*
     * If we want the filename, get some info we need while the
     * current directory is still locked.
     */
    if (bufp != NULL) {
        error = VOP_GETATTR(lvp, &va, p->p_ucred, p);
        if (error) {
            vput(lvp);
            *lvpp = NULL;
            *uvpp = NULL;
            return (error);
        }
    }

    cn.cn_nameiop = LOOKUP;
    cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
    cn.cn_proc = p;
    cn.cn_cred = p->p_ucred;
    cn.cn_pnbuf = NULL;
    cn.cn_nameptr = "..";
    cn.cn_namelen = 2;
    cn.cn_consume = 0;

    /* Get parent vnode using lookup of '..' */
    error = VOP_LOOKUP(lvp, uvpp, &cn);
    if (error) {
        vput(lvp);
        *lvpp = NULL;
        *uvpp = NULL;
        return (error);
    }

    uvp = *uvpp;

    /* If we don't care about the pathname, we're done */
    if (bufp == NULL) {
        vrele(lvp);
        *lvpp = NULL;
        return (0);
    }

    fileno = va.va_fileid;

    dirbuflen = DIRBLKSIZ;

    if (dirbuflen < va.va_blocksize)
        dirbuflen = va.va_blocksize;

    dirbuf = malloc(dirbuflen, M_TEMP, M_WAITOK);

    off = 0;

    do {
        char   *cpos;
        struct dirent *dp;

        iov.iov_base = dirbuf;
        iov.iov_len = dirbuflen;

        uio.uio_iov = &iov;
        uio.uio_iovcnt = 1;
        uio.uio_offset = off;
        uio.uio_resid = dirbuflen;
        uio.uio_segflg = UIO_SYSSPACE;
        uio.uio_rw = UIO_READ;
        uio.uio_procp = p;

        eofflag = 0;

        /* Call VOP_READDIR of parent */
        error = VOP_READDIR(uvp, &uio, p->p_ucred, &eofflag);

        off = uio.uio_offset;

        /* Try again if NFS tosses its cookies */
        if (error == EINVAL && tries < 3) {
            tries++;
            off = 0;
            continue;
        } else if (error) {
            goto out; /* Old userland getcwd() behaviour */
        }

        cpos = dirbuf;
        tries = 0;

        /* Scan directory page looking for matching vnode */
        for (len = (dirbuflen - uio.uio_resid); len > 0;
                len -= reclen) {
            dp = (struct dirent *)cpos;
            reclen = dp->d_reclen;

            /* Check for malformed directory */
            if (reclen < DIRENT_RECSIZE(1)) {
                error = EINVAL;
                goto out;
            }

            if (dp->d_fileno == fileno) {
                char *bp = *bpp;
                bp -= dp->d_namlen;

                if (bp <= bufp) {
                    error = ERANGE;
                    goto out;
                }

                memmove(bp, dp->d_name, dp->d_namlen);
                error = 0;
                *bpp = bp;

                goto out;
            }

            cpos += reclen;
        }

    } while (!eofflag);

    error = ENOENT;

out:

    vrele(lvp);
    *lvpp = NULL;

    free(dirbuf, M_TEMP);

    return (error);
}