codeset
[AROS.git] / workbench / fs / fat / disk.c
blob955fdc7c3778977afd1813a46495504d9ff9894b
1 /*
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.
10 * $Id$
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>
20 #include <dos/dos.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() */
33 #include "fat_fs.h"
34 #include "fat_protos.h"
36 #define DEBUG DEBUG_MISC
37 #include "debug.h"
39 #ifndef ID_BUSY
40 #define ID_BUSY 0x42555359
41 #endif
43 /* TD64 commands */
44 #ifndef TD_READ64
45 #define TD_READ64 24
46 #define TD_WRITE64 25
47 #endif
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;
56 if (glob->sb) {
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;
69 else {
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;
80 else
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) {
102 struct FSSuper *sb;
103 ULONG err;
105 if (glob->sb == NULL && (sb = AllocVecPooled(glob->mempool, sizeof(struct FSSuper)))) {
106 memset(sb, 0, sizeof(struct FSSuper));
108 err = ReadFATSuper(sb);
109 if (err == 0) {
110 LONG found = FALSE;
112 if (glob->sblist) {
113 struct FSSuper *ptr = glob->sblist, *prev = NULL;
115 while (ptr != NULL) {
116 if (CompareFATSuper(sb, ptr) == 0)
117 break;
119 prev = ptr;
120 ptr = ptr->next;
123 if (ptr) {
124 D(bug("\tFound FAT FS Super Block in spare list, freeing obsolete old one\n"));
126 sb->doslist = ptr->doslist;
127 ptr->doslist = NULL;
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]));
133 #else
134 sb->doslist->dol_Name = (BSTR)MKBADDR(&sb->volume.name);
135 #endif
137 if (prev)
138 prev->next = ptr->next;
139 else
140 glob->sblist = ptr->next;
142 FreeFATSuper(ptr);
143 FreeVecPooled(glob->mempool, ptr);
145 found = TRUE;
149 if (!found) {
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 :
167 ID_FAT12_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]));
173 #else
174 newvol->dol_Name = (BSTR)MKBADDR(&sb->volume.name);
175 #endif
177 sb->doslist = newvol;
179 SendVolumePacket(newvol, ACTION_VOLUME_ADD);
183 else
184 SendEvent(IECLASS_DISKINSERTED);
186 glob->sb = sb;
187 glob->last_num = -1;
189 D(bug("\tDisk successfully initialised\n"));
191 return;
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);
204 return;
207 void DoDiskRemove(void) {
209 if (glob->sb) {
210 struct FSSuper *sb = glob->sb;
212 glob->sb = NULL;
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);
219 sb->doslist = NULL;
220 FreeFATSuper(sb);
221 FreeVecPooled(glob->mempool, sb);
223 else {
224 sb->next = glob->sblist;
225 glob->sblist = sb;
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"));
239 return;
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;
252 DoDiskInsert();
254 else {
255 /* Disk has been removed. */
256 D(bug("\tDisk has been removed\n"));
257 glob->disk_inserted = FALSE;
258 DoDiskRemove();
261 D(bug("Done\n"));
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;
283 UWORD *nsd_cmd;
285 glob->readcmd = CMD_READ;
286 glob->writecmd = CMD_WRITE;
288 /* probe TD64 */
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;
301 /* probe NSD */
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;
312 break;
317 ULONG AccessDisk(BOOL do_write, ULONG num, ULONG nblocks, ULONG block_size,
318 UBYTE *data) {
319 UQUAD off;
320 ULONG err;
321 ULONG start, end;
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);
327 #endif
329 /* Adjust parameters if range is partially outside boundaries, or
330 * warn user and bale out if completely outside boundaries */
331 if (glob->sb) {
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;
350 num = start;
353 end = glob->sb->first_device_sector + glob->sb->total_sectors;
354 if (num >= end) {
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)
369 nblocks = end - num;
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);
386 return err;