console-handler: Tabs->spaces
[AROS.git] / rom / filesys / fat / disk.c
blobf50162f1d8f1fdbafb5b3ba04eb1566a94ad4d55
1 /*
2 * fat.handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007-2011 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) {
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) {
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(void) {
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 err = ReadFATSuper(sb);
114 if (err == 0) {
116 /* Scan volume list for a matching volume (would be better to
117 * match by serial number) */
118 dl = LockDosList(LDF_VOLUMES | LDF_WRITE);
119 dl = FindDosEntry(dl, sb->volume.name + 1,
120 LDF_VOLUMES | LDF_WRITE);
121 UnLockDosList(LDF_VOLUMES | LDF_WRITE);
123 if (dl != NULL) {
124 dl->dol_Task = glob->ourport;
125 sb->doslist = dl;
127 D(bug("\tFound old volume.\n"));
129 vol_info = BADDR(dl->dol_misc.dol_volume.dol_LockList);
131 #if 0 /* no point until we match volumes by serial number */
132 /* update name */
133 #ifdef AROS_FAST_BPTR
134 /* ReadFATSuper() sets a null byte after the
135 * string, so this should be fine */
136 CopyMem(sb->volume.name + 1, dl->dol_Name,
137 sb->volume.name[0] + 1);
138 #else
139 CopyMem(sb->volume.name, dl->dol_Name,
140 sb->volume.name[0] + 2);
141 #endif
142 #endif
144 /* patch locks and notifications to match this handler
145 * instance */
146 ForeachNode(&vol_info->locks, global_lock) {
147 ForeachNode(&global_lock->locks, lock_node) {
148 ext_lock = LOCKFROMNODE(lock_node);
149 D(bug("[fat] Patching adopted lock %p. old port = %p,"
150 " new port = %p\n", ext_lock, ext_lock->fl_Task,
151 glob->ourport));
152 ext_lock->fl_Task = glob->ourport;
153 ext_lock->sb = sb;
154 ext_lock->ioh.sb = sb;
158 ForeachNode(&vol_info->root_lock.locks, lock_node) {
159 ext_lock = LOCKFROMNODE(lock_node);
160 D(bug("[fat] Patching adopted ROOT lock %p. old port = %p,"
161 " new port = %p\n", ext_lock, ext_lock->fl_Task,
162 glob->ourport));
163 ext_lock->fl_Task = glob->ourport;
164 ext_lock->sb = sb;
165 ext_lock->ioh.sb = sb;
168 ForeachNode(&vol_info->notifies, nn)
169 nn->nr->nr_Handler = glob->ourport;
171 else {
172 D(bug("\tCreating new volume.\n"));
174 /* create transferable core volume info */
175 pool = CreatePool(MEMF_PUBLIC, DEF_POOL_SIZE, DEF_POOL_THRESHOLD);
176 if (pool != NULL) {
177 vol_info = AllocVecPooled(pool, sizeof(struct VolumeInfo));
178 if (vol_info != NULL) {
179 vol_info->mem_pool = pool;
180 vol_info->id = sb->volume_id;
181 NEWLIST(&vol_info->locks);
182 NEWLIST(&vol_info->notifies);
184 vol_info->root_lock.dir_cluster = FAT_ROOTDIR_MARK;
185 vol_info->root_lock.dir_entry = FAT_ROOTDIR_MARK;
186 vol_info->root_lock.access = SHARED_LOCK;
187 vol_info->root_lock.first_cluster = 0;
188 vol_info->root_lock.attr = ATTR_DIRECTORY;
189 vol_info->root_lock.size = 0;
190 CopyMem(sb->volume.name, vol_info->root_lock.name,
191 sb->volume.name[0] + 1);
192 NEWLIST(&vol_info->root_lock.locks);
195 if ((newvol = AllocVecPooled(pool, sizeof(struct DosList)))) {
196 newvol->dol_Next = BNULL;
197 newvol->dol_Type = DLT_VOLUME;
198 newvol->dol_Task = glob->ourport;
199 newvol->dol_Lock = BNULL;
201 CopyMem(&sb->volume.create_time, &newvol->dol_misc.dol_volume.dol_VolumeDate, sizeof(struct DateStamp));
203 newvol->dol_misc.dol_volume.dol_LockList = MKBADDR(vol_info);
205 newvol->dol_misc.dol_volume.dol_DiskType =
206 (sb->type == 12) ? ID_FAT12_DISK :
207 (sb->type == 16) ? ID_FAT16_DISK :
208 (sb->type == 32) ? ID_FAT32_DISK :
209 ID_FAT12_DISK;
211 if ((newvol->dol_Name = MKBADDR(AllocVecPooled(pool, 13)))) {
212 #ifdef AROS_FAST_BPTR
213 /* ReadFATSuper() sets a null byte after the
214 * string, so this should be fine */
215 CopyMem(sb->volume.name + 1, newvol->dol_Name,
216 sb->volume.name[0] + 1);
217 #else
218 CopyMem(sb->volume.name, BADDR(newvol->dol_Name),
219 sb->volume.name[0] + 2);
220 #endif
222 sb->doslist = newvol;
225 if (vol_info == NULL || newvol == NULL)
226 DeletePool(pool);
230 sb->info = vol_info;
231 glob->sb = sb;
232 glob->last_num = -1;
234 if (dl != NULL)
235 SendEvent(IECLASS_DISKINSERTED);
236 else
237 SendVolumePacket(newvol, ACTION_VOLUME_ADD);
239 D(bug("\tDisk successfully initialised\n"));
241 return;
243 else if (err == IOERR_BADADDRESS)
244 ErrorMessageArgs("Your device does not support 64-bit\n"
245 "access to the disk while it is needed!\n"
246 "In order to prevent data damage access to\n"
247 "this disk was blocked.\n"
248 "Please upgrade your device driver.", NULL);
250 FreeVecPooled(glob->mempool, sb);
253 SendEvent(IECLASS_DISKINSERTED);
255 return;
258 BOOL AttemptDestroyVolume(struct FSSuper *sb) {
259 BOOL destroyed = FALSE;
261 D(bug("[fat] Attempting to destroy volume\n"));
263 /* check if the volume can be removed */
264 if (IsListEmpty(&sb->info->locks) && IsListEmpty(&sb->info->notifies)) {
265 D(bug("\tRemoving volume completely\n"));
267 if (sb == glob->sb)
268 glob->sb = NULL;
269 else
270 Remove((struct Node *)sb);
272 SendVolumePacket(sb->doslist, ACTION_VOLUME_REMOVE);
274 FreeFATSuper(sb);
275 FreeVecPooled(glob->mempool, sb);
276 destroyed = TRUE;
279 return destroyed;
282 void DoDiskRemove(void) {
284 if (glob->sb) {
285 struct FSSuper *sb = glob->sb;
287 if(!AttemptDestroyVolume(sb)) {
288 sb->doslist->dol_Task = NULL;
289 glob->sb = NULL;
290 D(bug("\tMoved in-memory super block to spare list. "
291 "Waiting for locks and notifications to be freed\n"));
292 AddTail((struct List *)&glob->sblist, (struct Node *)sb);
293 SendEvent(IECLASS_DISKREMOVED);
298 void ProcessDiskChange(void) {
299 D(bug("\nGot disk change request\n"));
301 if (glob->disk_inhibited) {
302 D(bug("Disk is inhibited, ignoring disk change\n"));
303 return;
306 glob->diskioreq->iotd_Req.io_Command = TD_CHANGESTATE;
307 glob->diskioreq->iotd_Req.io_Data = NULL;
308 glob->diskioreq->iotd_Req.io_Length = 0;
309 glob->diskioreq->iotd_Req.io_Flags = IOF_QUICK;
310 DoIO((struct IORequest*) glob->diskioreq);
312 if (glob->diskioreq->iotd_Req.io_Error == 0 && glob->diskioreq->iotd_Req.io_Actual == 0) {
313 /* Disk has been inserted. */
314 D(bug("\tDisk has been inserted\n"));
315 glob->disk_inserted = TRUE;
316 DoDiskInsert();
318 else {
319 /* Disk has been removed. */
320 D(bug("\tDisk has been removed\n"));
321 glob->disk_inserted = FALSE;
322 DoDiskRemove();
325 D(bug("Done\n"));
328 void UpdateDisk(void) {
329 if (glob->sb)
330 Cache_Flush(glob->sb->cache);
332 glob->diskioreq->iotd_Req.io_Command = CMD_UPDATE;
333 DoIO((struct IORequest *)glob->diskioreq);
335 /* Turn off motor (where applicable) if nothing has happened during the
336 * last timer period */
337 if (!glob->restart_timer) {
338 D(bug("Stopping drive motor\n"));
339 glob->diskioreq->iotd_Req.io_Command = TD_MOTOR;
340 glob->diskioreq->iotd_Req.io_Length = 0;
341 DoIO((struct IORequest *)glob->diskioreq);
345 /* probe the device to determine 64-bit support */
346 void Probe_64bit_support(void) {
347 struct NSDeviceQueryResult nsd_query;
348 UWORD *nsd_cmd;
350 glob->readcmd = CMD_READ;
351 glob->writecmd = CMD_WRITE;
353 /* probe TD64 */
354 glob->diskioreq->iotd_Req.io_Command = TD_READ64;
355 glob->diskioreq->iotd_Req.io_Offset = 0;
356 glob->diskioreq->iotd_Req.io_Length = 0;
357 glob->diskioreq->iotd_Req.io_Actual = 0;
358 glob->diskioreq->iotd_Req.io_Data = 0;
360 if (DoIO((struct IORequest *) glob->diskioreq) != IOERR_NOCMD) {
361 D(bug("Probe_64bit_support: device supports 64-bit trackdisk extensions\n"));
362 glob->readcmd = TD_READ64;
363 glob->writecmd = TD_WRITE64;
366 /* probe NSD */
367 glob->diskioreq->iotd_Req.io_Command = NSCMD_DEVICEQUERY;
368 glob->diskioreq->iotd_Req.io_Length = sizeof(struct NSDeviceQueryResult);
369 glob->diskioreq->iotd_Req.io_Data = (APTR) &nsd_query;
371 if (DoIO((struct IORequest *) glob->diskioreq) == 0)
372 for (nsd_cmd = nsd_query.SupportedCommands; *nsd_cmd != 0; nsd_cmd++) {
373 if (*nsd_cmd == NSCMD_TD_READ64) {
374 D(bug("Probe_64bit_support: device supports NSD 64-bit trackdisk extensions\n"));
375 glob->readcmd = NSCMD_TD_READ64;
376 glob->writecmd = NSCMD_TD_WRITE64;
377 break;
382 ULONG AccessDisk(BOOL do_write, ULONG num, ULONG nblocks, ULONG block_size,
383 UBYTE *data) {
384 UQUAD off;
385 ULONG err;
386 ULONG start, end;
388 #if DEBUG_CACHESTATS > 1
389 ErrorMessage("Accessing %lu sector(s) starting at %lu.\n"
390 "First volume sector is %lu, sector size is %lu.\n", nblocks, num,
391 glob->sb->first_device_sector, block_size);
392 #endif
394 /* Adjust parameters if range is partially outside boundaries, or
395 * warn user and bale out if completely outside boundaries */
396 if (glob->sb) {
397 start = glob->sb->first_device_sector;
398 if (num + nblocks <= glob->sb->first_device_sector) {
399 if (num != glob->last_num) {
400 glob->last_num = num;
401 ErrorMessage("A handler attempted to %s %lu sector(s) starting\n"
402 "from %lu, before the actual volume space.\n"
403 "First volume sector is %lu, sector size is %lu.\n"
404 "Either your disk is damaged or it is a bug in\n"
405 "the handler. Please check your disk and/or\n"
406 "report this problem to the developers team.",
407 (IPTR)(do_write ? "write" : "read"), nblocks, num,
408 glob->sb->first_device_sector, block_size);
410 return IOERR_BADADDRESS;
412 else if (num < start) {
413 nblocks -= start - num;
414 data += (start - num) * block_size;
415 num = start;
418 end = glob->sb->first_device_sector + glob->sb->total_sectors;
419 if (num >= end) {
420 if (num != glob->last_num) {
421 glob->last_num = num;
422 ErrorMessage("A handler attempted to %s %lu sector(s) starting\n"
423 "from %lu, beyond the actual volume space.\n"
424 "Last volume sector is %lu, sector size is %lu.\n"
425 "Either your disk is damaged or it is a bug in\n"
426 "the handler. Please check your disk and/or\n"
427 "report this problem to the developers team.",
428 (IPTR)(do_write ? "write" : "read"), nblocks, num,
429 end - 1, block_size);
431 return IOERR_BADADDRESS;
433 else if (num + nblocks > end)
434 nblocks = end - num;
437 off = ((UQUAD) num) * block_size;
439 glob->diskioreq->iotd_Req.io_Offset = off & 0xFFFFFFFF;
440 glob->diskioreq->iotd_Req.io_Actual = off >> 32;
442 if (glob->diskioreq->iotd_Req.io_Actual && (glob->readcmd == CMD_READ))
443 return IOERR_BADADDRESS;
445 glob->diskioreq->iotd_Req.io_Length = nblocks * block_size;
446 glob->diskioreq->iotd_Req.io_Data = data;
447 glob->diskioreq->iotd_Req.io_Command = do_write ? glob->writecmd : glob->readcmd;
449 err = DoIO((struct IORequest *) glob->diskioreq);
451 return err;