Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / workbench / fs / fat / disk.c
blobd007ab068e02edf7075033a2181d98a3d54d6efd
1 /*
2 * fat.handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007-2008 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/execbase.h>
17 #include <exec/libraries.h>
18 #include <exec/errors.h>
20 #include <dos/dos.h>
21 #include <dos/dosextens.h>
22 #include <dos/dostags.h>
23 #include <dos/filehandler.h>
24 #include <devices/newstyle.h>
25 #include <devices/trackdisk.h>
26 #include <devices/inputevent.h>
28 #include <proto/exec.h>
29 #include <proto/dos.h>
31 #include <string.h> /* for memset() */
33 #include "fat_fs.h"
34 #include "fat_protos.h"
35 #include "timer.h"
37 #define DEBUG DEBUG_MISC
38 #include "debug.h"
40 #ifndef ID_BUSY
41 #define ID_BUSY 0x42555359
42 #endif
44 /* TD64 commands */
45 #ifndef TD_READ64
46 #define TD_READ64 24
47 #define TD_WRITE64 25
48 #endif
50 void FillDiskInfo (struct InfoData *id) {
51 struct DosEnvec *de = BADDR(glob->fssm->fssm_Environ);
53 id->id_NumSoftErrors = 0;
54 id->id_UnitNumber = glob->fssm->fssm_Unit;
55 id->id_DiskState = ID_VALIDATED;
57 if (glob->sb) {
58 CountFreeClusters(glob->sb);
60 id->id_NumBlocks = glob->sb->total_sectors;
61 id->id_NumBlocksUsed = glob->sb->total_sectors - ((glob->sb->free_clusters * glob->sb->clustersize) >> glob->sb->sectorsize_bits);
62 id->id_BytesPerBlock = glob->sb->sectorsize;
64 id->id_DiskType = ID_DOS_DISK;
66 id->id_VolumeNode = MKBADDR(glob->sb->doslist);
67 id->id_InUse = glob->sb->doslist->dol_misc.dol_volume.dol_LockList ? DOSTRUE : DOSFALSE;
70 else {
71 id->id_NumBlocks = de->de_Surfaces * de->de_BlocksPerTrack * (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 = NULL;
85 id->id_InUse = DOSFALSE;
89 void SendVolumePacket(struct DosList *vol, ULONG action) {
90 struct DosPacket *dospacket;
92 dospacket = AllocDosObject(DOS_STDPKT, TAG_DONE);
93 dospacket->dp_Type = ACTION_DISK_CHANGE;
94 dospacket->dp_Arg1 = ID_FAT_DISK;
95 dospacket->dp_Arg2 = (ULONG) vol;
96 dospacket->dp_Arg3 = action;
97 dospacket->dp_Port = NULL;
99 PutMsg(glob->ourport, dospacket->dp_Link);
102 void DoDiskInsert(void) {
103 struct FSSuper *sb;
104 ULONG err;
106 if (glob->sb == NULL && (sb = AllocVecPooled(glob->mempool, sizeof(struct FSSuper)))) {
107 memset(sb, 0, sizeof(struct FSSuper));
109 err = ReadFATSuper(sb);
110 if (err == 0) {
111 LONG found = FALSE;
113 if (glob->sblist) {
114 struct FSSuper *ptr = glob->sblist, *prev = NULL;
116 while (ptr != NULL) {
117 if (CompareFATSuper(sb, ptr) == 0)
118 break;
120 prev = ptr;
121 ptr = ptr->next;
124 if (ptr) {
125 D(bug("\tFound FAT FS Super Block in spare list, freeing obsolete old one\n"));
127 sb->doslist = ptr->doslist;
128 ptr->doslist = NULL;
130 #ifdef AROS_FAST_BPTR
131 /* ReadFATSuper() sets a null byte * after the string, so
132 * this should be fine */
133 sb->doslist->dol_Name = (BSTR)MKBADDR(&(sb->volume.name[1]));
134 #else
135 sb->doslist->dol_Name = (BSTR)MKBADDR(&sb->volume.name);
136 #endif
138 if (prev)
139 prev->next = ptr->next;
140 else
141 glob->sblist = ptr->next;
143 FreeFATSuper(ptr);
144 FreeVecPooled(glob->mempool, ptr);
146 found = TRUE;
150 if (!found) {
151 struct DosList *newvol;
153 D(bug("\tCreating new volume.\n"));
155 if ((newvol = AllocVecPooled(glob->mempool, sizeof(struct DosList)))) {
156 newvol->dol_Next = NULL;
157 newvol->dol_Type = DLT_VOLUME;
158 newvol->dol_Task = glob->ourport;
159 newvol->dol_Lock = NULL;
161 CopyMem(&sb->volume.create_time, &newvol->dol_misc.dol_volume.dol_VolumeDate, sizeof(struct DateStamp));
163 newvol->dol_misc.dol_volume.dol_LockList = NULL;
165 newvol->dol_misc.dol_volume.dol_DiskType = (sb->type == 12) ? ID_FAT12_DISK :
166 (sb->type == 16) ? ID_FAT16_DISK :
167 (sb->type == 32) ? ID_FAT32_DISK :
168 ID_FAT12_DISK;
170 #ifdef AROS_FAST_BPTR
171 /* ReadFATSuper() sets a null byte * after the string, so
172 * this should be fine */
173 newvol->dol_Name = (BSTR)MKBADDR(&(sb->volume.name[1]));
174 #else
175 newvol->dol_Name = (BSTR)MKBADDR(&sb->volume.name);
176 #endif
178 sb->doslist = newvol;
180 SendVolumePacket(newvol, ACTION_VOLUME_ADD);
184 else
185 SendEvent(IECLASS_DISKINSERTED);
187 glob->sb = sb;
189 D(bug("\tDisk successfully initialised\n"));
191 return;
192 } else if (err == IOERR_BADADDRESS)
193 ErrorMessage("Your device does not support 64-bit\n"
194 "access to the disk while it is needed!\n"
195 "In order to prevent data damage access to\n"
196 "this disk was blocked.\n"
197 "Please upgrade your device driver.");
199 FreeVecPooled(glob->mempool, sb);
202 SendEvent(IECLASS_DISKINSERTED);
204 return;
207 void DoDiskRemove(void) {
209 if (glob->sb) {
210 struct FSSuper *sb = glob->sb;
212 glob->sb = NULL;
214 if (sb->doslist->dol_misc.dol_volume.dol_LockList == NULL) { /* check if the device can be removed */
215 D(bug("\tRemoving disk completely\n"));
217 SendVolumePacket(sb->doslist, ACTION_VOLUME_REMOVE);
219 sb->doslist = NULL;
220 FreeFATSuper(sb);
221 FreeVecPooled(glob->mempool, sb);
223 else {
224 sb->next = glob->sblist;
225 glob->sblist = sb;
227 D(bug("\tMoved in-memory super block to spare list. Waiting for locks to be freed\n"));
229 SendEvent(IECLASS_DISKREMOVED);
234 void ProcessDiskChange(void) {
235 D(bug("\nGot disk change request\n"));
237 if (glob->disk_inhibited) {
238 D(bug("Disk is inhibited, ignoring disk change\n"));
239 return;
242 glob->diskioreq->iotd_Req.io_Command = TD_CHANGESTATE;
243 glob->diskioreq->iotd_Req.io_Data = NULL;
244 glob->diskioreq->iotd_Req.io_Length = 0;
245 glob->diskioreq->iotd_Req.io_Flags = IOF_QUICK;
246 DoIO((struct IORequest*) glob->diskioreq);
248 if (glob->diskioreq->iotd_Req.io_Error == 0 && glob->diskioreq->iotd_Req.io_Actual == 0) {
249 /* Disk has been inserted. */
250 D(bug("\tDisk has been inserted\n"));
251 glob->disk_inserted = TRUE;
252 DoDiskInsert();
254 else {
255 /* Disk has been removed. */
256 D(bug("\tDisk has been removed\n"));
257 glob->disk_inserted = FALSE;
258 DoDiskRemove();
261 D(bug("Done\n"));
264 void UpdateDisk(void)
266 struct cache *c;
268 c = glob->sb->cache;
269 if (c && (c->flags & CACHE_WRITEBACK))
270 cache_flush(c);
272 glob->diskioreq->iotd_Req.io_Command = CMD_UPDATE;
273 DoIO((struct IORequest *)glob->diskioreq);
275 glob->diskioreq->iotd_Req.io_Command = TD_MOTOR;
276 glob->diskioreq->iotd_Req.io_Length = 0;
277 DoIO((struct IORequest *)glob->diskioreq);
280 /* probe the device to determine 64-bit support */
281 void Probe_64bit_support(void) {
282 struct NSDeviceQueryResult nsd_query;
283 UWORD *nsd_cmd;
285 glob->readcmd = CMD_READ;
286 glob->writecmd = CMD_WRITE;
288 /* probe TD64 */
289 glob->diskioreq->iotd_Req.io_Command = TD_READ64;
290 glob->diskioreq->iotd_Req.io_Offset = 0;
291 glob->diskioreq->iotd_Req.io_Length = 0;
292 glob->diskioreq->iotd_Req.io_Actual = 0;
293 glob->diskioreq->iotd_Req.io_Data = 0;
295 if (DoIO((struct IORequest *) glob->diskioreq) != IOERR_NOCMD) {
296 D(bug("Probe_64bit_support: device supports 64-bit trackdisk extensions\n"));
297 glob->readcmd = TD_READ64;
298 glob->writecmd = TD_WRITE64;
301 /* probe NSD */
302 glob->diskioreq->iotd_Req.io_Command = NSCMD_DEVICEQUERY;
303 glob->diskioreq->iotd_Req.io_Length = sizeof(struct NSDeviceQueryResult);
304 glob->diskioreq->iotd_Req.io_Data = (APTR) &nsd_query;
306 if (DoIO((struct IORequest *) glob->diskioreq) == 0)
307 for (nsd_cmd = nsd_query.SupportedCommands; *nsd_cmd != 0; nsd_cmd++) {
308 if (*nsd_cmd == NSCMD_TD_READ64) {
309 D(bug("Probe_64bit_support: device supports NSD 64-bit trackdisk extensions\n"));
310 glob->readcmd = NSCMD_TD_READ64;
311 glob->writecmd = NSCMD_TD_WRITE64;
312 break;
317 ULONG AccessDisk(BOOL do_write, ULONG num, ULONG nblocks, ULONG block_size, UBYTE *data)
319 UQUAD off;
320 ULONG err;
321 ULONG end;
323 if (glob->sb) {
324 if (num < glob->sb->first_device_sector) {
325 if (num != glob->last_num) {
326 glob->last_num = num;
327 ErrorMessage("A handler attempted to %s %lu sector(s) starting\n"
328 "from %lu before the actual volume space.\n"
329 "First volume sector is %lu, sector size is %lu.\n"
330 "Either your disk is damaged or it is a bug in\n"
331 "the handler. Please check your disk and/or\n"
332 "report this problem to the developers team.",
333 do_write ? "write" : "read", nblocks, num,
334 glob->sb->first_device_sector, block_size);
336 return IOERR_BADADDRESS;
338 end = glob->sb->first_device_sector + glob->sb->total_sectors;
339 if (num + nblocks > end) {
340 if (num != glob->last_num) {
341 glob->last_num = num;
342 ErrorMessage("A handler attempted to %s %lu sector(s) starting\n"
343 "from %lu beyond the actual volume space.\n"
344 "Last volume sector is %lu, sector size is %lu.\n"
345 "Either your disk is damaged or it is a bug in\n"
346 "the handler. Please check your disk and/or\n"
347 "report this problem to the developers team.",
348 do_write ? "write" : "read", nblocks, num,
349 end - 1, block_size);
351 return IOERR_BADADDRESS;
355 off = ((UQUAD) num) * block_size;
357 glob->diskioreq->iotd_Req.io_Offset = off & 0xFFFFFFFF;
358 glob->diskioreq->iotd_Req.io_Actual = off >> 32;
360 if (glob->diskioreq->iotd_Req.io_Actual && (glob->readcmd == CMD_READ))
361 return IOERR_BADADDRESS;
363 glob->diskioreq->iotd_Req.io_Length = nblocks * block_size;
364 glob->diskioreq->iotd_Req.io_Data = data;
365 glob->diskioreq->iotd_Req.io_Command = do_write ? glob->writecmd : glob->readcmd;
367 err = DoIO((struct IORequest *) glob->diskioreq);
368 RestartTimer();
370 return err;