- Implemented ACTION_FORMAT.
[AROS.git] / rom / filesys / fat / disk.c
blob6c6a5a8611e3a7624ab6e1db4f1f14369e739ee4
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) {
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->last_num = -1;
233 if (dl != NULL)
234 SendEvent(IECLASS_DISKINSERTED);
235 else
236 SendVolumePacket(newvol, ACTION_VOLUME_ADD);
238 D(bug("\tDisk successfully initialised\n"));
240 return;
242 else if (err == IOERR_BADADDRESS)
243 ErrorMessageArgs("Your device does not support 64-bit\n"
244 "access to the disk while it is needed!\n"
245 "In order to prevent data damage access to\n"
246 "this disk was blocked.\n"
247 "Please upgrade your device driver.", NULL);
249 FreeVecPooled(glob->mempool, sb);
252 SendEvent(IECLASS_DISKINSERTED);
254 return;
257 BOOL AttemptDestroyVolume(struct FSSuper *sb) {
258 BOOL destroyed = FALSE;
260 D(bug("[fat] Attempting to destroy volume\n"));
262 /* check if the volume can be removed */
263 if (IsListEmpty(&sb->info->locks) && IsListEmpty(&sb->info->notifies)) {
264 D(bug("\tRemoving volume completely\n"));
266 if (sb == glob->sb)
267 glob->sb = NULL;
268 else
269 Remove((struct Node *)sb);
271 SendVolumePacket(sb->doslist, ACTION_VOLUME_REMOVE);
273 FreeFATSuper(sb);
274 FreeVecPooled(glob->mempool, sb);
275 destroyed = TRUE;
278 return destroyed;
281 void DoDiskRemove(void) {
283 if (glob->sb) {
284 struct FSSuper *sb = glob->sb;
286 if(!AttemptDestroyVolume(sb)) {
287 sb->doslist->dol_Task = NULL;
288 glob->sb = NULL;
289 D(bug("\tMoved in-memory super block to spare list. "
290 "Waiting for locks and notifications to be freed\n"));
291 AddTail((struct List *)&glob->sblist, (struct Node *)sb);
292 SendEvent(IECLASS_DISKREMOVED);
297 void ProcessDiskChange(void) {
298 D(bug("\nGot disk change request\n"));
300 if (glob->disk_inhibited) {
301 D(bug("Disk is inhibited, ignoring disk change\n"));
302 return;
305 glob->diskioreq->iotd_Req.io_Command = TD_CHANGESTATE;
306 glob->diskioreq->iotd_Req.io_Data = NULL;
307 glob->diskioreq->iotd_Req.io_Length = 0;
308 glob->diskioreq->iotd_Req.io_Flags = IOF_QUICK;
309 DoIO((struct IORequest*) glob->diskioreq);
311 if (glob->diskioreq->iotd_Req.io_Error == 0 && glob->diskioreq->iotd_Req.io_Actual == 0) {
312 /* Disk has been inserted. */
313 D(bug("\tDisk has been inserted\n"));
314 glob->disk_inserted = TRUE;
315 DoDiskInsert();
317 else {
318 /* Disk has been removed. */
319 D(bug("\tDisk has been removed\n"));
320 glob->disk_inserted = FALSE;
321 DoDiskRemove();
324 D(bug("Done\n"));
327 void UpdateDisk(void) {
328 if (glob->sb)
329 Cache_Flush(glob->sb->cache);
331 glob->diskioreq->iotd_Req.io_Command = CMD_UPDATE;
332 DoIO((struct IORequest *)glob->diskioreq);
334 /* Turn off motor (where applicable) if nothing has happened during the
335 * last timer period */
336 if (!glob->restart_timer) {
337 D(bug("Stopping drive motor\n"));
338 glob->diskioreq->iotd_Req.io_Command = TD_MOTOR;
339 glob->diskioreq->iotd_Req.io_Length = 0;
340 DoIO((struct IORequest *)glob->diskioreq);
344 /* probe the device to determine 64-bit support */
345 void Probe_64bit_support(void) {
346 struct NSDeviceQueryResult nsd_query;
347 UWORD *nsd_cmd;
349 glob->readcmd = CMD_READ;
350 glob->writecmd = CMD_WRITE;
352 /* probe TD64 */
353 glob->diskioreq->iotd_Req.io_Command = TD_READ64;
354 glob->diskioreq->iotd_Req.io_Offset = 0;
355 glob->diskioreq->iotd_Req.io_Length = 0;
356 glob->diskioreq->iotd_Req.io_Actual = 0;
357 glob->diskioreq->iotd_Req.io_Data = 0;
359 if (DoIO((struct IORequest *) glob->diskioreq) != IOERR_NOCMD) {
360 D(bug("Probe_64bit_support: device supports 64-bit trackdisk extensions\n"));
361 glob->readcmd = TD_READ64;
362 glob->writecmd = TD_WRITE64;
365 /* probe NSD */
366 glob->diskioreq->iotd_Req.io_Command = NSCMD_DEVICEQUERY;
367 glob->diskioreq->iotd_Req.io_Length = sizeof(struct NSDeviceQueryResult);
368 glob->diskioreq->iotd_Req.io_Data = (APTR) &nsd_query;
370 if (DoIO((struct IORequest *) glob->diskioreq) == 0)
371 for (nsd_cmd = nsd_query.SupportedCommands; *nsd_cmd != 0; nsd_cmd++) {
372 if (*nsd_cmd == NSCMD_TD_READ64) {
373 D(bug("Probe_64bit_support: device supports NSD 64-bit trackdisk extensions\n"));
374 glob->readcmd = NSCMD_TD_READ64;
375 glob->writecmd = NSCMD_TD_WRITE64;
376 break;
381 ULONG AccessDisk(BOOL do_write, ULONG num, ULONG nblocks, ULONG block_size,
382 UBYTE *data) {
383 UQUAD off;
384 ULONG err;
385 ULONG start, end;
387 #if DEBUG_CACHESTATS > 1
388 ErrorMessage("Accessing %lu sector(s) starting at %lu.\n"
389 "First volume sector is %lu, sector size is %lu.\n", nblocks, num,
390 glob->sb->first_device_sector, block_size);
391 #endif
393 /* Adjust parameters if range is partially outside boundaries, or
394 * warn user and bale out if completely outside boundaries */
395 if (glob->sb) {
396 start = glob->sb->first_device_sector;
397 if (num + nblocks <= glob->sb->first_device_sector) {
398 if (num != glob->last_num) {
399 glob->last_num = num;
400 ErrorMessage("A handler attempted to %s %lu sector(s) starting\n"
401 "from %lu, before the actual volume space.\n"
402 "First volume sector is %lu, sector size is %lu.\n"
403 "Either your disk is damaged or it is a bug in\n"
404 "the handler. Please check your disk and/or\n"
405 "report this problem to the developers team.",
406 (IPTR)(do_write ? "write" : "read"), nblocks, num,
407 glob->sb->first_device_sector, block_size);
409 return IOERR_BADADDRESS;
411 else if (num < start) {
412 nblocks -= start - num;
413 data += (start - num) * block_size;
414 num = start;
417 end = glob->sb->first_device_sector + glob->sb->total_sectors;
418 if (num >= end) {
419 if (num != glob->last_num) {
420 glob->last_num = num;
421 ErrorMessage("A handler attempted to %s %lu sector(s) starting\n"
422 "from %lu, beyond the actual volume space.\n"
423 "Last volume sector is %lu, sector size is %lu.\n"
424 "Either your disk is damaged or it is a bug in\n"
425 "the handler. Please check your disk and/or\n"
426 "report this problem to the developers team.",
427 (IPTR)(do_write ? "write" : "read"), nblocks, num,
428 end - 1, block_size);
430 return IOERR_BADADDRESS;
432 else if (num + nblocks > end)
433 nblocks = end - num;
436 off = ((UQUAD) num) * block_size;
438 glob->diskioreq->iotd_Req.io_Offset = off & 0xFFFFFFFF;
439 glob->diskioreq->iotd_Req.io_Actual = off >> 32;
441 if (glob->diskioreq->iotd_Req.io_Actual && (glob->readcmd == CMD_READ))
442 return IOERR_BADADDRESS;
444 glob->diskioreq->iotd_Req.io_Length = nblocks * block_size;
445 glob->diskioreq->iotd_Req.io_Data = data;
446 glob->diskioreq->iotd_Req.io_Command = do_write ? glob->writecmd : glob->readcmd;
448 err = DoIO((struct IORequest *) glob->diskioreq);
450 return err;