/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Portions of this source code were derived from Berkeley 4.3 BSD
* under license from the Regents of the University of California.
*/
/*
* This file contains the file lookup code for NFS.
*/
#include <rpc/rpc.h>
#include "brpc.h"
#include <rpc/types.h>
#include <rpc/auth.h>
#include <rpc/xdr.h>
#include <rpc/rpc_msg.h>
#include <sys/t_lock.h>
#include "clnt.h"
#include <rpcsvc/mount.h>
#include <st_pathname.h>
#include <sys/errno.h>
#include <sys/promif.h>
#include "nfs_inet.h"
#include "socket_inet.h"
#include <rpcsvc/nfs_prot.h>
#include <rpcsvc/nfs4_prot.h>
#include <sys/types.h>
#include <sys/salib.h>
#include <sys/sacache.h>
#include <sys/stat.h>
#include <sys/bootvfs.h>
#include <sys/bootdebug.h>
#include "mac.h"
static int root_inum = 1; /* Dummy i-node number for root */
static int next_inum = 1; /* Next dummy i-node number */
#define dprintf if (boothowto & RB_DEBUG) printf
/*
* starting at current directory (root for us), lookup the pathname.
* return the file handle of said file.
*/
static int stlookuppn(struct st_pathname *pnp, struct nfs_file *cfile,
bool_t needroothandle);
/*
* For NFSv4 we may be calling lookup in the context of evaluating the
* root path. In this case we set needroothandle to TRUE.
*/
int
lookup(char *pathname, struct nfs_file *cur_file, bool_t needroothandle)
{
struct st_pathname pnp;
int error;
static char lkup_path[NFS_MAXPATHLEN]; /* pn_alloc doesn't */
pnp.pn_buf = &lkup_path[0];
bzero(pnp.pn_buf, NFS_MAXPATHLEN);
error = stpn_get(pathname, &pnp);
if (error)
return (error);
error = stlookuppn(&pnp, cur_file, needroothandle);
return (error);
}
static int
stlookuppn(struct st_pathname *pnp, struct nfs_file *cfile,
bool_t needroothandle)
{
char component[NFS_MAXNAMLEN+1]; /* buffer for component */
int nlink = 0;
int error = 0;
int dino, cino;
struct nfs_file *cdp = NULL;
*cfile = roothandle; /* structure copy - start at the root. */
dino = root_inum;
begin:
/*
* Each time we begin a new name interpretation (e.g.
* when first called and after each symbolic link is
* substituted), we allow the search to start at the
* root directory if the name starts with a '/', otherwise
* continuing from the current directory.
*/
component[0] = '\0';
if (stpn_peekchar(pnp) == '/') {
if (!needroothandle)
*cfile = roothandle;
dino = root_inum;
stpn_skipslash(pnp);
}
next:
/*
* Make sure we have a directory.
*/
if (!cfile_is_dir(cfile)) {
error = ENOTDIR;
goto bad;
}
/*
* Process the next component of the pathname.
*/
error = stpn_stripcomponent(pnp, component);
if (error)
goto bad;
/*
* Check for degenerate name (e.g. / or "")
* which is a way of talking about a directory,
* e.g. "/." or ".".
*/
if (component[0] == '\0')
return (0);
/*
* Handle "..": two special cases.
* 1. If at root directory (e.g. after chroot)
* then ignore it so can't get out.
* 2. If this vnode is the root of a mounted
* file system, then replace it with the
* vnode which was mounted on so we take the
* .. in the other file system.
*/
if (strcmp(component, "..") == 0) {
if (cfile == &roothandle)
goto skip;
}
/*
* Perform a lookup in the current directory.
* We create a simple negative lookup cache by storing
* inode -1 to indicate file not found.
*/
cino = get_dcache(mac_get_dev(), component, dino);
if (cino == -1)
return (ENOENT);
#ifdef DEBUG
dprintf("lookup: component %s pathleft %s\n", component, pnp->pn_path);
#endif
if ((cino == 0) ||
((cdp = (struct nfs_file *)get_icache(mac_get_dev(), cino)) == 0)) {
struct nfs_file *lkp;
/*
* If an RPC error occurs, error is not changed,
* else it is the NFS error if NULL is returned.
*/
error = -1;
switch (cfile->version) {
case NFS_VERSION:
lkp = nfslookup(cfile, component, &error);
break;
case NFS_V3:
lkp = nfs3lookup(cfile, component, &error);
break;
case NFS_V4:
lkp = nfs4lookup(cfile, component, &error);
break;
default:
printf("lookup: NFS Version %d not supported\n",
cfile->version);
lkp = NULL;
break;
}
/*
* Check for RPC error
*/
if (error == -1) {
printf("lookup: lookup RPC error\n");
return (error);
}
/*
* Check for NFS error
*/
if (lkp == NULL) {
if ((error != NFSERR_NOENT) &&
(error != NFS3ERR_NOENT) &&
(error != NFS4ERR_NOENT)) {
#ifdef DEBUG
dprintf("lookup: lkp is NULL with error %d\n", error);
#endif
return (error);
}
#ifdef DEBUG
dprintf("lookup: lkp is NULL with error %d\n", error);
#endif
/*
* File not found so set cached inode to -1
*/
set_dcache(mac_get_dev(), component, dino, -1);
return (error);
}
if (cdp = (struct nfs_file *)
bkmem_alloc(sizeof (struct nfs_file))) {
/*
* Save this entry in cache for next time ...
*/
if (!cino)
cino = ++next_inum;
*cdp = *lkp;
set_dcache(mac_get_dev(), component, dino, cino);
set_icache(mac_get_dev(), cino, cdp,
sizeof (struct nfs_file));
} else {
/*
* Out of memory, clear cache keys so we don't get
* confused later.
*/
cino = 0;
cdp = lkp;
}
}
dino = cino;
/*
* If we hit a symbolic link and there is more path to be
* translated or this operation does not wish to apply
* to a link, then place the contents of the link at the
* front of the remaining pathname.
*/
if (cfile_is_lnk(cdp)) {
struct st_pathname linkpath;
static char path_tmp[NFS_MAXPATHLEN]; /* used for symlinks */
char *pathp;
linkpath.pn_buf = &path_tmp[0];
nlink++;
if (nlink > MAXSYMLINKS) {
error = ELOOP;
goto bad;
}
switch (cdp->version) {
case NFS_VERSION:
error = nfsgetsymlink(cdp, &pathp);
break;
case NFS_V3:
error = nfs3getsymlink(cdp, &pathp);
break;
case NFS_V4:
error = nfs4getsymlink(cdp, &pathp);
break;
default:
printf("getsymlink: NFS Version %d not supported\n",
cdp->version);
error = ENOTSUP;
break;
}
if (error)
goto bad;
stpn_get(pathp, &linkpath);
if (stpn_pathleft(&linkpath) == 0)
(void) stpn_set(&linkpath, ".");
error = stpn_combine(pnp, &linkpath); /* linkpath before pn */
if (error)
goto bad;
goto begin;
}
if (needroothandle) {
roothandle = *cdp;
needroothandle = FALSE;
}
*cfile = *cdp;
skip:
/*
* Skip to next component of the pathname.
* If no more components, return last directory (if wanted) and
* last component (if wanted).
*/
if (stpn_pathleft(pnp) == 0) {
(void) stpn_set(pnp, component);
return (0);
}
/*
* skip over slashes from end of last component
*/
stpn_skipslash(pnp);
goto next;
bad:
/*
* Error.
*/
return (error);
}