xref: /haiku/src/add-ons/kernel/file_systems/nfs4/InodeDir.cpp (revision a5ae9b4752c300bcc39d9531581cbfae2525aec3)
12a73e4c5SPawel Dziepak /*
22a73e4c5SPawel Dziepak  * Copyright 2012 Haiku, Inc. All rights reserved.
32a73e4c5SPawel Dziepak  * Distributed under the terms of the MIT License.
42a73e4c5SPawel Dziepak  *
52a73e4c5SPawel Dziepak  * Authors:
62a73e4c5SPawel Dziepak  *		Paweł Dziepak, pdziepak@quarnos.org
72a73e4c5SPawel Dziepak  */
82a73e4c5SPawel Dziepak 
92a73e4c5SPawel Dziepak 
102a73e4c5SPawel Dziepak #include "Inode.h"
112a73e4c5SPawel Dziepak 
122a73e4c5SPawel Dziepak #include <dirent.h>
132a73e4c5SPawel Dziepak #include <string.h>
142a73e4c5SPawel Dziepak 
15b2cea80cSPawel Dziepak #include "IdMap.h"
162a73e4c5SPawel Dziepak #include "Request.h"
172a73e4c5SPawel Dziepak #include "RootInode.h"
182a73e4c5SPawel Dziepak 
192a73e4c5SPawel Dziepak 
202a73e4c5SPawel Dziepak status_t
212a73e4c5SPawel Dziepak Inode::CreateDir(const char* name, int mode)
222a73e4c5SPawel Dziepak {
230dbff361SPawel Dziepak 	return CreateObject(name, NULL, mode, NF4DIR);
242a73e4c5SPawel Dziepak }
252a73e4c5SPawel Dziepak 
262a73e4c5SPawel Dziepak 
272a73e4c5SPawel Dziepak status_t
282a73e4c5SPawel Dziepak Inode::OpenDir(OpenDirCookie* cookie)
292a73e4c5SPawel Dziepak {
302a73e4c5SPawel Dziepak 	if (fType != NF4DIR)
312a73e4c5SPawel Dziepak 		return B_NOT_A_DIRECTORY;
322a73e4c5SPawel Dziepak 
330dbff361SPawel Dziepak 	status_t result = Access(R_OK);
342a73e4c5SPawel Dziepak 	if (result != B_OK)
352a73e4c5SPawel Dziepak 		return result;
362a73e4c5SPawel Dziepak 
3700a8558cSPawel Dziepak 	cookie->fFileSystem = fFileSystem;
386b9a91ebSPawel Dziepak 	cookie->fSpecial = 0;
3909dbdd36SPawel Dziepak 	cookie->fSnapshot = NULL;
4009dbdd36SPawel Dziepak 	cookie->fCurrent = NULL;
4109dbdd36SPawel Dziepak 	cookie->fEOF = false;
42f7c35cf4SPawel Dziepak 	cookie->fAttrDir = false;
43f7c35cf4SPawel Dziepak 
44f7c35cf4SPawel Dziepak 	return B_OK;
45f7c35cf4SPawel Dziepak }
46f7c35cf4SPawel Dziepak 
47f7c35cf4SPawel Dziepak 
48f7c35cf4SPawel Dziepak status_t
49f7c35cf4SPawel Dziepak Inode::OpenAttrDir(OpenDirCookie* cookie)
50f7c35cf4SPawel Dziepak {
51f7c35cf4SPawel Dziepak 	cookie->fFileSystem = fFileSystem;
52f7c35cf4SPawel Dziepak 	cookie->fSpecial = 0;
53f7c35cf4SPawel Dziepak 	cookie->fSnapshot = NULL;
54f7c35cf4SPawel Dziepak 	cookie->fCurrent = NULL;
55f7c35cf4SPawel Dziepak 	cookie->fEOF = false;
56f7c35cf4SPawel Dziepak 	cookie->fAttrDir = true;
57f7c35cf4SPawel Dziepak 
5820d1b02eSPawel Dziepak 	return LoadAttrDirHandle();
5920d1b02eSPawel Dziepak }
6020d1b02eSPawel Dziepak 
6120d1b02eSPawel Dziepak 
6220d1b02eSPawel Dziepak status_t
6320d1b02eSPawel Dziepak Inode::LoadAttrDirHandle()
6420d1b02eSPawel Dziepak {
65*a5ae9b47SPawel Dziepak 	if (!fFileSystem->NamedAttrs())
66*a5ae9b47SPawel Dziepak 		return B_UNSUPPORTED;
67*a5ae9b47SPawel Dziepak 
68f7c35cf4SPawel Dziepak 	if (fInfo.fAttrDir.fSize == 0) {
69f7c35cf4SPawel Dziepak 		FileHandle handle;
70f7c35cf4SPawel Dziepak 
71f7c35cf4SPawel Dziepak 		status_t result = NFS4Inode::OpenAttrDir(&handle);
72*a5ae9b47SPawel Dziepak 		if (result == B_UNSUPPORTED)
73*a5ae9b47SPawel Dziepak 			fFileSystem->SetNamedAttrs(false);
74*a5ae9b47SPawel Dziepak 
75f7c35cf4SPawel Dziepak 		if (result != B_OK)
76f7c35cf4SPawel Dziepak 			return result;
77f7c35cf4SPawel Dziepak 
78f7c35cf4SPawel Dziepak 		fInfo.fAttrDir = handle;
79f7c35cf4SPawel Dziepak 	}
802a73e4c5SPawel Dziepak 
812a73e4c5SPawel Dziepak 	return B_OK;
822a73e4c5SPawel Dziepak }
832a73e4c5SPawel Dziepak 
842a73e4c5SPawel Dziepak 
852a73e4c5SPawel Dziepak status_t
860dbff361SPawel Dziepak Inode::FillDirEntry(struct dirent* de, ino_t id, const char* name, uint32 pos,
872a73e4c5SPawel Dziepak 	uint32 size)
882a73e4c5SPawel Dziepak {
892a73e4c5SPawel Dziepak 	uint32 nameSize = strlen(name);
902a73e4c5SPawel Dziepak 	const uint32 entSize = sizeof(struct dirent);
912a73e4c5SPawel Dziepak 
922a73e4c5SPawel Dziepak 	if (pos + entSize + nameSize > size)
932a73e4c5SPawel Dziepak 		return B_BUFFER_OVERFLOW;
942a73e4c5SPawel Dziepak 
9500a8558cSPawel Dziepak 	de->d_dev = fFileSystem->DevId();
962a73e4c5SPawel Dziepak 	de->d_ino = id;
972a73e4c5SPawel Dziepak 	de->d_reclen = entSize + nameSize;
982a73e4c5SPawel Dziepak 	if (de->d_reclen % 8 != 0)
992a73e4c5SPawel Dziepak 		de->d_reclen += 8 - de->d_reclen % 8;
1002a73e4c5SPawel Dziepak 
1012a73e4c5SPawel Dziepak 	strcpy(de->d_name, name);
1022a73e4c5SPawel Dziepak 
1032a73e4c5SPawel Dziepak 	return B_OK;
1042a73e4c5SPawel Dziepak }
1052a73e4c5SPawel Dziepak 
1062a73e4c5SPawel Dziepak 
1072a73e4c5SPawel Dziepak status_t
1080dbff361SPawel Dziepak Inode::ReadDirUp(struct dirent* de, uint32 pos, uint32 size)
1092a73e4c5SPawel Dziepak {
1102a73e4c5SPawel Dziepak 	do {
11100a8558cSPawel Dziepak 		RPC::Server* serv = fFileSystem->Server();
1122a73e4c5SPawel Dziepak 		Request request(serv);
1132a73e4c5SPawel Dziepak 		RequestBuilder& req = request.Builder();
1142a73e4c5SPawel Dziepak 
115a28e8732SPawel Dziepak 		req.PutFH(fInfo.fHandle);
1162a73e4c5SPawel Dziepak 		req.LookUpUp();
1172a73e4c5SPawel Dziepak 		req.GetFH();
1182a73e4c5SPawel Dziepak 
11900a8558cSPawel Dziepak 		if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) {
1202a73e4c5SPawel Dziepak 			Attribute attr[] = { FATTR4_FILEID };
1212a73e4c5SPawel Dziepak 			req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
1222a73e4c5SPawel Dziepak 		}
1232a73e4c5SPawel Dziepak 
1242a73e4c5SPawel Dziepak 		status_t result = request.Send();
1252a73e4c5SPawel Dziepak 		if (result != B_OK)
1262a73e4c5SPawel Dziepak 			return result;
1272a73e4c5SPawel Dziepak 
1282a73e4c5SPawel Dziepak 		ReplyInterpreter& reply = request.Reply();
1292a73e4c5SPawel Dziepak 
1300dbff361SPawel Dziepak 		if (HandleErrors(reply.NFS4Error(), serv))
1312a73e4c5SPawel Dziepak 			continue;
1322a73e4c5SPawel Dziepak 
1332a73e4c5SPawel Dziepak 		reply.PutFH();
1342a73e4c5SPawel Dziepak 		result = reply.LookUpUp();
1352a73e4c5SPawel Dziepak 		if (result != B_OK)
1362a73e4c5SPawel Dziepak 			return result;
1372a73e4c5SPawel Dziepak 
13800a8558cSPawel Dziepak 		FileHandle fh;
1392a73e4c5SPawel Dziepak 		reply.GetFH(&fh);
1402a73e4c5SPawel Dziepak 
1412a73e4c5SPawel Dziepak 		uint64 fileId;
14200a8558cSPawel Dziepak 		if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) {
1432a73e4c5SPawel Dziepak 			AttrValue* values;
1442a73e4c5SPawel Dziepak 			uint32 count;
1452a73e4c5SPawel Dziepak 			reply.GetAttr(&values, &count);
1462a73e4c5SPawel Dziepak 			if (result != B_OK)
1472a73e4c5SPawel Dziepak 				return result;
1482a73e4c5SPawel Dziepak 
1492a73e4c5SPawel Dziepak 			fileId = values[0].fData.fValue64;
1502a73e4c5SPawel Dziepak 			delete[] values;
1512a73e4c5SPawel Dziepak 		} else
15200a8558cSPawel Dziepak 			fileId = fFileSystem->AllocFileId();
1532a73e4c5SPawel Dziepak 
1540dbff361SPawel Dziepak 		return FillDirEntry(de, FileIdToInoT(fileId), "..", pos, size);
1552a73e4c5SPawel Dziepak 	} while (true);
1562a73e4c5SPawel Dziepak }
1572a73e4c5SPawel Dziepak 
15809dbdd36SPawel Dziepak 
1592a73e4c5SPawel Dziepak status_t
1600dbff361SPawel Dziepak Inode::GetDirSnapshot(DirectoryCacheSnapshot** _snapshot,
1612314d073SPawel Dziepak 	OpenDirCookie* cookie, uint64* _change, bool attribute)
1622a73e4c5SPawel Dziepak {
16309dbdd36SPawel Dziepak 	DirectoryCacheSnapshot* snapshot = new DirectoryCacheSnapshot;
16409dbdd36SPawel Dziepak 	if (snapshot == NULL)
16509dbdd36SPawel Dziepak 		return B_NO_MEMORY;
16609dbdd36SPawel Dziepak 
16709dbdd36SPawel Dziepak 	uint64 change = 0;
16809dbdd36SPawel Dziepak 	uint64 dirCookie = 0;
16909dbdd36SPawel Dziepak 	uint64 dirCookieVerf = 0;
1702a73e4c5SPawel Dziepak 	bool eof = false;
1712a73e4c5SPawel Dziepak 
17209dbdd36SPawel Dziepak 	while (!eof) {
17309dbdd36SPawel Dziepak 		uint32 count;
17409dbdd36SPawel Dziepak 		DirEntry* dirents;
1752a73e4c5SPawel Dziepak 
1760dbff361SPawel Dziepak 		status_t result = ReadDirOnce(&dirents, &count, cookie, &eof, &change,
1772314d073SPawel Dziepak 			&dirCookie, &dirCookieVerf, attribute);
17809dbdd36SPawel Dziepak 		if (result != B_OK) {
17909dbdd36SPawel Dziepak 			delete snapshot;
18009dbdd36SPawel Dziepak 			return result;
18109dbdd36SPawel Dziepak 		}
18209dbdd36SPawel Dziepak 
18309dbdd36SPawel Dziepak 		uint32 i;
18409dbdd36SPawel Dziepak 		for (i = 0; i < count; i++) {
18509dbdd36SPawel Dziepak 
18609dbdd36SPawel Dziepak 			// FATTR4_FSID is mandatory
18709dbdd36SPawel Dziepak 			void* data = dirents[i].fAttrs[0].fData.fPointer;
18809dbdd36SPawel Dziepak 			FileSystemId* fsid = reinterpret_cast<FileSystemId*>(data);
18909dbdd36SPawel Dziepak 			if (*fsid != fFileSystem->FsId())
19009dbdd36SPawel Dziepak 				continue;
19109dbdd36SPawel Dziepak 
19209dbdd36SPawel Dziepak 			ino_t id;
1932314d073SPawel Dziepak 			if (!attribute) {
19409dbdd36SPawel Dziepak 				if (dirents[i].fAttrCount == 2)
1950dbff361SPawel Dziepak 					id = FileIdToInoT(dirents[i].fAttrs[1].fData.fValue64);
19609dbdd36SPawel Dziepak 				else
1970dbff361SPawel Dziepak 					id = FileIdToInoT(fFileSystem->AllocFileId());
198f7c35cf4SPawel Dziepak 			} else
199f7c35cf4SPawel Dziepak 				id = 0;
20009dbdd36SPawel Dziepak 
20109dbdd36SPawel Dziepak 			NameCacheEntry* entry = new NameCacheEntry(dirents[i].fName, id);
20209dbdd36SPawel Dziepak 			if (entry == NULL || entry->fName == NULL) {
20309dbdd36SPawel Dziepak 				if (entry->fName == NULL)
20409dbdd36SPawel Dziepak 					delete entry;
20509dbdd36SPawel Dziepak 				delete snapshot;
20609dbdd36SPawel Dziepak 				delete[] dirents;
20709dbdd36SPawel Dziepak 				return B_NO_MEMORY;
20809dbdd36SPawel Dziepak 			}
20909dbdd36SPawel Dziepak 			snapshot->fEntries.Add(entry);
21009dbdd36SPawel Dziepak 		}
21109dbdd36SPawel Dziepak 
21209dbdd36SPawel Dziepak 		delete[] dirents;
21309dbdd36SPawel Dziepak 	}
21409dbdd36SPawel Dziepak 
21509dbdd36SPawel Dziepak 	*_snapshot = snapshot;
21609dbdd36SPawel Dziepak 	*_change = change;
21709dbdd36SPawel Dziepak 
21809dbdd36SPawel Dziepak 	return B_OK;
21909dbdd36SPawel Dziepak }
22009dbdd36SPawel Dziepak 
2212a73e4c5SPawel Dziepak 
22209dbdd36SPawel Dziepak status_t
22309dbdd36SPawel Dziepak Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
22409dbdd36SPawel Dziepak 	OpenDirCookie* cookie)
22509dbdd36SPawel Dziepak {
22609dbdd36SPawel Dziepak 	if (cookie->fEOF) {
22709dbdd36SPawel Dziepak 		*_count = 0;
22809dbdd36SPawel Dziepak 		return B_OK;
22909dbdd36SPawel Dziepak 	}
2302a73e4c5SPawel Dziepak 
23109dbdd36SPawel Dziepak 	status_t result;
232f7c35cf4SPawel Dziepak 	DirectoryCache* cache = cookie->fAttrDir ? fAttrCache : fCache;
23309dbdd36SPawel Dziepak 	if (cookie->fSnapshot == NULL) {
23409dbdd36SPawel Dziepak 		fFileSystem->Revalidator().Lock();
235f7c35cf4SPawel Dziepak 		if (cache->Lock() != B_OK) {
236f7c35cf4SPawel Dziepak 			cache->ResetAndLock();
23709dbdd36SPawel Dziepak 		} else {
238f7c35cf4SPawel Dziepak 			fFileSystem->Revalidator().RemoveDirectory(cache);
23909dbdd36SPawel Dziepak 		}
24009dbdd36SPawel Dziepak 
241f7c35cf4SPawel Dziepak 		cookie->fSnapshot = cache->GetSnapshot();
24209dbdd36SPawel Dziepak 		if (cookie->fSnapshot == NULL) {
24309dbdd36SPawel Dziepak 			uint64 change;
2442314d073SPawel Dziepak 			result = GetDirSnapshot(&cookie->fSnapshot, cookie, &change,
2452314d073SPawel Dziepak 				cookie->fAttrDir);
24609dbdd36SPawel Dziepak 			if (result != B_OK) {
247f7c35cf4SPawel Dziepak 				cache->Unlock();
24809dbdd36SPawel Dziepak 				fFileSystem->Revalidator().Unlock();
2492a73e4c5SPawel Dziepak 				return result;
25009dbdd36SPawel Dziepak 			}
251f7c35cf4SPawel Dziepak 			cache->ValidateChangeInfo(change);
252f7c35cf4SPawel Dziepak 			cache->SetSnapshot(cookie->fSnapshot);
25309dbdd36SPawel Dziepak 		}
25409dbdd36SPawel Dziepak 		cookie->fSnapshot->AcquireReference();
255f7c35cf4SPawel Dziepak 		fFileSystem->Revalidator().AddDirectory(cache);
256f7c35cf4SPawel Dziepak 		cache->Unlock();
25709dbdd36SPawel Dziepak 		fFileSystem->Revalidator().Unlock();
25809dbdd36SPawel Dziepak 	}
2592a73e4c5SPawel Dziepak 
26009dbdd36SPawel Dziepak 	char* buffer = reinterpret_cast<char*>(_buffer);
26109dbdd36SPawel Dziepak 	uint32 pos = 0;
2626b9a91ebSPawel Dziepak 	uint32 i = 0;
2636b9a91ebSPawel Dziepak 	bool overflow = false;
2646b9a91ebSPawel Dziepak 
265f7c35cf4SPawel Dziepak 	if (cookie->fSpecial == 0 && i < *_count && !cookie->fAttrDir) {
2666b9a91ebSPawel Dziepak 		struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
2676b9a91ebSPawel Dziepak 
2686b9a91ebSPawel Dziepak 		status_t result;
2690dbff361SPawel Dziepak 		result = FillDirEntry(de, fInfo.fFileId, ".", pos, size);
2706b9a91ebSPawel Dziepak 
2716b9a91ebSPawel Dziepak 		if (result == B_BUFFER_OVERFLOW)
2726b9a91ebSPawel Dziepak 			overflow = true;
2736b9a91ebSPawel Dziepak 		else if (result == B_OK) {
2746b9a91ebSPawel Dziepak 			pos += de->d_reclen;
2756b9a91ebSPawel Dziepak 			i++;
2766b9a91ebSPawel Dziepak 			cookie->fSpecial++;
2776b9a91ebSPawel Dziepak 		} else
2786b9a91ebSPawel Dziepak 			return result;
2796b9a91ebSPawel Dziepak 	}
2806b9a91ebSPawel Dziepak 
281f7c35cf4SPawel Dziepak 	if (cookie->fSpecial == 1 && i < *_count && !cookie->fAttrDir) {
2826b9a91ebSPawel Dziepak 		struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
2836b9a91ebSPawel Dziepak 
2846b9a91ebSPawel Dziepak 		status_t result;
2850dbff361SPawel Dziepak 		result = ReadDirUp(de, pos, size);
2868afd81baSPawel Dziepak 		if (result == B_ENTRY_NOT_FOUND) {
2870dbff361SPawel Dziepak 			result = FillDirEntry(de, FileIdToInoT(fInfo.fFileId), "..", pos,
2880dbff361SPawel Dziepak 				size);
2890dbff361SPawel Dziepak 		}
2906b9a91ebSPawel Dziepak 
2916b9a91ebSPawel Dziepak 		if (result == B_BUFFER_OVERFLOW)
2926b9a91ebSPawel Dziepak 			overflow = true;
2936b9a91ebSPawel Dziepak 		else if (result == B_OK) {
2946b9a91ebSPawel Dziepak 			pos += de->d_reclen;
2956b9a91ebSPawel Dziepak 			i++;
2966b9a91ebSPawel Dziepak 			cookie->fSpecial++;
2976b9a91ebSPawel Dziepak 		} else
2986b9a91ebSPawel Dziepak 			return result;
2996b9a91ebSPawel Dziepak 	}
30009dbdd36SPawel Dziepak 
30109dbdd36SPawel Dziepak 	MutexLocker _(cookie->fSnapshot->fLock);
3026b9a91ebSPawel Dziepak 	for (; !overflow && i < *_count; i++) {
3032a73e4c5SPawel Dziepak 		struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
3042a73e4c5SPawel Dziepak 
30509dbdd36SPawel Dziepak 		if (cookie->fCurrent == NULL)
30609dbdd36SPawel Dziepak 			cookie->fCurrent = cookie->fSnapshot->fEntries.Head();
30709dbdd36SPawel Dziepak 		else {
30809dbdd36SPawel Dziepak 			cookie->fCurrent
30909dbdd36SPawel Dziepak 				= cookie->fSnapshot->fEntries.GetNext(cookie->fCurrent);
31009dbdd36SPawel Dziepak 		}
3112a73e4c5SPawel Dziepak 
31209dbdd36SPawel Dziepak 		if (cookie->fCurrent == NULL) {
31309dbdd36SPawel Dziepak 			cookie->fEOF = true;
31409dbdd36SPawel Dziepak 			break;
31509dbdd36SPawel Dziepak 		}
3162a73e4c5SPawel Dziepak 
3170dbff361SPawel Dziepak 		if (FillDirEntry(de, cookie->fCurrent->fNode, cookie->fCurrent->fName,
31809dbdd36SPawel Dziepak 			pos, size) == B_BUFFER_OVERFLOW) {
3192a73e4c5SPawel Dziepak 			overflow = true;
3202a73e4c5SPawel Dziepak 			break;
3212a73e4c5SPawel Dziepak 		}
3222a73e4c5SPawel Dziepak 
3232a73e4c5SPawel Dziepak 		pos += de->d_reclen;
3242a73e4c5SPawel Dziepak 	}
3252a73e4c5SPawel Dziepak 
32609dbdd36SPawel Dziepak 	if (i == 0 && overflow)
3272a73e4c5SPawel Dziepak 		return B_BUFFER_OVERFLOW;
3282a73e4c5SPawel Dziepak 
32909dbdd36SPawel Dziepak 	*_count = i;
3302a73e4c5SPawel Dziepak 
3312a73e4c5SPawel Dziepak 	return B_OK;
3322a73e4c5SPawel Dziepak }
3332a73e4c5SPawel Dziepak 
334