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