| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: vfs_lookup.c,v 1.116 2009/06/29 05:00:14 dholland Exp $ */ | | 1 | /* $NetBSD: vfs_lookup.c,v 1.117 2009/08/09 03:28:35 dholland Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 1982, 1986, 1989, 1993 | | 4 | * Copyright (c) 1982, 1986, 1989, 1993 |
5 | * The Regents of the University of California. All rights reserved. | | 5 | * The Regents of the University of California. All rights reserved. |
6 | * (c) UNIX System Laboratories, Inc. | | 6 | * (c) UNIX System Laboratories, Inc. |
7 | * All or some portions of this file are derived from material licensed | | 7 | * All or some portions of this file are derived from material licensed |
8 | * to the University of California by American Telephone and Telegraph | | 8 | * to the University of California by American Telephone and Telegraph |
9 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with | | 9 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with |
10 | * the permission of UNIX System Laboratories, Inc. | | 10 | * the permission of UNIX System Laboratories, Inc. |
11 | * | | 11 | * |
12 | * Redistribution and use in source and binary forms, with or without | | 12 | * Redistribution and use in source and binary forms, with or without |
13 | * modification, are permitted provided that the following conditions | | 13 | * modification, are permitted provided that the following conditions |
14 | * are met: | | 14 | * are met: |
| @@ -27,27 +27,27 @@ | | | @@ -27,27 +27,27 @@ |
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | | 27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
34 | * SUCH DAMAGE. | | 34 | * SUCH DAMAGE. |
35 | * | | 35 | * |
36 | * @(#)vfs_lookup.c 8.10 (Berkeley) 5/27/95 | | 36 | * @(#)vfs_lookup.c 8.10 (Berkeley) 5/27/95 |
37 | */ | | 37 | */ |
38 | | | 38 | |
39 | #include <sys/cdefs.h> | | 39 | #include <sys/cdefs.h> |
40 | __KERNEL_RCSID(0, "$NetBSD: vfs_lookup.c,v 1.116 2009/06/29 05:00:14 dholland Exp $"); | | 40 | __KERNEL_RCSID(0, "$NetBSD: vfs_lookup.c,v 1.117 2009/08/09 03:28:35 dholland Exp $"); |
41 | | | 41 | |
42 | #include "opt_magiclinks.h" | | 42 | #include "opt_magiclinks.h" |
43 | | | 43 | |
44 | #include <sys/param.h> | | 44 | #include <sys/param.h> |
45 | #include <sys/systm.h> | | 45 | #include <sys/systm.h> |
46 | #include <sys/kernel.h> | | 46 | #include <sys/kernel.h> |
47 | #include <sys/syslimits.h> | | 47 | #include <sys/syslimits.h> |
48 | #include <sys/time.h> | | 48 | #include <sys/time.h> |
49 | #include <sys/namei.h> | | 49 | #include <sys/namei.h> |
50 | #include <sys/vnode.h> | | 50 | #include <sys/vnode.h> |
51 | #include <sys/mount.h> | | 51 | #include <sys/mount.h> |
52 | #include <sys/errno.h> | | 52 | #include <sys/errno.h> |
53 | #include <sys/filedesc.h> | | 53 | #include <sys/filedesc.h> |
| @@ -206,262 +206,404 @@ symlink_magic(struct proc *p, char *cp, | | | @@ -206,262 +206,404 @@ symlink_magic(struct proc *p, char *cp, |
206 | * | | 206 | * |
207 | * The segflg defines whether the name is to be copied from user | | 207 | * The segflg defines whether the name is to be copied from user |
208 | * space or kernel space. | | 208 | * space or kernel space. |
209 | * | | 209 | * |
210 | * Overall outline of namei: | | 210 | * Overall outline of namei: |
211 | * | | 211 | * |
212 | * copy in name | | 212 | * copy in name |
213 | * get starting directory | | 213 | * get starting directory |
214 | * while (!done && !error) { | | 214 | * while (!done && !error) { |
215 | * call lookup to search path. | | 215 | * call lookup to search path. |
216 | * if symbolic link, massage name in buffer and continue | | 216 | * if symbolic link, massage name in buffer and continue |
217 | * } | | 217 | * } |
218 | */ | | 218 | */ |
219 | int | | 219 | |
220 | namei(struct nameidata *ndp) | | 220 | /* |
221 | { | | 221 | * Internal state for a namei operation. |
222 | struct cwdinfo *cwdi; /* pointer to cwd state */ | | 222 | */ |
223 | char *cp; /* pointer into pathname argument */ | | 223 | struct namei_state { |
| | | 224 | struct nameidata *ndp; |
| | | 225 | struct componentname *cnp; |
| | | 226 | |
224 | struct vnode *dp; /* the directory we are searching */ | | 227 | struct vnode *dp; /* the directory we are searching */ |
225 | struct iovec aiov; /* uio for reading symbolic links */ | | 228 | }; |
226 | struct lwp *l = curlwp; /* thread doing namei() */ | | 229 | |
227 | struct uio auio; | | 230 | /* |
228 | int error; | | 231 | * Initialize the namei working state. |
229 | size_t linklen; | | 232 | */ |
230 | struct componentname *cnp = &ndp->ni_cnd; | | 233 | static void |
| | | 234 | namei_init(struct namei_state *state, struct nameidata *ndp) |
| | | 235 | { |
| | | 236 | state->ndp = ndp; |
| | | 237 | state->cnp = &ndp->ni_cnd; |
| | | 238 | |
| | | 239 | state->dp = NULL; |
| | | 240 | } |
| | | 241 | |
| | | 242 | /* |
| | | 243 | * Clean up the working namei state, leaving things ready for return |
| | | 244 | * from namei. |
| | | 245 | */ |
| | | 246 | static void |
| | | 247 | namei_cleanup(struct namei_state *state) |
| | | 248 | { |
| | | 249 | KASSERT(state->cnp == &state->ndp->ni_cnd); |
| | | 250 | |
| | | 251 | //KASSERT(state->dp == NULL); // not yet |
| | | 252 | |
| | | 253 | /* nothing for now */ |
| | | 254 | (void)state; |
| | | 255 | } |
| | | 256 | |
| | | 257 | ////////////////////////////// |
| | | 258 | |
| | | 259 | /* |
| | | 260 | * Start up namei. Early portion. |
| | | 261 | * |
| | | 262 | * This is divided from namei_start2 by the emul_retry: point. |
| | | 263 | */ |
| | | 264 | static void |
| | | 265 | namei_start1(struct namei_state *state) |
| | | 266 | { |
231 | | | 267 | |
232 | #ifdef DIAGNOSTIC | | 268 | #ifdef DIAGNOSTIC |
233 | if (!cnp->cn_cred) | | 269 | if (!state->cnp->cn_cred) |
234 | panic("namei: bad cred/proc"); | | 270 | panic("namei: bad cred/proc"); |
235 | if (cnp->cn_nameiop & (~OPMASK)) | | 271 | if (state->cnp->cn_nameiop & (~OPMASK)) |
236 | panic("namei: nameiop contaminated with flags"); | | 272 | panic("namei: nameiop contaminated with flags"); |
237 | if (cnp->cn_flags & OPMASK) | | 273 | if (state->cnp->cn_flags & OPMASK) |
238 | panic("namei: flags contaminated with nameiops"); | | 274 | panic("namei: flags contaminated with nameiops"); |
239 | #endif | | 275 | #endif |
240 | | | 276 | |
241 | /* | | 277 | /* |
242 | * Get a buffer for the name to be translated, and copy the | | 278 | * Get a buffer for the name to be translated, and copy the |
243 | * name into the buffer. | | 279 | * name into the buffer. |
244 | */ | | 280 | */ |
245 | if ((cnp->cn_flags & HASBUF) == 0) | | 281 | if ((state->cnp->cn_flags & HASBUF) == 0) |
246 | cnp->cn_pnbuf = PNBUF_GET(); | | 282 | state->cnp->cn_pnbuf = PNBUF_GET(); |
247 | emul_retry: | | 283 | } |
| | | 284 | |
| | | 285 | /* |
| | | 286 | * Start up namei. Copy the path, find the root dir and cwd, establish |
| | | 287 | * the starting directory for lookup, and lock it. |
| | | 288 | */ |
| | | 289 | static int |
| | | 290 | namei_start2(struct namei_state *state) |
| | | 291 | { |
| | | 292 | struct nameidata *ndp = state->ndp; |
| | | 293 | struct componentname *cnp = state->cnp; |
| | | 294 | |
| | | 295 | struct cwdinfo *cwdi; /* pointer to cwd state */ |
| | | 296 | struct lwp *self = curlwp; /* thread doing namei() */ |
| | | 297 | int error; |
| | | 298 | |
248 | if (ndp->ni_segflg == UIO_SYSSPACE) | | 299 | if (ndp->ni_segflg == UIO_SYSSPACE) |
249 | error = copystr(ndp->ni_dirp, cnp->cn_pnbuf, | | 300 | error = copystr(ndp->ni_dirp, cnp->cn_pnbuf, |
250 | MAXPATHLEN, &ndp->ni_pathlen); | | 301 | MAXPATHLEN, &ndp->ni_pathlen); |
251 | else | | 302 | else |
252 | error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf, | | 303 | error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf, |
253 | MAXPATHLEN, &ndp->ni_pathlen); | | 304 | MAXPATHLEN, &ndp->ni_pathlen); |
254 | | | 305 | |
255 | /* | | 306 | /* |
256 | * POSIX.1 requirement: "" is not a valid file name. | | 307 | * POSIX.1 requirement: "" is not a valid file name. |
257 | */ | | 308 | */ |
258 | if (!error && ndp->ni_pathlen == 1) | | 309 | if (!error && ndp->ni_pathlen == 1) |
259 | error = ENOENT; | | 310 | error = ENOENT; |
260 | | | 311 | |
261 | if (error) { | | 312 | if (error) { |
262 | PNBUF_PUT(cnp->cn_pnbuf); | | 313 | PNBUF_PUT(cnp->cn_pnbuf); |
263 | ndp->ni_vp = NULL; | | 314 | ndp->ni_vp = NULL; |
264 | return (error); | | 315 | return (error); |
265 | } | | 316 | } |
266 | ndp->ni_loopcnt = 0; | | 317 | ndp->ni_loopcnt = 0; |
267 | | | 318 | |
268 | /* | | 319 | /* |
269 | * Get root directory for the translation. | | 320 | * Get root directory for the translation. |
270 | */ | | 321 | */ |
271 | cwdi = l->l_proc->p_cwdi; | | 322 | cwdi = self->l_proc->p_cwdi; |
272 | rw_enter(&cwdi->cwdi_lock, RW_READER); | | 323 | rw_enter(&cwdi->cwdi_lock, RW_READER); |
273 | dp = cwdi->cwdi_rdir; | | 324 | state->dp = cwdi->cwdi_rdir; |
274 | if (dp == NULL) | | 325 | if (state->dp == NULL) |
275 | dp = rootvnode; | | 326 | state->dp = rootvnode; |
276 | ndp->ni_rootdir = dp; | | 327 | ndp->ni_rootdir = state->dp; |
277 | | | 328 | |
278 | /* | | 329 | /* |
279 | * Check if starting from root directory or current directory. | | 330 | * Check if starting from root directory or current directory. |
280 | */ | | 331 | */ |
281 | if (cnp->cn_pnbuf[0] == '/') { | | 332 | if (cnp->cn_pnbuf[0] == '/') { |
282 | if (cnp->cn_flags & TRYEMULROOT) { | | 333 | if (cnp->cn_flags & TRYEMULROOT) { |
283 | if (cnp->cn_flags & EMULROOTSET) { | | 334 | if (cnp->cn_flags & EMULROOTSET) { |
284 | /* Called from (eg) emul_find_interp() */ | | 335 | /* Called from (eg) emul_find_interp() */ |
285 | dp = ndp->ni_erootdir; | | 336 | state->dp = ndp->ni_erootdir; |
286 | } else { | | 337 | } else { |
287 | if (cwdi->cwdi_edir == NULL | | 338 | if (cwdi->cwdi_edir == NULL |
288 | || (cnp->cn_pnbuf[1] == '.' | | 339 | || (cnp->cn_pnbuf[1] == '.' |
289 | && cnp->cn_pnbuf[2] == '.' | | 340 | && cnp->cn_pnbuf[2] == '.' |
290 | && cnp->cn_pnbuf[3] == '/')) { | | 341 | && cnp->cn_pnbuf[3] == '/')) { |
291 | ndp->ni_erootdir = NULL; | | 342 | ndp->ni_erootdir = NULL; |
292 | } else { | | 343 | } else { |
293 | dp = cwdi->cwdi_edir; | | 344 | state->dp = cwdi->cwdi_edir; |
294 | ndp->ni_erootdir = dp; | | 345 | ndp->ni_erootdir = state->dp; |
295 | } | | 346 | } |
296 | } | | 347 | } |
297 | } else { | | 348 | } else { |
298 | ndp->ni_erootdir = NULL; | | 349 | ndp->ni_erootdir = NULL; |
299 | if (cnp->cn_flags & NOCHROOT) | | 350 | if (cnp->cn_flags & NOCHROOT) |
300 | dp = ndp->ni_rootdir = rootvnode; | | 351 | state->dp = ndp->ni_rootdir = rootvnode; |
301 | } | | 352 | } |
302 | } else { | | 353 | } else { |
303 | dp = cwdi->cwdi_cdir; | | 354 | state->dp = cwdi->cwdi_cdir; |
304 | ndp->ni_erootdir = NULL; | | 355 | ndp->ni_erootdir = NULL; |
305 | } | | 356 | } |
306 | VREF(dp); | | 357 | VREF(state->dp); |
307 | rw_exit(&cwdi->cwdi_lock); | | 358 | rw_exit(&cwdi->cwdi_lock); |
308 | | | 359 | |
| | | 360 | /* |
| | | 361 | * Ktrace it. |
| | | 362 | */ |
309 | if (ktrpoint(KTR_NAMEI)) { | | 363 | if (ktrpoint(KTR_NAMEI)) { |
310 | if (ndp->ni_erootdir != NULL) { | | 364 | if (ndp->ni_erootdir != NULL) { |
311 | /* | | 365 | /* |
312 | * To make any sense, the trace entry need to have the | | 366 | * To make any sense, the trace entry need to have the |
313 | * text of the emulation path prepended. | | 367 | * text of the emulation path prepended. |
314 | * Usually we can get this from the current process, | | 368 | * Usually we can get this from the current process, |
315 | * but when called from emul_find_interp() it is only | | 369 | * but when called from emul_find_interp() it is only |
316 | * in the exec_package - so we get it passed in ni_next | | 370 | * in the exec_package - so we get it passed in ni_next |
317 | * (this is a hack). | | 371 | * (this is a hack). |
318 | */ | | 372 | */ |
319 | const char *emul_path; | | 373 | const char *emul_path; |
320 | if (cnp->cn_flags & EMULROOTSET) | | 374 | if (cnp->cn_flags & EMULROOTSET) |
321 | emul_path = ndp->ni_next; | | 375 | emul_path = ndp->ni_next; |
322 | else | | 376 | else |
323 | emul_path = l->l_proc->p_emul->e_path; | | 377 | emul_path = self->l_proc->p_emul->e_path; |
324 | ktrnamei2(emul_path, strlen(emul_path), | | 378 | ktrnamei2(emul_path, strlen(emul_path), |
325 | cnp->cn_pnbuf, ndp->ni_pathlen); | | 379 | cnp->cn_pnbuf, ndp->ni_pathlen); |
326 | } else | | 380 | } else |
327 | ktrnamei(cnp->cn_pnbuf, ndp->ni_pathlen); | | 381 | ktrnamei(cnp->cn_pnbuf, ndp->ni_pathlen); |
328 | } | | 382 | } |
329 | | | 383 | |
330 | vn_lock(dp, LK_EXCLUSIVE | LK_RETRY); | | 384 | vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY); |
| | | 385 | |
| | | 386 | return 0; |
| | | 387 | } |
| | | 388 | |
| | | 389 | /* |
| | | 390 | * Undo namei_start: unlock and release the current lookup directory, |
| | | 391 | * and discard the path buffer. |
| | | 392 | */ |
| | | 393 | static void |
| | | 394 | namei_end(struct namei_state *state) |
| | | 395 | { |
| | | 396 | vput(state->dp); |
| | | 397 | PNBUF_PUT(state->cnp->cn_pnbuf); |
| | | 398 | //state->cnp->cn_pnbuf = NULL; // not yet (just in case) (XXX) |
| | | 399 | } |
| | | 400 | |
| | | 401 | /* |
| | | 402 | * Check for being at a symlink. |
| | | 403 | */ |
| | | 404 | static inline int |
| | | 405 | namei_atsymlink(struct namei_state *state) |
| | | 406 | { |
| | | 407 | return (state->cnp->cn_flags & ISSYMLINK) != 0; |
| | | 408 | } |
| | | 409 | |
| | | 410 | /* |
| | | 411 | * Follow a symlink. |
| | | 412 | */ |
| | | 413 | static inline int |
| | | 414 | namei_follow(struct namei_state *state) |
| | | 415 | { |
| | | 416 | struct nameidata *ndp = state->ndp; |
| | | 417 | struct componentname *cnp = state->cnp; |
| | | 418 | |
| | | 419 | struct lwp *self = curlwp; /* thread doing namei() */ |
| | | 420 | struct iovec aiov; /* uio for reading symbolic links */ |
| | | 421 | struct uio auio; |
| | | 422 | char *cp; /* pointer into pathname argument */ |
| | | 423 | size_t linklen; |
| | | 424 | int error; |
| | | 425 | |
| | | 426 | if (ndp->ni_loopcnt++ >= MAXSYMLINKS) { |
| | | 427 | return ELOOP; |
| | | 428 | } |
| | | 429 | if (ndp->ni_vp->v_mount->mnt_flag & MNT_SYMPERM) { |
| | | 430 | error = VOP_ACCESS(ndp->ni_vp, VEXEC, cnp->cn_cred); |
| | | 431 | if (error != 0) |
| | | 432 | return error; |
| | | 433 | } |
| | | 434 | if (ndp->ni_pathlen > 1) |
| | | 435 | cp = PNBUF_GET(); |
| | | 436 | else |
| | | 437 | cp = cnp->cn_pnbuf; |
| | | 438 | aiov.iov_base = cp; |
| | | 439 | aiov.iov_len = MAXPATHLEN; |
| | | 440 | auio.uio_iov = &aiov; |
| | | 441 | auio.uio_iovcnt = 1; |
| | | 442 | auio.uio_offset = 0; |
| | | 443 | auio.uio_rw = UIO_READ; |
| | | 444 | auio.uio_resid = MAXPATHLEN; |
| | | 445 | UIO_SETUP_SYSSPACE(&auio); |
| | | 446 | error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred); |
| | | 447 | if (error) { |
| | | 448 | badlink: |
| | | 449 | if (ndp->ni_pathlen > 1) |
| | | 450 | PNBUF_PUT(cp); |
| | | 451 | return error; |
| | | 452 | } |
| | | 453 | linklen = MAXPATHLEN - auio.uio_resid; |
| | | 454 | if (linklen == 0) { |
| | | 455 | error = ENOENT; |
| | | 456 | goto badlink; |
| | | 457 | } |
| | | 458 | |
| | | 459 | /* |
| | | 460 | * Do symlink substitution, if appropriate, and |
| | | 461 | * check length for potential overflow. |
| | | 462 | */ |
| | | 463 | if ((vfs_magiclinks && |
| | | 464 | symlink_magic(self->l_proc, cp, &linklen)) || |
| | | 465 | (linklen + ndp->ni_pathlen >= MAXPATHLEN)) { |
| | | 466 | error = ENAMETOOLONG; |
| | | 467 | goto badlink; |
| | | 468 | } |
| | | 469 | if (ndp->ni_pathlen > 1) { |
| | | 470 | memcpy(cp + linklen, ndp->ni_next, ndp->ni_pathlen); |
| | | 471 | PNBUF_PUT(cnp->cn_pnbuf); |
| | | 472 | cnp->cn_pnbuf = cp; |
| | | 473 | } else |
| | | 474 | cnp->cn_pnbuf[linklen] = '\0'; |
| | | 475 | ndp->ni_pathlen += linklen; |
| | | 476 | vput(ndp->ni_vp); |
| | | 477 | state->dp = ndp->ni_dvp; |
| | | 478 | |
| | | 479 | /* |
| | | 480 | * Check if root directory should replace current directory. |
| | | 481 | */ |
| | | 482 | if (cnp->cn_pnbuf[0] == '/') { |
| | | 483 | vput(state->dp); |
| | | 484 | /* Keep absolute symbolic links inside emulation root */ |
| | | 485 | state->dp = ndp->ni_erootdir; |
| | | 486 | if (state->dp == NULL || (cnp->cn_pnbuf[1] == '.' |
| | | 487 | && cnp->cn_pnbuf[2] == '.' |
| | | 488 | && cnp->cn_pnbuf[3] == '/')) { |
| | | 489 | ndp->ni_erootdir = NULL; |
| | | 490 | state->dp = ndp->ni_rootdir; |
| | | 491 | } |
| | | 492 | VREF(state->dp); |
| | | 493 | vn_lock(state->dp, LK_EXCLUSIVE | LK_RETRY); |
| | | 494 | } |
| | | 495 | |
| | | 496 | return 0; |
| | | 497 | } |
| | | 498 | |
| | | 499 | ////////////////////////////// |
| | | 500 | |
| | | 501 | static int |
| | | 502 | do_namei(struct namei_state *state) |
| | | 503 | { |
| | | 504 | int error; |
| | | 505 | |
| | | 506 | struct nameidata *ndp = state->ndp; |
| | | 507 | struct componentname *cnp = state->cnp; |
| | | 508 | |
| | | 509 | KASSERT(cnp == &ndp->ni_cnd); |
| | | 510 | |
| | | 511 | namei_start1(state); |
| | | 512 | |
| | | 513 | emul_retry: |
| | | 514 | |
| | | 515 | error = namei_start2(state); |
| | | 516 | if (error) { |
| | | 517 | return error; |
| | | 518 | } |
| | | 519 | |
331 | /* Loop through symbolic links */ | | 520 | /* Loop through symbolic links */ |
332 | for (;;) { | | 521 | for (;;) { |
333 | if (!dp->v_mount) { | | 522 | if (!state->dp->v_mount) { |
334 | /* Give up if the directory is no longer mounted */ | | 523 | /* Give up if the directory is no longer mounted */ |
335 | vput(dp); | | 524 | namei_end(state); |
336 | PNBUF_PUT(cnp->cn_pnbuf); | | | |
337 | return (ENOENT); | | 525 | return (ENOENT); |
338 | } | | 526 | } |
339 | cnp->cn_nameptr = cnp->cn_pnbuf; | | 527 | cnp->cn_nameptr = cnp->cn_pnbuf; |
340 | ndp->ni_startdir = dp; | | 528 | ndp->ni_startdir = state->dp; |
341 | error = lookup(ndp); | | 529 | error = lookup(ndp); |
342 | if (error != 0) { | | 530 | if (error != 0) { |
| | | 531 | /* XXX this should use namei_end() */ |
343 | if (ndp->ni_dvp) { | | 532 | if (ndp->ni_dvp) { |
344 | vput(ndp->ni_dvp); | | 533 | vput(ndp->ni_dvp); |
345 | } | | 534 | } |
346 | if (ndp->ni_erootdir != NULL) { | | 535 | if (ndp->ni_erootdir != NULL) { |
347 | /* Retry the whole thing from the normal root */ | | 536 | /* Retry the whole thing from the normal root */ |
348 | cnp->cn_flags &= ~TRYEMULROOT; | | 537 | cnp->cn_flags &= ~TRYEMULROOT; |
349 | goto emul_retry; | | 538 | goto emul_retry; |
350 | } | | 539 | } |
351 | PNBUF_PUT(cnp->cn_pnbuf); | | 540 | PNBUF_PUT(cnp->cn_pnbuf); |
352 | return (error); | | 541 | return (error); |
353 | } | | 542 | } |
354 | | | 543 | |
355 | /* | | 544 | /* |
356 | * Check for symbolic link | | 545 | * Check for symbolic link |
357 | */ | | 546 | */ |
358 | if ((cnp->cn_flags & ISSYMLINK) == 0) { | | 547 | if (namei_atsymlink(state)) { |
359 | if ((cnp->cn_flags & LOCKPARENT) == 0 && ndp->ni_dvp) { | | 548 | error = namei_follow(state); |
360 | if (ndp->ni_dvp == ndp->ni_vp) { | | 549 | if (error) { |
361 | vrele(ndp->ni_dvp); | | 550 | KASSERT(ndp->ni_dvp != ndp->ni_vp); |
362 | } else { | | 551 | vput(ndp->ni_dvp); |
363 | vput(ndp->ni_dvp); | | 552 | vput(ndp->ni_vp); |
364 | } | | 553 | ndp->ni_vp = NULL; |
365 | } | | | |
366 | if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0) { | | | |
367 | PNBUF_PUT(cnp->cn_pnbuf); | | 554 | PNBUF_PUT(cnp->cn_pnbuf); |
368 | #if defined(DIAGNOSTIC) | | 555 | return error; |
369 | cnp->cn_pnbuf = NULL; | | | |
370 | #endif /* defined(DIAGNOSTIC) */ | | | |
371 | } else { | | | |
372 | cnp->cn_flags |= HASBUF; | | | |
373 | } | | 556 | } |
374 | return (0); | | | |
375 | } | | 557 | } |
376 | | | 558 | else { |
377 | if (ndp->ni_loopcnt++ >= MAXSYMLINKS) { | | | |
378 | error = ELOOP; | | | |
379 | break; | | 559 | break; |
380 | } | | 560 | } |
381 | if (ndp->ni_vp->v_mount->mnt_flag & MNT_SYMPERM) { | | 561 | } |
382 | error = VOP_ACCESS(ndp->ni_vp, VEXEC, cnp->cn_cred); | | | |
383 | if (error != 0) | | | |
384 | break; | | | |
385 | } | | | |
386 | if (ndp->ni_pathlen > 1) | | | |
387 | cp = PNBUF_GET(); | | | |
388 | else | | | |
389 | cp = cnp->cn_pnbuf; | | | |
390 | aiov.iov_base = cp; | | | |
391 | aiov.iov_len = MAXPATHLEN; | | | |
392 | auio.uio_iov = &aiov; | | | |
393 | auio.uio_iovcnt = 1; | | | |
394 | auio.uio_offset = 0; | | | |
395 | auio.uio_rw = UIO_READ; | | | |
396 | auio.uio_resid = MAXPATHLEN; | | | |
397 | UIO_SETUP_SYSSPACE(&auio); | | | |
398 | error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred); | | | |
399 | if (error) { | | | |
400 | badlink: | | | |
401 | if (ndp->ni_pathlen > 1) | | | |
402 | PNBUF_PUT(cp); | | | |
403 | break; | | | |
404 | } | | | |
405 | linklen = MAXPATHLEN - auio.uio_resid; | | | |
406 | if (linklen == 0) { | | | |
407 | error = ENOENT; | | | |
408 | goto badlink; | | | |
409 | } | | | |
410 | | | 562 | |
411 | /* | | 563 | /* |
412 | * Do symlink substitution, if appropriate, and | | 564 | * Done |
413 | * check length for potential overflow. | | 565 | */ |
414 | */ | | | |
415 | if ((vfs_magiclinks && | | | |
416 | symlink_magic(l->l_proc, cp, &linklen)) || | | | |
417 | (linklen + ndp->ni_pathlen >= MAXPATHLEN)) { | | | |
418 | error = ENAMETOOLONG; | | | |
419 | goto badlink; | | | |
420 | } | | | |
421 | if (ndp->ni_pathlen > 1) { | | | |
422 | memcpy(cp + linklen, ndp->ni_next, ndp->ni_pathlen); | | | |
423 | PNBUF_PUT(cnp->cn_pnbuf); | | | |
424 | cnp->cn_pnbuf = cp; | | | |
425 | } else | | | |
426 | cnp->cn_pnbuf[linklen] = '\0'; | | | |
427 | ndp->ni_pathlen += linklen; | | | |
428 | vput(ndp->ni_vp); | | | |
429 | dp = ndp->ni_dvp; | | | |
430 | | | 566 | |
431 | /* | | 567 | if ((cnp->cn_flags & LOCKPARENT) == 0 && ndp->ni_dvp) { |
432 | * Check if root directory should replace current directory. | | 568 | if (ndp->ni_dvp == ndp->ni_vp) { |
433 | */ | | 569 | vrele(ndp->ni_dvp); |
434 | if (cnp->cn_pnbuf[0] == '/') { | | 570 | } else { |
435 | vput(dp); | | 571 | vput(ndp->ni_dvp); |
436 | /* Keep absolute symbolic links inside emulation root */ | | | |
437 | dp = ndp->ni_erootdir; | | | |
438 | if (dp == NULL || (cnp->cn_pnbuf[1] == '.' | | | |
439 | && cnp->cn_pnbuf[2] == '.' | | | |
440 | && cnp->cn_pnbuf[3] == '/')) { | | | |
441 | ndp->ni_erootdir = NULL; | | | |
442 | dp = ndp->ni_rootdir; | | | |
443 | } | | | |
444 | VREF(dp); | | | |
445 | vn_lock(dp, LK_EXCLUSIVE | LK_RETRY); | | | |
446 | } | | 572 | } |
447 | } | | 573 | } |
448 | /* Failed to process a symbolic link */ | | 574 | if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0) { |
449 | KASSERT(ndp->ni_dvp != ndp->ni_vp); | | 575 | PNBUF_PUT(cnp->cn_pnbuf); |
450 | vput(ndp->ni_dvp); | | 576 | #if defined(DIAGNOSTIC) |
451 | vput(ndp->ni_vp); | | 577 | cnp->cn_pnbuf = NULL; |
452 | ndp->ni_vp = NULL; | | 578 | #endif /* defined(DIAGNOSTIC) */ |
453 | PNBUF_PUT(cnp->cn_pnbuf); | | 579 | } else { |
454 | return (error); | | 580 | cnp->cn_flags |= HASBUF; |
| | | 581 | } |
| | | 582 | |
| | | 583 | return 0; |
| | | 584 | } |
| | | 585 | |
| | | 586 | int |
| | | 587 | namei(struct nameidata *ndp) |
| | | 588 | { |
| | | 589 | struct namei_state state; |
| | | 590 | int error; |
| | | 591 | |
| | | 592 | namei_init(&state, ndp); |
| | | 593 | error = do_namei(&state); |
| | | 594 | namei_cleanup(&state); |
| | | 595 | |
| | | 596 | return error; |
455 | } | | 597 | } |
456 | | | 598 | |
457 | /* | | 599 | /* |
458 | * Determine the namei hash (for cn_hash) for name. | | 600 | * Determine the namei hash (for cn_hash) for name. |
459 | * If *ep != NULL, hash from name to ep-1. | | 601 | * If *ep != NULL, hash from name to ep-1. |
460 | * If *ep == NULL, hash from name until the first NUL or '/', and | | 602 | * If *ep == NULL, hash from name until the first NUL or '/', and |
461 | * return the location of this termination character in *ep. | | 603 | * return the location of this termination character in *ep. |
462 | * | | 604 | * |
463 | * This function returns an equivalent hash to the MI hash32_strn(). | | 605 | * This function returns an equivalent hash to the MI hash32_strn(). |
464 | * The latter isn't used because in the *ep == NULL case, determining | | 606 | * The latter isn't used because in the *ep == NULL case, determining |
465 | * the length of the string to the first NUL or `/' and then calling | | 607 | * the length of the string to the first NUL or `/' and then calling |
466 | * hash32_strn() involves unnecessary double-handling of the data. | | 608 | * hash32_strn() involves unnecessary double-handling of the data. |
467 | */ | | 609 | */ |