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 AROS_ALMOST_COMPATIBLE
14 #include <aros/macros.h>
15 #include <exec/types.h>
17 #include <dos/notify.h>
18 #include <proto/exec.h>
20 #include <dos/dosextens.h>
21 #include <dos/filehandler.h>
24 #include "ntfs_protos.h"
29 LONG
OpLockFile(struct ExtFileLock
*dirlock
, UBYTE
*name
, ULONG namelen
, LONG access
, struct ExtFileLock
**filelock
)
31 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__
));
33 // if they passed in a name, go searching for it
35 return LockFileByName(dirlock
, name
, namelen
, access
, filelock
);
37 // otherwise the empty filename, just make a copy
38 else if (dirlock
!= NULL
)
39 return CopyLock(dirlock
, filelock
);
41 // null dir lock means they want the root
43 return LockRoot(access
, filelock
);
46 void OpUnlockFile(struct ExtFileLock
*lock
)
48 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__
));
54 LONG
OpCopyLock(struct ExtFileLock
*lock
, struct ExtFileLock
**copy
)
56 D(bug("[NTFS]: %s(lock @ 0x%p)\n", __PRETTY_FUNCTION__
, lock
));
59 return CopyLock(lock
, copy
);
61 return LockRoot(SHARED_LOCK
, copy
);
64 LONG
OpLockParent(struct ExtFileLock
*lock
, struct ExtFileLock
**parent
)
69 struct NTFSMFTAttr dirattr
;
70 struct MFTAttr
*attrentry
;
72 D(bug("[NTFS]: %s(lock @ 0x%p)\n", __PRETTY_FUNCTION__
, lock
));
75 // the root has no parent, but as a special case we have to return success
77 if (lock
== NULL
|| lock
->gl
== &glob
->data
->info
->root_lock
) {
82 // if we're in the root directory, then the root is our parent
83 if (lock
->gl
->dir_cluster
== glob
->data
->info
->root_lock
.dir_cluster
)
84 return LockRoot(SHARED_LOCK
, parent
);
86 memset(&de
, 0, sizeof(struct DirEntry
));
87 memset(&dh
, 0, sizeof(struct DirHandle
));
90 if (lock
->gl
->attr
& ATTR_DIRECTORY
)
92 dh
.ioh
.mft
.mftrec_no
= lock
->dir
->ioh
.mft
.mftrec_no
;
93 InitDirHandle(glob
->data
, &dh
, FALSE
);
95 if ((err
= GetDirEntryByPath(&dh
, "/", 1, &de
)) != 0) {
101 dh
.ioh
.mft
.mftrec_no
= lock
->gl
->dir_cluster
/ glob
->data
->mft_size
;
102 InitDirHandle(glob
->data
, &dh
, FALSE
);
104 INIT_MFTATTRIB(&dirattr
, &dh
.ioh
.mft
);
105 attrentry
= FindMFTAttrib(&dirattr
, AT_FILENAME
);
106 attrentry
= (struct MFTAttr
*)((IPTR
)attrentry
+ AROS_LE2WORD(attrentry
->data
.resident
.value_offset
));
109 dh
.ioh
.mft
.mftrec_no
= AROS_LE2QUAD(*(UQUAD
*)((IPTR
)attrentry
)) & MFTREF_MASK
;
110 if (dh
.ioh
.mft
.mftrec_no
== 0x2)
111 dh
.ioh
.mft
.mftrec_no
= FILE_ROOT
;
112 dh
.ioh
.first_cluster
= dh
.ioh
.mft
.mftrec_no
* glob
->data
->mft_size
;
113 D(bug("[NTFS] %s: parent_mft = %u [%u]\n", __PRETTY_FUNCTION__
, (IPTR
)(dh
.ioh
.first_cluster
/ glob
->data
->mft_size
), (IPTR
)dh
.ioh
.mft
.mftrec_no
));
114 ReleaseDirHandle(&dh
);
115 InitDirHandle(dh
.ioh
.data
, &dh
, TRUE
);
117 if ((err
= GetDirEntryByCluster(&dh
, lock
->gl
->dir_cluster
, &de
)) != 0) {
122 D(bug("[NTFS] %s: found parent!\n", __PRETTY_FUNCTION__
));
124 err
= LockFile(&de
, SHARED_LOCK
, parent
);
130 * obtains a lock on the named file under the given dir. this is the service
131 * routine for DOS Open() (ie FINDINPUT/FINDOUTPUT/FINDUPDATE) and as such may
132 * only return a lock on a file, never on a dir.
134 LONG
OpOpenFile(struct ExtFileLock
*dirlock
, UBYTE
*name
, ULONG namelen
, LONG action
, struct ExtFileLock
**filelock
)
137 struct ExtFileLock
*lock
;
139 D(bug("[NTFS]: %s('", __PRETTY_FUNCTION__
); RawPutChars(name
, namelen
); bug("')\n"));
140 D(bug("[NTFS] %s: action = %s\n", __PRETTY_FUNCTION__
,
141 action
== ACTION_FINDINPUT
? "FINDINPUT" :
142 action
== ACTION_FINDOUTPUT
? "FINDOUTPUT" :
143 action
== ACTION_FINDUPDATE
? "FINDUPDATE" : "[unknown]"));
145 // no filename means they're trying to open whatever dirlock is (which
146 // despite the name may not actually be a dir). since there's already an
147 // extant lock, it's never going to be possible to get an exclusive lock,
148 // so this will only work for FINDINPUT (read-only)
150 D(bug("[NTFS] %s: trying to copy passed dir lock\n", __PRETTY_FUNCTION__
));
152 if (action
!= ACTION_FINDINPUT
) {
153 D(bug("[NTFS] %s: can't copy lock for write (exclusive)\n", __PRETTY_FUNCTION__
));
154 return ERROR_OBJECT_IN_USE
;
157 // dirs can't be opened
158 if (dirlock
== NULL
|| dirlock
->gl
->attr
& ATTR_DIRECTORY
) {
159 D(bug("[NTFS] %s: dir lock is a directory, which can't be opened\n", __PRETTY_FUNCTION__
));
160 return ERROR_OBJECT_WRONG_TYPE
;
163 // it's a file, just copy the lock
164 return CopyLock(dirlock
, filelock
);
168 err
= LockFileByName(dirlock
, name
, namelen
, action
== ACTION_FINDINPUT
? SHARED_LOCK
: EXCLUSIVE_LOCK
, &lock
);
172 D(bug("[NTFS] %s: found existing file\n", __PRETTY_FUNCTION__
));
174 // can't open directories
175 if (lock
->gl
->attr
& ATTR_DIRECTORY
) {
176 D(bug("[NTFS] %s: it's a directory, can't open it\n", __PRETTY_FUNCTION__
));
178 return ERROR_OBJECT_WRONG_TYPE
;
181 // INPUT/UPDATE use the file as/is
182 if (action
!= ACTION_FINDOUTPUT
) {
183 D(bug("[NTFS] %s: returning the lock\n", __PRETTY_FUNCTION__
));
188 // whereas OUTPUT truncates it
189 D(bug("[NTFS] %s: handling FINDOUTPUT, so truncating the file\n", __PRETTY_FUNCTION__
));
191 if (lock
->gl
->attr
& ATTR_READ_ONLY
) {
192 D(bug("[NTFS] %s: file is write protected, doing nothing\n", __PRETTY_FUNCTION__
));
194 return ERROR_WRITE_PROTECTED
;
197 // update the dir entry to make the file empty
198 UpdateDirEntry(lock
->entry
);
200 D(bug("[NTFS] %s: set first cluster and size to 0 in directory entry\n", __PRETTY_FUNCTION__
));
203 lock
->gl
->first_cluster
= lock
->dir
->ioh
.first_cluster
= 0xffffffff;
204 RESET_HANDLE(&lock
->dir
->ioh
);
207 D(bug("[NTFS] %s: file truncated, returning the lock\n", __PRETTY_FUNCTION__
));
215 // any error other than "not found" should be taken as-is
216 if (err
!= ERROR_OBJECT_NOT_FOUND
)
219 // not found. for INPUT we bail out
220 if (action
== ACTION_FINDINPUT
) {
221 D(bug("[NTFS] %s: file not found, and not creating it\n", __PRETTY_FUNCTION__
));
222 return ERROR_OBJECT_NOT_FOUND
;
225 D(bug("[NTFS] %s: trying to create '", __PRETTY_FUNCTION__
); RawPutChars(name
, namelen
); bug("'\n"));
228 (*filelock
)->do_notify
= TRUE
;
229 D(bug("[NTFS] %s: returning lock on new file\n", __PRETTY_FUNCTION__
));
235 /* find the named file in the directory referenced by dirlock, and delete it.
236 * if the file is a directory, it will only be deleted if it's empty */
237 LONG
OpDeleteFile(struct ExtFileLock
*dirlock
, UBYTE
*name
, ULONG namelen
)
241 D(bug("[NTFS]: %s('", __PRETTY_FUNCTION__
); RawPutChars(name
, namelen
); bug("')\n"));
243 #if defined(NTFS_READONLY)
244 err
= ERROR_DISK_WRITE_PROTECTED
;
252 LONG
OpRenameFile(struct ExtFileLock
*sdirlock
, UBYTE
*sname
, ULONG snamelen
, struct ExtFileLock
*ddirlock
, UBYTE
*dname
, ULONG dnamelen
)
256 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__
));
258 #if defined(NTFS_READONLY)
259 err
= ERROR_DISK_WRITE_PROTECTED
;
267 LONG
OpCreateDir(struct ExtFileLock
*dirlock
, UBYTE
*name
, ULONG namelen
, struct ExtFileLock
**newdirlock
)
271 D(bug("[NTFS]: %s('", __PRETTY_FUNCTION__
); RawPutChars(name
, namelen
); bug("')\n"));
273 #if defined(NTFS_READONLY)
274 err
= ERROR_DISK_WRITE_PROTECTED
;
282 LONG
OpRead(struct ExtFileLock
*lock
, UBYTE
*data
, UQUAD want
, UQUAD
*read
)
285 struct NTFSMFTAttr dataatrr
;
286 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__
));
287 D(bug("[NTFS] %s: %u bytes, pos %u\n", __PRETTY_FUNCTION__
, (IPTR
)want
, (IPTR
)lock
->pos
));
292 if (want
+ lock
->pos
> lock
->gl
->size
) {
293 want
= lock
->gl
->size
- lock
->pos
;
294 D(bug("[NTFS] %s: full read would take us past end-of-file, adjusted want to %u bytes\n", __PRETTY_FUNCTION__
, (IPTR
)want
));
297 INIT_MFTATTRIB(&dataatrr
, lock
->entry
->entry
);
298 if (MapMFTAttrib (&dataatrr
, lock
->entry
->entry
, AT_DATA
))
300 if (ReadMFTAttrib(&dataatrr
, data
, lock
->pos
, want
, 0) == 0)
303 lock
->pos
= lock
->pos
+ want
;
304 D(bug("[NTFS] %s: read %u bytes, new file pos is %u\n", __PRETTY_FUNCTION__
, (IPTR
)want
, (IPTR
)lock
->pos
));
310 LONG
OpWrite(struct ExtFileLock
*lock
, UBYTE
*data
, UQUAD want
, UQUAD
*written
)
314 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__
));
315 #if defined(NTFS_READONLY)
316 err
= ERROR_DISK_WRITE_PROTECTED
;
318 D(bug("[NTFS] %s: %u bytes, pos %u\n", __PRETTY_FUNCTION__
, (IPTR
)want
, (IPTR
)lock
->pos
));
324 LONG
OpSetFileSize(struct ExtFileLock
*lock
, UQUAD offset
, LONG offsetfrom
, UQUAD
*newsize
)
328 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__
));
329 #if defined(NTFS_READONLY)
330 err
= ERROR_DISK_WRITE_PROTECTED
;
337 LONG
OpSetProtect(struct ExtFileLock
*dirlock
, UBYTE
*name
, ULONG namelen
, ULONG prot
)
341 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__
));
342 #if defined(NTFS_READONLY)
343 err
= ERROR_DISK_WRITE_PROTECTED
;
350 LONG
OpSetDate(struct ExtFileLock
*dirlock
, UBYTE
*name
, ULONG namelen
, struct DateStamp
*ds
)
354 D(bug("[NTFS]: %s()\n", __PRETTY_FUNCTION__
));
355 #if defined(NTFS_READONLY)
356 err
= ERROR_DISK_WRITE_PROTECTED
;
363 LONG
OpAddNotify(struct NotifyRequest
*nr
)
368 struct GlobalLock
*gl
= NULL
, *tmp
;
369 struct NotifyNode
*nn
;
372 D(bug("[NTFS]: %s('%s')\n", __PRETTY_FUNCTION__
, nr
->nr_FullName
));
374 // if the request is for the volume root, then we just link to the root lock
375 if (nr
->nr_FullName
[strlen(nr
->nr_FullName
)-1] == ':')
377 D(bug("[NTFS] %s: adding notify for root dir\n", __PRETTY_FUNCTION__
));
378 gl
= &glob
->data
->info
->root_lock
;
381 dh
.ioh
.mft
.mftrec_no
= FILE_ROOT
;
382 dh
.ioh
.mft
.buf
= NULL
;
383 if ((err
= InitDirHandle(glob
->data
, &dh
, FALSE
)) != 0)
386 memset(&de
, 0, sizeof(struct DirEntry
));
388 // look for the entry
389 err
= GetDirEntryByPath(&dh
, nr
->nr_FullName
, strlen(nr
->nr_FullName
), &de
);
390 if (err
!= 0 && err
!= ERROR_OBJECT_NOT_FOUND
)
393 // if it was found, then it might be open. try to find the global lock
397 D(bug("[NTFS] %s: file exists (%ld/%ld), looking for global lock\n", __PRETTY_FUNCTION__
, de
.cluster
, de
.no
));
399 ForeachNode(&glob
->data
->info
->locks
, tmp
)
400 if (tmp
->dir_cluster
== de
.cluster
&& tmp
->dir_entry
== de
.no
) {
403 D(bug("[NTFS] %s: global lock 0x%0x\n", __PRETTY_FUNCTION__
, gl
));
412 D(bug("[NTFS] %s: file doesn't exist\n", __PRETTY_FUNCTION__
));
418 D(bug("[NTFS] %s: file not currently locked\n", __PRETTY_FUNCTION__
));
421 // allocate space for the notify node
422 if ((nn
= _AllocVecPooled(glob
->data
->info
->mem_pool
,
423 sizeof(struct NotifyNode
))) == NULL
)
424 return ERROR_NO_FREE_STORE
;
431 ADDTAIL(&glob
->data
->info
->notifies
, nn
);
433 // tell them that the file exists if they wanted to know
434 if (exists
&& nr
->nr_Flags
& NRF_NOTIFY_INITIAL
)
437 D(bug("[NTFS] %s: notifying for '%s'\n", __PRETTY_FUNCTION__
, nr
->nr_FullName
));
442 LONG
OpRemoveNotify(struct NotifyRequest
*nr
)
444 struct FSData
*fs_data
;
445 struct NotifyNode
*nn
, *nn2
;
447 D(bug("[NTFS]: %s('%s')\n", __PRETTY_FUNCTION__
, nr
->nr_FullName
));
449 /* search inserted volume for the request */
450 if (glob
->data
!= NULL
) {
451 ForeachNodeSafe(&glob
->data
->info
->notifies
, nn
, nn2
) {
453 D(bug("[NTFS] %s: found notify request in list, removing it\n", __PRETTY_FUNCTION__
));
455 _FreeVecPooled(glob
->data
->info
->mem_pool
, nn
);
461 /* search offline volumes for the request */
462 ForeachNode(&glob
->sblist
, fs_data
) {
463 ForeachNodeSafe(&fs_data
->info
->notifies
, nn
, nn2
) {
465 D(bug("[NTFS] %s: found notify request in list, removing it\n", __PRETTY_FUNCTION__
));
467 _FreeVecPooled(fs_data
->info
->mem_pool
, nn
);
468 AttemptDestroyVolume(fs_data
);
474 D(bug("[NTFS] %s: not found, doing nothing\n", __PRETTY_FUNCTION__
));