add remtask before addtask
[AROS.git] / rom / filesys / fat / disk.c
blob7346b808565fd4bb60c5343c37e40df305cfd6e2
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> /* for memset() */
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) {
47 struct DosEnvec *de = BADDR(glob->fssm->fssm_Environ);
49 id->id_NumSoftErrors = 0;
50 id->id_UnitNumber = glob->fssm->fssm_Unit;
51 id->id_DiskState = ID_VALIDATED;
53 if (glob->sb) {
54 id->id_NumBlocks = glob->sb->total_sectors;
55 id->id_NumBlocksUsed = glob->sb->total_sectors
56 - (glob->sb->free_clusters << glob->sb->cluster_sectors_bits);
57 id->id_BytesPerBlock = glob->sb->sectorsize;
59 id->id_DiskType = ID_DOS_DISK;
61 id->id_VolumeNode = MKBADDR(glob->sb->doslist);
62 id->id_InUse = (IsListEmpty(&glob->sb->info->locks)
63 && IsListEmpty(&glob->sb->info->notifies)) ? DOSFALSE : DOSTRUE;
66 else {
67 id->id_NumBlocks = de->de_Surfaces * de->de_BlocksPerTrack * (de->de_HighCyl+1 - de->de_LowCyl) / de->de_SectorPerBlock;
68 id->id_NumBlocksUsed = id->id_NumBlocks;
69 id->id_BytesPerBlock = de->de_SizeBlock << 2;
71 id->id_DiskState = ID_VALIDATED;
73 if (glob->disk_inhibited)
74 id->id_DiskType = ID_BUSY;
75 else if (glob->disk_inserted)
76 id->id_DiskType = ID_NOT_REALLY_DOS; //ID_UNREADABLE_DISK;
77 else
78 id->id_DiskType = ID_NO_DISK_PRESENT;
80 id->id_VolumeNode = BNULL;
81 id->id_InUse = DOSFALSE;
85 void SendVolumePacket(struct DosList *vol, ULONG action, struct Globals *glob) {
86 struct DosPacket *dospacket;
88 dospacket = AllocDosObject(DOS_STDPKT, TAG_DONE);
89 dospacket->dp_Type = ACTION_DISK_CHANGE;
90 dospacket->dp_Arg1 = ID_FAT_DISK;
91 dospacket->dp_Arg2 = (IPTR)vol;
92 dospacket->dp_Arg3 = action;
93 dospacket->dp_Port = NULL;
95 PutMsg(glob->ourport, dospacket->dp_Link);
98 void DoDiskInsert(struct Globals *glob) {
99 struct FSSuper *sb;
100 ULONG err;
101 struct DosList *dl;
102 struct VolumeInfo *vol_info = NULL;
103 struct GlobalLock *global_lock;
104 struct ExtFileLock *ext_lock;
105 struct MinNode *lock_node;
106 APTR pool;
107 struct NotifyNode *nn;
108 struct DosList *newvol = NULL;
110 if (glob->sb == NULL && (sb = AllocVecPooled(glob->mempool, sizeof(struct FSSuper)))) {
111 memset(sb, 0, sizeof(struct FSSuper));
113 sb->glob = glob;
114 err = ReadFATSuper(sb);
115 if (err == 0) {
117 /* Scan volume list for a matching volume (would be better to
118 * match by serial number) */
119 dl = LockDosList(LDF_VOLUMES | LDF_WRITE);
120 dl = FindDosEntry(dl, sb->volume.name + 1,
121 LDF_VOLUMES | LDF_WRITE);
122 UnLockDosList(LDF_VOLUMES | LDF_WRITE);
124 if (dl != NULL &&
125 CompareDates(&dl->dol_misc.dol_volume.dol_VolumeDate,
126 &sb->volume.create_time) != 0)
127 dl = NULL;
129 if (dl != NULL) {
130 dl->dol_Task = glob->ourport;
131 sb->doslist = dl;
133 D(bug("\tFound old volume.\n"));
135 vol_info = BADDR(dl->dol_misc.dol_volume.dol_LockList);
137 #if 0 /* no point until we match volumes by serial number */
138 /* update name */
139 #ifdef AROS_FAST_BPTR
140 /* ReadFATSuper() sets a null byte after the
141 * string, so this should be fine */
142 CopyMem(sb->volume.name + 1, dl->dol_Name,
143 sb->volume.name[0] + 1);
144 #else
145 CopyMem(sb->volume.name, dl->dol_Name,
146 sb->volume.name[0] + 2);
147 #endif
148 #endif
150 /* patch locks and notifications to match this handler
151 * instance */
152 ForeachNode(&vol_info->locks, global_lock) {
153 ForeachNode(&global_lock->locks, lock_node) {
154 ext_lock = LOCKFROMNODE(lock_node);
155 D(bug("[fat] Patching adopted lock %p. old port = %p,"
156 " new port = %p\n", ext_lock, ext_lock->fl_Task,
157 glob->ourport));
158 ext_lock->fl_Task = glob->ourport;
159 ext_lock->sb = sb;
160 ext_lock->ioh.sb = sb;
164 ForeachNode(&vol_info->root_lock.locks, lock_node) {
165 ext_lock = LOCKFROMNODE(lock_node);
166 D(bug("[fat] Patching adopted ROOT lock %p. old port = %p,"
167 " new port = %p\n", ext_lock, ext_lock->fl_Task,
168 glob->ourport));
169 ext_lock->fl_Task = glob->ourport;
170 ext_lock->sb = sb;
171 ext_lock->ioh.sb = sb;
174 ForeachNode(&vol_info->notifies, nn)
175 nn->nr->nr_Handler = glob->ourport;
177 else {
178 D(bug("\tCreating new volume.\n"));
180 /* create transferable core volume info */
181 pool = CreatePool(MEMF_PUBLIC, DEF_POOL_SIZE, DEF_POOL_THRESHOLD);
182 if (pool != NULL) {
183 vol_info = AllocVecPooled(pool, sizeof(struct VolumeInfo));
184 if (vol_info != NULL) {
185 vol_info->mem_pool = pool;
186 vol_info->id = sb->volume_id;
187 NEWLIST(&vol_info->locks);
188 NEWLIST(&vol_info->notifies);
190 vol_info->root_lock.dir_cluster = FAT_ROOTDIR_MARK;
191 vol_info->root_lock.dir_entry = FAT_ROOTDIR_MARK;
192 vol_info->root_lock.access = SHARED_LOCK;
193 vol_info->root_lock.first_cluster = 0;
194 vol_info->root_lock.attr = ATTR_DIRECTORY;
195 vol_info->root_lock.size = 0;
196 CopyMem(sb->volume.name, vol_info->root_lock.name,
197 sb->volume.name[0] + 1);
198 NEWLIST(&vol_info->root_lock.locks);
201 if ((newvol = AllocVecPooled(pool, sizeof(struct DosList)))) {
202 newvol->dol_Next = BNULL;
203 newvol->dol_Type = DLT_VOLUME;
204 newvol->dol_Task = glob->ourport;
205 newvol->dol_Lock = BNULL;
207 CopyMem(&sb->volume.create_time, &newvol->dol_misc.dol_volume.dol_VolumeDate, sizeof(struct DateStamp));
209 newvol->dol_misc.dol_volume.dol_LockList = MKBADDR(vol_info);
211 newvol->dol_misc.dol_volume.dol_DiskType =
212 (sb->type == 12) ? ID_FAT12_DISK :
213 (sb->type == 16) ? ID_FAT16_DISK :
214 (sb->type == 32) ? ID_FAT32_DISK :
215 ID_FAT12_DISK;
217 if ((newvol->dol_Name = MKBADDR(AllocVecPooled(pool, 13)))) {
218 #ifdef AROS_FAST_BPTR
219 /* ReadFATSuper() sets a null byte after the
220 * string, so this should be fine */
221 CopyMem(sb->volume.name + 1, newvol->dol_Name,
222 sb->volume.name[0] + 1);
223 #else
224 CopyMem(sb->volume.name, BADDR(newvol->dol_Name),
225 sb->volume.name[0] + 2);
226 #endif
228 sb->doslist = newvol;
231 if (vol_info == NULL || newvol == NULL)
232 DeletePool(pool);
236 sb->info = vol_info;
237 glob->last_num = -1;
239 if (dl != NULL)
240 SendEvent(IECLASS_DISKINSERTED, glob);
241 else
242 SendVolumePacket(newvol, ACTION_VOLUME_ADD, glob);
244 D(bug("\tDisk successfully initialised\n"));
246 return;
248 else if (err == IOERR_BADADDRESS)
249 ErrorMessageArgs("Your device does not support 64-bit\n"
250 "access to the disk while it is needed!\n"
251 "In order to prevent data damage access to\n"
252 "this disk was blocked.\n"
253 "Please upgrade your device driver.", NULL, glob);
255 FreeVecPooled(glob->mempool, sb);
258 SendEvent(IECLASS_DISKINSERTED, glob);
260 return;
263 BOOL AttemptDestroyVolume(struct FSSuper *sb) {
264 struct Globals *glob = sb->glob;
265 BOOL destroyed = FALSE;
267 D(bug("[fat] Attempting to destroy volume\n"));
269 /* check if the volume can be removed */
270 if (IsListEmpty(&sb->info->locks) && IsListEmpty(&sb->info->notifies)) {
271 D(bug("\tRemoving volume completely\n"));
273 if (sb == glob->sb)
274 glob->sb = NULL;
275 else
276 Remove((struct Node *)sb);
278 SendVolumePacket(sb->doslist, ACTION_VOLUME_REMOVE, glob);
280 FreeFATSuper(sb);
281 FreeVecPooled(glob->mempool, sb);
282 destroyed = TRUE;
285 return destroyed;
288 void DoDiskRemove(struct Globals *glob) {
290 if (glob->sb) {
291 struct FSSuper *sb = glob->sb;
293 if(!AttemptDestroyVolume(sb)) {
294 sb->doslist->dol_Task = NULL;
295 glob->sb = NULL;
296 D(bug("\tMoved in-memory super block to spare list. "
297 "Waiting for locks and notifications to be freed\n"));
298 AddTail((struct List *)&glob->sblist, (struct Node *)sb);
299 SendEvent(IECLASS_DISKREMOVED, glob);
304 void ProcessDiskChange(struct Globals *glob) {
305 D(bug("\nGot disk change request\n"));
307 if (glob->disk_inhibited) {
308 D(bug("Disk is inhibited, ignoring disk change\n"));
309 return;
312 glob->diskioreq->iotd_Req.io_Command = TD_CHANGESTATE;
313 glob->diskioreq->iotd_Req.io_Data = NULL;
314 glob->diskioreq->iotd_Req.io_Length = 0;
315 glob->diskioreq->iotd_Req.io_Flags = IOF_QUICK;
316 DoIO((struct IORequest*) glob->diskioreq);
318 if (glob->diskioreq->iotd_Req.io_Error == 0 && glob->diskioreq->iotd_Req.io_Actual == 0) {
319 /* Disk has been inserted. */
320 D(bug("\tDisk has been inserted\n"));
321 glob->disk_inserted = TRUE;
322 DoDiskInsert(glob);
324 else {
325 /* Disk has been removed. */
326 D(bug("\tDisk has been removed\n"));
327 glob->disk_inserted = FALSE;
328 DoDiskRemove(glob);
331 D(bug("Done\n"));
334 void UpdateDisk(struct Globals *glob) {
335 if (glob->sb) {
336 Cache_Flush(glob->sb->cache);
337 /* FIXME: Handle IO errors on disk flush! */
340 glob->diskioreq->iotd_Req.io_Command = CMD_UPDATE;
341 DoIO((struct IORequest *)glob->diskioreq);
343 /* Turn off motor (where applicable) if nothing has happened during the
344 * last timer period */
345 if (!glob->restart_timer) {
346 D(bug("Stopping drive motor\n"));
347 glob->diskioreq->iotd_Req.io_Command = TD_MOTOR;
348 glob->diskioreq->iotd_Req.io_Length = 0;
349 DoIO((struct IORequest *)glob->diskioreq);
353 /* probe the device to determine 64-bit support */
354 void Probe_64bit_support(struct Globals *glob) {
355 struct NSDeviceQueryResult nsd_query;
356 UWORD *nsd_cmd;
358 glob->readcmd = CMD_READ;
359 glob->writecmd = CMD_WRITE;
361 /* probe TD64 */
362 glob->diskioreq->iotd_Req.io_Command = TD_READ64;
363 glob->diskioreq->iotd_Req.io_Offset = 0;
364 glob->diskioreq->iotd_Req.io_Length = 0;
365 glob->diskioreq->iotd_Req.io_Actual = 0;
366 glob->diskioreq->iotd_Req.io_Data = 0;
368 if (DoIO((struct IORequest *) glob->diskioreq) != IOERR_NOCMD) {
369 D(bug("Probe_64bit_support: device supports 64-bit trackdisk extensions\n"));
370 glob->readcmd = TD_READ64;
371 glob->writecmd = TD_WRITE64;
374 /* probe NSD */
375 glob->diskioreq->iotd_Req.io_Command = NSCMD_DEVICEQUERY;
376 glob->diskioreq->iotd_Req.io_Length = sizeof(struct NSDeviceQueryResult);
377 glob->diskioreq->iotd_Req.io_Data = (APTR) &nsd_query;
379 if (DoIO((struct IORequest *) glob->diskioreq) == 0)
380 for (nsd_cmd = nsd_query.SupportedCommands; *nsd_cmd != 0; nsd_cmd++) {
381 if (*nsd_cmd == NSCMD_TD_READ64) {
382 D(bug("Probe_64bit_support: device supports NSD 64-bit trackdisk extensions\n"));
383 glob->readcmd = NSCMD_TD_READ64;
384 glob->writecmd = NSCMD_TD_WRITE64;
385 break;
390 ULONG AccessDisk(BOOL do_write, ULONG num, ULONG nblocks, ULONG block_size,
391 UBYTE *data, APTR priv) {
392 struct Globals *glob = priv;
393 UQUAD off;
394 ULONG err;
395 ULONG start, end;
397 #if DEBUG_CACHESTATS > 1
398 ErrorMessage("Accessing %lu sector(s) starting at %lu.\n"
399 "First volume sector is %lu, sector size is %lu.\n", nblocks, num,
400 glob->sb->first_device_sector, block_size);
401 #endif
403 /* Adjust parameters if range is partially outside boundaries, or
404 * warn user and bale out if completely outside boundaries */
405 if (glob->sb) {
406 start = glob->sb->first_device_sector;
407 if (num + nblocks <= glob->sb->first_device_sector) {
408 if (num != glob->last_num) {
409 glob->last_num = num;
410 ErrorMessage("A handler attempted to %s %lu sector(s) starting\n"
411 "from %lu, before the actual volume space.\n"
412 "First volume sector is %lu, sector size is %lu.\n"
413 "Either your disk is damaged or it is a bug in\n"
414 "the handler. Please check your disk and/or\n"
415 "report this problem to the developers team.",
416 (IPTR)(do_write ? "write" : "read"), nblocks, num,
417 glob->sb->first_device_sector, block_size);
419 return IOERR_BADADDRESS;
421 else if (num < start) {
422 nblocks -= start - num;
423 data += (start - num) * block_size;
424 num = start;
427 end = glob->sb->first_device_sector + glob->sb->total_sectors;
428 if (num >= end) {
429 if (num != glob->last_num) {
430 glob->last_num = num;
431 ErrorMessage("A handler attempted to %s %lu sector(s) starting\n"
432 "from %lu, beyond the actual volume space.\n"
433 "Last volume sector is %lu, sector size is %lu.\n"
434 "Either your disk is damaged or it is a bug in\n"
435 "the handler. Please check your disk and/or\n"
436 "report this problem to the developers team.",
437 (IPTR)(do_write ? "write" : "read"), nblocks, num,
438 end - 1, block_size);
440 return IOERR_BADADDRESS;
442 else if (num + nblocks > end)
443 nblocks = end - num;
446 off = ((UQUAD) num) * block_size;
448 glob->diskioreq->iotd_Req.io_Offset = off & 0xFFFFFFFF;
449 glob->diskioreq->iotd_Req.io_Actual = off >> 32;
451 if (glob->diskioreq->iotd_Req.io_Actual && (glob->readcmd == CMD_READ))
452 return IOERR_BADADDRESS;
454 glob->diskioreq->iotd_Req.io_Length = nblocks * block_size;
455 glob->diskioreq->iotd_Req.io_Data = data;
456 glob->diskioreq->iotd_Req.io_Command = do_write ? glob->writecmd : glob->readcmd;
458 err = DoIO((struct IORequest *) glob->diskioreq);
460 return err;