2 * fat.handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007-2011 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 #define USE_INLINE_STDARG
15 #include <exec/types.h>
16 #include <exec/errors.h>
19 #include <dos/dosextens.h>
20 #include <dos/filehandler.h>
21 #include <devices/newstyle.h>
22 #include <devices/trackdisk.h>
23 #include <devices/inputevent.h>
25 #include <proto/exec.h>
26 #include <proto/dos.h>
28 #include <string.h> /* for memset() */
31 #include "fat_protos.h"
33 #define DEBUG DEBUG_MISC
37 #define ID_BUSY 0x42555359
46 void FillDiskInfo (struct InfoData
*id
) {
47 struct DosEnvec
*de
= BADDR(glob
->fssm
->fssm_Environ
);
49 id
->id_NumSoftErrors
= 0;
50 id
->id_UnitNumber
= glob
->fssm
->fssm_Unit
;
51 id
->id_DiskState
= ID_VALIDATED
;
54 id
->id_NumBlocks
= glob
->sb
->total_sectors
;
55 id
->id_NumBlocksUsed
= glob
->sb
->total_sectors
56 - (glob
->sb
->free_clusters
<< glob
->sb
->cluster_sectors_bits
);
57 id
->id_BytesPerBlock
= glob
->sb
->sectorsize
;
59 id
->id_DiskType
= ID_DOS_DISK
;
61 id
->id_VolumeNode
= MKBADDR(glob
->sb
->doslist
);
62 id
->id_InUse
= (IsListEmpty(&glob
->sb
->info
->locks
)
63 && IsListEmpty(&glob
->sb
->info
->notifies
)) ? DOSFALSE
: DOSTRUE
;
67 id
->id_NumBlocks
= de
->de_Surfaces
* de
->de_BlocksPerTrack
* (de
->de_HighCyl
+1 - de
->de_LowCyl
) / de
->de_SectorPerBlock
;
68 id
->id_NumBlocksUsed
= id
->id_NumBlocks
;
69 id
->id_BytesPerBlock
= de
->de_SizeBlock
<< 2;
71 id
->id_DiskState
= ID_VALIDATED
;
73 if (glob
->disk_inhibited
)
74 id
->id_DiskType
= ID_BUSY
;
75 else if (glob
->disk_inserted
)
76 id
->id_DiskType
= ID_NOT_REALLY_DOS
; //ID_UNREADABLE_DISK;
78 id
->id_DiskType
= ID_NO_DISK_PRESENT
;
80 id
->id_VolumeNode
= BNULL
;
81 id
->id_InUse
= DOSFALSE
;
85 void SendVolumePacket(struct DosList
*vol
, ULONG action
) {
86 struct DosPacket
*dospacket
;
88 dospacket
= AllocDosObject(DOS_STDPKT
, TAG_DONE
);
89 dospacket
->dp_Type
= ACTION_DISK_CHANGE
;
90 dospacket
->dp_Arg1
= ID_FAT_DISK
;
91 dospacket
->dp_Arg2
= (IPTR
)vol
;
92 dospacket
->dp_Arg3
= action
;
93 dospacket
->dp_Port
= NULL
;
95 PutMsg(glob
->ourport
, dospacket
->dp_Link
);
98 void DoDiskInsert(void) {
102 struct VolumeInfo
*vol_info
= NULL
;
103 struct GlobalLock
*global_lock
;
104 struct ExtFileLock
*ext_lock
;
105 struct MinNode
*lock_node
;
107 struct NotifyNode
*nn
;
108 struct DosList
*newvol
= NULL
;
110 if (glob
->sb
== NULL
&& (sb
= AllocVecPooled(glob
->mempool
, sizeof(struct FSSuper
)))) {
111 memset(sb
, 0, sizeof(struct FSSuper
));
113 err
= ReadFATSuper(sb
);
116 /* Scan volume list for a matching volume (would be better to
117 * match by serial number) */
118 dl
= LockDosList(LDF_VOLUMES
| LDF_WRITE
);
119 dl
= FindDosEntry(dl
, sb
->volume
.name
+ 1,
120 LDF_VOLUMES
| LDF_WRITE
);
121 UnLockDosList(LDF_VOLUMES
| LDF_WRITE
);
124 dl
->dol_Task
= glob
->ourport
;
127 D(bug("\tFound old volume.\n"));
129 vol_info
= BADDR(dl
->dol_misc
.dol_volume
.dol_LockList
);
131 #if 0 /* no point until we match volumes by serial number */
133 #ifdef AROS_FAST_BPTR
134 /* ReadFATSuper() sets a null byte after the
135 * string, so this should be fine */
136 CopyMem(sb
->volume
.name
+ 1, dl
->dol_Name
,
137 sb
->volume
.name
[0] + 1);
139 CopyMem(sb
->volume
.name
, dl
->dol_Name
,
140 sb
->volume
.name
[0] + 2);
144 /* patch locks and notifications to match this handler
146 ForeachNode(&vol_info
->locks
, global_lock
) {
147 ForeachNode(&global_lock
->locks
, lock_node
) {
148 ext_lock
= LOCKFROMNODE(lock_node
);
149 D(bug("[fat] Patching adopted lock %p. old port = %p,"
150 " new port = %p\n", ext_lock
, ext_lock
->fl_Task
,
152 ext_lock
->fl_Task
= glob
->ourport
;
154 ext_lock
->ioh
.sb
= sb
;
158 ForeachNode(&vol_info
->root_lock
.locks
, lock_node
) {
159 ext_lock
= LOCKFROMNODE(lock_node
);
160 D(bug("[fat] Patching adopted ROOT lock %p. old port = %p,"
161 " new port = %p\n", ext_lock
, ext_lock
->fl_Task
,
163 ext_lock
->fl_Task
= glob
->ourport
;
165 ext_lock
->ioh
.sb
= sb
;
168 ForeachNode(&vol_info
->notifies
, nn
)
169 nn
->nr
->nr_Handler
= glob
->ourport
;
172 D(bug("\tCreating new volume.\n"));
174 /* create transferable core volume info */
175 pool
= CreatePool(MEMF_PUBLIC
, DEF_POOL_SIZE
, DEF_POOL_THRESHOLD
);
177 vol_info
= AllocVecPooled(pool
, sizeof(struct VolumeInfo
));
178 if (vol_info
!= NULL
) {
179 vol_info
->mem_pool
= pool
;
180 vol_info
->id
= sb
->volume_id
;
181 NEWLIST(&vol_info
->locks
);
182 NEWLIST(&vol_info
->notifies
);
184 vol_info
->root_lock
.dir_cluster
= FAT_ROOTDIR_MARK
;
185 vol_info
->root_lock
.dir_entry
= FAT_ROOTDIR_MARK
;
186 vol_info
->root_lock
.access
= SHARED_LOCK
;
187 vol_info
->root_lock
.first_cluster
= 0;
188 vol_info
->root_lock
.attr
= ATTR_DIRECTORY
;
189 vol_info
->root_lock
.size
= 0;
190 CopyMem(sb
->volume
.name
, vol_info
->root_lock
.name
,
191 sb
->volume
.name
[0] + 1);
192 NEWLIST(&vol_info
->root_lock
.locks
);
195 if ((newvol
= AllocVecPooled(pool
, sizeof(struct DosList
)))) {
196 newvol
->dol_Next
= BNULL
;
197 newvol
->dol_Type
= DLT_VOLUME
;
198 newvol
->dol_Task
= glob
->ourport
;
199 newvol
->dol_Lock
= BNULL
;
201 CopyMem(&sb
->volume
.create_time
, &newvol
->dol_misc
.dol_volume
.dol_VolumeDate
, sizeof(struct DateStamp
));
203 newvol
->dol_misc
.dol_volume
.dol_LockList
= MKBADDR(vol_info
);
205 newvol
->dol_misc
.dol_volume
.dol_DiskType
=
206 (sb
->type
== 12) ? ID_FAT12_DISK
:
207 (sb
->type
== 16) ? ID_FAT16_DISK
:
208 (sb
->type
== 32) ? ID_FAT32_DISK
:
211 if ((newvol
->dol_Name
= MKBADDR(AllocVecPooled(pool
, 13)))) {
212 #ifdef AROS_FAST_BPTR
213 /* ReadFATSuper() sets a null byte after the
214 * string, so this should be fine */
215 CopyMem(sb
->volume
.name
+ 1, newvol
->dol_Name
,
216 sb
->volume
.name
[0] + 1);
218 CopyMem(sb
->volume
.name
, BADDR(newvol
->dol_Name
),
219 sb
->volume
.name
[0] + 2);
222 sb
->doslist
= newvol
;
225 if (vol_info
== NULL
|| newvol
== NULL
)
235 SendEvent(IECLASS_DISKINSERTED
);
237 SendVolumePacket(newvol
, ACTION_VOLUME_ADD
);
239 D(bug("\tDisk successfully initialised\n"));
243 else if (err
== IOERR_BADADDRESS
)
244 ErrorMessageArgs("Your device does not support 64-bit\n"
245 "access to the disk while it is needed!\n"
246 "In order to prevent data damage access to\n"
247 "this disk was blocked.\n"
248 "Please upgrade your device driver.", NULL
);
250 FreeVecPooled(glob
->mempool
, sb
);
253 SendEvent(IECLASS_DISKINSERTED
);
258 BOOL
AttemptDestroyVolume(struct FSSuper
*sb
) {
259 BOOL destroyed
= FALSE
;
261 D(bug("[fat] Attempting to destroy volume\n"));
263 /* check if the volume can be removed */
264 if (IsListEmpty(&sb
->info
->locks
) && IsListEmpty(&sb
->info
->notifies
)) {
265 D(bug("\tRemoving volume completely\n"));
270 Remove((struct Node
*)sb
);
272 SendVolumePacket(sb
->doslist
, ACTION_VOLUME_REMOVE
);
275 FreeVecPooled(glob
->mempool
, sb
);
282 void DoDiskRemove(void) {
285 struct FSSuper
*sb
= glob
->sb
;
287 if(!AttemptDestroyVolume(sb
)) {
288 sb
->doslist
->dol_Task
= NULL
;
290 D(bug("\tMoved in-memory super block to spare list. "
291 "Waiting for locks and notifications to be freed\n"));
292 AddTail((struct List
*)&glob
->sblist
, (struct Node
*)sb
);
293 SendEvent(IECLASS_DISKREMOVED
);
298 void ProcessDiskChange(void) {
299 D(bug("\nGot disk change request\n"));
301 if (glob
->disk_inhibited
) {
302 D(bug("Disk is inhibited, ignoring disk change\n"));
306 glob
->diskioreq
->iotd_Req
.io_Command
= TD_CHANGESTATE
;
307 glob
->diskioreq
->iotd_Req
.io_Data
= NULL
;
308 glob
->diskioreq
->iotd_Req
.io_Length
= 0;
309 glob
->diskioreq
->iotd_Req
.io_Flags
= IOF_QUICK
;
310 DoIO((struct IORequest
*) glob
->diskioreq
);
312 if (glob
->diskioreq
->iotd_Req
.io_Error
== 0 && glob
->diskioreq
->iotd_Req
.io_Actual
== 0) {
313 /* Disk has been inserted. */
314 D(bug("\tDisk has been inserted\n"));
315 glob
->disk_inserted
= TRUE
;
319 /* Disk has been removed. */
320 D(bug("\tDisk has been removed\n"));
321 glob
->disk_inserted
= FALSE
;
328 void UpdateDisk(void) {
330 Cache_Flush(glob
->sb
->cache
);
332 glob
->diskioreq
->iotd_Req
.io_Command
= CMD_UPDATE
;
333 DoIO((struct IORequest
*)glob
->diskioreq
);
335 /* Turn off motor (where applicable) if nothing has happened during the
336 * last timer period */
337 if (!glob
->restart_timer
) {
338 D(bug("Stopping drive motor\n"));
339 glob
->diskioreq
->iotd_Req
.io_Command
= TD_MOTOR
;
340 glob
->diskioreq
->iotd_Req
.io_Length
= 0;
341 DoIO((struct IORequest
*)glob
->diskioreq
);
345 /* probe the device to determine 64-bit support */
346 void Probe_64bit_support(void) {
347 struct NSDeviceQueryResult nsd_query
;
350 glob
->readcmd
= CMD_READ
;
351 glob
->writecmd
= CMD_WRITE
;
354 glob
->diskioreq
->iotd_Req
.io_Command
= TD_READ64
;
355 glob
->diskioreq
->iotd_Req
.io_Offset
= 0;
356 glob
->diskioreq
->iotd_Req
.io_Length
= 0;
357 glob
->diskioreq
->iotd_Req
.io_Actual
= 0;
358 glob
->diskioreq
->iotd_Req
.io_Data
= 0;
360 if (DoIO((struct IORequest
*) glob
->diskioreq
) != IOERR_NOCMD
) {
361 D(bug("Probe_64bit_support: device supports 64-bit trackdisk extensions\n"));
362 glob
->readcmd
= TD_READ64
;
363 glob
->writecmd
= TD_WRITE64
;
367 glob
->diskioreq
->iotd_Req
.io_Command
= NSCMD_DEVICEQUERY
;
368 glob
->diskioreq
->iotd_Req
.io_Length
= sizeof(struct NSDeviceQueryResult
);
369 glob
->diskioreq
->iotd_Req
.io_Data
= (APTR
) &nsd_query
;
371 if (DoIO((struct IORequest
*) glob
->diskioreq
) == 0)
372 for (nsd_cmd
= nsd_query
.SupportedCommands
; *nsd_cmd
!= 0; nsd_cmd
++) {
373 if (*nsd_cmd
== NSCMD_TD_READ64
) {
374 D(bug("Probe_64bit_support: device supports NSD 64-bit trackdisk extensions\n"));
375 glob
->readcmd
= NSCMD_TD_READ64
;
376 glob
->writecmd
= NSCMD_TD_WRITE64
;
382 ULONG
AccessDisk(BOOL do_write
, ULONG num
, ULONG nblocks
, ULONG block_size
,
388 #if DEBUG_CACHESTATS > 1
389 ErrorMessage("Accessing %lu sector(s) starting at %lu.\n"
390 "First volume sector is %lu, sector size is %lu.\n", nblocks
, num
,
391 glob
->sb
->first_device_sector
, block_size
);
394 /* Adjust parameters if range is partially outside boundaries, or
395 * warn user and bale out if completely outside boundaries */
397 start
= glob
->sb
->first_device_sector
;
398 if (num
+ nblocks
<= glob
->sb
->first_device_sector
) {
399 if (num
!= glob
->last_num
) {
400 glob
->last_num
= num
;
401 ErrorMessage("A handler attempted to %s %lu sector(s) starting\n"
402 "from %lu, before the actual volume space.\n"
403 "First volume sector is %lu, sector size is %lu.\n"
404 "Either your disk is damaged or it is a bug in\n"
405 "the handler. Please check your disk and/or\n"
406 "report this problem to the developers team.",
407 (IPTR
)(do_write
? "write" : "read"), nblocks
, num
,
408 glob
->sb
->first_device_sector
, block_size
);
410 return IOERR_BADADDRESS
;
412 else if (num
< start
) {
413 nblocks
-= start
- num
;
414 data
+= (start
- num
) * block_size
;
418 end
= glob
->sb
->first_device_sector
+ glob
->sb
->total_sectors
;
420 if (num
!= glob
->last_num
) {
421 glob
->last_num
= num
;
422 ErrorMessage("A handler attempted to %s %lu sector(s) starting\n"
423 "from %lu, beyond the actual volume space.\n"
424 "Last volume sector is %lu, sector size is %lu.\n"
425 "Either your disk is damaged or it is a bug in\n"
426 "the handler. Please check your disk and/or\n"
427 "report this problem to the developers team.",
428 (IPTR
)(do_write
? "write" : "read"), nblocks
, num
,
429 end
- 1, block_size
);
431 return IOERR_BADADDRESS
;
433 else if (num
+ nblocks
> end
)
437 off
= ((UQUAD
) num
) * block_size
;
439 glob
->diskioreq
->iotd_Req
.io_Offset
= off
& 0xFFFFFFFF;
440 glob
->diskioreq
->iotd_Req
.io_Actual
= off
>> 32;
442 if (glob
->diskioreq
->iotd_Req
.io_Actual
&& (glob
->readcmd
== CMD_READ
))
443 return IOERR_BADADDRESS
;
445 glob
->diskioreq
->iotd_Req
.io_Length
= nblocks
* block_size
;
446 glob
->diskioreq
->iotd_Req
.io_Data
= data
;
447 glob
->diskioreq
->iotd_Req
.io_Command
= do_write
? glob
->writecmd
: glob
->readcmd
;
449 err
= DoIO((struct IORequest
*) glob
->diskioreq
);