2011-01-02 08:09:30 +03:00
|
|
|
/* $NetBSD: vfs_lookup.c,v 1.130 2011/01/02 05:09:31 dholland Exp $ */
|
1994-06-29 10:29:24 +04:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
1994-06-08 15:28:29 +04:00
|
|
|
* Copyright (c) 1982, 1986, 1989, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
1994-05-17 08:21:49 +04:00
|
|
|
* (c) UNIX System Laboratories, Inc.
|
|
|
|
* All or some portions of this file are derived from material licensed
|
|
|
|
* to the University of California by American Telephone and Telegraph
|
|
|
|
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
|
|
|
|
* the permission of UNIX System Laboratories, Inc.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
2003-08-07 20:26:28 +04:00
|
|
|
* 3. Neither the name of the University nor the names of its contributors
|
1994-05-17 08:21:49 +04:00
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
1998-03-01 05:20:01 +03:00
|
|
|
* @(#)vfs_lookup.c 8.10 (Berkeley) 5/27/95
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
|
|
|
|
2001-11-12 18:25:01 +03:00
|
|
|
#include <sys/cdefs.h>
|
2011-01-02 08:09:30 +03:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: vfs_lookup.c,v 1.130 2011/01/02 05:09:31 dholland Exp $");
|
2001-11-12 18:25:01 +03:00
|
|
|
|
2006-02-12 04:32:06 +03:00
|
|
|
#include "opt_magiclinks.h"
|
1998-06-26 01:17:15 +04:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
#include <sys/param.h>
|
1995-03-08 04:20:50 +03:00
|
|
|
#include <sys/systm.h>
|
2005-06-23 04:30:28 +04:00
|
|
|
#include <sys/kernel.h>
|
1994-05-17 08:21:49 +04:00
|
|
|
#include <sys/syslimits.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/namei.h>
|
|
|
|
#include <sys/vnode.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/errno.h>
|
|
|
|
#include <sys/filedesc.h>
|
2001-12-08 07:09:59 +03:00
|
|
|
#include <sys/hash.h>
|
1994-05-17 08:21:49 +04:00
|
|
|
#include <sys/proc.h>
|
2002-06-21 06:19:12 +04:00
|
|
|
#include <sys/syslog.h>
|
2006-05-15 01:15:11 +04:00
|
|
|
#include <sys/kauth.h>
|
1994-05-17 08:21:49 +04:00
|
|
|
#include <sys/ktrace.h>
|
2007-08-15 16:07:23 +04:00
|
|
|
|
2006-02-12 04:32:06 +03:00
|
|
|
#ifndef MAGICLINKS
|
|
|
|
#define MAGICLINKS 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int vfs_magiclinks = MAGICLINKS;
|
|
|
|
|
2007-11-07 03:23:13 +03:00
|
|
|
pool_cache_t pnbuf_cache; /* pathname buffer cache */
|
2000-08-04 00:41:05 +04:00
|
|
|
|
2005-06-23 04:30:28 +04:00
|
|
|
/*
|
|
|
|
* Substitute replacement text for 'magic' strings in symlinks.
|
|
|
|
* Returns 0 if successful, and returns non-zero if an error
|
|
|
|
* occurs. (Currently, the only possible error is running out
|
|
|
|
* of temporary pathname space.)
|
|
|
|
*
|
|
|
|
* Looks for "@<string>" and "@<string>/", where <string> is a
|
|
|
|
* recognized 'magic' string. Replaces the "@<string>" with the
|
|
|
|
* appropriate replacement text. (Note that in some cases the
|
|
|
|
* replacement text may have zero length.)
|
|
|
|
*
|
|
|
|
* This would have been table driven, but the variance in
|
|
|
|
* replacement strings (and replacement string lengths) made
|
|
|
|
* that impractical.
|
|
|
|
*/
|
2005-07-06 22:53:00 +04:00
|
|
|
#define VNL(x) \
|
|
|
|
(sizeof(x) - 1)
|
|
|
|
|
|
|
|
#define VO '{'
|
|
|
|
#define VC '}'
|
|
|
|
|
2005-06-23 04:30:28 +04:00
|
|
|
#define MATCH(str) \
|
2005-07-06 22:53:00 +04:00
|
|
|
((termchar == '/' && i + VNL(str) == *len) || \
|
|
|
|
(i + VNL(str) < *len && \
|
|
|
|
cp[i + VNL(str)] == termchar)) && \
|
|
|
|
!strncmp((str), &cp[i], VNL(str))
|
2005-06-23 04:30:28 +04:00
|
|
|
|
|
|
|
#define SUBSTITUTE(m, s, sl) \
|
2009-06-26 19:49:03 +04:00
|
|
|
if ((newlen + (sl)) >= MAXPATHLEN) \
|
|
|
|
return 1; \
|
2005-07-06 22:53:00 +04:00
|
|
|
i += VNL(m); \
|
|
|
|
if (termchar != '/') \
|
|
|
|
i++; \
|
2009-06-26 19:49:03 +04:00
|
|
|
(void)memcpy(&tmp[newlen], (s), (sl)); \
|
2005-07-06 22:53:00 +04:00
|
|
|
newlen += (sl); \
|
|
|
|
change = 1; \
|
|
|
|
termchar = '/';
|
2005-06-23 04:30:28 +04:00
|
|
|
|
|
|
|
static int
|
2009-06-26 19:49:03 +04:00
|
|
|
symlink_magic(struct proc *p, char *cp, size_t *len)
|
2005-06-23 04:30:28 +04:00
|
|
|
{
|
2006-02-04 15:09:50 +03:00
|
|
|
char *tmp;
|
2009-06-26 19:49:03 +04:00
|
|
|
size_t change, i, newlen, slen;
|
|
|
|
char termchar = '/';
|
|
|
|
char idtmp[11]; /* enough for 32 bit *unsigned* integer */
|
2007-12-05 01:09:01 +03:00
|
|
|
|
2005-06-23 04:30:28 +04:00
|
|
|
|
2006-02-04 15:09:50 +03:00
|
|
|
tmp = PNBUF_GET();
|
2005-06-23 04:30:28 +04:00
|
|
|
for (change = i = newlen = 0; i < *len; ) {
|
2005-07-06 22:53:00 +04:00
|
|
|
if (cp[i] != '@') {
|
2005-06-23 04:30:28 +04:00
|
|
|
tmp[newlen++] = cp[i++];
|
2005-07-06 22:53:00 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
/* Check for @{var} syntax. */
|
|
|
|
if (cp[i] == VO) {
|
|
|
|
termchar = VC;
|
2005-06-23 04:30:28 +04:00
|
|
|
i++;
|
2005-07-06 22:53:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The following checks should be ordered according
|
|
|
|
* to frequency of use.
|
|
|
|
*/
|
|
|
|
if (MATCH("machine_arch")) {
|
2009-06-26 19:49:03 +04:00
|
|
|
slen = VNL(MACHINE_ARCH);
|
|
|
|
SUBSTITUTE("machine_arch", MACHINE_ARCH, slen);
|
2005-07-06 22:53:00 +04:00
|
|
|
} else if (MATCH("machine")) {
|
2009-06-26 19:49:03 +04:00
|
|
|
slen = VNL(MACHINE);
|
|
|
|
SUBSTITUTE("machine", MACHINE, slen);
|
2005-07-06 22:53:00 +04:00
|
|
|
} else if (MATCH("hostname")) {
|
2009-06-26 19:49:03 +04:00
|
|
|
SUBSTITUTE("hostname", hostname, hostnamelen);
|
2005-07-06 22:53:00 +04:00
|
|
|
} else if (MATCH("osrelease")) {
|
2009-06-26 19:49:03 +04:00
|
|
|
slen = strlen(osrelease);
|
|
|
|
SUBSTITUTE("osrelease", osrelease, slen);
|
2005-07-06 22:53:00 +04:00
|
|
|
} else if (MATCH("emul")) {
|
2009-06-26 19:49:03 +04:00
|
|
|
slen = strlen(p->p_emul->e_name);
|
|
|
|
SUBSTITUTE("emul", p->p_emul->e_name, slen);
|
2005-07-06 22:53:00 +04:00
|
|
|
} else if (MATCH("kernel_ident")) {
|
2009-06-26 19:49:03 +04:00
|
|
|
slen = strlen(kernel_ident);
|
|
|
|
SUBSTITUTE("kernel_ident", kernel_ident, slen);
|
2005-07-06 22:53:00 +04:00
|
|
|
} else if (MATCH("domainname")) {
|
2009-06-26 19:49:03 +04:00
|
|
|
SUBSTITUTE("domainname", domainname, domainnamelen);
|
2005-07-06 22:53:00 +04:00
|
|
|
} else if (MATCH("ostype")) {
|
2009-06-26 19:49:03 +04:00
|
|
|
slen = strlen(ostype);
|
|
|
|
SUBSTITUTE("ostype", ostype, slen);
|
2006-11-04 13:14:00 +03:00
|
|
|
} else if (MATCH("uid")) {
|
2009-06-26 19:49:03 +04:00
|
|
|
slen = snprintf(idtmp, sizeof(idtmp), "%u",
|
2006-11-04 13:14:00 +03:00
|
|
|
kauth_cred_geteuid(kauth_cred_get()));
|
2009-06-26 19:49:03 +04:00
|
|
|
SUBSTITUTE("uid", idtmp, slen);
|
2007-12-05 01:09:01 +03:00
|
|
|
} else if (MATCH("ruid")) {
|
2009-06-26 19:49:03 +04:00
|
|
|
slen = snprintf(idtmp, sizeof(idtmp), "%u",
|
2007-12-05 01:09:01 +03:00
|
|
|
kauth_cred_getuid(kauth_cred_get()));
|
2009-06-26 19:49:03 +04:00
|
|
|
SUBSTITUTE("ruid", idtmp, slen);
|
|
|
|
} else if (MATCH("gid")) {
|
|
|
|
slen = snprintf(idtmp, sizeof(idtmp), "%u",
|
|
|
|
kauth_cred_getegid(kauth_cred_get()));
|
|
|
|
SUBSTITUTE("gid", idtmp, slen);
|
|
|
|
} else if (MATCH("rgid")) {
|
|
|
|
slen = snprintf(idtmp, sizeof(idtmp), "%u",
|
|
|
|
kauth_cred_getgid(kauth_cred_get()));
|
|
|
|
SUBSTITUTE("rgid", idtmp, slen);
|
2005-07-06 22:53:00 +04:00
|
|
|
} else {
|
|
|
|
tmp[newlen++] = '@';
|
|
|
|
if (termchar == VC)
|
|
|
|
tmp[newlen++] = VO;
|
2005-06-23 04:30:28 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-04 15:09:50 +03:00
|
|
|
if (change) {
|
2009-06-26 19:49:03 +04:00
|
|
|
(void)memcpy(cp, tmp, newlen);
|
2006-02-04 15:09:50 +03:00
|
|
|
*len = newlen;
|
|
|
|
}
|
|
|
|
PNBUF_PUT(tmp);
|
2005-06-23 04:30:28 +04:00
|
|
|
|
2009-06-26 19:49:03 +04:00
|
|
|
return 0;
|
2005-06-23 04:30:28 +04:00
|
|
|
}
|
|
|
|
|
2005-07-06 22:53:00 +04:00
|
|
|
#undef VNL
|
|
|
|
#undef VO
|
|
|
|
#undef VC
|
|
|
|
#undef MATCH
|
|
|
|
#undef SUBSTITUTE
|
|
|
|
|
2010-11-19 09:44:33 +03:00
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sealed abstraction for pathnames.
|
|
|
|
*
|
|
|
|
* System-call-layer level code that is going to call namei should
|
|
|
|
* first create a pathbuf and adjust all the bells and whistles on it
|
|
|
|
* as needed by context
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct pathbuf {
|
|
|
|
char *pb_path;
|
|
|
|
char *pb_pathcopy;
|
|
|
|
unsigned pb_pathcopyuses;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct pathbuf *
|
|
|
|
pathbuf_create_raw(void)
|
|
|
|
{
|
|
|
|
struct pathbuf *pb;
|
|
|
|
|
|
|
|
pb = kmem_alloc(sizeof(*pb), KM_SLEEP);
|
|
|
|
if (pb == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
pb->pb_path = PNBUF_GET();
|
|
|
|
if (pb->pb_path == NULL) {
|
|
|
|
kmem_free(pb, sizeof(*pb));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
pb->pb_pathcopy = NULL;
|
|
|
|
pb->pb_pathcopyuses = 0;
|
|
|
|
return pb;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
pathbuf_destroy(struct pathbuf *pb)
|
|
|
|
{
|
|
|
|
KASSERT(pb->pb_pathcopyuses == 0);
|
|
|
|
KASSERT(pb->pb_pathcopy == NULL);
|
|
|
|
PNBUF_PUT(pb->pb_path);
|
|
|
|
kmem_free(pb, sizeof(*pb));
|
|
|
|
}
|
|
|
|
|
2010-11-30 13:29:57 +03:00
|
|
|
struct pathbuf *
|
|
|
|
pathbuf_assimilate(char *pnbuf)
|
|
|
|
{
|
|
|
|
struct pathbuf *pb;
|
|
|
|
|
|
|
|
pb = kmem_alloc(sizeof(*pb), KM_SLEEP);
|
|
|
|
if (pb == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
pb->pb_path = pnbuf;
|
|
|
|
pb->pb_pathcopy = NULL;
|
|
|
|
pb->pb_pathcopyuses = 0;
|
|
|
|
return pb;
|
|
|
|
}
|
|
|
|
|
2010-11-19 09:44:33 +03:00
|
|
|
struct pathbuf *
|
|
|
|
pathbuf_create(const char *path)
|
|
|
|
{
|
|
|
|
struct pathbuf *pb;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
pb = pathbuf_create_raw();
|
|
|
|
if (pb == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
error = copystr(path, pb->pb_path, PATH_MAX, NULL);
|
|
|
|
if (error != 0) {
|
|
|
|
KASSERT(!"kernel path too long in pathbuf_create");
|
|
|
|
/* make sure it's null-terminated, just in case */
|
|
|
|
pb->pb_path[PATH_MAX-1] = '\0';
|
|
|
|
}
|
|
|
|
return pb;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
pathbuf_copyin(const char *userpath, struct pathbuf **ret)
|
|
|
|
{
|
|
|
|
struct pathbuf *pb;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
pb = pathbuf_create_raw();
|
|
|
|
if (pb == NULL) {
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
error = copyinstr(userpath, pb->pb_path, PATH_MAX, NULL);
|
|
|
|
if (error) {
|
|
|
|
pathbuf_destroy(pb);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
*ret = pb;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX should not exist
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
pathbuf_maybe_copyin(const char *path, enum uio_seg seg, struct pathbuf **ret)
|
|
|
|
{
|
|
|
|
if (seg == UIO_USERSPACE) {
|
|
|
|
return pathbuf_copyin(path, ret);
|
|
|
|
} else {
|
|
|
|
*ret = pathbuf_create(path);
|
|
|
|
if (*ret == NULL) {
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get a copy of the path buffer as it currently exists. If this is
|
|
|
|
* called after namei starts the results may be arbitrary.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
pathbuf_copystring(const struct pathbuf *pb, char *buf, size_t maxlen)
|
|
|
|
{
|
|
|
|
strlcpy(buf, pb->pb_path, maxlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* These two functions allow access to a saved copy of the original
|
|
|
|
* path string. The first copy should be gotten before namei is
|
|
|
|
* called. Each copy that is gotten should be put back.
|
|
|
|
*/
|
|
|
|
|
|
|
|
const char *
|
|
|
|
pathbuf_stringcopy_get(struct pathbuf *pb)
|
|
|
|
{
|
|
|
|
if (pb->pb_pathcopyuses == 0) {
|
|
|
|
pb->pb_pathcopy = PNBUF_GET();
|
|
|
|
strcpy(pb->pb_pathcopy, pb->pb_path);
|
|
|
|
}
|
|
|
|
pb->pb_pathcopyuses++;
|
|
|
|
return pb->pb_pathcopy;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
pathbuf_stringcopy_put(struct pathbuf *pb, const char *str)
|
|
|
|
{
|
|
|
|
KASSERT(str == pb->pb_pathcopy);
|
|
|
|
KASSERT(pb->pb_pathcopyuses > 0);
|
|
|
|
pb->pb_pathcopyuses--;
|
|
|
|
if (pb->pb_pathcopyuses == 0) {
|
|
|
|
PNBUF_PUT(pb->pb_pathcopy);
|
|
|
|
pb->pb_pathcopy = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
2006-03-03 19:15:11 +03:00
|
|
|
* Convert a pathname into a pointer to a locked vnode.
|
1994-05-17 08:21:49 +04:00
|
|
|
*
|
|
|
|
* The FOLLOW flag is set when symbolic links are to be followed
|
|
|
|
* when they occur at the end of the name translation process.
|
|
|
|
* Symbolic links are always followed for all other pathname
|
|
|
|
* components other than the last.
|
|
|
|
*
|
|
|
|
* The segflg defines whether the name is to be copied from user
|
|
|
|
* space or kernel space.
|
|
|
|
*
|
|
|
|
* Overall outline of namei:
|
|
|
|
*
|
|
|
|
* copy in name
|
|
|
|
* get starting directory
|
|
|
|
* while (!done && !error) {
|
|
|
|
* call lookup to search path.
|
|
|
|
* if symbolic link, massage name in buffer and continue
|
|
|
|
* }
|
|
|
|
*/
|
2009-08-09 07:28:35 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Internal state for a namei operation.
|
|
|
|
*/
|
|
|
|
struct namei_state {
|
|
|
|
struct nameidata *ndp;
|
|
|
|
struct componentname *cnp;
|
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
/* used by the pieces of namei */
|
|
|
|
struct vnode *namei_startdir; /* The directory namei() starts from. */
|
|
|
|
|
|
|
|
/* used by the pieces of lookup */
|
|
|
|
int lookup_alldone;
|
|
|
|
|
|
|
|
int docache; /* == 0 do not cache last component */
|
|
|
|
int rdonly; /* lookup read-only flag bit */
|
2000-03-30 13:27:11 +04:00
|
|
|
struct vnode *dp; /* the directory we are searching */
|
2009-08-09 11:27:54 +04:00
|
|
|
int slashes;
|
2009-08-09 07:28:35 +04:00
|
|
|
};
|
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
/* XXX reorder things to make this decl unnecessary */
|
2011-01-02 08:01:20 +03:00
|
|
|
static int do_lookup(struct namei_state *state, struct vnode *startdir);
|
2009-08-09 11:27:54 +04:00
|
|
|
|
|
|
|
|
2009-08-09 07:28:35 +04:00
|
|
|
/*
|
|
|
|
* Initialize the namei working state.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
namei_init(struct namei_state *state, struct nameidata *ndp)
|
|
|
|
{
|
|
|
|
state->ndp = ndp;
|
|
|
|
state->cnp = &ndp->ni_cnd;
|
2011-01-02 08:04:58 +03:00
|
|
|
KASSERT((state->cnp->cn_flags & INRELOOKUP) == 0);
|
2009-08-09 07:28:35 +04:00
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
state->namei_startdir = NULL;
|
|
|
|
|
|
|
|
state->lookup_alldone = 0;
|
|
|
|
|
|
|
|
state->docache = 0;
|
|
|
|
state->rdonly = 0;
|
2009-08-09 07:28:35 +04:00
|
|
|
state->dp = NULL;
|
2009-08-09 11:27:54 +04:00
|
|
|
state->slashes = 0;
|
2009-08-09 07:28:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clean up the working namei state, leaving things ready for return
|
|
|
|
* from namei.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
namei_cleanup(struct namei_state *state)
|
|
|
|
{
|
|
|
|
KASSERT(state->cnp == &state->ndp->ni_cnd);
|
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
//KASSERT(state->namei_startdir == NULL); // not yet
|
2009-08-09 07:28:35 +04:00
|
|
|
|
|
|
|
/* nothing for now */
|
|
|
|
(void)state;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start up namei. Early portion.
|
|
|
|
*
|
|
|
|
* This is divided from namei_start2 by the emul_retry: point.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
namei_start1(struct namei_state *state)
|
|
|
|
{
|
1994-05-17 08:21:49 +04:00
|
|
|
|
1994-06-08 15:28:29 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
2009-08-09 07:28:35 +04:00
|
|
|
if (!state->cnp->cn_cred)
|
2005-05-08 22:44:39 +04:00
|
|
|
panic("namei: bad cred/proc");
|
2009-08-09 07:28:35 +04:00
|
|
|
if (state->cnp->cn_nameiop & (~OPMASK))
|
2005-05-08 22:44:39 +04:00
|
|
|
panic("namei: nameiop contaminated with flags");
|
2009-08-09 07:28:35 +04:00
|
|
|
if (state->cnp->cn_flags & OPMASK)
|
2005-05-08 22:44:39 +04:00
|
|
|
panic("namei: flags contaminated with nameiops");
|
1994-06-08 15:28:29 +04:00
|
|
|
#endif
|
1994-05-17 08:21:49 +04:00
|
|
|
|
|
|
|
/*
|
2010-11-30 13:29:57 +03:00
|
|
|
* The buffer for name translation shall be the one inside the
|
|
|
|
* pathbuf.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
2010-11-30 13:29:57 +03:00
|
|
|
state->ndp->ni_pnbuf = state->ndp->ni_pathbuf->pb_path;
|
2009-08-09 07:28:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start up namei. Copy the path, find the root dir and cwd, establish
|
|
|
|
* the starting directory for lookup, and lock it.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
namei_start2(struct namei_state *state)
|
|
|
|
{
|
|
|
|
struct nameidata *ndp = state->ndp;
|
|
|
|
struct componentname *cnp = state->cnp;
|
|
|
|
|
|
|
|
struct cwdinfo *cwdi; /* pointer to cwd state */
|
|
|
|
struct lwp *self = curlwp; /* thread doing namei() */
|
|
|
|
|
2010-11-30 13:29:57 +03:00
|
|
|
#if 0 /* not any more */
|
2010-11-19 09:44:33 +03:00
|
|
|
/* as both buffers are size PATH_MAX this cannot overflow */
|
|
|
|
strcpy(cnp->cn_pnbuf, ndp->ni_pathbuf->pb_path);
|
2010-11-30 13:29:57 +03:00
|
|
|
#endif
|
2010-11-19 09:44:33 +03:00
|
|
|
|
|
|
|
/* length includes null terminator (was originally from copyinstr) */
|
2010-11-30 13:29:57 +03:00
|
|
|
ndp->ni_pathlen = strlen(ndp->ni_pnbuf) + 1;
|
1997-04-08 14:11:55 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* POSIX.1 requirement: "" is not a valid file name.
|
2005-02-27 00:34:55 +03:00
|
|
|
*/
|
2010-11-19 09:44:33 +03:00
|
|
|
if (ndp->ni_pathlen == 1) {
|
1994-05-17 08:21:49 +04:00
|
|
|
ndp->ni_vp = NULL;
|
2010-11-19 09:44:33 +03:00
|
|
|
return ENOENT;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
2010-11-19 09:44:33 +03:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
ndp->ni_loopcnt = 0;
|
1997-04-08 14:11:55 +04:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
2007-04-22 12:29:55 +04:00
|
|
|
* Get root directory for the translation.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
2009-08-09 07:28:35 +04:00
|
|
|
cwdi = self->l_proc->p_cwdi;
|
2007-07-10 00:51:58 +04:00
|
|
|
rw_enter(&cwdi->cwdi_lock, RW_READER);
|
2009-08-09 11:27:54 +04:00
|
|
|
state->namei_startdir = cwdi->cwdi_rdir;
|
|
|
|
if (state->namei_startdir == NULL)
|
|
|
|
state->namei_startdir = rootvnode;
|
|
|
|
ndp->ni_rootdir = state->namei_startdir;
|
2007-04-22 12:29:55 +04:00
|
|
|
|
1997-05-08 18:55:22 +04:00
|
|
|
/*
|
|
|
|
* Check if starting from root directory or current directory.
|
|
|
|
*/
|
2010-11-30 13:29:57 +03:00
|
|
|
if (ndp->ni_pnbuf[0] == '/') {
|
2007-04-22 12:29:55 +04:00
|
|
|
if (cnp->cn_flags & TRYEMULROOT) {
|
|
|
|
if (cnp->cn_flags & EMULROOTSET) {
|
2007-04-27 00:06:55 +04:00
|
|
|
/* Called from (eg) emul_find_interp() */
|
2009-08-09 11:27:54 +04:00
|
|
|
state->namei_startdir = ndp->ni_erootdir;
|
2007-04-22 12:29:55 +04:00
|
|
|
} else {
|
2007-04-27 00:58:37 +04:00
|
|
|
if (cwdi->cwdi_edir == NULL
|
2010-11-30 13:29:57 +03:00
|
|
|
|| (ndp->ni_pnbuf[1] == '.'
|
|
|
|
&& ndp->ni_pnbuf[2] == '.'
|
|
|
|
&& ndp->ni_pnbuf[3] == '/')) {
|
2007-04-27 00:58:37 +04:00
|
|
|
ndp->ni_erootdir = NULL;
|
|
|
|
} else {
|
2009-08-09 11:27:54 +04:00
|
|
|
state->namei_startdir = cwdi->cwdi_edir;
|
|
|
|
ndp->ni_erootdir = state->namei_startdir;
|
2007-04-22 12:29:55 +04:00
|
|
|
}
|
|
|
|
}
|
2008-11-15 00:57:14 +03:00
|
|
|
} else {
|
2007-04-22 12:29:55 +04:00
|
|
|
ndp->ni_erootdir = NULL;
|
2009-02-11 03:19:11 +03:00
|
|
|
if (cnp->cn_flags & NOCHROOT)
|
2009-08-09 11:27:54 +04:00
|
|
|
state->namei_startdir = ndp->ni_rootdir = rootvnode;
|
2008-11-15 00:57:14 +03:00
|
|
|
}
|
1997-05-08 18:55:22 +04:00
|
|
|
} else {
|
2009-08-09 11:27:54 +04:00
|
|
|
state->namei_startdir = cwdi->cwdi_cdir;
|
2007-04-22 12:29:55 +04:00
|
|
|
ndp->ni_erootdir = NULL;
|
1997-05-08 18:55:22 +04:00
|
|
|
}
|
2010-01-08 14:35:07 +03:00
|
|
|
vref(state->namei_startdir);
|
2007-07-10 00:51:58 +04:00
|
|
|
rw_exit(&cwdi->cwdi_lock);
|
2009-08-09 07:28:35 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ktrace it.
|
|
|
|
*/
|
2007-08-15 16:07:23 +04:00
|
|
|
if (ktrpoint(KTR_NAMEI)) {
|
2007-04-27 00:58:37 +04:00
|
|
|
if (ndp->ni_erootdir != NULL) {
|
2007-04-27 00:06:55 +04:00
|
|
|
/*
|
|
|
|
* To make any sense, the trace entry need to have the
|
|
|
|
* text of the emulation path prepended.
|
|
|
|
* Usually we can get this from the current process,
|
|
|
|
* but when called from emul_find_interp() it is only
|
|
|
|
* in the exec_package - so we get it passed in ni_next
|
|
|
|
* (this is a hack).
|
|
|
|
*/
|
|
|
|
const char *emul_path;
|
2007-04-26 20:27:32 +04:00
|
|
|
if (cnp->cn_flags & EMULROOTSET)
|
2007-04-27 00:06:55 +04:00
|
|
|
emul_path = ndp->ni_next;
|
2007-04-26 20:27:32 +04:00
|
|
|
else
|
2009-08-09 07:28:35 +04:00
|
|
|
emul_path = self->l_proc->p_emul->e_path;
|
2007-08-15 16:07:23 +04:00
|
|
|
ktrnamei2(emul_path, strlen(emul_path),
|
2010-11-30 13:29:57 +03:00
|
|
|
ndp->ni_pnbuf, ndp->ni_pathlen);
|
2007-04-26 20:27:32 +04:00
|
|
|
} else
|
2010-11-30 13:29:57 +03:00
|
|
|
ktrnamei(ndp->ni_pnbuf, ndp->ni_pathlen);
|
2007-04-26 20:27:32 +04:00
|
|
|
}
|
2007-08-15 16:07:23 +04:00
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
vn_lock(state->namei_startdir, LK_EXCLUSIVE | LK_RETRY);
|
2009-08-09 07:28:35 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Undo namei_start: unlock and release the current lookup directory,
|
|
|
|
* and discard the path buffer.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
namei_end(struct namei_state *state)
|
|
|
|
{
|
2009-08-09 11:27:54 +04:00
|
|
|
vput(state->namei_startdir);
|
2009-08-09 07:28:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for being at a symlink.
|
|
|
|
*/
|
|
|
|
static inline int
|
|
|
|
namei_atsymlink(struct namei_state *state)
|
|
|
|
{
|
|
|
|
return (state->cnp->cn_flags & ISSYMLINK) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Follow a symlink.
|
|
|
|
*/
|
|
|
|
static inline int
|
|
|
|
namei_follow(struct namei_state *state)
|
|
|
|
{
|
|
|
|
struct nameidata *ndp = state->ndp;
|
|
|
|
struct componentname *cnp = state->cnp;
|
|
|
|
|
|
|
|
struct lwp *self = curlwp; /* thread doing namei() */
|
|
|
|
struct iovec aiov; /* uio for reading symbolic links */
|
|
|
|
struct uio auio;
|
|
|
|
char *cp; /* pointer into pathname argument */
|
|
|
|
size_t linklen;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
|
|
|
|
return ELOOP;
|
|
|
|
}
|
|
|
|
if (ndp->ni_vp->v_mount->mnt_flag & MNT_SYMPERM) {
|
|
|
|
error = VOP_ACCESS(ndp->ni_vp, VEXEC, cnp->cn_cred);
|
|
|
|
if (error != 0)
|
|
|
|
return error;
|
|
|
|
}
|
2010-11-30 13:29:57 +03:00
|
|
|
|
|
|
|
/* FUTURE: fix this to not use a second buffer */
|
|
|
|
cp = PNBUF_GET();
|
2009-08-09 07:28:35 +04:00
|
|
|
aiov.iov_base = cp;
|
|
|
|
aiov.iov_len = MAXPATHLEN;
|
|
|
|
auio.uio_iov = &aiov;
|
|
|
|
auio.uio_iovcnt = 1;
|
|
|
|
auio.uio_offset = 0;
|
|
|
|
auio.uio_rw = UIO_READ;
|
|
|
|
auio.uio_resid = MAXPATHLEN;
|
|
|
|
UIO_SETUP_SYSSPACE(&auio);
|
|
|
|
error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
|
|
|
|
if (error) {
|
2010-11-30 13:29:57 +03:00
|
|
|
PNBUF_PUT(cp);
|
2009-08-09 07:28:35 +04:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
linklen = MAXPATHLEN - auio.uio_resid;
|
|
|
|
if (linklen == 0) {
|
2010-11-30 13:29:57 +03:00
|
|
|
PNBUF_PUT(cp);
|
|
|
|
return ENOENT;
|
2009-08-09 07:28:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do symlink substitution, if appropriate, and
|
|
|
|
* check length for potential overflow.
|
|
|
|
*/
|
|
|
|
if ((vfs_magiclinks &&
|
|
|
|
symlink_magic(self->l_proc, cp, &linklen)) ||
|
|
|
|
(linklen + ndp->ni_pathlen >= MAXPATHLEN)) {
|
2010-11-30 13:29:57 +03:00
|
|
|
PNBUF_PUT(cp);
|
|
|
|
return ENAMETOOLONG;
|
2009-08-09 07:28:35 +04:00
|
|
|
}
|
|
|
|
if (ndp->ni_pathlen > 1) {
|
2010-11-30 13:29:57 +03:00
|
|
|
/* includes a null-terminator */
|
2009-08-09 07:28:35 +04:00
|
|
|
memcpy(cp + linklen, ndp->ni_next, ndp->ni_pathlen);
|
2010-11-30 13:29:57 +03:00
|
|
|
} else {
|
|
|
|
cp[linklen] = '\0';
|
|
|
|
}
|
2009-08-09 07:28:35 +04:00
|
|
|
ndp->ni_pathlen += linklen;
|
2010-11-30 13:29:57 +03:00
|
|
|
memcpy(ndp->ni_pnbuf, cp, ndp->ni_pathlen);
|
|
|
|
PNBUF_PUT(cp);
|
2009-08-09 07:28:35 +04:00
|
|
|
vput(ndp->ni_vp);
|
2009-08-09 11:27:54 +04:00
|
|
|
state->namei_startdir = ndp->ni_dvp;
|
2009-08-09 07:28:35 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if root directory should replace current directory.
|
|
|
|
*/
|
2010-11-30 13:29:57 +03:00
|
|
|
if (ndp->ni_pnbuf[0] == '/') {
|
2009-08-09 11:27:54 +04:00
|
|
|
vput(state->namei_startdir);
|
2009-08-09 07:28:35 +04:00
|
|
|
/* Keep absolute symbolic links inside emulation root */
|
2009-08-09 11:27:54 +04:00
|
|
|
state->namei_startdir = ndp->ni_erootdir;
|
2010-11-30 13:29:57 +03:00
|
|
|
if (state->namei_startdir == NULL ||
|
|
|
|
(ndp->ni_pnbuf[1] == '.'
|
|
|
|
&& ndp->ni_pnbuf[2] == '.'
|
|
|
|
&& ndp->ni_pnbuf[3] == '/')) {
|
2009-08-09 07:28:35 +04:00
|
|
|
ndp->ni_erootdir = NULL;
|
2009-08-09 11:27:54 +04:00
|
|
|
state->namei_startdir = ndp->ni_rootdir;
|
2009-08-09 07:28:35 +04:00
|
|
|
}
|
2010-01-08 14:35:07 +03:00
|
|
|
vref(state->namei_startdir);
|
2009-08-09 11:27:54 +04:00
|
|
|
vn_lock(state->namei_startdir, LK_EXCLUSIVE | LK_RETRY);
|
2009-08-09 07:28:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
|
|
|
|
|
|
static int
|
|
|
|
do_namei(struct namei_state *state)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
struct nameidata *ndp = state->ndp;
|
|
|
|
struct componentname *cnp = state->cnp;
|
2010-11-30 13:29:57 +03:00
|
|
|
const char *savepath = NULL;
|
2009-08-09 07:28:35 +04:00
|
|
|
|
|
|
|
KASSERT(cnp == &ndp->ni_cnd);
|
|
|
|
|
|
|
|
namei_start1(state);
|
|
|
|
|
2010-11-30 13:29:57 +03:00
|
|
|
if (cnp->cn_flags & TRYEMULROOT) {
|
|
|
|
savepath = pathbuf_stringcopy_get(ndp->ni_pathbuf);
|
|
|
|
}
|
|
|
|
|
2009-08-09 07:28:35 +04:00
|
|
|
emul_retry:
|
|
|
|
|
2010-11-30 13:29:57 +03:00
|
|
|
if (savepath != NULL) {
|
|
|
|
/* kinda gross */
|
|
|
|
strcpy(ndp->ni_pathbuf->pb_path, savepath);
|
|
|
|
pathbuf_stringcopy_put(ndp->ni_pathbuf, savepath);
|
|
|
|
savepath = NULL;
|
|
|
|
}
|
|
|
|
|
2009-08-09 07:28:35 +04:00
|
|
|
error = namei_start2(state);
|
|
|
|
if (error) {
|
2010-11-30 13:29:57 +03:00
|
|
|
if (savepath != NULL) {
|
|
|
|
pathbuf_stringcopy_put(ndp->ni_pathbuf, savepath);
|
|
|
|
}
|
2009-08-09 07:28:35 +04:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2007-04-22 12:29:55 +04:00
|
|
|
/* Loop through symbolic links */
|
1994-05-17 08:21:49 +04:00
|
|
|
for (;;) {
|
2009-08-09 11:27:54 +04:00
|
|
|
if (state->namei_startdir->v_mount == NULL) {
|
2003-04-10 11:26:52 +04:00
|
|
|
/* Give up if the directory is no longer mounted */
|
2010-11-30 13:29:57 +03:00
|
|
|
if (savepath != NULL) {
|
|
|
|
pathbuf_stringcopy_put(ndp->ni_pathbuf, savepath);
|
|
|
|
}
|
2009-08-09 07:28:35 +04:00
|
|
|
namei_end(state);
|
2003-04-10 11:26:52 +04:00
|
|
|
return (ENOENT);
|
|
|
|
}
|
2010-11-30 13:29:57 +03:00
|
|
|
cnp->cn_nameptr = ndp->ni_pnbuf;
|
2011-01-02 08:01:20 +03:00
|
|
|
error = do_lookup(state, state->namei_startdir);
|
2006-12-09 19:11:50 +03:00
|
|
|
if (error != 0) {
|
2009-08-09 07:28:35 +04:00
|
|
|
/* XXX this should use namei_end() */
|
2006-12-09 19:11:50 +03:00
|
|
|
if (ndp->ni_dvp) {
|
|
|
|
vput(ndp->ni_dvp);
|
|
|
|
}
|
2007-04-27 00:58:37 +04:00
|
|
|
if (ndp->ni_erootdir != NULL) {
|
2007-04-22 12:29:55 +04:00
|
|
|
/* Retry the whole thing from the normal root */
|
|
|
|
cnp->cn_flags &= ~TRYEMULROOT;
|
|
|
|
goto emul_retry;
|
|
|
|
}
|
2010-11-30 13:29:57 +03:00
|
|
|
KASSERT(savepath == NULL);
|
1994-05-17 08:21:49 +04:00
|
|
|
return (error);
|
|
|
|
}
|
2006-12-09 19:11:50 +03:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
|
|
|
* Check for symbolic link
|
|
|
|
*/
|
2009-08-09 07:28:35 +04:00
|
|
|
if (namei_atsymlink(state)) {
|
|
|
|
error = namei_follow(state);
|
|
|
|
if (error) {
|
|
|
|
KASSERT(ndp->ni_dvp != ndp->ni_vp);
|
|
|
|
vput(ndp->ni_dvp);
|
|
|
|
vput(ndp->ni_vp);
|
|
|
|
ndp->ni_vp = NULL;
|
2010-11-30 13:29:57 +03:00
|
|
|
if (savepath != NULL) {
|
|
|
|
pathbuf_stringcopy_put(ndp->ni_pathbuf, savepath);
|
|
|
|
}
|
2009-08-09 07:28:35 +04:00
|
|
|
return error;
|
2009-05-04 10:05:19 +04:00
|
|
|
}
|
1997-10-31 01:47:06 +03:00
|
|
|
}
|
2009-08-09 07:28:35 +04:00
|
|
|
else {
|
1994-05-17 08:21:49 +04:00
|
|
|
break;
|
|
|
|
}
|
2009-08-09 07:28:35 +04:00
|
|
|
}
|
2006-12-09 19:11:50 +03:00
|
|
|
|
2009-08-09 07:28:35 +04:00
|
|
|
/*
|
|
|
|
* Done
|
|
|
|
*/
|
2006-12-09 19:11:50 +03:00
|
|
|
|
2009-08-09 07:28:35 +04:00
|
|
|
if ((cnp->cn_flags & LOCKPARENT) == 0 && ndp->ni_dvp) {
|
|
|
|
if (ndp->ni_dvp == ndp->ni_vp) {
|
|
|
|
vrele(ndp->ni_dvp);
|
|
|
|
} else {
|
|
|
|
vput(ndp->ni_dvp);
|
1997-05-08 18:55:22 +04:00
|
|
|
}
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
2010-11-30 13:29:57 +03:00
|
|
|
|
|
|
|
if (savepath != NULL) {
|
|
|
|
pathbuf_stringcopy_put(ndp->ni_pathbuf, savepath);
|
2009-08-09 07:28:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
namei(struct nameidata *ndp)
|
|
|
|
{
|
|
|
|
struct namei_state state;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
namei_init(&state, ndp);
|
|
|
|
error = do_namei(&state);
|
|
|
|
namei_cleanup(&state);
|
|
|
|
|
|
|
|
return error;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
|
2001-12-08 07:09:59 +03:00
|
|
|
/*
|
|
|
|
* Determine the namei hash (for cn_hash) for name.
|
|
|
|
* If *ep != NULL, hash from name to ep-1.
|
|
|
|
* If *ep == NULL, hash from name until the first NUL or '/', and
|
|
|
|
* return the location of this termination character in *ep.
|
|
|
|
*
|
|
|
|
* This function returns an equivalent hash to the MI hash32_strn().
|
|
|
|
* The latter isn't used because in the *ep == NULL case, determining
|
|
|
|
* the length of the string to the first NUL or `/' and then calling
|
|
|
|
* hash32_strn() involves unnecessary double-handling of the data.
|
|
|
|
*/
|
|
|
|
uint32_t
|
|
|
|
namei_hash(const char *name, const char **ep)
|
|
|
|
{
|
|
|
|
uint32_t hash;
|
|
|
|
|
|
|
|
hash = HASH32_STR_INIT;
|
|
|
|
if (*ep != NULL) {
|
|
|
|
for (; name < *ep; name++)
|
2005-05-30 02:24:14 +04:00
|
|
|
hash = hash * 33 + *(const uint8_t *)name;
|
2001-12-08 07:09:59 +03:00
|
|
|
} else {
|
|
|
|
for (; *name != '\0' && *name != '/'; name++)
|
2005-05-30 02:24:14 +04:00
|
|
|
hash = hash * 33 + *(const uint8_t *)name;
|
2001-12-08 07:09:59 +03:00
|
|
|
*ep = name;
|
|
|
|
}
|
|
|
|
return (hash + (hash >> 5));
|
|
|
|
}
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
|
|
|
* Search a pathname.
|
|
|
|
* This is a very central and rather complicated routine.
|
|
|
|
*
|
|
|
|
* The pathname is pointed to by ni_ptr and is of length ni_pathlen.
|
2011-01-02 08:01:20 +03:00
|
|
|
* The starting directory is passed in. The pathname is descended
|
|
|
|
* until done, or a symbolic link is encountered. The variable ni_more
|
|
|
|
* is clear if the path is completed; it is set to one if a symbolic
|
|
|
|
* link needing interpretation is encountered.
|
1994-05-17 08:21:49 +04:00
|
|
|
*
|
|
|
|
* 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
|
2007-01-08 00:33:24 +03:00
|
|
|
* locked. 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 ".".
|
2005-02-27 00:34:55 +03:00
|
|
|
*
|
1994-05-17 08:21:49 +04:00
|
|
|
* 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
|
2007-01-08 00:33:24 +03:00
|
|
|
* directory vnode returned in ni_dvp, locked.
|
1994-05-17 08:21:49 +04:00
|
|
|
* 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
|
|
|
|
*/
|
2009-08-09 11:27:54 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Begin lookup().
|
|
|
|
*/
|
|
|
|
static int
|
2011-01-02 08:01:20 +03:00
|
|
|
lookup_start(struct namei_state *state, struct vnode *startdir)
|
1994-05-17 08:21:49 +04:00
|
|
|
{
|
2000-03-30 13:27:11 +04:00
|
|
|
const char *cp; /* pointer into pathname argument */
|
2009-08-09 11:27:54 +04:00
|
|
|
|
|
|
|
struct componentname *cnp = state->cnp;
|
|
|
|
struct nameidata *ndp = state->ndp;
|
|
|
|
|
|
|
|
KASSERT(cnp == &ndp->ni_cnd);
|
|
|
|
|
|
|
|
state->lookup_alldone = 0;
|
|
|
|
state->dp = NULL;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup: break out flag bits into variables.
|
|
|
|
*/
|
2009-08-09 11:27:54 +04:00
|
|
|
state->docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
|
2007-01-07 23:43:59 +03:00
|
|
|
if (cnp->cn_nameiop == DELETE)
|
2009-08-09 11:27:54 +04:00
|
|
|
state->docache = 0;
|
|
|
|
state->rdonly = cnp->cn_flags & RDONLY;
|
1994-05-17 08:21:49 +04:00
|
|
|
ndp->ni_dvp = NULL;
|
1994-06-08 15:28:29 +04:00
|
|
|
cnp->cn_flags &= ~ISSYMLINK;
|
2011-01-02 08:01:20 +03:00
|
|
|
state->dp = startdir;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
1997-05-08 18:55:22 +04:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
if (state->dp->v_type != VDIR) {
|
|
|
|
vput(state->dp);
|
|
|
|
return ENOTDIR;
|
1997-05-08 18:55:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we've exhausted the path name, then just return the
|
2007-04-26 00:41:42 +04:00
|
|
|
* current node.
|
1997-05-08 18:55:22 +04:00
|
|
|
*/
|
|
|
|
if (cnp->cn_nameptr[0] == '\0') {
|
2009-08-09 11:27:54 +04:00
|
|
|
ndp->ni_vp = state->dp;
|
1997-05-08 18:55:22 +04:00
|
|
|
cnp->cn_flags |= ISLASTCN;
|
2009-08-09 11:27:54 +04:00
|
|
|
|
|
|
|
/* bleh */
|
|
|
|
state->lookup_alldone = 1;
|
|
|
|
return 0;
|
1997-05-08 18:55:22 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
lookup_parsepath(struct namei_state *state)
|
|
|
|
{
|
|
|
|
const char *cp; /* pointer into pathname argument */
|
|
|
|
|
|
|
|
struct componentname *cnp = state->cnp;
|
|
|
|
struct nameidata *ndp = state->ndp;
|
|
|
|
|
|
|
|
KASSERT(cnp == &ndp->ni_cnd);
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
|
|
|
* Search a new directory.
|
|
|
|
*
|
1994-06-08 15:28:29 +04:00
|
|
|
* The cn_hash value is for use by vfs_cache.
|
1994-05-17 08:21:49 +04:00
|
|
|
* The last component of the filename is left accessible via
|
1994-06-08 15:28:29 +04:00
|
|
|
* cnp->cn_nameptr for callers that need the name. Callers needing
|
1994-05-17 08:21:49 +04:00
|
|
|
* the name set the SAVENAME flag. When done, they assume
|
|
|
|
* responsibility for freeing the pathname buffer.
|
2010-12-20 03:12:46 +03:00
|
|
|
*
|
|
|
|
* At this point, our only vnode state is that "dp" is held and locked.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
1994-06-08 15:28:29 +04:00
|
|
|
cnp->cn_consume = 0;
|
2001-12-08 07:09:59 +03:00
|
|
|
cp = NULL;
|
|
|
|
cnp->cn_hash = namei_hash(cnp->cn_nameptr, &cp);
|
1994-06-08 15:28:29 +04:00
|
|
|
cnp->cn_namelen = cp - cnp->cn_nameptr;
|
|
|
|
if (cnp->cn_namelen > NAME_MAX) {
|
2010-12-20 03:12:46 +03:00
|
|
|
vput(state->dp);
|
|
|
|
ndp->ni_dvp = NULL;
|
2009-08-09 11:27:54 +04:00
|
|
|
return ENAMETOOLONG;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
#ifdef NAMEI_DIAGNOSTIC
|
|
|
|
{ char c = *cp;
|
2002-08-02 08:49:35 +04:00
|
|
|
*(char *)cp = '\0';
|
1996-10-13 06:32:29 +04:00
|
|
|
printf("{%s}: ", cnp->cn_nameptr);
|
2002-08-02 08:49:35 +04:00
|
|
|
*(char *)cp = c; }
|
2003-12-06 17:16:11 +03:00
|
|
|
#endif /* NAMEI_DIAGNOSTIC */
|
1994-06-08 15:28:29 +04:00
|
|
|
ndp->ni_pathlen -= cnp->cn_namelen;
|
1994-05-17 08:21:49 +04:00
|
|
|
ndp->ni_next = cp;
|
1997-05-08 18:55:22 +04:00
|
|
|
/*
|
|
|
|
* 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 == '/');
|
2009-08-09 11:27:54 +04:00
|
|
|
state->slashes = cp - ndp->ni_next;
|
|
|
|
ndp->ni_pathlen -= state->slashes;
|
1997-05-08 18:55:22 +04:00
|
|
|
ndp->ni_next = cp;
|
|
|
|
cnp->cn_flags |= REQUIREDIR;
|
|
|
|
} else {
|
2009-08-09 11:27:54 +04:00
|
|
|
state->slashes = 0;
|
1997-05-08 18:55:22 +04:00
|
|
|
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') {
|
2009-08-09 11:27:54 +04:00
|
|
|
if (state->docache)
|
1997-05-08 18:55:22 +04:00
|
|
|
cnp->cn_flags |= MAKEENTRY;
|
|
|
|
else
|
|
|
|
cnp->cn_flags &= ~MAKEENTRY;
|
|
|
|
cnp->cn_flags |= ISLASTCN;
|
|
|
|
} else {
|
|
|
|
cnp->cn_flags |= MAKEENTRY;
|
|
|
|
cnp->cn_flags &= ~ISLASTCN;
|
|
|
|
}
|
1994-06-08 15:28:29 +04:00
|
|
|
if (cnp->cn_namelen == 2 &&
|
|
|
|
cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
|
|
|
|
cnp->cn_flags |= ISDOTDOT;
|
|
|
|
else
|
|
|
|
cnp->cn_flags &= ~ISDOTDOT;
|
1994-05-17 08:21:49 +04:00
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
lookup_once(struct namei_state *state)
|
|
|
|
{
|
|
|
|
struct vnode *tdp; /* saved dp */
|
|
|
|
struct mount *mp; /* mount table entry */
|
|
|
|
struct lwp *l = curlwp;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
struct componentname *cnp = state->cnp;
|
|
|
|
struct nameidata *ndp = state->ndp;
|
|
|
|
|
|
|
|
KASSERT(cnp == &ndp->ni_cnd);
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
|
|
|
* Handle "..": two special cases.
|
|
|
|
* 1. If at root directory (e.g. after chroot)
|
1994-06-08 15:28:29 +04:00
|
|
|
* or at absolute root directory
|
1994-05-17 08:21:49 +04:00
|
|
|
* then ignore it so can't get out.
|
2007-04-22 12:29:55 +04:00
|
|
|
* 1a. If at the root of the emulation filesystem go to the real
|
|
|
|
* root. So "/../<path>" is always absolute.
|
|
|
|
* 1b. If we have somehow gotten out of a jail, warn
|
2002-06-21 06:19:12 +04:00
|
|
|
* and also ignore it so we can't get farther out.
|
1994-05-17 08:21:49 +04:00
|
|
|
* 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.
|
|
|
|
*/
|
1994-06-08 15:28:29 +04:00
|
|
|
if (cnp->cn_flags & ISDOTDOT) {
|
2005-12-11 15:16:03 +03:00
|
|
|
struct proc *p = l->l_proc;
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
for (;;) {
|
2009-08-09 11:27:54 +04:00
|
|
|
if (state->dp == ndp->ni_rootdir || state->dp == rootvnode) {
|
|
|
|
ndp->ni_dvp = state->dp;
|
|
|
|
ndp->ni_vp = state->dp;
|
2010-01-08 14:35:07 +03:00
|
|
|
vref(state->dp);
|
2009-08-09 11:27:54 +04:00
|
|
|
return 0;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
2002-06-21 06:19:12 +04:00
|
|
|
if (ndp->ni_rootdir != rootvnode) {
|
|
|
|
int retval;
|
2006-12-09 19:11:50 +03:00
|
|
|
|
2010-06-24 16:58:48 +04:00
|
|
|
VOP_UNLOCK(state->dp);
|
2009-08-09 11:27:54 +04:00
|
|
|
retval = vn_isunder(state->dp, ndp->ni_rootdir, l);
|
|
|
|
vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
|
2002-06-21 06:19:12 +04:00
|
|
|
if (!retval) {
|
|
|
|
/* Oops! We got out of jail! */
|
|
|
|
log(LOG_WARNING,
|
|
|
|
"chrooted pid %d uid %d (%s) "
|
|
|
|
"detected outside of its chroot\n",
|
2006-07-24 02:06:03 +04:00
|
|
|
p->p_pid, kauth_cred_geteuid(l->l_cred),
|
2005-12-11 15:16:03 +03:00
|
|
|
p->p_comm);
|
2002-06-21 06:19:12 +04:00
|
|
|
/* Put us at the jail root. */
|
2009-08-09 11:27:54 +04:00
|
|
|
vput(state->dp);
|
|
|
|
state->dp = ndp->ni_rootdir;
|
|
|
|
ndp->ni_dvp = state->dp;
|
|
|
|
ndp->ni_vp = state->dp;
|
2010-01-08 14:35:07 +03:00
|
|
|
vref(state->dp);
|
|
|
|
vref(state->dp);
|
2009-08-09 11:27:54 +04:00
|
|
|
vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
|
|
|
|
return 0;
|
2002-06-21 06:19:12 +04:00
|
|
|
}
|
|
|
|
}
|
2009-08-09 11:27:54 +04:00
|
|
|
if ((state->dp->v_vflag & VV_ROOT) == 0 ||
|
1994-06-08 15:28:29 +04:00
|
|
|
(cnp->cn_flags & NOCROSSMOUNT))
|
1994-05-17 08:21:49 +04:00
|
|
|
break;
|
2009-08-09 11:27:54 +04:00
|
|
|
tdp = state->dp;
|
|
|
|
state->dp = state->dp->v_mount->mnt_vnodecovered;
|
1994-05-17 08:21:49 +04:00
|
|
|
vput(tdp);
|
2010-01-08 14:35:07 +03:00
|
|
|
vref(state->dp);
|
2009-08-09 11:27:54 +04:00
|
|
|
vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We now have a segment name to search for, and a directory to search.
|
2006-12-09 19:11:50 +03:00
|
|
|
* Again, our only vnode state is that "dp" is held and locked.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
1994-06-08 15:28:29 +04:00
|
|
|
unionlookup:
|
2009-08-09 11:27:54 +04:00
|
|
|
ndp->ni_dvp = state->dp;
|
1998-03-01 05:20:01 +03:00
|
|
|
ndp->ni_vp = NULL;
|
2009-08-09 11:27:54 +04:00
|
|
|
error = VOP_LOOKUP(state->dp, &ndp->ni_vp, cnp);
|
2006-12-09 19:11:50 +03:00
|
|
|
if (error != 0) {
|
1994-05-17 08:21:49 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (ndp->ni_vp != NULL)
|
2003-01-21 02:57:49 +03:00
|
|
|
panic("leaf `%s' should be empty", cnp->cn_nameptr);
|
2003-12-06 17:16:11 +03:00
|
|
|
#endif /* DIAGNOSTIC */
|
1994-05-17 08:21:49 +04:00
|
|
|
#ifdef NAMEI_DIAGNOSTIC
|
1996-10-13 06:32:29 +04:00
|
|
|
printf("not found\n");
|
2003-12-06 17:16:11 +03:00
|
|
|
#endif /* NAMEI_DIAGNOSTIC */
|
1994-06-08 15:28:29 +04:00
|
|
|
if ((error == ENOENT) &&
|
2009-08-09 11:27:54 +04:00
|
|
|
(state->dp->v_vflag & VV_ROOT) &&
|
|
|
|
(state->dp->v_mount->mnt_flag & MNT_UNION)) {
|
|
|
|
tdp = state->dp;
|
|
|
|
state->dp = state->dp->v_mount->mnt_vnodecovered;
|
2006-12-09 19:11:50 +03:00
|
|
|
vput(tdp);
|
2010-01-08 14:35:07 +03:00
|
|
|
vref(state->dp);
|
2009-08-09 11:27:54 +04:00
|
|
|
vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
|
1994-06-08 15:28:29 +04:00
|
|
|
goto unionlookup;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
1994-06-08 15:28:29 +04:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
if (error != EJUSTRETURN)
|
2009-08-09 11:27:54 +04:00
|
|
|
return error;
|
2006-12-09 19:11:50 +03:00
|
|
|
|
1997-05-08 18:55:22 +04:00
|
|
|
/*
|
|
|
|
* If this was not the last component, or there were trailing
|
2003-09-11 21:33:42 +04:00
|
|
|
* slashes, and we are not going to create a directory,
|
|
|
|
* then the name must exist.
|
1997-05-08 18:55:22 +04:00
|
|
|
*/
|
2003-09-11 21:33:42 +04:00
|
|
|
if ((cnp->cn_flags & (REQUIREDIR | CREATEDIR)) == REQUIREDIR) {
|
2009-08-09 11:27:54 +04:00
|
|
|
return ENOENT;
|
1997-05-08 18:55:22 +04:00
|
|
|
}
|
2006-12-09 19:11:50 +03:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
|
|
|
* If creating and at end of pathname, then can consider
|
|
|
|
* allowing file to be created.
|
|
|
|
*/
|
2009-08-09 11:27:54 +04:00
|
|
|
if (state->rdonly) {
|
|
|
|
return EROFS;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
2006-12-09 19:11:50 +03:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
|
|
|
* We return with ni_vp NULL to indicate that the entry
|
|
|
|
* doesn't currently exist, leaving a pointer to the
|
2006-03-03 19:15:11 +03:00
|
|
|
* (possibly locked) directory vnode in ndp->ni_dvp.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
2009-08-09 11:27:54 +04:00
|
|
|
state->lookup_alldone = 1;
|
1994-05-17 08:21:49 +04:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
#ifdef NAMEI_DIAGNOSTIC
|
1996-10-13 06:32:29 +04:00
|
|
|
printf("found\n");
|
2003-12-06 17:16:11 +03:00
|
|
|
#endif /* NAMEI_DIAGNOSTIC */
|
1994-05-17 08:21:49 +04:00
|
|
|
|
1994-06-08 15:28:29 +04:00
|
|
|
/*
|
1997-05-08 18:55:22 +04:00
|
|
|
* Take into account any additional components consumed by the
|
|
|
|
* underlying filesystem. This will include any trailing slashes after
|
|
|
|
* the last component consumed.
|
1994-06-08 15:28:29 +04:00
|
|
|
*/
|
|
|
|
if (cnp->cn_consume > 0) {
|
2009-08-09 11:27:54 +04:00
|
|
|
ndp->ni_pathlen -= cnp->cn_consume - state->slashes;
|
|
|
|
ndp->ni_next += cnp->cn_consume - state->slashes;
|
1994-06-08 15:28:29 +04:00
|
|
|
cnp->cn_consume = 0;
|
1997-05-08 18:55:22 +04:00
|
|
|
if (ndp->ni_next[0] == '\0')
|
|
|
|
cnp->cn_flags |= ISLASTCN;
|
1994-06-08 15:28:29 +04:00
|
|
|
}
|
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
state->dp = ndp->ni_vp;
|
2006-12-09 19:11:50 +03:00
|
|
|
|
|
|
|
/*
|
2009-08-09 11:27:54 +04:00
|
|
|
* "state->dp" and "ndp->ni_dvp" are both locked and held,
|
2006-12-09 19:11:50 +03:00
|
|
|
* and may be the same vnode.
|
|
|
|
*/
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
|
|
|
* Check to see if the vnode has been mounted on;
|
|
|
|
* if so find the root of the mounted file system.
|
|
|
|
*/
|
2009-08-09 11:27:54 +04:00
|
|
|
while (state->dp->v_type == VDIR && (mp = state->dp->v_mountedhere) &&
|
1994-06-08 15:28:29 +04:00
|
|
|
(cnp->cn_flags & NOCROSSMOUNT) == 0) {
|
PR kern/38141 lookup/vfs_busy acquire rwlock recursively
Simplify the mount locking. Remove all the crud to deal with recursion on
the mount lock, and crud to deal with unmount as another weirdo lock.
Hopefully this will once and for all fix the deadlocks with this. With this
commit there are two locks on each mount:
- krwlock_t mnt_unmounting. This is used to prevent unmount across critical
sections like getnewvnode(). It's only ever read locked with rw_tryenter(),
and is only ever write locked in dounmount(). A write hold can't be taken
on this lock if the current LWP could hold a vnode lock.
- kmutex_t mnt_updating. This is taken by threads updating the mount, for
example when going r/o -> r/w, and is only present to serialize updates.
In order to take this lock, a read hold must first be taken on
mnt_unmounting, and the two need to be held across the operation.
One effect of this change: previously if an unmount failed, we would make a
half hearted attempt to back out of it gracefully, but that was unlikely to
work in a lot of cases. Now while an unmount that will be aborted is in
progress, new file operations within the mount will fail instead of being
delayed. That is unlikely to be a problem though, because if the admin
requests unmount of a file system then s(he) has made a decision to deny
access to the resource.
2008-05-06 22:43:44 +04:00
|
|
|
error = vfs_busy(mp, NULL);
|
2008-05-06 19:04:00 +04:00
|
|
|
if (error != 0) {
|
2009-08-09 11:27:54 +04:00
|
|
|
vput(state->dp);
|
|
|
|
return error;
|
2008-05-06 19:04:00 +04:00
|
|
|
}
|
2009-08-09 11:27:54 +04:00
|
|
|
KASSERT(ndp->ni_dvp != state->dp);
|
2010-06-24 16:58:48 +04:00
|
|
|
VOP_UNLOCK(ndp->ni_dvp);
|
2009-08-09 11:27:54 +04:00
|
|
|
vput(state->dp);
|
2003-06-29 22:43:21 +04:00
|
|
|
error = VFS_ROOT(mp, &tdp);
|
2008-04-30 16:49:16 +04:00
|
|
|
vfs_unbusy(mp, false, NULL);
|
1999-08-03 22:17:24 +04:00
|
|
|
if (error) {
|
2006-12-28 02:21:02 +03:00
|
|
|
vn_lock(ndp->ni_dvp, LK_EXCLUSIVE | LK_RETRY);
|
2009-08-09 11:27:54 +04:00
|
|
|
return error;
|
1999-08-03 22:17:24 +04:00
|
|
|
}
|
2010-12-20 03:12:46 +03:00
|
|
|
VOP_UNLOCK(tdp);
|
|
|
|
ndp->ni_vp = state->dp = tdp;
|
|
|
|
vn_lock(ndp->ni_dvp, LK_EXCLUSIVE | LK_RETRY);
|
|
|
|
vn_lock(ndp->ni_vp, LK_EXCLUSIVE | LK_RETRY);
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2011-01-02 08:01:20 +03:00
|
|
|
do_lookup(struct namei_state *state, struct vnode *startdir)
|
2009-08-09 11:27:54 +04:00
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
struct componentname *cnp = state->cnp;
|
|
|
|
struct nameidata *ndp = state->ndp;
|
|
|
|
|
|
|
|
KASSERT(cnp == &ndp->ni_cnd);
|
|
|
|
|
2011-01-02 08:01:20 +03:00
|
|
|
error = lookup_start(state, startdir);
|
2009-08-09 11:27:54 +04:00
|
|
|
if (error) {
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
// XXX: this case should not be necessary given proper handling
|
|
|
|
// of slashes elsewhere.
|
|
|
|
if (state->lookup_alldone) {
|
|
|
|
goto terminal;
|
|
|
|
}
|
|
|
|
|
|
|
|
dirloop:
|
|
|
|
error = lookup_parsepath(state);
|
|
|
|
if (error) {
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = lookup_once(state);
|
|
|
|
if (error) {
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
// XXX ought to be able to avoid this case too
|
|
|
|
if (state->lookup_alldone) {
|
|
|
|
/* this should NOT be "goto terminal;" */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1994-12-14 22:41:23 +03:00
|
|
|
/*
|
1997-05-08 18:55:22 +04:00
|
|
|
* Check for symbolic link. Back up over any slashes that we skipped,
|
|
|
|
* as we will need them again.
|
1994-12-14 22:41:23 +03:00
|
|
|
*/
|
2009-08-09 11:27:54 +04:00
|
|
|
if ((state->dp->v_type == VLNK) && (cnp->cn_flags & (FOLLOW|REQUIREDIR))) {
|
|
|
|
ndp->ni_pathlen += state->slashes;
|
|
|
|
ndp->ni_next -= state->slashes;
|
1994-12-14 22:41:23 +03:00
|
|
|
cnp->cn_flags |= ISSYMLINK;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1997-05-08 18:55:22 +04:00
|
|
|
/*
|
|
|
|
* Check for directory, if the component was followed by a series of
|
|
|
|
* slashes.
|
|
|
|
*/
|
2009-08-09 11:27:54 +04:00
|
|
|
if ((state->dp->v_type != VDIR) && (cnp->cn_flags & REQUIREDIR)) {
|
1997-05-08 18:55:22 +04:00
|
|
|
error = ENOTDIR;
|
2009-08-09 11:27:54 +04:00
|
|
|
KASSERT(state->dp != ndp->ni_dvp);
|
|
|
|
vput(state->dp);
|
2006-12-09 19:11:50 +03:00
|
|
|
goto bad;
|
1997-05-08 18:55:22 +04:00
|
|
|
}
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
1997-05-08 18:55:22 +04:00
|
|
|
* Not a symbolic link. If this was not the last component, then
|
|
|
|
* continue at the next component, else return.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
1997-05-08 18:55:22 +04:00
|
|
|
if (!(cnp->cn_flags & ISLASTCN)) {
|
1994-06-08 15:28:29 +04:00
|
|
|
cnp->cn_nameptr = ndp->ni_next;
|
2009-08-09 11:27:54 +04:00
|
|
|
if (ndp->ni_dvp == state->dp) {
|
2006-12-09 19:11:50 +03:00
|
|
|
vrele(ndp->ni_dvp);
|
|
|
|
} else {
|
|
|
|
vput(ndp->ni_dvp);
|
|
|
|
}
|
1994-05-17 08:21:49 +04:00
|
|
|
goto dirloop;
|
|
|
|
}
|
1997-05-08 18:55:22 +04:00
|
|
|
|
|
|
|
terminal:
|
2009-08-09 11:27:54 +04:00
|
|
|
if (state->dp == ndp->ni_erootdir) {
|
2007-04-26 00:41:42 +04:00
|
|
|
/*
|
|
|
|
* We are about to return the emulation root.
|
|
|
|
* This isn't a good idea because code might repeatedly
|
|
|
|
* lookup ".." until the file matches that returned
|
|
|
|
* for "/" and loop forever.
|
|
|
|
* So convert it to the real root.
|
|
|
|
*/
|
2009-08-09 11:27:54 +04:00
|
|
|
if (ndp->ni_dvp == state->dp)
|
|
|
|
vrele(state->dp);
|
2007-04-26 00:41:42 +04:00
|
|
|
else
|
|
|
|
if (ndp->ni_dvp != NULL)
|
|
|
|
vput(ndp->ni_dvp);
|
|
|
|
ndp->ni_dvp = NULL;
|
2009-08-09 11:27:54 +04:00
|
|
|
vput(state->dp);
|
|
|
|
state->dp = ndp->ni_rootdir;
|
2010-01-08 14:35:07 +03:00
|
|
|
vref(state->dp);
|
2009-08-09 11:27:54 +04:00
|
|
|
vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY);
|
|
|
|
ndp->ni_vp = state->dp;
|
2007-04-26 00:41:42 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 (ndp->ni_dvp == NULL && cnp->cn_nameiop != LOOKUP) {
|
|
|
|
switch (cnp->cn_nameiop) {
|
|
|
|
case CREATE:
|
|
|
|
error = EEXIST;
|
|
|
|
break;
|
|
|
|
case DELETE:
|
|
|
|
case RENAME:
|
|
|
|
error = EBUSY;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
KASSERT(0);
|
|
|
|
}
|
2009-08-09 11:27:54 +04:00
|
|
|
vput(state->dp);
|
2007-04-26 00:41:42 +04:00
|
|
|
goto bad;
|
|
|
|
}
|
2006-12-09 19:11:50 +03:00
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
2007-08-12 23:31:12 +04:00
|
|
|
* Disallow directory write attempts on read-only lookups.
|
2007-08-13 03:40:40 +04:00
|
|
|
* Prefers EEXIST over EROFS for the CREATE case.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
2009-08-09 11:27:54 +04:00
|
|
|
if (state->rdonly &&
|
2007-08-13 03:40:40 +04:00
|
|
|
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
|
1998-03-01 05:20:01 +03:00
|
|
|
error = EROFS;
|
2009-08-09 11:27:54 +04:00
|
|
|
if (state->dp != ndp->ni_dvp) {
|
|
|
|
vput(state->dp);
|
2006-12-09 19:11:50 +03:00
|
|
|
}
|
|
|
|
goto bad;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
2006-12-09 19:11:50 +03:00
|
|
|
if ((cnp->cn_flags & LOCKLEAF) == 0) {
|
2010-06-24 16:58:48 +04:00
|
|
|
VOP_UNLOCK(state->dp);
|
2006-12-09 19:11:50 +03:00
|
|
|
}
|
1994-05-17 08:21:49 +04:00
|
|
|
return (0);
|
|
|
|
|
|
|
|
bad:
|
|
|
|
ndp->ni_vp = NULL;
|
|
|
|
return (error);
|
|
|
|
}
|
1994-06-08 15:28:29 +04:00
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
/*
|
2009-09-27 21:19:07 +04:00
|
|
|
* Externally visible interfaces used by nfsd (bletch, yuk, XXX)
|
|
|
|
*
|
|
|
|
* The "index" version differs from the "main" version in that it's
|
|
|
|
* called from a different place in a different context. For now I
|
|
|
|
* want to be able to shuffle code in from one call site without
|
|
|
|
* affecting the other.
|
2009-08-09 11:27:54 +04:00
|
|
|
*/
|
2009-09-27 21:19:07 +04:00
|
|
|
|
|
|
|
int
|
2009-09-27 21:23:53 +04:00
|
|
|
lookup_for_nfsd(struct nameidata *ndp, struct vnode *dp, int neverfollow)
|
2009-09-27 21:19:07 +04:00
|
|
|
{
|
|
|
|
struct namei_state state;
|
|
|
|
int error;
|
|
|
|
|
2009-09-27 21:23:53 +04:00
|
|
|
struct iovec aiov;
|
|
|
|
struct uio auio;
|
|
|
|
int linklen;
|
|
|
|
char *cp;
|
|
|
|
|
2010-11-30 13:29:57 +03:00
|
|
|
ndp->ni_pnbuf = ndp->ni_pathbuf->pb_path;
|
2009-09-27 21:19:07 +04:00
|
|
|
namei_init(&state, ndp);
|
2009-09-27 21:23:53 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* BEGIN wodge of code from nfsd
|
|
|
|
*/
|
|
|
|
|
2010-01-08 14:35:07 +03:00
|
|
|
vref(dp);
|
2009-09-27 21:23:53 +04:00
|
|
|
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
2010-11-30 13:29:57 +03:00
|
|
|
state.cnp->cn_nameptr = state.ndp->ni_pnbuf;
|
2009-09-27 21:23:53 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* END wodge of code from nfsd
|
|
|
|
*/
|
|
|
|
|
2011-01-02 08:01:20 +03:00
|
|
|
error = do_lookup(&state, dp);
|
2009-09-27 21:23:53 +04:00
|
|
|
if (error) {
|
|
|
|
/* BEGIN from nfsd */
|
|
|
|
if (ndp->ni_dvp) {
|
|
|
|
vput(ndp->ni_dvp);
|
|
|
|
}
|
|
|
|
/* END from nfsd */
|
|
|
|
namei_cleanup(&state);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* BEGIN wodge of code from nfsd
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for encountering a symbolic link
|
|
|
|
*/
|
|
|
|
if ((state.cnp->cn_flags & ISSYMLINK) == 0) {
|
|
|
|
if ((state.cnp->cn_flags & LOCKPARENT) == 0 && state.ndp->ni_dvp) {
|
|
|
|
if (state.ndp->ni_dvp == state.ndp->ni_vp) {
|
|
|
|
vrele(state.ndp->ni_dvp);
|
|
|
|
} else {
|
|
|
|
vput(state.ndp->ni_dvp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
} else {
|
|
|
|
if (neverfollow) {
|
|
|
|
error = EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (state.ndp->ni_loopcnt++ >= MAXSYMLINKS) {
|
|
|
|
error = ELOOP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (state.ndp->ni_vp->v_mount->mnt_flag & MNT_SYMPERM) {
|
|
|
|
error = VOP_ACCESS(ndp->ni_vp, VEXEC, state.cnp->cn_cred);
|
|
|
|
if (error != 0)
|
|
|
|
goto out;
|
|
|
|
}
|
2010-11-30 13:29:57 +03:00
|
|
|
cp = PNBUF_GET();
|
2009-09-27 21:23:53 +04:00
|
|
|
aiov.iov_base = cp;
|
|
|
|
aiov.iov_len = MAXPATHLEN;
|
|
|
|
auio.uio_iov = &aiov;
|
|
|
|
auio.uio_iovcnt = 1;
|
|
|
|
auio.uio_offset = 0;
|
|
|
|
auio.uio_rw = UIO_READ;
|
|
|
|
auio.uio_resid = MAXPATHLEN;
|
|
|
|
UIO_SETUP_SYSSPACE(&auio);
|
|
|
|
error = VOP_READLINK(ndp->ni_vp, &auio, state.cnp->cn_cred);
|
|
|
|
if (error) {
|
2010-11-30 13:29:57 +03:00
|
|
|
PNBUF_PUT(cp);
|
2009-09-27 21:23:53 +04:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
linklen = MAXPATHLEN - auio.uio_resid;
|
|
|
|
if (linklen == 0) {
|
2010-11-30 13:29:57 +03:00
|
|
|
PNBUF_PUT(cp);
|
2009-09-27 21:23:53 +04:00
|
|
|
error = ENOENT;
|
2010-11-30 13:29:57 +03:00
|
|
|
goto out;
|
2009-09-27 21:23:53 +04:00
|
|
|
}
|
|
|
|
if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
|
2010-11-30 13:29:57 +03:00
|
|
|
PNBUF_PUT(cp);
|
2009-09-27 21:23:53 +04:00
|
|
|
error = ENAMETOOLONG;
|
2010-11-30 13:29:57 +03:00
|
|
|
goto out;
|
2009-09-27 21:23:53 +04:00
|
|
|
}
|
|
|
|
if (ndp->ni_pathlen > 1) {
|
2010-11-30 13:29:57 +03:00
|
|
|
/* includes a null-terminator */
|
2009-09-27 21:23:53 +04:00
|
|
|
memcpy(cp + linklen, ndp->ni_next, ndp->ni_pathlen);
|
2010-11-30 13:29:57 +03:00
|
|
|
} else {
|
|
|
|
cp[linklen] = '\0';
|
|
|
|
}
|
2009-09-27 21:23:53 +04:00
|
|
|
state.ndp->ni_pathlen += linklen;
|
2010-11-30 13:29:57 +03:00
|
|
|
memcpy(state.ndp->ni_pnbuf, cp, state.ndp->ni_pathlen);
|
|
|
|
PNBUF_PUT(cp);
|
2009-09-27 21:23:53 +04:00
|
|
|
vput(state.ndp->ni_vp);
|
|
|
|
dp = state.ndp->ni_dvp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if root directory should replace current directory.
|
|
|
|
*/
|
2010-11-30 13:29:57 +03:00
|
|
|
if (state.ndp->ni_pnbuf[0] == '/') {
|
2009-09-27 21:23:53 +04:00
|
|
|
vput(dp);
|
|
|
|
dp = ndp->ni_rootdir;
|
2010-01-08 14:35:07 +03:00
|
|
|
vref(dp);
|
2009-09-27 21:23:53 +04:00
|
|
|
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
vput(state.ndp->ni_vp);
|
|
|
|
vput(state.ndp->ni_dvp);
|
|
|
|
state.ndp->ni_vp = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* END wodge of code from nfsd
|
|
|
|
*/
|
2009-09-27 21:19:07 +04:00
|
|
|
namei_cleanup(&state);
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
int
|
2011-01-02 08:01:20 +03:00
|
|
|
lookup_for_nfsd_index(struct nameidata *ndp, struct vnode *startdir)
|
2009-08-09 11:27:54 +04:00
|
|
|
{
|
|
|
|
struct namei_state state;
|
|
|
|
int error;
|
|
|
|
|
2011-01-02 08:01:20 +03:00
|
|
|
vref(startdir);
|
|
|
|
|
2010-11-30 13:29:57 +03:00
|
|
|
ndp->ni_pnbuf = ndp->ni_pathbuf->pb_path;
|
|
|
|
ndp->ni_cnd.cn_nameptr = ndp->ni_pnbuf;
|
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
namei_init(&state, ndp);
|
2011-01-02 08:01:20 +03:00
|
|
|
error = do_lookup(&state, startdir);
|
2009-08-09 11:27:54 +04:00
|
|
|
namei_cleanup(&state);
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
1994-06-08 15:28:29 +04:00
|
|
|
/*
|
|
|
|
* Reacquire a path name component.
|
2006-12-09 19:11:50 +03:00
|
|
|
* dvp is locked on entry and exit.
|
|
|
|
* *vpp is locked on exit unless it's NULL.
|
1994-06-08 15:28:29 +04:00
|
|
|
*/
|
|
|
|
int
|
2011-01-02 08:09:30 +03:00
|
|
|
relookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, int dummy)
|
1994-06-08 15:28:29 +04:00
|
|
|
{
|
|
|
|
int rdonly; /* lookup read-only flag bit */
|
|
|
|
int error = 0;
|
2003-12-06 17:16:11 +03:00
|
|
|
#ifdef DEBUG
|
2007-02-04 18:03:20 +03:00
|
|
|
uint32_t newhash; /* DEBUG: check name hash */
|
2002-08-02 08:49:35 +04:00
|
|
|
const char *cp; /* DEBUG: check name ptr/len */
|
2003-12-06 17:16:11 +03:00
|
|
|
#endif /* DEBUG */
|
1994-06-08 15:28:29 +04:00
|
|
|
|
2011-01-02 08:09:30 +03:00
|
|
|
(void)dummy;
|
|
|
|
|
1994-06-08 15:28:29 +04:00
|
|
|
/*
|
|
|
|
* Setup: break out flag bits into variables.
|
|
|
|
*/
|
|
|
|
rdonly = cnp->cn_flags & RDONLY;
|
|
|
|
cnp->cn_flags &= ~ISSYMLINK;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2003-12-06 17:16:11 +03:00
|
|
|
#ifdef DEBUG
|
2001-12-08 07:09:59 +03:00
|
|
|
cp = NULL;
|
|
|
|
newhash = namei_hash(cnp->cn_nameptr, &cp);
|
2007-02-04 18:03:20 +03:00
|
|
|
if ((uint32_t)newhash != (uint32_t)cnp->cn_hash)
|
1994-06-08 15:28:29 +04:00
|
|
|
panic("relookup: bad hash");
|
|
|
|
if (cnp->cn_namelen != cp - cnp->cn_nameptr)
|
2005-05-08 22:44:39 +04:00
|
|
|
panic("relookup: bad len");
|
2003-12-06 17:17:13 +03:00
|
|
|
while (*cp == '/')
|
|
|
|
cp++;
|
1994-06-08 15:28:29 +04:00
|
|
|
if (*cp != 0)
|
|
|
|
panic("relookup: not last component");
|
2003-12-06 17:16:11 +03:00
|
|
|
#endif /* DEBUG */
|
1994-06-08 15:28:29 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for degenerate name (e.g. / or "")
|
|
|
|
* which is a way of talking about a directory,
|
|
|
|
* e.g. like "/." or ".".
|
|
|
|
*/
|
1997-05-08 18:55:22 +04:00
|
|
|
if (cnp->cn_nameptr[0] == '\0')
|
|
|
|
panic("relookup: null name");
|
1994-06-08 15:28:29 +04:00
|
|
|
|
|
|
|
if (cnp->cn_flags & ISDOTDOT)
|
2005-05-08 22:44:39 +04:00
|
|
|
panic("relookup: lookup on dot-dot");
|
1994-06-08 15:28:29 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We now have a segment name to search for, and a directory to search.
|
|
|
|
*/
|
2011-01-02 08:04:58 +03:00
|
|
|
cnp->cn_flags |= INRELOOKUP;
|
|
|
|
error = VOP_LOOKUP(dvp, vpp, cnp);
|
|
|
|
cnp->cn_flags &= ~INRELOOKUP;
|
|
|
|
if ((error) != 0) {
|
1994-06-08 15:28:29 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (*vpp != NULL)
|
2003-01-21 02:57:49 +03:00
|
|
|
panic("leaf `%s' should be empty", cnp->cn_nameptr);
|
1994-06-08 15:28:29 +04:00
|
|
|
#endif
|
|
|
|
if (error != EJUSTRETURN)
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
/*
|
|
|
|
* Check for symbolic link
|
|
|
|
*/
|
2007-02-04 18:03:20 +03:00
|
|
|
if (*vpp && (*vpp)->v_type == VLNK && (cnp->cn_flags & FOLLOW))
|
2005-05-08 22:44:39 +04:00
|
|
|
panic("relookup: symlink found");
|
1994-06-08 15:28:29 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
2007-08-12 23:31:12 +04:00
|
|
|
* Check for read-only lookups.
|
1994-06-08 15:28:29 +04:00
|
|
|
*/
|
2007-02-04 18:03:20 +03:00
|
|
|
if (rdonly && cnp->cn_nameiop != LOOKUP) {
|
1998-03-01 05:20:01 +03:00
|
|
|
error = EROFS;
|
2007-02-04 18:03:20 +03:00
|
|
|
if (*vpp) {
|
|
|
|
vput(*vpp);
|
|
|
|
}
|
2006-12-09 19:11:50 +03:00
|
|
|
goto bad;
|
1994-06-08 15:28:29 +04:00
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
bad:
|
|
|
|
*vpp = NULL;
|
|
|
|
return (error);
|
|
|
|
}
|
2009-06-29 09:00:14 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* namei_simple - simple forms of namei.
|
|
|
|
*
|
|
|
|
* These are wrappers to allow the simple case callers of namei to be
|
|
|
|
* left alone while everything else changes under them.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Flags */
|
|
|
|
struct namei_simple_flags_type {
|
|
|
|
int dummy;
|
|
|
|
};
|
|
|
|
static const struct namei_simple_flags_type ns_nn, ns_nt, ns_fn, ns_ft;
|
|
|
|
const namei_simple_flags_t NSM_NOFOLLOW_NOEMULROOT = &ns_nn;
|
|
|
|
const namei_simple_flags_t NSM_NOFOLLOW_TRYEMULROOT = &ns_nt;
|
|
|
|
const namei_simple_flags_t NSM_FOLLOW_NOEMULROOT = &ns_fn;
|
|
|
|
const namei_simple_flags_t NSM_FOLLOW_TRYEMULROOT = &ns_ft;
|
|
|
|
|
|
|
|
static
|
|
|
|
int
|
|
|
|
namei_simple_convert_flags(namei_simple_flags_t sflags)
|
|
|
|
{
|
|
|
|
if (sflags == NSM_NOFOLLOW_NOEMULROOT)
|
|
|
|
return NOFOLLOW | 0;
|
|
|
|
if (sflags == NSM_NOFOLLOW_TRYEMULROOT)
|
|
|
|
return NOFOLLOW | TRYEMULROOT;
|
|
|
|
if (sflags == NSM_FOLLOW_NOEMULROOT)
|
|
|
|
return FOLLOW | 0;
|
|
|
|
if (sflags == NSM_FOLLOW_TRYEMULROOT)
|
|
|
|
return FOLLOW | TRYEMULROOT;
|
|
|
|
panic("namei_simple_convert_flags: bogus sflags\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
namei_simple_kernel(const char *path, namei_simple_flags_t sflags,
|
|
|
|
struct vnode **vp_ret)
|
|
|
|
{
|
|
|
|
struct nameidata nd;
|
2010-11-19 09:44:33 +03:00
|
|
|
struct pathbuf *pb;
|
2009-06-29 09:00:14 +04:00
|
|
|
int err;
|
|
|
|
|
2010-11-19 09:44:33 +03:00
|
|
|
pb = pathbuf_create(path);
|
|
|
|
if (pb == NULL) {
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
|
2009-06-29 09:00:14 +04:00
|
|
|
NDINIT(&nd,
|
|
|
|
LOOKUP,
|
|
|
|
namei_simple_convert_flags(sflags),
|
2010-11-19 09:44:33 +03:00
|
|
|
pb);
|
2009-06-29 09:00:14 +04:00
|
|
|
err = namei(&nd);
|
|
|
|
if (err != 0) {
|
2010-11-19 09:44:33 +03:00
|
|
|
pathbuf_destroy(pb);
|
2009-06-29 09:00:14 +04:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
*vp_ret = nd.ni_vp;
|
2010-11-19 09:44:33 +03:00
|
|
|
pathbuf_destroy(pb);
|
2009-06-29 09:00:14 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
namei_simple_user(const char *path, namei_simple_flags_t sflags,
|
|
|
|
struct vnode **vp_ret)
|
|
|
|
{
|
2010-11-19 09:44:33 +03:00
|
|
|
struct pathbuf *pb;
|
2009-06-29 09:00:14 +04:00
|
|
|
struct nameidata nd;
|
|
|
|
int err;
|
|
|
|
|
2010-11-19 09:44:33 +03:00
|
|
|
err = pathbuf_copyin(path, &pb);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-06-29 09:00:14 +04:00
|
|
|
NDINIT(&nd,
|
|
|
|
LOOKUP,
|
|
|
|
namei_simple_convert_flags(sflags),
|
2010-11-19 09:44:33 +03:00
|
|
|
pb);
|
2009-06-29 09:00:14 +04:00
|
|
|
err = namei(&nd);
|
|
|
|
if (err != 0) {
|
2010-11-19 09:44:33 +03:00
|
|
|
pathbuf_destroy(pb);
|
2009-06-29 09:00:14 +04:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
*vp_ret = nd.ni_vp;
|
2010-11-19 09:44:33 +03:00
|
|
|
pathbuf_destroy(pb);
|
2009-06-29 09:00:14 +04:00
|
|
|
return 0;
|
|
|
|
}
|