]> git.saurik.com Git - apple/xnu.git/blob - bsd/ufs/ufs/ufs_lookup.c
8fdb5db0763cdf3b63c57ebc00885eb5e824efc7
[apple/xnu.git] / bsd / ufs / ufs / ufs_lookup.c
1 /*
2 * Copyright (c) 2006 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 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
31 /*
32 * Copyright (c) 1989, 1993
33 * The Regents of the University of California. All rights reserved.
34 * (c) UNIX System Laboratories, Inc.
35 * All or some portions of this file are derived from material licensed
36 * to the University of California by American Telephone and Telegraph
37 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
38 * the permission of UNIX System Laboratories, Inc.
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
42 * are met:
43 * 1. Redistributions of source code must retain the above copyright
44 * notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
48 * 3. All advertising materials mentioning features or use of this software
49 * must display the following acknowledgement:
50 * This product includes software developed by the University of
51 * California, Berkeley and its contributors.
52 * 4. Neither the name of the University nor the names of its contributors
53 * may be used to endorse or promote products derived from this software
54 * without specific prior written permission.
55 *
56 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
57 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
58 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
59 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
60 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
61 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
62 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
63 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
64 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
65 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
66 * SUCH DAMAGE.
67 *
68 * @(#)ufs_lookup.c 8.15 (Berkeley) 6/16/95
69 */
70 #include <rev_endian_fs.h>
71 #include <sys/param.h>
72 #include <sys/namei.h>
73 #include <sys/buf.h>
74 #include <sys/file.h>
75 #include <sys/mount_internal.h>
76 #include <sys/vnode_internal.h>
77 #include <sys/quota.h>
78 #include <sys/kauth.h>
79 #include <sys/uio_internal.h>
80
81 #include <ufs/ufs/quota.h>
82 #include <ufs/ufs/inode.h>
83 #include <ufs/ufs/dir.h>
84 #include <ufs/ufs/ufsmount.h>
85 #include <ufs/ufs/ufs_extern.h>
86 #include <ufs/ffs/ffs_extern.h>
87 #if REV_ENDIAN_FS
88 #include <ufs/ufs/ufs_byte_order.h>
89 #include <architecture/byte_order.h>
90 #endif /* REV_ENDIAN_FS */
91
92 extern struct nchstats nchstats;
93 #if DIAGNOSTIC
94 int dirchk = 1;
95 #else
96 int dirchk = 0;
97 #endif
98
99 #define FSFMT(vp) ((vp)->v_mount->mnt_maxsymlinklen <= 0)
100
101 /*
102 * Convert a component of a pathname into a pointer to a locked inode.
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 * If flag has LOCKPARENT or'ed into it and the target of the pathname
112 * exists, lookup returns both the target and its parent directory locked.
113 * When creating or renaming and LOCKPARENT is specified, the target may
114 * not be ".". When deleting and LOCKPARENT is specified, the target may
115 * be ".".,
116 *
117 * Overall outline of ufs_lookup:
118 *
119 * check accessibility of directory
120 * look for name in cache, if found, then if at end of path
121 * and deleting or creating, drop it, else return name
122 * search for name in directory, to found or notfound
123 * notfound:
124 * if creating, return locked directory, leaving info on available slots
125 * else return error
126 * found:
127 * if at end of path and deleting, return information to allow delete
128 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
129 * inode and return info to allow rewrite
130 * if not at end, add name to cache; if at end and neither creating
131 * nor deleting, add name to cache
132 */
133 int
134 ufs_lookup(ap)
135 struct vnop_lookup_args /* {
136 struct vnode *a_dvp;
137 struct vnode **a_vpp;
138 struct componentname *a_cnp;
139 vfs_context_t a_context
140 } */ *ap;
141 {
142 register struct vnode *vdp; /* vnode for directory being searched */
143 register struct inode *dp; /* inode for directory being searched */
144 struct buf *bp; /* a buffer of directory entries */
145 register struct direct *ep; /* the current directory entry */
146 int entryoffsetinblock; /* offset of ep in bp's buffer */
147 enum {NONE, COMPACT, FOUND} slotstatus;
148 doff_t slotoffset; /* offset of area with free space */
149 int slotsize; /* size of area at slotoffset */
150 int slotfreespace; /* amount of space free in slot */
151 int slotneeded; /* size of the entry we're seeking */
152 int numdirpasses; /* strategy for directory search */
153 doff_t endsearch; /* offset to end directory search */
154 doff_t prevoff; /* prev entry dp->i_offset */
155 struct vnode *pdp; /* saved dp during symlink work */
156 struct vnode *tdp; /* returned by VFS_VGET */
157 doff_t enduseful; /* pointer past last used dir slot */
158 u_long bmask; /* block offset mask */
159 int wantparent; /* 1 => wantparent or lockparent flag */
160 int namlen, error;
161 struct vnode **vpp = ap->a_vpp;
162 struct componentname *cnp = ap->a_cnp;
163 int flags = cnp->cn_flags;
164 int nameiop = cnp->cn_nameiop;
165 vfs_context_t context = ap->a_context;
166 kauth_cred_t cred;
167 #if REV_ENDIAN_FS
168 int rev_endian=0;
169 #endif /* REV_ENDIAN_FS */
170
171
172 cred = vfs_context_ucred(context);
173 bp = NULL;
174 slotoffset = -1;
175 *vpp = NULL;
176 vdp = ap->a_dvp;
177 dp = VTOI(vdp);
178
179 wantparent = flags & (LOCKPARENT|WANTPARENT);
180
181 #if REV_ENDIAN_FS
182 rev_endian=(vdp->v_mount->mnt_flag & MNT_REVEND);
183 #endif /* REV_ENDIAN_FS */
184
185 /*
186 * Check accessiblity of directory.
187 */
188 if ((dp->i_mode & IFMT) != IFDIR)
189 return (ENOTDIR);
190
191 /*
192 * We now have a segment name to search for, and a directory to search.
193 *
194 * Before tediously performing a linear scan of the directory,
195 * check the name cache to see if the directory/name pair
196 * we are looking for is known already.
197 */
198 if (error = cache_lookup(vdp, vpp, cnp)) {
199 if (error == ENOENT)
200 return (error);
201 return (0);
202 }
203 /*
204 * Suppress search for slots unless creating
205 * file and at end of pathname, in which case
206 * we watch for a place to put the new file in
207 * case it doesn't already exist.
208 */
209 slotstatus = FOUND;
210 slotfreespace = slotsize = slotneeded = 0;
211 if ((nameiop == CREATE || nameiop == RENAME) &&
212 (flags & ISLASTCN)) {
213 slotstatus = NONE;
214 slotneeded = (sizeof(struct direct) - MAXNAMLEN +
215 cnp->cn_namelen + 3) &~ 3;
216 }
217 /*
218 * If there is cached information on a previous search of
219 * this directory, pick up where we last left off.
220 * We cache only lookups as these are the most common
221 * and have the greatest payoff. Caching CREATE has little
222 * benefit as it usually must search the entire directory
223 * to determine that the entry does not exist. Caching the
224 * location of the last DELETE or RENAME has not reduced
225 * profiling time and hence has been removed in the interest
226 * of simplicity.
227 */
228 bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_vfsstat.f_iosize - 1;
229 if (nameiop != LOOKUP || dp->i_diroff == 0 ||
230 dp->i_diroff > dp->i_size) {
231 entryoffsetinblock = 0;
232 dp->i_offset = 0;
233 numdirpasses = 1;
234 } else {
235 dp->i_offset = dp->i_diroff;
236 if ((entryoffsetinblock = dp->i_offset & bmask) &&
237 (error = ffs_blkatoff(vdp, (off_t)dp->i_offset, NULL, &bp)))
238 goto out;
239 numdirpasses = 2;
240 nchstats.ncs_2passes++;
241 }
242 prevoff = dp->i_offset;
243 endsearch = roundup(dp->i_size, DIRBLKSIZ);
244 enduseful = 0;
245
246 searchloop:
247 while (dp->i_offset < endsearch) {
248 /*
249 * If necessary, get the next directory block.
250 */
251 if ((dp->i_offset & bmask) == 0) {
252 if (bp != NULL) {
253 #if REV_ENDIAN_FS
254 if (rev_endian)
255 byte_swap_dir_block_out(bp);
256 #endif /* REV_ENDIAN_FS */
257 buf_brelse(bp);
258 }
259 if (error = ffs_blkatoff(vdp, (off_t)dp->i_offset, NULL, &bp))
260 goto out;
261 entryoffsetinblock = 0;
262 }
263 /*
264 * If still looking for a slot, and at a DIRBLKSIZE
265 * boundary, have to start looking for free space again.
266 */
267 if (slotstatus == NONE &&
268 (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
269 slotoffset = -1;
270 slotfreespace = 0;
271 }
272 /*
273 * Get pointer to next entry.
274 * Full validation checks are slow, so we only check
275 * enough to insure forward progress through the
276 * directory. Complete checks can be run by patching
277 * "dirchk" to be true.
278 */
279 ep = (struct direct *)((char *)buf_dataptr(bp) + entryoffsetinblock);
280 if (ep->d_reclen == 0 ||
281 dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock)) {
282 int i;
283
284 ufs_dirbad(dp, dp->i_offset, "mangled entry");
285 i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
286 dp->i_offset += i;
287 entryoffsetinblock += i;
288 continue;
289 }
290
291 /*
292 * If an appropriate sized slot has not yet been found,
293 * check to see if one is available. Also accumulate space
294 * in the current block so that we can determine if
295 * compaction is viable.
296 */
297 if (slotstatus != FOUND) {
298 int size = ep->d_reclen;
299
300 if (ep->d_ino != 0)
301 size -= DIRSIZ(FSFMT(vdp), ep);
302 if (size > 0) {
303 if (size >= slotneeded) {
304 slotstatus = FOUND;
305 slotoffset = dp->i_offset;
306 slotsize = ep->d_reclen;
307 } else if (slotstatus == NONE) {
308 slotfreespace += size;
309 if (slotoffset == -1)
310 slotoffset = dp->i_offset;
311 if (slotfreespace >= slotneeded) {
312 slotstatus = COMPACT;
313 slotsize = dp->i_offset +
314 ep->d_reclen - slotoffset;
315 }
316 }
317 }
318 }
319
320 /*
321 * Check for a name match.
322 */
323 if (ep->d_ino) {
324 # if (BYTE_ORDER == LITTLE_ENDIAN)
325 if (vdp->v_mount->mnt_maxsymlinklen > 0)
326 namlen = ep->d_namlen;
327 else
328 namlen = ep->d_type;
329 # else
330 namlen = ep->d_namlen;
331 # endif
332 if (namlen == cnp->cn_namelen &&
333 !bcmp(cnp->cn_nameptr, ep->d_name,
334 (unsigned)namlen)) {
335 /*
336 * Save directory entry's inode number and
337 * reclen in ndp->ni_ufs area, and release
338 * directory buffer.
339 */
340 if (vdp->v_mount->mnt_maxsymlinklen > 0 &&
341 ep->d_type == DT_WHT) {
342 slotstatus = FOUND;
343 slotoffset = dp->i_offset;
344 slotsize = ep->d_reclen;
345 dp->i_reclen = slotsize;
346 enduseful = dp->i_size;
347 ap->a_cnp->cn_flags |= ISWHITEOUT;
348 numdirpasses--;
349 goto notfound;
350 }
351 dp->i_ino = ep->d_ino;
352 dp->i_reclen = ep->d_reclen;
353 #if REV_ENDIAN_FS
354 if (rev_endian)
355 byte_swap_dir_block_out(bp);
356 #endif /* REV_ENDIAN_FS */
357 buf_brelse(bp);
358 goto found;
359 }
360 }
361 prevoff = dp->i_offset;
362 dp->i_offset += ep->d_reclen;
363 entryoffsetinblock += ep->d_reclen;
364 if (ep->d_ino)
365 enduseful = dp->i_offset;
366 }
367 notfound:
368 /*
369 * If we started in the middle of the directory and failed
370 * to find our target, we must check the beginning as well.
371 */
372 if (numdirpasses == 2) {
373 numdirpasses--;
374 dp->i_offset = 0;
375 endsearch = dp->i_diroff;
376 goto searchloop;
377 }
378 if (bp != NULL) {
379 #if REV_ENDIAN_FS
380 if (rev_endian)
381 byte_swap_dir_block_out(bp);
382 #endif /* REV_ENDIAN_FS */
383 buf_brelse(bp);
384 }
385 /*
386 * If creating, and at end of pathname and current
387 * directory has not been removed, then can consider
388 * allowing file to be created.
389 */
390 if ((nameiop == CREATE || nameiop == RENAME ||
391 (nameiop == DELETE &&
392 (ap->a_cnp->cn_flags & DOWHITEOUT) &&
393 (ap->a_cnp->cn_flags & ISWHITEOUT))) &&
394 (flags & ISLASTCN) && dp->i_nlink != 0) {
395 /*
396 * Return an indication of where the new directory
397 * entry should be put. If we didn't find a slot,
398 * then set dp->i_count to 0 indicating
399 * that the new slot belongs at the end of the
400 * directory. If we found a slot, then the new entry
401 * can be put in the range from dp->i_offset to
402 * dp->i_offset + dp->i_count.
403 */
404 if (slotstatus == NONE) {
405 dp->i_offset = roundup(dp->i_size, DIRBLKSIZ);
406 dp->i_count = 0;
407 enduseful = dp->i_offset;
408 } else if (nameiop == DELETE) {
409 dp->i_offset = slotoffset;
410 if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
411 dp->i_count = 0;
412 else
413 dp->i_count = dp->i_offset - prevoff;
414 } else {
415 dp->i_offset = slotoffset;
416 dp->i_count = slotsize;
417 if (enduseful < slotoffset + slotsize)
418 enduseful = slotoffset + slotsize;
419 }
420 dp->i_endoff = roundup(enduseful, DIRBLKSIZ);
421 dp->i_flag |= IN_CHANGE | IN_UPDATE;
422 /*
423 * We return with the directory locked, so that
424 * the parameters we set up above will still be
425 * valid if we actually decide to do a direnter().
426 * We return ni_vp == NULL to indicate that the entry
427 * does not currently exist; we leave a pointer to
428 * the (locked) directory inode in ndp->ni_dvp.
429 *
430 * NB - if the directory is unlocked, then this
431 * information cannot be used.
432 */
433 error = EJUSTRETURN;
434 goto out;
435 }
436 /*
437 * Insert name into cache (as non-existent) if appropriate.
438 */
439 if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
440 cache_enter(vdp, *vpp, cnp);
441 error = ENOENT;
442 goto out;
443
444 found:
445 if (numdirpasses == 2)
446 nchstats.ncs_pass2++;
447 /*
448 * Check that directory length properly reflects presence
449 * of this entry.
450 */
451 if (entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep) > dp->i_size) {
452 ufs_dirbad(dp, dp->i_offset, "i_size too small");
453 dp->i_size = entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep);
454 dp->i_flag |= IN_CHANGE | IN_UPDATE;
455 }
456
457 /*
458 * Found component in pathname.
459 * If the final component of path name, save information
460 * in the cache as to where the entry was found.
461 */
462 if ((flags & ISLASTCN) && nameiop == LOOKUP)
463 dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1);
464
465 /*
466 * If deleting, and at end of pathname, return
467 * parameters which can be used to remove file.
468 * If the wantparent flag isn't set, we return only
469 * the directory (in ndp->ni_dvp), otherwise we go
470 * on and lock the inode, being careful with ".".
471 */
472 if (nameiop == DELETE && (flags & ISLASTCN)) {
473 /*
474 * Return pointer to current entry in dp->i_offset,
475 * and distance past previous entry (if there
476 * is a previous entry in this block) in dp->i_count.
477 * Save directory inode pointer in ndp->ni_dvp for dirremove().
478 */
479 if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
480 dp->i_count = 0;
481 else
482 dp->i_count = dp->i_offset - prevoff;
483 if (dp->i_number == dp->i_ino) {
484 vnode_get(vdp);
485 *vpp = vdp;
486 error = 0;
487 goto out;
488 }
489 if (error = ffs_vget_internal(vdp->v_mount, dp->i_ino, &tdp, vdp, cnp, 0, 0))
490 goto out;
491 *vpp = tdp;
492 goto out;
493 }
494
495 /*
496 * If rewriting (RENAME), return the inode and the
497 * information required to rewrite the present directory
498 * Must get inode of directory entry to verify it's a
499 * regular file, or empty directory.
500 */
501 if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
502 /*
503 * Careful about locking second inode.
504 * This can only occur if the target is ".".
505 */
506 if (dp->i_number == dp->i_ino) {
507 error =EISDIR;
508 goto out;
509 }
510 if (error = ffs_vget_internal(vdp->v_mount, dp->i_ino, &tdp, vdp, cnp, 0, 0))
511 goto out;
512 *vpp = tdp;
513
514 goto out;
515 }
516
517 /*
518 * Step through the translation in the name. We do not `vnode_put' the
519 * directory because we may need it again if a symbolic link
520 * is relative to the current directory. Instead we save it
521 * unlocked as "pdp". We must get the target inode before unlocking
522 * the directory to insure that the inode will not be removed
523 * before we get it. We prevent deadlock by always fetching
524 * inodes from the root, moving down the directory tree. Thus
525 * when following backward pointers ".." we must unlock the
526 * parent directory before getting the requested directory.
527 * There is a potential race condition here if both the current
528 * and parent directories are removed before the VFS_VGET for the
529 * inode associated with ".." returns. We hope that this occurs
530 * infrequently since we cannot avoid this race condition without
531 * implementing a sophisticated deadlock detection algorithm.
532 * Note also that this simple deadlock detection scheme will not
533 * work if the file system has any hard links other than ".."
534 * that point backwards in the directory structure.
535 */
536 pdp = vdp;
537 if (flags & ISDOTDOT) {
538 if (error = ffs_vget_internal(vdp->v_mount, dp->i_ino, &tdp, vdp, cnp, 0, 0)) {
539 goto out;
540 }
541 *vpp = tdp;
542 } else if (dp->i_number == dp->i_ino) {
543 vnode_get(vdp); /* we want ourself, ie "." */
544 *vpp = vdp;
545 } else {
546 if (error = ffs_vget_internal(vdp->v_mount, dp->i_ino, &tdp, vdp, cnp, 0, 0))
547 goto out;
548 *vpp = tdp;
549 }
550
551 error = 0;
552 out:
553 return (error);
554 }
555
556 void
557 ufs_dirbad(ip, offset, how)
558 struct inode *ip;
559 doff_t offset;
560 const char *how;
561 {
562 struct mount *mp;
563
564 mp = ITOV(ip)->v_mount;
565 (void)printf("%s: bad dir ino %d at offset %d: %s\n",
566 mp->mnt_vfsstat.f_mntonname, ip->i_number, offset, how);
567 if ((mp->mnt_vfsstat.f_flags & MNT_RDONLY) == 0)
568 panic("bad dir");
569 }
570
571 /*
572 * Do consistency checking on a directory entry:
573 * record length must be multiple of 4
574 * entry must fit in rest of its DIRBLKSIZ block
575 * record must be large enough to contain entry
576 * name is not longer than MAXNAMLEN
577 * name must be as long as advertised, and null terminated
578 */
579 int
580 ufs_dirbadentry(dp, ep, entryoffsetinblock)
581 struct vnode *dp;
582 register struct direct *ep;
583 int entryoffsetinblock;
584 {
585 register int i;
586 int namlen;
587
588 # if (BYTE_ORDER == LITTLE_ENDIAN)
589 if (dp->v_mount->mnt_maxsymlinklen > 0)
590 namlen = ep->d_namlen;
591 else
592 namlen = ep->d_type;
593 # else
594 namlen = ep->d_namlen;
595 # endif
596 if ((ep->d_reclen & 0x3) != 0 ||
597 ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
598 ep->d_reclen < DIRSIZ(FSFMT(dp), ep) || namlen > MAXNAMLEN) {
599 /*return (1); */
600 printf("First bad\n");
601 goto bad;
602 }
603 if (ep->d_ino == 0)
604 return (0);
605 for (i = 0; i < namlen; i++)
606 if (ep->d_name[i] == '\0') {
607 /*return (1); */
608 printf("Second bad\n");
609 goto bad;
610 }
611 if (ep->d_name[i])
612 goto bad;
613 return (0);
614 bad:
615 return (1);
616 }
617
618 /*
619 * Write a directory entry after a call to namei, using the parameters
620 * that it left in nameidata. The argument ip is the inode which the new
621 * directory entry will refer to. Dvp is a pointer to the directory to
622 * be written, which was left locked by namei. Remaining parameters
623 * (dp->i_offset, dp->i_count) indicate how the space for the new
624 * entry is to be obtained.
625 */
626 int
627 ufs_direnter(ip, dvp, cnp)
628 struct inode *ip;
629 struct vnode *dvp;
630 register struct componentname *cnp;
631 {
632 register struct inode *dp;
633 struct direct newdir;
634
635 dp = VTOI(dvp);
636 newdir.d_ino = ip->i_number;
637 newdir.d_namlen = cnp->cn_namelen;
638 bcopy(cnp->cn_nameptr, newdir.d_name, (unsigned)cnp->cn_namelen + 1);
639 if (dvp->v_mount->mnt_maxsymlinklen > 0)
640 newdir.d_type = IFTODT(ip->i_mode);
641 else {
642 newdir.d_type = 0;
643 # if (BYTE_ORDER == LITTLE_ENDIAN)
644 { u_char tmp = newdir.d_namlen;
645 newdir.d_namlen = newdir.d_type;
646 newdir.d_type = tmp; }
647 # endif
648 }
649 return (ufs_direnter2(dvp, &newdir, cnp->cn_context));
650 }
651
652 /*
653 * Common entry point for directory entry removal used by ufs_direnter
654 * and ufs_whiteout
655 */
656 int
657 ufs_direnter2(struct vnode *dvp, struct direct *dirp, vfs_context_t ctx)
658 {
659 int newentrysize;
660 struct inode *dp;
661 struct buf *bp;
662 uio_t auio;
663 u_int dsize;
664 struct direct *ep, *nep;
665 int error, loc, spacefree;
666 char *dirbuf;
667 char uio_buf[ UIO_SIZEOF(1) ];
668 #if REV_ENDIAN_FS
669 struct mount *mp=dvp->v_mount;
670 int rev_endian=(mp->mnt_flag & MNT_REVEND);
671 #endif /* REV_ENDIAN_FS */
672
673 dp = VTOI(dvp);
674 newentrysize = DIRSIZ(FSFMT(dvp), dirp);
675
676 if (dp->i_count == 0) {
677 /*
678 * If dp->i_count is 0, then namei could find no
679 * space in the directory. Here, dp->i_offset will
680 * be on a directory block boundary and we will write the
681 * new entry into a fresh block.
682 */
683 if (dp->i_offset & (DIRBLKSIZ - 1))
684 panic("ufs_direnter2: newblk");
685 dirp->d_reclen = DIRBLKSIZ;
686 auio = uio_createwithbuffer(1, dp->i_offset, UIO_SYSSPACE, UIO_WRITE,
687 &uio_buf[0], sizeof(uio_buf));
688 uio_addiov(auio, CAST_USER_ADDR_T(dirp), newentrysize);
689
690 error = ffs_write_internal(dvp, auio, IO_SYNC, vfs_context_ucred(ctx));
691 if (DIRBLKSIZ >
692 VFSTOUFS(dvp->v_mount)->um_mountp->mnt_vfsstat.f_bsize)
693 /* XXX should grow with balloc() */
694 panic("ufs_direnter2: frag size");
695 else if (!error) {
696 dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
697 dp->i_flag |= IN_CHANGE;
698 }
699 return (error);
700 }
701
702 /*
703 * If dp->i_count is non-zero, then namei found space
704 * for the new entry in the range dp->i_offset to
705 * dp->i_offset + dp->i_count in the directory.
706 * To use this space, we may have to compact the entries located
707 * there, by copying them together towards the beginning of the
708 * block, leaving the free space in one usable chunk at the end.
709 */
710
711 /*
712 * Increase size of directory if entry eats into new space.
713 * This should never push the size past a new multiple of
714 * DIRBLKSIZE.
715 *
716 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
717 */
718 if (dp->i_offset + dp->i_count > dp->i_size)
719 dp->i_size = dp->i_offset + dp->i_count;
720 /*
721 * Get the block containing the space for the new directory entry.
722 */
723 if (error = ffs_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf, &bp))
724 return (error);
725 /*
726 * Find space for the new entry. In the simple case, the entry at
727 * offset base will have the space. If it does not, then namei
728 * arranged that compacting the region dp->i_offset to
729 * dp->i_offset + dp->i_count would yield the
730 * space.
731 */
732 ep = (struct direct *)dirbuf;
733 dsize = DIRSIZ(FSFMT(dvp), ep);
734 spacefree = ep->d_reclen - dsize;
735 for (loc = ep->d_reclen; loc < dp->i_count; ) {
736 nep = (struct direct *)(dirbuf + loc);
737 if (ep->d_ino) {
738 /* trim the existing slot */
739 ep->d_reclen = dsize;
740 ep = (struct direct *)((char *)ep + dsize);
741 } else {
742 /* overwrite; nothing there; header is ours */
743 spacefree += dsize;
744 }
745 dsize = DIRSIZ(FSFMT(dvp), nep);
746 spacefree += nep->d_reclen - dsize;
747 loc += nep->d_reclen;
748 bcopy((caddr_t)nep, (caddr_t)ep, dsize);
749 }
750 /*
751 * Update the pointer fields in the previous entry (if any),
752 * copy in the new entry, and write out the block.
753 */
754 if (ep->d_ino == 0 ||
755 (ep->d_ino == WINO &&
756 bcmp(ep->d_name, dirp->d_name, dirp->d_namlen) == 0)) {
757 if (spacefree + dsize < newentrysize)
758 panic("ufs_direnter2: compact1");
759 dirp->d_reclen = spacefree + dsize;
760 } else {
761 if (spacefree < newentrysize)
762 panic("ufs_direnter2: compact2");
763 dirp->d_reclen = spacefree;
764 ep->d_reclen = dsize;
765 ep = (struct direct *)((char *)ep + dsize);
766 }
767 bcopy((caddr_t)dirp, (caddr_t)ep, (u_int)newentrysize);
768 #if REV_ENDIAN_FS
769 if (rev_endian)
770 byte_swap_dir_block_out(bp);
771 #endif /* REV_ENDIAN_FS */
772 if (mp->mnt_flag & MNT_ASYNC) {
773 error = 0;
774 buf_bdwrite(bp);
775 } else {
776 error = VNOP_BWRITE(bp);
777 }
778 dp->i_flag |= IN_CHANGE | IN_UPDATE;
779 if (!error && dp->i_endoff && dp->i_endoff < dp->i_size)
780 error = ffs_truncate_internal(dvp, (off_t)dp->i_endoff, IO_SYNC, vfs_context_ucred(ctx));
781
782 return (error);
783 }
784
785 /*
786 * Remove a directory entry after a call to namei, using
787 * the parameters which it left in nameidata. The entry
788 * dp->i_offset contains the offset into the directory of the
789 * entry to be eliminated. The dp->i_count field contains the
790 * size of the previous record in the directory. If this
791 * is 0, the first entry is being deleted, so we need only
792 * zero the inode number to mark the entry as free. If the
793 * entry is not the first in the directory, we must reclaim
794 * the space of the now empty record by adding the record size
795 * to the size of the previous entry.
796 */
797 int
798 ufs_dirremove(dvp, cnp)
799 struct vnode *dvp;
800 struct componentname *cnp;
801 {
802 register struct inode *dp;
803 struct direct *ep;
804 struct buf *bp;
805 int error;
806 #if REV_ENDIAN_FS
807 struct mount *mp=dvp->v_mount;
808 int rev_endian=(mp->mnt_flag & MNT_REVEND);
809 #endif /* REV_ENDIAN_FS */
810
811 dp = VTOI(dvp);
812
813 if (cnp->cn_flags & DOWHITEOUT) {
814 /*
815 * Whiteout entry: set d_ino to WINO.
816 */
817 if (error = ffs_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep, &bp))
818 return (error);
819 ep->d_ino = WINO;
820 ep->d_type = DT_WHT;
821 #if REV_ENDIAN_FS
822 if (rev_endian)
823 byte_swap_dir_block_out(bp);
824 #endif /* REV_ENDIAN_FS */
825 if (mp->mnt_flag & MNT_ASYNC) {
826 error = 0;
827 buf_bdwrite(bp);
828 } else {
829 error = VNOP_BWRITE(bp);
830 }
831 dp->i_flag |= IN_CHANGE | IN_UPDATE;
832 return (error);
833 }
834
835 if (dp->i_count == 0) {
836 /*
837 * First entry in block: set d_ino to zero.
838 */
839 if (error = ffs_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep, &bp))
840 return (error);
841 ep->d_ino = 0;
842 #if REV_ENDIAN_FS
843 if (rev_endian)
844 byte_swap_dir_block_out(bp);
845 #endif /* REV_ENDIAN_FS */
846 if (mp->mnt_flag & MNT_ASYNC) {
847 error = 0;
848 buf_bdwrite(bp);
849 } else {
850 error = VNOP_BWRITE(bp);
851 }
852 dp->i_flag |= IN_CHANGE | IN_UPDATE;
853 return (error);
854 }
855 /*
856 * Collapse new free space into previous entry.
857 */
858 if (error = ffs_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count),
859 (char **)&ep, &bp))
860 return (error);
861 ep->d_reclen += dp->i_reclen;
862 #if REV_ENDIAN_FS
863 if (rev_endian)
864 byte_swap_dir_block_out(bp);
865 #endif /* REV_ENDIAN_FS */
866 if (mp->mnt_flag & MNT_ASYNC) {
867 error = 0;
868 buf_bdwrite(bp);
869 } else {
870 error = VNOP_BWRITE(bp);
871 }
872 dp->i_flag |= IN_CHANGE | IN_UPDATE;
873
874 return (error);
875 }
876
877 /*
878 * Rewrite an existing directory entry to point at the inode
879 * supplied. The parameters describing the directory entry are
880 * set up by a call to namei.
881 */
882 int
883 ufs_dirrewrite(dp, ip, cnp)
884 struct inode *dp, *ip;
885 struct componentname *cnp;
886 {
887 struct buf *bp;
888 struct direct *ep;
889 struct vnode *vdp = ITOV(dp);
890 int error;
891
892 if (error = ffs_blkatoff(vdp, (off_t)dp->i_offset, (char **)&ep, &bp))
893 return (error);
894 ep->d_ino = ip->i_number;
895 if (vdp->v_mount->mnt_maxsymlinklen > 0)
896 ep->d_type = IFTODT(ip->i_mode);
897 #if REV_ENDIAN_FS
898 if (vdp->v_mount->mnt_flag & MNT_REVEND)
899 byte_swap_dir_block_out(bp);
900 #endif /* REV_ENDIAN_FS */
901 if (vdp->v_mount->mnt_flag & MNT_ASYNC) {
902 error = 0;
903 buf_bdwrite(bp);
904 } else {
905 error = VNOP_BWRITE(bp);
906 }
907 dp->i_flag |= IN_CHANGE | IN_UPDATE;
908 return (error);
909 }
910
911 /*
912 * Check if a directory is empty or not.
913 * Inode supplied must be locked.
914 *
915 * Using a struct dirtemplate here is not precisely
916 * what we want, but better than using a struct direct.
917 *
918 * NB: does not handle corrupted directories.
919 */
920 int
921 ufs_dirempty(struct inode *ip, ino_t parentino, kauth_cred_t cred)
922 {
923 register off_t off;
924 struct dirtemplate dbuf;
925 register struct direct *dp = (struct direct *)&dbuf;
926 int error, count, namlen;
927 #if REV_ENDIAN_FS
928 struct vnode *vp=ITOV(ip);
929 struct mount *mp=vp->v_mount;
930 int rev_endian=(mp->mnt_flag & MNT_REVEND);
931 #endif /* REV_ENDIAN_FS */
932
933 #define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
934
935 for (off = 0; off < ip->i_size; off += dp->d_reclen) {
936 error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off,
937 UIO_SYSSPACE32, IO_NODELOCKED, cred, &count, (struct proc *)0);
938 /*
939 * Since we read MINDIRSIZ, residual must
940 * be 0 unless we're at end of file.
941 */
942 if (error || count != 0)
943 return (0);
944 #if 0 /*REV_ENDIAN_FS */
945 if (rev_endian)
946 byte_swap_minidir_in(dp);
947 #endif /* REV_ENDIAN_FS */
948 /* avoid infinite loops */
949 if (dp->d_reclen == 0)
950 return (0);
951 /* skip empty entries */
952 if (dp->d_ino == 0 || dp->d_ino == WINO)
953 continue;
954 /* accept only "." and ".." */
955 # if (BYTE_ORDER == LITTLE_ENDIAN)
956 if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0)
957 namlen = dp->d_namlen;
958 else
959 namlen = dp->d_type;
960 # else
961 namlen = dp->d_namlen;
962 # endif
963 if (namlen > 2)
964 return (0);
965 if (dp->d_name[0] != '.')
966 return (0);
967 /*
968 * At this point namlen must be 1 or 2.
969 * 1 implies ".", 2 implies ".." if second
970 * char is also "."
971 */
972 if (namlen == 1)
973 continue;
974 if (dp->d_name[1] == '.' && dp->d_ino == parentino)
975 continue;
976 return (0);
977 }
978 return (1);
979 }
980
981 /*
982 * Check if source directory is in the path of the target directory.
983 * Target is supplied locked, source is unlocked.
984 */
985 int
986 ufs_checkpath(source, target, cred)
987 struct inode *source, *target;
988 kauth_cred_t cred;
989 {
990 struct vnode *vp;
991 int error, rootino, namlen;
992 int need_put = 0;
993 struct dirtemplate dirbuf;
994
995 vp = ITOV(target);
996 if (target->i_number == source->i_number) {
997 error = EEXIST;
998 goto out;
999 }
1000 rootino = ROOTINO;
1001 error = 0;
1002 if (target->i_number == rootino)
1003 goto out;
1004
1005 for (;;) {
1006 if (vp->v_type != VDIR) {
1007 error = ENOTDIR;
1008 break;
1009 }
1010 error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
1011 sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE32,
1012 IO_NODELOCKED, cred, (int *)0, (struct proc *)0);
1013 if (error != 0)
1014 break;
1015 # if (BYTE_ORDER == LITTLE_ENDIAN)
1016 if (vp->v_mount->mnt_maxsymlinklen > 0)
1017 namlen = dirbuf.dotdot_namlen;
1018 else
1019 namlen = dirbuf.dotdot_type;
1020 # else
1021 namlen = dirbuf.dotdot_namlen;
1022 # endif
1023 if (namlen != 2 ||
1024 dirbuf.dotdot_name[0] != '.' ||
1025 dirbuf.dotdot_name[1] != '.') {
1026 error = ENOTDIR;
1027 break;
1028 }
1029 if (dirbuf.dotdot_ino == source->i_number) {
1030 error = EINVAL;
1031 break;
1032 }
1033 if (dirbuf.dotdot_ino == rootino)
1034 break;
1035
1036 if (need_put)
1037 vnode_put(vp);
1038
1039 if (error = VFS_VGET(vp->v_mount, (ino64_t)dirbuf.dotdot_ino, &vp, NULL)) { /* XXX need context */
1040 vp = NULL;
1041 break;
1042 }
1043 need_put = 1;
1044 }
1045
1046 out:
1047 if (error == ENOTDIR)
1048 printf("checkpath: .. not a directory\n");
1049 if (need_put && vp)
1050 vnode_put(vp);
1051
1052 return (error);
1053 }