2011-04-11 06:21:17 +04:00
|
|
|
/* $NetBSD: vfs_lookup.c,v 1.173 2011/04/11 02:21:17 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-04-11 06:21:17 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: vfs_lookup.c,v 1.173 2011/04/11 02:21:17 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;
|
|
|
|
|
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
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
|
2011-01-04 10:43:42 +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++)
|
|
|
|
hash = hash * 33 + *(const uint8_t *)name;
|
|
|
|
} else {
|
|
|
|
for (; *name != '\0' && *name != '/'; name++)
|
|
|
|
hash = hash * 33 + *(const uint8_t *)name;
|
|
|
|
*ep = name;
|
|
|
|
}
|
|
|
|
return (hash + (hash >> 5));
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-04-11 06:21:17 +04:00
|
|
|
* XXX should not exist:
|
|
|
|
* 1. whether a pointer is kernel or user should be statically checkable
|
|
|
|
* 2. copyin should be handled by the upper part of the syscall layer,
|
|
|
|
* not in here.
|
2010-11-19 09:44:33 +03:00
|
|
|
*/
|
|
|
|
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
|
|
|
/*
|
2011-04-11 06:21:17 +04:00
|
|
|
* namei: convert a pathname into a pointer to a (maybe-locked) vnode,
|
|
|
|
* and maybe also its parent directory vnode, and assorted other guff.
|
|
|
|
* See namei(9) for the interface documentation.
|
|
|
|
*
|
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
|
|
|
|
2011-04-11 06:21:17 +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.
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* 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. 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, locked.
|
|
|
|
* 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 07:28:35 +04:00
|
|
|
/*
|
|
|
|
* Internal state for a namei operation.
|
2011-04-11 06:21:17 +04:00
|
|
|
*
|
|
|
|
* cnp is always equal to &ndp->ni_cnp.
|
2009-08-09 07:28:35 +04:00
|
|
|
*/
|
|
|
|
struct namei_state {
|
|
|
|
struct nameidata *ndp;
|
|
|
|
struct componentname *cnp;
|
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
int docache; /* == 0 do not cache last component */
|
|
|
|
int rdonly; /* lookup read-only flag bit */
|
|
|
|
int slashes;
|
2011-04-11 05:36:59 +04:00
|
|
|
|
|
|
|
unsigned attempt_retry:1; /* true if error allows emul retry */
|
2009-08-09 07:28:35 +04:00
|
|
|
};
|
|
|
|
|
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->docache = 0;
|
|
|
|
state->rdonly = 0;
|
|
|
|
state->slashes = 0;
|
2011-04-11 05:33:04 +04:00
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (!state->cnp->cn_cred)
|
|
|
|
panic("namei: bad cred/proc");
|
|
|
|
if (state->cnp->cn_nameiop & (~OPMASK))
|
|
|
|
panic("namei: nameiop contaminated with flags");
|
|
|
|
if (state->cnp->cn_flags & OPMASK)
|
|
|
|
panic("namei: flags contaminated with nameiops");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The buffer for name translation shall be the one inside the
|
|
|
|
* pathbuf.
|
|
|
|
*/
|
|
|
|
state->ndp->ni_pnbuf = state->ndp->ni_pathbuf->pb_path;
|
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);
|
|
|
|
|
|
|
|
/* nothing for now */
|
|
|
|
(void)state;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
|
|
|
|
|
|
/*
|
2011-04-11 05:33:04 +04:00
|
|
|
* Get the directory context.
|
|
|
|
* Initializes the rootdir and erootdir state and returns a reference
|
|
|
|
* to the starting dir.
|
2009-08-09 07:28:35 +04:00
|
|
|
*/
|
2011-04-11 05:33:04 +04:00
|
|
|
static struct vnode *
|
|
|
|
namei_getstartdir(struct namei_state *state)
|
2009-08-09 07:28:35 +04:00
|
|
|
{
|
|
|
|
struct nameidata *ndp = state->ndp;
|
|
|
|
struct componentname *cnp = state->cnp;
|
|
|
|
struct cwdinfo *cwdi; /* pointer to cwd state */
|
|
|
|
struct lwp *self = curlwp; /* thread doing namei() */
|
2011-04-11 05:33:04 +04:00
|
|
|
struct vnode *rootdir, *erootdir, *curdir, *startdir;
|
2009-08-09 07:28:35 +04:00
|
|
|
|
2011-04-11 05:33:04 +04:00
|
|
|
cwdi = self->l_proc->p_cwdi;
|
|
|
|
rw_enter(&cwdi->cwdi_lock, RW_READER);
|
1997-04-08 14:11:55 +04:00
|
|
|
|
2011-04-11 05:33:04 +04:00
|
|
|
/* root dir */
|
|
|
|
if (cwdi->cwdi_rdir == NULL || (cnp->cn_flags & NOCHROOT)) {
|
|
|
|
rootdir = rootvnode;
|
|
|
|
} else {
|
|
|
|
rootdir = cwdi->cwdi_rdir;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
2010-11-19 09:44:33 +03:00
|
|
|
|
2011-04-11 05:33:04 +04:00
|
|
|
/* emulation root dir, if any */
|
|
|
|
if ((cnp->cn_flags & TRYEMULROOT) == 0) {
|
|
|
|
/* if we don't want it, don't fetch it */
|
|
|
|
erootdir = NULL;
|
|
|
|
} else if (cnp->cn_flags & EMULROOTSET) {
|
|
|
|
/* explicitly set emulroot; "/../" doesn't override this */
|
|
|
|
erootdir = ndp->ni_erootdir;
|
|
|
|
} else if (!strncmp(ndp->ni_pnbuf, "/../", 4)) {
|
|
|
|
/* explicit reference to real rootdir */
|
|
|
|
erootdir = NULL;
|
|
|
|
} else {
|
|
|
|
/* may be null */
|
|
|
|
erootdir = cwdi->cwdi_edir;
|
|
|
|
}
|
1997-04-08 14:11:55 +04:00
|
|
|
|
2011-04-11 05:33:04 +04:00
|
|
|
/* current dir */
|
|
|
|
curdir = cwdi->cwdi_cdir;
|
2007-04-22 12:29:55 +04:00
|
|
|
|
2011-04-11 05:33:04 +04:00
|
|
|
if (ndp->ni_pnbuf[0] != '/') {
|
|
|
|
startdir = curdir;
|
|
|
|
erootdir = NULL;
|
|
|
|
} else if (cnp->cn_flags & TRYEMULROOT && erootdir != NULL) {
|
|
|
|
startdir = erootdir;
|
1997-05-08 18:55:22 +04:00
|
|
|
} else {
|
2011-04-11 05:33:04 +04:00
|
|
|
startdir = rootdir;
|
|
|
|
erootdir = NULL;
|
1997-05-08 18:55:22 +04:00
|
|
|
}
|
2011-04-11 05:33:04 +04:00
|
|
|
|
|
|
|
state->ndp->ni_rootdir = rootdir;
|
|
|
|
state->ndp->ni_erootdir = erootdir;
|
2009-08-09 07:28:35 +04:00
|
|
|
|
|
|
|
/*
|
2011-04-11 05:33:04 +04:00
|
|
|
* Get a reference to the start dir so we can safely unlock cwdi.
|
|
|
|
*
|
|
|
|
* XXX: should we hold references to rootdir and erootdir while
|
|
|
|
* we're running? What happens if a multithreaded process chroots
|
|
|
|
* during namei?
|
2009-08-09 07:28:35 +04:00
|
|
|
*/
|
2011-04-11 05:33:04 +04:00
|
|
|
vref(startdir);
|
|
|
|
|
|
|
|
rw_exit(&cwdi->cwdi_lock);
|
|
|
|
return startdir;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the directory context for the nfsd case, in parallel to
|
|
|
|
* getstartdir. Initializes the rootdir and erootdir state and
|
2011-04-11 06:21:17 +04:00
|
|
|
* returns a reference to the passed-in starting dir.
|
2011-04-11 05:33:04 +04:00
|
|
|
*/
|
|
|
|
static struct vnode *
|
|
|
|
namei_getstartdir_for_nfsd(struct namei_state *state, struct vnode *startdir)
|
|
|
|
{
|
|
|
|
/* always use the real root, and never set an emulation root */
|
|
|
|
state->ndp->ni_rootdir = rootvnode;
|
|
|
|
state->ndp->ni_erootdir = NULL;
|
|
|
|
|
|
|
|
vref(startdir);
|
|
|
|
return startdir;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ktrace the namei operation.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
namei_ktrace(struct namei_state *state)
|
|
|
|
{
|
|
|
|
struct nameidata *ndp = state->ndp;
|
|
|
|
struct componentname *cnp = state->cnp;
|
|
|
|
struct lwp *self = curlwp; /* thread doing namei() */
|
|
|
|
const char *emul_path;
|
|
|
|
|
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).
|
|
|
|
*/
|
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
|
|
|
}
|
2011-04-11 05:33:04 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-04-11 06:18:20 +04:00
|
|
|
* Start up namei. Find the root dir and cwd, establish the starting
|
|
|
|
* directory for lookup, and lock it. Also calls ktrace when
|
2011-04-11 05:33:04 +04:00
|
|
|
* appropriate.
|
|
|
|
*/
|
|
|
|
static int
|
2011-04-11 05:38:10 +04:00
|
|
|
namei_start(struct namei_state *state, struct vnode *forcecwd,
|
|
|
|
struct vnode **startdir_ret)
|
2011-04-11 05:33:04 +04:00
|
|
|
{
|
|
|
|
struct nameidata *ndp = state->ndp;
|
2011-04-11 05:38:10 +04:00
|
|
|
struct vnode *startdir;
|
2011-04-11 05:33:04 +04:00
|
|
|
|
|
|
|
/* length includes null terminator (was originally from copyinstr) */
|
|
|
|
ndp->ni_pathlen = strlen(ndp->ni_pnbuf) + 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* POSIX.1 requirement: "" is not a valid file name.
|
|
|
|
*/
|
|
|
|
if (ndp->ni_pathlen == 1) {
|
|
|
|
return ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
ndp->ni_loopcnt = 0;
|
|
|
|
|
|
|
|
/* Get starting directory, set up root, and ktrace. */
|
2011-04-11 05:35:00 +04:00
|
|
|
if (forcecwd != NULL) {
|
2011-04-11 05:38:10 +04:00
|
|
|
startdir = namei_getstartdir_for_nfsd(state, forcecwd);
|
2011-04-11 05:33:04 +04:00
|
|
|
/* no ktrace */
|
|
|
|
} else {
|
2011-04-11 05:38:10 +04:00
|
|
|
startdir = namei_getstartdir(state);
|
2011-04-11 05:33:04 +04:00
|
|
|
namei_ktrace(state);
|
|
|
|
}
|
2007-08-15 16:07:23 +04:00
|
|
|
|
2011-04-11 05:38:10 +04:00
|
|
|
vn_lock(startdir, LK_EXCLUSIVE | LK_RETRY);
|
2009-08-09 07:28:35 +04:00
|
|
|
|
2011-04-11 05:38:10 +04:00
|
|
|
*startdir_ret = startdir;
|
2009-08-09 07:28:35 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-04-11 06:21:17 +04:00
|
|
|
* Check for being at a symlink that we're going to follow.
|
2009-08-09 07:28:35 +04:00
|
|
|
*/
|
|
|
|
static inline int
|
2011-04-11 05:39:28 +04:00
|
|
|
namei_atsymlink(struct namei_state *state, struct vnode *foundobj)
|
2009-08-09 07:28:35 +04:00
|
|
|
{
|
2011-04-11 05:39:28 +04:00
|
|
|
return (foundobj->v_type == VLNK) &&
|
2011-04-11 05:37:43 +04:00
|
|
|
(state->cnp->cn_flags & (FOLLOW|REQUIREDIR));
|
2009-08-09 07:28:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Follow a symlink.
|
2011-04-11 06:21:17 +04:00
|
|
|
*
|
|
|
|
* Updates searchdir. inhibitmagic causes magic symlinks to not be
|
|
|
|
* interpreted; this is used by nfsd.
|
2009-08-09 07:28:35 +04:00
|
|
|
*/
|
|
|
|
static inline int
|
2011-04-11 05:38:24 +04:00
|
|
|
namei_follow(struct namei_state *state, int inhibitmagic,
|
2011-04-11 06:17:14 +04:00
|
|
|
struct vnode *searchdir, struct vnode *foundobj,
|
2011-04-11 05:38:24 +04:00
|
|
|
struct vnode **newsearchdir_ret)
|
2009-08-09 07:28:35 +04:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2011-04-11 06:17:14 +04:00
|
|
|
if (foundobj->v_mount->mnt_flag & MNT_SYMPERM) {
|
|
|
|
error = VOP_ACCESS(foundobj, VEXEC, cnp->cn_cred);
|
2009-08-09 07:28:35 +04:00
|
|
|
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);
|
2011-04-11 06:17:14 +04:00
|
|
|
error = VOP_READLINK(foundobj, &auio, cnp->cn_cred);
|
2009-08-09 07:28:35 +04:00
|
|
|
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.
|
2011-04-11 05:35:00 +04:00
|
|
|
*
|
|
|
|
* Inhibit symlink substitution for nfsd.
|
|
|
|
* XXX: This is how it was before; is that a bug or a feature?
|
2009-08-09 07:28:35 +04:00
|
|
|
*/
|
2011-04-11 05:35:00 +04:00
|
|
|
if ((!inhibitmagic && vfs_magiclinks &&
|
2009-08-09 07:28:35 +04:00
|
|
|
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);
|
2011-04-11 06:19:11 +04:00
|
|
|
|
|
|
|
/* we're now starting from the beginning of the buffer again */
|
|
|
|
cnp->cn_nameptr = ndp->ni_pnbuf;
|
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] == '/') {
|
2011-04-11 05:38:24 +04:00
|
|
|
vput(searchdir);
|
2009-08-09 07:28:35 +04:00
|
|
|
/* Keep absolute symbolic links inside emulation root */
|
2011-04-11 05:38:24 +04:00
|
|
|
searchdir = ndp->ni_erootdir;
|
|
|
|
if (searchdir == NULL ||
|
2010-11-30 13:29:57 +03:00
|
|
|
(ndp->ni_pnbuf[1] == '.'
|
|
|
|
&& ndp->ni_pnbuf[2] == '.'
|
|
|
|
&& ndp->ni_pnbuf[3] == '/')) {
|
2009-08-09 07:28:35 +04:00
|
|
|
ndp->ni_erootdir = NULL;
|
2011-04-11 05:38:24 +04:00
|
|
|
searchdir = ndp->ni_rootdir;
|
2009-08-09 07:28:35 +04:00
|
|
|
}
|
2011-04-11 05:38:24 +04:00
|
|
|
vref(searchdir);
|
|
|
|
vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);
|
2009-08-09 07:28:35 +04:00
|
|
|
}
|
|
|
|
|
2011-04-11 05:38:24 +04:00
|
|
|
*newsearchdir_ret = searchdir;
|
2009-08-09 07:28:35 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
|
|
|
1994-05-17 08:21:49 +04:00
|
|
|
/*
|
2011-04-11 06:21:17 +04:00
|
|
|
* Inspect the leading path component and update the state accordingly.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
2009-08-09 11:27:54 +04:00
|
|
|
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
|
|
|
*
|
2011-04-11 05:40:13 +04:00
|
|
|
* At this point, our only vnode state is that the search dir
|
|
|
|
* 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) {
|
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;
|
|
|
|
}
|
|
|
|
|
2011-04-11 06:21:17 +04:00
|
|
|
/*
|
|
|
|
* Call VOP_LOOKUP for a single lookup; return a new search directory
|
|
|
|
* (used when crossing mountpoints up or searching union mounts down) and
|
|
|
|
* the found object, which for create operations may be NULL on success.
|
|
|
|
*/
|
2009-08-09 11:27:54 +04:00
|
|
|
static int
|
2011-04-11 05:40:13 +04:00
|
|
|
lookup_once(struct namei_state *state,
|
|
|
|
struct vnode *searchdir,
|
2011-04-11 06:12:58 +04:00
|
|
|
struct vnode **newsearchdir_ret,
|
2011-04-11 05:40:13 +04:00
|
|
|
struct vnode **foundobj_ret)
|
2009-08-09 11:27:54 +04:00
|
|
|
{
|
2011-04-11 06:17:41 +04:00
|
|
|
struct vnode *tmpvn; /* scratch vnode */
|
|
|
|
struct vnode *foundobj; /* result */
|
2009-08-09 11:27:54 +04:00
|
|
|
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);
|
2011-04-11 06:15:09 +04:00
|
|
|
*newsearchdir_ret = searchdir;
|
2009-08-09 11:27:54 +04:00
|
|
|
|
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 (;;) {
|
2011-04-11 06:15:09 +04:00
|
|
|
if (searchdir == ndp->ni_rootdir ||
|
|
|
|
searchdir == rootvnode) {
|
2011-04-11 05:40:13 +04:00
|
|
|
foundobj = searchdir;
|
|
|
|
vref(foundobj);
|
|
|
|
*foundobj_ret = foundobj;
|
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
|
|
|
|
2011-04-11 05:40:13 +04:00
|
|
|
VOP_UNLOCK(searchdir);
|
|
|
|
retval = vn_isunder(searchdir, ndp->ni_rootdir, l);
|
|
|
|
vn_lock(searchdir, 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. */
|
2011-04-11 05:40:13 +04:00
|
|
|
vput(searchdir);
|
|
|
|
searchdir = NULL;
|
|
|
|
foundobj = ndp->ni_rootdir;
|
|
|
|
vref(foundobj);
|
|
|
|
vref(foundobj);
|
|
|
|
vn_lock(foundobj, LK_EXCLUSIVE | LK_RETRY);
|
2011-04-11 06:12:58 +04:00
|
|
|
*newsearchdir_ret = foundobj;
|
2011-04-11 05:40:13 +04:00
|
|
|
*foundobj_ret = foundobj;
|
2009-08-09 11:27:54 +04:00
|
|
|
return 0;
|
2002-06-21 06:19:12 +04:00
|
|
|
}
|
|
|
|
}
|
2011-04-11 05:40:13 +04:00
|
|
|
if ((searchdir->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;
|
2011-04-11 06:17:41 +04:00
|
|
|
tmpvn = searchdir;
|
2011-04-11 05:40:13 +04:00
|
|
|
searchdir = searchdir->v_mount->mnt_vnodecovered;
|
|
|
|
vref(searchdir);
|
2011-04-11 06:17:41 +04:00
|
|
|
vput(tmpvn);
|
2011-04-11 05:40:13 +04:00
|
|
|
vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);
|
2011-04-11 06:15:09 +04:00
|
|
|
*newsearchdir_ret = searchdir;
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We now have a segment name to search for, and a directory to search.
|
2011-04-11 05:40:13 +04:00
|
|
|
* Our vnode state here is that "searchdir" is held and locked.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
1994-06-08 15:28:29 +04:00
|
|
|
unionlookup:
|
2011-04-11 06:11:32 +04:00
|
|
|
foundobj = NULL;
|
|
|
|
error = VOP_LOOKUP(searchdir, &foundobj, cnp);
|
2011-04-11 06:15:09 +04:00
|
|
|
|
2006-12-09 19:11:50 +03:00
|
|
|
if (error != 0) {
|
1994-05-17 08:21:49 +04:00
|
|
|
#ifdef DIAGNOSTIC
|
2011-04-11 06:11:32 +04:00
|
|
|
if (foundobj != 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) &&
|
2011-04-11 05:40:13 +04:00
|
|
|
(searchdir->v_vflag & VV_ROOT) &&
|
|
|
|
(searchdir->v_mount->mnt_flag & MNT_UNION)) {
|
2011-04-11 06:17:41 +04:00
|
|
|
tmpvn = searchdir;
|
2011-04-11 05:40:13 +04:00
|
|
|
searchdir = searchdir->v_mount->mnt_vnodecovered;
|
|
|
|
vref(searchdir);
|
2011-04-11 06:17:41 +04:00
|
|
|
vput(tmpvn);
|
2011-04-11 05:40:13 +04:00
|
|
|
vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);
|
2011-04-11 06:15:09 +04:00
|
|
|
*newsearchdir_ret = searchdir;
|
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
|
|
|
/*
|
2011-04-11 06:18:20 +04:00
|
|
|
* We return success and a NULL foundobj to indicate
|
|
|
|
* that the entry doesn't currently exist, leaving a
|
2011-04-11 06:21:17 +04:00
|
|
|
* pointer to the (normally, locked) directory vnode
|
|
|
|
* as searchdir.
|
1994-05-17 08:21:49 +04:00
|
|
|
*/
|
2011-04-11 05:40:13 +04:00
|
|
|
*foundobj_ret = NULL;
|
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
|
|
|
}
|
|
|
|
|
2006-12-09 19:11:50 +03:00
|
|
|
/*
|
2011-04-11 06:11:32 +04:00
|
|
|
* "foundobj" and "searchdir" 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.
|
|
|
|
*/
|
2011-04-11 06:19:42 +04:00
|
|
|
while (foundobj->v_type == VDIR &&
|
|
|
|
(mp = foundobj->v_mountedhere) != NULL &&
|
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) {
|
2011-04-11 05:40:13 +04:00
|
|
|
vput(foundobj);
|
2009-08-09 11:27:54 +04:00
|
|
|
return error;
|
2008-05-06 19:04:00 +04:00
|
|
|
}
|
2011-04-11 06:11:32 +04:00
|
|
|
KASSERT(searchdir != foundobj);
|
|
|
|
VOP_UNLOCK(searchdir);
|
2011-04-11 05:40:13 +04:00
|
|
|
vput(foundobj);
|
2011-04-11 06:17:41 +04:00
|
|
|
error = VFS_ROOT(mp, &foundobj);
|
2008-04-30 16:49:16 +04:00
|
|
|
vfs_unbusy(mp, false, NULL);
|
1999-08-03 22:17:24 +04:00
|
|
|
if (error) {
|
2011-04-11 06:11:32 +04:00
|
|
|
vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);
|
2009-08-09 11:27:54 +04:00
|
|
|
return error;
|
1999-08-03 22:17:24 +04:00
|
|
|
}
|
2011-04-11 06:17:41 +04:00
|
|
|
VOP_UNLOCK(foundobj);
|
2011-04-11 06:11:32 +04:00
|
|
|
vn_lock(searchdir, LK_EXCLUSIVE | LK_RETRY);
|
|
|
|
vn_lock(foundobj, LK_EXCLUSIVE | LK_RETRY);
|
1994-05-17 08:21:49 +04:00
|
|
|
}
|
|
|
|
|
2011-04-11 05:40:13 +04:00
|
|
|
*foundobj_ret = foundobj;
|
2009-08-09 11:27:54 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-01-04 10:43:42 +03:00
|
|
|
//////////////////////////////
|
|
|
|
|
2011-04-11 06:21:17 +04:00
|
|
|
/*
|
|
|
|
* Do a complete path search from a single root directory.
|
|
|
|
* (This is called up to twice if TRYEMULROOT is in effect.)
|
|
|
|
*/
|
2011-01-04 10:43:42 +03:00
|
|
|
static int
|
2011-04-11 05:36:59 +04:00
|
|
|
namei_oneroot(struct namei_state *state, struct vnode *forcecwd,
|
2011-04-11 05:35:00 +04:00
|
|
|
int neverfollow, int inhibitmagic)
|
2011-01-04 10:43:42 +03:00
|
|
|
{
|
|
|
|
struct nameidata *ndp = state->ndp;
|
|
|
|
struct componentname *cnp = state->cnp;
|
2011-04-11 05:40:01 +04:00
|
|
|
struct vnode *searchdir, *foundobj;
|
2011-04-11 05:37:43 +04:00
|
|
|
const char *cp;
|
2011-04-11 05:36:59 +04:00
|
|
|
int error;
|
2011-01-04 10:43:42 +03:00
|
|
|
|
2011-04-11 06:13:22 +04:00
|
|
|
error = namei_start(state, forcecwd, &searchdir);
|
2011-01-04 10:43:42 +03:00
|
|
|
if (error) {
|
2011-04-11 06:17:54 +04:00
|
|
|
ndp->ni_dvp = NULL;
|
|
|
|
ndp->ni_vp = NULL;
|
2011-01-04 10:43:42 +03:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2011-04-11 05:37:43 +04:00
|
|
|
/*
|
|
|
|
* Setup: break out flag bits into variables.
|
|
|
|
*/
|
|
|
|
state->docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
|
|
|
|
if (cnp->cn_nameiop == DELETE)
|
|
|
|
state->docache = 0;
|
|
|
|
state->rdonly = cnp->cn_flags & RDONLY;
|
|
|
|
|
2011-04-11 05:33:04 +04:00
|
|
|
/*
|
|
|
|
* Keep going until we run out of path components.
|
|
|
|
*/
|
2011-04-11 05:37:43 +04:00
|
|
|
cnp->cn_nameptr = ndp->ni_pnbuf;
|
2011-01-04 10:43:42 +03:00
|
|
|
for (;;) {
|
2011-04-11 05:33:04 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the directory we're on is unmounted, bail out.
|
|
|
|
* XXX: should this also check if it's unlinked?
|
|
|
|
*/
|
2011-04-11 06:13:22 +04:00
|
|
|
if (searchdir->v_mount == NULL) {
|
|
|
|
vput(searchdir);
|
2011-04-11 06:17:54 +04:00
|
|
|
ndp->ni_dvp = NULL;
|
|
|
|
ndp->ni_vp = NULL;
|
2011-01-04 10:43:42 +03:00
|
|
|
return (ENOENT);
|
|
|
|
}
|
2011-04-11 05:33:04 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Look up the next path component.
|
|
|
|
* (currently, this may consume more than one)
|
|
|
|
*/
|
2011-04-11 05:37:14 +04:00
|
|
|
|
2011-04-11 05:37:43 +04:00
|
|
|
cnp->cn_flags &= ~ISSYMLINK;
|
2011-04-11 05:37:14 +04:00
|
|
|
|
2011-04-11 05:37:43 +04:00
|
|
|
dirloop:
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2011-04-11 05:39:46 +04:00
|
|
|
if (searchdir->v_type != VDIR) {
|
|
|
|
vput(searchdir);
|
2011-04-11 06:19:27 +04:00
|
|
|
ndp->ni_dvp = NULL;
|
2011-04-11 06:15:38 +04:00
|
|
|
ndp->ni_vp = NULL;
|
2011-04-11 05:37:43 +04:00
|
|
|
state->attempt_retry = 1;
|
|
|
|
return ENOTDIR;
|
2011-04-11 05:37:14 +04:00
|
|
|
}
|
2011-04-11 05:37:43 +04:00
|
|
|
}
|
2011-04-11 05:37:14 +04:00
|
|
|
|
2011-04-11 05:37:43 +04:00
|
|
|
/*
|
|
|
|
* If we've exhausted the path name, then just return the
|
|
|
|
* current node.
|
|
|
|
*/
|
|
|
|
if (cnp->cn_nameptr[0] == '\0') {
|
2011-04-11 06:15:54 +04:00
|
|
|
vref(searchdir);
|
2011-04-11 05:40:01 +04:00
|
|
|
foundobj = searchdir;
|
2011-04-11 05:37:43 +04:00
|
|
|
cnp->cn_flags |= ISLASTCN;
|
|
|
|
|
|
|
|
/* bleh */
|
|
|
|
goto terminal;
|
2011-04-11 05:37:14 +04:00
|
|
|
}
|
2011-04-11 05:37:43 +04:00
|
|
|
|
|
|
|
error = lookup_parsepath(state);
|
|
|
|
if (error) {
|
2011-04-11 05:39:46 +04:00
|
|
|
vput(searchdir);
|
2011-04-11 06:19:27 +04:00
|
|
|
ndp->ni_dvp = NULL;
|
2011-04-11 05:37:43 +04:00
|
|
|
ndp->ni_vp = NULL;
|
2011-04-11 05:36:59 +04:00
|
|
|
state->attempt_retry = 1;
|
2011-01-04 10:43:42 +03:00
|
|
|
return (error);
|
|
|
|
}
|
2011-04-11 05:37:14 +04:00
|
|
|
|
2011-04-11 06:12:58 +04:00
|
|
|
error = lookup_once(state, searchdir, &searchdir, &foundobj);
|
2011-04-11 05:37:43 +04:00
|
|
|
if (error) {
|
2011-04-11 06:15:38 +04:00
|
|
|
vput(searchdir);
|
2011-04-11 06:19:27 +04:00
|
|
|
ndp->ni_dvp = NULL;
|
2011-04-11 05:37:43 +04:00
|
|
|
ndp->ni_vp = NULL;
|
2011-04-11 05:37:14 +04:00
|
|
|
/*
|
2011-04-11 05:37:43 +04:00
|
|
|
* Note that if we're doing TRYEMULROOT we can
|
|
|
|
* retry with the normal root. Where this is
|
|
|
|
* currently set matches previous practice,
|
|
|
|
* but the previous practice didn't make much
|
|
|
|
* sense and somebody should sit down and
|
|
|
|
* figure out which cases should cause retry
|
|
|
|
* and which shouldn't. XXX.
|
2011-04-11 05:37:14 +04:00
|
|
|
*/
|
2011-04-11 05:37:43 +04:00
|
|
|
state->attempt_retry = 1;
|
|
|
|
return (error);
|
|
|
|
}
|
2011-04-11 06:15:54 +04:00
|
|
|
|
2011-04-11 06:17:28 +04:00
|
|
|
if (foundobj == NULL) {
|
|
|
|
/*
|
|
|
|
* Success with no object returned means we're
|
|
|
|
* creating something and it isn't already
|
|
|
|
* there. Break out of the main loop now so
|
|
|
|
* the code below doesn't have to test for
|
|
|
|
* foundobj == NULL.
|
|
|
|
*/
|
2011-04-11 05:37:43 +04:00
|
|
|
break;
|
2011-04-11 05:37:14 +04:00
|
|
|
}
|
2011-01-04 10:43:42 +03:00
|
|
|
|
|
|
|
/*
|
2011-04-11 05:37:43 +04:00
|
|
|
* Check for symbolic link. If we've reached one,
|
|
|
|
* follow it, unless we aren't supposed to. Back up
|
|
|
|
* over any slashes that we skipped, as we will need
|
|
|
|
* them again.
|
2011-01-04 10:43:42 +03:00
|
|
|
*/
|
2011-04-11 05:40:01 +04:00
|
|
|
if (namei_atsymlink(state, foundobj)) {
|
2011-04-11 05:37:43 +04:00
|
|
|
ndp->ni_pathlen += state->slashes;
|
|
|
|
ndp->ni_next -= state->slashes;
|
|
|
|
cnp->cn_flags |= ISSYMLINK;
|
2011-04-11 05:35:00 +04:00
|
|
|
if (neverfollow) {
|
|
|
|
error = EINVAL;
|
|
|
|
} else {
|
2011-04-11 06:13:22 +04:00
|
|
|
/*
|
|
|
|
* dholland 20110410: if we're at a
|
|
|
|
* union mount it might make sense to
|
|
|
|
* use the top of the union stack here
|
|
|
|
* rather than the layer we found the
|
|
|
|
* symlink in. (FUTURE)
|
|
|
|
*/
|
2011-04-11 05:38:24 +04:00
|
|
|
error = namei_follow(state, inhibitmagic,
|
2011-04-11 06:18:07 +04:00
|
|
|
searchdir, foundobj,
|
2011-04-11 06:13:22 +04:00
|
|
|
&searchdir);
|
2011-04-11 05:35:00 +04:00
|
|
|
}
|
2011-01-04 10:43:42 +03:00
|
|
|
if (error) {
|
2011-04-11 06:18:07 +04:00
|
|
|
KASSERT(searchdir != foundobj);
|
2011-04-11 06:15:54 +04:00
|
|
|
vput(searchdir);
|
2011-04-11 06:18:07 +04:00
|
|
|
vput(foundobj);
|
2011-04-11 06:19:27 +04:00
|
|
|
ndp->ni_dvp = NULL;
|
2011-01-04 10:43:42 +03:00
|
|
|
ndp->ni_vp = NULL;
|
|
|
|
return error;
|
|
|
|
}
|
2011-04-11 06:19:11 +04:00
|
|
|
vput(foundobj);
|
|
|
|
foundobj = NULL;
|
2011-04-11 05:37:43 +04:00
|
|
|
continue;
|
2011-01-04 10:43:42 +03:00
|
|
|
}
|
2011-04-11 05:37:43 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for directory, if the component was
|
|
|
|
* followed by a series of slashes.
|
|
|
|
*/
|
2011-04-11 05:40:01 +04:00
|
|
|
if ((foundobj->v_type != VDIR) && (cnp->cn_flags & REQUIREDIR)) {
|
2011-04-11 06:15:54 +04:00
|
|
|
KASSERT(foundobj != searchdir);
|
|
|
|
if (searchdir) {
|
|
|
|
vput(searchdir);
|
2011-04-11 05:37:43 +04:00
|
|
|
}
|
2011-04-11 06:19:27 +04:00
|
|
|
vput(foundobj);
|
|
|
|
ndp->ni_dvp = NULL;
|
|
|
|
ndp->ni_vp = NULL;
|
2011-04-11 05:37:43 +04:00
|
|
|
state->attempt_retry = 1;
|
|
|
|
return ENOTDIR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2011-04-11 06:15:54 +04:00
|
|
|
if (searchdir == foundobj) {
|
|
|
|
vrele(searchdir);
|
2011-04-11 05:37:43 +04:00
|
|
|
} else {
|
2011-04-11 06:15:54 +04:00
|
|
|
vput(searchdir);
|
2011-04-11 05:37:43 +04:00
|
|
|
}
|
2011-04-11 05:40:01 +04:00
|
|
|
searchdir = foundobj;
|
2011-04-11 06:15:54 +04:00
|
|
|
foundobj = NULL;
|
2011-04-11 05:37:43 +04:00
|
|
|
goto dirloop;
|
|
|
|
}
|
|
|
|
|
|
|
|
terminal:
|
2011-04-11 05:40:01 +04:00
|
|
|
if (foundobj == ndp->ni_erootdir) {
|
2011-04-11 05:37:43 +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.
|
|
|
|
*/
|
2011-04-11 06:20:00 +04:00
|
|
|
if (searchdir != NULL) {
|
|
|
|
if (searchdir == foundobj)
|
|
|
|
vrele(searchdir);
|
|
|
|
else
|
2011-04-11 06:15:54 +04:00
|
|
|
vput(searchdir);
|
2011-04-11 06:20:00 +04:00
|
|
|
searchdir = NULL;
|
|
|
|
}
|
2011-04-11 05:40:01 +04:00
|
|
|
vput(foundobj);
|
|
|
|
foundobj = ndp->ni_rootdir;
|
|
|
|
vref(foundobj);
|
|
|
|
vn_lock(foundobj, LK_EXCLUSIVE | LK_RETRY);
|
2011-04-11 05:37:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-04-11 06:16:07 +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, or we crossed
|
|
|
|
* a mount point), then we must fail.
|
2011-04-11 05:37:43 +04:00
|
|
|
*/
|
2011-04-11 06:16:07 +04:00
|
|
|
if (cnp->cn_nameiop != LOOKUP &&
|
|
|
|
(searchdir == NULL ||
|
|
|
|
searchdir->v_mount != foundobj->v_mount)) {
|
2011-04-11 06:20:00 +04:00
|
|
|
if (searchdir) {
|
|
|
|
vput(searchdir);
|
|
|
|
}
|
|
|
|
vput(foundobj);
|
|
|
|
foundobj = NULL;
|
|
|
|
ndp->ni_dvp = NULL;
|
|
|
|
ndp->ni_vp = NULL;
|
|
|
|
state->attempt_retry = 1;
|
|
|
|
|
2011-04-11 05:37:43 +04:00
|
|
|
switch (cnp->cn_nameiop) {
|
|
|
|
case CREATE:
|
2011-04-11 06:20:15 +04:00
|
|
|
return EEXIST;
|
2011-04-11 05:37:43 +04:00
|
|
|
case DELETE:
|
|
|
|
case RENAME:
|
2011-04-11 06:20:15 +04:00
|
|
|
return EBUSY;
|
2011-04-11 05:37:43 +04:00
|
|
|
default:
|
2011-04-11 06:20:15 +04:00
|
|
|
break;
|
2011-04-11 05:37:43 +04:00
|
|
|
}
|
2011-04-11 06:20:15 +04:00
|
|
|
panic("Invalid nameiop\n");
|
2011-01-04 10:43:42 +03:00
|
|
|
}
|
2011-04-11 05:37:43 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Disallow directory write attempts on read-only lookups.
|
|
|
|
* Prefers EEXIST over EROFS for the CREATE case.
|
|
|
|
*/
|
|
|
|
if (state->rdonly &&
|
|
|
|
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
|
2011-04-11 06:15:54 +04:00
|
|
|
if (searchdir) {
|
2011-04-11 06:20:00 +04:00
|
|
|
if (foundobj != searchdir) {
|
|
|
|
vput(searchdir);
|
|
|
|
} else {
|
|
|
|
vrele(searchdir);
|
|
|
|
}
|
|
|
|
searchdir = NULL;
|
2011-04-11 05:37:43 +04:00
|
|
|
}
|
2011-04-11 06:20:00 +04:00
|
|
|
vput(foundobj);
|
|
|
|
foundobj = NULL;
|
2011-04-11 06:19:27 +04:00
|
|
|
ndp->ni_dvp = NULL;
|
2011-04-11 06:15:54 +04:00
|
|
|
ndp->ni_vp = NULL;
|
2011-04-11 05:37:43 +04:00
|
|
|
state->attempt_retry = 1;
|
2011-04-11 06:20:15 +04:00
|
|
|
return EROFS;
|
2011-04-11 05:37:43 +04:00
|
|
|
}
|
|
|
|
if ((cnp->cn_flags & LOCKLEAF) == 0) {
|
2011-04-11 06:21:01 +04:00
|
|
|
/*
|
|
|
|
* Note: if LOCKPARENT but not LOCKLEAF is
|
|
|
|
* set, and searchdir == foundobj, this code
|
|
|
|
* necessarily unlocks the parent as well as
|
|
|
|
* the leaf. That is, just because you specify
|
|
|
|
* LOCKPARENT doesn't mean you necessarily get
|
|
|
|
* a locked parent vnode. The code in
|
|
|
|
* vfs_syscalls.c, and possibly elsewhere,
|
|
|
|
* that uses this combination "knows" this, so
|
|
|
|
* it can't be safely changed. Feh. XXX
|
|
|
|
*/
|
2011-04-11 05:40:01 +04:00
|
|
|
VOP_UNLOCK(foundobj);
|
2011-04-11 05:37:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2011-01-04 10:43:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-04-11 05:33:04 +04:00
|
|
|
* Done.
|
2011-01-04 10:43:42 +03:00
|
|
|
*/
|
|
|
|
|
2011-04-11 05:33:04 +04:00
|
|
|
/*
|
|
|
|
* If LOCKPARENT is not set, the parent directory isn't returned.
|
|
|
|
*/
|
2011-04-11 06:15:54 +04:00
|
|
|
if ((cnp->cn_flags & LOCKPARENT) == 0 && searchdir != NULL) {
|
2011-04-11 06:18:07 +04:00
|
|
|
if (searchdir == foundobj) {
|
2011-04-11 06:15:54 +04:00
|
|
|
vrele(searchdir);
|
2011-01-04 10:43:42 +03:00
|
|
|
} else {
|
2011-04-11 06:15:54 +04:00
|
|
|
vput(searchdir);
|
2011-01-04 10:43:42 +03:00
|
|
|
}
|
2011-04-11 06:15:54 +04:00
|
|
|
searchdir = NULL;
|
2011-01-04 10:43:42 +03:00
|
|
|
}
|
|
|
|
|
2011-04-11 06:15:54 +04:00
|
|
|
ndp->ni_dvp = searchdir;
|
2011-04-11 06:18:07 +04:00
|
|
|
ndp->ni_vp = foundobj;
|
2011-04-11 05:36:59 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-04-11 06:21:17 +04:00
|
|
|
/*
|
|
|
|
* Do namei; wrapper layer that handles TRYEMULROOT.
|
|
|
|
*/
|
2011-04-11 05:36:59 +04:00
|
|
|
static int
|
|
|
|
namei_tryemulroot(struct namei_state *state, struct vnode *forcecwd,
|
|
|
|
int neverfollow, int inhibitmagic)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
struct nameidata *ndp = state->ndp;
|
|
|
|
struct componentname *cnp = state->cnp;
|
|
|
|
const char *savepath = NULL;
|
|
|
|
|
|
|
|
KASSERT(cnp == &ndp->ni_cnd);
|
|
|
|
|
|
|
|
if (cnp->cn_flags & TRYEMULROOT) {
|
|
|
|
savepath = pathbuf_stringcopy_get(ndp->ni_pathbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
emul_retry:
|
|
|
|
state->attempt_retry = 0;
|
|
|
|
|
|
|
|
error = namei_oneroot(state, forcecwd, neverfollow, inhibitmagic);
|
|
|
|
if (error) {
|
|
|
|
/*
|
|
|
|
* Once namei has started up, the existence of ni_erootdir
|
|
|
|
* tells us whether we're working from an emulation root.
|
|
|
|
* The TRYEMULROOT flag isn't necessarily authoritative.
|
|
|
|
*/
|
|
|
|
if (ndp->ni_erootdir != NULL && state->attempt_retry) {
|
|
|
|
/* Retry the whole thing using the normal root */
|
|
|
|
cnp->cn_flags &= ~TRYEMULROOT;
|
|
|
|
state->attempt_retry = 0;
|
|
|
|
|
|
|
|
/* kinda gross */
|
|
|
|
strcpy(ndp->ni_pathbuf->pb_path, savepath);
|
|
|
|
pathbuf_stringcopy_put(ndp->ni_pathbuf, savepath);
|
|
|
|
savepath = NULL;
|
|
|
|
|
|
|
|
goto emul_retry;
|
|
|
|
}
|
|
|
|
}
|
2011-01-04 10:43:42 +03:00
|
|
|
if (savepath != NULL) {
|
|
|
|
pathbuf_stringcopy_put(ndp->ni_pathbuf, savepath);
|
|
|
|
}
|
2011-04-11 05:36:59 +04:00
|
|
|
return error;
|
2011-01-04 10:43:42 +03:00
|
|
|
}
|
|
|
|
|
2011-04-11 06:21:17 +04:00
|
|
|
/*
|
|
|
|
* External interface.
|
|
|
|
*/
|
2011-01-04 10:43:42 +03:00
|
|
|
int
|
|
|
|
namei(struct nameidata *ndp)
|
|
|
|
{
|
|
|
|
struct namei_state state;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
namei_init(&state, ndp);
|
2011-04-11 05:36:59 +04:00
|
|
|
error = namei_tryemulroot(&state, NULL,
|
|
|
|
0/*!neverfollow*/, 0/*!inhibitmagic*/);
|
2011-01-04 10:43:42 +03:00
|
|
|
namei_cleanup(&state);
|
|
|
|
|
2011-04-11 06:16:27 +04:00
|
|
|
if (error) {
|
|
|
|
/* make sure no stray refs leak out */
|
2011-04-11 06:17:54 +04:00
|
|
|
KASSERT(ndp->ni_dvp == NULL);
|
|
|
|
KASSERT(ndp->ni_vp == NULL);
|
2011-04-11 06:16:27 +04:00
|
|
|
}
|
|
|
|
|
2011-01-04 10:43:42 +03:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
/*
|
2011-04-11 06:21:17 +04:00
|
|
|
* External interface used by nfsd. This is basically different from
|
|
|
|
* namei only in that it has the ability to pass in the "current
|
|
|
|
* directory", and uses an extra flag "neverfollow" for which there's
|
|
|
|
* no physical flag defined in namei.h. (There used to be a cut&paste
|
|
|
|
* copy of about half of namei in nfsd to allow these minor
|
|
|
|
* adjustments to exist.)
|
2011-04-11 05:35:55 +04:00
|
|
|
*
|
2011-04-11 06:21:17 +04:00
|
|
|
* XXX: the namei interface should be adjusted so nfsd can just use
|
|
|
|
* ordinary namei().
|
2009-08-09 11:27:54 +04:00
|
|
|
*/
|
2011-04-11 05:35:00 +04:00
|
|
|
int
|
2011-04-11 05:35:55 +04:00
|
|
|
lookup_for_nfsd(struct nameidata *ndp, struct vnode *forcecwd, int neverfollow)
|
2011-04-11 05:35:00 +04:00
|
|
|
{
|
|
|
|
struct namei_state state;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
namei_init(&state, ndp);
|
2011-04-11 05:36:59 +04:00
|
|
|
error = namei_tryemulroot(&state, forcecwd,
|
|
|
|
neverfollow, 1/*inhibitmagic*/);
|
2009-09-27 21:19:07 +04:00
|
|
|
namei_cleanup(&state);
|
|
|
|
|
2011-04-11 06:16:27 +04:00
|
|
|
if (error) {
|
|
|
|
/* make sure no stray refs leak out */
|
2011-04-11 06:17:54 +04:00
|
|
|
KASSERT(ndp->ni_dvp == NULL);
|
|
|
|
KASSERT(ndp->ni_vp == NULL);
|
2011-04-11 06:16:27 +04:00
|
|
|
}
|
|
|
|
|
2009-09-27 21:19:07 +04:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2011-04-11 06:21:17 +04:00
|
|
|
/*
|
|
|
|
* A second external interface used by nfsd. This turns out to be a
|
|
|
|
* single lookup used by the WebNFS code (ha!) to get "index.html" or
|
|
|
|
* equivalent when asked for a directory. It should eventually evolve
|
|
|
|
* into some kind of namei_once() call; for the time being it's kind
|
|
|
|
* of a mess. XXX.
|
|
|
|
*
|
|
|
|
* dholland 20110109: I don't think it works, and I don't think it
|
|
|
|
* worked before I started hacking and slashing either, and I doubt
|
|
|
|
* anyone will ever notice.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Internals. This calls lookup_once() after setting up the assorted
|
|
|
|
* pieces of state the way they ought to be.
|
|
|
|
*/
|
2011-04-11 05:36:28 +04:00
|
|
|
static int
|
|
|
|
do_lookup_for_nfsd_index(struct namei_state *state, struct vnode *startdir)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
struct componentname *cnp = state->cnp;
|
|
|
|
struct nameidata *ndp = state->ndp;
|
2011-04-11 05:40:13 +04:00
|
|
|
struct vnode *foundobj;
|
2011-04-11 05:36:28 +04:00
|
|
|
const char *cp; /* pointer into pathname argument */
|
|
|
|
|
|
|
|
KASSERT(cnp == &ndp->ni_cnd);
|
|
|
|
|
|
|
|
cnp->cn_nameptr = ndp->ni_pnbuf;
|
|
|
|
state->docache = 1;
|
|
|
|
state->rdonly = cnp->cn_flags & RDONLY;
|
|
|
|
ndp->ni_dvp = NULL;
|
|
|
|
cnp->cn_flags &= ~ISSYMLINK;
|
|
|
|
|
|
|
|
cnp->cn_consume = 0;
|
|
|
|
cp = NULL;
|
|
|
|
cnp->cn_hash = namei_hash(cnp->cn_nameptr, &cp);
|
|
|
|
cnp->cn_namelen = cp - cnp->cn_nameptr;
|
|
|
|
KASSERT(cnp->cn_namelen <= NAME_MAX);
|
|
|
|
ndp->ni_pathlen -= cnp->cn_namelen;
|
|
|
|
ndp->ni_next = cp;
|
|
|
|
state->slashes = 0;
|
|
|
|
cnp->cn_flags &= ~REQUIREDIR;
|
|
|
|
cnp->cn_flags |= MAKEENTRY|ISLASTCN;
|
|
|
|
|
|
|
|
if (cnp->cn_namelen == 2 &&
|
|
|
|
cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
|
|
|
|
cnp->cn_flags |= ISDOTDOT;
|
|
|
|
else
|
|
|
|
cnp->cn_flags &= ~ISDOTDOT;
|
|
|
|
|
2011-04-11 06:17:01 +04:00
|
|
|
/*
|
|
|
|
* Because lookup_once can change the startdir, we need our
|
|
|
|
* own reference to it to avoid consuming the caller's.
|
|
|
|
*/
|
|
|
|
vref(startdir);
|
|
|
|
vn_lock(startdir, LK_EXCLUSIVE | LK_RETRY);
|
|
|
|
error = lookup_once(state, startdir, &startdir, &foundobj);
|
|
|
|
vput(startdir);
|
2011-04-11 05:36:28 +04:00
|
|
|
if (error) {
|
|
|
|
goto bad;
|
|
|
|
}
|
2011-04-11 06:12:42 +04:00
|
|
|
ndp->ni_vp = foundobj;
|
2011-04-11 06:17:28 +04:00
|
|
|
|
|
|
|
if (foundobj == NULL) {
|
2011-04-11 05:36:28 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-04-11 06:17:01 +04:00
|
|
|
KASSERT((cnp->cn_flags & LOCKPARENT) == 0);
|
2011-04-11 05:36:28 +04:00
|
|
|
if ((cnp->cn_flags & LOCKLEAF) == 0) {
|
2011-04-11 05:40:13 +04:00
|
|
|
VOP_UNLOCK(foundobj);
|
2011-04-11 05:36:28 +04:00
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
bad:
|
|
|
|
ndp->ni_vp = NULL;
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2011-04-11 06:21:17 +04:00
|
|
|
/*
|
|
|
|
* External interface. The partitioning between this function and the
|
|
|
|
* above isn't very clear - the above function exists mostly so code
|
|
|
|
* that uses "state->" can be shuffled around without having to change
|
|
|
|
* it to "state.".
|
|
|
|
*/
|
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-04-11 05:33:04 +04:00
|
|
|
/*
|
2011-04-11 05:35:55 +04:00
|
|
|
* Note: the name sent in here (is not|should not be) allowed
|
|
|
|
* to contain a slash.
|
2011-04-11 05:33:04 +04:00
|
|
|
*/
|
2011-04-11 05:36:28 +04:00
|
|
|
if (strlen(ndp->ni_pathbuf->pb_path) > NAME_MAX) {
|
|
|
|
return ENAMETOOLONG;
|
|
|
|
}
|
|
|
|
if (strchr(ndp->ni_pathbuf->pb_path, '/')) {
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2011-01-02 08:01:20 +03:00
|
|
|
|
2011-04-11 05:33:04 +04:00
|
|
|
ndp->ni_pathlen = strlen(ndp->ni_pathbuf->pb_path) + 1;
|
|
|
|
ndp->ni_pnbuf = NULL;
|
|
|
|
ndp->ni_cnd.cn_nameptr = NULL;
|
|
|
|
|
2009-08-09 11:27:54 +04:00
|
|
|
namei_init(&state, ndp);
|
2011-04-11 05:36:28 +04:00
|
|
|
error = do_lookup_for_nfsd_index(&state, startdir);
|
2009-08-09 11:27:54 +04:00
|
|
|
namei_cleanup(&state);
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2011-01-04 10:43:42 +03:00
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
|
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;
|
|
|
|
}
|