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 "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  */
read_boot(Stream_t * Stream,union bootsector * boot,int size)39 static 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 
fs_flush(Stream_t * Stream)62 static int fs_flush(Stream_t *Stream)
63 {
64 	DeclareThis(Fs_t);
65 
66 	fat_write(This);
67 	return 0;
68 }
69 
get_dosConvert(Stream_t * Stream)70 static doscp_t *get_dosConvert(Stream_t *Stream)
71 {
72   DeclareThis(Fs_t);
73   return This->cp;
74 }
75 
76 Class_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  */
get_media_type(Stream_t * St,union bootsector * boot)93 static 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 
GetFs(Stream_t * Fs)112 Stream_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  */
find_device(char drive,int mode,struct device * out_dev,union bootsector * boot,char * name,int * media,mt_size_t * maxSize,int * isRop)135 Stream_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 
fs_init(char drive,int mode,int * isRop)251 Stream_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 
getDrive(Stream_t * Stream)423 char 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 
fsPreallocateClusters(Fs_t * Fs,long size)433 int 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 }
441