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 #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>
31 #include "fat_protos.h"
33 #define DEBUG DEBUG_MISC
37 #define ID_BUSY 0x42555359
46 void FillDiskInfo(struct InfoData
*id
, struct Globals
*glob
)
48 struct DosEnvec
*de
= BADDR(glob
->fssm
->fssm_Environ
);
50 id
->id_NumSoftErrors
= 0;
51 id
->id_UnitNumber
= glob
->fssm
->fssm_Unit
;
52 id
->id_DiskState
= ID_VALIDATED
;
56 id
->id_NumBlocks
= glob
->sb
->total_sectors
;
57 id
->id_NumBlocksUsed
= glob
->sb
->total_sectors
58 - (glob
->sb
->free_clusters
<< glob
->sb
->cluster_sectors_bits
);
59 id
->id_BytesPerBlock
= glob
->sb
->sectorsize
;
61 id
->id_DiskType
= ID_DOS_DISK
;
63 id
->id_VolumeNode
= MKBADDR(glob
->sb
->doslist
);
64 id
->id_InUse
= (IsListEmpty(&glob
->sb
->info
->locks
)
65 && IsListEmpty(&glob
->sb
->info
->notifies
)) ? DOSFALSE
: DOSTRUE
;
70 id
->id_NumBlocks
= de
->de_Surfaces
* de
->de_BlocksPerTrack
71 * (de
->de_HighCyl
+ 1 - de
->de_LowCyl
) / de
->de_SectorPerBlock
;
72 id
->id_NumBlocksUsed
= id
->id_NumBlocks
;
73 id
->id_BytesPerBlock
= de
->de_SizeBlock
<< 2;
75 id
->id_DiskState
= ID_VALIDATED
;
77 if (glob
->disk_inhibited
)
78 id
->id_DiskType
= ID_BUSY
;
79 else if (glob
->disk_inserted
)
80 id
->id_DiskType
= ID_NOT_REALLY_DOS
; //ID_UNREADABLE_DISK;
82 id
->id_DiskType
= ID_NO_DISK_PRESENT
;
84 id
->id_VolumeNode
= BNULL
;
85 id
->id_InUse
= DOSFALSE
;
89 void SendVolumePacket(struct DosList
*vol
, ULONG action
,
92 struct DosPacket
*dospacket
;
94 dospacket
= AllocDosObject(DOS_STDPKT
, TAG_DONE
);
95 dospacket
->dp_Type
= ACTION_DISK_CHANGE
;
96 dospacket
->dp_Arg1
= ID_FAT_DISK
;
97 dospacket
->dp_Arg2
= (IPTR
) vol
;
98 dospacket
->dp_Arg3
= action
;
99 dospacket
->dp_Port
= NULL
;
101 PutMsg(glob
->ourport
, dospacket
->dp_Link
);
104 void DoDiskInsert(struct Globals
*glob
)
109 struct VolumeInfo
*vol_info
= NULL
;
110 struct GlobalLock
*global_lock
;
111 struct ExtFileLock
*ext_lock
;
112 struct MinNode
*lock_node
;
114 struct NotifyNode
*nn
;
115 struct DosList
*newvol
= NULL
;
118 && (sb
= AllocVecPooled(glob
->mempool
, sizeof(struct FSSuper
))))
120 memset(sb
, 0, sizeof(struct FSSuper
));
123 err
= ReadFATSuper(sb
);
127 /* Scan volume list for a matching volume (would be better to
128 * match by serial number) */
129 dl
= LockDosList(LDF_VOLUMES
| LDF_WRITE
);
130 dl
= FindDosEntry(dl
, sb
->volume
.name
+ 1,
131 LDF_VOLUMES
| LDF_WRITE
);
132 UnLockDosList(LDF_VOLUMES
| LDF_WRITE
);
135 CompareDates(&dl
->dol_misc
.dol_volume
.dol_VolumeDate
,
136 &sb
->volume
.create_time
) != 0)
141 dl
->dol_Task
= glob
->ourport
;
144 D(bug("\tFound old volume.\n"));
146 vol_info
= BADDR(dl
->dol_misc
.dol_volume
.dol_LockList
);
148 #if 0 /* No point until we match volumes by serial number */
150 #ifdef AROS_FAST_BPTR
151 /* ReadFATSuper() sets a null byte after the
152 * string, so this should be fine */
153 CopyMem(sb
->volume
.name
+ 1, dl
->dol_Name
,
154 sb
->volume
.name
[0] + 1);
156 CopyMem(sb
->volume
.name
, dl
->dol_Name
,
157 sb
->volume
.name
[0] + 2);
161 /* Patch locks and notifications to match this handler
163 ForeachNode(&vol_info
->locks
, global_lock
)
165 ForeachNode(&global_lock
->locks
, lock_node
)
167 ext_lock
= LOCKFROMNODE(lock_node
);
168 D(bug("[fat] Patching adopted lock %p. old port = %p,"
169 " new port = %p\n", ext_lock
,
170 ext_lock
->fl_Task
, glob
->ourport
));
171 ext_lock
->fl_Task
= glob
->ourport
;
173 ext_lock
->ioh
.sb
= sb
;
177 ForeachNode(&vol_info
->root_lock
.locks
, lock_node
)
179 ext_lock
= LOCKFROMNODE(lock_node
);
180 D(bug("[fat] Patching adopted ROOT lock %p. old port = %p,"
181 " new port = %p\n", ext_lock
, ext_lock
->fl_Task
,
183 ext_lock
->fl_Task
= glob
->ourport
;
185 ext_lock
->ioh
.sb
= sb
;
188 ForeachNode(&vol_info
->notifies
, nn
)
189 nn
->nr
->nr_Handler
= glob
->ourport
;
193 D(bug("\tCreating new volume.\n"));
195 /* Create transferable core volume info */
197 CreatePool(MEMF_PUBLIC
, DEF_POOL_SIZE
,
202 AllocVecPooled(pool
, sizeof(struct VolumeInfo
));
203 if (vol_info
!= NULL
)
205 vol_info
->mem_pool
= pool
;
206 vol_info
->id
= sb
->volume_id
;
207 NEWLIST(&vol_info
->locks
);
208 NEWLIST(&vol_info
->notifies
);
210 vol_info
->root_lock
.dir_cluster
= FAT_ROOTDIR_MARK
;
211 vol_info
->root_lock
.dir_entry
= FAT_ROOTDIR_MARK
;
212 vol_info
->root_lock
.access
= SHARED_LOCK
;
213 vol_info
->root_lock
.first_cluster
= 0;
214 vol_info
->root_lock
.attr
= ATTR_DIRECTORY
;
215 vol_info
->root_lock
.size
= 0;
216 CopyMem(sb
->volume
.name
, vol_info
->root_lock
.name
,
217 sb
->volume
.name
[0] + 1);
218 NEWLIST(&vol_info
->root_lock
.locks
);
222 AllocVecPooled(pool
, sizeof(struct DosList
))))
224 newvol
->dol_Next
= BNULL
;
225 newvol
->dol_Type
= DLT_VOLUME
;
226 newvol
->dol_Task
= glob
->ourport
;
227 newvol
->dol_Lock
= BNULL
;
229 CopyMem(&sb
->volume
.create_time
,
230 &newvol
->dol_misc
.dol_volume
.dol_VolumeDate
,
231 sizeof(struct DateStamp
));
233 newvol
->dol_misc
.dol_volume
.dol_LockList
=
236 newvol
->dol_misc
.dol_volume
.dol_DiskType
=
237 (sb
->type
== 12) ? ID_FAT12_DISK
:
238 (sb
->type
== 16) ? ID_FAT16_DISK
:
239 (sb
->type
== 32) ? ID_FAT32_DISK
:
242 if ((newvol
->dol_Name
= MKBADDR(
243 AllocVecPooled(pool
, FAT_MAX_SHORT_NAME
+ 2))))
245 #ifdef AROS_FAST_BPTR
246 /* ReadFATSuper() sets a null byte after the
247 * string, so this should be fine */
248 CopyMem(sb
->volume
.name
+ 1, newvol
->dol_Name
,
249 sb
->volume
.name
[0] + 1);
251 CopyMem(sb
->volume
.name
,
252 BADDR(newvol
->dol_Name
),
253 sb
->volume
.name
[0] + 2);
256 sb
->doslist
= newvol
;
259 if (vol_info
== NULL
|| newvol
== NULL
)
268 SendEvent(IECLASS_DISKINSERTED
, glob
);
270 SendVolumePacket(newvol
, ACTION_VOLUME_ADD
, glob
);
272 D(bug("\tDisk successfully initialised\n"));
276 else if (err
== IOERR_BADADDRESS
)
277 ErrorMessageArgs("Your device does not support 64-bit\n"
278 "access to the disk while it is needed!\n"
279 "In order to prevent data damage access to\n"
280 "this disk was blocked.\n"
281 "Please upgrade your device driver.", "OK", NULL
, glob
);
283 FreeVecPooled(glob
->mempool
, sb
);
286 SendEvent(IECLASS_DISKINSERTED
, glob
);
291 BOOL
AttemptDestroyVolume(struct FSSuper
*sb
)
293 struct Globals
*glob
= sb
->glob
;
294 BOOL destroyed
= FALSE
;
296 D(bug("[fat] Attempting to destroy volume\n"));
298 /* Check if the volume can be removed */
299 if (IsListEmpty(&sb
->info
->locks
) && IsListEmpty(&sb
->info
->notifies
))
301 D(bug("\tRemoving volume completely\n"));
306 Remove((struct Node
*)sb
);
308 SendVolumePacket(sb
->doslist
, ACTION_VOLUME_REMOVE
, glob
);
311 FreeVecPooled(glob
->mempool
, sb
);
318 void DoDiskRemove(struct Globals
*glob
)
323 struct FSSuper
*sb
= glob
->sb
;
325 if (!AttemptDestroyVolume(sb
))
327 sb
->doslist
->dol_Task
= NULL
;
329 D(bug("\tMoved in-memory super block to spare list. "
330 "Waiting for locks and notifications to be freed\n"));
331 AddTail((struct List
*)&glob
->sblist
, (struct Node
*)sb
);
332 SendEvent(IECLASS_DISKREMOVED
, glob
);
337 void ProcessDiskChange(struct Globals
*glob
)
339 D(bug("\nGot disk change request\n"));
341 if (glob
->disk_inhibited
)
343 D(bug("Disk is inhibited, ignoring disk change\n"));
347 glob
->diskioreq
->iotd_Req
.io_Command
= TD_CHANGESTATE
;
348 glob
->diskioreq
->iotd_Req
.io_Data
= NULL
;
349 glob
->diskioreq
->iotd_Req
.io_Length
= 0;
350 glob
->diskioreq
->iotd_Req
.io_Flags
= IOF_QUICK
;
351 DoIO((struct IORequest
*)glob
->diskioreq
);
353 if (glob
->diskioreq
->iotd_Req
.io_Error
== 0
354 && glob
->diskioreq
->iotd_Req
.io_Actual
== 0)
356 /* Disk has been inserted. */
357 D(bug("\tDisk has been inserted\n"));
358 glob
->disk_inserted
= TRUE
;
363 /* Disk has been removed. */
364 D(bug("\tDisk has been removed\n"));
365 glob
->disk_inserted
= FALSE
;
372 void UpdateDisk(struct Globals
*glob
)
376 Cache_Flush(glob
->sb
->cache
);
377 /* FIXME: Handle IO errors on disk flush! */
380 glob
->diskioreq
->iotd_Req
.io_Command
= CMD_UPDATE
;
381 DoIO((struct IORequest
*)glob
->diskioreq
);
383 /* Turn off motor (where applicable) if nothing has happened during the
384 * last timer period */
385 if (!glob
->restart_timer
)
387 D(bug("Stopping drive motor\n"));
388 glob
->diskioreq
->iotd_Req
.io_Command
= TD_MOTOR
;
389 glob
->diskioreq
->iotd_Req
.io_Length
= 0;
390 DoIO((struct IORequest
*)glob
->diskioreq
);
394 /* Probe the device to determine 64-bit support */
395 void Probe_64bit_support(struct Globals
*glob
)
397 struct NSDeviceQueryResult nsd_query
;
400 glob
->readcmd
= CMD_READ
;
401 glob
->writecmd
= CMD_WRITE
;
404 glob
->diskioreq
->iotd_Req
.io_Command
= TD_READ64
;
405 glob
->diskioreq
->iotd_Req
.io_Offset
= 0;
406 glob
->diskioreq
->iotd_Req
.io_Length
= 0;
407 glob
->diskioreq
->iotd_Req
.io_Actual
= 0;
408 glob
->diskioreq
->iotd_Req
.io_Data
= 0;
410 if (DoIO((struct IORequest
*)glob
->diskioreq
) != IOERR_NOCMD
)
412 D(bug("Probe_64bit_support:"
413 " device supports 64-bit trackdisk extensions\n"));
414 glob
->readcmd
= TD_READ64
;
415 glob
->writecmd
= TD_WRITE64
;
419 glob
->diskioreq
->iotd_Req
.io_Command
= NSCMD_DEVICEQUERY
;
420 glob
->diskioreq
->iotd_Req
.io_Length
=
421 sizeof(struct NSDeviceQueryResult
);
422 glob
->diskioreq
->iotd_Req
.io_Data
= (APTR
) &nsd_query
;
424 if (DoIO((struct IORequest
*)glob
->diskioreq
) == 0)
425 for (nsd_cmd
= nsd_query
.SupportedCommands
; *nsd_cmd
!= 0;
428 if (*nsd_cmd
== NSCMD_TD_READ64
)
430 D(bug("Probe_64bit_support:"
431 " device supports NSD 64-bit trackdisk extensions\n"));
432 glob
->readcmd
= NSCMD_TD_READ64
;
433 glob
->writecmd
= NSCMD_TD_WRITE64
;
439 ULONG
AccessDisk(BOOL do_write
, ULONG num
, ULONG nblocks
, ULONG block_size
,
440 UBYTE
*data
, APTR priv
)
442 struct Globals
*glob
= priv
;
447 CONST_STRPTR vol_name
= "";
449 #if DEBUG_CACHESTATS > 1
450 ErrorMessage("Accessing %lu sector(s) starting at %lu.\n"
451 "First volume sector is %lu, sector size is %lu.\n", "OK", nblocks
,
452 num
, glob
->sb
->first_device_sector
, block_size
);
455 /* Adjust parameters if range is partially outside boundaries, or
456 * warn user and bale out if completely outside boundaries */
459 vol_name
= glob
->sb
->volume
.name
+ 1;
461 start
= glob
->sb
->first_device_sector
;
462 if (num
+ nblocks
<= glob
->sb
->first_device_sector
)
464 if (num
!= glob
->last_num
)
466 glob
->last_num
= num
;
467 ErrorMessage("A handler attempted to %s %lu sector(s)\n"
468 "starting from %lu, before the actual volume space.\n"
469 "First volume sector is %lu, sector size is %lu.\n"
470 "Either your disk is damaged or it is a bug in\n"
471 "the handler. Please check your disk and/or\n"
472 "report this problem to the developers team.", "OK",
473 (IPTR
) (do_write
? "write" : "read"), nblocks
, num
,
474 glob
->sb
->first_device_sector
, block_size
);
476 return IOERR_BADADDRESS
;
478 else if (num
< start
)
480 nblocks
-= start
- num
;
481 data
+= (start
- num
) * block_size
;
485 end
= glob
->sb
->first_device_sector
+ glob
->sb
->total_sectors
;
488 if (num
!= glob
->last_num
)
490 glob
->last_num
= num
;
491 ErrorMessage("A handler attempted to %s %lu sector(s)\n"
492 "starting from %lu, beyond the actual volume space.\n"
493 "Last volume sector is %lu, sector size is %lu.\n"
494 "Either your disk is damaged or it is a bug in\n"
495 "the handler. Please check your disk and/or\n"
496 "report this problem to the developers team.", "OK",
497 (IPTR
) (do_write
? "write" : "read"), nblocks
, num
,
498 end
- 1, block_size
);
500 return IOERR_BADADDRESS
;
502 else if (num
+ nblocks
> end
)
506 off
= ((UQUAD
) num
) * block_size
;
510 glob
->diskioreq
->iotd_Req
.io_Offset
= off
& 0xFFFFFFFF;
511 glob
->diskioreq
->iotd_Req
.io_Actual
= off
>> 32;
513 glob
->diskioreq
->iotd_Req
.io_Length
= nblocks
* block_size
;
514 glob
->diskioreq
->iotd_Req
.io_Data
= data
;
515 glob
->diskioreq
->iotd_Req
.io_Command
=
516 do_write
? glob
->writecmd
: glob
->readcmd
;
518 err
= DoIO((struct IORequest
*)glob
->diskioreq
);
523 retry
= ErrorMessage("Volume %s\nhas a %s error\n"
524 "in the block range\n%lu to %lu",
525 "Retry|Cancel", (IPTR
)vol_name
,
526 (IPTR
)(do_write
? "write" : "read"), num
,
529 retry
= ErrorMessage("Volume %s\nhas a %s error\n"
531 "Retry|Cancel", (IPTR
)vol_name
,
532 (IPTR
)(do_write
? "write" : "read"), num
);