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