home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / pc / progrmng / dosfiles.lzh / dosfiles / msdos.c < prev    next >
C/C++ Source or Header  |  1990-11-29  |  13KB  |  486 lines

  1. /*
  2. **    M S - D O S   d i s k e t t e   f i l e s y s t e m
  3. */
  4.  
  5. /*
  6. **    Current limitations:
  7. **    Only one file may be "open" at a time - either for read or write
  8. **    There is no provision if the diskette becomes full when writing
  9. */
  10.  
  11. #include    "io.h"
  12. #include    "msdos.h"
  13.  
  14.     /* Macros for converting two bytes to word and vice versa */
  15. #define    GET_WORD(i,a)        ((unsigned int)(a[i] | (a[(i)+1]<<8)))
  16. #define    SET_WORD(i,a,v)        (a[i]=(v), a[(i)+1]=(v)>>8)
  17.  
  18.     /* Same for converting four bytes to long and vice versa */
  19. #define    C(a)            ((char *)&(a))
  20. #define    L(i,a)            ((unsigned long)(C(a)[i]))
  21.  
  22. #define    GET_LONG(l)        (L(0,l) |        (L(1,l)<<8) |\
  23.                 (L(2,l)<<16) |        (L(3,l)<<24))
  24. #define    SET_LONG(l,v)        ((C(l)[0]=(v)),        (C(l)[1]=(v)>>8),\
  25.                 (C(l)[2]=(v)>>16),    (C(l)[3]=(v)>>24))
  26.  
  27.     /* Error marking routines */
  28. #define    READ_ERROR(sector,status)    general_error("read",sector,status)
  29. #define    WRITE_ERROR(sector,status)    general_error("write",sector,status)
  30.  
  31.     /* Some markers */
  32. unsigned int sector_size=SECTORSIZE,sectors_per_fat,reserved_sectors,file_area;
  33. unsigned int fats_per_disk,file_slots,sectors_per_cluster,hidden_sectors;
  34.  
  35.     /* Single directory sector entry */
  36. unsigned int dirsector_number=-1;
  37. unsigned char dir_sector[SECTORSIZE];
  38.  
  39.     /* FAT buffer (largest floppy FAT is 9 sectors, on a 1.44M) */
  40. unsigned char fat[9*SECTORSIZE];
  41.  
  42.     /* Cluster buffer (largest floppy cluster is 2 sectors) */
  43. unsigned char cluster[2*SECTORSIZE];
  44.  
  45.     /* Some function prototypes */
  46. void read_cluster(unsigned int number);
  47. void write_cluster(unsigned int number);
  48.  
  49. void write_fat();
  50. unsigned int get_fat_entry(unsigned int cluster);
  51. void set_fat_entry(unsigned int cluster,unsigned int value);
  52.  
  53. void general_error(char *dowhat, unsigned int sector,unsigned int status);
  54.  
  55. /*
  56. **    Read sector zero, set values appropriately; read FAT into the buffer
  57. */
  58. void disk_open()    {
  59.     register int i,j;
  60.  
  61.     if(i=read_sector(0,cluster)) READ_ERROR(0,i);
  62.  
  63.     sector_size = GET_WORD(0x0B,cluster);
  64.     sectors_per_cluster = cluster[0x0D];
  65.     reserved_sectors = GET_WORD(0x0E,cluster);
  66.     fats_per_disk = cluster[0x10];
  67.     file_slots = GET_WORD(0x11,cluster);
  68.     sectors_per_fat = GET_WORD(0x16,cluster);
  69.     sectors_per_track = GET_WORD(0x18,cluster);
  70.     number_of_heads = GET_WORD(0x1A,cluster);
  71.     hidden_sectors = GET_WORD(0x1C,cluster);
  72.     file_area = reserved_sectors + hidden_sectors +
  73.         sectors_per_fat*fats_per_disk +
  74.         (file_slots*sizeof(directory))/sector_size;
  75.  
  76.     Db( printf(
  77.         "\ndrive %d\n"
  78.         "media OEM signature  \"%.8s\"\n"
  79.         "bytes/sector=%u\n"
  80.         "sectors/cluster=%u\n"
  81.         "reserved=%u\n"
  82.         "FATs=%u\n"
  83.         "root directory file slots=%u\n"
  84.         "total sectors=%u\n"
  85.         "media descriptor=%xh\n"
  86.         "sectors/FAT=%u\n"
  87.         "sectors/track=%u\n"
  88.         "heads=%u\n"
  89.         "hidden sectors=%u\n"
  90.         "boot signature=%02X%02X\n"
  91.         "file area begins at sector %d\n\n",
  92.         drive_number,
  93.         cluster+3,
  94.         sector_size,
  95.         sectors_per_cluster,
  96.         reserved_sectors,
  97.         fats_per_disk,
  98.         file_slots,
  99.         GET_WORD(0x13,cluster),
  100.         cluster[0x15],
  101.         sectors_per_fat,
  102.         sectors_per_track,
  103.         number_of_heads,
  104.         hidden_sectors,
  105.         cluster[sector_size-2],cluster[sector_size-1],
  106.         file_area);
  107.     )
  108.  
  109.         /* Can't read FAT as multiple sectors; FAT can span tracks */
  110.     for(j=0; j<sectors_per_fat; j++)
  111.         if(i=read_sector(reserved_sectors+j,fat+j*sector_size))
  112.             READ_ERROR(reserved_sectors,i);
  113. }
  114.  
  115.  
  116. /*
  117. **    Search root directory for given filename (or free entry if NULL given),
  118. **    return pointer to it's entry or NULL if no find
  119. */
  120. directory *find_dir(filename)
  121. char *filename;    {
  122.     register int i,j;
  123.     directory *dp = (directory *)dir_sector+sector_size;
  124.  
  125.     dirsector_number =    reserved_sectors + hidden_sectors +
  126.                 sectors_per_fat * fats_per_disk - 1;
  127.  
  128.     for(j=0; j<file_slots; j++, dp++)    {
  129.         if(dp >= (directory *)dir_sector+sector_size)    {
  130.             if(i=read_sector(++dirsector_number,dir_sector))
  131.                 READ_ERROR(dirsector_number,i);
  132.             dp = (directory *)dir_sector;
  133.         }
  134.         if(filename)    {
  135.             /* Name must match and must not be volabel, dir etc. */
  136.             if(!(strncmp(dp->filename,filename,8+3) ||
  137.                 (dp->attribute & ATTR_SPECIAL)) )
  138.                 return(dp);
  139.         } else    {
  140.             /* Must be either unused or deleted entry */
  141.             if(!(i=dp->filename[0]) || i==0xE5)
  142.                 return(dp);
  143.         }
  144.     }
  145.  
  146.     return(NULL);
  147. }
  148.  
  149.  
  150. /*
  151. **    "Open" a file for read, return true if file cannot be read (zero length)
  152. */
  153. static unsigned int current_cluster;
  154. static unsigned long int bytes_in_file;
  155. static unsigned char *next_char;
  156.  
  157. int file_open(dir_item)
  158. register directory *dir_item;    {
  159.     current_cluster = dir_item->cluster;
  160.     bytes_in_file = GET_LONG(dir_item->filesize);
  161.     next_char = cluster+sector_size*sectors_per_cluster;
  162.  
  163.     Db( printf("Opened file, size %ld bytes\n",bytes_in_file); )
  164.  
  165.     return(!current_cluster);
  166. }
  167.  
  168.  
  169. /*
  170. **    Get next character from file previously opened with file_open
  171. */
  172. int file_getc()    {
  173.     if(!bytes_in_file) return(EOF);
  174.     if(next_char >= cluster+sector_size*sectors_per_cluster)    {
  175.         next_char = cluster;
  176.         read_cluster(current_cluster);
  177.         current_cluster = get_fat_entry(current_cluster);
  178.     }
  179.     bytes_in_file--;
  180.     return(*next_char++);
  181. }
  182.  
  183.  
  184. /*
  185. **    "Open" filename for writing, return pointer to directory item if
  186. **    successful; return NULL if old file can't be overwritten or there
  187. **    is no room in the root directory for a new file
  188. */
  189. directory *file_create(filename)
  190. char *filename;    {
  191.     register int i;
  192.     directory *dp;
  193.  
  194.         /* If file already exists, truncate it to zero length */
  195.     if(dp=find_dir(filename))    {
  196.             /* Can't truncate if file is readonly */
  197.         if(dp->attribute & ATTR_READONLY) return(NULL);
  198.  
  199.             /* Remove FAT chain */
  200.         if(current_cluster = dp->cluster)
  201.             do    {
  202.                 current_cluster =
  203.                     get_fat_entry(i=current_cluster);
  204.                 set_fat_entry(i,0);
  205.                 Db( printf("Freeing cluster %03x\n",i); )
  206.             } while(current_cluster < 0xFF8);
  207.             /* Then rewrite FAT */
  208.         write_fat();
  209.     } else
  210.         /* Allocate a unused or deleted directory entry, copy name */
  211.         if(dp=find_dir(NULL))
  212.             for(i=0; i<8+3; i++) dp->filename[i]=filename[i];
  213.         else
  214.             return(NULL);
  215.  
  216.         /* Then clear directory entry, filesize and cluster ptr */
  217.     dp->filesize = 0L;
  218.     dp->cluster = 0;
  219.     dp->attribute = ATTR_ARCHIVE;    /* File archive bit set (modified) */
  220.     dp->date = 0x0000;    /* You may insert anything appropriate here */
  221.     dp->time = 0x0000;    /* These values mean "no date" for MS-DOS */
  222.     if(i=write_sector(dirsector_number,dir_sector))
  223.         WRITE_ERROR(dirsector_number,i);
  224.  
  225.         /* Clear indicators for file_putc() */
  226.     current_cluster = 0;
  227.     bytes_in_file = 0L;
  228.     next_char = cluster+sector_size*sectors_per_cluster;
  229.  
  230.     return(dp);
  231. }
  232.  
  233.  
  234. /*
  235. **    Put next character to the file previously opened with file_create
  236. */
  237. int file_putc(dp,c)
  238. directory *dp;
  239. int c;    {
  240.     register int i;
  241.  
  242.         /* Note that there is no provision for running out of FAT */
  243.     if(next_char >= cluster+sector_size*sectors_per_cluster)    {
  244.         if(current_cluster)
  245.             write_cluster(current_cluster);
  246.         for(i=current_cluster+1; get_fat_entry(i); i++);
  247.  
  248.         if(current_cluster)
  249.             set_fat_entry(current_cluster,i);
  250.         else
  251.             dp->cluster = i;
  252.  
  253.         Db( printf("Allocated cluster %03x\n",i); )
  254.         current_cluster = i;
  255.         next_char = cluster;
  256.     }
  257.     *next_char++ = c;
  258.     bytes_in_file++;
  259.     return(0);
  260. }
  261.  
  262.  
  263. /*
  264. **    Close the file written to by file_putc
  265. **    Rewrite directory item and FAT, write disk buffer
  266. */
  267. void file_close(dp)
  268. directory *dp;    {
  269.     register int i;
  270.  
  271.     Db( printf("Closing file, size %ld bytes\n",bytes_in_file); )
  272.  
  273.         /* Do nothing if file hasn't actually been written to */
  274.     if(!current_cluster) return;
  275.  
  276.         /* Update file size */
  277.     SET_LONG(dp->filesize,bytes_in_file);
  278.  
  279.     Db( printf("Closing file, size %ld bytes\n",dp->filesize); )
  280.  
  281.     if(i=write_sector(dirsector_number,dir_sector))
  282.         WRITE_ERROR(dirsector_number,i);
  283.  
  284.         /* Write cluster buffer to disk */
  285.     write_cluster(current_cluster);
  286.         /* Mark as end-of-file to FAT */
  287.     set_fat_entry(current_cluster,0xFFF);
  288.         /* Rewrite FAT to disk */
  289.     write_fat();
  290. }
  291.  
  292.  
  293. /*
  294. **    Erase disk's logical structure, create standard MS-DOS disk;
  295. **    this basically does a logical format to the target disk -- nothing
  296. **    is done physically.  THIS WILL TOTALLY ERASE THE WHOLE DISKETTE !
  297. **    Idea is that one does not have to do disk_open once this is done
  298. **
  299. **    Note: if Atari ST 720k diskette support is wanted, the media
  300. **    descriptor byte for type 2 should be set to 0xF7.
  301. */
  302.  
  303. static struct    {
  304.     unsigned char tracks,sectors,heads,media,fat,files,cluster;
  305. } disk_data[4] = {
  306. /*    tracks    sectors    heads    media    FAT    files    cluster    */
  307.     { 40,    9,    2,    0xFD,    2,    7,    2 },    /* 360k */
  308.     { 80,    15,    2,    0xF9,    7,    14,    1 },    /* 1.2M */
  309.     { 80,    9,    2,    0xF9,    3,    7,    2 },    /* 720k */
  310.     { 80,    18,    2,    0xF0,    9,    14,    1 },    /* 1.44M */
  311. };
  312. static unsigned char volume_label[8+3]="My Diskette";
  313.  
  314. void disk_clear(disktype)
  315. int disktype;    {
  316.     register int i;
  317.     directory *dp;
  318.  
  319.         /* First, make logical sector 0 */
  320.     for(i=0; i<SECTORSIZE; i++) cluster[i]='\0';
  321.  
  322.     sectors_per_track = disk_data[disktype].sectors;
  323.     number_of_heads = disk_data[disktype].heads;
  324.  
  325.     sector_size = SECTORSIZE;
  326.     sectors_per_fat = disk_data[disktype].fat;
  327.     reserved_sectors = 1;
  328.     fats_per_disk = 2;
  329.     file_slots = sector_size*disk_data[disktype].files/sizeof(directory);
  330.     sectors_per_cluster = disk_data[disktype].cluster;
  331.     hidden_sectors = 0;
  332.  
  333.     file_area = reserved_sectors + hidden_sectors +
  334.         sectors_per_fat*fats_per_disk +
  335.         disk_data[disktype].files;
  336.  
  337.         /*
  338.         ** Seems Messy-Dos can't read a 3.5" diskette unless these
  339.         ** are here (it has no problems reading 5.25" diskettes !)
  340.         ** they actually are the instructions JMP 003E and NOP for
  341.         ** the iAPX86 family microprocessors - talk about a kludge!
  342.         */
  343.     cluster[0]=0xEB; cluster[1]=0x3C; cluster[2]=0x90;
  344.  
  345.         /* Diskette creator (OEM) signature - that's me, folks! */
  346.     for(i=0; i<8; i++) cluster[i+3]="Mycroft*"[i];
  347.  
  348.     SET_WORD(0x0B,cluster,sector_size);
  349.     cluster[0x0D] = sectors_per_cluster;
  350.     SET_WORD(0x0E,cluster,reserved_sectors);
  351.     cluster[0x10] = fats_per_disk;
  352.     SET_WORD(0x11,cluster,file_slots);
  353.     SET_WORD(0x13,cluster,
  354.         disk_data[disktype].tracks*sectors_per_track*number_of_heads);
  355.     cluster[0x15] = disk_data[disktype].media;
  356.     SET_WORD(0x16,cluster,sectors_per_fat);
  357.     SET_WORD(0x18,cluster,sectors_per_track);
  358.     SET_WORD(0x1A,cluster,number_of_heads);
  359.     SET_WORD(0x1C,cluster,hidden_sectors);
  360.  
  361.         /*
  362.         ** This volume label is actually extraneous, since we haven't
  363.         ** inserted the MS-DOS 4.0 signature byte 0x29 at offset 0x26
  364.         ** (as we'd have to figure a volume serial number for the disk)
  365.         */        
  366.     for(i=0; i<8+3; i++) cluster[i+0x2B]=volume_label[i];
  367.  
  368.     if(i=write_sector(0,cluster))
  369.         WRITE_ERROR(0,i);
  370.  
  371.         /* Then create empty FAT */
  372.     fat[0] = disk_data[disktype].media;
  373.     fat[1] = 0xFF;
  374.     fat[2] = 0xFF;
  375.     for(i=3; i<sector_size*sectors_per_fat; i++) fat[i]=0;
  376.     write_fat();
  377.  
  378.         /* Lastly, clear directory area (except for volume label) */
  379.     for(i<0; i<SECTORSIZE; i++) dir_sector[i]='\0';
  380.     dp = (directory *)dir_sector;
  381.     for(i=0; i<8+3; i++) dp->filename[i]=volume_label[i];
  382.     dp->attribute = ATTR_VOLABEL;
  383.  
  384.     dirsector_number =    reserved_sectors + hidden_sectors +
  385.                 sectors_per_fat * fats_per_disk;
  386.  
  387.     if(i=write_sector(dirsector_number,dir_sector))
  388.         WRITE_ERROR(dirsector_number,i);
  389.  
  390.     for(i=0; i<11; i++) dp->filename[i]='\0';
  391.     dp->attribute = 0;
  392.  
  393.     while(++dirsector_number < disk_data[disktype].files)
  394.         if(i=write_sector(dirsector_number,dir_sector))
  395.             WRITE_ERROR(dirsector_number,i);
  396.  
  397. }
  398.  
  399.  
  400. /*
  401. **    Get the requested cluster to the cluster buffer
  402. */
  403. static void read_cluster(number)
  404. register unsigned int number;    {
  405.     register int i,j;
  406.     unsigned char *cp=cluster;
  407.  
  408.     Db( printf("Read cluster %03x\n",number); )
  409.  
  410.     number=CLUSTER_TO_SECTOR(number);
  411.     for(j=0; j<sectors_per_cluster; j++, number++, cp+=sector_size)
  412.         if(i=read_sector(number,cp))
  413.             READ_ERROR(number,i);
  414. }
  415.  
  416.  
  417. /*
  418. **    Put the requested cluster from the cluster buffer
  419. */
  420. static void write_cluster(number)
  421. register unsigned int number;    {
  422.     register int i,j;
  423.     unsigned char *cp=cluster;
  424.  
  425.     Db( printf("Wrote cluster %03x\n",number); )
  426.  
  427.     number=CLUSTER_TO_SECTOR(number);
  428.     for(j=0; j<sectors_per_cluster; j++, number++, cp+=sector_size)
  429.         if(i=write_sector(number,cp))
  430.             WRITE_ERROR(number,i);
  431. }
  432.  
  433.  
  434. /*
  435. **    Write modified FAT back to disk (both copies)
  436. */
  437. static void write_fat()    {
  438.     register int i,j;
  439.  
  440.     for(j=0; j<sectors_per_fat; j++)    {
  441.         if(i=write_sector(reserved_sectors+j,fat+j*sector_size))
  442.             WRITE_ERROR(reserved_sectors+j,i);
  443.         if(i=write_sector(reserved_sectors+sectors_per_fat+j,
  444.             fat+j*sector_size))
  445.             WRITE_ERROR(reserved_sectors+sectors_per_fat+j,i);
  446.     }
  447. }
  448.  
  449.  
  450. /*
  451. **    Get FAT entry for specified cluster
  452. */
  453. static unsigned int get_fat_entry(cluster)
  454. unsigned int cluster;    {
  455.     register unsigned int i=GET_WORD(3*cluster/2,fat);
  456.  
  457.     return (cluster&1)? (i>>4): (i&0x0FFF);
  458. }
  459.  
  460.  
  461. /*
  462. **    Set FAT entry for specified cluster
  463. */
  464. static void set_fat_entry(cluster,value)
  465. unsigned int cluster,value;    {
  466.     register unsigned int i=GET_WORD(3*cluster/2,fat);
  467.  
  468.     i = (cluster&1)?
  469.         ((value<<4)      |    (i & 0x000F)):
  470.         ((value & 0x0FFF) |    (i & 0xF000));
  471.  
  472.     SET_WORD(3*cluster/2,fat,i);
  473. }
  474.  
  475.  
  476. /*
  477. **    Report error and terminate
  478. */
  479. static void general_error(dowhat,sector,status)
  480. char *dowhat;
  481. unsigned int sector,status;    {
  482.     printf("Can't %s logical sector %d, status=%02x\n",
  483.         dowhat,sector,status);
  484.     exit(1);
  485. }
  486.