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