|
|
1.1 root 1: /*
2: * Written by Paul Popelka ([email protected])
3: *
4: * You can do anything you want with this software,
5: * just don't say you wrote it,
6: * and don't remove this notice.
7: *
8: * This software is provided "as is".
9: *
10: * The author supplies this software to be publicly
11: * redistributed on the understanding that the author
12: * is not responsible for the correct functioning of
13: * this software in any circumstances and is not liable
14: * for any damages caused by this software.
15: *
16: * October 1992
17: *
18: * pcfs_lookup.c,v 1.3 1993/05/20 03:34:16 cgd Exp
19: */
20:
21: #include "param.h"
22: #include "namei.h"
23: #include "buf.h"
24: #include "vnode.h"
25: #include "mount.h"
26:
27: #include "bpb.h"
28: #include "direntry.h"
29: #include "denode.h"
30: #include "pcfsmount.h"
31: #include "fat.h"
32:
33: /*
34: * When we search a directory the blocks containing directory
35: * entries are read and examined. The directory entries
36: * contain information that would normally be in the inode
37: * of a unix filesystem. This means that some of a directory's
38: * contents may also be in memory resident denodes (sort of
39: * an inode). This can cause problems if we are searching
40: * while some other process is modifying a directory. To
41: * prevent one process from accessing incompletely modified
42: * directory information we depend upon being the soul owner
43: * of a directory block. bread/brelse provide this service.
44: * This being the case, when a process modifies a directory
45: * it must first acquire the disk block that contains the
46: * directory entry to be modified. Then update the disk
47: * block and the denode, and then write the disk block out
48: * to disk. This way disk blocks containing directory
49: * entries and in memory denode's will be in synch.
50: */
51: int
52: pcfs_lookup(vdp, ndp, p)
53: struct vnode *vdp; /* vnode of directory to search */
54: struct nameidata *ndp;
55: struct proc *p;
56: {
57: daddr_t bn;
58: int flag;
59: int error;
60: int lockparent;
61: int wantparent;
62: int slotstatus;
63: #define NONE 0
64: #define FOUND 1
65: int slotoffset;
66: int slotcluster;
67: int frcn;
68: u_long cluster;
69: int rootreloff;
70: int diroff;
71: int isadir; /* ~0 if found direntry is a directory */
72: u_long scn; /* starting cluster number */
73: struct denode *dp;
74: struct denode *pdp;
75: struct denode *tdp;
76: struct pcfsmount *pmp;
77: struct buf *bp = 0;
78: struct direntry *dep;
79: u_char dosfilename[12];
80:
81: #if defined(PCFSDEBUG)
82: printf("pcfs_lookup(): looking for %s\n", ndp->ni_ptr);
83: #endif /* defined(PCFSDEBUG) */
84: ndp->ni_dvp = vdp;
85: ndp->ni_vp = NULL;
86: dp = VTODE(vdp);
87: pmp = dp->de_pmp;
88: lockparent = ndp->ni_nameiop & LOCKPARENT;
89: flag = ndp->ni_nameiop & OPMASK;
90: wantparent = ndp->ni_nameiop & (LOCKPARENT | WANTPARENT);
91: #if defined(PCFSDEBUG)
92: printf("pcfs_lookup(): vdp %08x, dp %08x, Attr %02x\n",
93: vdp, dp, dp->de_Attributes);
94: #endif /* defined(PCFSDEBUG) */
95:
96: /*
97: * Be sure vdp is a directory. Since dos filesystems
98: * don't have the concept of execute permission anybody
99: * can search a directory.
100: */
101: if ((dp->de_Attributes & ATTR_DIRECTORY) == 0)
102: return ENOTDIR;
103:
104: /*
105: * See if the component of the pathname we are looking for
106: * is in the directory cache. If so then do a few things
107: * and return.
108: */
109: if (error = cache_lookup(ndp)) {
110: int vpid;
111:
112: if (error == ENOENT)
113: return error;
114: #ifdef PARANOID
115: if (vdp == ndp->ni_rdir && ndp->ni_isdotdot)
116: panic("pcfs_lookup: .. thru root");
117: #endif /* PARANOID */
118: pdp = dp;
119: vdp = ndp->ni_vp;
120: dp = VTODE(vdp);
121: vpid = vdp->v_id;
122: if (pdp == dp) {
123: VREF(vdp);
124: error = 0;
125: } else if (ndp->ni_isdotdot) {
126: DEUNLOCK(pdp);
127: error = vget(vdp);
128: if (!error && lockparent && *ndp->ni_next == '\0')
129: DELOCK(pdp);
130: } else {
131: error = vget(vdp);
132: if (!lockparent || error || *ndp->ni_next != '\0')
133: DEUNLOCK(pdp);
134: }
135:
136: if (!error) {
137: if (vpid == vdp->v_id) {
138: #if defined(PCFSDEBUG)
139: printf("pcfs_lookup(): cache hit, vnode %08x, file %s\n", vdp, dp->de_Name);
140: #endif /* defined(PCFSDEBUG) */
141: return 0;
142: }
143: deput(dp);
144: if (lockparent && pdp != dp && *ndp->ni_next == '\0')
145: DEUNLOCK(pdp);
146: }
147: DELOCK(pdp);
148: dp = pdp;
149: vdp = DETOV(dp);
150: ndp->ni_vp = NULL;
151: }
152:
153: /*
154: * If they are going after the . or .. entry in the
155: * root directory, they won't find it. DOS filesystems
156: * don't have them in the root directory. So, we fake it.
157: * deget() is in on this scam too.
158: */
159: if ((vdp->v_flag & VROOT) && ndp->ni_ptr[0] == '.' &&
160: (ndp->ni_namelen == 1 ||
161: (ndp->ni_namelen == 2 && ndp->ni_ptr[1] == '.'))) {
162: isadir = ATTR_DIRECTORY;
163: scn = PCFSROOT;
164: #if defined(PCFSDEBUG)
165: printf("pcfs_lookup(): looking for . or .. in root directory\n");
166: #endif /* defined(PCFSDEBUG) */
167: cluster == PCFSROOT;
168: diroff = PCFSROOT_OFS;
169: goto foundroot;
170: }
171:
172: /*
173: * Don't search for free slots unless we are creating
174: * a filename and we are at the end of the pathname.
175: */
176: slotstatus = FOUND;
177: if ((flag == CREATE || flag == RENAME) && *ndp->ni_next == '\0') {
178: slotstatus = NONE;
179: slotoffset = -1;
180: }
181:
182: unix2dosfn((u_char *)ndp->ni_ptr, dosfilename, ndp->ni_namelen);
183: dosfilename[11] = 0;
184: #if defined(PCFSDEBUG)
185: printf("pcfs_lookup(): dos version of filename %s, length %d\n",
186: dosfilename, ndp->ni_namelen);
187: #endif /* defined(PCFSDEBUG) */
188: /*
189: * Search the directory pointed at by vdp for the
190: * name pointed at by ndp->ni_ptr.
191: */
192: tdp = NULL;
193: /*
194: * The outer loop ranges over the clusters that make
195: * up the directory. Note that the root directory is
196: * different from all other directories. It has a
197: * fixed number of blocks that are not part of the
198: * pool of allocatable clusters. So, we treat it a
199: * little differently.
200: * The root directory starts at "cluster" 0.
201: */
202: rootreloff = 0;
203: for (frcn = 0; ; frcn++) {
204: if (error = pcbmap(dp, frcn, &bn, &cluster)) {
205: if (error == E2BIG)
206: break;
207: return error;
208: }
209: error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, &bp);
210: if (error) {
211: brelse(bp);
212: return error;
213: }
214: dep = (struct direntry *)bp->b_un.b_addr;
215: for (diroff = 0; diroff < pmp->pm_depclust; diroff++, dep++) {
216: /*
217: * If the slot is empty and we are still looking for
218: * an empty then remember this one. If the slot is
219: * not empty then check to see if it matches what we
220: * are looking for. If the slot has never been filled
221: * with anything, then the remainder of the directory
222: * has never been used, so there is no point in searching
223: * it.
224: */
225: if (dep->deName[0] == SLOT_EMPTY ||
226: dep->deName[0] == SLOT_DELETED) {
227: if (slotstatus != FOUND) {
228: slotstatus = FOUND;
229: if (cluster == PCFSROOT)
230: slotoffset = rootreloff;
231: else
232: slotoffset = diroff;
233: slotcluster = cluster;
234: }
235: if (dep->deName[0] == SLOT_EMPTY) {
236: brelse(bp);
237: goto notfound;
238: }
239: } else {
240: /* Ignore volume labels (anywhere, not just
241: * the root directory). */
242: if ((dep->deAttributes & ATTR_VOLUME) == 0 &&
243: bcmp(dosfilename, dep->deName, 11) == 0) {
244: #if defined(PCFSDEBUG)
245: printf("pcfs_lookup(): match diroff %d, rootreloff %d\n", diroff, rootreloff);
246: #endif /* defined(PCFSDEBUG) */
247: /*
248: * Remember where this directory entry came from
249: * for whoever did this lookup.
250: * If this is the root directory we are interested
251: * in the offset relative to the beginning of the
252: * directory (not the beginning of the cluster).
253: */
254: if (cluster == PCFSROOT)
255: diroff = rootreloff;
256: ndp->ni_pcfs.pcfs_offset = diroff;
257: ndp->ni_pcfs.pcfs_cluster = cluster;
258: goto found;
259: }
260: }
261: rootreloff++;
262: } /* for (diroff = 0; .... */
263: /*
264: * Release the buffer holding the directory cluster
265: * just searched.
266: */
267: brelse(bp);
268: } /* for (frcn = 0; ; frcn++) */
269: notfound:;
270: /*
271: * We hold no disk buffers at this point.
272: */
273:
274: /*
275: * If we get here we didn't find the entry we were looking
276: * for. But that's ok if we are creating or renaming and
277: * are at the end of the pathname and the directory hasn't
278: * been removed.
279: */
280: #if defined(PCFSDEBUG)
281: printf("pcfs_lookup(): flag %d, refcnt %d, slotstatus %d\n",
282: flag, dp->de_refcnt, slotstatus);
283: printf(" slotoffset %d, slotcluster %d\n",
284: slotoffset, slotcluster);
285: #endif /* defined(PCFSDEBUG) */
286: if ((flag == CREATE || flag == RENAME) &&
287: *ndp->ni_next == '\0' && dp->de_refcnt != 0) {
288: if (slotstatus == NONE) {
289: ndp->ni_pcfs.pcfs_offset = 0;
290: ndp->ni_pcfs.pcfs_cluster = 0;
291: ndp->ni_pcfs.pcfs_count = 0;
292: } else {
293: #if defined(PCFSDEBUG)
294: printf("pcfs_lookup(): saving empty slot location\n");
295: #endif /* defined(PCFSDEBUG) */
296: ndp->ni_pcfs.pcfs_offset = slotoffset;
297: ndp->ni_pcfs.pcfs_cluster = slotcluster;
298: ndp->ni_pcfs.pcfs_count = 1;
299: }
300: /* dp->de_flag |= DEUPD; /* never update dos directories */
301: ndp->ni_nameiop |= SAVENAME;
302: if (!lockparent) /* leave searched dir locked? */
303: DEUNLOCK(dp);
304: }
305: /*
306: * Insert name in cache as non-existant if not
307: * trying to create it.
308: */
309: if (ndp->ni_makeentry && flag != CREATE)
310: cache_enter(ndp);
311: return ENOENT;
312:
313: found:;
314: /*
315: * NOTE: We still have the buffer with matched
316: * directory entry at this point.
317: */
318: isadir = dep->deAttributes & ATTR_DIRECTORY;
319: scn = dep->deStartCluster;
320:
321: foundroot:;
322: /*
323: * If we entered at foundroot, then we are looking
324: * for the . or .. entry of the filesystems root
325: * directory. isadir and scn were setup before
326: * jumping here. And, bp is null. There is no buf header.
327: */
328:
329: /*
330: * If deleting and at the end of the path, then
331: * if we matched on "." then don't deget() we would
332: * probably panic(). Otherwise deget() the directory
333: * entry.
334: */
335: if (flag == DELETE && ndp->ni_next == '\0') {
336: if (dp->de_StartCluster == scn &&
337: isadir) { /* "." */
338: VREF(vdp);
339: ndp->ni_vp = vdp;
340: if (bp) brelse(bp);
341: return 0;
342: }
343: error = deget(pmp, cluster, diroff, dep, &tdp);
344: if (error) {
345: if (bp) brelse(bp);
346: return error;
347: }
348: ndp->ni_vp = DETOV(tdp);
349: if (!lockparent)
350: DEUNLOCK(dp);
351: if (bp) brelse(bp);
352: return 0;
353: }
354:
355: /*
356: * If renaming.
357: */
358: if (flag == RENAME && wantparent && *ndp->ni_next == '\0') {
359: if (dp->de_StartCluster == scn &&
360: isadir) {
361: if (bp) brelse(bp);
362: return EISDIR;
363: }
364: error = deget(pmp, cluster, diroff, dep, &tdp);
365: if (error) {
366: if (bp) brelse(bp);
367: return error;
368: }
369: ndp->ni_vp = DETOV(tdp);
370: ndp->ni_nameiop |= SAVENAME;
371: if (!lockparent)
372: DEUNLOCK(dp);
373: if (bp) brelse(bp);
374: return 0;
375: }
376:
377: /*
378: * ?
379: */
380: pdp = dp;
381: if (ndp->ni_isdotdot) {
382: DEUNLOCK(pdp);
383: error = deget(pmp, cluster, diroff, dep, &tdp);
384: if (error) {
385: DELOCK(pdp);
386: if (bp) brelse(bp);
387: return error;
388: }
389: if (lockparent && *ndp->ni_next == '\0')
390: DELOCK(pdp);
391: ndp->ni_vp = DETOV(tdp);
392: } else if (dp->de_StartCluster == scn &&
393: isadir) { /* "." */
394: VREF(vdp);
395: ndp->ni_vp = vdp;
396: } else {
397: error = deget(pmp, cluster, diroff, dep, &tdp);
398: if (error) {
399: if (bp) brelse(bp);
400: return error;
401: }
402: if (!lockparent || *ndp->ni_next != '\0')
403: DEUNLOCK(pdp);
404: ndp->ni_vp = DETOV(tdp);
405: }
406: if (bp) brelse(bp);
407:
408: /*
409: * Insert name in cache if wanted.
410: */
411: if (ndp->ni_makeentry)
412: cache_enter(ndp);
413: return 0;
414: }
415:
416: /*
417: * dep - directory to copy into the directory
418: * ndp - nameidata structure containing info on
419: * where to put the directory entry in the directory.
420: * depp - return the address of the denode for the
421: * created directory entry if depp != 0
422: */
423: int
424: createde(dep, ndp, depp)
425: struct denode *dep;
426: struct nameidata *ndp;
427: struct denode **depp;
428: {
429: int bn;
430: int error;
431: u_long dirclust, diroffset;
432: struct direntry *ndep;
433: struct denode *ddep = VTODE(ndp->ni_dvp); /* directory to add to */
434: struct pcfsmount *pmp = dep->de_pmp;
435: struct buf *bp;
436: #if defined(PCFSDEBUG)
437: printf("createde(dep %08x, ndp %08x, depp %08x)\n", dep, ndp, depp);
438: #endif /* defined(PCFSDEBUG) */
439:
440: /*
441: * If no space left in the directory then allocate
442: * another cluster and chain it onto the end of the
443: * file. There is one exception to this. That is,
444: * if the root directory has no more space it can NOT
445: * be expanded. extendfile() checks for and fails attempts to
446: * extend the root directory. We just return an error
447: * in that case.
448: */
449: if (ndp->ni_pcfs.pcfs_count == 0) {
450: if (error = extendfile(ddep, &bp, &dirclust))
451: return error;
452: ndep = (struct direntry *)bp->b_un.b_addr;
453: /*
454: * Let caller know where we put the directory entry.
455: */
456: ndp->ni_pcfs.pcfs_cluster = dirclust;
457: ndp->ni_pcfs.pcfs_offset = diroffset = 0;
458: }
459:
460: else {
461: /*
462: * There is space in the existing directory. So,
463: * we just read in the cluster with space. Copy
464: * the new directory entry in. Then write it to
465: * disk.
466: * NOTE: DOS directories do not get smaller as
467: * clusters are emptied.
468: */
469: dirclust = ndp->ni_pcfs.pcfs_cluster;
470: diroffset = ndp->ni_pcfs.pcfs_offset;
471:
472: error = readep(pmp, dirclust, diroffset, &bp, &ndep);
473: if (error)
474: return error;
475: }
476: *ndep = dep->de_de;
477: /*
478: * If they want us to return with the denode gotten.
479: */
480: if (depp) {
481: error = deget(pmp, dirclust, diroffset, ndep, depp);
482: if (error)
483: return error;
484: }
485: if (error = bwrite(bp))
486: /*deput()?*/
487: return error;
488: return 0;
489: }
490:
491: /*
492: * Read in a directory entry and mark it as being deleted.
493: */
494: int
495: markdeleted(pmp, dirclust, diroffset)
496: struct pcfsmount *pmp;
497: u_long dirclust;
498: u_long diroffset;
499: {
500: int error;
501: struct direntry *ep;
502: struct buf *bp;
503:
504: error = readep(pmp, dirclust, diroffset, &bp, &ep);
505: if (error)
506: return error;
507: ep->deName[0] = SLOT_DELETED;
508: return bwrite(bp);
509: }
510:
511: /*
512: * Remove a directory entry.
513: * At this point the file represented by the directory
514: * entry to be removed is still full length until no
515: * one has it open. When the file no longer being
516: * used pcfs_inactive() is called and will truncate
517: * the file to 0 length. When the vnode containing
518: * the denode is needed for some other purpose by
519: * VFS it will call pcfs_reclaim() which will remove
520: * the denode from the denode cache.
521: */
522: int
523: removede(ndp)
524: struct nameidata *ndp;
525: {
526: struct denode *dep = VTODE(ndp->ni_vp); /* the file being removed */
527: struct pcfsmount *pmp = dep->de_pmp;
528: int error;
529:
530: #if defined(PCFSDEBUG)
531: /*printf("removede(): filename %s\n", dep->de_Name);
532: printf("rmde(): dep %08x, ndpcluster %d, ndpoffset %d\n",
533: dep, ndp->ni_pcfs.pcfs_cluster, ndp->ni_pcfs.pcfs_offset);*/
534: #endif /* defined(PCFSDEBUG) */
535:
536: /*
537: * Read the directory block containing the directory
538: * entry we are to make free. The nameidata structure
539: * holds the cluster number and directory entry index
540: * number of the entry to free.
541: */
542: error = markdeleted(pmp, ndp->ni_pcfs.pcfs_cluster,
543: ndp->ni_pcfs.pcfs_offset);
544:
545: dep->de_refcnt--;
546: return error;
547: }
548:
549: /*
550: * Be sure a directory is empty except for "." and "..".
551: * Return 1 if empty, return 0 if not empty or error.
552: */
553: int
554: dosdirempty(dep)
555: struct denode *dep;
556: {
557: int dei;
558: int error;
559: u_long cn;
560: daddr_t bn;
561: struct buf *bp;
562: struct pcfsmount *pmp = dep->de_pmp;
563: struct direntry *dentp;
564:
565: /*
566: * Since the filesize field in directory entries for a directory
567: * is zero, we just have to feel our way through the directory
568: * until we hit end of file.
569: */
570: for (cn = 0;; cn++) {
571: error = pcbmap(dep, cn, &bn, 0);
572: if (error == E2BIG)
573: return 1; /* it's empty */
574: error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED,
575: &bp);
576: if (error) {
577: brelse(bp);
578: return error;
579: }
580: dentp = (struct direntry *)bp->b_un.b_addr;
581: for (dei = 0; dei < pmp->pm_depclust; dei++) {
582: if (dentp->deName[0] != SLOT_DELETED) {
583: /*
584: * In dos directories an entry whose name starts with SLOT_EMPTY (0)
585: * starts the beginning of the unused part of the directory, so we
586: * can just return that it is empty.
587: */
588: if (dentp->deName[0] == SLOT_EMPTY) {
589: brelse(bp);
590: return 1;
591: }
592: /*
593: * Any names other than "." and ".." in a directory mean
594: * it is not empty.
595: */
596: if (bcmp(dentp->deName, ". ", 11) &&
597: bcmp(dentp->deName, ".. ", 11)) {
598: brelse(bp);
599: #if defined(PCFSDEBUG)
600: printf("dosdirempty(): entry %d found %02x, %02x\n", dei, dentp->deName[0],
601: dentp->deName[1]);
602: #endif /* defined(PCFSDEBUG) */
603: return 0; /* not empty */
604: }
605: }
606: dentp++;
607: }
608: brelse(bp);
609: }
610: /*NOTREACHED*/
611: }
612:
613: /*
614: * Check to see if the directory described by target is
615: * in some subdirectory of source. This prevents something
616: * like the following from succeeding and leaving a bunch
617: * or files and directories orphaned.
618: * mv /a/b/c /a/b/c/d/e/f
619: * Where c and f are directories.
620: * source - the inode for /a/b/c
621: * target - the inode for /a/b/c/d/e/f
622: * Returns 0 if target is NOT a subdirectory of source.
623: * Otherwise returns a non-zero error number.
624: * The target inode is always unlocked on return.
625: */
626: int
627: doscheckpath(source, target)
628: struct denode *source;
629: struct denode *target;
630: {
631: daddr_t scn;
632: struct denode dummy;
633: struct pcfsmount *pmp;
634: struct direntry *ep;
635: struct denode *dep;
636: struct buf *bp = NULL;
637: int error = 0;
638:
639: dep = target;
640: if ((target->de_Attributes & ATTR_DIRECTORY) == 0 ||
641: (source->de_Attributes & ATTR_DIRECTORY) == 0) {
642: error = ENOTDIR;
643: goto out;
644: }
645: if (dep->de_StartCluster == source->de_StartCluster) {
646: error = EEXIST;
647: goto out;
648: }
649: if (dep->de_StartCluster == PCFSROOT)
650: goto out;
651: for (;;) {
652: if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
653: error = ENOTDIR;
654: break;
655: }
656: pmp = dep->de_pmp;
657: scn = dep->de_StartCluster;
658: error = readep(pmp, scn, 1, &bp, &ep);
659: if (error)
660: break;
661: if ((ep->deAttributes & ATTR_DIRECTORY) == 0 ||
662: bcmp(ep->deName, ".. ", 11) != 0) {
663: /* Bad Directory */
664: error = ENOTDIR;
665: break;
666: }
667: if (ep->deStartCluster == source->de_StartCluster) {
668: error = EINVAL;
669: break;
670: }
671: if (ep->deStartCluster == PCFSROOT)
672: break;
673: deput(dep);
674: /* NOTE: deget() clears dep on error */
675: error = deget(pmp, ep->deStartCluster, 0, ep, &dep);
676: brelse(bp);
677: bp = NULL;
678: if (error)
679: break;
680: }
681: out:;
682: if (bp)
683: brelse(bp);
684: if (error == ENOTDIR)
685: printf("doscheckpath(): .. not a directory?\n");
686: if (dep != NULL)
687: deput(dep);
688: return error;
689: }
690:
691: /*
692: * Read in the disk block containing the directory entry
693: * (dirclu, dirofs) and return the address of the buf header,
694: * and the address of the directory entry within the block.
695: */
696: int
697: readep(pmp, dirclu, dirofs, bpp, epp)
698: struct pcfsmount *pmp;
699: u_long dirclu, dirofs;
700: struct buf **bpp;
701: struct direntry **epp;
702: {
703: int error;
704: daddr_t bn;
705:
706: bn = detobn(pmp, dirclu, dirofs);
707: if (error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, bpp)) {
708: brelse(*bpp);
709: *bpp = NULL;
710: return error;
711: }
712: if (epp)
713: *epp = bptoep(pmp, *bpp, dirofs);
714: return 0;
715: }
716:
717:
718: /*
719: * Read in the disk block containing the directory entry
720: * dep came from and return the address of the buf header,
721: * and the address of the directory entry within the block.
722: */
723: int
724: readde(dep, bpp, epp)
725: struct denode *dep;
726: struct buf **bpp;
727: struct direntry **epp;
728: {
729: return readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset,
730: bpp, epp);
731: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.