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