dosread.c

Go to the documentation of this file.
00001 /* dos{dir|read|write} - {list|read|write} MS-DOS disks  Author: M. Huisjes */
00002 
00003 /* Dosdir - list MS-DOS directories. doswrite - write stdin to DOS-file
00004  * dosread - read DOS-file to stdout
00005  *
00006  * Author: Michiel Huisjes.
00007  *
00008  * Usage: dos... [-lra] drive [file/dir]
00009  *        l: Give long listing.
00010  *        r: List recursively.
00011  *        a: Set ASCII bit.
00012  */
00013 
00014 #include <assert.h>
00015 #include <ctype.h>
00016 #include <errno.h>
00017 #include <limits.h>
00018 #include <sys/types.h>
00019 #include <sys/stat.h>
00020 #include <fcntl.h>
00021 #include <stdlib.h>
00022 #include <stdio.h>
00023 #include <string.h>
00024 #include <time.h>
00025 #include <sys/times.h>
00026 #include <unistd.h>
00027 
00028 
00029 #define MAX_CLUSTER_SIZE        4096
00030 #define MAX_ROOT_ENTRIES        512
00031 #define FAT_START               512L    /* After bootsector */
00032 #define ROOTADDR                (FAT_START + 2L * fat_size)
00033 #define clus_add(cl_no)         ((long) (((long) cl_no - 2L) \
00034                                  * (long) cluster_size \
00035                                  + data_start \
00036                                 ))
00037 struct dir_entry {
00038   unsigned char d_name[8];
00039   unsigned char d_ext[3];
00040   unsigned char d_attribute;
00041   unsigned char d_reserved[10];
00042   unsigned short d_time;
00043   unsigned short d_date;
00044   unsigned short d_cluster;
00045   unsigned long d_size;
00046 };
00047 
00048 typedef struct dir_entry DIRECTORY;
00049 
00050 #define NOT_USED        0x00
00051 #define ERASED          0xE5
00052 #define DIR             0x2E
00053 #define DIR_SIZE        (sizeof (struct dir_entry))
00054 #define SUB_DIR         0x10
00055 #define NIL_DIR         ((DIRECTORY *) 0)
00056 
00057 #define LAST_CLUSTER12  0xFFF
00058 #define LAST_CLUSTER    0xFFFF
00059 #define FREE            0x000
00060 #define BAD             0xFF0
00061 #define BAD16           0xFFF0
00062 
00063 typedef int BOOL;
00064 
00065 #define TRUE    1
00066 #define FALSE   0
00067 #define NIL_PTR ((char *) 0)
00068 
00069 #define DOS_TIME        315532800L      /* 1970 - 1980 */
00070 
00071 #define READ                    0
00072 #define WRITE                   1
00073 
00074 #define FIND    3
00075 #define LABEL   4
00076 #define ENTRY   5
00077 #define find_entry(d, e, p)     directory(d, e, FIND, p)
00078 #define list_dir(d, e, f)       (void) directory(d, e, f, NIL_PTR)
00079 #define label()                 directory(root, root_entries, LABEL, NIL_PTR)
00080 #define new_entry(d, e)         directory(d, e, ENTRY, NIL_PTR)
00081 
00082 #define is_dir(d)               ((d)->d_attribute & SUB_DIR)
00083 
00084 #define STD_OUT                 1
00085 
00086 char    *cmnd;
00087 
00088 static int disk;        /* File descriptor for disk I/O */
00089 
00090 static DIRECTORY root[MAX_ROOT_ENTRIES];
00091 static DIRECTORY save_entry;
00092 static char drive[] = "/dev/dosX";
00093 #define DRIVE_NR        (sizeof (drive) - 2)
00094 static char null[MAX_CLUSTER_SIZE], *device = drive, path[128];
00095 static long data_start;
00096 static long mark;       /* offset of directory entry to be written */
00097 static unsigned short total_clusters, cluster_size, root_entries, sub_entries;
00098 static unsigned long fat_size;
00099 
00100 static BOOL Rflag, Lflag, Aflag, dos_read, dos_write, dos_dir, fat_16 = 0;
00101 static BOOL big_endian;
00102 
00103 /* maximum size of a cooked 12bit FAT. Also Size of 16bit FAT cache
00104  * if not enough memory for whole FAT
00105  */
00106 #define COOKED_SIZE             8192
00107 /* raw FAT. Only used for 12bit FAT to make conversion easier 
00108  */
00109 static unsigned char    *raw_fat;
00110 /* Cooked FAT. May be only part of the FAT for 16 bit FATs
00111  */
00112 static unsigned short   *cooked_fat;
00113 /* lowest and highest entry in fat cache
00114  */
00115 static unsigned short   fat_low = USHRT_MAX,
00116                         fat_high = 0;
00117 static BOOL             fat_dirty = FALSE;
00118 static unsigned int     cache_size;
00119 
00120 
00121 /* Prototypes. */
00122 _PROTOTYPE(void usage, (char *prog_name) );
00123 _PROTOTYPE(unsigned c2u2, (unsigned char *ucarray) );
00124 _PROTOTYPE(unsigned long c4u4, (unsigned char *ucarray) );
00125 _PROTOTYPE(void determine, (void));
00126 _PROTOTYPE(int main, (int argc, char *argv []));
00127 _PROTOTYPE(DIRECTORY *directory, (DIRECTORY *dir, int entries, BOOL function, char *pathname) );
00128 _PROTOTYPE(void extract, (DIRECTORY *entry) );
00129 _PROTOTYPE(void make_file, (DIRECTORY *dir_ptr, int entries, char *name) );
00130 _PROTOTYPE(void fill_date, (DIRECTORY *entry) );
00131 _PROTOTYPE(char *make_name, (DIRECTORY *dir_ptr, int dir_fl) );
00132 _PROTOTYPE(int fill, (char *buffer, size_t size) );
00133 _PROTOTYPE(void xmodes, (int mode) );
00134 _PROTOTYPE(void show, (DIRECTORY *dir_ptr, char *name) );
00135 _PROTOTYPE(void free_blocks, (void));
00136 _PROTOTYPE(DIRECTORY *read_cluster, (unsigned int cluster) );
00137 _PROTOTYPE(unsigned short free_cluster, (BOOL leave_fl) );
00138 _PROTOTYPE(void link_fat, (unsigned int cl_1, unsigned int cl_2) );
00139 _PROTOTYPE(unsigned short next_cluster, (unsigned int cl_no) );
00140 _PROTOTYPE(char *slash, (char *str) );
00141 _PROTOTYPE(void add_path, (char *file, BOOL slash_fl) );
00142 _PROTOTYPE(void disk_io, (BOOL op, unsigned long seek, void *address, unsigned bytes) );
00143 _PROTOTYPE(void flush_fat, (void));
00144 _PROTOTYPE(void read_fat, (unsigned int cl_no));
00145 _PROTOTYPE(BOOL free_range, (unsigned short *first, unsigned short *last));
00146 _PROTOTYPE(long lmin, (long a, long b));
00147 
00148 
00149 void usage(prog_name)
00150 register char *prog_name;
00151 {
00152   fprintf (stderr, "Usage: %s [%s\n", prog_name,
00153              (dos_dir ? "-lr] drive [dir]" : "-a] drive file"));
00154   exit(1);
00155 }
00156 
00157 unsigned c2u2(ucarray)
00158 unsigned char *ucarray;
00159 {
00160   return ucarray[0] + (ucarray[1] << 8);        /* parens vital */
00161 }
00162 
00163 unsigned long c4u4(ucarray)
00164 unsigned char *ucarray;
00165 {
00166   return ucarray[0] + ((unsigned long) ucarray[1] << 8) +
00167                       ((unsigned long) ucarray[2] << 16) +
00168                       ((unsigned long) ucarray[3] << 24);
00169 }
00170 
00171 void determine()
00172 {
00173   struct dosboot {
00174         unsigned char cjump[2]; /* unsigneds avoid bugs */
00175         unsigned char nop;
00176         unsigned char name[8];
00177         unsigned char cbytepers[2];     /* don't use shorts, etc */
00178         unsigned char secpclus;         /* to avoid struct member */
00179         unsigned char creservsec[2];    /* alignment and byte */
00180         unsigned char fats;             /* order bugs */
00181         unsigned char cdirents[2];
00182         unsigned char ctotsec[2];
00183         unsigned char media;
00184         unsigned char csecpfat[2];
00185         unsigned char csecptrack[2];
00186         unsigned char cheads[2];
00187         unsigned char chiddensec[2];
00188         unsigned char dos4hidd2[2];
00189         unsigned char dos4totsec[4];
00190         /* Char    fill[476]; */
00191   } boot;
00192   unsigned short boot_magic;    /* last of boot block */
00193   unsigned bytepers, reservsec, dirents;
00194   unsigned secpfat, secptrack, heads, hiddensec;
00195   unsigned long totsec;
00196   unsigned char fat_info, fat_check;
00197   unsigned short endiantest = 1;
00198   int errcount = 0;
00199 
00200   big_endian = !(*(unsigned char *)&endiantest);
00201 
00202   /* Read Bios-Parameterblock */
00203   disk_io(READ, 0L, &boot, sizeof boot);
00204   disk_io(READ, 0x1FEL, &boot_magic, sizeof boot_magic);
00205 
00206   /* Convert some arrays */
00207   bytepers = c2u2(boot.cbytepers);
00208   reservsec = c2u2(boot.creservsec);
00209   dirents = c2u2(boot.cdirents);
00210   totsec = c2u2(boot.ctotsec);
00211   if (totsec == 0) totsec = c4u4(boot.dos4totsec);
00212   secpfat = c2u2(boot.csecpfat);
00213   secptrack = c2u2(boot.csecptrack);
00214   heads = c2u2(boot.cheads);
00215 
00216   /* The `hidden sectors' are the sectors before the partition.
00217    * The calculation here is probably wrong (I think the dos4hidd2
00218    * bytes are the msbs), but that doesn't matter, since the
00219    * value isn't used anyway
00220    */
00221   hiddensec = c2u2(boot.chiddensec);
00222   if (hiddensec == 0) hiddensec = c2u2 (boot.dos4hidd2);
00223 
00224   /* Safety checking */
00225   if (boot_magic != 0xAA55) {
00226         fprintf (stderr, "%s: magic != 0xAA55\n", cmnd);
00227         ++errcount;
00228   }
00229 
00230   /* Check sectors per track instead of inadequate media byte */
00231   if (secptrack < 15 &&         /* assume > 15 hard disk & wini OK */
00232 #ifdef SECT10                   /* BIOS modified for 10 sec/track */
00233       secptrack != 10 &&
00234 #endif
00235 #ifdef SECT8                    /* BIOS modified for 8 sec/track */
00236       secptrack != 8 &&
00237 #endif
00238       secptrack != 9) {
00239         fprintf (stderr, "%s: %d sectors per track not supported\n", cmnd, secptrack);
00240         ++errcount;
00241   }
00242   if (bytepers == 0) {
00243         fprintf (stderr, "%s: bytes per sector == 0\n", cmnd);
00244         ++errcount;
00245   }
00246   if (boot.secpclus == 0) {
00247         fprintf (stderr, "%s: sectors per cluster == 0\n", cmnd);
00248         ++errcount;
00249   }
00250   if (boot.fats != 2 && dos_write) {
00251         fprintf (stderr, "%s: fats != 2\n", cmnd);
00252         ++errcount;
00253   }
00254   if (reservsec != 1) {
00255         fprintf (stderr, "%s: reserved != 1\n", cmnd);
00256         ++errcount;
00257   }
00258   if (errcount != 0) {
00259         fprintf (stderr, "%s: Can't handle disk\n", cmnd);
00260         exit(2);
00261   }
00262 
00263   /* Calculate everything. */
00264   if (boot.secpclus == 0) boot.secpclus = 1;
00265   total_clusters =
00266         (totsec - boot.fats * secpfat - reservsec -
00267          dirents * 32L / bytepers                   ) / boot.secpclus + 2;
00268         /* first 2 entries in FAT aren't used */
00269   cluster_size = bytepers * boot.secpclus;
00270   fat_size = (unsigned long) secpfat * (unsigned long) bytepers;
00271   data_start = (long) bytepers + (long) boot.fats * fat_size
00272         + (long) dirents *32L;
00273   root_entries = dirents;
00274   sub_entries = boot.secpclus * bytepers / 32;
00275   if (total_clusters > 4096) fat_16 = 1;
00276 
00277   /* Further safety checking */
00278   if (cluster_size > MAX_CLUSTER_SIZE) {
00279         fprintf (stderr, "%s: cluster size too big\n", cmnd);
00280         ++errcount;
00281   }
00282 
00283   disk_io(READ, FAT_START, &fat_info, 1);
00284   disk_io(READ, FAT_START + fat_size, &fat_check, 1);
00285   if (fat_check != fat_info) {
00286         fprintf (stderr, "%s: Disk type in FAT copy differs from disk type in FAT original.\n", cmnd);
00287         ++errcount;
00288   }
00289   if (errcount != 0) {
00290         fprintf (stderr, "%s: Can't handle disk\n", cmnd);
00291         exit(2);
00292   }
00293 }
00294 
00295 int main(argc, argv)
00296 int argc;
00297 register char *argv[];
00298 {
00299   register char *arg_ptr = slash(argv[0]);
00300   DIRECTORY *entry;
00301   short idx = 1;
00302   char dev_nr = '0';
00303 
00304   cmnd = arg_ptr;       /* needed for error messages */
00305   if (!strcmp(arg_ptr, "dosdir"))
00306         dos_dir = TRUE;
00307   else if (!strcmp(arg_ptr, "dosread"))
00308         dos_read = TRUE;
00309   else if (!strcmp(arg_ptr, "doswrite"))
00310         dos_write = TRUE;
00311   else {
00312         fprintf (stderr, "%s: Program should be named dosread, doswrite or dosdir.\n", cmnd);
00313         exit(1);
00314   }
00315 
00316   if (argc == 1) usage(argv[0]);
00317 
00318   if (argv[1][0] == '-') {
00319         for (arg_ptr = &argv[1][1]; *arg_ptr; arg_ptr++) {
00320                 if (*arg_ptr == 'l' && dos_dir) {
00321                         Lflag = TRUE;
00322                 } else if (*arg_ptr == 'r' && dos_dir) {
00323                         Rflag = TRUE;
00324                 } else if (*arg_ptr == 'a' && !dos_dir) {
00325                         assert ('\n' == 10);
00326                         assert ('\r' == 13);
00327                         Aflag = TRUE;
00328                 } else {
00329                         usage(argv[0]);
00330                 }
00331         }
00332         idx++;
00333   }
00334   if (idx == argc) usage(argv[0]);
00335 
00336   if (strlen(argv[idx]) > 1) {
00337         device = argv[idx++];
00338 
00339         /* If the device does not contain a / we assume that it
00340          * is the name of a device in /dev. Instead of prepending
00341          * /dev/ we try to chdir there.
00342          */
00343         if (strchr(device, '/') == NULL && chdir("/dev") < 0) {
00344                 perror("/dev");
00345                 exit(1);
00346         }
00347   } else {
00348         if ((dev_nr = toupper (*argv[idx++])) < 'A' || dev_nr > 'Z')
00349                 usage(argv[0]);
00350 
00351         device[DRIVE_NR] = dev_nr;
00352   }
00353 
00354   if ((disk = open(device, dos_write ? O_RDWR : O_RDONLY)) < 0) {
00355         fprintf (stderr, "%s: cannot open %s: %s\n",
00356                  cmnd, device, strerror (errno));
00357         exit(1);
00358   }
00359   determine();
00360   disk_io(READ, ROOTADDR, root, DIR_SIZE * root_entries);
00361 
00362   if (dos_dir && Lflag) {
00363         entry = label();
00364         printf ("Volume in drive %c ", dev_nr);
00365         if (entry == NIL_DIR)
00366                 printf("has no label.\n\n");
00367         else
00368                 printf ("is %.11s\n\n", entry->d_name);
00369   }
00370   if (argv[idx] == NIL_PTR) {
00371         if (!dos_dir) usage(argv[0]);
00372         if (Lflag) printf ("Root directory:\n");
00373         list_dir(root, root_entries, FALSE);
00374         if (Lflag) free_blocks();
00375         fflush (stdout);
00376         exit(0);
00377   }
00378   for (arg_ptr = argv[idx]; *arg_ptr; arg_ptr++)
00379         if (*arg_ptr == '\\')   *arg_ptr = '/';
00380         else                    *arg_ptr = toupper (*arg_ptr);
00381   if (*--arg_ptr == '/') *arg_ptr = '\0';       /* skip trailing '/' */
00382 
00383   add_path(argv[idx], FALSE);
00384   add_path("/", FALSE);
00385 
00386   if (dos_dir && Lflag) printf ( "Directory %s:\n", path);
00387 
00388   entry = find_entry(root, root_entries, argv[idx]);
00389 
00390   if (dos_dir) {
00391         list_dir(entry, sub_entries, FALSE);
00392         if (Lflag) free_blocks();
00393   } else if (dos_read)
00394         extract(entry);
00395   else {
00396         if (entry != NIL_DIR) {
00397                 fflush (stdout);
00398                 if (is_dir(entry))
00399                         fprintf (stderr, "%s: %s is a directory.\n", cmnd, path);
00400                 else
00401                         fprintf (stderr, "%s: %s already exists.\n", cmnd, argv[idx]);
00402                 exit(1);
00403         }
00404         add_path(NIL_PTR, TRUE);
00405 
00406         if (*path) make_file(find_entry(root, root_entries, path),
00407                           sub_entries, slash(argv[idx]));
00408         else
00409                 make_file(root, root_entries, argv[idx]);
00410   }
00411 
00412   (void) close(disk);
00413   fflush (stdout);
00414   exit(0);
00415   return(0);
00416 }
00417 
00418 
00419 /* General directory search routine.
00420  * 
00421  * dir:
00422  *      Points to one or more directory entries
00423  * entries:
00424  *      number of entries
00425  *      if entries == root_entries, dir points to the entire
00426  *      root directory. Otherwise it points to a single directory
00427  *      entry describing the directory to be searched.
00428  *      
00429  * function:
00430  *      FIND ... find pathname relative to directory dir.
00431  *      LABEL ... find first label entry in dir.
00432  *      ENTRY ... create a new empty entry.
00433  *      FALSE ... list directory
00434  *
00435  * pathname:
00436  *      name of the file to be found or directory to be listed.
00437  *      must be in upper case, pathname components must be
00438  *      separated by slashes, but can be longer than than 
00439  *      8+3 characters (The rest is ignored).
00440  */
00441 DIRECTORY *directory(dir, entries, function, pathname)
00442 DIRECTORY *dir;
00443 int entries;
00444 int function;
00445 register char *pathname;
00446 {
00447   register DIRECTORY *dir_ptr = dir;
00448   DIRECTORY *mem = NIL_DIR;
00449   unsigned short cl_no = dir->d_cluster;
00450   unsigned short type, last = 0;
00451   char file_name[14];
00452   char *name;
00453   int i = 0;
00454 
00455   if (function == FIND) {
00456         while (*pathname != '/' && *pathname != '.' && *pathname &&
00457                i < 8) {
00458                 file_name[i++] = *pathname++;
00459         }
00460         if (*pathname == '.') {
00461                 int j = 0;
00462                 file_name[i++] = *pathname++;
00463                 while (*pathname != '/' && *pathname != '.' && *pathname &&
00464                        j++ < 3) {
00465                         file_name[i++] = *pathname++;
00466                 }
00467         }
00468         while (*pathname != '/' && *pathname) pathname++;
00469         file_name[i] = '\0';
00470   }
00471   do {
00472         if (entries != root_entries) {
00473                 mem = dir_ptr = read_cluster(cl_no);
00474                 last = cl_no;
00475                 cl_no = next_cluster(cl_no);
00476         }
00477         for (i = 0; i < entries; i++, dir_ptr++) {
00478                 type = dir_ptr->d_name[0] & 0x0FF;
00479                 if (function == ENTRY) {
00480                         if (type == NOT_USED || type == ERASED) {
00481                                 if (!mem)
00482                                         mark = ROOTADDR + (long) i *(long) DIR_SIZE;
00483                                 else
00484                                         mark = clus_add(last) + (long) i *(long) DIR_SIZE;
00485                                 return dir_ptr;
00486                         }
00487                         continue;
00488                 }
00489                 if (type == NOT_USED) break;
00490                 if (dir_ptr->d_attribute & 0x08) {
00491                         if (function == LABEL) return dir_ptr;
00492                         continue;
00493                 }
00494                 if (type == DIR || type == ERASED || function == LABEL)
00495                         continue;
00496                 type = is_dir(dir_ptr);
00497                 name = make_name(dir_ptr,
00498                                  (function == FIND) ?  FALSE : type);
00499                 if (function == FIND) {
00500                         if (strcmp(file_name, name) != 0) continue;
00501                         if (!type) {
00502                                 if (dos_dir || *pathname) {
00503                                         fflush (stdout);
00504                                         fprintf (stderr, "%s: Not a directory: %s\n", cmnd, file_name);
00505                                         exit(1);
00506                                 }
00507                         } else if (*pathname == '\0' && dos_read) {
00508                                 fflush (stdout);
00509                                 fprintf (stderr, "%s: %s is a directory.\n", cmnd, path);
00510                                 exit(1);
00511                         }
00512                         if (*pathname) {
00513                                 dir_ptr = find_entry(dir_ptr,
00514                                          sub_entries, pathname + 1);
00515                         }
00516                         if (mem) {
00517                                 if (dir_ptr) {
00518                                         memcpy((char *)&save_entry, (char *)dir_ptr, DIR_SIZE);
00519                                         dir_ptr = &save_entry;
00520                                 }
00521                                 free( (void *) mem);
00522                         }
00523                         return dir_ptr;
00524                 } else {
00525                         if (function == FALSE) {
00526                                 show(dir_ptr, name);
00527                         } else if (type) {      /* Recursive */
00528                                 printf ( "Directory %s%s:\n", path, name);
00529                                 add_path(name, FALSE);
00530                                 list_dir(dir_ptr, sub_entries, FALSE);
00531                                 add_path(NIL_PTR, FALSE);
00532                         }
00533                 }
00534         }
00535         if (mem) free( (void *) mem);
00536   } while (cl_no != LAST_CLUSTER && mem);
00537 
00538   switch (function) {
00539       case FIND:
00540         if (dos_write && *pathname == '\0') return NIL_DIR;
00541         fflush (stdout);
00542         fprintf (stderr, "%s: Cannot find `%s'.\n", cmnd, file_name);
00543         exit(1);
00544       case LABEL:
00545         return NIL_DIR;
00546       case ENTRY:
00547         if (!mem) {
00548                 fflush (stdout);
00549                 fprintf (stderr, "%s: No entries left in root directory.\n", cmnd);
00550                 exit(1);
00551         }
00552         cl_no = free_cluster(TRUE);
00553         link_fat(last, cl_no);
00554         link_fat(cl_no, LAST_CLUSTER);
00555         disk_io(WRITE, clus_add(cl_no), null, cluster_size);
00556 
00557         return new_entry(dir, entries);
00558       case FALSE:
00559         if (Rflag) {
00560                 printf ("\n");
00561                 list_dir(dir, entries, TRUE);
00562         }
00563   }
00564   return NULL;
00565 }
00566 
00567 void extract(entry)
00568 register DIRECTORY *entry;
00569 {
00570   register unsigned short cl_no = entry->d_cluster;
00571   char buffer[MAX_CLUSTER_SIZE];
00572   int rest, i;
00573 
00574   if (entry->d_size == 0)       /* Empty file */
00575         return;
00576 
00577   do {
00578         disk_io(READ, clus_add(cl_no), buffer, cluster_size);
00579         rest = (entry->d_size > (long) cluster_size) ? cluster_size : (short) entry->d_size;
00580 
00581         if (Aflag) {
00582                 for (i = 0; i < rest; i ++) {
00583                         if (buffer [i] != '\r') putchar (buffer [i]);
00584                 }
00585                 if (ferror (stdout)) {
00586                         fprintf (stderr, "%s: cannot write to stdout: %s\n",
00587                                  cmnd, strerror (errno));
00588                         exit (1);
00589                 }
00590         } else {
00591                 if (fwrite (buffer, 1, rest, stdout) != rest) {
00592                         fprintf (stderr, "%s: cannot write to stdout: %s\n",
00593                                  cmnd, strerror (errno));
00594                         exit (1);
00595                 }
00596         }
00597         entry->d_size -= (long) rest;
00598         cl_no = next_cluster(cl_no);
00599         if (cl_no == BAD16) {
00600                 fflush (stdout);
00601                 fprintf (stderr, "%s: reserved cluster value %x encountered.\n",
00602                          cmnd, cl_no);
00603                 exit (1);
00604         }
00605   } while (entry->d_size && cl_no != LAST_CLUSTER);
00606 
00607   if (cl_no != LAST_CLUSTER)
00608         fprintf (stderr, "%s: Too many clusters allocated for file.\n", cmnd);
00609   else if (entry->d_size != 0)
00610         fprintf (stderr, "%s: Premature EOF: %ld bytes left.\n", cmnd,
00611                      entry->d_size);
00612 }
00613 
00614 
00615 /* Minimum of two long values
00616  */
00617 long lmin (a, b)
00618 long a, b;
00619 {
00620         if (a < b) return a;
00621         else return b;
00622 }
00623 
00624 
00625 void make_file(dir_ptr, entries, name)
00626 DIRECTORY *dir_ptr;
00627 int entries;
00628 char *name;
00629 {
00630   register DIRECTORY *entry = new_entry(dir_ptr, entries);
00631   register char *ptr;
00632   char buffer[MAX_CLUSTER_SIZE];
00633   unsigned short cl_no = 0;
00634   int i, r;
00635   long size = 0L;
00636   unsigned short first_cluster, last_cluster;
00637   long chunk;
00638 
00639   memset (&entry->d_name[0], ' ', 11);    /* clear entry */
00640   for (i = 0, ptr = name; i < 8 && *ptr != '.' && *ptr; i++)
00641         entry->d_name[i] = *ptr++;
00642   while (*ptr != '.' && *ptr) ptr++;
00643   if (*ptr == '.') ptr++;
00644   for (i = 0; i < 3 && *ptr != '.' && *ptr; i++) entry->d_ext[i] = *ptr++;
00645 
00646   for (i = 0; i < 10; i++) entry->d_reserved[i] = '\0';
00647   entry->d_attribute = '\0';
00648 
00649   entry->d_cluster = 0;
00650 
00651   while (free_range (&first_cluster, &last_cluster)) {
00652         do {
00653                 unsigned short  nr_clus;
00654 
00655                 chunk = lmin ((long) (last_cluster - first_cluster + 1) *
00656                                           cluster_size,
00657                               (long) MAX_CLUSTER_SIZE);
00658                 r = fill(buffer, chunk);
00659                 if (r == 0) goto done;
00660                 nr_clus = (r + cluster_size - 1) / cluster_size;
00661                 disk_io(WRITE, clus_add(first_cluster), buffer, r);
00662 
00663                 for (i = 0; i < nr_clus; i ++) {
00664                         if (entry->d_cluster == 0)
00665                                 cl_no = entry->d_cluster = first_cluster;
00666                         else {
00667                                 link_fat(cl_no, first_cluster);
00668                                 cl_no = first_cluster;
00669                         }
00670                         first_cluster ++;
00671                 }
00672 
00673                 size += r;
00674         } while (first_cluster <= last_cluster);
00675   }
00676   fprintf (stderr, "%s: disk full. File truncated\n", cmnd);
00677 done:
00678   if (entry->d_cluster != 0) link_fat(cl_no, LAST_CLUSTER);
00679   entry->d_size = size;
00680   fill_date(entry);
00681   disk_io(WRITE, mark, entry, DIR_SIZE);
00682 
00683   if (fat_dirty) flush_fat ();
00684 
00685 }
00686 
00687 
00688 #define SEC_MIN 60L
00689 #define SEC_HOUR        (60L * SEC_MIN)
00690 #define SEC_DAY (24L * SEC_HOUR)
00691 #define SEC_YEAR        (365L * SEC_DAY)
00692 #define SEC_LYEAR       (366L * SEC_DAY)
00693 
00694 unsigned short mon_len[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
00695 
00696 void fill_date(entry)
00697 DIRECTORY *entry;
00698 {
00699   register long cur_time = time((long *) 0) - DOS_TIME;
00700   unsigned short year = 0, month = 1, day, hour, minutes, seconds;
00701   int i;
00702   long tmp;
00703 
00704   if (cur_time < 0)             /* Date not set on booting ... */
00705         cur_time = 0;
00706   for (;;) {
00707         tmp = (year % 4 == 0) ? SEC_LYEAR : SEC_YEAR;
00708         if (cur_time < tmp) break;
00709         cur_time -= tmp;
00710         year++;
00711   }
00712 
00713   day = (unsigned short) (cur_time / SEC_DAY);
00714   cur_time -= (long) day *SEC_DAY;
00715 
00716   hour = (unsigned short) (cur_time / SEC_HOUR);
00717   cur_time -= (long) hour *SEC_HOUR;
00718 
00719   minutes = (unsigned short) (cur_time / SEC_MIN);
00720   cur_time -= (long) minutes *SEC_MIN;
00721 
00722   seconds = (unsigned short) cur_time;
00723 
00724   mon_len[1] = (year % 4 == 0) ? 29 : 28;
00725   i = 0;
00726   while (day >= mon_len[i]) {
00727         month++;
00728         day -= mon_len[i++];
00729   }
00730   day++;
00731 
00732   entry->d_date = (year << 9) | (month << 5) | day;
00733   entry->d_time = (hour << 11) | (minutes << 5) | seconds;
00734 }
00735 
00736 char *make_name(dir_ptr, dir_fl)
00737 register DIRECTORY *dir_ptr;
00738 short dir_fl;
00739 {
00740   static char name_buf[14];
00741   register char *ptr = name_buf;
00742   short i;
00743 
00744   for (i = 0; i < 8; i++) *ptr++ = dir_ptr->d_name[i];
00745 
00746   while (*--ptr == ' ');
00747   assert (ptr >= name_buf);
00748 
00749   ptr++;
00750   if (dir_ptr->d_ext[0] != ' ') {
00751         *ptr++ = '.';
00752         for (i = 0; i < 3; i++) *ptr++ = dir_ptr->d_ext[i];
00753         while (*--ptr == ' ');
00754         ptr++;
00755   }
00756   if (dir_fl) *ptr++ = '/';
00757   *ptr = '\0';
00758 
00759   return name_buf;
00760 }
00761 
00762 
00763 int fill(buffer, size)
00764 register char *buffer;
00765 size_t  size;
00766 {
00767   static BOOL nl_mark = FALSE;
00768   char *last = &buffer[size];
00769   char *begin = buffer;
00770   register int c;
00771 
00772   while (buffer < last) {
00773         if (nl_mark) {
00774                 *buffer ++ = '\n';
00775                 nl_mark = FALSE;
00776         } else {
00777                 c = getchar();
00778                 if (c == EOF) break;
00779                 if (Aflag && c == '\n') {
00780                         *buffer ++ = '\r';
00781                         nl_mark = TRUE;
00782                 } else {
00783                         *buffer++ = c;
00784                 }
00785         }
00786   }
00787 
00788   return (buffer - begin);
00789 }
00790 
00791 #define HOUR    0xF800          /* Upper 5 bits */
00792 #define MIN     0x07E0          /* Middle 6 bits */
00793 #define YEAR    0xFE00          /* Upper 7 bits */
00794 #define MONTH   0x01E0          /* Mid 4 bits */
00795 #define DAY     0x01F           /* Lowest 5 bits */
00796 
00797 char *month[] = {
00798          "Jan", "Feb", "Mar", "Apr", "May", "Jun",
00799          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
00800 };
00801 
00802 void xmodes(mode)
00803 int mode;
00804 {
00805   printf ( "\t%c%c%c%c%c", (mode & SUB_DIR) ? 'd' : '-',
00806              (mode & 02) ? 'h' : '-', (mode & 04) ? 's' : '-',
00807              (mode & 01) ? '-' : 'w', (mode & 0x20) ? 'a' : '-');
00808 }
00809 
00810 void show(dir_ptr, name)
00811 DIRECTORY *dir_ptr;
00812 char *name;
00813 {
00814   register unsigned short e_date = dir_ptr->d_date;
00815   register unsigned short e_time = dir_ptr->d_time;
00816   unsigned short next;
00817   char bname[20];
00818   short i = 0;
00819 
00820   while (*name && *name != '/') bname[i++] = *name++;
00821   bname[i] = '\0';
00822   if (!Lflag) {
00823         printf ( "%s\n", bname);
00824         return;
00825   }
00826   xmodes( (int) dir_ptr->d_attribute);
00827   printf ( "\t%s%s", bname, strlen(bname) < 8 ? "\t\t" : "\t");
00828   i = 1;
00829   if (is_dir(dir_ptr)) {
00830         next = dir_ptr->d_cluster;
00831         while ((next = next_cluster(next)) != LAST_CLUSTER) i++;
00832         printf ("%8ld", (long) i * (long) cluster_size);
00833   } else
00834         printf ("%8ld", dir_ptr->d_size);
00835   printf (" %02d:%02d %2d %s %d\n", ((e_time & HOUR) >> 11),
00836              ((e_time & MIN) >> 5), (e_date & DAY),
00837    month[((e_date & MONTH) >> 5) - 1], ((e_date & YEAR) >> 9) + 1980);
00838 }
00839 
00840 void free_blocks()
00841 {
00842   register unsigned short cl_no;
00843   long nr_free = 0;
00844   long nr_bad = 0;
00845 
00846   for (cl_no = 2; cl_no < total_clusters; cl_no++) {
00847         switch (next_cluster(cl_no)) {
00848             case FREE:  nr_free++;      break;
00849             case BAD16: nr_bad++;       break;
00850         }
00851   }
00852 
00853   printf ("Free space: %ld bytes.\n", nr_free * (long) cluster_size);
00854   if (nr_bad != 0)
00855         printf ("Bad sectors: %ld bytes.\n", nr_bad * (long) cluster_size);
00856 }
00857 
00858 
00859 DIRECTORY *read_cluster(cluster)
00860 register unsigned int cluster;
00861 {
00862   register DIRECTORY *sub_dir;
00863 
00864   if ((sub_dir = malloc(cluster_size)) == NULL) {
00865         fprintf (stderr, "%s: Cannot set break!\n", cmnd);
00866         exit(1);
00867   }
00868   disk_io(READ, clus_add(cluster), sub_dir, cluster_size);
00869 
00870   return sub_dir;
00871 }
00872 
00873 static unsigned short cl_index = 2;
00874 
00875 /* find a range of consecutive free clusters. Return TRUE if found
00876  * and return the first and last cluster in the |*first| and |*last|.
00877  * If no free clusters are left, return FALSE.
00878  *
00879  * Warning: Assumes that all of the range is used before the next call
00880  *      to free_range or free_cluster.
00881  */
00882 BOOL free_range (first, last)
00883 unsigned short *first, *last;
00884 {
00885   while (cl_index < total_clusters && next_cluster(cl_index) != FREE)
00886         cl_index++;
00887   if (cl_index >= total_clusters) return FALSE;
00888   *first = cl_index;
00889   while (cl_index < total_clusters && next_cluster(cl_index) == FREE)
00890         cl_index++;
00891   *last = cl_index - 1;
00892   return TRUE;
00893 }
00894 
00895 
00896 /* find a free cluster.
00897  * Return the number of the free cluster or a number > |total_clusters|
00898  * if none is found.
00899  * If |leave_fl| is TRUE, the the program will be terminated if 
00900  * no free cluster can be found
00901  *
00902  * Warning: Assumes that the cluster is used before the next call
00903  *      to free_range or free_cluster.
00904  */
00905 unsigned short free_cluster(leave_fl)
00906 BOOL leave_fl;
00907 {
00908   while (cl_index < total_clusters && next_cluster(cl_index) != FREE)
00909         cl_index++;
00910 
00911   if (leave_fl && cl_index >= total_clusters) {
00912         fprintf (stderr, "%s: Diskette full. File not added.\n", cmnd);
00913         exit(1);
00914   }
00915   return cl_index++;
00916 }
00917 
00918 
00919 /* read a portion of the fat containing |cl_no| into the cache
00920  */
00921 void read_fat (cl_no) 
00922   unsigned int cl_no;
00923 {
00924 
00925   if (!cooked_fat) {
00926         /* Read the fat for the first time. We have to allocate all the
00927          * buffers
00928          */
00929         if (fat_16) {
00930                 /* FAT consists of little endian shorts. Easy to convert
00931                  */
00932                 if ((cooked_fat = malloc (fat_size)) == NULL) {
00933                         /* Oops, FAT doesn't fit into memory, just read
00934                          * a chunk
00935                          */
00936                         if ((cooked_fat = malloc (COOKED_SIZE)) == NULL) {
00937                                 fprintf (stderr, "%s: not enough memory for FAT cache. Use chmem\n",
00938                                          cmnd);
00939                                 exit (1);
00940                         }
00941                         cache_size = COOKED_SIZE / 2;
00942                 } else {
00943                         cache_size = fat_size / 2;
00944                 }
00945         } else {
00946                 /* 12 bit FAT. Difficult encoding, but small. Keep
00947                  * both raw FAT and cooked version in memory.
00948                  */
00949                 if ((cooked_fat = malloc (total_clusters * sizeof (short))) == NULL ||
00950                     (raw_fat = malloc (fat_size)) == NULL) {
00951                         fprintf (stderr, "%s: not enough memory for FAT cache. Use chmem\n",
00952                                  cmnd);
00953                         exit (1);
00954                 }
00955                 cache_size = total_clusters;
00956         }
00957   }
00958   fat_low = cl_no / cache_size * cache_size;
00959   fat_high = fat_low + cache_size - 1;
00960 
00961   if (!fat_16) {
00962         unsigned short  *cp;
00963         unsigned char   *rp;
00964         unsigned short  i;
00965 
00966         disk_io (READ, FAT_START, raw_fat, fat_size);
00967         for (rp = raw_fat, cp = cooked_fat, i = 0;
00968              i < cache_size;
00969              rp += 3, i += 2) {
00970                 *cp = *rp + ((*(rp + 1) & 0x0f) << 8);
00971                 if (*cp == BAD) *cp = BAD16;
00972                 else if (*cp == LAST_CLUSTER12) *cp = LAST_CLUSTER;
00973                 cp ++;
00974                 *cp = ((*(rp + 1) & 0xf0) >> 4) + (*(rp + 2) << 4);
00975                 if (*cp == BAD) *cp = BAD16;
00976                 else if (*cp == LAST_CLUSTER12) *cp = LAST_CLUSTER;
00977                 cp ++;
00978         }
00979   } else {
00980 
00981         assert (sizeof (short) == 2);
00982         assert (CHAR_BIT == 8);         /* just in case */
00983 
00984         disk_io (READ, FAT_START + fat_low * 2, (void *)cooked_fat, cache_size * 2);
00985         if (big_endian) {
00986                 unsigned short  *cp;
00987                 unsigned char   *rp;
00988                 unsigned short  i;
00989 
00990                 for (i = 0, rp = (unsigned char *)cooked_fat /* sic */, cp = cooked_fat;
00991                      i < cache_size;
00992                      rp += 2, cp ++, i ++) {
00993                         *cp = c2u2 (rp);
00994                 }
00995         }
00996   }
00997 }
00998 
00999 
01000 /* flush the fat cache out to disk
01001  */
01002 void flush_fat ()
01003 {
01004   if (fat_16) {
01005         if (big_endian) {
01006                 unsigned short  *cp;
01007                 unsigned char   *rp;
01008                 unsigned short  i;
01009 
01010                 for (i = 0, rp = (unsigned char *)cooked_fat /* sic */, cp = cooked_fat;
01011                      i < cache_size;
01012                      rp += 2, cp ++, i ++) {
01013                         *rp = *cp;
01014                         *(rp + 1) = *cp >> 8;
01015                 }
01016         }
01017         disk_io (WRITE, FAT_START + fat_low * 2, (void *)cooked_fat, cache_size * 2);
01018         disk_io (WRITE, FAT_START + fat_size + fat_low * 2, (void *)cooked_fat, cache_size * 2);
01019   } else {
01020         unsigned short  *cp;
01021         unsigned char   *rp;
01022         unsigned short  i;
01023 
01024         for (rp = raw_fat, cp = cooked_fat, i = 0;
01025              i < cache_size;
01026              rp += 3, cp += 2, i += 2) {
01027                 *rp = *cp;
01028                 *(rp + 1) = ((*cp & 0xf00) >> 8) |
01029                             ((*(cp + 1) & 0x00f) << 4);
01030                 *(rp + 2) = ((*(cp + 1) & 0xff0) >> 4);
01031         }
01032         disk_io (WRITE, FAT_START, raw_fat, fat_size);
01033         disk_io (WRITE, FAT_START + fat_size, raw_fat, fat_size);
01034   }
01035 }
01036 
01037 
01038 /* make cl_2 the successor of cl_1
01039  */
01040 void link_fat(cl_1, cl_2)
01041 unsigned int cl_1;
01042 unsigned int cl_2;
01043 {
01044   if (cl_1 < fat_low || cl_1 > fat_high) {
01045         if (fat_dirty) flush_fat ();
01046         read_fat (cl_1);
01047   }
01048   cooked_fat [cl_1 - fat_low] = cl_2;
01049   fat_dirty = TRUE;
01050 }
01051 
01052 
01053 unsigned short next_cluster(cl_no)
01054 register unsigned int cl_no;
01055 {
01056   if (cl_no < fat_low || cl_no > fat_high) {
01057         if (fat_dirty) flush_fat ();
01058         read_fat (cl_no);
01059   }
01060   return cooked_fat [cl_no - fat_low];
01061 }
01062 
01063 char *slash(str)
01064 register char *str;
01065 {
01066   register char *result = str;
01067 
01068   while (*str)
01069         if (*str++ == '/') result = str;
01070 
01071   return result;
01072 }
01073 
01074 void add_path(file, slash_fl)
01075 char *file;
01076 BOOL slash_fl;
01077 {
01078   register char *ptr = path;
01079 
01080   while (*ptr) ptr++;
01081 
01082   if (file == NIL_PTR) {
01083         if (ptr != path) ptr--;
01084         if (ptr != path) do {
01085                         ptr--;
01086                 } while (*ptr != '/' && ptr != path);
01087         if (ptr != path && !slash_fl) *ptr++ = '/';
01088         *ptr = '\0';
01089   } else
01090         strcpy (ptr, file);
01091 }
01092 
01093 
01094 void disk_io(op, seek, address, bytes)
01095 register BOOL op;
01096 unsigned long seek;
01097 void *address;
01098 register unsigned bytes;
01099 {
01100   unsigned int r;
01101 
01102   if (lseek(disk, seek, SEEK_SET) < 0L) {
01103         fflush (stdout);
01104         fprintf (stderr, "%s: Bad lseek: %s\n", cmnd, strerror (errno));
01105         exit(1);
01106   }
01107   if (op == READ)
01108         r = read(disk, (char *) address, bytes);
01109   else {
01110         r = write(disk, (char *) address, bytes);
01111   }
01112 
01113   if (r != bytes) {
01114         fprintf (stderr, "%s: read error: %s\n", cmnd, strerror (errno));
01115         exit (1);
01116   }
01117 }
01118 
01119 char dosread_c_rcs_id [] = 
01120         "$Id: dosread.c,v 1.2 2005/07/13 10:02:14 beng Exp $";

Generated on Fri Apr 14 22:56:57 2006 for minix by  doxygen 1.4.6