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