xref: /illumos-gate/usr/src/stand/lib/fs/nfs/lookup.c (revision f3041bfa)
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 
63 static int root_inum = 1;	/* Dummy i-node number for root */
64 static 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 
73 static 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  */
80 int
81 lookup(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 
97 static int
98 stlookuppn(struct st_pathname *pnp, struct nfs_file *cfile,
99 bool_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;
109 begin:
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 
125 next:
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 		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 
306 skip:
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;
321 bad:
322 	/*
323 	 * Error.
324 	 */
325 	return (error);
326 }
327