|
|
1.1 ! root 1: /* ! 2: * Written by Paul Popelka ([email protected]) ! 3: * ! 4: * You can do anything you want with this software, ! 5: * just don't say you wrote it, ! 6: * and don't remove this notice. ! 7: * ! 8: * This software is provided "as is". ! 9: * ! 10: * The author supplies this software to be publicly ! 11: * redistributed on the understanding that the author ! 12: * is not responsible for the correct functioning of ! 13: * this software in any circumstances and is not liable ! 14: * for any damages caused by this software. ! 15: * ! 16: * October 1992 ! 17: * ! 18: * pcfs_denode.c,v 1.4.2.1 1993/08/05 02:37:20 cgd Exp ! 19: */ ! 20: ! 21: #include "param.h" ! 22: #include "systm.h" ! 23: #include "mount.h" ! 24: #include "proc.h" ! 25: #include "buf.h" ! 26: #include "vnode.h" ! 27: #include "kernel.h" /* defines "time" */ ! 28: ! 29: #include "bpb.h" ! 30: #include "pcfsmount.h" ! 31: #include "direntry.h" ! 32: #include "denode.h" ! 33: #include "fat.h" ! 34: ! 35: #define DEHSZ 512 ! 36: #if ((DEHSZ & (DEHSZ-1)) == 0) ! 37: #define DEHASH(dev, deno) (((dev)+(deno)+((deno)>>16))&(DEHSZ-1)) ! 38: #else ! 39: #define DEHASH(dev, deno) (((dev)+(deno)+((deno)>>16))%DEHSZ) ! 40: #endif /* ((DEHSZ & (DEHSZ-1)) == 0) */ ! 41: ! 42: union dehead { ! 43: union dehead *deh_head[2]; ! 44: struct denode *deh_chain[2]; ! 45: } dehead[DEHSZ]; ! 46: ! 47: pcfs_init() ! 48: { ! 49: int i; ! 50: union dehead *deh; ! 51: ! 52: if (VN_MAXPRIVATE < sizeof(struct denode)) ! 53: panic("pcfs_init: vnode too small"); ! 54: ! 55: for (i = DEHSZ, deh = dehead; --i >= 0; deh++) { ! 56: deh->deh_head[0] = deh; ! 57: deh->deh_head[1] = deh; ! 58: } ! 59: } ! 60: ! 61: /* ! 62: * If deget() succeeds it returns with the gotten denode ! 63: * locked(). ! 64: * pmp - address of pcfsmount structure of the filesystem ! 65: * containing the denode of interest. The pm_dev field ! 66: * and the address of the pcfsmount structure are used. ! 67: * dirclust - which cluster bp contains, if dirclust is 0 ! 68: * (root directory) diroffset is relative to the beginning ! 69: * of the root directory, otherwise it is cluster relative. ! 70: * diroffset - offset past begin of cluster of denode we ! 71: * want ! 72: * direntptr - address of the direntry structure of interest. ! 73: * direntptr is NULL, the block is read if necessary. ! 74: * depp - returns the address of the gotten denode. ! 75: */ ! 76: int ! 77: deget (pmp, dirclust, diroffset, direntptr, depp) ! 78: struct pcfsmount *pmp; /* so we know the maj/min number */ ! 79: u_long dirclust; /* cluster this dir entry came from */ ! 80: u_long diroffset; /* index of entry within the cluster */ ! 81: struct direntry *direntptr; ! 82: struct denode **depp; /* returns the addr of the gotten denode*/ ! 83: { ! 84: int error; ! 85: dev_t dev = pmp->pm_dev; ! 86: union dehead *deh; ! 87: struct mount *mntp = pmp->pm_mountp; ! 88: extern struct vnodeops pcfs_vnodeops; ! 89: struct denode *ldep; ! 90: struct vnode *nvp; ! 91: struct buf *bp; ! 92: #if defined(PCFSDEBUG) ! 93: printf("deget(pmp %08x, dirclust %d, diroffset %x, direntptr %x, depp %08x)\n", ! 94: pmp, dirclust, diroffset, direntptr, depp); ! 95: #endif /* defined(PCFSDEBUG) */ ! 96: ! 97: /* If dir entry is given and refers to a directory, convert to ! 98: * canonical form ! 99: */ ! 100: if (direntptr && (direntptr->deAttributes & ATTR_DIRECTORY)) { ! 101: dirclust = direntptr->deStartCluster; ! 102: if (dirclust == PCFSROOT) ! 103: diroffset = PCFSROOT_OFS; ! 104: else ! 105: diroffset = 0; ! 106: } ! 107: ! 108: /* ! 109: * See if the denode is in the denode cache. Use the location of ! 110: * the directory entry to compute the hash value. ! 111: * For subdir use address of "." entry. ! 112: * for root dir use cluster PCFSROOT, offset PCFSROOT_OFS ! 113: * ! 114: * NOTE: The check for de_refcnt > 0 below insures the denode ! 115: * being examined does not represent an unlinked but ! 116: * still open file. These files are not to be accessible ! 117: * even when the directory entry that represented the ! 118: * file happens to be reused while the deleted file is still ! 119: * open. ! 120: */ ! 121: deh = &dehead[DEHASH(dev, dirclust + diroffset)]; ! 122: loop: ! 123: for (ldep = deh->deh_chain[0]; ldep != (struct denode *)deh; ! 124: ldep = ldep->de_forw) { ! 125: if (dev != ldep->de_dev || ldep->de_refcnt == 0) ! 126: continue; ! 127: if (dirclust != ldep->de_dirclust ! 128: || diroffset != ldep->de_diroffset) ! 129: continue; ! 130: if (ldep->de_flag & DELOCKED) { ! 131: /* should we brelse() the passed buf hdr to ! 132: * avoid some potential deadlock? */ ! 133: ldep->de_flag |= DEWANT; ! 134: sleep((caddr_t)ldep, PINOD); ! 135: goto loop; ! 136: } ! 137: if (vget(DETOV(ldep))) ! 138: goto loop; ! 139: *depp = ldep; ! 140: return 0; ! 141: } ! 142: ! 143: ! 144: /* ! 145: * Directory entry was not in cache, have to create ! 146: * a vnode and copy it from the passed disk buffer. ! 147: */ ! 148: /* getnewvnode() does a VREF() on the vnode */ ! 149: if (error = getnewvnode(VT_MSDOSFS, mntp, &pcfs_vnodeops, &nvp)) { ! 150: *depp = 0; ! 151: return error; ! 152: } ! 153: ldep = VTODE(nvp); ! 154: ldep->de_vnode = nvp; ! 155: ldep->de_flag = 0; ! 156: ldep->de_devvp = 0; ! 157: ldep->de_lockf = 0; ! 158: ldep->de_dev = dev; ! 159: fc_purge(ldep, 0); /* init the fat cache for this denode */ ! 160: ! 161: /* ! 162: * Insert the denode into the hash queue and lock the ! 163: * denode so it can't be accessed until we've read it ! 164: * in and have done what we need to it. ! 165: */ ! 166: insque(ldep, deh); ! 167: DELOCK(ldep); ! 168: ! 169: /* ! 170: * Copy the directory entry into the denode area of the ! 171: * vnode. ! 172: */ ! 173: if (dirclust == PCFSROOT && diroffset == PCFSROOT_OFS) { ! 174: /* Directory entry for the root directory. ! 175: * There isn't one, so we manufacture one. ! 176: * We should probably rummage through the root directory and ! 177: * find a label entry (if it exists), and then use the time ! 178: * and date from that entry as the time and date for the ! 179: * root denode. ! 180: */ ! 181: ldep->de_Attributes = ATTR_DIRECTORY; ! 182: ldep->de_StartCluster = PCFSROOT; ! 183: ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec; ! 184: /* fill in time and date so that dos2unixtime() doesn't ! 185: * spit up when called from pcfs_getattr() with root denode */ ! 186: ldep->de_Time = 0x0000; /* 00:00:00 */ ! 187: ldep->de_Date = (0 << 9) | (1 << 5) | (1 << 0); ! 188: /* Jan 1, 1980 */ ! 189: /* leave the other fields as garbage */ ! 190: } ! 191: else { ! 192: bp = NULL; ! 193: if (!direntptr) { ! 194: error = readep(pmp, dirclust, diroffset, &bp, ! 195: &direntptr); ! 196: if (error) ! 197: return error; ! 198: } ! 199: ldep->de_de = *direntptr; ! 200: if (bp) ! 201: brelse (bp); ! 202: } ! 203: ! 204: /* ! 205: * Fill in a few fields of the vnode and finish filling ! 206: * in the denode. Then return the address of the found ! 207: * denode. ! 208: */ ! 209: ldep->de_pmp = pmp; ! 210: ldep->de_devvp = pmp->pm_devvp; ! 211: ldep->de_refcnt = 1; ! 212: ldep->de_dirclust = dirclust; ! 213: ldep->de_diroffset = diroffset; ! 214: if (ldep->de_Attributes & ATTR_DIRECTORY) { ! 215: /* ! 216: * Since DOS directory entries that describe directories ! 217: * have 0 in the filesize field, we take this opportunity ! 218: * to find out the length of the directory and plug it ! 219: * into the denode structure. ! 220: */ ! 221: u_long size; ! 222: ! 223: nvp->v_type = VDIR; ! 224: if (ldep->de_StartCluster == PCFSROOT) ! 225: nvp->v_flag |= VROOT; ! 226: else { ! 227: error = pcbmap(ldep, 0xffff, 0, &size); ! 228: if (error == E2BIG) { ! 229: ldep->de_FileSize = size << pmp->pm_cnshift; ! 230: error = 0; ! 231: } ! 232: else ! 233: printf("deget(): pcbmap returned %d\n", error); ! 234: } ! 235: } ! 236: else ! 237: nvp->v_type = VREG; ! 238: VREF(ldep->de_devvp); ! 239: *depp = ldep; ! 240: return 0; ! 241: } ! 242: ! 243: void ! 244: deput(dep) ! 245: struct denode *dep; ! 246: { ! 247: if ((dep->de_flag & DELOCKED) == 0) ! 248: panic("deput: denode not locked"); ! 249: DEUNLOCK(dep); ! 250: vrele(DETOV(dep)); ! 251: } ! 252: ! 253: int ! 254: deupdat(dep, tp, waitfor) ! 255: struct denode *dep; ! 256: struct timeval *tp; ! 257: int waitfor; ! 258: { ! 259: int error; ! 260: daddr_t bn; ! 261: int diro; ! 262: struct buf *bp; ! 263: struct direntry *dirp; ! 264: struct pcfsmount *pmp = dep->de_pmp; ! 265: struct vnode *vp = DETOV(dep); ! 266: #if defined(PCFSDEBUG) ! 267: printf("deupdat(): dep %08x\n", dep); ! 268: #endif /* defined(PCFSDEBUG) */ ! 269: ! 270: /* ! 271: * If the update bit is off, or this denode is from ! 272: * a readonly filesystem, or this denode is for a ! 273: * directory, or the denode represents an open but ! 274: * unlinked file then don't do anything. DOS directory ! 275: * entries that describe a directory do not ever ! 276: * get updated. This is the way dos treats them. ! 277: */ ! 278: if ((dep->de_flag & DEUPD) == 0 || ! 279: vp->v_mount->mnt_flag & MNT_RDONLY || ! 280: dep->de_Attributes & ATTR_DIRECTORY || ! 281: dep->de_refcnt <= 0) ! 282: return 0; ! 283: ! 284: /* ! 285: * Read in the cluster containing the directory entry ! 286: * we want to update. ! 287: */ ! 288: if (error = readde(dep, &bp, &dirp)) ! 289: return error; ! 290: ! 291: /* ! 292: * Put the passed in time into the directory entry. ! 293: */ ! 294: unix2dostime(&time, (union dosdate *)&dep->de_Date, ! 295: (union dostime *)&dep->de_Time); ! 296: dep->de_flag &= ~DEUPD; ! 297: ! 298: /* ! 299: * Copy the directory entry out of the denode into ! 300: * the cluster it came from. ! 301: */ ! 302: *dirp = dep->de_de; /* structure copy */ ! 303: ! 304: /* ! 305: * Write the cluster back to disk. If they asked ! 306: * for us to wait for the write to complete, then ! 307: * use bwrite() otherwise use bdwrite(). ! 308: */ ! 309: error = 0; /* note that error is 0 from above, but ... */ ! 310: if (waitfor) ! 311: error = bwrite(bp); ! 312: else ! 313: bdwrite(bp); ! 314: return error; ! 315: } ! 316: ! 317: /* ! 318: * Truncate the file described by dep to the length ! 319: * specified by length. ! 320: */ ! 321: int ! 322: detrunc(dep, length, flags) ! 323: struct denode *dep; ! 324: u_long length; ! 325: int flags; ! 326: { ! 327: int error; ! 328: int allerror; ! 329: u_long eofentry; ! 330: u_long chaintofree; ! 331: daddr_t bn; ! 332: int boff; ! 333: int isadir = dep->de_Attributes & ATTR_DIRECTORY; ! 334: struct buf *bp; ! 335: struct pcfsmount *pmp = dep->de_pmp; ! 336: #if defined(PCFSDEBUG) ! 337: printf("detrunc(): file %s, length %d, flags %d\n", dep->de_Name, length, flags); ! 338: #endif /* defined(PCFSDEBUG) */ ! 339: ! 340: /* ! 341: * Disallow attempts to truncate the root directory ! 342: * since it is of fixed size. That's just the way ! 343: * dos filesystems are. We use the VROOT bit in the ! 344: * vnode because checking for the directory bit and ! 345: * a startcluster of 0 in the denode is not adequate ! 346: * to recognize the root directory at this point in ! 347: * a file or directory's life. ! 348: */ ! 349: if (DETOV(dep)->v_flag & VROOT) { ! 350: printf("detrunc(): can't truncate root directory, clust %d, offset %d\n", ! 351: dep->de_dirclust, dep->de_diroffset); ! 352: return EINVAL; ! 353: } ! 354: ! 355: vnode_pager_setsize(DETOV(dep), length); ! 356: ! 357: if (dep->de_FileSize <= length) { ! 358: dep->de_flag |= DEUPD; ! 359: error = deupdat(dep, &time, 1); ! 360: #if defined(PCFSDEBUG) ! 361: printf("detrunc(): file is shorter return point, errno %d\n", error); ! 362: #endif /* defined(PCFSDEBUG) */ ! 363: return error; ! 364: } ! 365: ! 366: /* ! 367: * If the desired length is 0 then remember the starting ! 368: * cluster of the file and set the StartCluster field in ! 369: * the directory entry to 0. If the desired length is ! 370: * not zero, then get the number of the last cluster in ! 371: * the shortened file. Then get the number of the first ! 372: * cluster in the part of the file that is to be freed. ! 373: * Then set the next cluster pointer in the last cluster ! 374: * of the file to CLUST_EOFE. ! 375: */ ! 376: if (length == 0) { ! 377: chaintofree = dep->de_StartCluster; ! 378: dep->de_StartCluster = 0; ! 379: eofentry = ~0; ! 380: } else { ! 381: error = pcbmap(dep, (length-1) >> pmp->pm_cnshift, ! 382: 0, &eofentry); ! 383: if (error) { ! 384: #if defined(PCFSDEBUG) ! 385: printf("detrunc(): pcbmap fails %d\n", error); ! 386: #endif /* defined(PCFSDEBUG) */ ! 387: return error; ! 388: } ! 389: } ! 390: ! 391: fc_purge(dep, (length + pmp->pm_crbomask) >> pmp->pm_cnshift); ! 392: ! 393: /* ! 394: * If the new length is not a multiple of the cluster size ! 395: * then we must zero the tail end of the new last cluster in case ! 396: * it becomes part of the file again because of a seek. ! 397: */ ! 398: if ((boff = length & pmp->pm_crbomask) != 0) { ! 399: /* should read from file vnode or ! 400: * filesystem vnode depending on if file or dir */ ! 401: if (isadir) { ! 402: bn = cntobn(pmp, eofentry); ! 403: error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, ! 404: NOCRED, &bp); ! 405: } else { ! 406: bn = (length-1) >> pmp->pm_cnshift; ! 407: error = bread(DETOV(dep), bn, pmp->pm_bpcluster, ! 408: NOCRED, &bp); ! 409: } ! 410: if (error) { ! 411: brelse(bp); ! 412: return error; ! 413: } ! 414: vnode_pager_uncache(DETOV(dep)); /* what's this for? */ ! 415: /* is this the right ! 416: * place for it? */ ! 417: bzero(bp->b_un.b_addr + boff, pmp->pm_bpcluster - boff); ! 418: if (flags & IO_SYNC) ! 419: bwrite(bp); ! 420: else ! 421: bdwrite(bp); ! 422: } ! 423: ! 424: /* ! 425: * Write out the updated directory entry. Even ! 426: * if the update fails we free the trailing clusters. ! 427: */ ! 428: dep->de_FileSize = length; ! 429: dep->de_flag |= DEUPD; ! 430: vinvalbuf(DETOV(dep), length > 0); ! 431: allerror = deupdat(dep, &time, MNT_WAIT); ! 432: #if defined(PCFSDEBUG) ! 433: printf("detrunc(): allerror %d, eofentry %d\n", ! 434: allerror, eofentry); ! 435: #endif /* defined(PCFSDEBUG) */ ! 436: ! 437: /* ! 438: * If we need to break the cluster chain for the file ! 439: * then do it now. ! 440: */ ! 441: if (eofentry != ~0) { ! 442: error = fatentry(FAT_GET_AND_SET, pmp, eofentry, ! 443: &chaintofree, CLUST_EOFE); ! 444: if (error) { ! 445: #if defined(PCFSDEBUG) ! 446: printf("detrunc(): fatentry errors %d\n", error); ! 447: #endif /* defined(PCFSDEBUG) */ ! 448: return error; ! 449: } ! 450: fc_setcache(dep, FC_LASTFC, (length - 1) >> pmp->pm_cnshift, ! 451: eofentry); ! 452: } ! 453: ! 454: /* ! 455: * Now free the clusters removed from the file because ! 456: * of the truncation. ! 457: */ ! 458: if (chaintofree != 0 && !PCFSEOF(chaintofree)) ! 459: freeclusterchain(pmp, chaintofree); ! 460: ! 461: return allerror; ! 462: } ! 463: ! 464: /* ! 465: * Move a denode to its correct hash queue after ! 466: * the file it represents has been moved to a new ! 467: * directory. ! 468: */ ! 469: reinsert(dep) ! 470: struct denode *dep; ! 471: { ! 472: struct pcfsmount *pmp = dep->de_pmp; ! 473: union dehead *deh; ! 474: ! 475: /* ! 476: * Fix up the denode cache. If the denode is ! 477: * for a directory, there is nothing to do since the ! 478: * hash is based on the starting cluster of the directory ! 479: * file and that hasn't changed. If for a file the hash ! 480: * is based on the location ! 481: * of the directory entry, so we must remove it from the ! 482: * cache and re-enter it with the hash based on the new ! 483: * location of the directory entry. ! 484: */ ! 485: if ((dep->de_Attributes & ATTR_DIRECTORY) == 0) { ! 486: remque(dep); ! 487: deh = &dehead[DEHASH(pmp->pm_dev, ! 488: dep->de_dirclust + dep->de_diroffset)]; ! 489: insque(dep, deh); ! 490: } ! 491: } ! 492: ! 493: int pcfs_prtactive; /* print reclaims of active vnodes */ ! 494: ! 495: int ! 496: pcfs_reclaim(vp) ! 497: struct vnode *vp; ! 498: { ! 499: struct denode *dep = VTODE(vp); ! 500: int i; ! 501: #if defined(PCFSDEBUG) ! 502: printf("pcfs_reclaim(): dep %08x, file %s, refcnt %d\n", ! 503: dep, dep->de_Name, dep->de_refcnt); ! 504: #endif /* defined(PCFSDEBUG) */ ! 505: ! 506: if (pcfs_prtactive && vp->v_usecount != 0) ! 507: vprint("pcfs_reclaim(): pushing active", vp); ! 508: ! 509: /* ! 510: * Remove the denode from the denode hash chain we ! 511: * are in. ! 512: */ ! 513: remque(dep); ! 514: dep->de_forw = dep; ! 515: dep->de_back = dep; ! 516: ! 517: cache_purge(vp); ! 518: /* ! 519: * Indicate that one less file on the filesystem is open. ! 520: */ ! 521: if (dep->de_devvp) { ! 522: vrele(dep->de_devvp); ! 523: dep->de_devvp = 0; ! 524: } ! 525: ! 526: dep->de_flag = 0; ! 527: return 0; ! 528: } ! 529: ! 530: int ! 531: pcfs_inactive(vp, p) ! 532: struct vnode *vp; ! 533: struct proc *p; ! 534: { ! 535: struct denode *dep = VTODE(vp); ! 536: int error = 0; ! 537: #if defined(PCFSDEBUG) ! 538: printf("pcfs_inactive(): dep %08x, de_Name[0] %x\n", dep, dep->de_Name[0]); ! 539: #endif /* defined(PCFSDEBUG) */ ! 540: ! 541: if (pcfs_prtactive && vp->v_usecount != 0) ! 542: vprint("pcfs_inactive(): pushing active", vp); ! 543: ! 544: /* ! 545: * Get rid of denodes related to stale file handles. ! 546: * Hmmm, what does this really do? ! 547: */ ! 548: if (dep->de_Name[0] == SLOT_DELETED) { ! 549: if ((vp->v_flag & VXLOCK) == 0) ! 550: vgone(vp); ! 551: return 0; ! 552: } ! 553: ! 554: /* ! 555: * If the file has been deleted and it is on a read/write ! 556: * filesystem, then truncate the file, and mark the directory ! 557: * slot as empty. (This may not be necessary for the dos ! 558: * filesystem. ! 559: */ ! 560: #if defined(PCFSDEBUG) ! 561: printf("pcfs_inactive(): dep %08x, refcnt %d, mntflag %x, MNT_RDONLY %x\n", ! 562: dep, dep->de_refcnt, vp->v_mount->mnt_flag, MNT_RDONLY); ! 563: #endif /* defined(PCFSDEBUG) */ ! 564: DELOCK(dep); ! 565: if (dep->de_refcnt <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { ! 566: error = detrunc(dep, (u_long)0, 0); ! 567: dep->de_flag |= DEUPD; ! 568: dep->de_Name[0] = SLOT_DELETED; ! 569: } ! 570: DEUPDAT(dep, &time, 0); ! 571: DEUNLOCK(dep); ! 572: dep->de_flag = 0; ! 573: ! 574: /* ! 575: * If we are done with the denode, then reclaim ! 576: * it so that it can be reused now. ! 577: */ ! 578: #if defined(PCFSDEBUG) ! 579: printf("pcfs_inactive(): v_usecount %d, de_Name[0] %x\n", vp->v_usecount, ! 580: dep->de_Name[0]); ! 581: #endif /* defined(PCFSDEBUG) */ ! 582: if (vp->v_usecount == 0 && dep->de_Name[0] == SLOT_DELETED) ! 583: vgone(vp); ! 584: return error; ! 585: } ! 586: ! 587: int ! 588: delock(dep) ! 589: struct denode *dep; ! 590: { ! 591: while (dep->de_flag & DELOCKED) { ! 592: dep->de_flag |= DEWANT; ! 593: if (dep->de_spare0 == curproc->p_pid) ! 594: panic("delock: locking against myself"); ! 595: dep->de_spare1 = curproc->p_pid; ! 596: (void) sleep((caddr_t)dep, PINOD); ! 597: } ! 598: dep->de_spare1 = 0; ! 599: dep->de_spare0 = curproc->p_pid; ! 600: dep->de_flag |= DELOCKED; ! 601: ! 602: return 0; ! 603: } ! 604: ! 605: int ! 606: deunlock(dep) ! 607: struct denode *dep; ! 608: { ! 609: if ((dep->de_flag & DELOCKED) == 0) ! 610: vprint("deunlock: found unlocked denode", DETOV(dep)); ! 611: dep->de_spare0 = 0; ! 612: dep->de_flag &= ~DELOCKED; ! 613: if (dep->de_flag & DEWANT) { ! 614: dep->de_flag &= ~DEWANT; ! 615: wakeup((caddr_t)dep); ! 616: } ! 617: ! 618: return 0; ! 619: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.