Annotation of sys/msdosfs/msdosfs_lookup.c, Revision 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