Improvements to error handling:
[AROS.git] / rom / filesys / fat / disk.c
blobbc6a70bb1057d6e15dc9b2ee842b070f66a7acf2
1 /*
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.
10 * $Id$
13 #define USE_INLINE_STDARG
15 #include <exec/types.h>
16 #include <exec/errors.h>
18 #include <dos/dos.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>
29 #include <stdio.h>
31 #include "fat_fs.h"
32 #include "fat_protos.h"
34 #define DEBUG DEBUG_MISC
35 #include "debug.h"
37 #ifndef ID_BUSY
38 #define ID_BUSY 0x42555359
39 #endif
41 /* TD64 commands */
42 #ifndef TD_READ64
43 #define TD_READ64 24
44 #define TD_WRITE64 25
45 #endif
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;
55 if (glob->sb)
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;
69 else
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;
82 else
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,
91 struct Globals *glob)
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)
107 struct FSSuper *sb;
108 ULONG err;
109 struct DosList *dl;
110 struct VolumeInfo *vol_info = NULL;
111 struct GlobalLock *global_lock;
112 struct ExtFileLock *ext_lock;
113 struct MinNode *lock_node;
114 APTR pool;
115 struct NotifyNode *nn;
116 struct DosList *newvol = NULL;
118 if (glob->sb == NULL
119 && (sb = AllocVecPooled(glob->mempool, sizeof(struct FSSuper))))
121 memset(sb, 0, sizeof(struct FSSuper));
123 sb->glob = glob;
124 err = ReadFATSuper(sb);
125 if (err == 0)
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);
135 if (dl != NULL &&
136 CompareDates(&dl->dol_misc.dol_volume.dol_VolumeDate,
137 &sb->volume.create_time) != 0)
138 dl = NULL;
140 if (dl != NULL)
142 dl->dol_Task = glob->ourport;
143 sb->doslist = dl;
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 */
150 /* Update name */
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);
156 #else
157 CopyMem(sb->volume.name, dl->dol_Name,
158 sb->volume.name[0] + 2);
159 #endif
160 #endif
162 /* Patch locks and notifications to match this handler
163 * instance */
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;
173 ext_lock->sb = sb;
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,
183 glob->ourport));
184 ext_lock->fl_Task = glob->ourport;
185 ext_lock->sb = sb;
186 ext_lock->ioh.sb = sb;
189 ForeachNode(&vol_info->notifies, nn)
190 nn->nr->nr_Handler = glob->ourport;
192 else
194 D(bug("\tCreating new volume.\n"));
196 /* Create transferable core volume info */
197 pool =
198 CreatePool(MEMF_PUBLIC, DEF_POOL_SIZE,
199 DEF_POOL_THRESHOLD);
200 if (pool != NULL)
202 vol_info =
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);
222 if ((newvol =
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 =
235 MKBADDR(vol_info);
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 :
241 ID_FAT12_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);
251 #else
252 CopyMem(sb->volume.name,
253 BADDR(newvol->dol_Name),
254 sb->volume.name[0] + 2);
255 #endif
257 sb->doslist = newvol;
260 if (vol_info == NULL || newvol == NULL)
261 DeletePool(pool);
265 sb->info = vol_info;
266 glob->last_num = -1;
268 if (dl != NULL)
269 SendEvent(IECLASS_DISKINSERTED, glob);
270 else
271 SendVolumePacket(newvol, ACTION_VOLUME_ADD, glob);
273 D(bug("\tDisk successfully initialised\n"));
275 return;
278 FreeVecPooled(glob->mempool, sb);
281 SendEvent(IECLASS_DISKINSERTED, glob);
283 return;
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"));
298 if (sb == glob->sb)
299 glob->sb = NULL;
300 else
301 Remove((struct Node *)sb);
303 SendVolumePacket(sb->doslist, ACTION_VOLUME_REMOVE, glob);
305 FreeFATSuper(sb);
306 FreeVecPooled(glob->mempool, sb);
307 destroyed = TRUE;
310 return destroyed;
313 void DoDiskRemove(struct Globals *glob)
316 if (glob->sb)
318 struct FSSuper *sb = glob->sb;
320 if (!AttemptDestroyVolume(sb))
322 sb->doslist->dol_Task = NULL;
323 glob->sb = 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"));
339 return;
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;
354 DoDiskInsert(glob);
356 else
358 /* Disk has been removed. */
359 D(bug("\tDisk has been removed\n"));
360 glob->disk_inserted = FALSE;
361 DoDiskRemove(glob);
364 D(bug("Done\n"));
367 void UpdateDisk(struct Globals *glob)
369 if (glob->sb)
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;
390 UWORD *nsd_cmd;
392 glob->readcmd = CMD_READ;
393 glob->writecmd = CMD_WRITE;
395 /* Probe TD64 */
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;
410 /* Probe NSD */
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;
418 nsd_cmd++)
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;
426 break;
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;
436 UQUAD off;
437 ULONG err;
438 ULONG start, end;
439 BOOL retry = TRUE;
440 TEXT vol_name[100];
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);
446 #endif
448 /* Adjust parameters if range is partially outside boundaries, or
449 * warn user and bale out if completely outside boundaries */
450 if (glob->sb)
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;
473 num = start;
476 end = glob->sb->first_device_sector + glob->sb->total_sectors;
477 if (num >= end)
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)
494 nblocks = end - num;
497 off = ((UQUAD) num) * block_size;
499 while (retry)
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);
511 if (err != 0)
513 if (glob->sb && glob->sb->volume.name[0] != '\0')
514 snprintf(vol_name, 100, "Volume %s",
515 glob->sb->volume.name + 1);
516 else
517 snprintf(vol_name, 100, "Device %s",
518 AROS_BSTR_ADDR(glob->devnode->dol_Name));
520 if (nblocks > 1)
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,
525 num + nblocks - 1);
526 else
527 retry = ErrorMessage("%s\nhas a %s error\n"
528 "on block %lu",
529 "Retry|Cancel", (IPTR)vol_name,
530 (IPTR)(do_write ? "write" : "read"), num);
532 else
533 retry = FALSE;
536 return err;