msdosfs_lookup.c revision 36154
1/* $Id: msdosfs_lookup.c,v 1.24 1998/05/17 21:18:08 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 u_long boff; 861 862 boff = diroffset & ~pmp->pm_crbomask; 863 blsize = pmp->pm_bpcluster; 864 if (dirclust == MSDOSFSROOT 865 && de_blk(pmp, diroffset + blsize) > pmp->pm_rootdirsize) 866 blsize = de_bn2off(pmp, pmp->pm_rootdirsize) & pmp->pm_crbomask; 867 bn = detobn(pmp, dirclust, diroffset); 868 if ((error = bread(pmp->pm_devvp, bn, blsize, NOCRED, bpp)) != 0) { 869 brelse(*bpp); 870 *bpp = NULL; 871 return (error); 872 } 873 if (epp) 874 *epp = bptoep(pmp, *bpp, diroffset); 875 return (0); 876} 877 878/* 879 * Read in the disk block containing the directory entry dep came from and 880 * return the address of the buf header, and the address of the directory 881 * entry within the block. 882 */ 883int 884readde(dep, bpp, epp) 885 struct denode *dep; 886 struct buf **bpp; 887 struct direntry **epp; 888{ 889 890 return (readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset, 891 bpp, epp)); 892} 893 894/* 895 * Remove a directory entry. At this point the file represented by the 896 * directory entry to be removed is still full length until noone has it 897 * open. When the file no longer being used msdosfs_inactive() is called 898 * and will truncate the file to 0 length. When the vnode containing the 899 * denode is needed for some other purpose by VFS it will call 900 * msdosfs_reclaim() which will remove the denode from the denode cache. 901 */ 902int 903removede(pdep, dep) 904 struct denode *pdep; /* directory where the entry is removed */ 905 struct denode *dep; /* file to be removed */ 906{ 907 int error; 908 struct direntry *ep; 909 struct buf *bp; 910 daddr_t bn; 911 int blsize; 912 struct msdosfsmount *pmp = pdep->de_pmp; 913 u_long offset = pdep->de_fndoffset; 914 915#ifdef MSDOSFS_DEBUG 916 printf("removede(): filename %s, dep %p, offset %08lx\n", 917 dep->de_Name, dep, offset); 918#endif 919 920 dep->de_refcnt--; 921 offset += sizeof(struct direntry); 922 do { 923 offset -= sizeof(struct direntry); 924 error = pcbmap(pdep, de_cluster(pmp, offset), &bn, 0, &blsize); 925 if (error) 926 return error; 927 error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); 928 if (error) { 929 brelse(bp); 930 return error; 931 } 932 ep = bptoep(pmp, bp, offset); 933 /* 934 * Check whether, if we came here the second time, i.e. 935 * when underflowing into the previous block, the last 936 * entry in this block is a longfilename entry, too. 937 */ 938 if (ep->deAttributes != ATTR_WIN95 939 && offset != pdep->de_fndoffset) { 940 brelse(bp); 941 break; 942 } 943 offset += sizeof(struct direntry); 944 while (1) { 945 /* 946 * We are a bit agressive here in that we delete any Win95 947 * entries preceding this entry, not just the ones we "own". 948 * Since these presumably aren't valid anyway, 949 * there should be no harm. 950 */ 951 offset -= sizeof(struct direntry); 952 ep--->deName[0] = SLOT_DELETED; 953 if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95) 954 || !(offset & pmp->pm_crbomask) 955 || ep->deAttributes != ATTR_WIN95) 956 break; 957 } 958 if ((error = bwrite(bp)) != 0) 959 return error; 960 } while (!(pmp->pm_flags & MSDOSFSMNT_NOWIN95) 961 && !(offset & pmp->pm_crbomask) 962 && offset); 963 return 0; 964} 965 966/* 967 * Create a unique DOS name in dvp 968 */ 969int 970uniqdosname(dep, cnp, cp) 971 struct denode *dep; 972 struct componentname *cnp; 973 u_char *cp; 974{ 975 struct msdosfsmount *pmp = dep->de_pmp; 976 struct direntry *dentp; 977 int gen; 978 int blsize; 979 u_long cn; 980 daddr_t bn; 981 struct buf *bp; 982 int error; 983 984 if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) 985 return (unix2dosfn((const u_char *)cnp->cn_nameptr, cp, 986 cnp->cn_namelen, 0, 987 pmp->pm_flags & MSDOSFSMNT_U2WTABLE, pmp->pm_u2d, 988 pmp->pm_flags & MSDOSFSMNT_ULTABLE, pmp->pm_lu) ? 989 0 : EINVAL); 990 991 for (gen = 1;; gen++) { 992 /* 993 * Generate DOS name with generation number 994 */ 995 if (!unix2dosfn((const u_char *)cnp->cn_nameptr, cp, 996 cnp->cn_namelen, gen, 997 pmp->pm_flags & MSDOSFSMNT_U2WTABLE, pmp->pm_u2d, 998 pmp->pm_flags & MSDOSFSMNT_ULTABLE, pmp->pm_lu)) 999 return gen == 1 ? EINVAL : EEXIST; 1000 1001 /* 1002 * Now look for a dir entry with this exact name 1003 */ 1004 for (cn = error = 0; !error; cn++) { 1005 if ((error = pcbmap(dep, cn, &bn, 0, &blsize)) != 0) { 1006 if (error == E2BIG) /* EOF reached and not found */ 1007 return 0; 1008 return error; 1009 } 1010 error = bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp); 1011 if (error) { 1012 brelse(bp); 1013 return error; 1014 } 1015 for (dentp = (struct direntry *)bp->b_data; 1016 (char *)dentp < bp->b_data + blsize; 1017 dentp++) { 1018 if (dentp->deName[0] == SLOT_EMPTY) { 1019 /* 1020 * Last used entry and not found 1021 */ 1022 brelse(bp); 1023 return 0; 1024 } 1025 /* 1026 * Ignore volume labels and Win95 entries 1027 */ 1028 if (dentp->deAttributes & ATTR_VOLUME) 1029 continue; 1030 if (!bcmp(dentp->deName, cp, 11)) { 1031 error = EEXIST; 1032 break; 1033 } 1034 } 1035 brelse(bp); 1036 } 1037 } 1038} 1039 1040/* 1041 * Find any Win'95 long filename entry in directory dep 1042 */ 1043int 1044findwin95(dep) 1045 struct denode *dep; 1046{ 1047 struct msdosfsmount *pmp = dep->de_pmp; 1048 struct direntry *dentp; 1049 int blsize; 1050 u_long cn; 1051 daddr_t bn; 1052 struct buf *bp; 1053 1054 /* 1055 * Read through the directory looking for Win'95 entries 1056 * Note: Error currently handled just as EOF XXX 1057 */ 1058 for (cn = 0;; cn++) { 1059 if (pcbmap(dep, cn, &bn, 0, &blsize)) 1060 return 0; 1061 if (bread(pmp->pm_devvp, bn, blsize, NOCRED, &bp)) { 1062 brelse(bp); 1063 return 0; 1064 } 1065 for (dentp = (struct direntry *)bp->b_data; 1066 (char *)dentp < bp->b_data + blsize; 1067 dentp++) { 1068 if (dentp->deName[0] == SLOT_EMPTY) { 1069 /* 1070 * Last used entry and not found 1071 */ 1072 brelse(bp); 1073 return 0; 1074 } 1075 if (dentp->deName[0] == SLOT_DELETED) { 1076 /* 1077 * Ignore deleted files 1078 * Note: might be an indication of Win'95 anyway XXX 1079 */ 1080 continue; 1081 } 1082 if (dentp->deAttributes == ATTR_WIN95) { 1083 brelse(bp); 1084 return 1; 1085 } 1086 } 1087 brelse(bp); 1088 } 1089} 1090