Moved tests into appropriate subdirs.
[AROS.git] / workbench / fs / ntfs / disk.c
blob33b8fbb4d69c7c6d09e7d6832fbc9154b6018f91
1 /*
2 * ntfs.handler - New Technology FileSystem handler
4 * Copyright © 2012 The AROS Development Team
6 * This program is free software; you can redistribute it and/or modify it
7 * under the same terms as AROS itself.
9 * $Id $
12 #define USE_INLINE_STDARG
14 #include <exec/types.h>
15 #include <exec/errors.h>
17 #include <dos/dos.h>
18 #include <dos/dosextens.h>
19 #include <dos/filehandler.h>
20 #include <devices/newstyle.h>
21 #include <devices/trackdisk.h>
22 #include <devices/inputevent.h>
24 #include <proto/exec.h>
25 #include <proto/dos.h>
26 #include <proto/uuid.h>
28 #include <string.h> /* for memset() */
30 #include "ntfs_fs.h"
31 #include "ntfs_protos.h"
32 #include "cache.h"
33 #include "support.h"
35 #include "debug.h"
37 #ifndef ID_BUSY
38 #define ID_BUSY 0x42555359
39 #endif
41 /* TD64 commands */
42 #ifndef TD_READ64
43 #define TD_READ64 24
44 #define TD_WRITE64 25
45 #endif
47 void FillDiskInfo (struct InfoData *id)
49 struct DosEnvec *de = BADDR(glob->fssm->fssm_Environ);
51 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__));
53 id->id_NumSoftErrors = 0;
54 id->id_UnitNumber = glob->fssm->fssm_Unit;
55 id->id_DiskState = ID_VALIDATED;
57 if (glob->data) {
58 id->id_NumBlocks = glob->data->total_sectors;
59 id->id_NumBlocksUsed = glob->data->used_sectors;
60 id->id_BytesPerBlock = glob->data->sectorsize;
62 id->id_DiskType = ID_NTFS_DISK;
64 #if defined(NTFS_READONLY)
65 id->id_DiskState = ID_WRITE_PROTECTED;
66 #endif
68 id->id_VolumeNode = MKBADDR(glob->data->doslist);
69 id->id_InUse = (IsListEmpty(&glob->data->info->locks)
70 && IsListEmpty(&glob->data->info->notifies)) ? DOSFALSE : DOSTRUE;
73 else {
74 id->id_NumBlocks = de->de_Surfaces * de->de_BlocksPerTrack * (de->de_HighCyl+1 - de->de_LowCyl) / de->de_SectorPerBlock;
75 id->id_NumBlocksUsed = id->id_NumBlocks;
76 id->id_BytesPerBlock = de->de_SizeBlock << 2;
78 id->id_DiskState = ID_VALIDATED;
80 if (glob->disk_inhibited)
81 id->id_DiskType = ID_BUSY;
82 else if (glob->disk_inserted)
83 id->id_DiskType = ID_NOT_REALLY_DOS; //ID_UNREADABLE_DISK;
84 else
85 id->id_DiskType = ID_NO_DISK_PRESENT;
87 id->id_VolumeNode = BNULL;
88 id->id_InUse = DOSFALSE;
92 void SendVolumePacket(struct DosList *vol, ULONG action)
94 struct DosPacket *dospacket;
96 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__));
98 dospacket = AllocDosObject(DOS_STDPKT, TAG_DONE);
99 dospacket->dp_Type = ACTION_DISK_CHANGE;
100 dospacket->dp_Arg1 = ID_NTFS_DISK;
101 dospacket->dp_Arg2 = (IPTR)vol;
102 dospacket->dp_Arg3 = action;
103 dospacket->dp_Port = NULL;
105 PutMsg(glob->ourport, dospacket->dp_Link);
108 void DoDiskInsert(void)
110 struct FSData *fs_data;
111 ULONG err;
112 struct DosList *dl;
113 struct VolumeInfo *vol_info = NULL;
114 struct GlobalLock *global_lock;
115 struct ExtFileLock *ext_lock;
116 struct MinNode *lock_node;
117 APTR pool;
118 struct NotifyNode *nn;
119 struct DosList *newvol = NULL;
121 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__));
123 if (glob->data == NULL && (fs_data = _AllocVecPooled(glob->mempool, sizeof(struct FSData))))
125 memset(fs_data, 0, sizeof(struct FSData));
127 err = ReadBootSector(fs_data);
128 if (err == 0) {
130 /* Scan volume list for a matching volume (would be better to
131 * match by serial number) */
132 dl = LockDosList(LDF_VOLUMES | LDF_WRITE);
133 dl = FindDosEntry(dl, fs_data->volume.name + 1,
134 LDF_VOLUMES | LDF_WRITE);
135 UnLockDosList(LDF_VOLUMES | LDF_WRITE);
137 if (dl != NULL)
139 dl->dol_Task = glob->ourport;
140 fs_data->doslist = dl;
142 D(bug("[NTFS] %s: Found old volume.\n", __PRETTY_FUNCTION__));
144 vol_info = BADDR(dl->dol_misc.dol_volume.dol_LockList);
146 #if 0 /* no point until we match volumes by serial number */
147 /* update name */
148 #ifdef AROS_FAST_BPTR
149 /* ReadBootSector() sets a null byte after the
150 * string, so this should be fine */
151 CopyMem(fs_data->volume.name + 1, dl->dol_Name,
152 fs_data->volume.name[0] + 1);
153 #else
154 CopyMem(fs_data->volume.name, dl->dol_Name,
155 fs_data->volume.name[0] + 2);
156 #endif
157 #endif
159 /* patch locks and notifications to match this handler
160 * instance */
161 ForeachNode(&vol_info->locks, global_lock) {
162 ForeachNode(&global_lock->locks, lock_node) {
163 ext_lock = LOCKFROMNODE(lock_node);
164 D(bug("[NTFS] %s: Patching adopted lock %p. old port = %p,"
165 " new port = %p\n", __PRETTY_FUNCTION__, ext_lock, ext_lock->fl_Task,
166 glob->ourport));
167 ext_lock->fl_Task = glob->ourport;
168 ext_lock->data = fs_data;
169 ext_lock->dir->ioh.data = fs_data;
173 ForeachNode(&vol_info->root_lock.locks, lock_node) {
174 ext_lock = LOCKFROMNODE(lock_node);
175 D(bug("[NTFS] %s: Patching adopted ROOT lock %p. old port = %p,"
176 " new port = %p\n", __PRETTY_FUNCTION__, ext_lock, ext_lock->fl_Task,
177 glob->ourport));
178 ext_lock->fl_Task = glob->ourport;
179 ext_lock->data = fs_data;
180 ext_lock->dir->ioh.data = fs_data;
183 ForeachNode(&vol_info->notifies, nn)
184 nn->nr->nr_Handler = glob->ourport;
186 else
188 D(bug("[NTFS] %s: Creating new volume.\n", __PRETTY_FUNCTION__));
190 /* create transferable core volume info */
191 pool = CreatePool(MEMF_PUBLIC, DEF_POOL_SIZE, DEF_POOL_THRESHOLD);
192 if (pool != NULL) {
193 vol_info = _AllocVecPooled(pool, sizeof(struct VolumeInfo));
194 if (vol_info != NULL) {
195 vol_info->mem_pool = pool;
196 UUID_Copy((const uuid_t *)&fs_data->uuid, (uuid_t *)&vol_info->uuid);
197 NEWLIST(&vol_info->locks);
198 NEWLIST(&vol_info->notifies);
200 vol_info->root_lock.dir_cluster = FILE_ROOT * fs_data->mft_size;
201 vol_info->root_lock.dir_entry = -1;
202 vol_info->root_lock.access = SHARED_LOCK;
203 vol_info->root_lock.first_cluster = FILE_ROOT * fs_data->mft_size;
204 vol_info->root_lock.attr = ATTR_DIRECTORY;
205 vol_info->root_lock.size = 0;
206 CopyMem(fs_data->volume.name, vol_info->root_lock.name,
207 fs_data->volume.name[0] + 1);
208 NEWLIST(&vol_info->root_lock.locks);
211 if ((newvol = _AllocVecPooled(pool, sizeof(struct DosList)))) {
212 newvol->dol_Next = BNULL;
213 newvol->dol_Type = DLT_VOLUME;
214 newvol->dol_Task = glob->ourport;
215 newvol->dol_Lock = BNULL;
217 CopyMem(&fs_data->volume.create_time, &newvol->dol_misc.dol_volume.dol_VolumeDate, sizeof(struct DateStamp));
219 newvol->dol_misc.dol_volume.dol_LockList = MKBADDR(&vol_info->locks);
221 newvol->dol_misc.dol_volume.dol_DiskType = ID_NTFS_DISK;
223 if ((newvol->dol_Name = MKBADDR(_AllocVecPooled(pool, fs_data->volume.name[0] + 2)))) {
224 #ifdef AROS_FAST_BPTR
225 /* ReadBootSector() sets a null byte after the string, so this should be fine */
226 CopyMem(fs_data->volume.name + 1, newvol->dol_Name,
227 fs_data->volume.name[0] + 1);
228 #else
229 CopyMem(fs_data->volume.name, BADDR(newvol->dol_Name),
230 fs_data->volume.name[0] + 2);
231 #endif
233 fs_data->doslist = newvol;
236 if (vol_info == NULL || newvol == NULL)
237 DeletePool(pool);
241 fs_data->info = vol_info;
242 glob->data = fs_data;
243 glob->last_num = -1;
245 if (dl != NULL)
247 SendEvent(IECLASS_DISKINSERTED);
249 else
250 SendVolumePacket(newvol, ACTION_VOLUME_ADD);
252 D(bug("[NTFS] %s: Disk successfully initialised\n", __PRETTY_FUNCTION__));
254 return;
256 else if (err == IOERR_BADADDRESS)
258 ErrorMessageArgs("Your device does not support 64-bit\n"
259 "access to the disk while it is needed!\n"
260 "In order to prevent data damage access to\n"
261 "this disk was blocked.\n"
262 "Please upgrade your device driver.", NULL);
265 _FreeVecPooled(glob->mempool, fs_data);
268 SendEvent(IECLASS_DISKINSERTED);
270 return;
273 BOOL AttemptDestroyVolume(struct FSData *fs_data)
275 BOOL destroyed = FALSE;
277 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__));
279 /* check if the volume can be removed */
280 if (IsListEmpty(&fs_data->info->locks) && IsListEmpty(&fs_data->info->notifies)) {
281 D(bug("[NTFS] %s: Removing volume completely\n", __PRETTY_FUNCTION__));
283 if (fs_data == glob->data)
284 glob->data = NULL;
285 else
286 Remove((struct Node *)fs_data);
288 SendVolumePacket(fs_data->doslist, ACTION_VOLUME_REMOVE);
290 FreeBootSector(fs_data);
291 _FreeVecPooled(glob->mempool, fs_data);
292 destroyed = TRUE;
295 return destroyed;
298 void DoDiskRemove(void)
300 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__));
302 if (glob->data) {
303 struct FSData *fs_data = glob->data;
305 if(!AttemptDestroyVolume(fs_data)) {
306 fs_data->doslist->dol_Task = NULL;
307 glob->data = NULL;
308 D(bug("\tMoved in-memory super block to spare list. "
309 "Waiting for locks and notifications to be freed\n"));
310 AddTail((struct List *)&glob->sblist, (struct Node *)fs_data);
311 SendEvent(IECLASS_DISKREMOVED);
316 void ProcessDiskChange(void)
318 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__));
320 if (glob->disk_inhibited) {
321 D(bug("[NTFS] %s: Disk is inhibited, ignoring disk change\n", __PRETTY_FUNCTION__));
322 return;
325 glob->diskioreq->iotd_Req.io_Command = TD_CHANGESTATE;
326 glob->diskioreq->iotd_Req.io_Data = NULL;
327 glob->diskioreq->iotd_Req.io_Length = 0;
328 glob->diskioreq->iotd_Req.io_Flags = IOF_QUICK;
329 DoIO((struct IORequest*) glob->diskioreq);
331 if (glob->diskioreq->iotd_Req.io_Error == 0 && glob->diskioreq->iotd_Req.io_Actual == 0) {
332 /* Disk has been inserted. */
333 D(bug("[NTFS] %s: Disk INSERTED\n", __PRETTY_FUNCTION__));
334 glob->disk_inserted = TRUE;
335 DoDiskInsert();
337 else {
338 /* Disk has been removed. */
339 D(bug("[NTFS] %s: Disk REMOVED\n", __PRETTY_FUNCTION__));
340 glob->disk_inserted = FALSE;
341 DoDiskRemove();
344 D(bug("[NTFS] %s: Done\n", __PRETTY_FUNCTION__));
347 void UpdateDisk(void)
349 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__));
351 if (glob->data)
353 Cache_Flush(glob->data->cache);
356 glob->diskioreq->iotd_Req.io_Command = CMD_UPDATE;
357 DoIO((struct IORequest *)glob->diskioreq);
359 /* Turn off motor (where applicable) if nothing has happened during the
360 * last timer period */
361 if (!glob->restart_timer) {
362 D(bug("[NTFS] %s: Stopping drive motor\n", __PRETTY_FUNCTION__));
363 glob->diskioreq->iotd_Req.io_Command = TD_MOTOR;
364 glob->diskioreq->iotd_Req.io_Length = 0;
365 DoIO((struct IORequest *)glob->diskioreq);
369 /* probe the device to determine 64-bit support */
370 void Probe_64bit_support(void)
372 struct NSDeviceQueryResult nsd_query;
373 UWORD *nsd_cmd;
375 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__));
377 glob->readcmd = CMD_READ;
378 glob->writecmd = CMD_WRITE;
380 /* probe TD64 */
381 glob->diskioreq->iotd_Req.io_Command = TD_READ64;
382 glob->diskioreq->iotd_Req.io_Offset = 0;
383 glob->diskioreq->iotd_Req.io_Length = 0;
384 glob->diskioreq->iotd_Req.io_Actual = 0;
385 glob->diskioreq->iotd_Req.io_Data = 0;
387 if (DoIO((struct IORequest *) glob->diskioreq) != IOERR_NOCMD) {
388 D(bug("[NTFS] %s: 64-bit trackdisk extensions supported\n", __PRETTY_FUNCTION__));
389 glob->readcmd = TD_READ64;
390 glob->writecmd = TD_WRITE64;
393 /* probe NSD */
394 glob->diskioreq->iotd_Req.io_Command = NSCMD_DEVICEQUERY;
395 glob->diskioreq->iotd_Req.io_Length = sizeof(struct NSDeviceQueryResult);
396 glob->diskioreq->iotd_Req.io_Data = (APTR) &nsd_query;
398 if (DoIO((struct IORequest *) glob->diskioreq) == 0)
399 for (nsd_cmd = nsd_query.SupportedCommands; *nsd_cmd != 0; nsd_cmd++) {
400 if (*nsd_cmd == NSCMD_TD_READ64) {
401 D(bug("[NTFS] %s: NSD 64-bit trackdisk extensions supported\n", __PRETTY_FUNCTION__));
402 glob->readcmd = NSCMD_TD_READ64;
403 glob->writecmd = NSCMD_TD_WRITE64;
404 break;
409 ULONG AccessDisk(BOOL do_write, ULONG num, ULONG nblocks, ULONG block_size, UBYTE *data)
411 UQUAD off;
412 ULONG err;
413 ULONG start, end;
415 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__));
417 #if DEBUG_CACHESTATS > 1
418 ErrorMessage("Accessing %lu sector(s) starting at %lu.\n"
419 "First volume sector is %lu, sector size is %lu.\n", nblocks, num,
420 glob->data->first_device_sector, block_size);
421 #endif
423 /* Adjust parameters if range is partially outside boundaries, or
424 * warn user and bale out if completely outside boundaries */
425 if (glob->data) {
426 start = glob->data->first_device_sector;
427 if (num + nblocks <= glob->data->first_device_sector) {
428 if (num != glob->last_num) {
429 glob->last_num = num;
430 ErrorMessage("A handler attempted to %s %lu sector(s) starting\n"
431 "from %lu, before the actual volume space.\n"
432 "First volume sector is %lu, sector size is %lu.\n"
433 "Either your disk is damaged or it is a bug in\n"
434 "the handler. Please check your disk and/or\n"
435 "report this problem to the developers team.",
436 (IPTR)(do_write ? "write" : "read"), nblocks, num,
437 glob->data->first_device_sector, block_size);
439 return IOERR_BADADDRESS;
441 else if (num < start) {
442 nblocks -= start - num;
443 data += (start - num) * block_size;
444 num = start;
447 end = glob->data->first_device_sector + glob->data->total_sectors;
448 if (num >= end) {
449 if (num != glob->last_num) {
450 glob->last_num = num;
451 ErrorMessage("A handler attempted to %s %lu sector(s) starting\n"
452 "from %lu, beyond the actual volume space.\n"
453 "Last volume sector is %lu, sector size is %lu.\n"
454 "Either your disk is damaged or it is a bug in\n"
455 "the handler. Please check your disk and/or\n"
456 "report this problem to the developers team.",
457 (IPTR)(do_write ? "write" : "read"), nblocks, num,
458 end - 1, block_size);
460 return IOERR_BADADDRESS;
462 else if (num + nblocks > end)
463 nblocks = end - num;
466 off = ((UQUAD) num) * block_size;
468 glob->diskioreq->iotd_Req.io_Offset = off & 0xFFFFFFFF;
469 glob->diskioreq->iotd_Req.io_Actual = off >> 32;
471 if (glob->diskioreq->iotd_Req.io_Actual && (glob->readcmd == CMD_READ))
472 return IOERR_BADADDRESS;
474 glob->diskioreq->iotd_Req.io_Length = nblocks * block_size;
475 glob->diskioreq->iotd_Req.io_Data = data;
476 glob->diskioreq->iotd_Req.io_Command = do_write ? glob->writecmd : glob->readcmd;
478 err = DoIO((struct IORequest *) glob->diskioreq);
480 return err;