aboutsummaryrefslogtreecommitdiff
path: root/init.c
diff options
context:
space:
mode:
Diffstat (limited to 'init.c')
-rw-r--r--init.c546
1 files changed, 372 insertions, 174 deletions
diff --git a/init.c b/init.c
index 62574f9..4b9b7e5 100644
--- a/init.c
+++ b/init.c
@@ -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;
+}