Made comments and debug output more consistent.
[AROS.git] / rom / filesys / fat / disk.c
blob485968342dbe4fbde91c5806a9cef5831a445b7d
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>
30 #include "fat_fs.h"
31 #include "fat_protos.h"
33 #define DEBUG DEBUG_MISC
34 #include "debug.h"
36 #ifndef ID_BUSY
37 #define ID_BUSY 0x42555359
38 #endif
40 /* TD64 commands */
41 #ifndef TD_READ64
42 #define TD_READ64 24
43 #define TD_WRITE64 25
44 #endif
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;
54 if (glob->sb)
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;
68 else
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;
81 else
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,
90 struct Globals *glob)
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)
106 struct FSSuper *sb;
107 ULONG err;
108 struct DosList *dl;
109 struct VolumeInfo *vol_info = NULL;
110 struct GlobalLock *global_lock;
111 struct ExtFileLock *ext_lock;
112 struct MinNode *lock_node;
113 APTR pool;
114 struct NotifyNode *nn;
115 struct DosList *newvol = NULL;
117 if (glob->sb == NULL
118 && (sb = AllocVecPooled(glob->mempool, sizeof(struct FSSuper))))
120 memset(sb, 0, sizeof(struct FSSuper));
122 sb->glob = glob;
123 err = ReadFATSuper(sb);
124 if (err == 0)
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);
134 if (dl != NULL &&
135 CompareDates(&dl->dol_misc.dol_volume.dol_VolumeDate,
136 &sb->volume.create_time) != 0)
137 dl = NULL;
139 if (dl != NULL)
141 dl->dol_Task = glob->ourport;
142 sb->doslist = dl;
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 */
149 /* Update name */
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);
155 #else
156 CopyMem(sb->volume.name, dl->dol_Name,
157 sb->volume.name[0] + 2);
158 #endif
159 #endif
161 /* Patch locks and notifications to match this handler
162 * instance */
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;
172 ext_lock->sb = sb;
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,
182 glob->ourport));
183 ext_lock->fl_Task = glob->ourport;
184 ext_lock->sb = sb;
185 ext_lock->ioh.sb = sb;
188 ForeachNode(&vol_info->notifies, nn)
189 nn->nr->nr_Handler = glob->ourport;
191 else
193 D(bug("\tCreating new volume.\n"));
195 /* Create transferable core volume info */
196 pool =
197 CreatePool(MEMF_PUBLIC, DEF_POOL_SIZE,
198 DEF_POOL_THRESHOLD);
199 if (pool != NULL)
201 vol_info =
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);
221 if ((newvol =
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 =
234 MKBADDR(vol_info);
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 :
240 ID_FAT12_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);
250 #else
251 CopyMem(sb->volume.name,
252 BADDR(newvol->dol_Name),
253 sb->volume.name[0] + 2);
254 #endif
256 sb->doslist = newvol;
259 if (vol_info == NULL || newvol == NULL)
260 DeletePool(pool);
264 sb->info = vol_info;
265 glob->last_num = -1;
267 if (dl != NULL)
268 SendEvent(IECLASS_DISKINSERTED, glob);
269 else
270 SendVolumePacket(newvol, ACTION_VOLUME_ADD, glob);
272 D(bug("\tDisk successfully initialised\n"));
274 return;
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.", NULL, glob);
283 FreeVecPooled(glob->mempool, sb);
286 SendEvent(IECLASS_DISKINSERTED, glob);
288 return;
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"));
303 if (sb == glob->sb)
304 glob->sb = NULL;
305 else
306 Remove((struct Node *)sb);
308 SendVolumePacket(sb->doslist, ACTION_VOLUME_REMOVE, glob);
310 FreeFATSuper(sb);
311 FreeVecPooled(glob->mempool, sb);
312 destroyed = TRUE;
315 return destroyed;
318 void DoDiskRemove(struct Globals *glob)
321 if (glob->sb)
323 struct FSSuper *sb = glob->sb;
325 if (!AttemptDestroyVolume(sb))
327 sb->doslist->dol_Task = NULL;
328 glob->sb = 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"));
344 return;
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;
359 DoDiskInsert(glob);
361 else
363 /* Disk has been removed. */
364 D(bug("\tDisk has been removed\n"));
365 glob->disk_inserted = FALSE;
366 DoDiskRemove(glob);
369 D(bug("Done\n"));
372 void UpdateDisk(struct Globals *glob)
374 if (glob->sb)
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;
398 UWORD *nsd_cmd;
400 glob->readcmd = CMD_READ;
401 glob->writecmd = CMD_WRITE;
403 /* Probe TD64 */
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;
418 /* Probe NSD */
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;
426 nsd_cmd++)
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;
434 break;
439 ULONG AccessDisk(BOOL do_write, ULONG num, ULONG nblocks, ULONG block_size,
440 UBYTE *data, APTR priv)
442 struct Globals *glob = priv;
443 UQUAD off;
444 ULONG err;
445 ULONG start, end;
447 #if DEBUG_CACHESTATS > 1
448 ErrorMessage("Accessing %lu sector(s) starting at %lu.\n"
449 "First volume sector is %lu, sector size is %lu.\n", nblocks, num,
450 glob->sb->first_device_sector, block_size);
451 #endif
453 /* Adjust parameters if range is partially outside boundaries, or
454 * warn user and bale out if completely outside boundaries */
455 if (glob->sb)
457 start = glob->sb->first_device_sector;
458 if (num + nblocks <= glob->sb->first_device_sector)
460 if (num != glob->last_num)
462 glob->last_num = num;
463 ErrorMessage("A handler attempted to %s %lu sector(s)\n"
464 "starting from %lu, before the actual volume space.\n"
465 "First volume sector is %lu, sector size is %lu.\n"
466 "Either your disk is damaged or it is a bug in\n"
467 "the handler. Please check your disk and/or\n"
468 "report this problem to the developers team.",
469 (IPTR) (do_write ? "write" : "read"), nblocks, num,
470 glob->sb->first_device_sector, block_size);
472 return IOERR_BADADDRESS;
474 else if (num < start)
476 nblocks -= start - num;
477 data += (start - num) * block_size;
478 num = start;
481 end = glob->sb->first_device_sector + glob->sb->total_sectors;
482 if (num >= end)
484 if (num != glob->last_num)
486 glob->last_num = num;
487 ErrorMessage("A handler attempted to %s %lu sector(s)\n"
488 "starting from %lu, beyond the actual volume space.\n"
489 "Last volume sector is %lu, sector size is %lu.\n"
490 "Either your disk is damaged or it is a bug in\n"
491 "the handler. Please check your disk and/or\n"
492 "report this problem to the developers team.",
493 (IPTR) (do_write ? "write" : "read"), nblocks, num,
494 end - 1, block_size);
496 return IOERR_BADADDRESS;
498 else if (num + nblocks > end)
499 nblocks = end - num;
502 off = ((UQUAD) num) * block_size;
504 glob->diskioreq->iotd_Req.io_Offset = off & 0xFFFFFFFF;
505 glob->diskioreq->iotd_Req.io_Actual = off >> 32;
507 if (glob->diskioreq->iotd_Req.io_Actual && (glob->readcmd == CMD_READ))
508 return IOERR_BADADDRESS;
510 glob->diskioreq->iotd_Req.io_Length = nblocks * block_size;
511 glob->diskioreq->iotd_Req.io_Data = data;
512 glob->diskioreq->iotd_Req.io_Command =
513 do_write ? glob->writecmd : glob->readcmd;
515 err = DoIO((struct IORequest *)glob->diskioreq);
517 return err;