2 * fat.handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007-2010 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/execbase.h>
17 #include <exec/libraries.h>
18 #include <exec/errors.h>
21 #include <dos/dosextens.h>
22 #include <dos/dostags.h>
23 #include <dos/filehandler.h>
24 #include <devices/newstyle.h>
25 #include <devices/trackdisk.h>
26 #include <devices/inputevent.h>
28 #include <proto/exec.h>
29 #include <proto/dos.h>
31 #include <string.h> /* for memset() */
34 #include "fat_protos.h"
36 #define DEBUG DEBUG_MISC
40 #define ID_BUSY 0x42555359
49 void FillDiskInfo (struct InfoData
*id
) {
50 struct DosEnvec
*de
= BADDR(glob
->fssm
->fssm_Environ
);
52 id
->id_NumSoftErrors
= 0;
53 id
->id_UnitNumber
= glob
->fssm
->fssm_Unit
;
54 id
->id_DiskState
= ID_VALIDATED
;
57 CountFreeClusters(glob
->sb
);
59 id
->id_NumBlocks
= glob
->sb
->total_sectors
;
60 id
->id_NumBlocksUsed
= glob
->sb
->total_sectors
- ((glob
->sb
->free_clusters
* glob
->sb
->clustersize
) >> glob
->sb
->sectorsize_bits
);
61 id
->id_BytesPerBlock
= glob
->sb
->sectorsize
;
63 id
->id_DiskType
= ID_DOS_DISK
;
65 id
->id_VolumeNode
= MKBADDR(glob
->sb
->doslist
);
66 id
->id_InUse
= glob
->sb
->doslist
->dol_misc
.dol_volume
.dol_LockList
? DOSTRUE
: DOSFALSE
;
70 id
->id_NumBlocks
= de
->de_Surfaces
* de
->de_BlocksPerTrack
* (de
->de_HighCyl
+1 - de
->de_LowCyl
) / de
->de_SectorPerBlock
;
71 id
->id_NumBlocksUsed
= id
->id_NumBlocks
;
72 id
->id_BytesPerBlock
= de
->de_SizeBlock
<< 2;
74 id
->id_DiskState
= ID_VALIDATED
;
76 if (glob
->disk_inhibited
)
77 id
->id_DiskType
= ID_BUSY
;
78 else if (glob
->disk_inserted
)
79 id
->id_DiskType
= ID_NOT_REALLY_DOS
; //ID_UNREADABLE_DISK;
81 id
->id_DiskType
= ID_NO_DISK_PRESENT
;
83 id
->id_VolumeNode
= NULL
;
84 id
->id_InUse
= DOSFALSE
;
88 void SendVolumePacket(struct DosList
*vol
, ULONG action
) {
89 struct DosPacket
*dospacket
;
91 dospacket
= AllocDosObject(DOS_STDPKT
, TAG_DONE
);
92 dospacket
->dp_Type
= ACTION_DISK_CHANGE
;
93 dospacket
->dp_Arg1
= ID_FAT_DISK
;
94 dospacket
->dp_Arg2
= (IPTR
)vol
;
95 dospacket
->dp_Arg3
= action
;
96 dospacket
->dp_Port
= NULL
;
98 PutMsg(glob
->ourport
, dospacket
->dp_Link
);
101 void DoDiskInsert(void) {
105 if (glob
->sb
== NULL
&& (sb
= AllocVecPooled(glob
->mempool
, sizeof(struct FSSuper
)))) {
106 memset(sb
, 0, sizeof(struct FSSuper
));
108 err
= ReadFATSuper(sb
);
113 struct FSSuper
*ptr
= glob
->sblist
, *prev
= NULL
;
115 while (ptr
!= NULL
) {
116 if (CompareFATSuper(sb
, ptr
) == 0)
124 D(bug("\tFound FAT FS Super Block in spare list, freeing obsolete old one\n"));
126 sb
->doslist
= ptr
->doslist
;
129 #ifdef AROS_FAST_BPTR
130 /* ReadFATSuper() sets a null byte * after the string, so
131 * this should be fine */
132 sb
->doslist
->dol_Name
= (BSTR
)MKBADDR(&(sb
->volume
.name
[1]));
134 sb
->doslist
->dol_Name
= (BSTR
)MKBADDR(&sb
->volume
.name
);
138 prev
->next
= ptr
->next
;
140 glob
->sblist
= ptr
->next
;
143 FreeVecPooled(glob
->mempool
, ptr
);
150 struct DosList
*newvol
;
152 D(bug("\tCreating new volume.\n"));
154 if ((newvol
= AllocVecPooled(glob
->mempool
, sizeof(struct DosList
)))) {
155 newvol
->dol_Next
= NULL
;
156 newvol
->dol_Type
= DLT_VOLUME
;
157 newvol
->dol_Task
= glob
->ourport
;
158 newvol
->dol_Lock
= NULL
;
160 CopyMem(&sb
->volume
.create_time
, &newvol
->dol_misc
.dol_volume
.dol_VolumeDate
, sizeof(struct DateStamp
));
162 newvol
->dol_misc
.dol_volume
.dol_LockList
= NULL
;
164 newvol
->dol_misc
.dol_volume
.dol_DiskType
= (sb
->type
== 12) ? ID_FAT12_DISK
:
165 (sb
->type
== 16) ? ID_FAT16_DISK
:
166 (sb
->type
== 32) ? ID_FAT32_DISK
:
169 #ifdef AROS_FAST_BPTR
170 /* ReadFATSuper() sets a null byte * after the string, so
171 * this should be fine */
172 newvol
->dol_Name
= (BSTR
)MKBADDR(&(sb
->volume
.name
[1]));
174 newvol
->dol_Name
= (BSTR
)MKBADDR(&sb
->volume
.name
);
177 sb
->doslist
= newvol
;
179 SendVolumePacket(newvol
, ACTION_VOLUME_ADD
);
184 SendEvent(IECLASS_DISKINSERTED
);
189 D(bug("\tDisk successfully initialised\n"));
192 } else if (err
== IOERR_BADADDRESS
)
193 ErrorMessage("Your device does not support 64-bit\n"
194 "access to the disk while it is needed!\n"
195 "In order to prevent data damage access to\n"
196 "this disk was blocked.\n"
197 "Please upgrade your device driver.");
199 FreeVecPooled(glob
->mempool
, sb
);
202 SendEvent(IECLASS_DISKINSERTED
);
207 void DoDiskRemove(void) {
210 struct FSSuper
*sb
= glob
->sb
;
214 if (sb
->doslist
->dol_misc
.dol_volume
.dol_LockList
== NULL
) { /* check if the device can be removed */
215 D(bug("\tRemoving disk completely\n"));
217 SendVolumePacket(sb
->doslist
, ACTION_VOLUME_REMOVE
);
221 FreeVecPooled(glob
->mempool
, sb
);
224 sb
->next
= glob
->sblist
;
227 D(bug("\tMoved in-memory super block to spare list. Waiting for locks to be freed\n"));
229 SendEvent(IECLASS_DISKREMOVED
);
234 void ProcessDiskChange(void) {
235 D(bug("\nGot disk change request\n"));
237 if (glob
->disk_inhibited
) {
238 D(bug("Disk is inhibited, ignoring disk change\n"));
242 glob
->diskioreq
->iotd_Req
.io_Command
= TD_CHANGESTATE
;
243 glob
->diskioreq
->iotd_Req
.io_Data
= NULL
;
244 glob
->diskioreq
->iotd_Req
.io_Length
= 0;
245 glob
->diskioreq
->iotd_Req
.io_Flags
= IOF_QUICK
;
246 DoIO((struct IORequest
*) glob
->diskioreq
);
248 if (glob
->diskioreq
->iotd_Req
.io_Error
== 0 && glob
->diskioreq
->iotd_Req
.io_Actual
== 0) {
249 /* Disk has been inserted. */
250 D(bug("\tDisk has been inserted\n"));
251 glob
->disk_inserted
= TRUE
;
255 /* Disk has been removed. */
256 D(bug("\tDisk has been removed\n"));
257 glob
->disk_inserted
= FALSE
;
264 void UpdateDisk(void) {
265 Cache_Flush(glob
->sb
->cache
);
267 glob
->diskioreq
->iotd_Req
.io_Command
= CMD_UPDATE
;
268 DoIO((struct IORequest
*)glob
->diskioreq
);
270 /* Turn off motor (where applicable) if nothing has happened during the
271 * last timer period */
272 if (!glob
->restart_timer
) {
273 D(bug("Stopping drive motor\n"));
274 glob
->diskioreq
->iotd_Req
.io_Command
= TD_MOTOR
;
275 glob
->diskioreq
->iotd_Req
.io_Length
= 0;
276 DoIO((struct IORequest
*)glob
->diskioreq
);
280 /* probe the device to determine 64-bit support */
281 void Probe_64bit_support(void) {
282 struct NSDeviceQueryResult nsd_query
;
285 glob
->readcmd
= CMD_READ
;
286 glob
->writecmd
= CMD_WRITE
;
289 glob
->diskioreq
->iotd_Req
.io_Command
= TD_READ64
;
290 glob
->diskioreq
->iotd_Req
.io_Offset
= 0;
291 glob
->diskioreq
->iotd_Req
.io_Length
= 0;
292 glob
->diskioreq
->iotd_Req
.io_Actual
= 0;
293 glob
->diskioreq
->iotd_Req
.io_Data
= 0;
295 if (DoIO((struct IORequest
*) glob
->diskioreq
) != IOERR_NOCMD
) {
296 D(bug("Probe_64bit_support: device supports 64-bit trackdisk extensions\n"));
297 glob
->readcmd
= TD_READ64
;
298 glob
->writecmd
= TD_WRITE64
;
302 glob
->diskioreq
->iotd_Req
.io_Command
= NSCMD_DEVICEQUERY
;
303 glob
->diskioreq
->iotd_Req
.io_Length
= sizeof(struct NSDeviceQueryResult
);
304 glob
->diskioreq
->iotd_Req
.io_Data
= (APTR
) &nsd_query
;
306 if (DoIO((struct IORequest
*) glob
->diskioreq
) == 0)
307 for (nsd_cmd
= nsd_query
.SupportedCommands
; *nsd_cmd
!= 0; nsd_cmd
++) {
308 if (*nsd_cmd
== NSCMD_TD_READ64
) {
309 D(bug("Probe_64bit_support: device supports NSD 64-bit trackdisk extensions\n"));
310 glob
->readcmd
= NSCMD_TD_READ64
;
311 glob
->writecmd
= NSCMD_TD_WRITE64
;
317 ULONG
AccessDisk(BOOL do_write
, ULONG num
, ULONG nblocks
, ULONG block_size
,
323 #if DEBUG_CACHESTATS > 1
324 ErrorMessage("Accessing %lu sector(s) starting at %lu.\n"
325 "First volume sector is %lu, sector size is %lu.\n", nblocks
, num
,
326 glob
->sb
->first_device_sector
, block_size
);
329 /* Adjust parameters if range is partially outside boundaries, or
330 * warn user and bale out if completely outside boundaries */
332 start
= glob
->sb
->first_device_sector
;
333 if (num
+ nblocks
<= glob
->sb
->first_device_sector
) {
334 if (num
!= glob
->last_num
) {
335 glob
->last_num
= num
;
336 ErrorMessage("A handler attempted to %s %lu sector(s) starting\n"
337 "from %lu, before the actual volume space.\n"
338 "First volume sector is %lu, sector size is %lu.\n"
339 "Either your disk is damaged or it is a bug in\n"
340 "the handler. Please check your disk and/or\n"
341 "report this problem to the developers team.",
342 do_write
? "write" : "read", nblocks
, num
,
343 glob
->sb
->first_device_sector
, block_size
);
345 return IOERR_BADADDRESS
;
347 else if (num
< start
) {
348 nblocks
-= start
- num
;
349 data
+= (start
- num
) * block_size
;
353 end
= glob
->sb
->first_device_sector
+ glob
->sb
->total_sectors
;
355 if (num
!= glob
->last_num
) {
356 glob
->last_num
= num
;
357 ErrorMessage("A handler attempted to %s %lu sector(s) starting\n"
358 "from %lu, beyond the actual volume space.\n"
359 "Last volume sector is %lu, sector size is %lu.\n"
360 "Either your disk is damaged or it is a bug in\n"
361 "the handler. Please check your disk and/or\n"
362 "report this problem to the developers team.",
363 do_write
? "write" : "read", nblocks
, num
,
364 end
- 1, block_size
);
366 return IOERR_BADADDRESS
;
368 else if (num
+ nblocks
> end
)
372 off
= ((UQUAD
) num
) * block_size
;
374 glob
->diskioreq
->iotd_Req
.io_Offset
= off
& 0xFFFFFFFF;
375 glob
->diskioreq
->iotd_Req
.io_Actual
= off
>> 32;
377 if (glob
->diskioreq
->iotd_Req
.io_Actual
&& (glob
->readcmd
== CMD_READ
))
378 return IOERR_BADADDRESS
;
380 glob
->diskioreq
->iotd_Req
.io_Length
= nblocks
* block_size
;
381 glob
->diskioreq
->iotd_Req
.io_Data
= data
;
382 glob
->diskioreq
->iotd_Req
.io_Command
= do_write
? glob
->writecmd
: glob
->readcmd
;
384 err
= DoIO((struct IORequest
*) glob
->diskioreq
);