- Make use of MUIA_List_Pool#? attributes. Unfortunately there's no
[AROS.git] / rom / filesys / fat / volume.c
blob6d472351096fba840ec903883b8cffeae8c1c6e0
1 /*
2 * fat-handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007-2015 The AROS Development Team
7 * This program is free software; you can redistribute it and/or modify it
8 * under the same terms as AROS itself.
10 * $Id$
13 #include <exec/types.h>
14 #include <devices/inputevent.h>
15 #include <dos/dos.h>
16 #include <dos/dosextens.h>
18 #include <proto/exec.h>
19 #include <proto/dos.h>
20 #include <proto/timer.h>
21 #include <clib/alib_protos.h>
23 #include <clib/macros.h>
25 #include <ctype.h>
27 #include "fat_fs.h"
28 #include "fat_protos.h"
30 #define DEBUG DEBUG_MISC
31 #include "debug.h"
33 static const UBYTE default_oem_name[] = "MSWIN4.1";
34 static const UBYTE default_filsystype[] = "FAT16 ";
36 static const ULONG fat16_cluster_thresholds[] =
38 8400,
39 32680,
40 262144,
41 524288,
42 1048576,
43 0xFFFFFFFF
46 static const ULONG fat32_cluster_thresholds[] =
48 16777216,
49 33554432,
50 67108864,
51 0xFFFFFFFF
54 /* 01-01-1981 */
55 static const struct DateStamp unset_date_limit =
57 1096,
62 #ifndef ID_BUSY
63 #define ID_BUSY 0x42555359
64 #endif
66 static LONG GetVolumeIdentity(struct FSSuper *sb,
67 struct VolumeIdentity *volume);
68 static void FreeFATSuper(struct FSSuper *s);
70 LONG ReadFATSuper(struct FSSuper *sb)
72 struct Globals *glob = sb->glob;
73 struct DosEnvec *de = BADDR(glob->fssm->fssm_Environ);
74 LONG err = 0, td_err;
75 ULONG bsize = de->de_SizeBlock * 4, total_sectors, id;
76 struct FATBootSector *boot;
77 struct FATEBPB *ebpb;
78 struct FATFSInfo *fsinfo;
79 BOOL invalid = FALSE;
80 ULONG end;
81 LONG i;
82 struct DirHandle dh;
83 struct DirEntry dir_entry;
84 APTR block_ref;
85 UBYTE *fat_block;
87 D(bug("[fat] reading boot sector\n"));
89 boot = AllocMem(bsize, MEMF_ANY);
90 if (!boot)
91 return ERROR_NO_FREE_STORE;
93 sb->first_device_sector =
94 de->de_BlocksPerTrack * de->de_Surfaces * de->de_LowCyl;
96 /* Get a preliminary total-sectors value so we don't risk going outside
97 * partition limits */
98 sb->total_sectors =
99 de->de_BlocksPerTrack * de->de_Surfaces * (de->de_HighCyl + 1)
100 - sb->first_device_sector;
102 D(bug("[fat] boot sector at sector %ld\n", sb->first_device_sector));
105 * Read the boot sector. We go direct because we don't have a cache yet,
106 * and can't create one until we know the sector size, which is held in
107 * the boot sector. In practice it doesn't matter - we're going to use
108 * this once and once only.
110 if ((td_err = AccessDisk(FALSE, sb->first_device_sector, 1, bsize,
111 (UBYTE *) boot, glob)) != 0)
113 D(bug("[fat] couldn't read boot block (%ld)\n", td_err));
114 FreeMem(boot, bsize);
115 return ERROR_UNKNOWN;
118 D(bug("\tBoot sector:\n"));
120 sb->sectorsize = AROS_LE2WORD(boot->bpb_bytes_per_sect);
121 sb->sectorsize_bits = log2(sb->sectorsize);
122 D(bug("\tSectorSize = %ld\n", sb->sectorsize));
123 D(bug("\tSectorSize Bits = %ld\n", sb->sectorsize_bits));
125 sb->cluster_sectors = boot->bpb_sect_per_clust;
126 sb->clustersize = sb->sectorsize * boot->bpb_sect_per_clust;
127 sb->clustersize_bits = log2(sb->clustersize);
128 sb->cluster_sectors_bits = sb->clustersize_bits - sb->sectorsize_bits;
130 D(bug("\tSectorsPerCluster = %ld\n", (ULONG) boot->bpb_sect_per_clust));
131 D(bug("\tClusterSize = %ld\n", sb->clustersize));
132 D(bug("\tClusterSize Bits = %ld\n", sb->clustersize_bits));
133 D(bug("\tCluster Sectors Bits = %ld\n", sb->cluster_sectors_bits));
135 sb->first_fat_sector = AROS_LE2WORD(boot->bpb_rsvd_sect_count);
136 D(bug("\tFirst FAT Sector = %ld\n", sb->first_fat_sector));
138 sb->fat_count = boot->bpb_num_fats;
139 D(bug("\tNumber of FATs = %d\n", sb->fat_count));
141 if (boot->bpb_fat_size_16 != 0)
142 sb->fat_size = AROS_LE2WORD(boot->bpb_fat_size_16);
143 else
144 sb->fat_size = AROS_LE2LONG(boot->ebpbs.ebpb32.bpb_fat_size_32);
145 D(bug("\tFAT Size = %ld\n", sb->fat_size));
147 if (boot->bpb_total_sectors_16 != 0)
148 total_sectors = AROS_LE2WORD(boot->bpb_total_sectors_16);
149 else
150 total_sectors = AROS_LE2LONG(boot->bpb_total_sectors_32);
151 D(bug("\tTotal Sectors = %ld\n", sb->total_sectors));
153 /* Check that the boot block's sector count is the same as the
154 * partition's sector count. This stops a resized partition being
155 * mounted before reformatting */
156 if (total_sectors != sb->total_sectors)
157 invalid = TRUE;
159 sb->rootdir_sectors = ((AROS_LE2WORD(boot->bpb_root_entries_count)
160 * sizeof(struct FATDirEntry)) + (sb->sectorsize - 1))
161 >> sb->sectorsize_bits;
162 D(bug("\tRootDir Sectors = %ld\n", sb->rootdir_sectors));
164 sb->data_sectors = sb->total_sectors - (sb->first_fat_sector
165 + (sb->fat_count * sb->fat_size) + sb->rootdir_sectors);
166 D(bug("\tData Sectors = %ld\n", sb->data_sectors));
168 sb->clusters_count = sb->data_sectors >> sb->cluster_sectors_bits;
169 D(bug("\tClusters Count = %ld\n", sb->clusters_count));
171 sb->first_rootdir_sector =
172 sb->first_fat_sector + (sb->fat_count * sb->fat_size);
173 D(bug("\tFirst RootDir Sector = %ld\n", sb->first_rootdir_sector));
175 sb->first_data_sector =
176 sb->first_fat_sector + (sb->fat_count * sb->fat_size)
177 + sb->rootdir_sectors;
178 D(bug("\tFirst Data Sector = %ld\n", sb->first_data_sector));
180 /* Check if disk is in fact a FAT filesystem */
182 /* Valid sector size: 512, 1024, 2048, 4096 */
183 if (sb->sectorsize != 512 && sb->sectorsize != 1024
184 && sb->sectorsize != 2048 && sb->sectorsize != 4096)
185 invalid = TRUE;
187 /* Valid bpb_sect_per_clust: 1, 2, 4, 8, 16, 32, 64, 128 */
188 if ((boot->bpb_sect_per_clust & (boot->bpb_sect_per_clust - 1)) != 0
189 || boot->bpb_sect_per_clust == 0 || boot->bpb_sect_per_clust > 128)
190 invalid = TRUE;
192 /* Valid cluster size: 512, 1024, 2048, 4096, 8192, 16k, 32k, 64k */
193 if (sb->clustersize > 64 * 1024)
194 invalid = TRUE;
196 if (sb->first_fat_sector == 0)
197 invalid = TRUE;
199 if (sb->fat_count == 0)
200 invalid = TRUE;
202 if (boot->bpb_media < 0xF0)
203 invalid = TRUE;
205 /* FAT "signature" */
206 if (boot->bpb_signature[0] != 0x55 || boot->bpb_signature[1] != 0xaa)
207 invalid = TRUE;
209 if (invalid)
211 D(bug("\tInvalid FAT Boot Sector\n"));
212 FreeMem(boot, bsize);
213 return ERROR_NOT_A_DOS_DISK;
216 end = 0xFFFFFFFF / sb->sectorsize;
217 if ((sb->first_device_sector + sb->total_sectors - 1 > end)
218 && (glob->readcmd == CMD_READ))
220 D(bug("\tDevice is too large\n"));
221 ErrorMessage("Your device driver does not support 64-bit\n"
222 "disk addressing, but it is needed to access\n"
223 "the volume in device %s.\n\n"
224 "In order to prevent data damage, access to\n"
225 "this volume was blocked. Please upgrade\n"
226 "your device driver.", "OK",
227 (IPTR)(AROS_BSTR_ADDR(glob->devnode->dol_Name)));
228 FreeMem(boot, bsize);
229 return ERROR_UNKNOWN;
232 sb->cache = Cache_CreateCache(glob, 64, 64, sb->sectorsize, SysBase,
233 DOSBase);
234 if (sb->cache == NULL)
236 err = IoErr();
237 FreeMem(boot, bsize);
238 return err;
241 if (sb->clusters_count < 4085)
243 D(bug("\tFAT12 filesystem detected\n"));
244 sb->type = 12;
245 sb->eoc_mark = 0x0FFF;
246 sb->func_get_fat_entry = GetFat12Entry;
247 sb->func_set_fat_entry = SetFat12Entry;
249 else if (sb->clusters_count < 65525)
251 D(bug("\tFAT16 filesystem detected\n"));
252 sb->type = 16;
253 sb->eoc_mark = 0xFFFF;
254 sb->func_get_fat_entry = GetFat16Entry;
255 sb->func_set_fat_entry = SetFat16Entry;
257 else
259 D(bug("\tFAT32 filesystem detected\n"));
260 sb->type = 32;
261 sb->eoc_mark = 0x0FFFFFFF;
262 sb->func_get_fat_entry = GetFat32Entry;
263 sb->func_set_fat_entry = SetFat32Entry;
266 /* Set up the FAT cache and load the first blocks */
267 sb->fat_cachesize = 4096;
268 sb->fat_cachesize_bits = log2(sb->fat_cachesize);
269 sb->fat_cache_block = 0xffffffff;
271 sb->fat_blocks_count =
272 MIN(sb->fat_size, sb->fat_cachesize >> sb->sectorsize_bits);
273 sb->fat_blocks = AllocVecPooled(glob->mempool,
274 sizeof(APTR) * sb->fat_blocks_count);
275 sb->fat_buffers = AllocVecPooled(glob->mempool,
276 sizeof(APTR) * sb->fat_blocks_count);
277 if (sb->fat_blocks == NULL || sb->fat_buffers == NULL)
279 err = ERROR_NO_FREE_STORE;
280 FreeVecPooled(glob->mempool, sb->fat_blocks);
281 FreeVecPooled(glob->mempool, sb->fat_buffers);
282 FreeMem(boot, bsize);
283 return err;
285 glob->sb = sb;
287 if (sb->type != 32)
289 /* Set up volume ID */
290 sb->volume_id = AROS_LE2LONG(boot->ebpbs.ebpb.bs_volid);
292 /* Location of root directory */
293 sb->rootdir_cluster = 0;
294 sb->rootdir_sector = sb->first_rootdir_sector;
295 ebpb = &boot->ebpbs.ebpb;
297 else
299 /* Set up volume ID */
300 sb->volume_id = AROS_LE2LONG(boot->ebpbs.ebpb32.ebpb.bs_volid);
302 /* Location of root directory */
303 sb->rootdir_cluster =
304 AROS_LE2LONG(boot->ebpbs.ebpb32.bpb_root_cluster);
305 sb->rootdir_sector = 0;
306 ebpb = &boot->ebpbs.ebpb32.ebpb;
309 D(bug("[fat] rootdir at cluster %ld sector %ld\n", sb->rootdir_cluster,
310 sb->rootdir_sector));
312 /* Initialise the root directory if this is a newly formatted volume */
313 if (glob->formatting)
315 /* Clear all FAT sectors */
316 for (i = 0; err == 0 && i < sb->fat_size * 2; i++)
318 block_ref = Cache_GetBlock(sb->cache,
319 sb->first_device_sector + sb->first_fat_sector + i,
320 &fat_block);
321 if (block_ref != NULL)
323 /* FIXME: Handle IO errors on cache read! */
324 memset(fat_block, 0, bsize);
325 if (i == 0)
327 /* The first two entries are special */
328 if (sb->type == 32)
329 *(UQUAD *) fat_block =
330 AROS_QUAD2LE(0x0FFFFFFF0FFFFFF8);
331 else if (sb->type == 16)
332 *(ULONG *) fat_block = AROS_LONG2LE(0xFFFFFFF8);
333 else
334 *(ULONG *) fat_block = AROS_LONG2LE(0x00FFFFF8);
336 Cache_MarkBlockDirty(sb->cache, block_ref);
337 Cache_FreeBlock(sb->cache, block_ref);
339 else
340 err = IoErr();
343 if (err == 0)
345 /* Allocate first cluster of the root directory */
346 if (sb->type == 32)
347 AllocCluster(sb, sb->rootdir_cluster);
349 /* Get a handle on the root directory */
350 InitDirHandle(sb, 0, &dh, FALSE, glob);
353 /* Clear all entries in the root directory */
354 for (i = 0; err == 0 && GetDirEntry(&dh, i, &dir_entry, glob) == 0;
355 i++)
357 memset(&dir_entry.e.entry, 0, sizeof(struct FATDirEntry));
358 err = UpdateDirEntry(&dir_entry, glob);
361 if (err == 0)
363 err = SetVolumeName(sb, ebpb->bs_vollab, FAT_MAX_SHORT_NAME);
365 ReleaseDirHandle(&dh, glob);
366 glob->formatting = FALSE;
367 D(bug("\tRoot dir created.\n"));
370 if (err == 0)
372 /* Check everything is really written to disk before we proceed */
373 if (!Cache_Flush(sb->cache))
374 err = IoErr();
378 if (GetVolumeIdentity(sb, &(sb->volume)) != 0)
380 LONG i;
381 UBYTE *uu = (void *)&sb->volume_id;
383 /* No volume name entry, so construct name from serial number */
384 for (i = 1; i < 10;)
386 int d;
388 if (i == 5)
389 sb->volume.name[i++] = '-';
391 d = (*uu) & 0x0f;
392 sb->volume.name[i++] = (d < 10) ? '0' + d : 'A' - 10 + d;
393 d = ((*uu) & 0xf0) >> 4;
394 sb->volume.name[i++] = (d < 10) ? '0' + d : 'A' - 10 + d;
396 uu++;
399 sb->volume.name[i] = '\0';
400 sb->volume.name[0] = 9;
403 /* Many FAT volumes do not have a creation date set, with the result
404 * that two volumes with the same name are likely to be indistinguishable
405 * on the DOS list. To work around this problem, we set the ds_Tick field
406 * of such volumes' dol_VolumeDate timestamp to a pseudo-random value based
407 * on the serial number. Since there are 3000 ticks in a minute, we use an
408 * 11-bit hash value in the range 0 to 2047.
410 if (CompareDates(&sb->volume.create_time, &unset_date_limit) > 0)
412 id = sb->volume_id;
413 sb->volume.create_time.ds_Days = 0;
414 sb->volume.create_time.ds_Minute = 0;
415 sb->volume.create_time.ds_Tick = (id >> 22 ^ id >> 11 ^ id) & 0x7FF;
416 D(bug("[fat] Set hash time to %ld ticks\n",
417 sb->volume.create_time.ds_Tick));
420 /* Get initial number of free clusters */
421 sb->free_clusters = -1;
422 sb->next_cluster = -1;
423 if (sb->type == 32)
425 sb->fsinfo_block = Cache_GetBlock(sb->cache, sb->first_device_sector
426 + AROS_LE2WORD(boot->ebpbs.ebpb32.bpb_fs_info),
427 (UBYTE **) &fsinfo);
428 if (sb->fsinfo_block != NULL)
430 if (fsinfo->lead_sig == AROS_LONG2LE(FSI_LEAD_SIG)
431 && fsinfo->struct_sig == AROS_LONG2LE(FSI_STRUCT_SIG)
432 && fsinfo->trail_sig == AROS_LONG2LE(FSI_TRAIL_SIG))
434 sb->free_clusters = AROS_LE2LONG(fsinfo->free_count);
435 sb->next_cluster = AROS_LE2LONG(fsinfo->next_free);
436 D(bug("[fat] valid FATFSInfo block found\n"));
437 sb->fsinfo_buffer = fsinfo;
439 else
440 Cache_FreeBlock(sb->cache, sb->fsinfo_block);
443 if (sb->free_clusters == -1)
444 CountFreeClusters(sb);
445 if (sb->next_cluster == -1)
446 sb->next_cluster = 2;
448 FreeMem(boot, bsize);
449 if (err != 0)
450 FreeFATSuper(sb);
451 else
453 D(bug("\tFAT Filesystem successfully detected.\n"));
454 D(bug("\tFree Clusters = %ld\n", sb->free_clusters));
455 D(bug("\tNext Free Cluster = %ld\n", sb->next_cluster));
458 return err;
461 static LONG GetVolumeIdentity(struct FSSuper *sb,
462 struct VolumeIdentity *volume)
464 struct Globals *glob = sb->glob;
465 struct DirHandle dh;
466 struct DirEntry de;
467 LONG err;
468 int i;
470 D(bug("[fat] searching root directory for volume name\n"));
472 /* Search the directory for the volume ID entry. It would've been nice to
473 * just use GetNextDirEntry but I didn't want a flag or something to tell
474 * it not to skip the volume name */
475 InitDirHandle(sb, sb->rootdir_cluster, &dh, FALSE, glob);
477 while ((err = GetDirEntry(&dh, dh.cur_index + 1, &de, glob)) == 0)
480 /* Match the volume ID entry */
481 if ((de.e.entry.attr & ATTR_VOLUME_ID_MASK) == ATTR_VOLUME_ID
482 && de.e.entry.name[0] != 0xe5)
484 D(bug("[fat] found volume id entry %ld\n", dh.cur_index));
486 /* Copy the name in. 'volume->name' is a BSTR */
487 volume->name[1] = de.e.entry.name[0];
488 for (i = 1; i < FAT_MAX_SHORT_NAME; i++)
490 if (volume->name[i] == ' ')
491 volume->name[i + 1] = de.e.entry.name[i];
492 else
493 volume->name[i + 1] = tolower(de.e.entry.name[i]);
496 for (i = 10; volume->name[i + 1] == ' '; i--);
497 volume->name[i + 2] = '\0';
498 volume->name[0] = strlen(&(volume->name[1]));
500 /* Get the volume creation date too */
501 ConvertFATDate(de.e.entry.create_date, de.e.entry.create_time,
502 &volume->create_time, glob);
504 D(bug("[fat] volume name is '%s'\n", &(volume->name[1])));
506 break;
509 /* Bail out if we hit the end of the dir */
510 if (de.e.entry.name[0] == 0x00)
512 D(bug("[fat] found end-of-directory marker,"
513 " volume name entry not found\n"));
514 err = ERROR_OBJECT_NOT_FOUND;
515 break;
519 ReleaseDirHandle(&dh, glob);
520 return err;
523 LONG FormatFATVolume(const UBYTE *name, UWORD len, struct Globals *glob)
525 struct DosEnvec *de = BADDR(glob->fssm->fssm_Environ);
526 LONG td_err;
527 ULONG bsize = de->de_SizeBlock * 4;
528 struct FATBootSector *boot;
529 struct FATEBPB *ebpb;
530 struct FATFSInfo *fsinfo;
531 UWORD type, i, root_entries_count;
532 struct EClockVal eclock;
533 ULONG sectors_per_cluster = 0, sector_count, first_fat_sector,
534 fat_size, root_dir_sectors, first_device_sector, temp1, temp2;
536 /* Decide on FAT type based on number of sectors */
537 sector_count = (de->de_HighCyl - de->de_LowCyl + 1)
538 * de->de_Surfaces * de->de_BlocksPerTrack;
539 if (sector_count < 4085)
540 type = 12;
541 else if (sector_count < 1024 * 1024)
542 type = 16;
543 else
544 type = 32;
546 D(bug("[fat] writing boot sector\n"));
548 /* Decide on cluster size and root dir entries */
549 first_fat_sector = 1;
550 if (type == 12)
552 if (sector_count == 1440)
554 sectors_per_cluster = 2;
555 root_entries_count = 112;
557 else if (sector_count == 2880)
559 sectors_per_cluster = 1;
560 root_entries_count = 224;
562 else if (sector_count == 5760)
564 sectors_per_cluster = 2;
565 root_entries_count = 240;
567 else
569 /* We only support some common 3.5" floppy formats */
570 return ERROR_NOT_IMPLEMENTED;
573 else if (type == 16)
575 for (i = 0; fat16_cluster_thresholds[i] < sector_count; i++);
576 sectors_per_cluster = 1 << i;
577 root_entries_count = 512;
579 else
581 for (i = 0; fat32_cluster_thresholds[i] < sector_count; i++);
582 sectors_per_cluster = 8 << i;
583 root_entries_count = 0;
584 first_fat_sector = 32;
587 D(bug("\tFirst FAT Sector = %ld\n", first_fat_sector));
589 /* Determine FAT size */
590 root_dir_sectors = (root_entries_count * 32 + (bsize - 1)) / bsize;
591 temp1 = sector_count - (first_fat_sector + root_dir_sectors);
592 temp2 = 256 * sectors_per_cluster + 2;
593 if (type == 32)
594 temp2 /= 2;
595 fat_size = (temp1 + temp2 - 1) / temp2;
597 boot = AllocMem(bsize, MEMF_CLEAR);
598 if (!boot)
599 return ERROR_NO_FREE_STORE;
601 /* Install x86 infinite loop boot code to keep major OSes happy */
602 boot->bs_jmp_boot[0] = 0xEB;
603 boot->bs_jmp_boot[1] = 0xFE;
604 boot->bs_jmp_boot[2] = 0x90;
606 CopyMem(default_oem_name, boot->bs_oem_name, 8);
608 boot->bpb_bytes_per_sect = AROS_WORD2LE(bsize);
609 boot->bpb_sect_per_clust = sectors_per_cluster;
611 boot->bpb_rsvd_sect_count = AROS_WORD2LE(first_fat_sector);
613 boot->bpb_num_fats = 2;
615 boot->bpb_root_entries_count = AROS_WORD2LE(root_entries_count);
617 if (sector_count < 0x10000 && type != 32)
618 boot->bpb_total_sectors_16 = AROS_WORD2LE(sector_count);
619 else
620 boot->bpb_total_sectors_32 = AROS_LONG2LE(sector_count);
622 boot->bpb_media = 0xF8;
624 boot->bpb_sect_per_track = AROS_WORD2LE(de->de_BlocksPerTrack);
625 boot->bpb_num_heads = AROS_WORD2LE(de->de_Surfaces);
626 boot->bpb_hidden_sect = AROS_LONG2LE(de->de_Reserved);
628 if (type == 32)
630 boot->ebpbs.ebpb32.bpb_fat_size_32 = AROS_LONG2LE(fat_size);
631 boot->ebpbs.ebpb32.bpb_root_cluster = AROS_LONG2LE(2);
632 boot->ebpbs.ebpb32.bpb_fs_info = AROS_WORD2LE(1);
633 boot->ebpbs.ebpb32.bpb_back_bootsec = AROS_WORD2LE(6);
634 ebpb = &boot->ebpbs.ebpb32.ebpb;
636 else
638 boot->bpb_fat_size_16 = AROS_WORD2LE(fat_size);
639 ebpb = &boot->ebpbs.ebpb;
642 ebpb->bs_drvnum = 0x80;
643 ebpb->bs_bootsig = 0x29;
645 /* Generate a pseudo-random serial number. Not the original algorithm,
646 * but it shouldn't matter */
647 ReadEClock(&eclock);
648 ebpb->bs_volid = FastRand(eclock.ev_lo ^ eclock.ev_hi);
650 /* Copy volume name in */
651 for (i = 0; i < FAT_MAX_SHORT_NAME; i++)
652 if (i < len)
653 ebpb->bs_vollab[i] = toupper(name[i]);
654 else
655 ebpb->bs_vollab[i] = ' ';
657 CopyMem(default_filsystype, ebpb->bs_filsystype, 8);
658 if (type != 16)
660 if (type == 32)
661 ebpb->bs_filsystype[3] = '3';
662 ebpb->bs_filsystype[4] = '2';
665 boot->bpb_signature[0] = 0x55;
666 boot->bpb_signature[1] = 0xaa;
668 /* Write the boot sector */
669 first_device_sector =
670 de->de_BlocksPerTrack * de->de_Surfaces * de->de_LowCyl;
672 D(bug("[fat] boot sector at sector %ld\n", first_device_sector));
674 if ((td_err = AccessDisk(TRUE, first_device_sector, 1, bsize,
675 (UBYTE *) boot, glob)) != 0)
677 D(bug("[fat] couldn't write boot block (%ld)\n", td_err));
678 FreeMem(boot, bsize);
679 return ERROR_UNKNOWN;
682 /* Write back-up boot sector and FS info sector */
683 if (type == 32)
685 if ((td_err = AccessDisk(TRUE, first_device_sector + 6, 1, bsize,
686 (UBYTE *) boot, glob)) != 0)
688 D(bug("[fat] couldn't write back-up boot block (%ld)\n", td_err));
689 FreeMem(boot, bsize);
690 return ERROR_UNKNOWN;
693 fsinfo = (APTR) boot;
694 memset(fsinfo, 0, bsize);
696 fsinfo->lead_sig = AROS_LONG2LE(FSI_LEAD_SIG);
697 fsinfo->struct_sig = AROS_LONG2LE(FSI_STRUCT_SIG);
698 fsinfo->trail_sig = AROS_LONG2LE(FSI_TRAIL_SIG);
699 fsinfo->free_count = AROS_LONG2LE(0xFFFFFFFF);
700 fsinfo->next_free = AROS_LONG2LE(0xFFFFFFFF);
702 if ((td_err = AccessDisk(TRUE, first_device_sector + 1, 1, bsize,
703 (UBYTE *) fsinfo, glob)) != 0)
705 D(bug("[fat] couldn't write back-up boot block (%ld)\n", td_err));
706 FreeMem(boot, bsize);
707 return ERROR_UNKNOWN;
711 FreeMem(boot, bsize);
713 glob->formatting = TRUE;
715 return 0;
718 LONG SetVolumeName(struct FSSuper *sb, UBYTE *name, UWORD len)
720 struct Globals *glob = sb->glob;
721 struct DirHandle dh;
722 struct DirEntry de;
723 LONG err, td_err;
724 int i;
725 struct DosEnvec *dosenv = BADDR(glob->fssm->fssm_Environ);
726 ULONG bsize = dosenv->de_SizeBlock * 4;
727 struct FATBootSector *boot;
729 /* Truncate name if necessary */
730 if (len > FAT_MAX_SHORT_NAME)
731 len = FAT_MAX_SHORT_NAME;
733 /* Read boot block */
734 boot = AllocMem(bsize, MEMF_ANY);
735 if (!boot)
736 return ERROR_NO_FREE_STORE;
738 if ((td_err = AccessDisk(FALSE, sb->first_device_sector, 1, bsize,
739 (UBYTE *) boot, glob)) != 0)
741 D(bug("[fat] couldn't read boot block (%ld)\n", td_err));
742 FreeMem(boot, bsize);
743 return ERROR_UNKNOWN;
746 D(bug("[fat] searching root directory for volume name\n"));
748 /* Search the directory for the volume ID entry. It would've been nice to
749 * just use GetNextDirEntry but I didn't want a flag or something to tell
750 * it not to skip the volume name */
751 InitDirHandle(sb, 0, &dh, FALSE, glob);
753 while ((err = GetDirEntry(&dh, dh.cur_index + 1, &de, glob)) == 0)
756 /* Match the volume ID entry */
757 if ((de.e.entry.attr & ATTR_VOLUME_ID_MASK) == ATTR_VOLUME_ID
758 && de.e.entry.name[0] != 0xe5)
760 D(bug("[fat] found volume id entry %ld\n", dh.cur_index));
761 err = 0;
762 break;
765 /* Bail out if we hit the end of the dir */
766 if (de.e.entry.name[0] == 0x00)
768 D(bug("[fat] found end-of-directory marker,"
769 " volume name entry not found\n"));
770 err = ERROR_OBJECT_NOT_FOUND;
771 break;
775 /* Create a new volume ID entry if there wasn't one */
776 if (err != 0)
778 err = AllocDirEntry(&dh, 0, &de, glob);
779 if (err == 0)
780 FillDirEntry(&de, ATTR_VOLUME_ID, 0, glob);
783 /* Copy the name in */
784 if (err == 0)
786 for (i = 0; i < FAT_MAX_SHORT_NAME; i++)
787 if (i < len)
788 de.e.entry.name[i] = toupper(name[i]);
789 else
790 de.e.entry.name[i] = ' ';
792 if ((err = UpdateDirEntry(&de, glob)) != 0)
794 D(bug("[fat] couldn't change volume name\n"));
795 return err;
799 /* Copy name to boot block as well, and save */
800 if (sb->type == 32)
801 CopyMem(de.e.entry.name, boot->ebpbs.ebpb32.ebpb.bs_vollab,
802 FAT_MAX_SHORT_NAME);
803 else
804 CopyMem(de.e.entry.name, boot->ebpbs.ebpb.bs_vollab,
805 FAT_MAX_SHORT_NAME);
807 if ((td_err = AccessDisk(TRUE, sb->first_device_sector, 1, bsize,
808 (UBYTE *) boot, glob)) != 0)
809 D(bug("[fat] couldn't write boot block (%ld)\n", td_err));
810 FreeMem(boot, bsize);
812 /* Update name in SB */
813 sb->volume.name[0] = len;
814 sb->volume.name[1] = toupper(name[0]);
815 for (i = 1; i < len; i++)
816 sb->volume.name[i + 1] = tolower(name[i]);
817 sb->volume.name[len + 1] = '\0';
819 D(bug("[fat] new volume name is '%s'\n", &(sb->volume.name[1])));
821 ReleaseDirHandle(&dh, glob);
822 return err;
825 static void FreeFATSuper(struct FSSuper *sb)
827 struct Globals *glob = sb->glob;
828 D(bug("\tRemoving Super Block from memory\n"));
829 Cache_DestroyCache(sb->cache);
830 FreeVecPooled(glob->mempool, sb->fat_buffers);
831 sb->fat_buffers = NULL;
832 FreeVecPooled(glob->mempool, sb->fat_blocks);
833 sb->fat_blocks = NULL;
836 void FillDiskInfo(struct InfoData *id, struct Globals *glob)
838 struct DosEnvec *de = BADDR(glob->fssm->fssm_Environ);
840 id->id_NumSoftErrors = 0;
841 id->id_UnitNumber = glob->fssm->fssm_Unit;
842 id->id_DiskState = ID_VALIDATED;
844 if (glob->sb)
846 id->id_NumBlocks = glob->sb->total_sectors;
847 id->id_NumBlocksUsed = glob->sb->total_sectors
848 - (glob->sb->free_clusters << glob->sb->cluster_sectors_bits);
849 id->id_BytesPerBlock = glob->sb->sectorsize;
851 id->id_DiskType = ID_DOS_DISK;
853 id->id_VolumeNode = MKBADDR(glob->sb->doslist);
854 id->id_InUse = (IsListEmpty(&glob->sb->info->locks)
855 && IsListEmpty(&glob->sb->info->notifies)) ? DOSFALSE : DOSTRUE;
858 else
860 id->id_NumBlocks = de->de_Surfaces * de->de_BlocksPerTrack
861 * (de->de_HighCyl + 1 - de->de_LowCyl) / de->de_SectorPerBlock;
862 id->id_NumBlocksUsed = id->id_NumBlocks;
863 id->id_BytesPerBlock = de->de_SizeBlock << 2;
865 id->id_DiskState = ID_VALIDATED;
867 if (glob->disk_inhibited != 0)
868 id->id_DiskType = ID_BUSY;
869 else if (glob->disk_inserted)
870 id->id_DiskType = ID_NOT_REALLY_DOS; //ID_UNREADABLE_DISK;
871 else
872 id->id_DiskType = ID_NO_DISK_PRESENT;
874 id->id_VolumeNode = BNULL;
875 id->id_InUse = DOSFALSE;
879 static void SendVolumePacket(struct DosList *vol, ULONG action,
880 struct Globals *glob)
882 struct DosPacket *dospacket;
884 dospacket = AllocDosObject(DOS_STDPKT, TAG_DONE);
885 dospacket->dp_Type = ACTION_DISK_CHANGE;
886 dospacket->dp_Arg1 = ID_FAT_DISK;
887 dospacket->dp_Arg2 = (IPTR) vol;
888 dospacket->dp_Arg3 = action;
889 dospacket->dp_Port = NULL;
891 PutMsg(glob->ourport, dospacket->dp_Link);
894 void DoDiskInsert(struct Globals *glob)
896 struct FSSuper *sb;
897 ULONG err;
898 struct DosList *dl;
899 struct VolumeInfo *vol_info = NULL;
900 struct GlobalLock *global_lock;
901 struct ExtFileLock *ext_lock;
902 struct MinNode *lock_node;
903 APTR pool;
904 struct NotifyNode *nn;
905 struct DosList *newvol = NULL;
907 if (glob->sb == NULL
908 && (sb = AllocVecPooled(glob->mempool, sizeof(struct FSSuper))))
910 memset(sb, 0, sizeof(struct FSSuper));
912 sb->glob = glob;
913 err = ReadFATSuper(sb);
914 if (err == 0)
917 /* Scan volume list for a matching volume (would be better to
918 * match by serial number) */
919 dl = LockDosList(LDF_VOLUMES | LDF_WRITE);
920 dl = FindDosEntry(dl, sb->volume.name + 1,
921 LDF_VOLUMES | LDF_WRITE);
922 UnLockDosList(LDF_VOLUMES | LDF_WRITE);
924 if (dl != NULL &&
925 CompareDates(&dl->dol_misc.dol_volume.dol_VolumeDate,
926 &sb->volume.create_time) != 0)
927 dl = NULL;
929 if (dl != NULL)
931 dl->dol_Task = glob->ourport;
932 sb->doslist = dl;
934 D(bug("\tFound old volume.\n"));
936 vol_info = BADDR(dl->dol_misc.dol_volume.dol_LockList);
938 #if 0 /* No point until we match volumes by serial number */
939 /* Update name */
940 #ifdef AROS_FAST_BPTR
941 /* ReadFATSuper() sets a null byte after the
942 * string, so this should be fine */
943 CopyMem(sb->volume.name + 1, dl->dol_Name,
944 sb->volume.name[0] + 1);
945 #else
946 CopyMem(sb->volume.name, dl->dol_Name,
947 sb->volume.name[0] + 2);
948 #endif
949 #endif
951 /* Patch locks and notifications to match this handler
952 * instance */
953 ForeachNode(&vol_info->locks, global_lock)
955 ForeachNode(&global_lock->locks, lock_node)
957 ext_lock = LOCKFROMNODE(lock_node);
958 D(bug("[fat] Patching adopted lock %p. old port = %p,"
959 " new port = %p\n", ext_lock,
960 ext_lock->fl_Task, glob->ourport));
961 ext_lock->fl_Task = glob->ourport;
962 ext_lock->sb = sb;
963 ext_lock->ioh.sb = sb;
967 ForeachNode(&vol_info->root_lock.locks, lock_node)
969 ext_lock = LOCKFROMNODE(lock_node);
970 D(bug("[fat] Patching adopted ROOT lock %p. old port = %p,"
971 " new port = %p\n", ext_lock, ext_lock->fl_Task,
972 glob->ourport));
973 ext_lock->fl_Task = glob->ourport;
974 ext_lock->sb = sb;
975 ext_lock->ioh.sb = sb;
978 ForeachNode(&vol_info->notifies, nn)
979 nn->nr->nr_Handler = glob->ourport;
981 else
983 D(bug("\tCreating new volume.\n"));
985 /* Create transferable core volume info */
986 pool =
987 CreatePool(MEMF_PUBLIC, DEF_POOL_SIZE,
988 DEF_POOL_THRESHOLD);
989 if (pool != NULL)
991 vol_info =
992 AllocVecPooled(pool, sizeof(struct VolumeInfo));
993 if (vol_info != NULL)
995 vol_info->mem_pool = pool;
996 vol_info->id = sb->volume_id;
997 NEWLIST(&vol_info->locks);
998 NEWLIST(&vol_info->notifies);
1000 vol_info->root_lock.dir_cluster = FAT_ROOTDIR_MARK;
1001 vol_info->root_lock.dir_entry = FAT_ROOTDIR_MARK;
1002 vol_info->root_lock.access = SHARED_LOCK;
1003 vol_info->root_lock.first_cluster = 0;
1004 vol_info->root_lock.attr = ATTR_DIRECTORY;
1005 vol_info->root_lock.size = 0;
1006 CopyMem(sb->volume.name, vol_info->root_lock.name,
1007 sb->volume.name[0] + 1);
1008 NEWLIST(&vol_info->root_lock.locks);
1011 if ((newvol =
1012 AllocVecPooled(pool, sizeof(struct DosList))))
1014 newvol->dol_Next = BNULL;
1015 newvol->dol_Type = DLT_VOLUME;
1016 newvol->dol_Task = glob->ourport;
1017 newvol->dol_Lock = BNULL;
1019 CopyMem(&sb->volume.create_time,
1020 &newvol->dol_misc.dol_volume.dol_VolumeDate,
1021 sizeof(struct DateStamp));
1023 newvol->dol_misc.dol_volume.dol_LockList =
1024 MKBADDR(vol_info);
1026 newvol->dol_misc.dol_volume.dol_DiskType =
1027 (sb->type == 12) ? ID_FAT12_DISK :
1028 (sb->type == 16) ? ID_FAT16_DISK :
1029 (sb->type == 32) ? ID_FAT32_DISK :
1030 ID_FAT12_DISK;
1032 if ((newvol->dol_Name = MKBADDR(
1033 AllocVecPooled(pool, FAT_MAX_SHORT_NAME + 2))))
1035 #ifdef AROS_FAST_BPTR
1036 /* ReadFATSuper() sets a null byte after the
1037 * string, so this should be fine */
1038 CopyMem(sb->volume.name + 1, newvol->dol_Name,
1039 sb->volume.name[0] + 1);
1040 #else
1041 CopyMem(sb->volume.name,
1042 BADDR(newvol->dol_Name),
1043 sb->volume.name[0] + 2);
1044 #endif
1046 sb->doslist = newvol;
1049 if (vol_info == NULL || newvol == NULL)
1050 DeletePool(pool);
1054 sb->info = vol_info;
1055 glob->last_num = -1;
1057 if (dl != NULL)
1058 SendEvent(IECLASS_DISKINSERTED, glob);
1059 else
1060 SendVolumePacket(newvol, ACTION_VOLUME_ADD, glob);
1062 D(bug("\tDisk successfully initialised\n"));
1064 return;
1067 FreeVecPooled(glob->mempool, sb);
1070 SendEvent(IECLASS_DISKINSERTED, glob);
1072 return;
1075 BOOL AttemptDestroyVolume(struct FSSuper *sb)
1077 struct Globals *glob = sb->glob;
1078 BOOL destroyed = FALSE;
1080 D(bug("[fat] Attempting to destroy volume\n"));
1082 /* Check if the volume can be removed */
1083 if (IsListEmpty(&sb->info->locks) && IsListEmpty(&sb->info->notifies))
1085 D(bug("\tRemoving volume completely\n"));
1087 if (sb == glob->sb)
1088 glob->sb = NULL;
1089 else
1090 Remove((struct Node *)sb);
1092 SendVolumePacket(sb->doslist, ACTION_VOLUME_REMOVE, glob);
1094 FreeFATSuper(sb);
1095 FreeVecPooled(glob->mempool, sb);
1096 destroyed = TRUE;
1099 return destroyed;
1102 void DoDiskRemove(struct Globals *glob)
1105 if (glob->sb)
1107 struct FSSuper *sb = glob->sb;
1109 if (!AttemptDestroyVolume(sb))
1111 sb->doslist->dol_Task = NULL;
1112 glob->sb = NULL;
1113 D(bug("\tMoved in-memory super block to spare list. "
1114 "Waiting for locks and notifications to be freed\n"));
1115 AddTail((struct List *)&glob->sblist, (struct Node *)sb);
1116 SendEvent(IECLASS_DISKREMOVED, glob);