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
lookup(char * pathname,struct nfs_file * cur_file,bool_t needroothandle)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
stlookuppn(struct st_pathname * pnp,struct nfs_file * cfile,bool_t needroothandle)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