1 /* Copyright 1986-1992 Emmet P. Gray.
2 * Copyright 1996-2002,2006-2009 Alain Knaff.
3 * This file is part of mtools.
4 *
5 * Mtools is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * Mtools is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Initialize an MSDOS diskette. Read the boot sector, and switch to the
19 * proper floppy disk device to match the format on the disk. Sets a bunch
20 * of global variables. Returns 0 on success, or 1 on failure.
21 */
22
23 #include "sysincludes.h"
24 #include "msdos.h"
25 #include "stream.h"
26 #include "mtools.h"
27 #include "device.h"
28 #include "old_dos.h"
29 #include "fsP.h"
30 #include "buffer.h"
31 #include "file_name.h"
32 #include "open_image.h"
33
34 #define FULL_CYL
35
sectorsToBytes(Fs_t * This,uint32_t off)36 mt_off_t sectorsToBytes(Fs_t *This, uint32_t off)
37 {
38 return (mt_off_t) off << This->sectorShift;
39 }
40
41 /*
42 * Read the boot sector. We glean the disk parameters from this sector.
43 */
read_boot(Stream_t * Stream,union bootsector * boot,size_t size)44 static int read_boot(Stream_t *Stream, union bootsector * boot, size_t size)
45 {
46 size_t boot_sector_size; /* sector size, as stored in boot sector */
47
48 /* read the first sector, or part of it */
49 if(!size)
50 size = BOOTSIZE;
51 if(size > MAX_BOOT)
52 size = MAX_BOOT;
53
54 if (force_pread(Stream, boot->characters, 0, size) != (ssize_t) size)
55 return -1;
56
57 boot_sector_size = WORD(secsiz);
58 if(boot_sector_size < sizeof(boot->bytes)) {
59 /* zero rest of in-memory boot sector */
60 memset(boot->bytes+boot_sector_size, 0,
61 sizeof(boot->bytes) - boot_sector_size);
62 }
63
64 return 0;
65 }
66
fs_flush(Stream_t * Stream)67 static int fs_flush(Stream_t *Stream)
68 {
69 DeclareThis(Fs_t);
70
71 fat_write(This);
72 return 0;
73 }
74
get_dosConvert(Stream_t * Stream)75 static doscp_t *get_dosConvert(Stream_t *Stream)
76 {
77 DeclareThis(Fs_t);
78 return This->cp;
79 }
80
81 Class_t FsClass = {
82 0,
83 0,
84 pread_pass_through, /* read */
85 pwrite_pass_through, /* write */
86 fs_flush,
87 fs_free, /* free */
88 0, /* set geometry */
89 get_data_pass_through,
90 0, /* pre allocate */
91 get_dosConvert, /* dosconvert */
92 0 /* discard */
93 };
94
95 /**
96 * Get media type byte from boot sector (BIOS Parameter Block 2) or
97 * from FAT (if media byte from BPB 2 looks fishy)
98 * Return the media byte + 0x100 if found in BPB 2, or as is if found in FAT.
99 */
get_media_type(Stream_t * St,union bootsector * boot)100 static int get_media_type(Stream_t *St, union bootsector *boot)
101 {
102 int media;
103
104 media = boot->boot.descr;
105 if(media < 0xf0){
106 char temp[512];
107 /* old DOS disk. Media descriptor in the first FAT byte */
108 /* we assume 512-byte sectors here */
109 if (force_pread(St,temp,512,512) == 512)
110 media = (unsigned char) temp[0];
111 else
112 media = 0;
113 } else
114 media += 0x100;
115 return media;
116 }
117
118
GetFs(Stream_t * Fs)119 Stream_t *GetFs(Stream_t *Fs)
120 {
121 while(Fs && Fs->Class != &FsClass)
122 Fs = Fs->Next;
123 return Fs;
124 }
125
boot_to_geom(struct device * dev,int media,union bootsector * boot)126 static void boot_to_geom(struct device *dev, int media,
127 union bootsector *boot) {
128 uint32_t tot_sectors;
129 int BootP, Infp0, InfpX, InfTm;
130 int j;
131 unsigned char sum;
132 uint16_t sect_per_track;
133 struct label_blk_t *labelBlock;
134
135 dev->ssize = 2; /* allow for init_geom to change it */
136 dev->use_2m = 0x80; /* disable 2m mode to begin */
137
138 if(media == 0xf0 || media >= 0x100){
139 dev->heads = WORD(nheads);
140 dev->sectors = WORD(nsect);
141 tot_sectors = DWORD(bigsect);
142 SET_INT(tot_sectors, WORD(psect));
143 sect_per_track = dev->heads * dev->sectors;
144 if(sect_per_track == 0) {
145 if(mtools_skip_check) {
146 /* add some fake values if sect_per_track is
147 * zero. Indeed, some atari disks lack the
148 * geometry values (i.e. have zeroes in their
149 * place). In order to avoid division by zero
150 * errors later on, plug 1 everywhere
151 */
152 dev->heads = 1;
153 dev->sectors = 1;
154 sect_per_track = 1;
155 } else {
156 fprintf(stderr, "The devil is in the details: zero number of heads or sectors\n");
157 exit(1);
158 }
159 }
160 dev->tracks = tot_sectors / sect_per_track;
161 if(tot_sectors % sect_per_track)
162 /* round size up */
163 dev->tracks++;
164
165 BootP = WORD(ext.old.BootP);
166 Infp0 = WORD(ext.old.Infp0);
167 InfpX = WORD(ext.old.InfpX);
168 InfTm = WORD(ext.old.InfTm);
169
170 if(WORD(fatlen)) {
171 labelBlock = &boot->boot.ext.old.labelBlock;
172 } else {
173 labelBlock = &boot->boot.ext.fat32.labelBlock;
174 }
175
176 if (boot->boot.descr >= 0xf0 &&
177 has_BPB4 &&
178 strncmp( boot->boot.banner,"2M", 2 ) == 0 &&
179 BootP < 512 && Infp0 < 512 && InfpX < 512 && InfTm < 512 &&
180 BootP >= InfTm + 2 && InfTm >= InfpX && InfpX >= Infp0 &&
181 Infp0 >= 76 ){
182 for (sum=0, j=63; j < BootP; j++)
183 sum += boot->bytes[j];/* checksum */
184 dev->ssize = boot->bytes[InfTm];
185 if (!sum && dev->ssize <= 7){
186 dev->use_2m = 0xff;
187 dev->ssize |= 0x80; /* is set */
188 }
189 }
190 dev->sector_size = WORD(secsiz);
191 } else
192 if(setDeviceFromOldDos(media, dev) < 0)
193 exit(1);
194 }
195
196 /**
197 * Tries out one device definition for the given drive number
198 * Parameters
199 * - dev: device definition to try
200 * - mode: file open mode
201 * - out_dev: device parameters (geometry, etc.) are returned here
202 * - boot: boot sector is read from the disk into this structure
203 * - name: "name" of device definition (returned)
204 * - media: media byte is returned here (ored with 0x100 if there is a
205 * BIOS Parameter block present)
206 * - maxSize: maximal size supported by (physical) drive returned here
207 * - try_writable: whether to try opening it writable from the get-go,
208 * even if not specified as writable in mode (used for mlabel)
209 * - isRop: whether device is read-only is returned here
210 * Return value:
211 * - a Stream allowing to read from this device, must be closed by caller
212 *
213 * If a geometry change is needed, drive is re-opened RW, as geometry
214 * change ioctl needs write access. However, in such case, the lock
215 * acquired is still only a read lock.
216 */
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)217 static Stream_t *try_device(struct device *dev,
218 int mode, struct device *out_dev,
219 union bootsector *boot,
220 char *name, int *media, mt_off_t *maxSize,
221 int *isRop, int try_writable,
222 char *errmsg)
223 {
224 int retry_write;
225 int have_read_bootsector=0;
226 int modeFlags = mode & ~O_ACCMODE;
227 int openMode;
228 int lockMode;
229
230 *out_dev = *dev;
231 expand(dev->name,name);
232 #ifdef USING_NEW_VOLD
233 strcpy(name, getVoldName(dev, name));
234 #endif
235
236 if(try_writable) {
237 /* Caller asks up to try first read-write, and only fall back
238 * if not feasible */
239 openMode = O_RDWR | modeFlags;
240 } else {
241 openMode = mode;
242 }
243 lockMode = openMode;
244
245 for(retry_write=0; retry_write<2; retry_write++) {
246 Stream_t *Stream;
247 int r;
248 int geomFailure=0;
249
250 if(retry_write)
251 mode |= O_RDWR;
252
253 Stream = OpenImage(out_dev, dev, name, openMode, errmsg,
254 0, lockMode,
255 maxSize, &geomFailure, NULL);
256 if(Stream == NULL) {
257 if(geomFailure && (mode & O_ACCMODE) == O_RDONLY) {
258 /* Our first attempt was to open read-only,
259 but this resulted in failure setting the
260 geometry */
261 openMode = modeFlags | O_RDWR;
262 continue;
263 }
264
265 if(try_writable &&
266 (errno == EPERM ||
267 errno == EACCES ||
268 errno == EROFS)) {
269 /* Our first attempt was to open
270 * read-write, but this resulted in a
271 * read-protection problem */
272 lockMode = openMode = modeFlags | O_RDONLY;
273 continue;
274 }
275 return NULL;
276 }
277 if(!have_read_bootsector) {
278 /* read the boot sector */
279 if ((r=read_boot(Stream, boot, out_dev->blocksize)) < 0){
280 sprintf(errmsg,
281 "init %c: could not read boot sector",
282 dev->drive);
283 FREE(&Stream);
284 return NULL;
285 }
286
287 if((*media= get_media_type(Stream, boot)) <= 0xf0 ){
288 if (boot->boot.jump[2]=='L')
289 sprintf(errmsg,
290 "diskette %c: is Linux LILO, not DOS",
291 dev->drive);
292 else
293 sprintf(errmsg,"init %c: non DOS media", dev->drive);
294 FREE(&Stream);
295 return NULL;
296 }
297 have_read_bootsector=1;
298 }
299
300 /* set new parameters, if needed */
301 errno = 0;
302 boot_to_geom(out_dev, *media, boot);
303 if(SET_GEOM(Stream, out_dev, dev)){
304 if(errno == EBADF || errno == EPERM) {
305 /* Retry with write */
306 FREE(&Stream);
307 openMode = modeFlags | O_RDWR;
308 continue;
309 }
310 if(errno)
311 #ifdef HAVE_SNPRINTF
312 snprintf(errmsg, 199,
313 "Can't set disk parameters for %c: %s",
314 dev->drive, strerror(errno));
315 #else
316 sprintf(errmsg,
317 "Can't set disk parameters for %c: %s",
318 drive, strerror(errno));
319 #endif
320 else
321 sprintf(errmsg,
322 "Can't set disk parameters for %c",
323 dev->drive);
324 FREE(&Stream);
325 return NULL;
326 }
327 if(isRop) {
328 *isRop = (openMode & O_ACCMODE) == O_RDONLY;
329 }
330 return Stream;
331 }
332 return NULL;
333 }
334
calc_clus_start(Fs_t * Fs)335 uint32_t calc_clus_start(Fs_t *Fs) {
336 return Fs->fat_start + Fs->fat_len*Fs->num_fat + Fs->dir_len;
337 }
338
339 /* Calculates number of clusters, and fills it in into Fs->num_clus
340 * Returns 0 if calculation could be performed, and -1 if less sectors than
341 * clus_start
342 */
calc_num_clus(Fs_t * Fs,uint32_t tot_sectors)343 int calc_num_clus(Fs_t *Fs, uint32_t tot_sectors)
344 {
345 Fs->clus_start = calc_clus_start(Fs);
346 if(tot_sectors <= Fs->clus_start)
347 return -1;
348 Fs->num_clus = (tot_sectors - Fs->clus_start) / Fs->cluster_size;
349 return 0;
350 }
351
352 /**
353 * Tries out all device definitions for the given drive letter, until one
354 * is found that is able to read from the device
355 * Parameters
356 * - drive: drive letter to check
357 * - mode: file open mode
358 * - out_dev: device parameters (geometry, etc.) are returned here
359 * - boot: boot sector is read from the disk into this structure
360 * - name: "name" of device definition (returned)
361 * - media: media byte is returned here (ored with 0x100 if there is a
362 * BIOS Parameter block present)
363 * - maxSize: maximal size supported by (physical) drive returned here
364 * - isRop: whether device is read-only is returned here
365 * Return value:
366 * - a Stream allowing to read from this device, must be closed by caller
367 */
find_device(char drive,int mode,struct device * out_dev,union bootsector * boot,char * name,int * media,mt_off_t * maxSize,int * isRop)368 Stream_t *find_device(char drive, int mode, struct device *out_dev,
369 union bootsector *boot,
370 char *name, int *media, mt_off_t *maxSize,
371 int *isRop)
372 {
373 char errmsg[200];
374 struct device *dev;
375
376 sprintf(errmsg, "Drive '%c:' not supported", drive);
377 /* open the device */
378 for (dev=devices; dev->name; dev++) {
379 Stream_t *Stream;
380 int isRo;
381 isRo=0;
382 if (dev->drive != drive)
383 continue;
384
385 Stream = try_device(dev, mode, out_dev,
386 boot,
387 name, media, maxSize,
388 &isRo, isRop != NULL,
389 errmsg);
390 if(Stream) {
391 if(isRop)
392 *isRop = isRo;
393 return Stream;
394 }
395 }
396
397 /* print error msg if needed */
398 fprintf(stderr,"%s\n",errmsg);
399 return NULL;
400 }
401
402
parseFsParams(Fs_t * This,union bootsector * boot,int media,unsigned int cylinder_size)403 uint32_t parseFsParams( Fs_t *This,
404 union bootsector *boot,
405 int media,
406 unsigned int cylinder_size)
407 {
408 uint32_t tot_sectors;
409
410 if ((media & ~7) == 0xf8){
411 /* This bit of code is only entered if there is no BPB, or
412 * else result of the AND would be 0x1xx
413 */
414 struct OldDos_t *params=getOldDosByMedia(media);
415 if(params == NULL) {
416 fprintf(stderr, "Unknown media byte %02x\n", media);
417 return 0;
418 }
419 This->cluster_size = params->cluster_size;
420 tot_sectors = cylinder_size * params->tracks;
421 This->fat_start = 1;
422 This->fat_len = params->fat_len;
423 This->dir_len = params->dir_len;
424 This->num_fat = 2;
425 This->sector_size = 512;
426 This->sectorShift = 9;
427 This->sectorMask = 511;
428 } else {
429 struct label_blk_t *labelBlock;
430 unsigned int i;
431
432 This->sector_size = WORD(secsiz);
433 if(This->sector_size > MAX_SECTOR){
434 fprintf(stderr,"init: sector size too big\n");
435 return 0;
436 }
437
438 i = log_2(This->sector_size);
439
440 if(i == 24) {
441 fprintf(stderr,
442 "init: sector size (%d) not a small power of two\n",
443 This->sector_size);
444 return 0;
445 }
446 This->sectorShift = i;
447 This->sectorMask = This->sector_size - 1;
448
449 /*
450 * all numbers are in sectors, except num_clus
451 * (which is in clusters)
452 */
453 tot_sectors = WORD(psect);
454 if(!tot_sectors)
455 tot_sectors = DWORD(bigsect);
456
457 This->cluster_size = boot->boot.clsiz;
458 This->fat_start = WORD(nrsvsect);
459 This->fat_len = WORD(fatlen);
460 This->dir_len = WORD(dirents) * MDIR_SIZE / This->sector_size;
461 This->num_fat = boot->boot.nfat;
462
463 if (This->fat_len) {
464 labelBlock = &boot->boot.ext.old.labelBlock;
465 } else {
466 labelBlock = &boot->boot.ext.fat32.labelBlock;
467 This->fat_len = DWORD(ext.fat32.bigFat);
468 This->backupBoot = WORD(ext.fat32.backupBoot);
469 }
470
471 if(has_BPB4) {
472 This->serialized = 1;
473 This->serial_number = _DWORD(labelBlock->serial);
474 }
475 }
476
477 if(calc_num_clus(This, tot_sectors) < 0)
478 /* Too few sectors */
479 return 0;
480 set_fat(This);
481
482 return tot_sectors;
483 }
484
485
fs_init(char drive,int mode,int * isRop)486 Stream_t *fs_init(char drive, int mode, int *isRop)
487 {
488 uint32_t blocksize;
489 int media;
490 size_t disk_size = 0; /* In case we don't happen to set this below */
491 uint32_t tot_sectors;
492 char name[EXPAND_BUF];
493 unsigned int cylinder_size;
494 struct device dev;
495 mt_off_t maxSize;
496 char errmsg[81];
497
498 union bootsector boot;
499
500 Fs_t *This;
501
502 This = New(Fs_t);
503 if (!This)
504 return NULL;
505
506 init_head(&This->head, &FsClass, NULL);
507 This->preallocatedClusters = 0;
508 This->lastFatSectorNr = 0;
509 This->lastFatAccessMode = 0;
510 This->lastFatSectorData = 0;
511 This->drive = drive;
512 This->last = 0;
513
514 This->head.Next = find_device(drive, mode, &dev, &boot, name, &media,
515 &maxSize, isRop);
516 if(!This->head.Next)
517 return NULL;
518
519 cylinder_size = dev.heads * dev.sectors;
520 This->serialized = 0;
521
522 tot_sectors = parseFsParams(This, &boot, media, cylinder_size);
523 if(tot_sectors == 0) {
524 /* Error raised by parseFsParams */
525 return NULL;
526 }
527
528 if (check_if_sectors_fit(tot_sectors, maxSize,
529 This->sector_size, errmsg) < 0) {
530 fprintf(stderr, "%s", errmsg);
531 return NULL;
532 }
533
534 /* full cylinder buffering */
535 #ifdef FULL_CYL
536 disk_size = (dev.tracks) ? cylinder_size : 512;
537 #else /* FULL_CYL */
538 disk_size = (dev.tracks) ? dev.sectors : 512;
539 #endif /* FULL_CYL */
540
541 #if (defined OS_sysv4 && !defined OS_solaris)
542 /*
543 * The driver in Dell's SVR4 v2.01 is unreliable with large writes.
544 */
545 disk_size = 0;
546 #endif /* (defined sysv4 && !defined(solaris)) */
547
548 #ifdef OS_linux
549 disk_size = cylinder_size;
550 #endif
551
552 #if 1
553 if(disk_size > 256) {
554 disk_size = dev.sectors;
555 if(dev.sectors % 2)
556 disk_size <<= 1;
557 }
558 #endif
559 if (disk_size % 2)
560 disk_size *= 2;
561
562 if(!dev.blocksize || dev.blocksize < This->sector_size)
563 blocksize = This->sector_size;
564 else
565 blocksize = dev.blocksize;
566 if (disk_size) {
567 Stream_t *Buffer = buf_init(This->head.Next,
568 8 * disk_size * blocksize,
569 disk_size * blocksize,
570 This->sector_size);
571
572 if (Buffer != NULL)
573 This->head.Next = Buffer;
574 else
575 perror("init: allocate buffer");
576 }
577
578 /* read the FAT sectors */
579 if(fat_read(This, &boot, dev.use_2m&0x7f)){
580 fprintf(stderr, "Error reading FAT\n");
581 This->num_fat = 1;
582 FREE(&This->head.Next);
583 Free(This->head.Next);
584 return NULL;
585 }
586
587 /* Set the codepage */
588 This->cp = cp_open(dev.codepage);
589 if(This->cp == NULL) {
590 fprintf(stderr, "Error setting code page\n");
591 fs_free((Stream_t *)This);
592 FREE(&This->head.Next);
593 Free(This->head.Next);
594 return NULL;
595 }
596
597 return (Stream_t *) This;
598 }
599
getDrive(Stream_t * Stream)600 char getDrive(Stream_t *Stream)
601 {
602 DeclareThis(Fs_t);
603
604 if(This->head.Class != &FsClass)
605 return getDrive(GetFs(Stream));
606 else
607 return This->drive;
608 }
609
610 /*
611 * Upper layer asks to pre-allocated more additional clusters
612 * Parameters:
613 * size: new additional clusters to pre-allocate
614 * Return:
615 * 0 if pre-allocation was granted
616 * -1 if not enough clusters could be found
617 */
fsPreallocateClusters(Fs_t * Fs,uint32_t size)618 int fsPreallocateClusters(Fs_t *Fs, uint32_t size)
619 {
620 if(size > 0 && getfreeMinClusters((Stream_t *)Fs, size) != 1)
621 return -1;
622
623 Fs->preallocatedClusters += size;
624 return 0;
625 }
626
627 /*
628 * Upper layer wants to release some clusters that it had
629 * pre-allocated before Usually done because they have now been really
630 * allocated, and thus pre-allocation needs to be released to prevent
631 * counting them twice.
632 * Parameters:
633 * size: new additional clusters to pre-allocate
634 */
fsReleasePreallocateClusters(Fs_t * Fs,uint32_t size)635 void fsReleasePreallocateClusters(Fs_t *Fs, uint32_t size)
636 {
637 Fs->preallocatedClusters -= size;
638 }
639