blob: 62574f94e4a2c6bb5ab4ea404c1d19b49da3c2e3 [file] [log] [blame]
Alistair Delvabeaee832021-02-24 11:27:23 -08001/* 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 "fsP.h"
28#include "plain_io.h"
29#include "floppyd_io.h"
30#include "xdf_io.h"
31#include "buffer.h"
32#include "file_name.h"
33
34#define FULL_CYL
35
36/*
37 * Read the boot sector. We glean the disk parameters from this sector.
38 */
39static int read_boot(Stream_t *Stream, union bootsector * boot, int size)
40{
41 size_t boot_sector_size; /* sector size, as stored in boot sector */
42
43 /* read the first sector, or part of it */
44 if(!size)
45 size = BOOTSIZE;
46 if(size > MAX_BOOT)
47 size = MAX_BOOT;
48
49 if (force_read(Stream, boot->characters, 0, size) != size)
50 return -1;
51
52 boot_sector_size = WORD(secsiz);
53 if(boot_sector_size < sizeof(boot->bytes)) {
54 /* zero rest of in-memory boot sector */
55 memset(boot->bytes+boot_sector_size, 0,
56 sizeof(boot->bytes) - boot_sector_size);
57 }
58
59 return 0;
60}
61
62static int fs_flush(Stream_t *Stream)
63{
64 DeclareThis(Fs_t);
65
66 fat_write(This);
67 return 0;
68}
69
70static doscp_t *get_dosConvert(Stream_t *Stream)
71{
72 DeclareThis(Fs_t);
73 return This->cp;
74}
75
76Class_t FsClass = {
77 read_pass_through, /* read */
78 write_pass_through, /* write */
79 fs_flush,
80 fs_free, /* free */
81 0, /* set geometry */
82 get_data_pass_through,
83 0, /* pre allocate */
84 get_dosConvert, /* dosconvert */
85 0 /* discard */
86};
87
88/**
89 * Get media type byte from boot sector (BIOS Parameter Block 2) or
90 * from FAT (if media byte from BPB 2 looks fishy)
91 * Return the media byte + 0x100 if found in BPB 2, or as is if found in FAT.
92 */
93static int get_media_type(Stream_t *St, union bootsector *boot)
94{
95 int media;
96
97 media = boot->boot.descr;
98 if(media < 0xf0){
99 char temp[512];
100 /* old DOS disk. Media descriptor in the first FAT byte */
101 /* we assume 512-byte sectors here */
102 if (force_read(St,temp,(mt_off_t) 512,512) == 512)
103 media = (unsigned char) temp[0];
104 else
105 media = 0;
106 } else
107 media += 0x100;
108 return media;
109}
110
111
112Stream_t *GetFs(Stream_t *Fs)
113{
114 while(Fs && Fs->Class != &FsClass)
115 Fs = Fs->Next;
116 return Fs;
117}
118
119/**
120 * Tries out all device definitions for the given drive number, until one
121 * is found that is able to read from the device
122 * Parameters
123 * - drive: drive letter to check
124 * - mode: file open mode
125 * - out_dev: device parameters (geometry, etc.) are returned here
126 * - boot: boot sector is read from the disk into this structure
127 * - name: "name" of device definition (returned)
128 * - media: media byte is returned here (ored with 0x100 if there is a
129 * BIOS Parameter block present)
130 * - maxSize: maximal size supported by (physical) drive returned here
131 * - isRop: whether device is read-only is returned here
132 * Return value:
133 * - a Stream allowing to read from this device, must be closed by caller
134 */
135Stream_t *find_device(char drive, int mode, struct device *out_dev,
136 union bootsector *boot,
137 char *name, int *media, mt_size_t *maxSize,
138 int *isRop)
139{
140 char errmsg[200];
141 Stream_t *Stream;
142 struct device *dev;
143 int r;
144 int isRo=0;
145
146 Stream = NULL;
147 sprintf(errmsg, "Drive '%c:' not supported", drive);
148 /* open the device */
149 for (dev=devices; dev->name; dev++) {
150 FREE(&Stream);
151 if (dev->drive != drive)
152 continue;
153 *out_dev = *dev;
154 expand(dev->name,name);
155#ifdef USING_NEW_VOLD
156 strcpy(name, getVoldName(dev, name));
157#endif
158
159 Stream = 0;
160 if(out_dev->misc_flags & FLOPPYD_FLAG) {
161 Stream = 0;
162#ifdef USE_FLOPPYD
163 Stream = FloppydOpen(out_dev, name, mode,
164 errmsg, maxSize);
165#endif
166 } else {
167
168#ifdef USE_XDF
169 Stream = XdfOpen(out_dev, name, mode, errmsg, 0);
170 if(Stream) {
171 out_dev->use_2m = 0x7f;
172 if(maxSize)
173 *maxSize = max_off_t_31;
174 }
175#endif
176
177
178 if (!Stream)
179 Stream = SimpleFileOpen(out_dev, dev, name,
180 isRop ? mode | O_RDWR: mode,
181 errmsg, 0, 1, maxSize);
182
183 if(Stream) {
184 isRo=0;
185 } else if(isRop &&
186 (errno == EPERM || errno == EACCES || errno == EROFS)) {
187 Stream = SimpleFileOpen(out_dev, dev, name,
188 mode | O_RDONLY,
189 errmsg, 0, 1, maxSize);
190 if(Stream) {
191 isRo=1;
192 }
193 }
194 }
195
196 if( !Stream)
197 continue;
198
199 /* read the boot sector */
200 if ((r=read_boot(Stream, boot, out_dev->blocksize)) < 0){
201 sprintf(errmsg,
202 "init %c: could not read boot sector",
203 drive);
204 continue;
205 }
206
207 if((*media= get_media_type(Stream, boot)) <= 0xf0 ){
208 if (boot->boot.jump[2]=='L')
209 sprintf(errmsg,
210 "diskette %c: is Linux LILO, not DOS",
211 drive);
212 else
213 sprintf(errmsg,"init %c: non DOS media", drive);
214 continue;
215 }
216
217 /* set new parameters, if needed */
218 errno = 0;
219 if(SET_GEOM(Stream, out_dev, dev, *media, boot)){
220 if(errno)
221#ifdef HAVE_SNPRINTF
222 snprintf(errmsg, 199,
223 "Can't set disk parameters for %c: %s",
224 drive, strerror(errno));
225#else
226 sprintf(errmsg,
227 "Can't set disk parameters for %c: %s",
228 drive, strerror(errno));
229#endif
230 else
231 sprintf(errmsg,
232 "Can't set disk parameters for %c",
233 drive);
234 continue;
235 }
236 break;
237 }
238
239 /* print error msg if needed */
240 if ( dev->drive == 0 ){
241 FREE(&Stream);
242 fprintf(stderr,"%s\n",errmsg);
243 return NULL;
244 }
245 if(isRop)
246 *isRop = isRo;
247 return Stream;
248}
249
250
251Stream_t *fs_init(char drive, int mode, int *isRop)
252{
253 int blocksize;
254 int media;
255 int disk_size = 0; /* In case we don't happen to set this below */
256 size_t tot_sectors;
257 char name[EXPAND_BUF];
258 int cylinder_size;
259 struct device dev;
260 mt_size_t maxSize;
261
262 union bootsector boot;
263
264 Fs_t *This;
265
266 This = New(Fs_t);
267 if (!This)
268 return NULL;
269
270 This->Direct = NULL;
271 This->Next = NULL;
272 This->refs = 1;
273 This->Buffer = 0;
274 This->Class = &FsClass;
275 This->preallocatedClusters = 0;
276 This->lastFatSectorNr = 0;
277 This->lastFatAccessMode = 0;
278 This->lastFatSectorData = 0;
279 This->drive = drive;
280 This->last = 0;
281
282 This->Direct = find_device(drive, mode, &dev, &boot, name, &media,
283 &maxSize, isRop);
284 if(!This->Direct)
285 return NULL;
286
287 cylinder_size = dev.heads * dev.sectors;
288 This->serialized = 0;
289 if ((media & ~7) == 0xf8){
290 /* This bit of code is only entered if there is no BPB, or
291 * else result of the AND would be 0x1xx
292 */
293 struct OldDos_t *params=getOldDosByMedia(media);
294 if(params == NULL)
295 return NULL;
296 This->cluster_size = params->cluster_size;
297 tot_sectors = cylinder_size * params->tracks;
298 This->fat_start = 1;
299 This->fat_len = params->fat_len;
300 This->dir_len = params->dir_len;
301 This->num_fat = 2;
302 This->sector_size = 512;
303 This->sectorShift = 9;
304 This->sectorMask = 511;
305 This->fat_bits = 12;
306 } else {
307 struct label_blk_t *labelBlock;
308 int i;
309
310 This->sector_size = WORD_S(secsiz);
311 if(This->sector_size > MAX_SECTOR){
312 fprintf(stderr,"init %c: sector size too big\n", drive);
313 return NULL;
314 }
315
316 i = log_2(This->sector_size);
317
318 if(i == 24) {
319 fprintf(stderr,
320 "init %c: sector size (%d) not a small power of two\n",
321 drive, This->sector_size);
322 return NULL;
323 }
324 This->sectorShift = i;
325 This->sectorMask = This->sector_size - 1;
326
327 /*
328 * all numbers are in sectors, except num_clus
329 * (which is in clusters)
330 */
331 tot_sectors = WORD_S(psect);
332 if(!tot_sectors)
333 tot_sectors = DWORD_S(bigsect);
334
335 This->cluster_size = boot.boot.clsiz;
336 This->fat_start = WORD_S(nrsvsect);
337 This->fat_len = WORD_S(fatlen);
338 This->dir_len = WORD_S(dirents) * MDIR_SIZE / This->sector_size;
339 This->num_fat = boot.boot.nfat;
340
341 if (This->fat_len) {
342 labelBlock = &boot.boot.ext.old.labelBlock;
343 } else {
344 labelBlock = &boot.boot.ext.fat32.labelBlock;
345 }
346
347 if(has_BPB4) {
348 This->serialized = 1;
349 This->serial_number = _DWORD(labelBlock->serial);
350 }
351 }
352
353 if (tot_sectors >= (maxSize >> This->sectorShift)) {
354 fprintf(stderr, "Big disks not supported on this architecture\n");
355 exit(1);
356 }
357
358 /* full cylinder buffering */
359#ifdef FULL_CYL
360 disk_size = (dev.tracks) ? cylinder_size : 512;
361#else /* FULL_CYL */
362 disk_size = (dev.tracks) ? dev.sectors : 512;
363#endif /* FULL_CYL */
364
365#if (defined OS_sysv4 && !defined OS_solaris)
366 /*
367 * The driver in Dell's SVR4 v2.01 is unreliable with large writes.
368 */
369 disk_size = 0;
370#endif /* (defined sysv4 && !defined(solaris)) */
371
372#ifdef OS_linux
373 disk_size = cylinder_size;
374#endif
375
376#if 1
377 if(disk_size > 256) {
378 disk_size = dev.sectors;
379 if(dev.sectors % 2)
380 disk_size <<= 1;
381 }
382#endif
383 if (disk_size % 2)
384 disk_size *= 2;
385
386 if(!dev.blocksize || dev.blocksize < This->sector_size)
387 blocksize = This->sector_size;
388 else
389 blocksize = dev.blocksize;
390 if (disk_size)
391 This->Next = buf_init(This->Direct,
392 8 * disk_size * blocksize,
393 disk_size * blocksize,
394 This->sector_size);
395 else
396 This->Next = This->Direct;
397
398 if (This->Next == NULL) {
399 perror("init: allocate buffer");
400 This->Next = This->Direct;
401 }
402
403 /* read the FAT sectors */
404 if(fat_read(This, &boot, tot_sectors, dev.use_2m&0x7f)){
405 This->num_fat = 1;
406 FREE(&This->Next);
407 Free(This->Next);
408 return NULL;
409 }
410
411 /* Set the codepage */
412 This->cp = cp_open(dev.codepage);
413 if(This->cp == NULL) {
414 fs_free((Stream_t *)This);
415 FREE(&This->Next);
416 Free(This->Next);
417 return NULL;
418 }
419
420 return (Stream_t *) This;
421}
422
423char getDrive(Stream_t *Stream)
424{
425 DeclareThis(Fs_t);
426
427 if(This->Class != &FsClass)
428 return getDrive(GetFs(Stream));
429 else
430 return This->drive;
431}
432
433int fsPreallocateClusters(Fs_t *Fs, long size)
434{
435 if(size > 0 && getfreeMinClusters((Stream_t *)Fs, size) != 1)
436 return -1;
437
438 Fs->preallocatedClusters += size;
439 return 0;
440}