1 /* $FreeBSD$ */ 2 /* $NetBSD: msdosfs_lookup.c,v 1.14 1994/08/21 18:44:07 ws Exp $ */ 3 4 /*- 5 * Copyright (C) 1994 Wolfgang Solfrank. 6 * Copyright (C) 1994 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 #include <sys/proc.h> 58 59 #include <msdosfs/bpb.h> 60 #include <msdosfs/direntry.h> 61 #include <msdosfs/denode.h> 62 #include <msdosfs/msdosfsmount.h> 63 #include <msdosfs/fat.h> 64 65 static int markdeleted __P((struct msdosfsmount *pmp, u_long dirclust, 66 u_long diroffset)); 67 68 /* 69 * When we search a directory the blocks containing directory entries are 70 * read and examined. The directory entries contain information that would 71 * normally be in the inode of a unix filesystem. This means that some of 72 * a directory's contents may also be in memory resident denodes (sort of 73 * an inode). This can cause problems if we are searching while some other 74 * process is modifying a directory. To prevent one process from accessing 75 * incompletely modified directory information we depend upon being the 76 * soul owner of a directory block. bread/brelse provide this service. 77 * This being the case, when a process modifies a directory it must first 78 * acquire the disk block that contains the directory entry to be modified. 79 * Then update the disk block and the denode, and then write the disk block 80 * out to disk. This way disk blocks containing directory entries and in 81 * memory denode's will be in synch. 82 */ 83 int 84 msdosfs_lookup(ap) 85 struct vop_lookup_args /* { 86 struct vnode *a_dvp; 87 struct vnode **a_vpp; 88 struct componentname *a_cnp; 89 } */ *ap; 90 { 91 struct vnode *vdp = ap->a_dvp; 92 struct vnode **vpp = ap->a_vpp; 93 struct componentname *cnp = ap->a_cnp; 94 daddr_t bn; 95 int error; 96 int lockparent; 97 int wantparent; 98 int slotstatus; 99 100 #define NONE 0 101 #define FOUND 1 102 int slotoffset = -1; 103 int slotcluster = -1; 104 int frcn; 105 u_long cluster; 106 int rootreloff; 107 int diroff; 108 int isadir; /* ~0 if found direntry is a directory */ 109 u_long scn; /* starting cluster number */ 110 struct vnode *pdp; 111 struct denode *dp; 112 struct denode *tdp; 113 struct msdosfsmount *pmp; 114 struct buf *bp = 0; 115 struct direntry *dep = NULL; 116 struct ucred *cred = cnp->cn_cred; 117 u_char dosfilename[12]; 118 int flags = cnp->cn_flags; 119 int nameiop = cnp->cn_nameiop; 120 121 #ifdef MSDOSFS_DEBUG 122 printf("msdosfs_lookup(): looking for %s\n", cnp->cn_nameptr); 123 #endif 124 dp = VTODE(vdp); 125 pmp = dp->de_pmp; 126 *vpp = NULL; 127 lockparent = flags & LOCKPARENT; 128 wantparent = flags & (LOCKPARENT | WANTPARENT); 129 #ifdef MSDOSFS_DEBUG 130 printf("msdosfs_lookup(): vdp %08x, dp %08x, Attr %02x\n", 131 vdp, dp, dp->de_Attributes); 132 #endif 133 134 /* 135 * Be sure vdp is a directory. Since dos filesystems don't have 136 * the concept of execute permission anybody can search a 137 * directory. 138 */ 139 if ((dp->de_Attributes & ATTR_DIRECTORY) == 0) 140 return ENOTDIR; 141 142 /* 143 * See if the component of the pathname we are looking for is in 144 * the directory cache. If so then do a few things and return. 145 */ 146 error = cache_lookup(vdp, vpp, cnp); 147 if (error) { 148 int vpid; 149 150 if (error == ENOENT) 151 return error; 152 pdp = vdp; 153 vdp = *vpp; 154 dp = VTODE(vdp); 155 vpid = vdp->v_id; 156 if (pdp == vdp) { 157 VREF(vdp); 158 error = 0; 159 } else if (flags & ISDOTDOT) { 160 VOP_UNLOCK(pdp, 0, curproc); 161 error = vget(vdp, LK_EXCLUSIVE | LK_INTERLOCK, curproc); 162 if (!error && lockparent && (flags & ISLASTCN)) 163 error = vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, curproc); 164 } else { 165 error = vget(vdp, LK_EXCLUSIVE | LK_INTERLOCK, curproc); 166 if (!lockparent || error || !(flags & ISLASTCN)) 167 VOP_UNLOCK(pdp, 0, curproc); 168 } 169 170 if (!error) { 171 if (vpid == vdp->v_id) { 172 #ifdef MSDOSFS_DEBUG 173 printf("msdosfs_lookup(): cache hit, vnode %08x, file %s\n", 174 vdp, dp->de_Name); 175 #endif 176 #ifdef PC98 177 /* 178 * 1024 byte/sector support 179 */ 180 if (pmp->pm_BytesPerSec == 1024) 181 vdp->v_flag |= 0x10000; 182 #endif 183 return 0; 184 } 185 vput(vdp); 186 if (lockparent && pdp != vdp && (flags & ISLASTCN)) 187 VOP_UNLOCK(pdp, 0, curproc); 188 } 189 error = vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, curproc); 190 if (error) 191 return error; 192 vdp = pdp; 193 dp = VTODE(vdp); 194 *vpp = NULL; 195 } 196 197 /* 198 * If they are going after the . or .. entry in the root directory, 199 * they won't find it. DOS filesystems don't have them in the root 200 * directory. So, we fake it. deget() is in on this scam too. 201 */ 202 if ((vdp->v_flag & VROOT) && cnp->cn_nameptr[0] == '.' && 203 (cnp->cn_namelen == 1 || 204 (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) { 205 isadir = ATTR_DIRECTORY; 206 scn = MSDOSFSROOT; 207 #ifdef MSDOSFS_DEBUG 208 printf("msdosfs_lookup(): looking for . or .. in root directory\n"); 209 #endif 210 cluster = MSDOSFSROOT; 211 diroff = MSDOSFSROOT_OFS; 212 goto foundroot; 213 } 214 215 /* 216 * Don't search for free slots unless we are creating a filename 217 * and we are at the end of the pathname. 218 */ 219 slotstatus = FOUND; 220 if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) { 221 slotstatus = NONE; 222 slotoffset = -1; 223 } 224 225 unix2dosfn((u_char *) cnp->cn_nameptr, dosfilename, cnp->cn_namelen); 226 dosfilename[11] = 0; 227 #ifdef MSDOSFS_DEBUG 228 printf("msdosfs_lookup(): dos version of filename %s, length %d\n", 229 dosfilename, cnp->cn_namelen); 230 #endif 231 /* 232 * Search the directory pointed at by vdp for the name pointed at 233 * by cnp->cn_nameptr. 234 */ 235 tdp = NULL; 236 /* 237 * The outer loop ranges over the clusters that make up the 238 * directory. Note that the root directory is different from all 239 * other directories. It has a fixed number of blocks that are not 240 * part of the pool of allocatable clusters. So, we treat it a 241 * little differently. The root directory starts at "cluster" 0. 242 */ 243 rootreloff = 0; 244 for (frcn = 0;; frcn++) { 245 error = pcbmap(dp, frcn, &bn, &cluster); 246 if (error) { 247 if (error == E2BIG) 248 break; 249 return error; 250 } 251 error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster,NOCRED,&bp); 252 if (error) 253 return error; 254 for (diroff = 0; diroff < pmp->pm_depclust; diroff++) { 255 dep = (struct direntry *) bp->b_data + diroff; 256 257 /* 258 * If the slot is empty and we are still looking 259 * for an empty then remember this one. If the 260 * slot is not empty then check to see if it 261 * matches what we are looking for. If the slot 262 * has never been filled with anything, then the 263 * remainder of the directory has never been used, 264 * so there is no point in searching it. 265 */ 266 if (dep->deName[0] == SLOT_EMPTY || 267 dep->deName[0] == SLOT_DELETED) { 268 if (slotstatus != FOUND) { 269 slotstatus = FOUND; 270 if (cluster == MSDOSFSROOT) 271 slotoffset = rootreloff; 272 else 273 slotoffset = diroff; 274 slotcluster = cluster; 275 } 276 if (dep->deName[0] == SLOT_EMPTY) { 277 brelse(bp); 278 goto notfound; 279 } 280 } else { 281 /* 282 * Ignore volume labels (anywhere, not just 283 * the root directory). 284 */ 285 if ((dep->deAttributes & ATTR_VOLUME) == 0 && 286 bcmp(dosfilename, dep->deName, 11) == 0) { 287 #ifdef MSDOSFS_DEBUG 288 printf("msdosfs_lookup(): match diroff %d, rootreloff %d\n", 289 diroff, rootreloff); 290 #endif 291 /* 292 * Remember where this directory 293 * entry came from for whoever did 294 * this lookup. If this is the root 295 * directory we are interested in 296 * the offset relative to the 297 * beginning of the directory (not 298 * the beginning of the cluster). 299 */ 300 if (cluster == MSDOSFSROOT) 301 diroff = rootreloff; 302 dp->de_fndoffset = diroff; 303 dp->de_fndclust = cluster; 304 goto found; 305 } 306 } 307 rootreloff++; 308 } /* for (diroff = 0; .... */ 309 /* 310 * Release the buffer holding the directory cluster just 311 * searched. 312 */ 313 brelse(bp); 314 } /* for (frcn = 0; ; frcn++) */ 315 notfound:; 316 /* 317 * We hold no disk buffers at this point. 318 */ 319 320 /* 321 * If we get here we didn't find the entry we were looking for. But 322 * that's ok if we are creating or renaming and are at the end of 323 * the pathname and the directory hasn't been removed. 324 */ 325 #ifdef MSDOSFS_DEBUG 326 printf("msdosfs_lookup(): op %d, refcnt %d, slotstatus %d\n", 327 nameiop, dp->de_refcnt, slotstatus); 328 printf(" slotoffset %d, slotcluster %d\n", 329 slotoffset, slotcluster); 330 #endif 331 if ((nameiop == CREATE || nameiop == RENAME) && 332 (flags & ISLASTCN) && dp->de_refcnt != 0) { 333 error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc); 334 if (error) 335 return error; 336 if (slotstatus == NONE) { 337 dp->de_fndoffset = (u_long)-1; 338 dp->de_fndclust = (u_long)-1; 339 } else { 340 #ifdef MSDOSFS_DEBUG 341 printf("msdosfs_lookup(): saving empty slot location\n"); 342 #endif 343 dp->de_fndoffset = slotoffset; 344 dp->de_fndclust = slotcluster; 345 } 346 /* dp->de_flag |= DE_UPDATE; never update dos directories */ 347 cnp->cn_flags |= SAVENAME; 348 if (!lockparent)/* leave searched dir locked? */ 349 VOP_UNLOCK(vdp, 0, curproc); 350 return EJUSTRETURN; 351 } 352 /* 353 * Insert name in cache as non-existant if not trying to create it. 354 */ 355 if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) 356 cache_enter(vdp, *vpp, cnp); 357 return ENOENT; 358 359 found: ; 360 /* 361 * NOTE: We still have the buffer with matched directory entry at 362 * this point. 363 */ 364 isadir = dep->deAttributes & ATTR_DIRECTORY; 365 scn = getushort(dep->deStartCluster); 366 367 foundroot:; 368 /* 369 * If we entered at foundroot, then we are looking for the . or .. 370 * entry of the filesystems root directory. isadir and scn were 371 * setup before jumping here. And, bp is null. There is no buf 372 * header. 373 */ 374 375 /* 376 * If deleting and at the end of the path, then if we matched on 377 * "." then don't deget() we would probably panic(). Otherwise 378 * deget() the directory entry. 379 */ 380 if (nameiop == DELETE && (flags & ISLASTCN)) { 381 error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc); 382 if (error) { 383 if (bp) 384 brelse(bp); 385 return error; 386 } 387 if (dp->de_StartCluster == scn && isadir) { /* "." */ 388 VREF(vdp); 389 *vpp = vdp; 390 if (bp) 391 brelse(bp); 392 return 0; 393 } 394 error = deget(pmp, cluster, diroff, dep, &tdp); 395 if (error) { 396 if (bp) 397 brelse(bp); 398 return error; 399 } 400 *vpp = DETOV(tdp); 401 if (!lockparent) 402 VOP_UNLOCK(vdp, 0, curproc); 403 if (bp) 404 brelse(bp); 405 return 0; 406 } 407 408 /* 409 * If renaming. 410 */ 411 if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { 412 error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc); 413 if (error) { 414 if (bp) 415 brelse(bp); 416 return error; 417 } 418 if (dp->de_StartCluster == scn && isadir) { 419 if (bp) 420 brelse(bp); 421 return EISDIR; 422 } 423 error = deget(pmp, cluster, diroff, dep, &tdp); 424 if (error) { 425 if (bp) 426 brelse(bp); 427 return error; 428 } 429 *vpp = DETOV(tdp); 430 cnp->cn_flags |= SAVENAME; 431 if (!lockparent) 432 VOP_UNLOCK(vdp, 0, curproc); 433 if (bp) 434 brelse(bp); 435 return 0; 436 } 437 438 /* 439 * ? 440 */ 441 pdp = vdp; 442 if (flags & ISDOTDOT) { 443 VOP_UNLOCK(pdp, 0, curproc); 444 error = deget(pmp, cluster, diroff, dep, &tdp); 445 if (error) { 446 vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, curproc); 447 if (bp) 448 brelse(bp); 449 return error; 450 } 451 if (lockparent && (flags & ISLASTCN) 452 && (error = vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, curproc))) { 453 vput(DETOV(tdp)); 454 return error; 455 } 456 *vpp = DETOV(tdp); 457 } else if (dp->de_StartCluster == scn && isadir) { /* "." */ 458 VREF(vdp); 459 *vpp = vdp; 460 } else { 461 error = deget(pmp, cluster, diroff, dep, &tdp); 462 if (error) { 463 if (bp) 464 brelse(bp); 465 return error; 466 } 467 if (!lockparent || !(flags & ISLASTCN)) 468 VOP_UNLOCK(pdp, 0, curproc); 469 *vpp = DETOV(tdp); 470 } 471 if (bp) 472 brelse(bp); 473 474 /* 475 * Insert name in cache if wanted. 476 */ 477 if (cnp->cn_flags & MAKEENTRY) 478 cache_enter(vdp, *vpp, cnp); 479 return 0; 480 } 481 482 /* 483 * dep - directory entry to copy into the directory 484 * ddep - directory to add to 485 * depp - return the address of the denode for the created directory entry 486 * if depp != 0 487 */ 488 int 489 createde(dep, ddep, depp) 490 struct denode *dep; 491 struct denode *ddep; 492 struct denode **depp; 493 { 494 int error; 495 u_long dirclust, diroffset; 496 struct direntry *ndep; 497 struct msdosfsmount *pmp = ddep->de_pmp; 498 struct buf *bp; 499 500 #ifdef MSDOSFS_DEBUG 501 printf("createde(dep %08x, ddep %08x, depp %08x)\n", dep, ddep, depp); 502 #endif 503 504 /* 505 * If no space left in the directory then allocate another cluster 506 * and chain it onto the end of the file. There is one exception 507 * to this. That is, if the root directory has no more space it 508 * can NOT be expanded. extendfile() checks for and fails attempts 509 * to extend the root directory. We just return an error in that 510 * case. 511 */ 512 if (ddep->de_fndclust == (u_long)-1) { 513 error = extendfile(ddep, 1, &bp, &dirclust, DE_CLEAR); 514 if (error) 515 return error; 516 ndep = (struct direntry *) bp->b_data; 517 /* 518 * Let caller know where we put the directory entry. 519 */ 520 ddep->de_fndclust = dirclust; 521 ddep->de_fndoffset = diroffset = 0; 522 /* 523 * Update the size of the directory 524 */ 525 ddep->de_FileSize += pmp->pm_bpcluster; 526 } else { 527 /* 528 * There is space in the existing directory. So, we just 529 * read in the cluster with space. Copy the new directory 530 * entry in. Then write it to disk. NOTE: DOS directories 531 * do not get smaller as clusters are emptied. 532 */ 533 dirclust = ddep->de_fndclust; 534 diroffset = ddep->de_fndoffset; 535 536 error = readep(pmp, dirclust, diroffset, &bp, &ndep); 537 if (error) 538 return error; 539 } 540 DE_EXTERNALIZE(ndep, dep); 541 542 /* 543 * If they want us to return with the denode gotten. 544 */ 545 if (depp) { 546 error = deget(pmp, dirclust, diroffset, ndep, depp); 547 if (error) 548 return error; 549 } 550 error = bwrite(bp); 551 if (error) { 552 vput(DETOV(*depp)); /* free the vnode we got on error */ 553 return error; 554 } 555 return 0; 556 } 557 558 /* 559 * Read in a directory entry and mark it as being deleted. 560 */ 561 static int 562 markdeleted(pmp, dirclust, diroffset) 563 struct msdosfsmount *pmp; 564 u_long dirclust; 565 u_long diroffset; 566 { 567 int error; 568 struct direntry *ep; 569 struct buf *bp; 570 571 error = readep(pmp, dirclust, diroffset, &bp, &ep); 572 if (error) 573 return error; 574 ep->deName[0] = SLOT_DELETED; 575 return bwrite(bp); 576 } 577 578 /* 579 * Remove a directory entry. At this point the file represented by the 580 * directory entry to be removed is still full length until no one has it 581 * open. When the file no longer being used msdosfs_inactive() is called 582 * and will truncate the file to 0 length. When the vnode containing the 583 * denode is needed for some other purpose by VFS it will call 584 * msdosfs_reclaim() which will remove the denode from the denode cache. 585 */ 586 int 587 removede(pdep,dep) 588 struct denode *pdep; /* directory where the entry is removed */ 589 struct denode *dep; /* file to be removed */ 590 { 591 struct msdosfsmount *pmp = pdep->de_pmp; 592 int error; 593 594 #ifdef MSDOSFS_DEBUG 595 printf("removede(): filename %s\n", dep->de_Name); 596 printf("removede(): dep %08x, ndpcluster %d, ndpoffset %d\n", 597 dep, pdep->de_fndclust, pdep->de_fndoffset); 598 #endif 599 600 /* 601 * Read the directory block containing the directory entry we are 602 * to make free. The nameidata structure holds the cluster number 603 * and directory entry index number of the entry to free. 604 */ 605 error = markdeleted(pmp, pdep->de_fndclust, pdep->de_fndoffset); 606 607 if (error == 0) 608 dep->de_refcnt--; 609 return error; 610 } 611 612 /* 613 * Be sure a directory is empty except for "." and "..". Return 1 if empty, 614 * return 0 if not empty or error. 615 */ 616 int 617 dosdirempty(dep) 618 struct denode *dep; 619 { 620 int dei; 621 int error; 622 u_long cn; 623 daddr_t bn; 624 struct buf *bp; 625 struct msdosfsmount *pmp = dep->de_pmp; 626 struct direntry *dentp; 627 628 /* 629 * Since the filesize field in directory entries for a directory is 630 * zero, we just have to feel our way through the directory until 631 * we hit end of file. 632 */ 633 for (cn = 0;; cn++) { 634 error = pcbmap(dep, cn, &bn, 0); 635 if (error == E2BIG) 636 return 1; /* it's empty */ 637 error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, 638 &bp); 639 if (error) 640 return error; 641 dentp = (struct direntry *) bp->b_data; 642 for (dei = 0; dei < pmp->pm_depclust; dei++) { 643 if (dentp->deName[0] != SLOT_DELETED) { 644 /* 645 * In dos directories an entry whose name 646 * starts with SLOT_EMPTY (0) starts the 647 * beginning of the unused part of the 648 * directory, so we can just return that it 649 * is empty. 650 */ 651 if (dentp->deName[0] == SLOT_EMPTY) { 652 brelse(bp); 653 return 1; 654 } 655 /* 656 * Any names other than "." and ".." in a 657 * directory mean it is not empty. 658 */ 659 if (bcmp(dentp->deName, ". ", 11) && 660 bcmp(dentp->deName, ".. ", 11)) { 661 brelse(bp); 662 #ifdef MSDOSFS_DEBUG 663 printf("dosdirempty(): entry %d found %02x, %02x\n", 664 dei, dentp->deName[0], dentp->deName[1]); 665 #endif 666 return 0; /* not empty */ 667 } 668 } 669 dentp++; 670 } 671 brelse(bp); 672 } 673 /* NOTREACHED */ 674 } 675 676 /* 677 * Check to see if the directory described by target is in some 678 * subdirectory of source. This prevents something like the following from 679 * succeeding and leaving a bunch or files and directories orphaned. mv 680 * /a/b/c /a/b/c/d/e/f Where c and f are directories. 681 * 682 * source - the inode for /a/b/c 683 * target - the inode for /a/b/c/d/e/f 684 * 685 * Returns 0 if target is NOT a subdirectory of source. 686 * Otherwise returns a non-zero error number. 687 * The target inode is always unlocked on return. 688 */ 689 int 690 doscheckpath(source, target) 691 struct denode *source; 692 struct denode *target; 693 { 694 daddr_t scn; 695 struct msdosfsmount *pmp; 696 struct direntry *ep; 697 struct denode *dep; 698 struct buf *bp = NULL; 699 int error = 0; 700 701 dep = target; 702 if ((target->de_Attributes & ATTR_DIRECTORY) == 0 || 703 (source->de_Attributes & ATTR_DIRECTORY) == 0) { 704 error = ENOTDIR; 705 goto out; 706 } 707 if (dep->de_StartCluster == source->de_StartCluster) { 708 error = EEXIST; 709 goto out; 710 } 711 if (dep->de_StartCluster == MSDOSFSROOT) 712 goto out; 713 for (;;) { 714 if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) { 715 error = ENOTDIR; 716 goto out; 717 } 718 pmp = dep->de_pmp; 719 scn = dep->de_StartCluster; 720 error = bread(pmp->pm_devvp, cntobn(pmp, scn), 721 pmp->pm_bpcluster, NOCRED, &bp); 722 if (error) { 723 break; 724 } 725 ep = (struct direntry *) bp->b_data + 1; 726 if ((ep->deAttributes & ATTR_DIRECTORY) == 0 || 727 bcmp(ep->deName, ".. ", 11) != 0) { 728 error = ENOTDIR; 729 break; 730 } 731 scn = getushort(ep->deStartCluster); 732 if (scn == source->de_StartCluster) { 733 error = EINVAL; 734 break; 735 } 736 if (scn == MSDOSFSROOT) 737 break; 738 vput(DETOV(dep)); 739 /* NOTE: deget() clears dep on error */ 740 error = deget(pmp, scn, 0, ep, &dep); 741 brelse(bp); 742 bp = NULL; 743 if (error) 744 break; 745 } 746 out: ; 747 if (bp) 748 brelse(bp); 749 if (error == ENOTDIR) 750 printf("doscheckpath(): .. not a directory?\n"); 751 if (dep != NULL) 752 vput(DETOV(dep)); 753 return error; 754 } 755 756 /* 757 * Read in the disk block containing the directory entry (dirclu, dirofs) 758 * and return the address of the buf header, and the address of the 759 * directory entry within the block. 760 */ 761 int 762 readep(pmp, dirclu, dirofs, bpp, epp) 763 struct msdosfsmount *pmp; 764 u_long dirclu, dirofs; 765 struct buf **bpp; 766 struct direntry **epp; 767 { 768 int error; 769 daddr_t bn; 770 771 bn = detobn(pmp, dirclu, dirofs); 772 error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, NOCRED, bpp); 773 if (error) { 774 *bpp = NULL; 775 return error; 776 } 777 if (epp) 778 *epp = bptoep(pmp, *bpp, dirofs); 779 return 0; 780 } 781 782 783 /* 784 * Read in the disk block containing the directory entry dep came from and 785 * return the address of the buf header, and the address of the directory 786 * entry within the block. 787 */ 788 int 789 readde(dep, bpp, epp) 790 struct denode *dep; 791 struct buf **bpp; 792 struct direntry **epp; 793 { 794 return readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset, 795 bpp, epp); 796 } 797