]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_lookup.c
e01cf96ef1b84e37cfb26cb3adcfd226bbb2fd06
[apple/xnu.git] / bsd / hfs / hfs_lookup.c
1 /*
2 * Copyright (c) 1999-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30 /*
31 * Copyright (c) 1989, 1993
32 * The Regents of the University of California. All rights reserved.
33 * (c) UNIX System Laboratories, Inc.
34 * All or some portions of this file are derived from material licensed
35 * to the University of California by American Telephone and Telegraph
36 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
37 * the permission of UNIX System Laboratories, Inc.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 * 3. All advertising materials mentioning features or use of this software
48 * must display the following acknowledgement:
49 * This product includes software developed by the University of
50 * California, Berkeley and its contributors.
51 * 4. Neither the name of the University nor the names of its contributors
52 * may be used to endorse or promote products derived from this software
53 * without specific prior written permission.
54 *
55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 * SUCH DAMAGE.
66 *
67 * @(#)hfs_lookup.c 1.0
68 * derived from @(#)ufs_lookup.c 8.15 (Berkeley) 6/16/95
69 *
70 * (c) 1998-1999 Apple Computer, Inc. All Rights Reserved
71 * (c) 1990, 1992 NeXT Computer, Inc. All Rights Reserved
72 *
73 *
74 * hfs_lookup.c -- code to handle directory traversal on HFS/HFS+ volume
75 */
76
77 #include <sys/param.h>
78 #include <sys/file.h>
79 #include <sys/mount.h>
80 #include <sys/vnode.h>
81 #include <sys/malloc.h>
82 #include <sys/paths.h>
83 #include <sys/kdebug.h>
84 #include <sys/kauth.h>
85
86 #include "hfs.h"
87 #include "hfs_catalog.h"
88 #include "hfs_cnode.h"
89
90 #define LEGACY_FORK_NAMES 1
91
92 static int forkcomponent(struct componentname *cnp, int *rsrcfork);
93
94 #define _PATH_DATAFORKSPEC "/..namedfork/data"
95
96 #if LEGACY_FORK_NAMES
97 #define LEGACY_RSRCFORKSPEC "/rsrc"
98 #endif
99
100 /*
101 * FROM FREEBSD 3.1
102 * Convert a component of a pathname into a pointer to a locked cnode.
103 * This is a very central and rather complicated routine.
104 * If the file system is not maintained in a strict tree hierarchy,
105 * this can result in a deadlock situation (see comments in code below).
106 *
107 * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
108 * on whether the name is to be looked up, created, renamed, or deleted.
109 * When CREATE, RENAME, or DELETE is specified, information usable in
110 * creating, renaming, or deleting a directory entry may be calculated.
111 * Notice that these are the only operations that can affect the directory of the target.
112 *
113 * LOCKPARENT and WANTPARENT actually refer to the parent of the last item,
114 * so if ISLASTCN is not set, they should be ignored. Also they are mutually exclusive, or
115 * WANTPARENT really implies DONTLOCKPARENT. Either of them set means that the calling
116 * routine wants to access the parent of the target, locked or unlocked.
117 *
118 * Keeping the parent locked as long as possible protects from other processes
119 * looking up the same item, so it has to be locked until the cnode is totally finished
120 *
121 * hfs_cache_lookup() performs the following for us:
122 * check that it is a directory
123 * check accessibility of directory
124 * check for modification attempts on read-only mounts
125 * if name found in cache
126 * if at end of path and deleting or creating
127 * drop it
128 * else
129 * return name.
130 * return hfs_lookup()
131 *
132 * Overall outline of hfs_lookup:
133 *
134 * handle simple cases of . and ..
135 * search for name in directory, to found or notfound
136 * notfound:
137 * if creating, return locked directory, leaving info on available slots
138 * else return error
139 * found:
140 * if at end of path and deleting, return information to allow delete
141 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
142 * cnode and return info to allow rewrite
143 * if not at end, add name to cache; if at end and neither creating
144 * nor deleting, add name to cache
145 */
146
147
148 /*
149 * Lookup *cnp in directory *dvp, return it in *vpp.
150 * **vpp is held on exit.
151 * We create a cnode for the file, but we do NOT open the file here.
152
153 #% lookup dvp L ? ?
154 #% lookup vpp - L -
155
156 IN struct vnode *dvp - Parent node of file;
157 INOUT struct vnode **vpp - node of target file, its a new node if
158 the target vnode did not exist;
159 IN struct componentname *cnp - Name of file;
160
161 * When should we lock parent_hp in here ??
162 */
163 static int
164 hfs_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, vfs_context_t context, int *cnode_locked)
165 {
166 struct cnode *dcp; /* cnode for directory being searched */
167 struct vnode *tvp; /* target vnode */
168 struct hfsmount *hfsmp;
169 kauth_cred_t cred;
170 struct proc *p;
171 int wantrsrc = 0;
172 int forknamelen = 0;
173 int flags;
174 int nameiop;
175 int retval = 0;
176 int isDot;
177 struct cat_desc desc;
178 struct cat_desc cndesc;
179 struct cat_attr attr;
180 struct cat_fork fork;
181 int lockflags;
182
183 dcp = VTOC(dvp);
184 hfsmp = VTOHFS(dvp);
185 *vpp = NULL;
186 *cnode_locked = 0;
187 isDot = FALSE;
188 tvp = NULL;
189 nameiop = cnp->cn_nameiop;
190 flags = cnp->cn_flags;
191 bzero(&desc, sizeof(desc));
192
193 cred = vfs_context_ucred(context);
194 p = vfs_context_proc(context);
195
196 /*
197 * First check to see if it is a . or .., else look it up.
198 */
199 if (flags & ISDOTDOT) { /* Wanting the parent */
200 cnp->cn_flags &= ~MAKEENTRY;
201 goto found; /* .. is always defined */
202 } else if ((cnp->cn_nameptr[0] == '.') && (cnp->cn_namelen == 1)) {
203 isDot = TRUE;
204 cnp->cn_flags &= ~MAKEENTRY;
205 goto found; /* We always know who we are */
206 } else {
207 /* Check fork suffix to see if we want the resource fork */
208 forknamelen = forkcomponent(cnp, &wantrsrc);
209
210 /* Resource fork names are not cached. */
211 if (wantrsrc)
212 cnp->cn_flags &= ~MAKEENTRY;
213
214 if (hfs_lock(dcp, HFS_EXCLUSIVE_LOCK) != 0) {
215 goto notfound;
216 }
217
218 /* No need to go to catalog if there are no children */
219 if (dcp->c_entries == 0) {
220 hfs_unlock(dcp);
221 goto notfound;
222 }
223
224 bzero(&cndesc, sizeof(cndesc));
225 cndesc.cd_nameptr = cnp->cn_nameptr;
226 cndesc.cd_namelen = cnp->cn_namelen;
227 cndesc.cd_parentcnid = dcp->c_cnid;
228 cndesc.cd_hint = dcp->c_childhint;
229
230 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
231
232 retval = cat_lookup(hfsmp, &cndesc, wantrsrc, &desc, &attr, &fork, NULL);
233
234 hfs_systemfile_unlock(hfsmp, lockflags);
235
236 if (retval == 0) {
237 dcp->c_childhint = desc.cd_hint;
238 hfs_unlock(dcp);
239 goto found;
240 }
241 hfs_unlock(dcp);
242 notfound:
243 /* ENAMETOOLONG supersedes other errors */
244 if (((nameiop != CREATE) && (nameiop != RENAME)) &&
245 (retval != ENAMETOOLONG) &&
246 (cnp->cn_namelen > kHFSPlusMaxFileNameChars)) {
247 retval = ENAMETOOLONG;
248 } else if (retval == 0) {
249 retval = ENOENT;
250 }
251 /*
252 * This is a non-existing entry
253 *
254 * If creating, and at end of pathname and current
255 * directory has not been removed, then can consider
256 * allowing file to be created.
257 */
258 if ((nameiop == CREATE || nameiop == RENAME ||
259 (nameiop == DELETE &&
260 (cnp->cn_flags & DOWHITEOUT) &&
261 (cnp->cn_flags & ISWHITEOUT))) &&
262 (flags & ISLASTCN) &&
263 (retval == ENOENT)) {
264 retval = EJUSTRETURN;
265 goto exit;
266 }
267 /*
268 * Insert name into cache (as non-existent) if appropriate.
269 *
270 * Only done for case-sensitive HFS+ volumes.
271 */
272 if ((retval == ENOENT) &&
273 (hfsmp->hfs_flags & HFS_CASE_SENSITIVE) &&
274 (cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) {
275 cache_enter(dvp, NULL, cnp);
276 }
277 goto exit;
278 }
279
280 found:
281 /*
282 * Process any fork specifiers
283 */
284 if (forknamelen && S_ISREG(attr.ca_mode)) {
285 /* fork names are only for lookups */
286 if ((nameiop != LOOKUP) && (nameiop != CREATE)) {
287 retval = EPERM;
288 goto exit;
289 }
290 cnp->cn_consume = forknamelen;
291 flags |= ISLASTCN;
292 } else {
293 wantrsrc = 0;
294 forknamelen = 0;
295 }
296 if (flags & ISLASTCN) {
297 switch(nameiop) {
298 case DELETE:
299 cnp->cn_flags &= ~MAKEENTRY;
300 break;
301
302 case RENAME:
303 cnp->cn_flags &= ~MAKEENTRY;
304 if (isDot) {
305 retval = EISDIR;
306 goto exit;
307 }
308 break;
309 }
310 }
311
312 if (isDot) {
313 if ((retval = vnode_get(dvp)))
314 goto exit;
315 *vpp = dvp;
316 } else if (flags & ISDOTDOT) {
317 if ((retval = hfs_vget(hfsmp, dcp->c_parentcnid, &tvp, 0)))
318 goto exit;
319 *cnode_locked = 1;
320 *vpp = tvp;
321 } else {
322 int type = (attr.ca_mode & S_IFMT);
323
324 if (!(flags & ISLASTCN) && (type != S_IFDIR) && (type != S_IFLNK)) {
325 retval = ENOTDIR;
326 goto exit;
327 }
328
329 /* Names with composed chars are not cached. */
330 if (cnp->cn_namelen != desc.cd_namelen)
331 cnp->cn_flags &= ~MAKEENTRY;
332
333 /* Resource fork vnode names include the fork specifier. */
334 if (wantrsrc && (flags & ISLASTCN))
335 cnp->cn_namelen += forknamelen;
336
337 retval = hfs_getnewvnode(hfsmp, dvp, cnp, &desc, wantrsrc, &attr, &fork, &tvp);
338
339 if (wantrsrc && (flags & ISLASTCN))
340 cnp->cn_namelen -= forknamelen;
341
342 if (retval)
343 goto exit;
344 *cnode_locked = 1;
345 *vpp = tvp;
346 }
347 exit:
348 cat_releasedesc(&desc);
349 return (retval);
350 }
351
352
353
354 /*
355 * Name caching works as follows:
356 *
357 * Names found by directory scans are retained in a cache
358 * for future reference. It is managed LRU, so frequently
359 * used names will hang around. Cache is indexed by hash value
360 * obtained from (vp, name) where vp refers to the directory
361 * containing name.
362 *
363 * If it is a "negative" entry, (i.e. for a name that is known NOT to
364 * exist) the vnode pointer will be NULL.
365 *
366 * Upon reaching the last segment of a path, if the reference
367 * is for DELETE, or NOCACHE is set (rewrite), and the
368 * name is located in the cache, it will be dropped.
369 *
370 */
371
372 #define S_IXALL 0000111
373
374 __private_extern__
375 int
376 hfs_vnop_lookup(struct vnop_lookup_args *ap)
377 {
378 struct vnode *dvp = ap->a_dvp;
379 struct vnode *vp;
380 struct cnode *cp;
381 struct cnode *dcp;
382 int error;
383 struct vnode **vpp = ap->a_vpp;
384 struct componentname *cnp = ap->a_cnp;
385 int flags = cnp->cn_flags;
386 int cnode_locked;
387
388 *vpp = NULL;
389 dcp = VTOC(dvp);
390
391 /*
392 * Lookup an entry in the cache
393 *
394 * If the lookup succeeds, the vnode is returned in *vpp,
395 * and a status of -1 is returned.
396 *
397 * If the lookup determines that the name does not exist
398 * (negative cacheing), a status of ENOENT is returned.
399 *
400 * If the lookup fails, a status of zero is returned.
401 */
402 error = cache_lookup(dvp, vpp, cnp);
403 if (error != -1) {
404 if (error == ENOENT) /* found a negative cache entry */
405 goto exit;
406 goto lookup; /* did not find it in the cache */
407 }
408 /*
409 * We have a name that matched
410 * cache_lookup returns the vp with an iocount reference already taken
411 */
412 error = 0;
413 vp = *vpp;
414
415 /*
416 * If this is a hard-link vnode then we need to update
417 * the name (of the link), the parent ID, the cnid, the
418 * text encoding and the catalog hint. This enables
419 * getattrlist calls to return the correct link info.
420 */
421 cp = VTOC(vp);
422
423 if ((flags & ISLASTCN) && (cp->c_flag & C_HARDLINK)) {
424 hfs_lock(cp, HFS_FORCE_LOCK);
425 if ((cp->c_parentcnid != VTOC(dvp)->c_cnid) ||
426 (bcmp(cnp->cn_nameptr, cp->c_desc.cd_nameptr, cp->c_desc.cd_namelen) != 0)) {
427 struct cat_desc desc;
428 int lockflags;
429
430 /*
431 * Get an updated descriptor
432 */
433 bzero(&desc, sizeof(desc));
434 desc.cd_nameptr = cnp->cn_nameptr;
435 desc.cd_namelen = cnp->cn_namelen;
436 desc.cd_parentcnid = VTOC(dvp)->c_cnid;
437 desc.cd_hint = VTOC(dvp)->c_childhint;
438
439 lockflags = hfs_systemfile_lock(VTOHFS(dvp), SFL_CATALOG, HFS_SHARED_LOCK);
440 if (cat_lookup(VTOHFS(vp), &desc, 0, &desc, NULL, NULL, NULL) == 0)
441 replace_desc(cp, &desc);
442 hfs_systemfile_unlock(VTOHFS(dvp), lockflags);
443 }
444 hfs_unlock(cp);
445 }
446 if (dvp != vp && !(flags & ISDOTDOT)) {
447 if ((flags & ISLASTCN) == 0 && vnode_isreg(vp)) {
448 int wantrsrc = 0;
449
450 cnp->cn_consume = forkcomponent(cnp, &wantrsrc);
451 if (cnp->cn_consume) {
452 flags |= ISLASTCN;
453 /* Fork names are only for lookups */
454 if (cnp->cn_nameiop != LOOKUP &&
455 cnp->cn_nameiop != CREATE) {
456 vnode_put(vp);
457 error = EPERM;
458 goto exit;
459 }
460 }
461 /*
462 * Use cnode's rsrcfork vnode if possible.
463 */
464 if (wantrsrc) {
465 int vid;
466
467 *vpp = NULL;
468
469 if (cp->c_rsrc_vp == NULL) {
470 vnode_put(vp);
471 goto lookup;
472 }
473 vid = vnode_vid(cp->c_rsrc_vp);
474
475 error = vnode_getwithvid(cp->c_rsrc_vp, vid);
476 if (error) {
477 vnode_put(vp);
478 goto lookup;
479 }
480 *vpp = cp->c_rsrc_vp;
481 vnode_put(vp);
482 vp = *vpp;
483 }
484 }
485 }
486 return (error);
487
488 lookup:
489 /*
490 * The vnode was not in the name cache or it was stale.
491 *
492 * So we need to do a real lookup.
493 */
494 cnode_locked = 0;
495
496 error = hfs_lookup(dvp, vpp, cnp, ap->a_context, &cnode_locked);
497
498 if (cnode_locked)
499 hfs_unlock(VTOC(*vpp));
500 exit:
501 return (error);
502 }
503
504
505 /*
506 * forkcomponent - look for a fork suffix in the component name
507 *
508 */
509 static int
510 forkcomponent(struct componentname *cnp, int *rsrcfork)
511 {
512 char *suffix = cnp->cn_nameptr + cnp->cn_namelen;
513 int consume = 0;
514
515 *rsrcfork = 0;
516 if (*suffix == '\0')
517 return (0);
518 /*
519 * There are only 3 valid fork suffixes:
520 * "/..namedfork/rsrc"
521 * "/..namedfork/data"
522 * "/rsrc" (legacy)
523 */
524 if (bcmp(suffix, _PATH_RSRCFORKSPEC, sizeof(_PATH_RSRCFORKSPEC)) == 0) {
525 consume = sizeof(_PATH_RSRCFORKSPEC) - 1;
526 *rsrcfork = 1;
527 } else if (bcmp(suffix, _PATH_DATAFORKSPEC, sizeof(_PATH_DATAFORKSPEC)) == 0) {
528 consume = sizeof(_PATH_DATAFORKSPEC) - 1;
529 }
530
531 #if LEGACY_FORK_NAMES
532 else if (bcmp(suffix, LEGACY_RSRCFORKSPEC, sizeof(LEGACY_RSRCFORKSPEC)) == 0) {
533 consume = sizeof(LEGACY_RSRCFORKSPEC) - 1;
534 *rsrcfork = 1;
535 printf("HFS: /rsrc paths are deprecated (%s)\n", cnp->cn_nameptr);
536 }
537 #endif
538 return (consume);
539 }
540