1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28/* All Rights Reserved */
29
30/*
31 * Portions of this source code were derived from Berkeley 4.3 BSD
32 * under license from the Regents of the University of California.
33 */
34
35/*
36 * This file contains the file lookup code for NFS.
37 */
38
39#include <rpc/rpc.h>
40#include "brpc.h"
41#include <rpc/types.h>
42#include <rpc/auth.h>
43#include <rpc/xdr.h>
44#include <rpc/rpc_msg.h>
45#include <sys/t_lock.h>
46#include "clnt.h"
47#include <rpcsvc/mount.h>
48#include <st_pathname.h>
49#include <sys/errno.h>
50#include <sys/promif.h>
51#include "nfs_inet.h"
52#include "socket_inet.h"
53#include <rpcsvc/nfs_prot.h>
54#include <rpcsvc/nfs4_prot.h>
55#include <sys/types.h>
56#include <sys/salib.h>
57#include <sys/sacache.h>
58#include <sys/stat.h>
59#include <sys/bootvfs.h>
60#include <sys/bootdebug.h>
61#include "mac.h"
62
63static int root_inum = 1;	/* Dummy i-node number for root */
64static int next_inum = 1;	/* Next dummy i-node number	*/
65
66#define	dprintf	if (boothowto & RB_DEBUG) printf
67
68/*
69 * starting at current directory (root for us), lookup the pathname.
70 * return the file handle of said file.
71 */
72
73static int stlookuppn(struct st_pathname *pnp, struct nfs_file *cfile,
74			bool_t needroothandle);
75
76/*
77 * For NFSv4 we may be calling lookup in the context of evaluating the
78 * root path.  In this case we set needroothandle to TRUE.
79 */
80int
81lookup(char *pathname, struct nfs_file *cur_file, bool_t needroothandle)
82{
83	struct st_pathname pnp;
84	int error;
85
86	static char lkup_path[NFS_MAXPATHLEN];	/* pn_alloc doesn't */
87
88	pnp.pn_buf = &lkup_path[0];
89	bzero(pnp.pn_buf, NFS_MAXPATHLEN);
90	error = stpn_get(pathname, &pnp);
91	if (error)
92		return (error);
93	error = stlookuppn(&pnp, cur_file, needroothandle);
94	return (error);
95}
96
97static int
98stlookuppn(struct st_pathname *pnp, struct nfs_file *cfile,
99bool_t needroothandle)
100{
101	char component[NFS_MAXNAMLEN+1];	/* buffer for component */
102	int nlink = 0;
103	int error = 0;
104	int dino, cino;
105	struct nfs_file *cdp = NULL;
106
107	*cfile = roothandle;	/* structure copy - start at the root. */
108	dino = root_inum;
109begin:
110	/*
111	 * Each time we begin a new name interpretation (e.g.
112	 * when first called and after each symbolic link is
113	 * substituted), we allow the search to start at the
114	 * root directory if the name starts with a '/', otherwise
115	 * continuing from the current directory.
116	 */
117	component[0] = '\0';
118	if (stpn_peekchar(pnp) == '/') {
119		if (!needroothandle)
120			*cfile = roothandle;
121		dino = root_inum;
122		stpn_skipslash(pnp);
123	}
124
125next:
126	/*
127	 * Make sure we have a directory.
128	 */
129	if (!cfile_is_dir(cfile)) {
130		error = ENOTDIR;
131		goto bad;
132	}
133	/*
134	 * Process the next component of the pathname.
135	 */
136	error = stpn_stripcomponent(pnp, component);
137	if (error)
138		goto bad;
139
140	/*
141	 * Check for degenerate name (e.g. / or "")
142	 * which is a way of talking about a directory,
143	 * e.g. "/." or ".".
144	 */
145	if (component[0] == '\0')
146		return (0);
147
148	/*
149	 * Handle "..": two special cases.
150	 * 1. If at root directory (e.g. after chroot)
151	 *    then ignore it so can't get out.
152	 * 2. If this vnode is the root of a mounted
153	 *    file system, then replace it with the
154	 *    vnode which was mounted on so we take the
155	 *    .. in the other file system.
156	 */
157	if (strcmp(component, "..") == 0) {
158		if (cfile == &roothandle)
159			goto skip;
160	}
161
162	/*
163	 * Perform a lookup in the current directory.
164	 * We create a simple negative lookup cache by storing
165	 * inode -1 to indicate file not found.
166	 */
167	cino = get_dcache(mac_get_dev(), component, dino);
168	if (cino == -1)
169		return (ENOENT);
170#ifdef DEBUG
171	dprintf("lookup: component %s pathleft %s\n", component, pnp->pn_path);
172#endif
173	if ((cino == 0) ||
174	    ((cdp = (struct nfs_file *)get_icache(mac_get_dev(), cino)) == 0)) {
175		struct nfs_file *lkp;
176
177		/*
178		 * If an RPC error occurs, error is not changed,
179		 * else it is the NFS error if NULL is returned.
180		 */
181		error = -1;
182		switch (cfile->version) {
183		case NFS_VERSION:
184			lkp = nfslookup(cfile, component, &error);
185			break;
186		case NFS_V3:
187			lkp = nfs3lookup(cfile, component, &error);
188			break;
189		case NFS_V4:
190			lkp = nfs4lookup(cfile, component, &error);
191			break;
192		default:
193			printf("lookup: NFS Version %d not supported\n",
194			    cfile->version);
195			lkp = NULL;
196			break;
197		}
198
199		/*
200		 * Check for RPC error
201		 */
202		if (error == -1) {
203			printf("lookup: lookup RPC error\n");
204			return (error);
205		}
206
207		/*
208		 * Check for NFS error
209		 */
210		if (lkp == NULL) {
211			if ((error != NFSERR_NOENT) &&
212			    (error != NFS3ERR_NOENT) &&
213			    (error != NFS4ERR_NOENT)) {
214#ifdef DEBUG
215			dprintf("lookup: lkp is NULL with error %d\n", error);
216#endif
217				return (error);
218			}
219#ifdef DEBUG
220			dprintf("lookup: lkp is NULL with error %d\n", error);
221#endif
222			/*
223			 * File not found so set cached inode to -1
224			 */
225			set_dcache(mac_get_dev(), component, dino, -1);
226			return (error);
227		}
228
229		if (cdp = (struct nfs_file *)
230		    bkmem_alloc(sizeof (struct nfs_file))) {
231			/*
232			 *  Save this entry in cache for next time ...
233			 */
234			if (!cino)
235				cino = ++next_inum;
236			*cdp = *lkp;
237
238			set_dcache(mac_get_dev(), component, dino, cino);
239			set_icache(mac_get_dev(), cino, cdp,
240						sizeof (struct nfs_file));
241		} else {
242			/*
243			 *  Out of memory, clear cache keys so we don't get
244			 *  confused later.
245			 */
246			cino = 0;
247			cdp = lkp;
248		}
249	}
250	dino = cino;
251
252	/*
253	 * If we hit a symbolic link and there is more path to be
254	 * translated or this operation does not wish to apply
255	 * to a link, then place the contents of the link at the
256	 * front of the remaining pathname.
257	 */
258	if (cfile_is_lnk(cdp)) {
259		struct st_pathname linkpath;
260		static char path_tmp[NFS_MAXPATHLEN];	/* used for symlinks */
261		char *pathp;
262
263		linkpath.pn_buf = &path_tmp[0];
264
265		nlink++;
266		if (nlink > MAXSYMLINKS) {
267			error = ELOOP;
268			goto bad;
269		}
270		switch (cdp->version) {
271		case NFS_VERSION:
272			error = nfsgetsymlink(cdp, &pathp);
273			break;
274		case NFS_V3:
275			error = nfs3getsymlink(cdp, &pathp);
276			break;
277		case NFS_V4:
278			error = nfs4getsymlink(cdp, &pathp);
279			break;
280		default:
281			printf("getsymlink: NFS Version %d not supported\n",
282			    cdp->version);
283			error = ENOTSUP;
284			break;
285		}
286
287		if (error)
288			goto bad;
289
290		(void) stpn_get(pathp, &linkpath);
291
292		if (stpn_pathleft(&linkpath) == 0)
293			(void) stpn_set(&linkpath, ".");
294		error = stpn_combine(pnp, &linkpath); /* linkpath before pn */
295		if (error)
296			goto bad;
297		goto begin;
298	}
299
300	if (needroothandle) {
301		roothandle = *cdp;
302		needroothandle = FALSE;
303	}
304	*cfile = *cdp;
305
306skip:
307	/*
308	 * Skip to next component of the pathname.
309	 * If no more components, return last directory (if wanted)  and
310	 * last component (if wanted).
311	 */
312	if (stpn_pathleft(pnp) == 0) {
313		(void) stpn_set(pnp, component);
314		return (0);
315	}
316	/*
317	 * skip over slashes from end of last component
318	 */
319	stpn_skipslash(pnp);
320	goto next;
321bad:
322	/*
323	 * Error.
324	 */
325	return (error);
326}
327