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>
32 #include "fat_protos.h"
34 #define DEBUG DEBUG_MISC
38 #define ID_BUSY 0x42555359
47 void FillDiskInfo(struct InfoData
*id
, struct Globals
*glob
)
49 struct DosEnvec
*de
= BADDR(glob
->fssm
->fssm_Environ
);
51 id
->id_NumSoftErrors
= 0;
52 id
->id_UnitNumber
= glob
->fssm
->fssm_Unit
;
53 id
->id_DiskState
= ID_VALIDATED
;
57 id
->id_NumBlocks
= glob
->sb
->total_sectors
;
58 id
->id_NumBlocksUsed
= glob
->sb
->total_sectors
59 - (glob
->sb
->free_clusters
<< glob
->sb
->cluster_sectors_bits
);
60 id
->id_BytesPerBlock
= glob
->sb
->sectorsize
;
62 id
->id_DiskType
= ID_DOS_DISK
;
64 id
->id_VolumeNode
= MKBADDR(glob
->sb
->doslist
);
65 id
->id_InUse
= (IsListEmpty(&glob
->sb
->info
->locks
)
66 && IsListEmpty(&glob
->sb
->info
->notifies
)) ? DOSFALSE
: DOSTRUE
;
71 id
->id_NumBlocks
= de
->de_Surfaces
* de
->de_BlocksPerTrack
72 * (de
->de_HighCyl
+ 1 - de
->de_LowCyl
) / de
->de_SectorPerBlock
;
73 id
->id_NumBlocksUsed
= id
->id_NumBlocks
;
74 id
->id_BytesPerBlock
= de
->de_SizeBlock
<< 2;
76 id
->id_DiskState
= ID_VALIDATED
;
78 if (glob
->disk_inhibited
)
79 id
->id_DiskType
= ID_BUSY
;
80 else if (glob
->disk_inserted
)
81 id
->id_DiskType
= ID_NOT_REALLY_DOS
; //ID_UNREADABLE_DISK;
83 id
->id_DiskType
= ID_NO_DISK_PRESENT
;
85 id
->id_VolumeNode
= BNULL
;
86 id
->id_InUse
= DOSFALSE
;
90 void SendVolumePacket(struct DosList
*vol
, ULONG action
,
93 struct DosPacket
*dospacket
;
95 dospacket
= AllocDosObject(DOS_STDPKT
, TAG_DONE
);
96 dospacket
->dp_Type
= ACTION_DISK_CHANGE
;
97 dospacket
->dp_Arg1
= ID_FAT_DISK
;
98 dospacket
->dp_Arg2
= (IPTR
) vol
;
99 dospacket
->dp_Arg3
= action
;
100 dospacket
->dp_Port
= NULL
;
102 PutMsg(glob
->ourport
, dospacket
->dp_Link
);
105 void DoDiskInsert(struct Globals
*glob
)
110 struct VolumeInfo
*vol_info
= NULL
;
111 struct GlobalLock
*global_lock
;
112 struct ExtFileLock
*ext_lock
;
113 struct MinNode
*lock_node
;
115 struct NotifyNode
*nn
;
116 struct DosList
*newvol
= NULL
;
119 && (sb
= AllocVecPooled(glob
->mempool
, sizeof(struct FSSuper
))))
121 memset(sb
, 0, sizeof(struct FSSuper
));
124 err
= ReadFATSuper(sb
);
128 /* Scan volume list for a matching volume (would be better to
129 * match by serial number) */
130 dl
= LockDosList(LDF_VOLUMES
| LDF_WRITE
);
131 dl
= FindDosEntry(dl
, sb
->volume
.name
+ 1,
132 LDF_VOLUMES
| LDF_WRITE
);
133 UnLockDosList(LDF_VOLUMES
| LDF_WRITE
);
136 CompareDates(&dl
->dol_misc
.dol_volume
.dol_VolumeDate
,
137 &sb
->volume
.create_time
) != 0)
142 dl
->dol_Task
= glob
->ourport
;
145 D(bug("\tFound old volume.\n"));
147 vol_info
= BADDR(dl
->dol_misc
.dol_volume
.dol_LockList
);
149 #if 0 /* No point until we match volumes by serial number */
151 #ifdef AROS_FAST_BPTR
152 /* ReadFATSuper() sets a null byte after the
153 * string, so this should be fine */
154 CopyMem(sb
->volume
.name
+ 1, dl
->dol_Name
,
155 sb
->volume
.name
[0] + 1);
157 CopyMem(sb
->volume
.name
, dl
->dol_Name
,
158 sb
->volume
.name
[0] + 2);
162 /* Patch locks and notifications to match this handler
164 ForeachNode(&vol_info
->locks
, global_lock
)
166 ForeachNode(&global_lock
->locks
, lock_node
)
168 ext_lock
= LOCKFROMNODE(lock_node
);
169 D(bug("[fat] Patching adopted lock %p. old port = %p,"
170 " new port = %p\n", ext_lock
,
171 ext_lock
->fl_Task
, glob
->ourport
));
172 ext_lock
->fl_Task
= glob
->ourport
;
174 ext_lock
->ioh
.sb
= sb
;
178 ForeachNode(&vol_info
->root_lock
.locks
, lock_node
)
180 ext_lock
= LOCKFROMNODE(lock_node
);
181 D(bug("[fat] Patching adopted ROOT lock %p. old port = %p,"
182 " new port = %p\n", ext_lock
, ext_lock
->fl_Task
,
184 ext_lock
->fl_Task
= glob
->ourport
;
186 ext_lock
->ioh
.sb
= sb
;
189 ForeachNode(&vol_info
->notifies
, nn
)
190 nn
->nr
->nr_Handler
= glob
->ourport
;
194 D(bug("\tCreating new volume.\n"));
196 /* Create transferable core volume info */
198 CreatePool(MEMF_PUBLIC
, DEF_POOL_SIZE
,
203 AllocVecPooled(pool
, sizeof(struct VolumeInfo
));
204 if (vol_info
!= NULL
)
206 vol_info
->mem_pool
= pool
;
207 vol_info
->id
= sb
->volume_id
;
208 NEWLIST(&vol_info
->locks
);
209 NEWLIST(&vol_info
->notifies
);
211 vol_info
->root_lock
.dir_cluster
= FAT_ROOTDIR_MARK
;
212 vol_info
->root_lock
.dir_entry
= FAT_ROOTDIR_MARK
;
213 vol_info
->root_lock
.access
= SHARED_LOCK
;
214 vol_info
->root_lock
.first_cluster
= 0;
215 vol_info
->root_lock
.attr
= ATTR_DIRECTORY
;
216 vol_info
->root_lock
.size
= 0;
217 CopyMem(sb
->volume
.name
, vol_info
->root_lock
.name
,
218 sb
->volume
.name
[0] + 1);
219 NEWLIST(&vol_info
->root_lock
.locks
);
223 AllocVecPooled(pool
, sizeof(struct DosList
))))
225 newvol
->dol_Next
= BNULL
;
226 newvol
->dol_Type
= DLT_VOLUME
;
227 newvol
->dol_Task
= glob
->ourport
;
228 newvol
->dol_Lock
= BNULL
;
230 CopyMem(&sb
->volume
.create_time
,
231 &newvol
->dol_misc
.dol_volume
.dol_VolumeDate
,
232 sizeof(struct DateStamp
));
234 newvol
->dol_misc
.dol_volume
.dol_LockList
=
237 newvol
->dol_misc
.dol_volume
.dol_DiskType
=
238 (sb
->type
== 12) ? ID_FAT12_DISK
:
239 (sb
->type
== 16) ? ID_FAT16_DISK
:
240 (sb
->type
== 32) ? ID_FAT32_DISK
:
243 if ((newvol
->dol_Name
= MKBADDR(
244 AllocVecPooled(pool
, FAT_MAX_SHORT_NAME
+ 2))))
246 #ifdef AROS_FAST_BPTR
247 /* ReadFATSuper() sets a null byte after the
248 * string, so this should be fine */
249 CopyMem(sb
->volume
.name
+ 1, newvol
->dol_Name
,
250 sb
->volume
.name
[0] + 1);
252 CopyMem(sb
->volume
.name
,
253 BADDR(newvol
->dol_Name
),
254 sb
->volume
.name
[0] + 2);
257 sb
->doslist
= newvol
;
260 if (vol_info
== NULL
|| newvol
== NULL
)
269 SendEvent(IECLASS_DISKINSERTED
, glob
);
271 SendVolumePacket(newvol
, ACTION_VOLUME_ADD
, glob
);
273 D(bug("\tDisk successfully initialised\n"));
278 FreeVecPooled(glob
->mempool
, sb
);
281 SendEvent(IECLASS_DISKINSERTED
, glob
);
286 BOOL
AttemptDestroyVolume(struct FSSuper
*sb
)
288 struct Globals
*glob
= sb
->glob
;
289 BOOL destroyed
= FALSE
;
291 D(bug("[fat] Attempting to destroy volume\n"));
293 /* Check if the volume can be removed */
294 if (IsListEmpty(&sb
->info
->locks
) && IsListEmpty(&sb
->info
->notifies
))
296 D(bug("\tRemoving volume completely\n"));
301 Remove((struct Node
*)sb
);
303 SendVolumePacket(sb
->doslist
, ACTION_VOLUME_REMOVE
, glob
);
306 FreeVecPooled(glob
->mempool
, sb
);
313 void DoDiskRemove(struct Globals
*glob
)
318 struct FSSuper
*sb
= glob
->sb
;
320 if (!AttemptDestroyVolume(sb
))
322 sb
->doslist
->dol_Task
= NULL
;
324 D(bug("\tMoved in-memory super block to spare list. "
325 "Waiting for locks and notifications to be freed\n"));
326 AddTail((struct List
*)&glob
->sblist
, (struct Node
*)sb
);
327 SendEvent(IECLASS_DISKREMOVED
, glob
);
332 void ProcessDiskChange(struct Globals
*glob
)
334 D(bug("\nGot disk change request\n"));
336 if (glob
->disk_inhibited
)
338 D(bug("Disk is inhibited, ignoring disk change\n"));
342 glob
->diskioreq
->iotd_Req
.io_Command
= TD_CHANGESTATE
;
343 glob
->diskioreq
->iotd_Req
.io_Data
= NULL
;
344 glob
->diskioreq
->iotd_Req
.io_Length
= 0;
345 glob
->diskioreq
->iotd_Req
.io_Flags
= IOF_QUICK
;
346 DoIO((struct IORequest
*)glob
->diskioreq
);
348 if (glob
->diskioreq
->iotd_Req
.io_Error
== 0
349 && glob
->diskioreq
->iotd_Req
.io_Actual
== 0)
351 /* Disk has been inserted. */
352 D(bug("\tDisk has been inserted\n"));
353 glob
->disk_inserted
= TRUE
;
358 /* Disk has been removed. */
359 D(bug("\tDisk has been removed\n"));
360 glob
->disk_inserted
= FALSE
;
367 void UpdateDisk(struct Globals
*glob
)
370 Cache_Flush(glob
->sb
->cache
);
372 glob
->diskioreq
->iotd_Req
.io_Command
= CMD_UPDATE
;
373 DoIO((struct IORequest
*)glob
->diskioreq
);
375 /* Turn off motor (where applicable) if nothing has happened during the
376 * last timer period */
377 if (!glob
->restart_timer
)
379 D(bug("Stopping drive motor\n"));
380 glob
->diskioreq
->iotd_Req
.io_Command
= TD_MOTOR
;
381 glob
->diskioreq
->iotd_Req
.io_Length
= 0;
382 DoIO((struct IORequest
*)glob
->diskioreq
);
386 /* Probe the device to determine 64-bit support */
387 void Probe_64bit_support(struct Globals
*glob
)
389 struct NSDeviceQueryResult nsd_query
;
392 glob
->readcmd
= CMD_READ
;
393 glob
->writecmd
= CMD_WRITE
;
396 glob
->diskioreq
->iotd_Req
.io_Command
= TD_READ64
;
397 glob
->diskioreq
->iotd_Req
.io_Offset
= 0;
398 glob
->diskioreq
->iotd_Req
.io_Length
= 0;
399 glob
->diskioreq
->iotd_Req
.io_Actual
= 0;
400 glob
->diskioreq
->iotd_Req
.io_Data
= 0;
402 if (DoIO((struct IORequest
*)glob
->diskioreq
) != IOERR_NOCMD
)
404 D(bug("Probe_64bit_support:"
405 " device supports 64-bit trackdisk extensions\n"));
406 glob
->readcmd
= TD_READ64
;
407 glob
->writecmd
= TD_WRITE64
;
411 glob
->diskioreq
->iotd_Req
.io_Command
= NSCMD_DEVICEQUERY
;
412 glob
->diskioreq
->iotd_Req
.io_Length
=
413 sizeof(struct NSDeviceQueryResult
);
414 glob
->diskioreq
->iotd_Req
.io_Data
= (APTR
) &nsd_query
;
416 if (DoIO((struct IORequest
*)glob
->diskioreq
) == 0)
417 for (nsd_cmd
= nsd_query
.SupportedCommands
; *nsd_cmd
!= 0;
420 if (*nsd_cmd
== NSCMD_TD_READ64
)
422 D(bug("Probe_64bit_support:"
423 " device supports NSD 64-bit trackdisk extensions\n"));
424 glob
->readcmd
= NSCMD_TD_READ64
;
425 glob
->writecmd
= NSCMD_TD_WRITE64
;
431 /* N.B. returns an Exec error code, not a DOS error code! */
432 LONG
AccessDisk(BOOL do_write
, ULONG num
, ULONG nblocks
, ULONG block_size
,
433 UBYTE
*data
, APTR priv
)
435 struct Globals
*glob
= priv
;
442 #if DEBUG_CACHESTATS > 1
443 ErrorMessage("Accessing %lu sector(s) starting at %lu.\n"
444 "First volume sector is %lu, sector size is %lu.\n", "OK", nblocks
,
445 num
, glob
->sb
->first_device_sector
, block_size
);
448 /* Adjust parameters if range is partially outside boundaries, or
449 * warn user and bale out if completely outside boundaries */
452 start
= glob
->sb
->first_device_sector
;
453 if (num
+ nblocks
<= glob
->sb
->first_device_sector
)
455 if (num
!= glob
->last_num
)
457 glob
->last_num
= num
;
458 ErrorMessage("A handler attempted to %s %lu sector(s)\n"
459 "starting from %lu, before the actual volume space.\n"
460 "First volume sector is %lu, sector size is %lu.\n"
461 "Either your disk is damaged or it is a bug in\n"
462 "the handler. Please check your disk and/or\n"
463 "report this problem to the developers team.", "OK",
464 (IPTR
) (do_write
? "write" : "read"), nblocks
, num
,
465 glob
->sb
->first_device_sector
, block_size
);
467 return IOERR_BADADDRESS
;
469 else if (num
< start
)
471 nblocks
-= start
- num
;
472 data
+= (start
- num
) * block_size
;
476 end
= glob
->sb
->first_device_sector
+ glob
->sb
->total_sectors
;
479 if (num
!= glob
->last_num
)
481 glob
->last_num
= num
;
482 ErrorMessage("A handler attempted to %s %lu sector(s)\n"
483 "starting from %lu, beyond the actual volume space.\n"
484 "Last volume sector is %lu, sector size is %lu.\n"
485 "Either your disk is damaged or it is a bug in\n"
486 "the handler. Please check your disk and/or\n"
487 "report this problem to the developers team.", "OK",
488 (IPTR
) (do_write
? "write" : "read"), nblocks
, num
,
489 end
- 1, block_size
);
491 return IOERR_BADADDRESS
;
493 else if (num
+ nblocks
> end
)
497 off
= ((UQUAD
) num
) * block_size
;
501 glob
->diskioreq
->iotd_Req
.io_Offset
= off
& 0xFFFFFFFF;
502 glob
->diskioreq
->iotd_Req
.io_Actual
= off
>> 32;
504 glob
->diskioreq
->iotd_Req
.io_Length
= nblocks
* block_size
;
505 glob
->diskioreq
->iotd_Req
.io_Data
= data
;
506 glob
->diskioreq
->iotd_Req
.io_Command
=
507 do_write
? glob
->writecmd
: glob
->readcmd
;
509 err
= DoIO((struct IORequest
*)glob
->diskioreq
);
513 if (glob
->sb
&& glob
->sb
->volume
.name
[0] != '\0')
514 snprintf(vol_name
, 100, "Volume %s",
515 glob
->sb
->volume
.name
+ 1);
517 snprintf(vol_name
, 100, "Device %s",
518 AROS_BSTR_ADDR(glob
->devnode
->dol_Name
));
521 retry
= ErrorMessage("%s\nhas a %s error\n"
522 "in the block range\n%lu to %lu",
523 "Retry|Cancel", (IPTR
)vol_name
,
524 (IPTR
)(do_write
? "write" : "read"), num
,
527 retry
= ErrorMessage("%s\nhas a %s error\n"
529 "Retry|Cancel", (IPTR
)vol_name
,
530 (IPTR
)(do_write
? "write" : "read"), num
);