diff options
Diffstat (limited to 'init.c')
-rw-r--r-- | init.c | 546 |
1 files changed, 372 insertions, 174 deletions
@@ -24,19 +24,24 @@ #include "msdos.h" #include "stream.h" #include "mtools.h" +#include "device.h" +#include "old_dos.h" #include "fsP.h" -#include "plain_io.h" -#include "floppyd_io.h" -#include "xdf_io.h" #include "buffer.h" #include "file_name.h" +#include "open_image.h" #define FULL_CYL +mt_off_t sectorsToBytes(Fs_t *This, uint32_t off) +{ + return (mt_off_t) off << This->sectorShift; +} + /* * Read the boot sector. We glean the disk parameters from this sector. */ -static int read_boot(Stream_t *Stream, union bootsector * boot, int size) +static int read_boot(Stream_t *Stream, union bootsector * boot, size_t size) { size_t boot_sector_size; /* sector size, as stored in boot sector */ @@ -46,10 +51,10 @@ static int read_boot(Stream_t *Stream, union bootsector * boot, int size) if(size > MAX_BOOT) size = MAX_BOOT; - if (force_read(Stream, boot->characters, 0, size) != size) + if (force_pread(Stream, boot->characters, 0, size) != (ssize_t) size) return -1; - boot_sector_size = WORD(secsiz); + boot_sector_size = WORD(secsiz); if(boot_sector_size < sizeof(boot->bytes)) { /* zero rest of in-memory boot sector */ memset(boot->bytes+boot_sector_size, 0, @@ -74,8 +79,10 @@ static doscp_t *get_dosConvert(Stream_t *Stream) } Class_t FsClass = { - read_pass_through, /* read */ - write_pass_through, /* write */ + 0, + 0, + pread_pass_through, /* read */ + pwrite_pass_through, /* write */ fs_flush, fs_free, /* free */ 0, /* set geometry */ @@ -99,7 +106,7 @@ static int get_media_type(Stream_t *St, union bootsector *boot) char temp[512]; /* old DOS disk. Media descriptor in the first FAT byte */ /* we assume 512-byte sectors here */ - if (force_read(St,temp,(mt_off_t) 512,512) == 512) + if (force_pread(St,temp,512,512) == 512) media = (unsigned char) temp[0]; else media = 0; @@ -116,11 +123,80 @@ Stream_t *GetFs(Stream_t *Fs) return Fs; } +static void boot_to_geom(struct device *dev, int media, + union bootsector *boot) { + uint32_t tot_sectors; + int BootP, Infp0, InfpX, InfTm; + int j; + unsigned char sum; + uint16_t sect_per_track; + struct label_blk_t *labelBlock; + + dev->ssize = 2; /* allow for init_geom to change it */ + dev->use_2m = 0x80; /* disable 2m mode to begin */ + + if(media == 0xf0 || media >= 0x100){ + dev->heads = WORD(nheads); + dev->sectors = WORD(nsect); + tot_sectors = DWORD(bigsect); + SET_INT(tot_sectors, WORD(psect)); + sect_per_track = dev->heads * dev->sectors; + if(sect_per_track == 0) { + if(mtools_skip_check) { + /* add some fake values if sect_per_track is + * zero. Indeed, some atari disks lack the + * geometry values (i.e. have zeroes in their + * place). In order to avoid division by zero + * errors later on, plug 1 everywhere + */ + dev->heads = 1; + dev->sectors = 1; + sect_per_track = 1; + } else { + fprintf(stderr, "The devil is in the details: zero number of heads or sectors\n"); + exit(1); + } + } + dev->tracks = tot_sectors / sect_per_track; + if(tot_sectors % sect_per_track) + /* round size up */ + dev->tracks++; + + BootP = WORD(ext.old.BootP); + Infp0 = WORD(ext.old.Infp0); + InfpX = WORD(ext.old.InfpX); + InfTm = WORD(ext.old.InfTm); + + if(WORD(fatlen)) { + labelBlock = &boot->boot.ext.old.labelBlock; + } else { + labelBlock = &boot->boot.ext.fat32.labelBlock; + } + + if (boot->boot.descr >= 0xf0 && + has_BPB4 && + strncmp( boot->boot.banner,"2M", 2 ) == 0 && + BootP < 512 && Infp0 < 512 && InfpX < 512 && InfTm < 512 && + BootP >= InfTm + 2 && InfTm >= InfpX && InfpX >= Infp0 && + Infp0 >= 76 ){ + for (sum=0, j=63; j < BootP; j++) + sum += boot->bytes[j];/* checksum */ + dev->ssize = boot->bytes[InfTm]; + if (!sum && dev->ssize <= 7){ + dev->use_2m = 0xff; + dev->ssize |= 0x80; /* is set */ + } + } + dev->sector_size = WORD(secsiz); + } else + if(setDeviceFromOldDos(media, dev) < 0) + exit(1); +} + /** - * Tries out all device definitions for the given drive number, until one - * is found that is able to read from the device + * Tries out one device definition for the given drive number * Parameters - * - drive: drive letter to check + * - dev: device definition to try * - mode: file open mode * - out_dev: device parameters (geometry, etc.) are returned here * - boot: boot sector is read from the disk into this structure @@ -128,171 +204,218 @@ Stream_t *GetFs(Stream_t *Fs) * - media: media byte is returned here (ored with 0x100 if there is a * BIOS Parameter block present) * - maxSize: maximal size supported by (physical) drive returned here + * - try_writable: whether to try opening it writable from the get-go, + * even if not specified as writable in mode (used for mlabel) * - isRop: whether device is read-only is returned here * Return value: * - a Stream allowing to read from this device, must be closed by caller + * + * If a geometry change is needed, drive is re-opened RW, as geometry + * change ioctl needs write access. However, in such case, the lock + * acquired is still only a read lock. */ -Stream_t *find_device(char drive, int mode, struct device *out_dev, - union bootsector *boot, - char *name, int *media, mt_size_t *maxSize, - int *isRop) +static Stream_t *try_device(struct device *dev, + int mode, struct device *out_dev, + union bootsector *boot, + char *name, int *media, mt_off_t *maxSize, + int *isRop, int try_writable, + char *errmsg) { - char errmsg[200]; - Stream_t *Stream; - struct device *dev; - int r; - int isRo=0; - - Stream = NULL; - sprintf(errmsg, "Drive '%c:' not supported", drive); - /* open the device */ - for (dev=devices; dev->name; dev++) { - FREE(&Stream); - if (dev->drive != drive) - continue; - *out_dev = *dev; - expand(dev->name,name); + int retry_write; + int have_read_bootsector=0; + int modeFlags = mode & ~O_ACCMODE; + int openMode; + int lockMode; + + *out_dev = *dev; + expand(dev->name,name); #ifdef USING_NEW_VOLD - strcpy(name, getVoldName(dev, name)); -#endif - - Stream = 0; - if(out_dev->misc_flags & FLOPPYD_FLAG) { - Stream = 0; -#ifdef USE_FLOPPYD - Stream = FloppydOpen(out_dev, name, mode, - errmsg, maxSize); -#endif - } else { - -#ifdef USE_XDF - Stream = XdfOpen(out_dev, name, mode, errmsg, 0); - if(Stream) { - out_dev->use_2m = 0x7f; - if(maxSize) - *maxSize = max_off_t_31; - } + strcpy(name, getVoldName(dev, name)); #endif - - if (!Stream) - Stream = SimpleFileOpen(out_dev, dev, name, - isRop ? mode | O_RDWR: mode, - errmsg, 0, 1, maxSize); - - if(Stream) { - isRo=0; - } else if(isRop && - (errno == EPERM || errno == EACCES || errno == EROFS)) { - Stream = SimpleFileOpen(out_dev, dev, name, - mode | O_RDONLY, - errmsg, 0, 1, maxSize); - if(Stream) { - isRo=1; + if(try_writable) { + /* Caller asks up to try first read-write, and only fall back + * if not feasible */ + openMode = O_RDWR | modeFlags; + } else { + openMode = mode; + } + lockMode = openMode; + + for(retry_write=0; retry_write<2; retry_write++) { + Stream_t *Stream; + int r; + int geomFailure=0; + + if(retry_write) + mode |= O_RDWR; + + Stream = OpenImage(out_dev, dev, name, openMode, errmsg, + 0, lockMode, + maxSize, &geomFailure, NULL); + if(Stream == NULL) { + if(geomFailure && (mode & O_ACCMODE) == O_RDONLY) { + /* Our first attempt was to open read-only, + but this resulted in failure setting the + geometry */ + openMode = modeFlags | O_RDWR; + continue; } - } - } - if( !Stream) - continue; - - /* read the boot sector */ - if ((r=read_boot(Stream, boot, out_dev->blocksize)) < 0){ - sprintf(errmsg, - "init %c: could not read boot sector", - drive); - continue; + if(try_writable && + (errno == EPERM || + errno == EACCES || + errno == EROFS)) { + /* Our first attempt was to open + * read-write, but this resulted in a + * read-protection problem */ + lockMode = openMode = modeFlags | O_RDONLY; + continue; + } + return NULL; } - - if((*media= get_media_type(Stream, boot)) <= 0xf0 ){ - if (boot->boot.jump[2]=='L') + if(!have_read_bootsector) { + /* read the boot sector */ + if ((r=read_boot(Stream, boot, out_dev->blocksize)) < 0){ sprintf(errmsg, - "diskette %c: is Linux LILO, not DOS", - drive); - else - sprintf(errmsg,"init %c: non DOS media", drive); - continue; + "init %c: could not read boot sector", + dev->drive); + FREE(&Stream); + return NULL; + } + + if((*media= get_media_type(Stream, boot)) <= 0xf0 ){ + if (boot->boot.jump[2]=='L') + sprintf(errmsg, + "diskette %c: is Linux LILO, not DOS", + dev->drive); + else + sprintf(errmsg,"init %c: non DOS media", dev->drive); + FREE(&Stream); + return NULL; + } + have_read_bootsector=1; } /* set new parameters, if needed */ errno = 0; - if(SET_GEOM(Stream, out_dev, dev, *media, boot)){ + boot_to_geom(out_dev, *media, boot); + if(SET_GEOM(Stream, out_dev, dev)){ + if(errno == EBADF || errno == EPERM) { + /* Retry with write */ + FREE(&Stream); + openMode = modeFlags | O_RDWR; + continue; + } if(errno) #ifdef HAVE_SNPRINTF snprintf(errmsg, 199, - "Can't set disk parameters for %c: %s", - drive, strerror(errno)); + "Can't set disk parameters for %c: %s", + dev->drive, strerror(errno)); #else - sprintf(errmsg, - "Can't set disk parameters for %c: %s", - drive, strerror(errno)); + sprintf(errmsg, + "Can't set disk parameters for %c: %s", + drive, strerror(errno)); #endif else sprintf(errmsg, "Can't set disk parameters for %c", - drive); - continue; + dev->drive); + FREE(&Stream); + return NULL; + } + if(isRop) { + *isRop = (openMode & O_ACCMODE) == O_RDONLY; } - break; + return Stream; } + return NULL; +} - /* print error msg if needed */ - if ( dev->drive == 0 ){ - FREE(&Stream); - fprintf(stderr,"%s\n",errmsg); - return NULL; - } - if(isRop) - *isRop = isRo; - return Stream; +uint32_t calc_clus_start(Fs_t *Fs) { + return Fs->fat_start + Fs->fat_len*Fs->num_fat + Fs->dir_len; } +/* Calculates number of clusters, and fills it in into Fs->num_clus + * Returns 0 if calculation could be performed, and -1 if less sectors than + * clus_start + */ +int calc_num_clus(Fs_t *Fs, uint32_t tot_sectors) +{ + Fs->clus_start = calc_clus_start(Fs); + if(tot_sectors <= Fs->clus_start) + return -1; + Fs->num_clus = (tot_sectors - Fs->clus_start) / Fs->cluster_size; + return 0; +} -Stream_t *fs_init(char drive, int mode, int *isRop) +/** + * Tries out all device definitions for the given drive letter, until one + * is found that is able to read from the device + * Parameters + * - drive: drive letter to check + * - mode: file open mode + * - out_dev: device parameters (geometry, etc.) are returned here + * - boot: boot sector is read from the disk into this structure + * - name: "name" of device definition (returned) + * - media: media byte is returned here (ored with 0x100 if there is a + * BIOS Parameter block present) + * - maxSize: maximal size supported by (physical) drive returned here + * - isRop: whether device is read-only is returned here + * Return value: + * - a Stream allowing to read from this device, must be closed by caller + */ +Stream_t *find_device(char drive, int mode, struct device *out_dev, + union bootsector *boot, + char *name, int *media, mt_off_t *maxSize, + int *isRop) { - int blocksize; - int media; - int disk_size = 0; /* In case we don't happen to set this below */ - size_t tot_sectors; - char name[EXPAND_BUF]; - int cylinder_size; - struct device dev; - mt_size_t maxSize; + char errmsg[200]; + struct device *dev; - union bootsector boot; + sprintf(errmsg, "Drive '%c:' not supported", drive); + /* open the device */ + for (dev=devices; dev->name; dev++) { + Stream_t *Stream; + int isRo; + isRo=0; + if (dev->drive != drive) + continue; - Fs_t *This; + Stream = try_device(dev, mode, out_dev, + boot, + name, media, maxSize, + &isRo, isRop != NULL, + errmsg); + if(Stream) { + if(isRop) + *isRop = isRo; + return Stream; + } + } - This = New(Fs_t); - if (!This) - return NULL; + /* print error msg if needed */ + fprintf(stderr,"%s\n",errmsg); + return NULL; +} - This->Direct = NULL; - This->Next = NULL; - This->refs = 1; - This->Buffer = 0; - This->Class = &FsClass; - This->preallocatedClusters = 0; - This->lastFatSectorNr = 0; - This->lastFatAccessMode = 0; - This->lastFatSectorData = 0; - This->drive = drive; - This->last = 0; - This->Direct = find_device(drive, mode, &dev, &boot, name, &media, - &maxSize, isRop); - if(!This->Direct) - return NULL; +uint32_t parseFsParams( Fs_t *This, + union bootsector *boot, + int media, + unsigned int cylinder_size) +{ + uint32_t tot_sectors; - cylinder_size = dev.heads * dev.sectors; - This->serialized = 0; if ((media & ~7) == 0xf8){ /* This bit of code is only entered if there is no BPB, or * else result of the AND would be 0x1xx */ struct OldDos_t *params=getOldDosByMedia(media); - if(params == NULL) - return NULL; + if(params == NULL) { + fprintf(stderr, "Unknown media byte %02x\n", media); + return 0; + } This->cluster_size = params->cluster_size; tot_sectors = cylinder_size * params->tracks; This->fat_start = 1; @@ -302,24 +425,23 @@ Stream_t *fs_init(char drive, int mode, int *isRop) This->sector_size = 512; This->sectorShift = 9; This->sectorMask = 511; - This->fat_bits = 12; } else { struct label_blk_t *labelBlock; - int i; - - This->sector_size = WORD_S(secsiz); + unsigned int i; + + This->sector_size = WORD(secsiz); if(This->sector_size > MAX_SECTOR){ - fprintf(stderr,"init %c: sector size too big\n", drive); - return NULL; + fprintf(stderr,"init: sector size too big\n"); + return 0; } i = log_2(This->sector_size); if(i == 24) { fprintf(stderr, - "init %c: sector size (%d) not a small power of two\n", - drive, This->sector_size); - return NULL; + "init: sector size (%d) not a small power of two\n", + This->sector_size); + return 0; } This->sectorShift = i; This->sectorMask = This->sector_size - 1; @@ -328,20 +450,22 @@ Stream_t *fs_init(char drive, int mode, int *isRop) * all numbers are in sectors, except num_clus * (which is in clusters) */ - tot_sectors = WORD_S(psect); + tot_sectors = WORD(psect); if(!tot_sectors) - tot_sectors = DWORD_S(bigsect); + tot_sectors = DWORD(bigsect); - This->cluster_size = boot.boot.clsiz; - This->fat_start = WORD_S(nrsvsect); - This->fat_len = WORD_S(fatlen); - This->dir_len = WORD_S(dirents) * MDIR_SIZE / This->sector_size; - This->num_fat = boot.boot.nfat; + This->cluster_size = boot->boot.clsiz; + This->fat_start = WORD(nrsvsect); + This->fat_len = WORD(fatlen); + This->dir_len = WORD(dirents) * MDIR_SIZE / This->sector_size; + This->num_fat = boot->boot.nfat; if (This->fat_len) { - labelBlock = &boot.boot.ext.old.labelBlock; + labelBlock = &boot->boot.ext.old.labelBlock; } else { - labelBlock = &boot.boot.ext.fat32.labelBlock; + labelBlock = &boot->boot.ext.fat32.labelBlock; + This->fat_len = DWORD(ext.fat32.bigFat); + This->backupBoot = WORD(ext.fat32.backupBoot); } if(has_BPB4) { @@ -350,9 +474,61 @@ Stream_t *fs_init(char drive, int mode, int *isRop) } } - if (tot_sectors >= (maxSize >> This->sectorShift)) { - fprintf(stderr, "Big disks not supported on this architecture\n"); - exit(1); + if(calc_num_clus(This, tot_sectors) < 0) + /* Too few sectors */ + return 0; + set_fat(This); + + return tot_sectors; +} + + +Stream_t *fs_init(char drive, int mode, int *isRop) +{ + uint32_t blocksize; + int media; + size_t disk_size = 0; /* In case we don't happen to set this below */ + uint32_t tot_sectors; + char name[EXPAND_BUF]; + unsigned int cylinder_size; + struct device dev; + mt_off_t maxSize; + char errmsg[81]; + + union bootsector boot; + + Fs_t *This; + + This = New(Fs_t); + if (!This) + return NULL; + + init_head(&This->head, &FsClass, NULL); + This->preallocatedClusters = 0; + This->lastFatSectorNr = 0; + This->lastFatAccessMode = 0; + This->lastFatSectorData = 0; + This->drive = drive; + This->last = 0; + + This->head.Next = find_device(drive, mode, &dev, &boot, name, &media, + &maxSize, isRop); + if(!This->head.Next) + return NULL; + + cylinder_size = dev.heads * dev.sectors; + This->serialized = 0; + + tot_sectors = parseFsParams(This, &boot, media, cylinder_size); + if(tot_sectors == 0) { + /* Error raised by parseFsParams */ + return NULL; + } + + if (check_if_sectors_fit(tot_sectors, maxSize, + This->sector_size, errmsg) < 0) { + fprintf(stderr, "%s", errmsg); + return NULL; } /* full cylinder buffering */ @@ -387,33 +563,34 @@ Stream_t *fs_init(char drive, int mode, int *isRop) blocksize = This->sector_size; else blocksize = dev.blocksize; - if (disk_size) - This->Next = buf_init(This->Direct, - 8 * disk_size * blocksize, - disk_size * blocksize, - This->sector_size); - else - This->Next = This->Direct; - - if (This->Next == NULL) { - perror("init: allocate buffer"); - This->Next = This->Direct; + if (disk_size) { + Stream_t *Buffer = buf_init(This->head.Next, + 8 * disk_size * blocksize, + disk_size * blocksize, + This->sector_size); + + if (Buffer != NULL) + This->head.Next = Buffer; + else + perror("init: allocate buffer"); } /* read the FAT sectors */ - if(fat_read(This, &boot, tot_sectors, dev.use_2m&0x7f)){ + if(fat_read(This, &boot, dev.use_2m&0x7f)){ + fprintf(stderr, "Error reading FAT\n"); This->num_fat = 1; - FREE(&This->Next); - Free(This->Next); + FREE(&This->head.Next); + Free(This->head.Next); return NULL; } /* Set the codepage */ This->cp = cp_open(dev.codepage); if(This->cp == NULL) { + fprintf(stderr, "Error setting code page\n"); fs_free((Stream_t *)This); - FREE(&This->Next); - Free(This->Next); + FREE(&This->head.Next); + Free(This->head.Next); return NULL; } @@ -424,13 +601,21 @@ char getDrive(Stream_t *Stream) { DeclareThis(Fs_t); - if(This->Class != &FsClass) + if(This->head.Class != &FsClass) return getDrive(GetFs(Stream)); else return This->drive; } -int fsPreallocateClusters(Fs_t *Fs, long size) +/* + * Upper layer asks to pre-allocated more additional clusters + * Parameters: + * size: new additional clusters to pre-allocate + * Return: + * 0 if pre-allocation was granted + * -1 if not enough clusters could be found + */ +int fsPreallocateClusters(Fs_t *Fs, uint32_t size) { if(size > 0 && getfreeMinClusters((Stream_t *)Fs, size) != 1) return -1; @@ -438,3 +623,16 @@ int fsPreallocateClusters(Fs_t *Fs, long size) Fs->preallocatedClusters += size; return 0; } + +/* + * Upper layer wants to release some clusters that it had + * pre-allocated before Usually done because they have now been really + * allocated, and thus pre-allocation needs to be released to prevent + * counting them twice. + * Parameters: + * size: new additional clusters to pre-allocate + */ +void fsReleasePreallocateClusters(Fs_t *Fs, uint32_t size) +{ + Fs->preallocatedClusters -= size; +} |