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