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.
13 #include <exec/types.h>
14 #include <devices/inputevent.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>
28 #include "fat_protos.h"
30 #define DEBUG DEBUG_MISC
33 static const UBYTE default_oem_name
[] = "MSWIN4.1";
34 static const UBYTE default_filsystype
[] = "FAT16 ";
36 static const ULONG fat16_cluster_thresholds
[] =
46 static const ULONG fat32_cluster_thresholds
[] =
55 static const struct DateStamp unset_date_limit
=
63 #define ID_BUSY 0x42555359
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
);
75 ULONG bsize
= de
->de_SizeBlock
* 4, total_sectors
, id
;
76 struct FATBootSector
*boot
;
78 struct FATFSInfo
*fsinfo
;
83 struct DirEntry dir_entry
;
87 D(bug("[fat] reading boot sector\n"));
89 boot
= AllocMem(bsize
, MEMF_ANY
);
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
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
);
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
);
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
)
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)
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)
192 /* Valid cluster size: 512, 1024, 2048, 4096, 8192, 16k, 32k, 64k */
193 if (sb
->clustersize
> 64 * 1024)
196 if (sb
->first_fat_sector
== 0)
199 if (sb
->fat_count
== 0)
202 if (boot
->bpb_media
< 0xF0)
205 /* FAT "signature" */
206 if (boot
->bpb_signature
[0] != 0x55 || boot
->bpb_signature
[1] != 0xaa)
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
,
234 if (sb
->cache
== NULL
)
237 FreeMem(boot
, bsize
);
241 if (sb
->clusters_count
< 4085)
243 D(bug("\tFAT12 filesystem detected\n"));
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"));
253 sb
->eoc_mark
= 0xFFFF;
254 sb
->func_get_fat_entry
= GetFat16Entry
;
255 sb
->func_set_fat_entry
= SetFat16Entry
;
259 D(bug("\tFAT32 filesystem detected\n"));
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
);
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
;
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
,
321 if (block_ref
!= NULL
)
323 /* FIXME: Handle IO errors on cache read! */
324 memset(fat_block
, 0, bsize
);
327 /* The first two entries are special */
329 *(UQUAD
*) fat_block
=
330 AROS_QUAD2LE(0x0FFFFFFF0FFFFFF8);
331 else if (sb
->type
== 16)
332 *(ULONG
*) fat_block
= AROS_LONG2LE(0xFFFFFFF8);
334 *(ULONG
*) fat_block
= AROS_LONG2LE(0x00FFFFF8);
336 Cache_MarkBlockDirty(sb
->cache
, block_ref
);
337 Cache_FreeBlock(sb
->cache
, block_ref
);
345 /* Allocate first cluster of the root directory */
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;
357 memset(&dir_entry
.e
.entry
, 0, sizeof(struct FATDirEntry
));
358 err
= UpdateDirEntry(&dir_entry
, glob
);
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"));
372 /* Check everything is really written to disk before we proceed */
373 if (!Cache_Flush(sb
->cache
))
378 if (GetVolumeIdentity(sb
, &(sb
->volume
)) != 0)
381 UBYTE
*uu
= (void *)&sb
->volume_id
;
383 /* No volume name entry, so construct name from serial number */
389 sb
->volume
.name
[i
++] = '-';
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
;
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)
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;
425 sb
->fsinfo_block
= Cache_GetBlock(sb
->cache
, sb
->first_device_sector
426 + AROS_LE2WORD(boot
->ebpbs
.ebpb32
.bpb_fs_info
),
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
;
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
);
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
));
461 static LONG
GetVolumeIdentity(struct FSSuper
*sb
,
462 struct VolumeIdentity
*volume
)
464 struct Globals
*glob
= sb
->glob
;
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
];
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])));
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
;
519 ReleaseDirHandle(&dh
, glob
);
523 LONG
FormatFATVolume(const UBYTE
*name
, UWORD len
, struct Globals
*glob
)
525 struct DosEnvec
*de
= BADDR(glob
->fssm
->fssm_Environ
);
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)
541 else if (sector_count
< 1024 * 1024)
546 D(bug("[fat] writing boot sector\n"));
548 /* Decide on cluster size and root dir entries */
549 first_fat_sector
= 1;
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;
569 /* We only support some common 3.5" floppy formats */
570 return ERROR_NOT_IMPLEMENTED
;
575 for (i
= 0; fat16_cluster_thresholds
[i
] < sector_count
; i
++);
576 sectors_per_cluster
= 1 << i
;
577 root_entries_count
= 512;
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;
595 fat_size
= (temp1
+ temp2
- 1) / temp2
;
597 boot
= AllocMem(bsize
, MEMF_CLEAR
);
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
);
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
);
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
;
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 */
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
++)
653 ebpb
->bs_vollab
[i
] = toupper(name
[i
]);
655 ebpb
->bs_vollab
[i
] = ' ';
657 CopyMem(default_filsystype
, ebpb
->bs_filsystype
, 8);
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 */
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
;
718 LONG
SetVolumeName(struct FSSuper
*sb
, UBYTE
*name
, UWORD len
)
720 struct Globals
*glob
= sb
->glob
;
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
);
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
));
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
;
775 /* Create a new volume ID entry if there wasn't one */
778 err
= AllocDirEntry(&dh
, 0, &de
, glob
);
780 FillDirEntry(&de
, ATTR_VOLUME_ID
, 0, glob
);
783 /* Copy the name in */
786 for (i
= 0; i
< FAT_MAX_SHORT_NAME
; i
++)
788 de
.e
.entry
.name
[i
] = toupper(name
[i
]);
790 de
.e
.entry
.name
[i
] = ' ';
792 if ((err
= UpdateDirEntry(&de
, glob
)) != 0)
794 D(bug("[fat] couldn't change volume name\n"));
799 /* Copy name to boot block as well, and save */
801 CopyMem(de
.e
.entry
.name
, boot
->ebpbs
.ebpb32
.ebpb
.bs_vollab
,
804 CopyMem(de
.e
.entry
.name
, boot
->ebpbs
.ebpb
.bs_vollab
,
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
);
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
;
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
;
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;
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
)
899 struct VolumeInfo
*vol_info
= NULL
;
900 struct GlobalLock
*global_lock
;
901 struct ExtFileLock
*ext_lock
;
902 struct MinNode
*lock_node
;
904 struct NotifyNode
*nn
;
905 struct DosList
*newvol
= NULL
;
908 && (sb
= AllocVecPooled(glob
->mempool
, sizeof(struct FSSuper
))))
910 memset(sb
, 0, sizeof(struct FSSuper
));
913 err
= ReadFATSuper(sb
);
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
);
925 CompareDates(&dl
->dol_misc
.dol_volume
.dol_VolumeDate
,
926 &sb
->volume
.create_time
) != 0)
931 dl
->dol_Task
= glob
->ourport
;
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 */
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);
946 CopyMem(sb
->volume
.name
, dl
->dol_Name
,
947 sb
->volume
.name
[0] + 2);
951 /* Patch locks and notifications to match this handler
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
;
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
,
973 ext_lock
->fl_Task
= glob
->ourport
;
975 ext_lock
->ioh
.sb
= sb
;
978 ForeachNode(&vol_info
->notifies
, nn
)
979 nn
->nr
->nr_Handler
= glob
->ourport
;
983 D(bug("\tCreating new volume.\n"));
985 /* Create transferable core volume info */
987 CreatePool(MEMF_PUBLIC
, DEF_POOL_SIZE
,
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
);
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
=
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
:
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);
1041 CopyMem(sb
->volume
.name
,
1042 BADDR(newvol
->dol_Name
),
1043 sb
->volume
.name
[0] + 2);
1046 sb
->doslist
= newvol
;
1049 if (vol_info
== NULL
|| newvol
== NULL
)
1054 sb
->info
= vol_info
;
1055 glob
->last_num
= -1;
1058 SendEvent(IECLASS_DISKINSERTED
, glob
);
1060 SendVolumePacket(newvol
, ACTION_VOLUME_ADD
, glob
);
1062 D(bug("\tDisk successfully initialised\n"));
1067 FreeVecPooled(glob
->mempool
, sb
);
1070 SendEvent(IECLASS_DISKINSERTED
, glob
);
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"));
1090 Remove((struct Node
*)sb
);
1092 SendVolumePacket(sb
->doslist
, ACTION_VOLUME_REMOVE
, glob
);
1095 FreeVecPooled(glob
->mempool
, sb
);
1102 void DoDiskRemove(struct Globals
*glob
)
1107 struct FSSuper
*sb
= glob
->sb
;
1109 if (!AttemptDestroyVolume(sb
))
1111 sb
->doslist
->dol_Task
= 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
);