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