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