2 * Copyright 2012-2016 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Paweł Dziepak, pdziepak@quarnos.org
15 #include <AutoDeleter.h>
17 #include <NodeMonitor.h>
21 #include "RootInode.h"
34 fAIOWait(create_sem(1, NULL
)),
37 rw_lock_init(&fDelegationLock
, NULL
);
38 mutex_init(&fStateLock
, NULL
);
39 mutex_init(&fFileCacheLock
, NULL
);
40 rw_lock_init(&fWriteLock
, NULL
);
41 mutex_init(&fAIOLock
, NULL
);
46 Inode::CreateInode(FileSystem
* fs
, const FileInfo
& fi
, Inode
** _inode
)
49 ASSERT(_inode
!= NULL
);
52 if (fs
->Root() == NULL
)
53 inode
= new(std::nothrow
) RootInode
;
55 inode
= new(std::nothrow
) Inode
;
61 inode
->fFileSystem
= fs
;
66 RPC::Server
* serv
= fs
->Server();
67 Request
request(serv
, fs
);
68 RequestBuilder
& req
= request
.Builder();
70 req
.PutFH(inode
->fInfo
.fHandle
);
72 Attribute attr
[] = { FATTR4_TYPE
, FATTR4_CHANGE
, FATTR4_SIZE
,
73 FATTR4_FSID
, FATTR4_FILEID
};
74 req
.GetAttr(attr
, sizeof(attr
) / sizeof(Attribute
));
76 status_t result
= request
.Send();
80 ReplyInterpreter
& reply
= request
.Reply();
82 if (inode
->HandleErrors(attempt
, reply
.NFS4Error(), serv
))
89 result
= reply
.GetAttr(&values
, &count
);
93 if (fi
.fFileId
== 0) {
94 if (count
< 5 || values
[4].fAttribute
!= FATTR4_FILEID
)
95 inode
->fInfo
.fFileId
= fs
->AllocFileId();
97 inode
->fInfo
.fFileId
= values
[4].fData
.fValue64
;
99 inode
->fInfo
.fFileId
= fi
.fFileId
;
101 // FATTR4_TYPE is mandatory
102 inode
->fType
= values
[0].fData
.fValue32
;
104 if (inode
->fType
== NF4DIR
)
105 inode
->fCache
= new DirectoryCache(inode
);
106 inode
->fAttrCache
= new DirectoryCache(inode
, true);
108 // FATTR4_CHANGE is mandatory
109 inode
->fChange
= values
[1].fData
.fValue64
;
111 // FATTR4_SIZE is mandatory
112 size
= values
[2].fData
.fValue64
;
113 inode
->fMaxFileSize
= size
;
115 // FATTR4_FSID is mandatory
117 = reinterpret_cast<FileSystemId
*>(values
[3].fData
.fPointer
);
118 if (*fsid
!= fs
->FsId()) {
120 return B_ENTRY_NOT_FOUND
;
130 if (inode
->fType
== NF4REG
)
131 inode
->fFileCache
= file_cache_create(fs
->DevId(), inode
->ID(), size
);
139 if (fDelegation
!= NULL
)
142 if (fFileCache
!= NULL
)
143 file_cache_delete(fFileCache
);
148 delete_sem(fAIOWait
);
149 mutex_destroy(&fAIOLock
);
150 mutex_destroy(&fStateLock
);
151 mutex_destroy(&fFileCacheLock
);
152 rw_lock_destroy(&fDelegationLock
);
153 rw_lock_destroy(&fWriteLock
);
155 ASSERT(fAIOCount
== 0);
160 Inode::RevalidateFileCache()
162 if (fDelegation
!= NULL
)
166 status_t result
= GetChangeInfo(&change
);
170 MutexLocker
_(fFileCacheLock
);
171 if (change
== fChange
)
175 file_cache_delete(fFileCache
);
178 fMetaCache
.InvalidateStat();
181 fMaxFileSize
= st
.st_size
;
182 fFileCache
= file_cache_create(fFileSystem
->DevId(), ID(), fMaxFileSize
);
190 Inode::LookUp(const char* name
, ino_t
* id
)
192 ASSERT(name
!= NULL
);
196 return B_NOT_A_DIRECTORY
;
201 status_t result
= NFS4Inode::LookUp(name
, &change
, &fileID
, &handle
);
205 *id
= FileIdToInoT(fileID
);
207 result
= ChildAdded(name
, fileID
, handle
);
212 if (!fCache
->Valid()) {
214 fCache
->SetChangeInfo(change
);
216 fCache
->ValidateChangeInfo(change
);
218 fCache
->AddEntry(name
, *id
);
226 Inode::Link(Inode
* dir
, const char* name
)
229 ASSERT(name
!= NULL
);
231 ChangeInfo changeInfo
;
232 status_t result
= NFS4Inode::Link(dir
, name
, &changeInfo
);
236 fFileSystem
->Root()->MakeInfoInvalid();
237 fInfo
.fNames
->AddName(dir
->fInfo
.fNames
, name
);
240 if (dir
->fCache
->Valid()) {
241 if (changeInfo
.fAtomic
242 && dir
->fCache
->ChangeInfo() == changeInfo
.fBefore
) {
243 dir
->fCache
->AddEntry(name
, fInfo
.fFileId
, true);
244 dir
->fCache
->SetChangeInfo(changeInfo
.fAfter
);
246 dir
->fCache
->Trash();
248 dir
->fCache
->Unlock();
250 notify_entry_created(fFileSystem
->DevId(), dir
->ID(), name
, ID());
257 Inode::Remove(const char* name
, FileType type
, ino_t
* id
)
259 ASSERT(name
!= NULL
);
261 MemoryDeleter nameDeleter
;
262 if (type
== NF4NAMEDATTR
) {
263 status_t result
= LoadAttrDirHandle();
267 name
= AttrToFileName(name
);
270 nameDeleter
.SetTo(const_cast<char*>(name
));
273 ChangeInfo changeInfo
;
275 status_t result
= NFS4Inode::RemoveObject(name
, type
, &changeInfo
, &fileID
);
279 DirectoryCache
* cache
= type
!= NF4NAMEDATTR
? fCache
: fAttrCache
;
281 if (cache
->Valid()) {
282 if (changeInfo
.fAtomic
283 && fCache
->ChangeInfo() == changeInfo
.fBefore
) {
284 cache
->RemoveEntry(name
);
285 cache
->SetChangeInfo(changeInfo
.fAfter
);
286 } else if (cache
->ChangeInfo() != changeInfo
.fBefore
)
291 fFileSystem
->Root()->MakeInfoInvalid();
293 *id
= FileIdToInoT(fileID
);
295 if (type
== NF4NAMEDATTR
) {
296 notify_attribute_changed(fFileSystem
->DevId(), -1, ID(), name
,
299 notify_entry_removed(fFileSystem
->DevId(), ID(), name
,
300 FileIdToInoT(fileID
));
308 Inode::Rename(Inode
* from
, Inode
* to
, const char* fromName
, const char* toName
,
309 bool attribute
, ino_t
* id
, ino_t
* oldID
)
311 ASSERT(from
!= NULL
);
312 ASSERT(fromName
!= NULL
);
314 ASSERT(toName
!= NULL
);
316 if (from
->fFileSystem
!= to
->fFileSystem
)
317 return B_DONT_DO_THAT
;
319 MemoryDeleter fromNameDeleter
;
320 MemoryDeleter toNameDeleter
;
322 status_t result
= from
->LoadAttrDirHandle();
326 result
= to
->LoadAttrDirHandle();
330 fromName
= from
->AttrToFileName(fromName
);
331 toName
= to
->AttrToFileName(toName
);
333 fromNameDeleter
.SetTo(const_cast<char*>(fromName
));
334 toNameDeleter
.SetTo(const_cast<char*>(toName
));
335 if (fromName
== NULL
|| toName
== NULL
)
339 uint64 oldFileID
= 0;
341 to
->NFS4Inode::LookUp(toName
, NULL
, &oldFileID
, NULL
);
344 ChangeInfo fromChange
, toChange
;
345 status_t result
= NFS4Inode::RenameNode(from
, to
, fromName
, toName
,
346 &fromChange
, &toChange
, &fileID
, attribute
);
350 from
->fFileSystem
->Root()->MakeInfoInvalid();
353 *id
= FileIdToInoT(fileID
);
355 *oldID
= FileIdToInoT(oldFileID
);
357 DirectoryCache
* cache
= attribute
? from
->fAttrCache
: from
->fCache
;
359 if (cache
->Valid()) {
360 if (fromChange
.fAtomic
&& cache
->ChangeInfo() == fromChange
.fBefore
) {
361 cache
->RemoveEntry(fromName
);
363 cache
->AddEntry(toName
, fileID
, true);
364 cache
->SetChangeInfo(fromChange
.fAfter
);
371 cache
= attribute
? to
->fAttrCache
: to
->fCache
;
373 if (cache
->Valid()) {
375 && (cache
->ChangeInfo() == toChange
.fBefore
)) {
376 cache
->AddEntry(toName
, fileID
, true);
377 cache
->SetChangeInfo(toChange
.fAfter
);
385 notify_attribute_changed(from
->fFileSystem
->DevId(), -1, from
->ID(),
386 fromName
, B_ATTR_REMOVED
);
387 notify_attribute_changed(to
->fFileSystem
->DevId(), -1, to
->ID(), toName
,
390 notify_entry_moved(from
->fFileSystem
->DevId(), from
->ID(), fromName
,
391 to
->ID(), toName
, FileIdToInoT(fileID
));
399 Inode::CreateLink(const char* name
, const char* path
, int mode
, ino_t
* id
)
401 return CreateObject(name
, path
, mode
, NF4LNK
, id
);
406 Inode::CreateObject(const char* name
, const char* path
, int mode
, FileType type
,
409 ASSERT(name
!= NULL
);
410 ASSERT(type
!= NF4LNK
|| path
!= NULL
);
412 ChangeInfo changeInfo
;
416 status_t result
= NFS4Inode::CreateObject(name
, path
, mode
, type
,
417 &changeInfo
, &fileID
, &handle
);
421 fFileSystem
->Root()->MakeInfoInvalid();
423 result
= ChildAdded(name
, fileID
, handle
);
428 if (fCache
->Valid()) {
429 if (changeInfo
.fAtomic
&& fCache
->ChangeInfo() == changeInfo
.fBefore
) {
430 fCache
->AddEntry(name
, fileID
, true);
431 fCache
->SetChangeInfo(changeInfo
.fAfter
);
437 notify_entry_created(fFileSystem
->DevId(), ID(), name
,
438 FileIdToInoT(fileID
));
440 *id
= FileIdToInoT(fileID
);
446 Inode::Access(int mode
)
451 bool cache
= fFileSystem
->GetConfiguration().fCacheMetadata
;
452 status_t result
= fMetaCache
.GetAccess(geteuid(), &allowed
);
453 if (result
!= B_OK
|| !cache
) {
454 result
= NFS4Inode::Access(&allowed
);
457 fMetaCache
.SetAccess(geteuid(), allowed
);
460 if ((allowed
& ACCESS4_READ
) != 0)
463 if ((allowed
& ACCESS4_LOOKUP
) != 0)
466 if ((allowed
& ACCESS4_EXECUTE
) != 0)
469 if ((allowed
& ACCESS4_MODIFY
) != 0)
472 if ((mode
& acc
) != mode
)
473 return B_NOT_ALLOWED
;
480 Inode::Stat(struct stat
* st
, OpenAttrCookie
* attr
)
485 return GetStat(st
, attr
);
487 bool cache
= fFileSystem
->GetConfiguration().fCacheMetadata
;
489 return GetStat(st
, NULL
);
491 status_t result
= fMetaCache
.GetStat(st
);
492 if (result
!= B_OK
) {
494 result
= GetStat(&temp
);
497 fMetaCache
.SetStat(temp
);
498 fMetaCache
.GetStat(st
);
506 Inode::GetStat(struct stat
* st
, OpenAttrCookie
* attr
)
512 status_t result
= NFS4Inode::GetStat(&values
, &count
, attr
);
516 // FATTR4_SIZE is mandatory
517 if (count
< 1 || values
[0].fAttribute
!= FATTR4_SIZE
) {
521 st
->st_size
= values
[0].fData
.fValue64
;
524 st
->st_mode
= Type();
525 if (count
>= next
&& values
[next
].fAttribute
== FATTR4_MODE
) {
526 st
->st_mode
|= values
[next
].fData
.fValue32
;
531 if (count
>= next
&& values
[next
].fAttribute
== FATTR4_NUMLINKS
) {
532 st
->st_nlink
= values
[next
].fData
.fValue32
;
537 if (count
>= next
&& values
[next
].fAttribute
== FATTR4_OWNER
) {
538 char* owner
= reinterpret_cast<char*>(values
[next
].fData
.fPointer
);
539 if (owner
!= NULL
&& isdigit(owner
[0]))
540 st
->st_uid
= atoi(owner
);
542 st
->st_uid
= gIdMapper
->GetUserId(owner
);
547 if (count
>= next
&& values
[next
].fAttribute
== FATTR4_OWNER_GROUP
) {
548 char* group
= reinterpret_cast<char*>(values
[next
].fData
.fPointer
);
549 if (group
!= NULL
&& isdigit(group
[0]))
550 st
->st_gid
= atoi(group
);
552 st
->st_gid
= gIdMapper
->GetGroupId(group
);
557 if (count
>= next
&& values
[next
].fAttribute
== FATTR4_TIME_ACCESS
) {
558 memcpy(&st
->st_atim
, values
[next
].fData
.fPointer
,
562 memset(&st
->st_atim
, 0, sizeof(timespec
));
564 if (count
>= next
&& values
[next
].fAttribute
== FATTR4_TIME_CREATE
) {
565 memcpy(&st
->st_crtim
, values
[next
].fData
.fPointer
,
569 memset(&st
->st_crtim
, 0, sizeof(timespec
));
571 if (count
>= next
&& values
[next
].fAttribute
== FATTR4_TIME_METADATA
) {
572 memcpy(&st
->st_ctim
, values
[next
].fData
.fPointer
,
576 memset(&st
->st_ctim
, 0, sizeof(timespec
));
578 if (count
>= next
&& values
[next
].fAttribute
== FATTR4_TIME_MODIFY
) {
579 memcpy(&st
->st_mtim
, values
[next
].fData
.fPointer
,
583 memset(&st
->st_mtim
, 0, sizeof(timespec
));
586 st
->st_blksize
= fFileSystem
->Root()->IOSize();
587 st
->st_blocks
= st
->st_size
/ st
->st_blksize
;
588 st
->st_blocks
+= st
->st_size
% st
->st_blksize
== 0 ? 0 : 1;
595 Inode::WriteStat(const struct stat
* st
, uint32 mask
, OpenAttrCookie
* cookie
)
603 if ((mask
& B_STAT_SIZE
) != 0) {
604 fMaxFileSize
= st
->st_size
;
605 file_cache_set_size(fFileCache
, st
->st_size
);
607 attr
[i
].fAttribute
= FATTR4_SIZE
;
608 attr
[i
].fFreePointer
= false;
609 attr
[i
].fData
.fValue64
= st
->st_size
;
613 if ((mask
& B_STAT_MODE
) != 0) {
614 attr
[i
].fAttribute
= FATTR4_MODE
;
615 attr
[i
].fFreePointer
= false;
616 attr
[i
].fData
.fValue32
= st
->st_mode
;
620 if ((mask
& B_STAT_UID
) != 0) {
621 attr
[i
].fAttribute
= FATTR4_OWNER
;
622 attr
[i
].fFreePointer
= true;
623 attr
[i
].fData
.fPointer
= gIdMapper
->GetOwner(st
->st_uid
);
627 if ((mask
& B_STAT_GID
) != 0) {
628 attr
[i
].fAttribute
= FATTR4_OWNER_GROUP
;
629 attr
[i
].fFreePointer
= true;
630 attr
[i
].fData
.fPointer
= gIdMapper
->GetOwnerGroup(st
->st_gid
);
634 if ((mask
& B_STAT_ACCESS_TIME
) != 0) {
635 attr
[i
].fAttribute
= FATTR4_TIME_ACCESS_SET
;
636 attr
[i
].fFreePointer
= true;
637 attr
[i
].fData
.fPointer
= malloc(sizeof(st
->st_atim
));
638 memcpy(attr
[i
].fData
.fPointer
, &st
->st_atim
, sizeof(st
->st_atim
));
642 if ((mask
& B_STAT_MODIFICATION_TIME
) != 0) {
643 attr
[i
].fAttribute
= FATTR4_TIME_MODIFY_SET
;
644 attr
[i
].fFreePointer
= true;
645 attr
[i
].fData
.fPointer
= malloc(sizeof(st
->st_mtim
));
646 memcpy(attr
[i
].fData
.fPointer
, &st
->st_mtim
, sizeof(st
->st_mtim
));
650 if (cookie
== NULL
) {
651 MutexLocker
stateLocker(fStateLock
);
652 ASSERT(fOpenState
!= NULL
|| !(mask
& B_STAT_SIZE
));
653 result
= NFS4Inode::WriteStat(fOpenState
, attr
, i
);
655 result
= NFS4Inode::WriteStat(cookie
->fOpenState
, attr
, i
);
657 fMetaCache
.InvalidateStat();
659 const uint32 kAccessMask
= B_STAT_MODE
| B_STAT_UID
| B_STAT_GID
;
660 if ((mask
& kAccessMask
) != 0)
661 fMetaCache
.InvalidateAccess();
668 Inode::CheckLockType(short ltype
, uint32 mode
)
675 if ((mode
& O_RDONLY
) == 0 && (mode
& O_RDWR
) == 0)
680 if ((mode
& O_WRONLY
) == 0 && (mode
& O_RDWR
) == 0)
691 Inode::TestLock(OpenFileCookie
* cookie
, struct flock
* lock
)
693 ASSERT(cookie
!= NULL
);
694 ASSERT(lock
!= NULL
);
696 if (lock
->l_type
== F_UNLCK
)
699 status_t result
= CheckLockType(lock
->l_type
, cookie
->fMode
);
703 LockType ltype
= sGetLockType(lock
->l_type
, false);
704 uint64 position
= lock
->l_start
;
706 if (lock
->l_len
+ lock
->l_start
== OFF_MAX
)
709 length
= lock
->l_len
;
712 result
= NFS4Inode::TestLock(cookie
, <ype
, &position
, &length
, conflict
);
717 lock
->l_type
= sLockTypeToHaiku(ltype
);
718 lock
->l_start
= static_cast<off_t
>(position
);
719 if (length
>= OFF_MAX
)
720 lock
->l_len
= OFF_MAX
;
722 lock
->l_len
= static_cast<off_t
>(length
);
724 lock
->l_type
= F_UNLCK
;
731 Inode::AcquireLock(OpenFileCookie
* cookie
, const struct flock
* lock
,
734 ASSERT(cookie
!= NULL
);
735 ASSERT(lock
!= NULL
);
737 OpenState
* state
= cookie
->fOpenState
;
739 status_t result
= CheckLockType(lock
->l_type
, cookie
->fMode
);
744 get_thread_info(find_thread(NULL
), &info
);
746 MutexLocker
locker(state
->fOwnerLock
);
747 LockOwner
* owner
= state
->GetLockOwner(info
.team
);
751 LockInfo
* linfo
= new(std::nothrow
) LockInfo(owner
);
756 linfo
->fStart
= lock
->l_start
;
757 if (lock
->l_len
+ lock
->l_start
== OFF_MAX
)
758 linfo
->fLength
= UINT64_MAX
;
760 linfo
->fLength
= lock
->l_len
;
761 linfo
->fType
= sGetLockType(lock
->l_type
, wait
);
763 result
= NFS4Inode::AcquireLock(cookie
, linfo
, wait
);
764 if (result
!= B_OK
) {
769 MutexLocker
_(state
->fLocksLock
);
770 state
->AddLock(linfo
);
771 cookie
->AddLock(linfo
);
778 Inode::ReleaseLock(OpenFileCookie
* cookie
, const struct flock
* lock
)
780 ASSERT(cookie
!= NULL
);
781 ASSERT(lock
!= NULL
);
785 LockInfo
* prev
= NULL
;
788 get_thread_info(find_thread(NULL
), &info
);
789 uint32 owner
= info
.team
;
791 OpenState
* state
= cookie
->fOpenState
;
792 MutexLocker
locker(state
->fLocksLock
);
793 LockInfo
* linfo
= state
->fLocks
;
794 while (linfo
!= NULL
) {
795 if (linfo
->fOwner
->fOwner
== owner
&& *linfo
== *lock
) {
796 state
->RemoveLock(linfo
, prev
);
801 linfo
= linfo
->fNext
;
805 linfo
= cookie
->fLocks
;
806 while (linfo
!= NULL
) {
807 if (linfo
->fOwner
->fOwner
== owner
&& *linfo
== *lock
) {
808 cookie
->RemoveLock(linfo
, prev
);
813 linfo
= linfo
->fCookieNext
;
820 status_t result
= NFS4Inode::ReleaseLock(cookie
, linfo
);
824 state
->DeleteLock(linfo
);
831 Inode::ReleaseAllLocks(OpenFileCookie
* cookie
)
833 ASSERT(cookie
!= NULL
);
838 OpenState
* state
= cookie
->fOpenState
;
839 MutexLocker
_(state
->fLocksLock
);
840 LockInfo
* linfo
= cookie
->fLocks
;
841 while (linfo
!= NULL
) {
842 cookie
->RemoveLock(linfo
, NULL
);
844 LockInfo
* prev
= NULL
;
845 LockInfo
* stateLock
= state
->fLocks
;
846 while (stateLock
!= NULL
) {
847 if (*linfo
== *stateLock
) {
848 state
->RemoveLock(stateLock
, prev
);
853 stateLock
= stateLock
->fNext
;
856 NFS4Inode::ReleaseLock(cookie
, linfo
);
857 state
->DeleteLock(linfo
);
859 linfo
= cookie
->fLocks
;
867 Inode::ChildAdded(const char* name
, uint64 fileID
,
868 const FileHandle
& fileHandle
)
870 ASSERT(name
!= NULL
);
872 fFileSystem
->Root()->MakeInfoInvalid();
876 fi
.fHandle
= fileHandle
;
878 return fFileSystem
->InoIdMap()->AddName(fi
, fInfo
.fNames
, name
,
879 FileIdToInoT(fileID
));
886 ASSERT(fInfo
.fNames
->fNames
.Head() != NULL
);
887 return fInfo
.fNames
->fNames
.Head()->fName
;
892 Inode::SetDelegation(Delegation
* delegation
)
894 ASSERT(delegation
!= NULL
);
896 WriteLocker
_(fDelegationLock
);
898 fMetaCache
.InvalidateStat();
901 fMetaCache
.LockValid();
903 fDelegation
= delegation
;
904 fOpenState
->AcquireReference();
905 fOpenState
->fDelegation
= delegation
;
906 fFileSystem
->AddDelegation(delegation
);
911 Inode::RecallDelegation(bool truncate
)
913 WriteLocker
_(fDelegationLock
);
914 if (fDelegation
== NULL
)
916 ReturnDelegation(truncate
);
921 Inode::RecallReadDelegation()
923 WriteLocker
_(fDelegationLock
);
924 if (fDelegation
== NULL
|| fDelegation
->Type() != OPEN_DELEGATE_READ
)
926 ReturnDelegation(false);
931 Inode::ReturnDelegation(bool truncate
)
933 ASSERT(fDelegation
!= NULL
);
935 fDelegation
->GiveUp(truncate
);
937 fMetaCache
.UnlockValid();
938 fFileSystem
->RemoveDelegation(fDelegation
);
940 MutexLocker
stateLocker(fStateLock
);
941 fOpenState
->fDelegation
= NULL
;
950 Inode::ReleaseOpenState()
952 ASSERT(fOpenState
!= NULL
);
954 if (fOpenState
->ReleaseReference() == 1) {
955 ASSERT(fAIOCount
== 0);
962 Inode::SyncAndCommit(bool force
)
964 if (!force
&& fDelegation
!= NULL
)
967 file_cache_sync(fFileCache
);
976 MutexLocker
_(fAIOLock
);
979 acquire_sem(fAIOWait
);
986 MutexLocker
_(fAIOLock
);
987 ASSERT(fAIOCount
> 0);
990 release_sem(fAIOWait
);