|
|
1.1 ! root 1: /* ! 2: * dosshrink.c ! 3: * 4/2/90 ! 4: * ! 5: * Usage: dosshrink [ -cv ] xdevice part device ! 6: * Options: ! 7: * -c Check only, do not write changes ! 8: * -v Print verbose description of actions ! 9: * Default device is defined by INFILE in dos1.h. ! 10: * ! 11: * Restructure the given MSDOS filesystem ! 12: * by shifting files forward to first available free space. ! 13: * ! 14: * Uses functions defined in dos1.c: ! 15: * cc -o dosshrink dosshrink.c dos1.c -f ! 16: * Produces extra debugging output if compiled with DEBUG or FATDUMP defined. ! 17: */ ! 18: ! 19: #include "dos1.h" ! 20: #include <sys/fdisk.h> ! 21: #include <sys/hdioctl.h> ! 22: ! 23: #define VERSION "1.3" /* Version id */ ! 24: #define USAGE "Usage:\t/etc/dosshrink [ -cv ] xdevice part device\n" ! 25: #define NBUF 128 /* interactive input buffer size */ ! 26: #define offsetof(s,id) ((long)(&(((s *)0)->id))) ! 27: ! 28: /* (unsigned int) clusters to (double) megabytes. */ ! 29: #define meg(cl) (((double)(cl))*clsize*ssize/1000000.) ! 30: ! 31: /* Consistency check cluster bitmap. */ ! 32: #define CHAR_BIT 8 /* Bits per char */ ! 33: #define isflag(n) ((bitmap[n/CHAR_BIT] >> (n%CHAR_BIT)) & 1) ! 34: #define setflag(n) bitmap[n/CHAR_BIT] |= 1 << (n%CHAR_BIT); ! 35: ! 36: /* Conversions. */ ! 37: #define sec_to_c(sec) ((unsigned)((sec) / (heads * nspt))) ! 38: #define sec_to_h(sec) ((unsigned)(((sec) / nspt) % heads)) ! 39: #define sec_to_s(sec) ((unsigned)(((sec) % nspt) + 1)) ! 40: ! 41: /* Clear the IBM AT console screen. */ ! 42: #define cls() putchar(0x1B); putchar('c'); fflush(stdout) ! 43: ! 44: /* Externals. */ ! 45: extern double atof(); ! 46: extern char *calloc(); ! 47: extern char *malloc(); ! 48: extern char *memcpy(); ! 49: extern char *memset(); ! 50: extern char *realloc(); ! 51: ! 52: /* Functions. */ ! 53: extern void checkdir(); ! 54: extern void consistency(); ! 55: #if DEBUG ! 56: extern void dumpname(); ! 57: #endif ! 58: extern void info(); ! 59: extern MDIR *readdir(); ! 60: extern void readpart(); ! 61: extern void shuffle(); ! 62: extern unsigned int shuffle1(); ! 63: extern MDIR *writedir(); ! 64: extern void writepart(); ! 65: ! 66: /* Globals. */ ! 67: unsigned char *bitmap; /* Consistency check bitmap */ ! 68: unsigned int maxhidden; /* Max hidden cluster */ ! 69: unsigned int nclusters; /* Clusters written */ ! 70: unsigned int ndirs; /* Directories written */ ! 71: unsigned int removed; /* Number of clusters removed */ ! 72: unsigned int scluster; /* First cluster to shuffle */ ! 73: ! 74: int ! 75: main(argc, argv) int argc; char *argv[]; ! 76: { ! 77: FDISK_S hd_part; ! 78: int xfd, partno; ! 79: register char c; ! 80: register char *s; ! 81: ! 82: argv0 = argv[0]; ! 83: usagemsg = USAGE; ! 84: while (argc > 1 && argv[1][0] == '-') { ! 85: s = &argv[1][1]; ! 86: --argc; ! 87: ++argv; ! 88: while ((c = *s++) != '\0') { ! 89: if (c == 'c') ! 90: ++cflag; ! 91: else if (c == 'v') ! 92: ++vflag; ! 93: else if (c == 'V') ! 94: fprintf(stderr, "%s: V%s\n", argv0, VERSION); ! 95: else ! 96: usage(); ! 97: } ! 98: } ! 99: if (argc != 4) ! 100: usage(); ! 101: if ((xfd = open(argv[1], (cflag) ? 0 : 2)) < 0) ! 102: fatal("cannot open partition table device \"%s\"", argv[1]); ! 103: if ((partno = atoi(argv[2])) < 0 || partno >= NPARTN) ! 104: fatal("invalid partition number %d\n", partno); ! 105: readpart(xfd, partno, &hd_part); ! 106: if ((fsfd = open(argv[3], (cflag) ? 0 : 2)) < 0) ! 107: fatal("cannot open file system \"%s\"", argv[3]); ! 108: info(); ! 109: if (scluster != 0) { ! 110: if ((clbuf = malloc(clsize * ssize)) == NULL) ! 111: fatal("cluster buffer allocation failed"); ! 112: shuffle(0, 0); /* shuffle clusters */ ! 113: free(clbuf); ! 114: } ! 115: writepart(xfd, partno, &hd_part); /* write new partition info */ ! 116: if (fatflag) ! 117: writefat(); /* write new FAT */ ! 118: if (vflag) ! 119: printf("%s %u directories and moved %u clusters\n", ! 120: (cflag) ? "would have rewritten" : "rewrote", ! 121: ndirs, nclusters); ! 122: if (close(fsfd) == -1) ! 123: fatal("cannot close file system \"%s\"", argv[3]); ! 124: exit(0); ! 125: } ! 126: ! 127: /* ! 128: * Read an MSDOS directory and mark used clusters. ! 129: * Read subdirectories recursively. ! 130: */ ! 131: void ! 132: checkdir(cluster, parent) unsigned int cluster, parent; ! 133: { ! 134: register MDIR *mdp, *mp; ! 135: MDIR *edp; ! 136: register unsigned int n; ! 137: int hflag; ! 138: ! 139: dbprintf(("checkdir(%u, %u)\n", cluster, parent)); ! 140: ! 141: /* Scan through the directory. */ ! 142: for (mdp = mp = readdir(cluster, &edp); mdp < edp; mdp++) { ! 143: if ((n = mdp->m_name[0]) == MFREE || n == MEMPTY) ! 144: continue; /* never used or erased */ ! 145: else if (n == MMDIR) { /* "." or "..", hopefully */ ! 146: if ((n = mdp->m_name[1]) == MMDIR && mdp->m_cluster == parent) ! 147: continue; ! 148: else if (n == ' ' && mdp->m_cluster == cluster) ! 149: continue; ! 150: else ! 151: fatal("checkdir: \"%c%c\" %u %u %u", ! 152: MMDIR, n, mdp->m_cluster, cluster, parent); ! 153: } else if (mdp->m_size == 0L && !isdir(mdp)) /* 0-size file */ ! 154: continue; ! 155: #if DEBUG ! 156: dumpname(mdp); ! 157: #endif ! 158: hflag = ishidden(mdp); ! 159: for (n = mdp->m_cluster; n <= CLMAX; n = getcluster(n)) { ! 160: if (n < 2 || n > maxcluster) ! 161: fatal("checkdir: bad cluster number %u", n); ! 162: else if (isflag(n)) ! 163: fatal("cross-linked cluster %u", n); ! 164: else ! 165: setflag(n); ! 166: if (hflag && n > maxhidden) ! 167: maxhidden = n; ! 168: } ! 169: if (n <= CLBAD) ! 170: fatal("unexpected 0x%x in file cluster chain", n); ! 171: if (isdir(mdp)) ! 172: checkdir(mdp->m_cluster, cluster); ! 173: } ! 174: free(mp); ! 175: } ! 176: ! 177: /* ! 178: * Consistency check. ! 179: */ ! 180: void ! 181: consistency() ! 182: { ! 183: register unsigned int i, n; ! 184: ! 185: if ((bitmap = calloc((maxcluster+1+CHAR_BIT-1)/CHAR_BIT, 1)) == NULL) ! 186: fatal("bitmap allocation failed"); ! 187: checkdir(0, 0); /* recursive starting at root */ ! 188: for (i = 2; i <= maxcluster; i++) { ! 189: if (isflag(i)) ! 190: continue; /* marked used by checkdir */ ! 191: n = fat[i]; /* FAT value */ ! 192: if (n == CLFREE || (n > CLMAX && n <= CLBAD)) ! 193: continue; /* free, bad or reserved */ ! 194: else ! 195: fatal("lost cluster %u, value %u", i, n); ! 196: } ! 197: free(bitmap); ! 198: } ! 199: ! 200: #if DEBUG ! 201: /* ! 202: * Print an MS-DOS filename. ! 203: */ ! 204: void ! 205: dumpname(mdp) register MDIR *mdp; ! 206: { ! 207: register char *cp; ! 208: char buf[13]; ! 209: ! 210: cp = lcname(buf, mdp->m_name, 8); ! 211: if (mdp->m_ext[0] != ' ') { ! 212: *cp++ = '.'; ! 213: cp = lcname(cp, mdp->m_ext, 3); ! 214: } ! 215: printf("%s:\n", buf); ! 216: } ! 217: #endif ! 218: ! 219: /* ! 220: * Print information about the MS-DOS file system. ! 221: */ ! 222: void ! 223: info() ! 224: { ! 225: static char buf[NBUF]; ! 226: register unsigned int n, i; ! 227: unsigned int free, bad, reserved, eof, ordinary; ! 228: unsigned int used, dosused, dosfree, minfree, maxused; ! 229: ! 230: cls(); ! 231: readfat(); ! 232: if (vflag) ! 233: printf("heads=%u nspt=%u clsize=%u ssize=%u\n", ! 234: heads, nspt, clsize, ssize); ! 235: free = bad = reserved = eof = ordinary = maxused = 0; ! 236: for (n = 2; n <= maxcluster; n++) { ! 237: #if FATDUMP ! 238: printf("fat[%u]=%u\n", n, getcluster(n)); ! 239: #endif ! 240: if ((i = getcluster(n)) == 0) ! 241: ++free; ! 242: else if (i == CLBAD) ! 243: ++bad; ! 244: else if (i > CLMAX && i < CLBAD) ! 245: ++reserved; ! 246: else if (i == CLEOF) { ! 247: ++eof; ! 248: maxused = n; ! 249: } else { ! 250: ++ordinary; ! 251: maxused = n; ! 252: } ! 253: } ! 254: if (vflag) { ! 255: printf("%u clusters: %u free, %u EOF, %u continuation, %u bad, %u reserved\n", ! 256: maxcluster - 1, free, eof, ordinary, bad, reserved); ! 257: printf("maxused=%u\n", maxused); ! 258: } ! 259: ! 260: /* Make sure the file system is consistent with the FAT. */ ! 261: consistency(); ! 262: if (vflag) ! 263: printf("maxhidden=%u\n", maxhidden); ! 264: ! 265: used = ordinary + eof; ! 266: printf("The MS-DOS file system currently contains:\n"); ! 267: printf("\t%6.2f\tmegabytes used,\n", meg(used)); ! 268: printf("\t%6.2f\tmegabytes free,\n", meg(free)); ! 269: if (bad != 0) ! 270: printf("\t%6.2f\tmegabytes in bad clusters,\n", meg(bad)); ! 271: if (reserved != 0) ! 272: printf("\t%6.2f\tmegabytes reserved,\n", meg(reserved)); ! 273: printf("or\t%6.2f\tmegabytes total.\n", meg(maxcluster-1)); ! 274: if (maxhidden > used) { ! 275: minfree = maxhidden - (used); ! 276: printf("This program will not move hidden files, which are sometimes\n"); ! 277: printf("used by copy protection schemes. Because of a hidden file,\n"); ! 278: printf("you must leave at least %.2f megabytes free for MS-DOS.\n", ! 279: meg(minfree)); ! 280: } else ! 281: minfree = 0; ! 282: printf("\n"); ! 283: again: ! 284: printf("How many megabytes do you wish leave for use by MS-DOS? "); ! 285: fflush(stdout); ! 286: fgets(buf, NBUF, stdin); ! 287: dosused = (unsigned)(atof(buf) * 1000000. / (clsize * ssize)); ! 288: dosfree = dosused - used; ! 289: if (dosused < used || dosfree < minfree || dosfree > free) { ! 290: printf("Please enter a value between %.2f and %.2f.\n", ! 291: meg(used + minfree), meg(used + free)); ! 292: goto again; ! 293: } else ! 294: free -= dosfree; ! 295: printf("The modified file system will contain %.2f free megabytes.\n", ! 296: meg(dosfree)); ! 297: printf("The unused space after the file system will contain %.2f free megabytes.\n", ! 298: meg(free)); ! 299: if (!cflag) { ! 300: printf("Do you want to modify the MS-DOS file system [y]?"); ! 301: fgets(buf, NBUF, stdin); ! 302: if (buf[0] != '\0' && buf[0] != '\n' && buf[0] != 'y') ! 303: fatal("aborted"); ! 304: } ! 305: ! 306: /* Find the first cluster which needs to be shuffled. */ ! 307: removed = scluster = 0; ! 308: for (n = maxcluster; free > 0 && n >= 2; n--) { ! 309: ++removed; ! 310: if ((i = getcluster(n)) == CLFREE) ! 311: --free; ! 312: else if (i <= CLMAX || i == CLEOF) { ! 313: --free; ! 314: scluster = n; ! 315: } ! 316: } ! 317: if (vflag) ! 318: printf("removed=%u\nscluster=%u\n", removed, scluster); ! 319: } ! 320: ! 321: /* ! 322: * Read an MSDOS directory. ! 323: * Return a pointer to the allocated directory buffer. ! 324: * Store an end pointer through epp. ! 325: */ ! 326: MDIR * ! 327: readdir(cluster, epp) unsigned int cluster; MDIR **epp; ! 328: { ! 329: register MDIR *mdp, *mp, *edp; ! 330: register unsigned int n, c, blocks, files; ! 331: static unsigned int rootfiles; ! 332: ! 333: if (cluster == 0) { ! 334: if (rootfiles == 0) { ! 335: files = bpb->b_files; ! 336: blocks = dirsize; ! 337: } else { ! 338: files = rootfiles; ! 339: blocks = (files * sizeof(MDIR) + ssize - 1) / ssize; ! 340: } ! 341: } else { ! 342: for (c = cluster, blocks = 1; (c = getcluster(c)) <= CLMAX; ++blocks) ! 343: ; ! 344: blocks *= clsize; ! 345: files = blocks * BSIZE / sizeof(MDIR); ! 346: } ! 347: dbprintf(("readdir(cluster=%u) files=%u blocks=%u\n", cluster, files, blocks)); ! 348: if ((mp = (MDIR *)malloc(blocks * BSIZE)) == NULL) ! 349: fatal("directory allocation failed"); ! 350: mdp = mp; ! 351: if (cluster == 0) ! 352: diskread(mdp, dirbase, blocks, "directory"); ! 353: else ! 354: for (n = cluster; n <= CLMAX; n = getcluster(n)) { ! 355: diskread(mdp, cltosec(n), clsize, "subdirectory"); ! 356: mdp += mdirsize; ! 357: } ! 358: ! 359: /* To conserve space, free unused portion of directory. */ ! 360: for (mdp = mp; mdp < mp + files; mdp++) ! 361: if ((n = mdp->m_name[0]) != MFREE && n != MEMPTY) ! 362: edp = mdp; ! 363: if (++edp != mp + files && realloc(mp, (char *)edp - (char *)mp) == NULL) ! 364: fatal("realloc failed"); ! 365: if (cluster == 0) ! 366: rootfiles = edp - mp; ! 367: *epp = edp; ! 368: return mp; ! 369: } ! 370: ! 371: /* ! 372: * Read an entry from the partition table. ! 373: * Look for a valid signature. ! 374: */ ! 375: void ! 376: readpart(xfd, partno, pp) int xfd, partno; FDISK_S *pp; ! 377: { ! 378: unsigned short sig; ! 379: ! 380: if (lseek(xfd, offsetof(HDISK_S, hd_partn[partno]), 0) == -1) ! 381: fatal("partition table seek failed"); ! 382: else if (read(xfd, pp, sizeof(FDISK_S)) != sizeof(FDISK_S)) ! 383: fatal("partition table read failed"); ! 384: else if (lseek(xfd, offsetof(HDISK_S, hd_sig), 0) == -1) ! 385: fatal("partition table seek failed"); ! 386: else if (read(xfd, &sig, sizeof sig) != sizeof sig) ! 387: fatal("partition table read failed"); ! 388: else if (sig != HDSIG) ! 389: fatal("invalid partition table signature %x", sig); ! 390: } ! 391: ! 392: /* ! 393: * Shuffle blocks, rewrite FAT and subdirectory entries ! 394: * to shift all files to front of file system. ! 395: * To keep life simple, this moves as few blocks as possible; ! 396: * each free block at the front is replaced by a used block from the back. ! 397: * Watch out for moving subdirectories (with "." and ".." entries) ! 398: * and for the readonly flag (where nothing really changes on the file system). ! 399: */ ! 400: void ! 401: shuffle(cluster, parent) unsigned int cluster, parent; ! 402: { ! 403: register MDIR *mdp, *mp, *edp; ! 404: register unsigned int n, lastn, changed; ! 405: ! 406: dbprintf(("shuffle(%u, %u)\n", cluster, parent)); ! 407: /* Scan through the directory. */ ! 408: changed = 0; ! 409: for (mdp = mp = readdir(cluster, &edp); mdp < edp; mdp++) { ! 410: if ((n = mdp->m_name[0]) == MFREE || n == MEMPTY) ! 411: continue; /* empty or never used */ ! 412: else if (n == MMDIR) { /* "." or ".." */ ! 413: /* Watch out for "." and "..", something may have moved. */ ! 414: if ((n = mdp->m_name[1]) == MMDIR) { ! 415: if (mdp->m_cluster != parent) { ! 416: mdp->m_cluster = parent; ! 417: ++changed; ! 418: } ! 419: } else if (n == ' ') { ! 420: if (mdp->m_cluster != cluster) { ! 421: mdp->m_cluster = cluster; ! 422: ++changed; ! 423: } ! 424: } else ! 425: fatal("shuffle: \"%c%c\" botch", MMDIR, n); ! 426: continue; ! 427: } else if (mdp->m_size == 0L && !isdir(mdp)) /* 0-size file */ ! 428: continue; ! 429: lastn = 0; ! 430: for (n = mdp->m_cluster; n <= CLMAX; n = getcluster(n)) { ! 431: if (n < 2 || n > maxcluster) ! 432: fatal("shuffle: bad cluster number %u", n); ! 433: if (n >= scluster) { ! 434: n = shuffle1(n); ! 435: if (lastn == 0) { ! 436: mdp->m_cluster = n; ! 437: ++changed; ! 438: } else ! 439: putcluster(lastn, n); ! 440: } ! 441: lastn = n; ! 442: } ! 443: if (isdir(mdp)) ! 444: shuffle(mdp->m_cluster, cluster); ! 445: } ! 446: if (changed) { ! 447: ++ndirs; ! 448: /* Rewrite the directory on the disk. */ ! 449: dbprintf(("rewrite disk directory cluster=%u\n", cluster)); ! 450: if (!cflag) { ! 451: mdp = mp; ! 452: if (cluster == 0) ! 453: writedir(mdp, edp, dirbase, dirsize); ! 454: else ! 455: for (n = cluster; n <= CLMAX; n = getcluster(n)) ! 456: mdp = writedir(mdp, edp, cltosec(n), clsize); ! 457: } ! 458: } ! 459: free(mp); ! 460: } ! 461: ! 462: /* ! 463: * Shuffle cluster n to the first free cluster. ! 464: * Return the new cluster number. ! 465: * The directory or FAT entry which points to n gets updated by the caller. ! 466: */ ! 467: unsigned int ! 468: shuffle1(n) unsigned int n; ! 469: { ! 470: register unsigned int i; ! 471: ! 472: ++nclusters; ! 473: for (i = 2; i <= maxcluster; i++) ! 474: if (getcluster(i) == CLFREE) ! 475: break; ! 476: if (i > maxcluster) ! 477: fatal("no free blocks in shuffle1"); ! 478: dbprintf(("cluster %u -> %u\n", n, i)); ! 479: putcluster(i, getcluster(n)); /* next cl to new FAT entry */ ! 480: if (cflag) ! 481: return n; /* return old value */ ! 482: putcluster(n, CLFREE); /* free current FAT entry */ ! 483: diskread(clbuf, cltosec(n), clsize, "cluster"); ! 484: diskwrite(clbuf, cltosec(i), clsize, "cluster"); /* copy cluster */ ! 485: return i; ! 486: } ! 487: ! 488: /* ! 489: * Rewrite a directory. ! 490: * The directory entries start at mdp and end at edp. ! 491: * The target is nsecs sectors starting at sector n. ! 492: * Pad with zeros if necessary. ! 493: * Return a pointer past the last byte written from mdp. ! 494: * Use the cluster buffer clbuf to write up to one cluster at a time. ! 495: * This is all necessary only because of the code which realloc's ! 496: * directories, to save space; otherwise, it would be a diskwrite() call. ! 497: */ ! 498: MDIR * ! 499: writedir(mdp, edp, n, nsecs) register MDIR *mdp, *edp; unsigned int n, nsecs; ! 500: { ! 501: unsigned int secs, nb, mb, s1, s2; ! 502: ! 503: diskseek(n); ! 504: while (nsecs != 0) { ! 505: mb = (char *)edp - (char *)mdp; /* bytes at mdp */ ! 506: secs = (nsecs <= clsize) ? nsecs : clsize; /* sectors */ ! 507: nb = secs * ssize; /* bytes in clbuf */ ! 508: s1 = (mb <= nb) ? mb : nb; /* mdp bytes */ ! 509: s2 = nb - s1; /* zero bytes */ ! 510: if (s1 != 0) ! 511: memcpy(clbuf, mdp, s1); ! 512: if (s2 != 0) ! 513: memset(clbuf + s1, 0, s2); ! 514: if (write(fsfd, clbuf, nb) != nb) ! 515: fatal("directory write error"); ! 516: nsecs -= secs; ! 517: mdp += s1; ! 518: } ! 519: return mdp; ! 520: } ! 521: ! 522: /* ! 523: * Write an updated partition table entry. ! 524: * Also update the sector count in the BPB. ! 525: */ ! 526: void ! 527: writepart(xfd, partno, pp) int xfd, partno; FDISK_S *pp; ! 528: { ! 529: unsigned int c, h, s; ! 530: unsigned long size, end; ! 531: int ofatbytes; ! 532: ! 533: if (cflag) ! 534: return; ! 535: ! 536: size = pp->p_size; /* old size */ ! 537: size -= clsize * (long) removed; /* new size */ ! 538: end = pp->p_base + size - 1; /* new end */ ! 539: c = sec_to_c(end); ! 540: h = sec_to_h(end); ! 541: s = sec_to_s(end); ! 542: ! 543: /* Extend paritition to nearest track boundary. */ ! 544: if (s != nspt) { ! 545: size += nspt - s; ! 546: end += nspt - s; ! 547: s = nspt; ! 548: } ! 549: ! 550: /* Update the size and end parameters in the partition table. */ ! 551: pp->p_size = size; ! 552: pp->p_ecyl = c & 0xFF; ! 553: pp->p_ehd = h; ! 554: pp->p_esec = ((c >> 2) & CYLMASK) | s; ! 555: ! 556: /* Write the updated partition table entry. */ ! 557: if (lseek(xfd, offsetof(HDISK_S, hd_partn[partno]), 0) == -1) ! 558: fatal("partition table seek failed"); ! 559: else if (write(xfd, pp, sizeof(FDISK_S)) != sizeof(FDISK_S)) ! 560: fatal("partition table read failed"); ! 561: ! 562: /* Update the BPB sector count. */ ! 563: if (lseek(fsfd, (long)BPBOFF + offsetof(BPB, b_sectors), 0) == -1) ! 564: fatal("BPB seek failed"); ! 565: s = size; ! 566: if (write(fsfd, &s, sizeof s) != sizeof s) ! 567: fatal("BPB write failed"); ! 568: ! 569: /* ! 570: * If the partition shrinks enough, ! 571: * 2-byte FAT entries become 1.5-byte entries. ! 572: */ ! 573: ofatbytes = fatbytes; ! 574: fatbytes = (size / clsize > (FATMASK & CLMAX)) ? 2 : 1; ! 575: if (ofatbytes != fatbytes) ! 576: ++fatflag; /* rewrite the FAT */ ! 577: } ! 578: ! 579: /* end of dosshrink.c */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.