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.
12 #define USE_INLINE_STDARG
14 #include <exec/types.h>
15 #include <exec/errors.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() */
31 #include "ntfs_protos.h"
38 #define ID_BUSY 0x42555359
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
;
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
;
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
;
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;
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
;
113 struct VolumeInfo
*vol_info
= NULL
;
114 struct GlobalLock
*global_lock
;
115 struct ExtFileLock
*ext_lock
;
116 struct MinNode
*lock_node
;
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
);
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
);
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 */
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);
154 CopyMem(fs_data
->volume
.name
, dl
->dol_Name
,
155 fs_data
->volume
.name
[0] + 2);
159 /* patch locks and notifications to match this handler
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
,
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
,
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
;
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
);
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);
229 CopyMem(fs_data
->volume
.name
, BADDR(newvol
->dol_Name
),
230 fs_data
->volume
.name
[0] + 2);
233 fs_data
->doslist
= newvol
;
236 if (vol_info
== NULL
|| newvol
== NULL
)
241 fs_data
->info
= vol_info
;
242 glob
->data
= fs_data
;
247 SendEvent(IECLASS_DISKINSERTED
);
250 SendVolumePacket(newvol
, ACTION_VOLUME_ADD
);
252 D(bug("[NTFS] %s: Disk successfully initialised\n", __PRETTY_FUNCTION__
));
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
);
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
)
286 Remove((struct Node
*)fs_data
);
288 SendVolumePacket(fs_data
->doslist
, ACTION_VOLUME_REMOVE
);
290 FreeBootSector(fs_data
);
291 _FreeVecPooled(glob
->mempool
, fs_data
);
298 void DoDiskRemove(void)
300 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__
));
303 struct FSData
*fs_data
= glob
->data
;
305 if(!AttemptDestroyVolume(fs_data
)) {
306 fs_data
->doslist
->dol_Task
= 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__
));
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
;
338 /* Disk has been removed. */
339 D(bug("[NTFS] %s: Disk REMOVED\n", __PRETTY_FUNCTION__
));
340 glob
->disk_inserted
= FALSE
;
344 D(bug("[NTFS] %s: Done\n", __PRETTY_FUNCTION__
));
347 void UpdateDisk(void)
349 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__
));
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
;
375 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__
));
377 glob
->readcmd
= CMD_READ
;
378 glob
->writecmd
= CMD_WRITE
;
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
;
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
;
409 ULONG
AccessDisk(BOOL do_write
, ULONG num
, ULONG nblocks
, ULONG block_size
, UBYTE
*data
)
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
);
423 /* Adjust parameters if range is partially outside boundaries, or
424 * warn user and bale out if completely outside boundaries */
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
;
447 end
= glob
->data
->first_device_sector
+ glob
->data
->total_sectors
;
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
)
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
);