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