xref: /haiku/src/add-ons/kernel/file_systems/nfs4/InodeDir.cpp (revision 14b32de1d5efe99b4c6d4ef8c25df47eb009cf0f)
1 /*
2  * Copyright 2012 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Paweł Dziepak, pdziepak@quarnos.org
7  */
8 
9 
10 #include "Inode.h"
11 
12 #include <dirent.h>
13 #include <string.h>
14 
15 #include "IdMap.h"
16 #include "Request.h"
17 #include "RootInode.h"
18 
19 
20 status_t
21 Inode::CreateDir(const char* name, int mode, ino_t* id)
22 {
23 	return CreateObject(name, NULL, mode, NF4DIR, id);
24 }
25 
26 
27 status_t
28 Inode::OpenDir(OpenDirCookie* cookie)
29 {
30 	ASSERT(cookie != NULL);
31 
32 	if (fType != NF4DIR)
33 		return B_NOT_A_DIRECTORY;
34 
35 	status_t result = Access(R_OK);
36 	if (result != B_OK)
37 		return result;
38 
39 	cookie->fFileSystem = fFileSystem;
40 	cookie->fSpecial = 0;
41 	cookie->fSnapshot = NULL;
42 	cookie->fCurrent = NULL;
43 	cookie->fEOF = false;
44 	cookie->fAttrDir = false;
45 
46 	return B_OK;
47 }
48 
49 
50 status_t
51 Inode::OpenAttrDir(OpenDirCookie* cookie)
52 {
53 	ASSERT(cookie != NULL);
54 
55 	cookie->fFileSystem = fFileSystem;
56 	cookie->fSpecial = 0;
57 	cookie->fSnapshot = NULL;
58 	cookie->fCurrent = NULL;
59 	cookie->fEOF = false;
60 	cookie->fAttrDir = true;
61 
62 	return LoadAttrDirHandle();
63 }
64 
65 
66 status_t
67 Inode::LoadAttrDirHandle()
68 {
69 	if (fInfo.fAttrDir.fSize != 0)
70 		return B_OK;
71 
72 	FileHandle handle;
73 	status_t result;
74 
75 	if (fFileSystem->NamedAttrs()) {
76 		result = NFS4Inode::OpenAttrDir(&handle);
77 		if (result == B_OK) {
78 			fInfo.fAttrDir = handle;
79 			return B_OK;
80 		}
81 
82 		if (result != B_UNSUPPORTED)
83 			return result;
84 
85 		fFileSystem->SetNamedAttrs(false);
86 	}
87 
88 	if (!fFileSystem->GetConfiguration().fEmulateNamedAttrs)
89 		return B_UNSUPPORTED;
90 
91 	char* attrDir
92 		= reinterpret_cast<char*>(malloc(strlen(Name()) + 32));
93 	if (attrDir == NULL)
94 		return B_NO_MEMORY;
95 	strcpy(attrDir, ".");
96 	strcat(attrDir, Name());
97 	strcat(attrDir, "-haiku-attrs");
98 
99 	result = NFS4Inode::LookUp(attrDir, NULL, NULL, &handle, true);
100 	if (result == B_ENTRY_NOT_FOUND) {
101 		ChangeInfo change;
102 		struct stat st;
103 		Stat(&st);
104 		st.st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
105 		result = NFS4Inode::CreateObject(attrDir, NULL, st.st_mode, NF4DIR,
106 			&change, NULL, &handle, true);
107 	}
108 
109 	free(attrDir);
110 
111 	if (result != B_OK)
112 		return result;
113 
114 	fInfo.fAttrDir = handle;
115 	return B_OK;
116 }
117 
118 
119 status_t
120 Inode::FillDirEntry(struct dirent* de, ino_t id, const char* name, uint32 pos,
121 	uint32 size)
122 {
123 	ASSERT(de != NULL);
124 	ASSERT(name != NULL);
125 
126 	uint32 nameSize = strlen(name) + 1;
127 	const uint32 entSize = sizeof(struct dirent);
128 
129 	if (pos + entSize + nameSize > size)
130 		return B_BUFFER_OVERFLOW;
131 
132 	de->d_dev = fFileSystem->DevId();
133 	de->d_ino = id;
134 	de->d_reclen = entSize + nameSize;
135 	if (de->d_reclen % 8 != 0)
136 		de->d_reclen += 8 - de->d_reclen % 8;
137 
138 	strcpy(de->d_name, name);
139 
140 	return B_OK;
141 }
142 
143 
144 status_t
145 Inode::ReadDirUp(struct dirent* de, uint32 pos, uint32 size)
146 {
147 	ASSERT(de != NULL);
148 
149 	uint32 attempt = 0;
150 	do {
151 		RPC::Server* serv = fFileSystem->Server();
152 		Request request(serv, fFileSystem);
153 		RequestBuilder& req = request.Builder();
154 
155 		req.PutFH(fInfo.fHandle);
156 		req.LookUpUp();
157 		req.GetFH();
158 
159 		if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) {
160 			Attribute attr[] = { FATTR4_FILEID };
161 			req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
162 		}
163 
164 		status_t result = request.Send();
165 		if (result != B_OK)
166 			return result;
167 
168 		ReplyInterpreter& reply = request.Reply();
169 
170 		if (HandleErrors(attempt, reply.NFS4Error(), serv))
171 			continue;
172 
173 		reply.PutFH();
174 		result = reply.LookUpUp();
175 		if (result != B_OK)
176 			return result;
177 
178 		FileHandle fh;
179 		reply.GetFH(&fh);
180 
181 		uint64 fileId;
182 		if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) {
183 			AttrValue* values;
184 			uint32 count;
185 			result = reply.GetAttr(&values, &count);
186 			if (result != B_OK)
187 				return result;
188 
189 			fileId = values[0].fData.fValue64;
190 			delete[] values;
191 		} else
192 			fileId = fFileSystem->AllocFileId();
193 
194 		return FillDirEntry(de, FileIdToInoT(fileId), "..", pos, size);
195 	} while (true);
196 }
197 
198 
199 static char*
200 FileToAttrName(const char* path)
201 {
202 	ASSERT(path != NULL);
203 
204 	char* name = strdup(path);
205 	if (name == NULL)
206 		return NULL;
207 
208 	char* current = strpbrk(name, "#$");
209 	while (current != NULL) {
210 		switch (*current) {
211 			case '#':
212 				*current = '/';
213 				break;
214 			case '$':
215 				*current = ':';
216 				break;
217 		}
218 		current = strpbrk(name, "#$");
219 	}
220 
221 	return name;
222 }
223 
224 
225 status_t
226 Inode::GetDirSnapshot(DirectoryCacheSnapshot** _snapshot,
227 	OpenDirCookie* cookie, uint64* _change, bool attribute)
228 {
229 	ASSERT(_snapshot != NULL);
230 
231 	DirectoryCacheSnapshot* snapshot = new DirectoryCacheSnapshot;
232 	if (snapshot == NULL)
233 		return B_NO_MEMORY;
234 
235 	uint64 change = 0;
236 	uint64 dirCookie = 0;
237 	uint64 dirCookieVerf = 0;
238 	bool eof = false;
239 
240 	while (!eof) {
241 		uint32 count;
242 		DirEntry* dirents;
243 
244 		status_t result = ReadDirOnce(&dirents, &count, cookie, &eof, &change,
245 			&dirCookie, &dirCookieVerf, attribute);
246 		if (result != B_OK) {
247 			delete snapshot;
248 			return result;
249 		}
250 
251 		uint32 i;
252 		for (i = 0; i < count; i++) {
253 
254 			// FATTR4_FSID is mandatory
255 			void* data = dirents[i].fAttrs[0].fData.fPointer;
256 			FileSystemId* fsid = reinterpret_cast<FileSystemId*>(data);
257 			if (*fsid != fFileSystem->FsId())
258 				continue;
259 
260 			if (strstr(dirents[i].fName, "-haiku-attrs") != NULL)
261 				continue;
262 
263 			ino_t id;
264 			if (!attribute) {
265 				if (dirents[i].fAttrCount == 2)
266 					id = FileIdToInoT(dirents[i].fAttrs[1].fData.fValue64);
267 				else
268 					id = FileIdToInoT(fFileSystem->AllocFileId());
269 			} else
270 				id = 0;
271 
272 			const char* name = dirents[i].fName;
273 			if (attribute)
274 				name = FileToAttrName(name);
275 			if (name == NULL) {
276 				delete snapshot;
277 				delete[] dirents;
278 				return B_NO_MEMORY;
279 			}
280 
281 			NameCacheEntry* entry = new NameCacheEntry(name, id);
282 			if (attribute)
283 				free(const_cast<char*>(name));
284 
285 			if (entry == NULL || entry->fName == NULL) {
286 				if (entry != NULL)
287 					delete entry;
288 				delete snapshot;
289 				delete[] dirents;
290 				return B_NO_MEMORY;
291 			}
292 			snapshot->fEntries.Add(entry);
293 		}
294 
295 		delete[] dirents;
296 	}
297 
298 	*_snapshot = snapshot;
299 	*_change = change;
300 
301 	return B_OK;
302 }
303 
304 
305 status_t
306 Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
307 	OpenDirCookie* cookie)
308 {
309 	ASSERT(_buffer != NULL);
310 	ASSERT(_count != NULL);
311 	ASSERT(cookie != NULL);
312 
313 	if (cookie->fEOF) {
314 		*_count = 0;
315 		return B_OK;
316 	}
317 
318 	status_t result;
319 	DirectoryCache* cache = cookie->fAttrDir ? fAttrCache : fCache;
320 	if (cookie->fSnapshot == NULL) {
321 		cache->Lock();
322 		result = cache->Revalidate();
323 		if (result != B_OK) {
324 			cache->Unlock();
325 			return result;
326 		}
327 
328 		DirectoryCacheSnapshot* snapshot;
329 		result = cache->GetSnapshot(&snapshot);
330 		if (result != B_OK) {
331 			cache->Unlock();
332 			return result;
333 		}
334 
335 		cookie->fSnapshot = new DirectoryCacheSnapshot(*snapshot);
336 		cache->Unlock();
337 
338 		if (cookie->fSnapshot == NULL)
339 			return B_NO_MEMORY;
340 	}
341 
342 	char* buffer = reinterpret_cast<char*>(_buffer);
343 	uint32 pos = 0;
344 	uint32 i = 0;
345 	bool overflow = false;
346 
347 	if (cookie->fSpecial == 0 && i < *_count && !cookie->fAttrDir) {
348 		struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
349 
350 		status_t result;
351 		result = FillDirEntry(de, fInfo.fFileId, ".", pos, size);
352 
353 		if (result == B_BUFFER_OVERFLOW)
354 			overflow = true;
355 		else if (result == B_OK) {
356 			pos += de->d_reclen;
357 			i++;
358 			cookie->fSpecial++;
359 		} else
360 			return result;
361 	}
362 
363 	if (cookie->fSpecial == 1 && i < *_count && !cookie->fAttrDir) {
364 		struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
365 
366 		status_t result;
367 		result = ReadDirUp(de, pos, size);
368 		if (result == B_ENTRY_NOT_FOUND) {
369 			result = FillDirEntry(de, FileIdToInoT(fInfo.fFileId), "..", pos,
370 				size);
371 		}
372 
373 		if (result == B_BUFFER_OVERFLOW)
374 			overflow = true;
375 		else if (result == B_OK) {
376 			pos += de->d_reclen;
377 			i++;
378 			cookie->fSpecial++;
379 		} else
380 			return result;
381 	}
382 
383 	MutexLocker _(cookie->fSnapshot->fLock);
384 	for (; !overflow && i < *_count; i++) {
385 		struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
386 		NameCacheEntry* temp = cookie->fCurrent;
387 
388 		if (cookie->fCurrent == NULL)
389 			cookie->fCurrent = cookie->fSnapshot->fEntries.Head();
390 		else {
391 			cookie->fCurrent
392 				= cookie->fSnapshot->fEntries.GetNext(cookie->fCurrent);
393 		}
394 
395 		if (cookie->fCurrent == NULL) {
396 			cookie->fEOF = true;
397 			break;
398 		}
399 
400 		if (FillDirEntry(de, cookie->fCurrent->fNode, cookie->fCurrent->fName,
401 			pos, size) == B_BUFFER_OVERFLOW) {
402 			cookie->fCurrent = temp;
403 			overflow = true;
404 			break;
405 		}
406 
407 		pos += de->d_reclen;
408 	}
409 
410 	if (i == 0 && overflow)
411 		return B_BUFFER_OVERFLOW;
412 
413 	*_count = i;
414 
415 	return B_OK;
416 }
417 
418