|
|
1.1 root 1: /*
2: * Copyright (c) 1989 The Regents of the University of California.
3: * All rights reserved.
4: *
5: * Redistribution is only permitted until one year after the first shipment
6: * of 4.4BSD by the Regents. Otherwise, redistribution and use in source and
7: * binary forms are permitted provided that: (1) source distributions retain
8: * this entire copyright notice and comment, and (2) distributions including
9: * binaries display the following acknowledgement: This product includes
10: * software developed by the University of California, Berkeley and its
11: * contributors'' in the documentation or other materials provided with the
12: * distribution and in all advertising materials mentioning features or use
13: * of this software. Neither the name of the University nor the names of
14: * its contributors may be used to endorse or promote products derived from
15: * this software without specific prior written permission.
16: * THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
17: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
18: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19: *
20: * @(#)ufs_lookup.c 7.23 (Berkeley) 6/28/90
21: */
22:
23: #include "param.h"
24: #include "user.h"
25: #include "buf.h"
26: #include "file.h"
27: #include "vnode.h"
28: #include "../ufs/quota.h"
29: #include "../ufs/inode.h"
30: #include "../ufs/fs.h"
31:
32: struct nchstats nchstats;
33: int dirchk = 1;
34:
35: /*
36: * Convert a component of a pathname into a pointer to a locked inode.
37: * This is a very central and rather complicated routine.
38: * If the file system is not maintained in a strict tree hierarchy,
39: * this can result in a deadlock situation (see comments in code below).
40: *
41: * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
42: * whether the name is to be looked up, created, renamed, or deleted.
43: * When CREATE, RENAME, or DELETE is specified, information usable in
44: * creating, renaming, or deleting a directory entry may be calculated.
45: * If flag has LOCKPARENT or'ed into it and the target of the pathname
46: * exists, lookup returns both the target and its parent directory locked.
47: * When creating or renaming and LOCKPARENT is specified, the target may
48: * not be ".". When deleting and LOCKPARENT is specified, the target may
49: * be "."., but the caller must check to ensure it does an vrele and iput
50: * instead of two iputs.
51: *
52: * Overall outline of ufs_lookup:
53: *
54: * check accessibility of directory
55: * look for name in cache, if found, then if at end of path
56: * and deleting or creating, drop it, else return name
57: * search for name in directory, to found or notfound
58: * notfound:
59: * if creating, return locked directory, leaving info on available slots
60: * else return error
61: * found:
62: * if at end of path and deleting, return information to allow delete
63: * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
64: * inode and return info to allow rewrite
65: * if not at end, add name to cache; if at end and neither creating
66: * nor deleting, add name to cache
67: *
68: * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked.
69: */
70: ufs_lookup(vdp, ndp)
71: register struct vnode *vdp;
72: register struct nameidata *ndp;
73: {
74: register struct inode *dp; /* the directory we are searching */
75: register struct fs *fs; /* file system that directory is in */
76: struct buf *bp = 0; /* a buffer of directory entries */
77: register struct direct *ep; /* the current directory entry */
78: int entryoffsetinblock; /* offset of ep in bp's buffer */
79: enum {NONE, COMPACT, FOUND} slotstatus;
80: int slotoffset = -1; /* offset of area with free space */
81: int slotsize; /* size of area at slotoffset */
82: int slotfreespace; /* amount of space free in slot */
83: int slotneeded; /* size of the entry we're seeking */
84: int numdirpasses; /* strategy for directory search */
85: int endsearch; /* offset to end directory search */
86: int prevoff; /* ndp->ni_offset of previous entry */
87: struct inode *pdp; /* saved dp during symlink work */
88: struct inode *tdp; /* returned by iget */
89: off_t enduseful; /* pointer past last used dir slot */
90: int flag; /* LOOKUP, CREATE, RENAME, or DELETE */
91: int lockparent; /* 1 => lockparent flag is set */
92: int wantparent; /* 1 => wantparent or lockparent flag */
93: int error;
94:
95: ndp->ni_dvp = vdp;
96: ndp->ni_vp = NULL;
97: dp = VTOI(vdp);
98: fs = dp->i_fs;
99: lockparent = ndp->ni_nameiop & LOCKPARENT;
100: flag = ndp->ni_nameiop & OPFLAG;
101: wantparent = ndp->ni_nameiop & (LOCKPARENT|WANTPARENT);
102:
103: /*
104: * Check accessiblity of directory.
105: */
106: if ((dp->i_mode&IFMT) != IFDIR)
107: return (ENOTDIR);
108: if (error = ufs_access(vdp, VEXEC, ndp->ni_cred))
109: return (error);
110:
111: /*
112: * We now have a segment name to search for, and a directory to search.
113: *
114: * Before tediously performing a linear scan of the directory,
115: * check the name cache to see if the directory/name pair
116: * we are looking for is known already.
117: */
118: if (error = cache_lookup(ndp)) {
119: int vpid; /* capability number of vnode */
120:
121: if (error == ENOENT)
122: return (error);
123: if (vdp == ndp->ni_rdir && ndp->ni_isdotdot)
124: panic("ufs_lookup: .. through root");
125: /*
126: * Get the next vnode in the path.
127: * See comment below starting `Step through' for
128: * an explaination of the locking protocol.
129: */
130: pdp = dp;
131: dp = VTOI(ndp->ni_vp);
132: vdp = ndp->ni_vp;
133: vpid = vdp->v_id;
134: if (pdp == dp) {
135: VREF(vdp);
136: error = 0;
137: } else if (ndp->ni_isdotdot) {
138: IUNLOCK(pdp);
139: error = vget(vdp);
140: } else {
141: error = vget(vdp);
142: IUNLOCK(pdp);
143: }
144: /*
145: * Check that the capability number did not change
146: * while we were waiting for the lock.
147: */
148: if (!error) {
149: if (vpid == vdp->v_id)
150: return (0);
151: else
152: iput(dp);
153: }
154: ILOCK(pdp);
155: dp = pdp;
156: vdp = ITOV(dp);
157: ndp->ni_vp = NULL;
158: }
159:
160: /*
161: * Suppress search for slots unless creating
162: * file and at end of pathname, in which case
163: * we watch for a place to put the new file in
164: * case it doesn't already exist.
165: */
166: slotstatus = FOUND;
167: if ((flag == CREATE || flag == RENAME) && *ndp->ni_next == 0) {
168: slotstatus = NONE;
169: slotfreespace = 0;
170: slotneeded = DIRSIZ(&ndp->ni_dent);
171: }
172:
173: /*
174: * If there is cached information on a previous search of
175: * this directory, pick up where we last left off.
176: * We cache only lookups as these are the most common
177: * and have the greatest payoff. Caching CREATE has little
178: * benefit as it usually must search the entire directory
179: * to determine that the entry does not exist. Caching the
180: * location of the last DELETE or RENAME has not reduced
181: * profiling time and hence has been removed in the interest
182: * of simplicity.
183: */
184: if (flag != LOOKUP || dp->i_diroff == 0 || dp->i_diroff > dp->i_size) {
185: ndp->ni_offset = 0;
186: numdirpasses = 1;
187: } else {
188: ndp->ni_offset = dp->i_diroff;
189: entryoffsetinblock = blkoff(fs, ndp->ni_offset);
190: if (entryoffsetinblock != 0) {
191: error = blkatoff(dp, ndp->ni_offset, (char **)0, &bp);
192: if (error)
193: return (error);
194: }
195: numdirpasses = 2;
196: nchstats.ncs_2passes++;
197: }
198: endsearch = roundup(dp->i_size, DIRBLKSIZ);
199: enduseful = 0;
200:
201: searchloop:
202: while (ndp->ni_offset < endsearch) {
203: /*
204: * If offset is on a block boundary,
205: * read the next directory block.
206: * Release previous if it exists.
207: */
208: if (blkoff(fs, ndp->ni_offset) == 0) {
209: if (bp != NULL)
210: brelse(bp);
211: error = blkatoff(dp, ndp->ni_offset, (char **)0, &bp);
212: if (error)
213: return (error);
214: entryoffsetinblock = 0;
215: }
216: /*
217: * If still looking for a slot, and at a DIRBLKSIZE
218: * boundary, have to start looking for free space again.
219: */
220: if (slotstatus == NONE &&
221: (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
222: slotoffset = -1;
223: slotfreespace = 0;
224: }
225: /*
226: * Get pointer to next entry.
227: * Full validation checks are slow, so we only check
228: * enough to insure forward progress through the
229: * directory. Complete checks can be run by patching
230: * "dirchk" to be true.
231: */
232: ep = (struct direct *)(bp->b_un.b_addr + entryoffsetinblock);
233: if (ep->d_reclen == 0 ||
234: dirchk && dirbadentry(ep, entryoffsetinblock)) {
235: int i;
236:
237: dirbad(dp, ndp->ni_offset, "mangled entry");
238: i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
239: ndp->ni_offset += i;
240: entryoffsetinblock += i;
241: continue;
242: }
243:
244: /*
245: * If an appropriate sized slot has not yet been found,
246: * check to see if one is available. Also accumulate space
247: * in the current block so that we can determine if
248: * compaction is viable.
249: */
250: if (slotstatus != FOUND) {
251: int size = ep->d_reclen;
252:
253: if (ep->d_ino != 0)
254: size -= DIRSIZ(ep);
255: if (size > 0) {
256: if (size >= slotneeded) {
257: slotstatus = FOUND;
258: slotoffset = ndp->ni_offset;
259: slotsize = ep->d_reclen;
260: } else if (slotstatus == NONE) {
261: slotfreespace += size;
262: if (slotoffset == -1)
263: slotoffset = ndp->ni_offset;
264: if (slotfreespace >= slotneeded) {
265: slotstatus = COMPACT;
266: slotsize = ndp->ni_offset +
267: ep->d_reclen - slotoffset;
268: }
269: }
270: }
271: }
272:
273: /*
274: * Check for a name match.
275: */
276: if (ep->d_ino) {
277: if (ep->d_namlen == ndp->ni_dent.d_namlen &&
278: !bcmp(ndp->ni_ptr, ep->d_name,
279: (unsigned)ep->d_namlen)) {
280: /*
281: * Save directory entry's inode number and
282: * reclen in ndp->ni_dent, and release
283: * directory buffer.
284: */
285: ndp->ni_dent.d_ino = ep->d_ino;
286: ndp->ni_dent.d_reclen = ep->d_reclen;
287: brelse(bp);
288: goto found;
289: }
290: }
291: prevoff = ndp->ni_offset;
292: ndp->ni_offset += ep->d_reclen;
293: entryoffsetinblock += ep->d_reclen;
294: if (ep->d_ino)
295: enduseful = ndp->ni_offset;
296: }
297: /* notfound: */
298: /*
299: * If we started in the middle of the directory and failed
300: * to find our target, we must check the beginning as well.
301: */
302: if (numdirpasses == 2) {
303: numdirpasses--;
304: ndp->ni_offset = 0;
305: endsearch = dp->i_diroff;
306: goto searchloop;
307: }
308: if (bp != NULL)
309: brelse(bp);
310: /*
311: * If creating, and at end of pathname and current
312: * directory has not been removed, then can consider
313: * allowing file to be created.
314: */
315: if ((flag == CREATE || flag == RENAME) &&
316: *ndp->ni_next == 0 && dp->i_nlink != 0) {
317: /*
318: * Access for write is interpreted as allowing
319: * creation of files in the directory.
320: */
321: if (error = ufs_access(vdp, VWRITE, ndp->ni_cred))
322: return (error);
323: /*
324: * Return an indication of where the new directory
325: * entry should be put. If we didn't find a slot,
326: * then set ndp->ni_count to 0 indicating that the new
327: * slot belongs at the end of the directory. If we found
328: * a slot, then the new entry can be put in the range
329: * [ndp->ni_offset .. ndp->ni_offset + ndp->ni_count)
330: */
331: if (slotstatus == NONE) {
332: ndp->ni_offset = roundup(dp->i_size, DIRBLKSIZ);
333: ndp->ni_count = 0;
334: enduseful = ndp->ni_offset;
335: } else {
336: ndp->ni_offset = slotoffset;
337: ndp->ni_count = slotsize;
338: if (enduseful < slotoffset + slotsize)
339: enduseful = slotoffset + slotsize;
340: }
341: ndp->ni_endoff = roundup(enduseful, DIRBLKSIZ);
342: dp->i_flag |= IUPD|ICHG;
343: /*
344: * We return with the directory locked, so that
345: * the parameters we set up above will still be
346: * valid if we actually decide to do a direnter().
347: * We return ni_vp == NULL to indicate that the entry
348: * does not currently exist; we leave a pointer to
349: * the (locked) directory inode in ndp->ni_dvp.
350: *
351: * NB - if the directory is unlocked, then this
352: * information cannot be used.
353: */
354: if (!lockparent)
355: IUNLOCK(dp);
356: }
357: /*
358: * Insert name into cache (as non-existent) if appropriate.
359: */
360: if (ndp->ni_makeentry)
361: cache_enter(ndp);
362: return (ENOENT);
363:
364: found:
365: if (numdirpasses == 2)
366: nchstats.ncs_pass2++;
367: /*
368: * Check that directory length properly reflects presence
369: * of this entry.
370: */
371: if (entryoffsetinblock + DIRSIZ(ep) > dp->i_size) {
372: dirbad(dp, ndp->ni_offset, "i_size too small");
373: dp->i_size = entryoffsetinblock + DIRSIZ(ep);
374: dp->i_flag |= IUPD|ICHG;
375: }
376:
377: /*
378: * Found component in pathname.
379: * If the final component of path name, save information
380: * in the cache as to where the entry was found.
381: */
382: if (*ndp->ni_next == '\0' && flag == LOOKUP)
383: dp->i_diroff = ndp->ni_offset &~ (DIRBLKSIZ - 1);
384:
385: /*
386: * If deleting, and at end of pathname, return
387: * parameters which can be used to remove file.
388: * If the wantparent flag isn't set, we return only
389: * the directory (in ndp->ni_dvp), otherwise we go
390: * on and lock the inode, being careful with ".".
391: */
392: if (flag == DELETE && *ndp->ni_next == 0) {
393: /*
394: * Write access to directory required to delete files.
395: */
396: if (error = ufs_access(vdp, VWRITE, ndp->ni_cred))
397: return (error);
398: /*
399: * Return pointer to current entry in ndp->ni_offset,
400: * and distance past previous entry (if there
401: * is a previous entry in this block) in ndp->ni_count.
402: * Save directory inode pointer in ndp->ni_dvp for dirremove().
403: */
404: if ((ndp->ni_offset&(DIRBLKSIZ-1)) == 0)
405: ndp->ni_count = 0;
406: else
407: ndp->ni_count = ndp->ni_offset - prevoff;
408: if (dp->i_number == ndp->ni_dent.d_ino) {
409: VREF(vdp);
410: ndp->ni_vp = vdp;
411: return (0);
412: }
413: if (error = iget(dp, ndp->ni_dent.d_ino, &tdp))
414: return (error);
415: /*
416: * If directory is "sticky", then user must own
417: * the directory, or the file in it, else she
418: * may not delete it (unless she's root). This
419: * implements append-only directories.
420: */
421: if ((dp->i_mode & ISVTX) &&
422: ndp->ni_cred->cr_uid != 0 &&
423: ndp->ni_cred->cr_uid != dp->i_uid &&
424: tdp->i_uid != ndp->ni_cred->cr_uid) {
425: iput(tdp);
426: return (EPERM);
427: }
428: ndp->ni_vp = ITOV(tdp);
429: if (!lockparent)
430: IUNLOCK(dp);
431: return (0);
432: }
433:
434: /*
435: * If rewriting (RENAME), return the inode and the
436: * information required to rewrite the present directory
437: * Must get inode of directory entry to verify it's a
438: * regular file, or empty directory.
439: */
440: if (flag == RENAME && wantparent && *ndp->ni_next == 0) {
441: if (error = ufs_access(vdp, VWRITE, ndp->ni_cred))
442: return (error);
443: /*
444: * Careful about locking second inode.
445: * This can only occur if the target is ".".
446: */
447: if (dp->i_number == ndp->ni_dent.d_ino)
448: return (EISDIR);
449: if (error = iget(dp, ndp->ni_dent.d_ino, &tdp))
450: return (error);
451: ndp->ni_vp = ITOV(tdp);
452: if (!lockparent)
453: IUNLOCK(dp);
454: return (0);
455: }
456:
457: /*
458: * Step through the translation in the name. We do not `iput' the
459: * directory because we may need it again if a symbolic link
460: * is relative to the current directory. Instead we save it
461: * unlocked as "pdp". We must get the target inode before unlocking
462: * the directory to insure that the inode will not be removed
463: * before we get it. We prevent deadlock by always fetching
464: * inodes from the root, moving down the directory tree. Thus
465: * when following backward pointers ".." we must unlock the
466: * parent directory before getting the requested directory.
467: * There is a potential race condition here if both the current
468: * and parent directories are removed before the `iget' for the
469: * inode associated with ".." returns. We hope that this occurs
470: * infrequently since we cannot avoid this race condition without
471: * implementing a sophisticated deadlock detection algorithm.
472: * Note also that this simple deadlock detection scheme will not
473: * work if the file system has any hard links other than ".."
474: * that point backwards in the directory structure.
475: */
476: pdp = dp;
477: if (ndp->ni_isdotdot) {
478: IUNLOCK(pdp); /* race to get the inode */
479: if (error = iget(dp, ndp->ni_dent.d_ino, &tdp)) {
480: ILOCK(pdp);
481: return (error);
482: }
483: if (lockparent && *ndp->ni_next == '\0')
484: ILOCK(pdp);
485: ndp->ni_vp = ITOV(tdp);
486: } else if (dp->i_number == ndp->ni_dent.d_ino) {
487: VREF(vdp); /* we want ourself, ie "." */
488: ndp->ni_vp = vdp;
489: } else {
490: if (error = iget(dp, ndp->ni_dent.d_ino, &tdp))
491: return (error);
492: if (!lockparent || *ndp->ni_next != '\0')
493: IUNLOCK(pdp);
494: ndp->ni_vp = ITOV(tdp);
495: }
496:
497: /*
498: * Insert name into cache if appropriate.
499: */
500: if (ndp->ni_makeentry)
501: cache_enter(ndp);
502: return (0);
503: }
504:
505:
506: dirbad(ip, offset, how)
507: struct inode *ip;
508: off_t offset;
509: char *how;
510: {
511:
512: printf("%s: bad dir ino %d at offset %d: %s\n",
513: ip->i_fs->fs_fsmnt, ip->i_number, offset, how);
514: panic("bad dir");
515: }
516:
517: /*
518: * Do consistency checking on a directory entry:
519: * record length must be multiple of 4
520: * entry must fit in rest of its DIRBLKSIZ block
521: * record must be large enough to contain entry
522: * name is not longer than MAXNAMLEN
523: * name must be as long as advertised, and null terminated
524: */
525: dirbadentry(ep, entryoffsetinblock)
526: register struct direct *ep;
527: int entryoffsetinblock;
528: {
529: register int i;
530:
531: if ((ep->d_reclen & 0x3) != 0 ||
532: ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
533: ep->d_reclen < DIRSIZ(ep) || ep->d_namlen > MAXNAMLEN)
534: return (1);
535: for (i = 0; i < ep->d_namlen; i++)
536: if (ep->d_name[i] == '\0')
537: return (1);
538: return (ep->d_name[i]);
539: }
540:
541: /*
542: * Write a directory entry after a call to namei, using the parameters
543: * which it left in nameidata. The argument ip is the inode which the
544: * new directory entry will refer to. The nameidata field ndp->ni_dvp
545: * is a pointer to the directory to be written, which was left locked by
546: * namei. Remaining parameters (ndp->ni_offset, ndp->ni_count) indicate
547: * how the space for the new entry is to be gotten.
548: */
549: direnter(ip, ndp)
550: struct inode *ip;
551: register struct nameidata *ndp;
552: {
553: register struct direct *ep, *nep;
554: register struct inode *dp = VTOI(ndp->ni_dvp);
555: struct buf *bp;
556: int loc, spacefree, error = 0;
557: u_int dsize;
558: int newentrysize;
559: char *dirbuf;
560:
561: ndp->ni_dent.d_ino = ip->i_number;
562: newentrysize = DIRSIZ(&ndp->ni_dent);
563: if (ndp->ni_count == 0) {
564: /*
565: * If ndp->ni_count is 0, then namei could find no space in the
566: * directory. In this case ndp->ni_offset will be on a directory
567: * block boundary and we will write the new entry into a fresh
568: * block.
569: */
570: if (ndp->ni_offset&(DIRBLKSIZ-1))
571: panic("wdir: newblk");
572: ndp->ni_dent.d_reclen = DIRBLKSIZ;
573: ndp->ni_count = newentrysize;
574: ndp->ni_resid = newentrysize;
575: ndp->ni_base = (caddr_t)&ndp->ni_dent;
576: ndp->ni_uioseg = UIO_SYSSPACE;
577: error =
578: ufs_write(ndp->ni_dvp, &ndp->ni_uio, IO_SYNC, ndp->ni_cred);
579: if (DIRBLKSIZ > dp->i_fs->fs_fsize) {
580: panic("wdir: blksize"); /* XXX - should grow w/balloc */
581: } else {
582: dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
583: dp->i_flag |= ICHG;
584: }
585: iput(dp);
586: return (error);
587: }
588:
589: /*
590: * If ndp->ni_count is non-zero, then namei found space for the new
591: * entry in the range ndp->ni_offset to ndp->ni_offset + ndp->ni_count.
592: * in the directory. To use this space, we may have to compact
593: * the entries located there, by copying them together towards
594: * the beginning of the block, leaving the free space in
595: * one usable chunk at the end.
596: */
597:
598: /*
599: * Increase size of directory if entry eats into new space.
600: * This should never push the size past a new multiple of
601: * DIRBLKSIZE.
602: *
603: * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
604: */
605: if (ndp->ni_offset + ndp->ni_count > dp->i_size)
606: dp->i_size = ndp->ni_offset + ndp->ni_count;
607: /*
608: * Get the block containing the space for the new directory entry.
609: */
610: if (error = blkatoff(dp, ndp->ni_offset, (char **)&dirbuf, &bp)) {
611: iput(dp);
612: return (error);
613: }
614: /*
615: * Find space for the new entry. In the simple case, the
616: * entry at offset base will have the space. If it does
617: * not, then namei arranged that compacting the region
618: * ndp->ni_offset to ndp->ni_offset+ndp->ni_count would yield the space.
619: */
620: ep = (struct direct *)dirbuf;
621: dsize = DIRSIZ(ep);
622: spacefree = ep->d_reclen - dsize;
623: for (loc = ep->d_reclen; loc < ndp->ni_count; ) {
624: nep = (struct direct *)(dirbuf + loc);
625: if (ep->d_ino) {
626: /* trim the existing slot */
627: ep->d_reclen = dsize;
628: ep = (struct direct *)((char *)ep + dsize);
629: } else {
630: /* overwrite; nothing there; header is ours */
631: spacefree += dsize;
632: }
633: dsize = DIRSIZ(nep);
634: spacefree += nep->d_reclen - dsize;
635: loc += nep->d_reclen;
636: bcopy((caddr_t)nep, (caddr_t)ep, dsize);
637: }
638: /*
639: * Update the pointer fields in the previous entry (if any),
640: * copy in the new entry, and write out the block.
641: */
642: if (ep->d_ino == 0) {
643: if (spacefree + dsize < newentrysize)
644: panic("wdir: compact1");
645: ndp->ni_dent.d_reclen = spacefree + dsize;
646: } else {
647: if (spacefree < newentrysize)
648: panic("wdir: compact2");
649: ndp->ni_dent.d_reclen = spacefree;
650: ep->d_reclen = dsize;
651: ep = (struct direct *)((char *)ep + dsize);
652: }
653: bcopy((caddr_t)&ndp->ni_dent, (caddr_t)ep, (u_int)newentrysize);
654: error = bwrite(bp);
655: dp->i_flag |= IUPD|ICHG;
656: if (!error && ndp->ni_endoff && ndp->ni_endoff < dp->i_size)
657: error = itrunc(dp, (u_long)ndp->ni_endoff, IO_SYNC);
658: iput(dp);
659: return (error);
660: }
661:
662: /*
663: * Remove a directory entry after a call to namei, using
664: * the parameters which it left in nameidata. The entry
665: * ni_offset contains the offset into the directory of the
666: * entry to be eliminated. The ni_count field contains the
667: * size of the previous record in the directory. If this
668: * is 0, the first entry is being deleted, so we need only
669: * zero the inode number to mark the entry as free. If the
670: * entry isn't the first in the directory, we must reclaim
671: * the space of the now empty record by adding the record size
672: * to the size of the previous entry.
673: */
674: dirremove(ndp)
675: register struct nameidata *ndp;
676: {
677: register struct inode *dp = VTOI(ndp->ni_dvp);
678: struct direct *ep;
679: struct buf *bp;
680: int error;
681:
682: if (ndp->ni_count == 0) {
683: /*
684: * First entry in block: set d_ino to zero.
685: */
686: ndp->ni_dent.d_ino = 0;
687: ndp->ni_count = ndp->ni_resid = DIRSIZ(&ndp->ni_dent);
688: ndp->ni_base = (caddr_t)&ndp->ni_dent;
689: ndp->ni_uioseg = UIO_SYSSPACE;
690: error =
691: ufs_write(ndp->ni_dvp, &ndp->ni_uio, IO_SYNC, ndp->ni_cred);
692: } else {
693: /*
694: * Collapse new free space into previous entry.
695: */
696: if (error = blkatoff(dp, ndp->ni_offset - ndp->ni_count,
697: (char **)&ep, &bp)) {
698: return (error);
699: }
700: ep->d_reclen += ndp->ni_dent.d_reclen;
701: error = bwrite(bp);
702: dp->i_flag |= IUPD|ICHG;
703: }
704: return (error);
705: }
706:
707: /*
708: * Rewrite an existing directory entry to point at the inode
709: * supplied. The parameters describing the directory entry are
710: * set up by a call to namei.
711: */
712: dirrewrite(dp, ip, ndp)
713: struct inode *dp, *ip;
714: struct nameidata *ndp;
715: {
716:
717: ndp->ni_dent.d_ino = ip->i_number;
718: ndp->ni_count = ndp->ni_resid = DIRSIZ(&ndp->ni_dent);
719: ndp->ni_base = (caddr_t)&ndp->ni_dent;
720: ndp->ni_uioseg = UIO_SYSSPACE;
721: return (ufs_write(ITOV(dp), &ndp->ni_uio, IO_SYNC, ndp->ni_cred));
722: }
723:
724: /*
725: * Return buffer with contents of block "offset"
726: * from the beginning of directory "ip". If "res"
727: * is non-zero, fill it in with a pointer to the
728: * remaining space in the directory.
729: */
730: blkatoff(ip, offset, res, bpp)
731: struct inode *ip;
732: off_t offset;
733: char **res;
734: struct buf **bpp;
735: {
736: register struct fs *fs = ip->i_fs;
737: daddr_t lbn = lblkno(fs, offset);
738: int bsize = blksize(fs, ip, lbn);
739: struct buf *bp;
740: daddr_t bn;
741: int error;
742:
743: *bpp = 0;
744: if (error = bread(ITOV(ip), lbn, bsize, NOCRED, &bp)) {
745: brelse(bp);
746: return (error);
747: }
748: if (res)
749: *res = bp->b_un.b_addr + blkoff(fs, offset);
750: *bpp = bp;
751: return (0);
752: }
753:
754: /*
755: * Check if a directory is empty or not.
756: * Inode supplied must be locked.
757: *
758: * Using a struct dirtemplate here is not precisely
759: * what we want, but better than using a struct direct.
760: *
761: * NB: does not handle corrupted directories.
762: */
763: dirempty(ip, parentino, cred)
764: register struct inode *ip;
765: ino_t parentino;
766: struct ucred *cred;
767: {
768: register off_t off;
769: struct dirtemplate dbuf;
770: register struct direct *dp = (struct direct *)&dbuf;
771: int error, count;
772: #define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
773:
774: for (off = 0; off < ip->i_size; off += dp->d_reclen) {
775: error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ,
776: off, UIO_SYSSPACE, IO_NODELOCKED, cred, &count);
777: /*
778: * Since we read MINDIRSIZ, residual must
779: * be 0 unless we're at end of file.
780: */
781: if (error || count != 0)
782: return (0);
783: /* avoid infinite loops */
784: if (dp->d_reclen == 0)
785: return (0);
786: /* skip empty entries */
787: if (dp->d_ino == 0)
788: continue;
789: /* accept only "." and ".." */
790: if (dp->d_namlen > 2)
791: return (0);
792: if (dp->d_name[0] != '.')
793: return (0);
794: /*
795: * At this point d_namlen must be 1 or 2.
796: * 1 implies ".", 2 implies ".." if second
797: * char is also "."
798: */
799: if (dp->d_namlen == 1)
800: continue;
801: if (dp->d_name[1] == '.' && dp->d_ino == parentino)
802: continue;
803: return (0);
804: }
805: return (1);
806: }
807:
808: /*
809: * Check if source directory is in the path of the target directory.
810: * Target is supplied locked, source is unlocked.
811: * The target is always iput() before returning.
812: */
813: checkpath(source, target, cred)
814: struct inode *source, *target;
815: struct ucred *cred;
816: {
817: struct dirtemplate dirbuf;
818: struct inode *ip;
819: int error = 0;
820:
821: ip = target;
822: if (ip->i_number == source->i_number) {
823: error = EEXIST;
824: goto out;
825: }
826: if (ip->i_number == ROOTINO)
827: goto out;
828:
829: for (;;) {
830: if ((ip->i_mode&IFMT) != IFDIR) {
831: error = ENOTDIR;
832: break;
833: }
834: error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)&dirbuf,
835: sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
836: IO_NODELOCKED, cred, (int *)0);
837: if (error != 0)
838: break;
839: if (dirbuf.dotdot_namlen != 2 ||
840: dirbuf.dotdot_name[0] != '.' ||
841: dirbuf.dotdot_name[1] != '.') {
842: error = ENOTDIR;
843: break;
844: }
845: if (dirbuf.dotdot_ino == source->i_number) {
846: error = EINVAL;
847: break;
848: }
849: if (dirbuf.dotdot_ino == ROOTINO)
850: break;
851: iput(ip);
852: if (error = iget(ip, dirbuf.dotdot_ino, &ip))
853: break;
854: }
855:
856: out:
857: if (error == ENOTDIR)
858: printf("checkpath: .. not a directory\n");
859: if (ip != NULL)
860: iput(ip);
861: return (error);
862: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.