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