Annotation of sys/msdosfs/msdosfs_lookup.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: msdosfs_lookup.c,v 1.19 2007/06/02 02:04:21 deraadt Exp $ */
2: /* $NetBSD: msdosfs_lookup.c,v 1.34 1997/10/18 22:12:27 ws Exp $ */
3:
4: /*-
5: * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
6: * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
7: * All rights reserved.
8: * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: * 3. All advertising materials mentioning features or use of this software
19: * must display the following acknowledgement:
20: * This product includes software developed by TooLs GmbH.
21: * 4. The name of TooLs GmbH may not be used to endorse or promote products
22: * derived from this software without specific prior written permission.
23: *
24: * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
25: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27: * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
30: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34: */
35: /*
36: * Written by Paul Popelka (paulp@uts.amdahl.com)
37: *
38: * You can do anything you want with this software, just don't say you wrote
39: * it, and don't remove this notice.
40: *
41: * This software is provided "as is".
42: *
43: * The author supplies this software to be publicly redistributed on the
44: * understanding that the author is not responsible for the correct
45: * functioning of this software in any circumstances and is not liable for
46: * any damages caused by this software.
47: *
48: * October 1992
49: */
50:
51: #include <sys/param.h>
52: #include <sys/systm.h>
53: #include <sys/namei.h>
54: #include <sys/buf.h>
55: #include <sys/vnode.h>
56: #include <sys/mount.h>
57: #include <sys/dirent.h>
58:
59: #include <msdosfs/bpb.h>
60: #include <msdosfs/direntry.h>
61: #include <msdosfs/denode.h>
62: #include <msdosfs/msdosfsmount.h>
63: #include <msdosfs/fat.h>
64:
65: /*
66: * When we search a directory the blocks containing directory entries are
67: * read and examined. The directory entries contain information that would
68: * normally be in the inode of a unix filesystem. This means that some of
69: * a directory's contents may also be in memory resident denodes (sort of
70: * an inode). This can cause problems if we are searching while some other
71: * process is modifying a directory. To prevent one process from accessing
72: * incompletely modified directory information we depend upon being the
73: * sole owner of a directory block. bread/brelse provide this service.
74: * This being the case, when a process modifies a directory it must first
75: * acquire the disk block that contains the directory entry to be modified.
76: * Then update the disk block and the denode, and then write the disk block
77: * out to disk. This way disk blocks containing directory entries and in
78: * memory denode's will be in synch.
79: */
80: int
81: msdosfs_lookup(v)
82: void *v;
83: {
84: struct vop_lookup_args *ap = v;
85: struct vnode *vdp = ap->a_dvp;
86: struct vnode **vpp = ap->a_vpp;
87: struct componentname *cnp = ap->a_cnp;
88: struct proc *p = cnp->cn_proc;
89: daddr64_t bn;
90: int error;
91: int lockparent;
92: int wantparent;
93: int slotcount;
94: int slotoffset = 0;
95: int frcn;
96: uint32_t cluster;
97: int blkoff;
98: int diroff;
99: int blsize;
100: int isadir; /* ~0 if found direntry is a directory */
101: uint32_t scn; /* starting cluster number */
102: struct vnode *pdp;
103: struct denode *dp;
104: struct denode *tdp;
105: struct msdosfsmount *pmp;
106: struct buf *bp = 0;
107: struct direntry *dep;
108: u_char dosfilename[12];
109: u_char *adjp;
110: int adjlen;
111: int flags;
112: int nameiop = cnp->cn_nameiop;
113: int wincnt = 1;
114: int chksum = -1, chksum_ok;
115: int olddos = 1;
116:
117: cnp->cn_flags &= ~PDIRUNLOCK; /* XXX why this ?? */
118: flags = cnp->cn_flags;
119:
120: #ifdef MSDOSFS_DEBUG
121: printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr);
122: #endif
123: dp = VTODE(vdp);
124: pmp = dp->de_pmp;
125: *vpp = NULL;
126: lockparent = flags & LOCKPARENT;
127: wantparent = flags & (LOCKPARENT | WANTPARENT);
128: #ifdef MSDOSFS_DEBUG
129: printf("msdosfs_lookup(): vdp %08x, dp %08x, Attr %02x\n",
130: vdp, dp, dp->de_Attributes);
131: #endif
132:
133: /*
134: * Check accessiblity of directory.
135: */
136: if ((dp->de_Attributes & ATTR_DIRECTORY) == 0)
137: return (ENOTDIR);
138: if ((error = VOP_ACCESS(vdp, VEXEC, cnp->cn_cred, cnp->cn_proc)) != 0)
139: return (error);
140:
141: /*
142: * We now have a segment name to search for, and a directory to search.
143: *
144: * Before tediously performing a linear scan of the directory,
145: * check the name cache to see if the directory/name pair
146: * we are looking for is known already.
147: */
148: if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
149: return (error);
150:
151: /*
152: * If they are going after the . or .. entry in the root directory,
153: * they won't find it. DOS filesystems don't have them in the root
154: * directory. So, we fake it. deget() is in on this scam too.
155: */
156: if ((vdp->v_flag & VROOT) && cnp->cn_nameptr[0] == '.' &&
157: (cnp->cn_namelen == 1 ||
158: (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) {
159: isadir = ATTR_DIRECTORY;
160: scn = MSDOSFSROOT;
161: #ifdef MSDOSFS_DEBUG
162: printf("msdosfs_lookup(): looking for . or .. in root directory\n");
163: #endif
164: cluster = MSDOSFSROOT;
165: blkoff = MSDOSFSROOT_OFS;
166: goto foundroot;
167: }
168:
169: switch (unix2dosfn((u_char *)cnp->cn_nameptr, dosfilename, cnp->cn_namelen, 0)) {
170: case 0:
171: return (EINVAL);
172: case 1:
173: break;
174: case 2:
175: wincnt = winSlotCnt((u_char *)cnp->cn_nameptr, cnp->cn_namelen) + 1;
176: break;
177: case 3:
178: olddos = 0;
179: wincnt = winSlotCnt((u_char *)cnp->cn_nameptr, cnp->cn_namelen) + 1;
180: break;
181: }
182: if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
183: wincnt = 1;
184:
185: /*
186: * Suppress search for slots unless creating
187: * file and at end of pathname, in which case
188: * we watch for a place to put the new file in
189: * case it doesn't already exist.
190: */
191: slotcount = wincnt;
192: if ((nameiop == CREATE || nameiop == RENAME) &&
193: (flags & ISLASTCN))
194: slotcount = 0;
195:
196: #ifdef MSDOSFS_DEBUG
197: printf("msdosfs_lookup(): dos version of filename %s, length %d\n",
198: dosfilename, cnp->cn_namelen);
199: #endif
200: /*
201: * We want to search the directory pointed to by vdp for the name
202: * pointed to by cnp->cn_nameptr.
203: *
204: * XXX UNIX allows filenames with trailing dots and blanks; we don't.
205: * Most of the routines in msdosfs_conv.c adjust for this, but
206: * winChkName() does not, so we do it here. Otherwise, a file
207: * such as ".foobar." cannot be retrieved properly.
208: *
209: * (Note that this is also faster: perform the adjustment once,
210: * rather than on each call to winChkName. However, it is still
211: * a nasty hack.)
212: */
213: adjp = cnp->cn_nameptr;
214: adjlen = cnp->cn_namelen;
215:
216: for (adjp += adjlen; adjlen > 0; adjlen--)
217: if (*--adjp != ' ' && *adjp != '.')
218: break;
219:
220: tdp = NULL;
221: /*
222: * The outer loop ranges over the clusters that make up the
223: * directory. Note that the root directory is different from all
224: * other directories. It has a fixed number of blocks that are not
225: * part of the pool of allocatable clusters. So, we treat it a
226: * little differently. The root directory starts at "cluster" 0.
227: */
228: diroff = 0;
229: for (frcn = 0;; frcn++) {
230: if ((error = pcbmap(dp, frcn, &bn, &cluster, &blsize)) != 0) {
231: if (error == E2BIG)
232: break;
233: return (error);
234: }
235: error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
236: if (error) {
237: brelse(bp);
238: return (error);
239: }
240: for (blkoff = 0; blkoff < blsize;
241: blkoff += sizeof(struct direntry),
242: diroff += sizeof(struct direntry)) {
243: dep = (struct direntry *)(bp->b_data + blkoff);
244: /*
245: * If the slot is empty and we are still looking
246: * for an empty then remember this one. If the
247: * slot is not empty then check to see if it
248: * matches what we are looking for. If the slot
249: * has never been filled with anything, then the
250: * remainder of the directory has never been used,
251: * so there is no point in searching it.
252: */
253: if (dep->deName[0] == SLOT_EMPTY ||
254: dep->deName[0] == SLOT_DELETED) {
255: /*
256: * Drop memory of previous long matches
257: */
258: chksum = -1;
259:
260: if (slotcount < wincnt) {
261: slotcount++;
262: slotoffset = diroff;
263: }
264: if (dep->deName[0] == SLOT_EMPTY) {
265: brelse(bp);
266: goto notfound;
267: }
268: } else {
269: /*
270: * If there wasn't enough space for our winentries,
271: * forget about the empty space
272: */
273: if (slotcount < wincnt)
274: slotcount = 0;
275:
276: /*
277: * Check for Win95 long filename entry
278: */
279: if (dep->deAttributes == ATTR_WIN95) {
280: if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
281: continue;
282:
283: chksum = winChkName((u_char *)cnp->cn_nameptr,
284: adjlen,
285: (struct winentry *)dep,
286: chksum);
287: continue;
288: }
289:
290: /*
291: * Ignore volume labels (anywhere, not just
292: * the root directory).
293: */
294: if (dep->deAttributes & ATTR_VOLUME) {
295: chksum = -1;
296: continue;
297: }
298:
299: /*
300: * Check for a checksum or name match
301: */
302: chksum_ok = (chksum == winChksum(dep->deName));
303: if (!chksum_ok
304: && (!olddos || bcmp(dosfilename, dep->deName, 11))) {
305: chksum = -1;
306: continue;
307: }
308: #ifdef MSDOSFS_DEBUG
309: printf("msdosfs_lookup(): match blkoff %d, diroff %d\n",
310: blkoff, diroff);
311: #endif
312: /*
313: * Remember where this directory
314: * entry came from for whoever did
315: * this lookup.
316: */
317: dp->de_fndoffset = diroff;
318: if (chksum_ok && nameiop == RENAME) {
319: /*
320: * Target had correct long name
321: * directory entries, reuse them as
322: * needed.
323: */
324: dp->de_fndcnt = wincnt - 1;
325: } else {
326: /*
327: * Long name directory entries not
328: * present or corrupt, can only reuse
329: * dos directory entry.
330: */
331: dp->de_fndcnt = 0;
332: }
333: goto found;
334: }
335: } /* for (blkoff = 0; .... */
336: /*
337: * Release the buffer holding the directory cluster just
338: * searched.
339: */
340: brelse(bp);
341: } /* for (frcn = 0; ; frcn++) */
342:
343: notfound:;
344: /*
345: * We hold no disk buffers at this point.
346: */
347:
348: /*
349: * Fixup the slot description to point to the place where
350: * we might put the new DOS direntry (putting the Win95
351: * long name entries before that)
352: */
353: if (!slotcount) {
354: slotcount = 1;
355: slotoffset = diroff;
356: }
357: if (wincnt > slotcount)
358: slotoffset += sizeof(struct direntry) * (wincnt - slotcount);
359:
360: /*
361: * If we get here we didn't find the entry we were looking for. But
362: * that's ok if we are creating or renaming and are at the end of
363: * the pathname and the directory hasn't been removed.
364: */
365: #ifdef MSDOSFS_DEBUG
366: printf("msdosfs_lookup(): op %d, refcnt %d\n",
367: nameiop, dp->de_refcnt);
368: printf(" slotcount %d, slotoffset %d\n",
369: slotcount, slotoffset);
370: #endif
371: if ((nameiop == CREATE || nameiop == RENAME) &&
372: (flags & ISLASTCN) && dp->de_refcnt != 0) {
373: /*
374: * Access for write is interpreted as allowing
375: * creation of files in the directory.
376: */
377: error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc);
378: if (error)
379: return (error);
380: /*
381: * Return an indication of where the new directory
382: * entry should be put.
383: */
384: dp->de_fndoffset = slotoffset;
385: dp->de_fndcnt = wincnt - 1;
386:
387: /*
388: * We return with the directory locked, so that
389: * the parameters we set up above will still be
390: * valid if we actually decide to do a direnter().
391: * We return ni_vp == NULL to indicate that the entry
392: * does not currently exist; we leave a pointer to
393: * the (locked) directory inode in ndp->ni_dvp.
394: * The pathname buffer is saved so that the name
395: * can be obtained later.
396: *
397: * NB - if the directory is unlocked, then this
398: * information cannot be used.
399: */
400: cnp->cn_flags |= SAVENAME;
401: if (!lockparent) {
402: VOP_UNLOCK(vdp, 0, p);
403: cnp->cn_flags |= PDIRUNLOCK;
404: }
405: return (EJUSTRETURN);
406: }
407: /*
408: * Insert name into cache (as non-existent) if appropriate.
409: */
410: if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
411: cache_enter(vdp, *vpp, cnp);
412: return (ENOENT);
413:
414: found:;
415: /*
416: * NOTE: We still have the buffer with matched directory entry at
417: * this point.
418: */
419: isadir = dep->deAttributes & ATTR_DIRECTORY;
420: scn = getushort(dep->deStartCluster);
421: if (FAT32(pmp)) {
422: scn |= getushort(dep->deHighClust) << 16;
423: if (scn == pmp->pm_rootdirblk) {
424: /*
425: * There should actually be 0 here.
426: * Just ignore the error.
427: */
428: scn = MSDOSFSROOT;
429: }
430: }
431:
432: if (cluster == MSDOSFSROOT)
433: blkoff = diroff;
434:
435: if (isadir) {
436: cluster = scn;
437: if (cluster == MSDOSFSROOT)
438: blkoff = MSDOSFSROOT_OFS;
439: else
440: blkoff = 0;
441: }
442:
443: /*
444: * Now release buf to allow deget to read the entry again.
445: * Reserving it here and giving it to deget could result
446: * in a deadlock.
447: */
448: brelse(bp);
449:
450: foundroot:;
451: /*
452: * If we entered at foundroot, then we are looking for the . or ..
453: * entry of the filesystems root directory. isadir and scn were
454: * setup before jumping here. And, bp is already null.
455: */
456: if (FAT32(pmp) && scn == MSDOSFSROOT)
457: scn = pmp->pm_rootdirblk;
458:
459: /*
460: * If deleting, and at end of pathname, return
461: * parameters which can be used to remove file.
462: * If the wantparent flag isn't set, we return only
463: * the directory (in ndp->ni_dvp), otherwise we go
464: * on and lock the inode, being careful with ".".
465: */
466: if (nameiop == DELETE && (flags & ISLASTCN)) {
467: /*
468: * Don't allow deleting the root.
469: */
470: if (blkoff == MSDOSFSROOT_OFS)
471: return EROFS; /* really? XXX */
472:
473: /*
474: * Write access to directory required to delete files.
475: */
476: error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc);
477: if (error)
478: return (error);
479:
480: /*
481: * Return pointer to current entry in dp->i_offset.
482: * Save directory inode pointer in ndp->ni_dvp for dirremove().
483: */
484: if (dp->de_StartCluster == scn && isadir) { /* "." */
485: VREF(vdp);
486: *vpp = vdp;
487: return (0);
488: }
489: if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
490: return (error);
491: *vpp = DETOV(tdp);
492: if (!lockparent) {
493: VOP_UNLOCK(vdp, 0, p);
494: cnp->cn_flags |= PDIRUNLOCK;
495: }
496: return (0);
497: }
498:
499: /*
500: * If rewriting (RENAME), return the inode and the
501: * information required to rewrite the present directory
502: * Must get inode of directory entry to verify it's a
503: * regular file, or empty directory.
504: */
505: if (nameiop == RENAME && wantparent &&
506: (flags & ISLASTCN)) {
507: if (blkoff == MSDOSFSROOT_OFS)
508: return EROFS; /* really? XXX */
509:
510: error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred, cnp->cn_proc);
511: if (error)
512: return (error);
513:
514: /*
515: * Careful about locking second inode.
516: * This can only occur if the target is ".".
517: */
518: if (dp->de_StartCluster == scn && isadir)
519: return (EISDIR);
520:
521: if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
522: return (error);
523: *vpp = DETOV(tdp);
524: cnp->cn_flags |= SAVENAME;
525: if (!lockparent)
526: VOP_UNLOCK(vdp, 0, p);
527: return (0);
528: }
529:
530: /*
531: * Step through the translation in the name. We do not `vput' the
532: * directory because we may need it again if a symbolic link
533: * is relative to the current directory. Instead we save it
534: * unlocked as "pdp". We must get the target inode before unlocking
535: * the directory to insure that the inode will not be removed
536: * before we get it. We prevent deadlock by always fetching
537: * inodes from the root, moving down the directory tree. Thus
538: * when following backward pointers ".." we must unlock the
539: * parent directory before getting the requested directory.
540: * There is a potential race condition here if both the current
541: * and parent directories are removed before the VFS_VGET for the
542: * inode associated with ".." returns. We hope that this occurs
543: * infrequently since we cannot avoid this race condition without
544: * implementing a sophisticated deadlock detection algorithm.
545: * Note also that this simple deadlock detection scheme will not
546: * work if the file system has any hard links other than ".."
547: * that point backwards in the directory structure.
548: */
549: pdp = vdp;
550: if (flags & ISDOTDOT) {
551: VOP_UNLOCK(pdp, 0, p); /* race to get the inode */
552: cnp->cn_flags |= PDIRUNLOCK;
553: if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0) {
554: if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p) == 0)
555: cnp->cn_flags &= ~PDIRUNLOCK;
556: return (error);
557: }
558: if (lockparent && (flags & ISLASTCN)) {
559: if ((error = vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY,
560: p))) {
561: vput(DETOV(tdp));
562: return (error);
563: }
564: cnp->cn_flags &= ~PDIRUNLOCK;
565: }
566: *vpp = DETOV(tdp);
567: } else if (dp->de_StartCluster == scn && isadir) {
568: VREF(vdp); /* we want ourself, ie "." */
569: *vpp = vdp;
570: } else {
571: if ((error = deget(pmp, cluster, blkoff, &tdp)) != 0)
572: return (error);
573: if (!lockparent || !(flags & ISLASTCN)) {
574: VOP_UNLOCK(pdp, 0, p);
575: cnp->cn_flags |= PDIRUNLOCK;
576: }
577: *vpp = DETOV(tdp);
578: }
579:
580: /*
581: * Insert name into cache if appropriate.
582: */
583: if (cnp->cn_flags & MAKEENTRY)
584: cache_enter(vdp, *vpp, cnp);
585: return (0);
586: }
587:
588: /*
589: * dep - directory entry to copy into the directory
590: * ddep - directory to add to
591: * depp - return the address of the denode for the created directory entry
592: * if depp != 0
593: * cnp - componentname needed for Win95 long filenames
594: */
595: int
596: createde(dep, ddep, depp, cnp)
597: struct denode *dep;
598: struct denode *ddep;
599: struct denode **depp;
600: struct componentname *cnp;
601: {
602: int error;
603: uint32_t dirclust, diroffset;
604: struct direntry *ndep;
605: struct msdosfsmount *pmp = ddep->de_pmp;
606: struct buf *bp;
607: daddr64_t bn;
608: int blsize;
609:
610: #ifdef MSDOSFS_DEBUG
611: printf("createde(dep %08x, ddep %08x, depp %08x, cnp %08x)\n",
612: dep, ddep, depp, cnp);
613: #endif
614:
615: /*
616: * If no space left in the directory then allocate another cluster
617: * and chain it onto the end of the file. There is one exception
618: * to this. That is, if the root directory has no more space it
619: * can NOT be expanded. extendfile() checks for and fails attempts
620: * to extend the root directory. We just return an error in that
621: * case.
622: */
623: if (ddep->de_fndoffset >= ddep->de_FileSize) {
624: diroffset = ddep->de_fndoffset + sizeof(struct direntry)
625: - ddep->de_FileSize;
626: dirclust = de_clcount(pmp, diroffset);
627: if ((error = extendfile(ddep, dirclust, 0, 0, DE_CLEAR)) != 0) {
628: (void)detrunc(ddep, ddep->de_FileSize, 0, NOCRED, NULL);
629: return error;
630: }
631:
632: /*
633: * Update the size of the directory
634: */
635: ddep->de_FileSize += de_cn2off(pmp, dirclust);
636: }
637:
638: /*
639: * We just read in the cluster with space. Copy the new directory
640: * entry in. Then write it to disk. NOTE: DOS directories
641: * do not get smaller as clusters are emptied.
642: */
643: error = pcbmap(ddep, de_cluster(pmp, ddep->de_fndoffset),
644: &bn, &dirclust, &blsize);
645: if (error)
646: return error;
647: diroffset = ddep->de_fndoffset;
648: if (dirclust != MSDOSFSROOT)
649: diroffset &= pmp->pm_crbomask;
650: if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) != 0) {
651: brelse(bp);
652: return error;
653: }
654: ndep = bptoep(pmp, bp, ddep->de_fndoffset);
655:
656: DE_EXTERNALIZE(ndep, dep);
657:
658: /*
659: * Now write the Win95 long name
660: */
661: if (ddep->de_fndcnt > 0) {
662: u_int8_t chksum = winChksum(ndep->deName);
663: u_char *un = (u_char *)cnp->cn_nameptr;
664: int unlen = cnp->cn_namelen;
665: int cnt = 1;
666:
667: while (--ddep->de_fndcnt >= 0) {
668: if (!(ddep->de_fndoffset & pmp->pm_crbomask)) {
669: if ((error = bwrite(bp)) != 0)
670: return error;
671:
672: ddep->de_fndoffset -= sizeof(struct direntry);
673: error = pcbmap(ddep,
674: de_cluster(pmp,
675: ddep->de_fndoffset),
676: &bn, 0, &blsize);
677: if (error)
678: return error;
679:
680: error = bread(pmp->pm_devvp, bn, blsize,
681: NOCRED, &bp);
682: if (error) {
683: brelse(bp);
684: return error;
685: }
686: ndep = bptoep(pmp, bp, ddep->de_fndoffset);
687: } else {
688: ndep--;
689: ddep->de_fndoffset -= sizeof(struct direntry);
690: }
691: if (!unix2winfn(un, unlen, (struct winentry *)ndep, cnt++, chksum))
692: break;
693: }
694: }
695:
696: if ((error = bwrite(bp)) != 0)
697: return error;
698:
699: /*
700: * If they want us to return with the denode gotten.
701: */
702: if (depp) {
703: if (dep->de_Attributes & ATTR_DIRECTORY) {
704: dirclust = dep->de_StartCluster;
705: if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)
706: dirclust = MSDOSFSROOT;
707: if (dirclust == MSDOSFSROOT)
708: diroffset = MSDOSFSROOT_OFS;
709: else
710: diroffset = 0;
711: }
712: return deget(pmp, dirclust, diroffset, depp);
713: }
714:
715: return 0;
716: }
717:
718: /*
719: * Be sure a directory is empty except for "." and "..". Return 1 if empty,
720: * return 0 if not empty or error.
721: */
722: int
723: dosdirempty(dep)
724: struct denode *dep;
725: {
726: int blsize;
727: int error;
728: uint32_t cn;
729: daddr64_t bn;
730: struct buf *bp;
731: struct msdosfsmount *pmp = dep->de_pmp;
732: struct direntry *dentp;
733:
734: /*
735: * Since the filesize field in directory entries for a directory is
736: * zero, we just have to feel our way through the directory until
737: * we hit end of file.
738: */
739: for (cn = 0;; cn++) {
740: if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {
741: if (error == E2BIG)
742: return (1); /* it's empty */
743: return (0);
744: }
745: error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
746: if (error) {
747: brelse(bp);
748: return (0);
749: }
750: for (dentp = (struct direntry *)bp->b_data;
751: (char *)dentp < bp->b_data + blsize;
752: dentp++) {
753: if (dentp->deName[0] != SLOT_DELETED &&
754: (dentp->deAttributes & ATTR_VOLUME) == 0) {
755: /*
756: * In dos directories an entry whose name
757: * starts with SLOT_EMPTY (0) starts the
758: * beginning of the unused part of the
759: * directory, so we can just return that it
760: * is empty.
761: */
762: if (dentp->deName[0] == SLOT_EMPTY) {
763: brelse(bp);
764: return (1);
765: }
766: /*
767: * Any names other than "." and ".." in a
768: * directory mean it is not empty.
769: */
770: if (bcmp(dentp->deName, ". ", 11) &&
771: bcmp(dentp->deName, ".. ", 11)) {
772: brelse(bp);
773: #ifdef MSDOSFS_DEBUG
774: printf("dosdirempty(): entry found %02x, %02x\n",
775: dentp->deName[0], dentp->deName[1]);
776: #endif
777: return (0); /* not empty */
778: }
779: }
780: }
781: brelse(bp);
782: }
783: /* NOTREACHED */
784: }
785:
786: /*
787: * Check to see if the directory described by target is in some
788: * subdirectory of source. This prevents something like the following from
789: * succeeding and leaving a bunch or files and directories orphaned. mv
790: * /a/b/c /a/b/c/d/e/f Where c and f are directories.
791: *
792: * source - the inode for /a/b/c
793: * target - the inode for /a/b/c/d/e/f
794: *
795: * Returns 0 if target is NOT a subdirectory of source.
796: * Otherwise returns a non-zero error number.
797: * The target inode is always unlocked on return.
798: */
799: int
800: doscheckpath(source, target)
801: struct denode *source;
802: struct denode *target;
803: {
804: uint32_t scn;
805: struct msdosfsmount *pmp;
806: struct direntry *ep;
807: struct denode *dep;
808: struct buf *bp = NULL;
809: int error = 0;
810:
811: dep = target;
812: if ((target->de_Attributes & ATTR_DIRECTORY) == 0 ||
813: (source->de_Attributes & ATTR_DIRECTORY) == 0) {
814: error = ENOTDIR;
815: goto out;
816: }
817: if (dep->de_StartCluster == source->de_StartCluster) {
818: error = EEXIST;
819: goto out;
820: }
821: if (dep->de_StartCluster == MSDOSFSROOT)
822: goto out;
823: pmp = dep->de_pmp;
824: #ifdef DIAGNOSTIC
825: if (pmp != source->de_pmp)
826: panic("doscheckpath: source and target on different filesystems");
827: #endif
828: if (FAT32(pmp) && dep->de_StartCluster == pmp->pm_rootdirblk)
829: goto out;
830:
831: for (;;) {
832: if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) {
833: error = ENOTDIR;
834: break;
835: }
836: scn = dep->de_StartCluster;
837: error = bread(pmp->pm_devvp, cntobn(pmp, scn),
838: pmp->pm_bpcluster, NOCRED, &bp);
839: if (error)
840: break;
841:
842: ep = (struct direntry *) bp->b_data + 1;
843: if ((ep->deAttributes & ATTR_DIRECTORY) == 0 ||
844: bcmp(ep->deName, ".. ", 11) != 0) {
845: error = ENOTDIR;
846: break;
847: }
848: scn = getushort(ep->deStartCluster);
849: if (FAT32(pmp))
850: scn |= getushort(ep->deHighClust) << 16;
851:
852: if (scn == source->de_StartCluster) {
853: error = EINVAL;
854: break;
855: }
856: if (scn == MSDOSFSROOT)
857: break;
858: if (FAT32(pmp) && scn == pmp->pm_rootdirblk) {
859: /*
860: * scn should be 0 in this case,
861: * but we silently ignore the error.
862: */
863: break;
864: }
865:
866: vput(DETOV(dep));
867: brelse(bp);
868: bp = NULL;
869: /* NOTE: deget() clears dep on error */
870: if ((error = deget(pmp, scn, 0, &dep)) != 0)
871: break;
872: }
873: out:;
874: if (bp)
875: brelse(bp);
876: if (error == ENOTDIR)
877: printf("doscheckpath(): .. not a directory?\n");
878: if (dep != NULL)
879: vput(DETOV(dep));
880: return (error);
881: }
882:
883: /*
884: * Read in the disk block containing the directory entry (dirclu, dirofs)
885: * and return the address of the buf header, and the address of the
886: * directory entry within the block.
887: */
888: int
889: readep(pmp, dirclust, diroffset, bpp, epp)
890: struct msdosfsmount *pmp;
891: uint32_t dirclust, diroffset;
892: struct buf **bpp;
893: struct direntry **epp;
894: {
895: int error;
896: daddr64_t bn;
897: int blsize;
898: uint32_t boff;
899:
900: boff = diroffset & ~pmp->pm_crbomask;
901: blsize = pmp->pm_bpcluster;
902: if (dirclust == MSDOSFSROOT
903: && de_blk(pmp, diroffset + blsize) > pmp->pm_rootdirsize)
904: blsize = de_bn2off(pmp, pmp->pm_rootdirsize) & pmp->pm_crbomask;
905: bn = detobn(pmp, dirclust, diroffset);
906: if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, bpp)) != 0) {
907: brelse(*bpp);
908: *bpp = NULL;
909: return (error);
910: }
911: if (epp)
912: *epp = bptoep(pmp, *bpp, diroffset);
913: return (0);
914: }
915:
916: /*
917: * Read in the disk block containing the directory entry dep came from and
918: * return the address of the buf header, and the address of the directory
919: * entry within the block.
920: */
921: int
922: readde(dep, bpp, epp)
923: struct denode *dep;
924: struct buf **bpp;
925: struct direntry **epp;
926: {
927:
928: return (readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset,
929: bpp, epp));
930: }
931:
932: /*
933: * Remove a directory entry. At this point the file represented by the
934: * directory entry to be removed is still full length until noone has it
935: * open. When the file no longer being used msdosfs_inactive() is called
936: * and will truncate the file to 0 length. When the vnode containing the
937: * denode is needed for some other purpose by VFS it will call
938: * msdosfs_reclaim() which will remove the denode from the denode cache.
939: */
940: int
941: removede(pdep, dep)
942: struct denode *pdep; /* directory where the entry is removed */
943: struct denode *dep; /* file to be removed */
944: {
945: int error;
946: struct direntry *ep;
947: struct buf *bp;
948: daddr64_t bn;
949: int blsize;
950: struct msdosfsmount *pmp = pdep->de_pmp;
951: uint32_t offset = pdep->de_fndoffset;
952:
953: #ifdef MSDOSFS_DEBUG
954: printf("removede(): filename %s, dep %08x, offset %08x\n",
955: dep->de_Name, dep, offset);
956: #endif
957:
958: dep->de_refcnt--;
959: offset += sizeof(struct direntry);
960: do {
961: offset -= sizeof(struct direntry);
962: error = pcbmap(pdep, de_cluster(pmp, offset), &bn, 0, &blsize);
963: if (error)
964: return error;
965: error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
966: if (error) {
967: brelse(bp);
968: return error;
969: }
970: ep = bptoep(pmp, bp, offset);
971: /*
972: * Check whether, if we came here the second time, i.e.
973: * when underflowing into the previous block, the last
974: * entry in this block is a longfilename entry, too.
975: */
976: if (ep->deAttributes != ATTR_WIN95
977: && offset != pdep->de_fndoffset) {
978: brelse(bp);
979: break;
980: }
981: offset += sizeof(struct direntry);
982: while (1) {
983: /*
984: * We are a bit agressive here in that we delete any Win95
985: * entries preceding this entry, not just the ones we "own".
986: * Since these presumably aren't valid anyway,
987: * there should be no harm.
988: */
989: offset -= sizeof(struct direntry);
990: ep--->deName[0] = SLOT_DELETED;
991: if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95)
992: || !(offset & pmp->pm_crbomask)
993: || ep->deAttributes != ATTR_WIN95)
994: break;
995: }
996: if ((error = bwrite(bp)) != 0)
997: return error;
998: } while (!(pmp->pm_flags & MSDOSFSMNT_NOWIN95)
999: && !(offset & pmp->pm_crbomask)
1000: && offset);
1001: return 0;
1002: }
1003:
1004: /*
1005: * Create a unique DOS name in dvp
1006: */
1007: int
1008: uniqdosname(dep, cnp, cp)
1009: struct denode *dep;
1010: struct componentname *cnp;
1011: u_char *cp;
1012: {
1013: struct msdosfsmount *pmp = dep->de_pmp;
1014: struct direntry *dentp;
1015: int gen;
1016: int blsize;
1017: uint32_t cn;
1018: daddr64_t bn;
1019: struct buf *bp;
1020: int error;
1021:
1022: for (gen = 1;; gen++) {
1023: /*
1024: * Generate DOS name with generation number
1025: */
1026: if (!unix2dosfn((u_char *)cnp->cn_nameptr, cp, cnp->cn_namelen, gen))
1027: return gen == 1 ? EINVAL : EEXIST;
1028:
1029: /*
1030: * Now look for a dir entry with this exact name
1031: */
1032: for (cn = error = 0; !error; cn++) {
1033: if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {
1034: if (error == E2BIG) /* EOF reached and not found */
1035: return 0;
1036: return error;
1037: }
1038: error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp);
1039: if (error) {
1040: brelse(bp);
1041: return error;
1042: }
1043: for (dentp = (struct direntry *)bp->b_data;
1044: (char *)dentp < bp->b_data + blsize;
1045: dentp++) {
1046: if (dentp->deName[0] == SLOT_EMPTY) {
1047: /*
1048: * Last used entry and not found
1049: */
1050: brelse(bp);
1051: return 0;
1052: }
1053: /*
1054: * Ignore volume labels and Win95 entries
1055: */
1056: if (dentp->deAttributes & ATTR_VOLUME)
1057: continue;
1058: if (!bcmp(dentp->deName, cp, 11)) {
1059: error = EEXIST;
1060: break;
1061: }
1062: }
1063: brelse(bp);
1064: }
1065: }
1066:
1067: return (EEXIST);
1068: }
1069:
1070: /*
1071: * Find any Win'95 long filename entry in directory dep
1072: */
1073: int
1074: findwin95(dep)
1075: struct denode *dep;
1076: {
1077: struct msdosfsmount *pmp = dep->de_pmp;
1078: struct direntry *dentp;
1079: int blsize;
1080: uint32_t cn;
1081: daddr64_t bn;
1082: struct buf *bp;
1083:
1084: /*
1085: * Read through the directory looking for Win'95 entries
1086: * Note: Error currently handled just as EOF XXX
1087: */
1088: for (cn = 0;; cn++) {
1089: if (pcbmap(dep, cn, &bn, 0, &blsize))
1090: return 0;
1091: if (bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) {
1092: brelse(bp);
1093: return 0;
1094: }
1095: for (dentp = (struct direntry *)bp->b_data;
1096: (char *)dentp < bp->b_data + blsize;
1097: dentp++) {
1098: if (dentp->deName[0] == SLOT_EMPTY) {
1099: /*
1100: * Last used entry and not found
1101: */
1102: brelse(bp);
1103: return 0;
1104: }
1105: if (dentp->deName[0] == SLOT_DELETED) {
1106: /*
1107: * Ignore deleted files
1108: * Note: might be an indication of Win'95 anyway XXX
1109: */
1110: continue;
1111: }
1112: if (dentp->deAttributes == ATTR_WIN95) {
1113: brelse(bp);
1114: return 1;
1115: }
1116: }
1117: brelse(bp);
1118: }
1119: }
CVSweb