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