xref: /haiku/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp (revision 372a66634410cf0450e426716c14ad42d40c0da4)
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 <stdio.h>
11 
12 #include <AutoDeleter.h>
13 #include <fs_cache.h>
14 #include <fs_interface.h>
15 
16 #include "Connection.h"
17 #include "FileSystem.h"
18 #include "IdMap.h"
19 #include "Inode.h"
20 #include "NFS4Defs.h"
21 #include "RequestBuilder.h"
22 #include "ReplyInterpreter.h"
23 #include "RootInode.h"
24 #include "RPCCallbackServer.h"
25 #include "RPCServer.h"
26 #include "VnodeToInode.h"
27 #include "WorkQueue.h"
28 
29 #ifdef DEBUG
30 #define TRACE_NFS4
31 #endif
32 
33 #ifdef TRACE_NFS4
34 static mutex	gTraceLock	= MUTEX_INITIALIZER(NULL);
35 
36 #define TRACE(x...)	\
37 	{	\
38 		mutex_lock(&gTraceLock);	\
39 		dprintf("nfs4: %s(): ", __FUNCTION__);	\
40 		dprintf(x);	\
41 		dprintf("\n");	\
42 		mutex_unlock(&gTraceLock);	\
43 	}
44 #else
45 #define TRACE(x...)	(void)0
46 #endif
47 
48 extern fs_volume_ops gNFSv4VolumeOps;
49 extern fs_vnode_ops gNFSv4VnodeOps;
50 
51 
52 RPC::ServerManager* gRPCServerManager;
53 
54 
55 RPC::ProgramData*
56 CreateNFS4Server(RPC::Server* serv)
57 {
58 	return new NFS4Server(serv);
59 }
60 
61 
62 // Format: ip{4,6}_address:path options
63 // Available options:
64 //	hard		- retry requests until success
65 //	soft		- retry requests no more than retrans times (default)
66 //  timeo=X		- request time limit before next retransmission (default: 60s)
67 //	retrans=X	- retry requests X times (default: 5)
68 //	ac			- use metadata cache (default)
69 //	noac		- do not use metadata cache
70 //	xattr-emu	- emulate named attributes
71 //	noxattr-emu	- do not emulate named attributes (default)
72 //	port=X		- connect to port X (default: 2049)
73 //	proto=X		- user transport protocol X (default: tcp)
74 //	dirtime=X	- attempt revalidate directory cache not more often than each X
75 //				  seconds
76 static status_t
77 ParseArguments(const char* _args, AddressResolver** address, char** _path,
78 	MountConfiguration* conf)
79 {
80 	if (_args == NULL)
81 		return B_BAD_VALUE;
82 
83 	char* args = strdup(_args);
84 	if (args == NULL)
85 		return B_NO_MEMORY;
86 	MemoryDeleter argsDeleter(args);
87 
88 	char* options = strchr(args, ' ');
89 	if (options != NULL)
90 		*options++ = '\0';
91 
92 	char* path = strrchr(args, ':');
93 	if (path == NULL)
94 		return B_MISMATCHED_VALUES;
95 	*path++ = '\0';
96 
97 	*address = new AddressResolver(args);
98 	if (*address == NULL)
99 		return B_NO_MEMORY;
100 
101 	*_path = strdup(path);
102 	if (*_path == NULL) {
103 		delete *address;
104 		return B_NO_MEMORY;
105 	}
106 
107 	conf->fHard = false;
108 	conf->fRetryLimit = 5;
109 	conf->fRequestTimeout = sSecToBigTime(60);
110 	conf->fEmulateNamedAttrs = false;
111 	conf->fCacheMetadata = true;
112 	conf->fDirectoryCacheTime = sSecToBigTime(5);
113 
114 	char* optionsEnd = NULL;
115 	if (options != NULL)
116 		optionsEnd = strchr(options, ' ');
117 	while (options != NULL && *options != '\0') {
118 		if (optionsEnd != NULL)
119 			*optionsEnd++ = '\0';
120 
121 		if (strcmp(options, "hard") == 0)
122 			conf->fHard = true;
123 		else if (strncmp(options, "retrans=", 8) == 0) {
124 			options += strlen("retrans=");
125 			conf->fRetryLimit = atoi(options);
126 		} else if (strncmp(options, "timeo=", 6) == 0) {
127 			options += strlen("timeo=");
128 			conf->fRequestTimeout = atoi(options);
129 		} else if (strcmp(options, "noac") == 0)
130 			conf->fCacheMetadata = false;
131 		else if (strcmp(options, "xattr-emu") == 0)
132 			conf->fEmulateNamedAttrs = true;
133 		else if (strncmp(options, "port=", 5) == 0) {
134 			options += strlen("port=");
135 			(*address)->ForcePort(atoi(options));
136 		} else if (strncmp(options, "proto=", 6) == 0) {
137 			options += strlen("proto=");
138 			(*address)->ForceProtocol(options);
139 		} else if (strncmp(options, "dirtime=", 8) == 0) {
140 			options += strlen("dirtime=");
141 			conf->fDirectoryCacheTime = sSecToBigTime(atoi(options));
142 		}
143 
144 		options = optionsEnd;
145 		if (options != NULL)
146 			optionsEnd = strchr(options, ' ');
147 	}
148 
149 	return B_OK;
150 }
151 
152 
153 static status_t
154 nfs4_mount(fs_volume* volume, const char* device, uint32 flags,
155 			const char* args, ino_t* _rootVnodeID)
156 {
157 	TRACE("volume = %p, device = %s, flags = %" B_PRIu32 ", args = %s", volume,
158 		device, flags, args);
159 
160 	status_t result;
161 
162 	/* prepare idmapper server */
163 	MutexLocker locker(gIdMapperLock);
164 	gIdMapper = new(std::nothrow) IdMap;
165 	if (gIdMapper == NULL)
166 		return B_NO_MEMORY;
167 
168 	result = gIdMapper->InitStatus();
169 	if (result != B_OK) {
170 		delete gIdMapper;
171 		gIdMapper = NULL;
172 		return result;
173 	}
174 	locker.Unlock();
175 
176 	AddressResolver* resolver;
177 	MountConfiguration config;
178 	char* path;
179 	result = ParseArguments(args, &resolver, &path, &config);
180 	if (result != B_OK)
181 		return result;
182 	MemoryDeleter pathDeleter(path);
183 
184 	RPC::Server* server;
185 	result = gRPCServerManager->Acquire(&server, resolver, CreateNFS4Server);
186 	delete resolver;
187 	if (result != B_OK)
188 		return result;
189 
190 	FileSystem* fs;
191 	result = FileSystem::Mount(&fs, server, path, volume->id, config);
192 	if (result != B_OK) {
193 		gRPCServerManager->Release(server);
194 		return result;
195 	}
196 
197 	Inode* inode = fs->Root();
198 	if (inode == NULL) {
199 		delete fs;
200 		gRPCServerManager->Release(server);
201 
202 		return B_IO_ERROR;
203 	}
204 
205 	volume->private_volume = fs;
206 	volume->ops = &gNFSv4VolumeOps;
207 
208 	VnodeToInode* vti = new VnodeToInode(inode->ID(), fs);
209 	if (vti == NULL) {
210 		delete fs;
211 		gRPCServerManager->Release(server);
212 		return B_NO_MEMORY;
213 	}
214 
215 	vti->Replace(inode);
216 	result = publish_vnode(volume, inode->ID(), vti, &gNFSv4VnodeOps,
217 							inode->Type(), 0);
218 	if (result != B_OK)
219 		return result;
220 
221 	*_rootVnodeID = inode->ID();
222 
223 	TRACE("*_rootVnodeID = %" B_PRIi64, inode->ID());
224 
225 	return B_OK;
226 }
227 
228 
229 static status_t
230 nfs4_get_vnode(fs_volume* volume, ino_t id, fs_vnode* vnode, int* _type,
231 	uint32* _flags, bool reenter)
232 {
233 	FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
234 	TRACE("volume = %p, id = %" B_PRIi64, volume, id);
235 
236 	VnodeToInode* vnodeToInode = new VnodeToInode(id, fs);
237 	if (vnodeToInode == NULL)
238 		return B_NO_MEMORY;
239 
240 	Inode* inode;
241 	status_t result = fs->GetInode(id, &inode);
242 	if (result != B_OK) {
243 		delete vnodeToInode;
244 		return result;
245 	}
246 
247 	vnodeToInode->Replace(inode);
248 	vnode->ops = &gNFSv4VnodeOps;
249 	vnode->private_node = vnodeToInode;
250 
251 	*_type = inode->Type();
252 	*_flags = 0;
253 
254 	return B_OK;
255 }
256 
257 
258 static status_t
259 nfs4_unmount(fs_volume* volume)
260 {
261 	TRACE("volume = %p", volume);
262 	FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
263 	RPC::Server* server = fs->Server();
264 
265 	delete fs;
266 	gRPCServerManager->Release(server);
267 
268 	return B_OK;
269 }
270 
271 
272 static status_t
273 nfs4_read_fs_info(fs_volume* volume, struct fs_info* info)
274 {
275 	TRACE("volume = %p", volume);
276 
277 	FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
278 	RootInode* inode = reinterpret_cast<RootInode*>(fs->Root());
279 	return inode->ReadInfo(info);
280 }
281 
282 
283 static status_t
284 nfs4_lookup(fs_volume* volume, fs_vnode* dir, const char* name, ino_t* _id)
285 {
286 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(dir->private_node);
287 
288 	if (!strcmp(name, ".")) {
289 		*_id = vti->ID();
290 		void* ptr;
291 		return get_vnode(volume, *_id, &ptr);
292 	}
293 
294 	VnodeToInodeLocker locker(vti);
295 
296 	Inode* inode = vti->Get();
297 	if (inode == NULL)
298 		return B_ENTRY_NOT_FOUND;
299 
300 	TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s", volume, vti->ID(),
301 		name);
302 
303 	status_t result = inode->LookUp(name, _id);
304 	if (result != B_OK)
305 		return result;
306 	locker.Unlock();
307 
308 	TRACE("*_id = %" B_PRIi64, *_id);
309 
310 	// If VTI holds an outdated Inode next operation performed on it will
311 	// return either ERR_STALE or ERR_FHEXPIRED. Both of these error codes
312 	// will cause FileInfo data to be updated (the former will also cause Inode
313 	// object to be recreated). We are taking an optimistic (an lazy) approach
314 	// here. The following code just ensures VTI won't be removed too soon.
315 	void* ptr;
316 	result = get_vnode(volume, *_id, &ptr);
317 	if (result == B_OK)
318 		unremove_vnode(volume, *_id);
319 
320 	return result;
321 }
322 
323 
324 static status_t
325 nfs4_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
326 {
327 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
328 	TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID());
329 
330 	delete vti;
331 	return B_OK;
332 }
333 
334 
335 static status_t
336 nfs4_remove_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
337 {
338 	FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
339 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
340 	TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID());
341 
342 	if (fs->Root() == vti->GetPointer())
343 		return B_OK;
344 
345 	ASSERT(vti->GetPointer() == NULL);
346 	delete vti;
347 
348 	return B_OK;
349 }
350 
351 
352 static status_t
353 nfs4_read_pages(fs_volume* _volume, fs_vnode* vnode, void* _cookie, off_t pos,
354 	const iovec* vecs, size_t count, size_t* _numBytes)
355 {
356 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
357 	TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \
358 		", count = %lu, numBytes = %lu", _volume, vti->ID(), _cookie, pos,
359 		count, *_numBytes);
360 
361 	VnodeToInodeLocker _(vti);
362 	Inode* inode = vti->Get();
363 	if (inode == NULL)
364 		return B_ENTRY_NOT_FOUND;
365 
366 	OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
367 
368 	status_t result;
369 	size_t totalRead = 0;
370 	bool eof = false;
371 	for (size_t i = 0; i < count && !eof; i++) {
372 		size_t bytesLeft = vecs[i].iov_len;
373 		char* buffer = reinterpret_cast<char*>(vecs[i].iov_base);
374 
375 		do {
376 			size_t bytesRead = bytesLeft;
377 			result = inode->ReadDirect(cookie, pos, buffer, &bytesRead, &eof);
378 			if (result != B_OK)
379 				return result;
380 
381 			totalRead += bytesRead;
382 			pos += bytesRead;
383 			buffer += bytesRead;
384 			bytesLeft -= bytesRead;
385 		} while (bytesLeft > 0 && !eof);
386 	}
387 
388 	*_numBytes = totalRead;
389 
390 	TRACE("*numBytes = %lu", totalRead);
391 
392 	return B_OK;
393 }
394 
395 
396 static status_t
397 nfs4_write_pages(fs_volume* _volume, fs_vnode* vnode, void* _cookie, off_t pos,
398 	const iovec* vecs, size_t count, size_t* _numBytes)
399 {
400 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
401 	TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \
402 		", count = %lu, numBytes = %lu", _volume, vti->ID(), _cookie, pos,
403 		count, *_numBytes);
404 
405 	VnodeToInodeLocker _(vti);
406 	Inode* inode = vti->Get();
407 	if (inode == NULL)
408 		return B_ENTRY_NOT_FOUND;
409 
410 	OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
411 
412 	status_t result;
413 	for (size_t i = 0; i < count; i++) {
414 		uint64 bytesLeft = vecs[i].iov_len;
415 		if (pos + bytesLeft > inode->MaxFileSize())
416 			bytesLeft = inode->MaxFileSize() - pos;
417 
418 		char* buffer = reinterpret_cast<char*>(vecs[i].iov_base);
419 
420 		do {
421 			size_t bytesWritten = bytesLeft;
422 
423 			result = inode->WriteDirect(cookie, pos, buffer, &bytesWritten);
424 			if (result != B_OK)
425 				return result;
426 
427 			bytesLeft -= bytesWritten;
428 			pos += bytesWritten;
429 			buffer += bytesWritten;
430 		} while (bytesLeft > 0);
431 	}
432 
433 	return B_OK;
434 }
435 
436 
437 static status_t
438 nfs4_io(fs_volume* volume, fs_vnode* vnode, void* cookie, io_request* request)
439 {
440 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
441 	TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume, vti->ID(),
442 		cookie);
443 
444 	VnodeToInodeLocker _(vti);
445 	Inode* inode = vti->Get();
446 	if (inode == NULL)
447 		return B_ENTRY_NOT_FOUND;
448 
449 	IORequestArgs* args = new(std::nothrow) IORequestArgs;
450 	if (args == NULL) {
451 		notify_io_request(request, B_NO_MEMORY);
452 		return B_NO_MEMORY;
453 	}
454 	args->fRequest = request;
455 	args->fInode = inode;
456 
457 	status_t result = gWorkQueue->EnqueueJob(IORequest, args);
458 	if (result != B_OK)
459 		notify_io_request(request, result);
460 
461 	return result;
462 }
463 
464 
465 static status_t
466 nfs4_get_file_map(fs_volume* volume, fs_vnode* vnode, off_t _offset,
467 	size_t size, struct file_io_vec* vecs, size_t* _count)
468 {
469 	return B_ERROR;
470 }
471 
472 
473 static status_t
474 nfs4_set_flags(fs_volume* volume, fs_vnode* vnode, void* _cookie, int flags)
475 {
476 	TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, flags = %d", volume,
477 		reinterpret_cast<VnodeToInode*>(vnode->private_node)->ID(), _cookie,
478 		flags);
479 
480 	OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
481 	cookie->fMode = (cookie->fMode & ~(O_APPEND | O_NONBLOCK)) | flags;
482 	return B_OK;
483 }
484 
485 
486 static status_t
487 nfs4_fsync(fs_volume* volume, fs_vnode* vnode)
488 {
489 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
490 	TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID());
491 
492 	VnodeToInodeLocker _(vti);
493 	Inode* inode = vti->Get();
494 	if (inode == NULL)
495 		return B_ENTRY_NOT_FOUND;
496 
497 	return inode->SyncAndCommit();
498 }
499 
500 
501 static status_t
502 nfs4_read_symlink(fs_volume* volume, fs_vnode* link, char* buffer,
503 	size_t* _bufferSize)
504 {
505 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(link->private_node);
506 	TRACE("volume = %p, link = %" B_PRIi64, volume, vti->ID());
507 
508 	VnodeToInodeLocker _(vti);
509 	Inode* inode = vti->Get();
510 	if (inode == NULL)
511 		return B_ENTRY_NOT_FOUND;
512 
513 	return inode->ReadLink(buffer, _bufferSize);
514 }
515 
516 
517 static status_t
518 nfs4_create_symlink(fs_volume* volume, fs_vnode* dir, const char* name,
519 	const char* path, int mode)
520 {
521 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(dir->private_node);
522 	TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s, path = %s, mode = %d",
523 		volume, vti->ID(), name, path, mode);
524 
525 	VnodeToInodeLocker _(vti);
526 	Inode* inode = vti->Get();
527 	if (inode == NULL)
528 		return B_ENTRY_NOT_FOUND;
529 
530 	ino_t id;
531 	status_t result = inode->CreateLink(name, path, mode, &id);
532 	if (result != B_OK)
533 		return result;
534 
535 	result = get_vnode(volume, id, reinterpret_cast<void**>(&vti));
536 	if (result == B_OK) {
537 		unremove_vnode(volume, id);
538 		vti->Clear();
539 		put_vnode(volume, id);
540 	}
541 
542 	return B_OK;
543 }
544 
545 
546 static status_t
547 nfs4_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* vnode)
548 {
549 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
550 	VnodeToInode* dirVti = reinterpret_cast<VnodeToInode*>(dir->private_node);
551 	TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s, vnode = %" B_PRIi64,
552 		volume, dirVti->ID(), name, vti->ID());
553 
554 	VnodeToInodeLocker _dir(dirVti);
555 	Inode* dirInode = dirVti->Get();
556 	if (dirInode == NULL)
557 		return B_ENTRY_NOT_FOUND;
558 
559 
560 	VnodeToInodeLocker _(vti);
561 	Inode* inode = vti->Get();
562 	if (inode == NULL)
563 		return B_ENTRY_NOT_FOUND;
564 
565 	return inode->Link(dirInode, name);
566 }
567 
568 
569 static status_t
570 nfs4_unlink(fs_volume* volume, fs_vnode* dir, const char* name)
571 {
572 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(dir->private_node);
573 
574 	VnodeToInodeLocker locker(vti);
575 	Inode* inode = vti->Get();
576 	if (inode == NULL)
577 		return B_ENTRY_NOT_FOUND;
578 
579 	TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s", volume, vti->ID(),
580 		name);
581 
582 	ino_t id;
583 	status_t result = inode->Remove(name, NF4REG, &id);
584 	if (result != B_OK)
585 		return result;
586 	locker.Unlock();
587 
588 	result = acquire_vnode(volume, id);
589 	if (result == B_OK) {
590 		result = get_vnode(volume, id, reinterpret_cast<void**>(&vti));
591 		ASSERT(result == B_OK);
592 
593 		if (vti->Unlink(inode->fInfo.fNames, name))
594 			remove_vnode(volume, id);
595 
596 		put_vnode(volume, id);
597 		put_vnode(volume, id);
598 	}
599 
600 	return B_OK;
601 }
602 
603 
604 static status_t
605 nfs4_rename(fs_volume* volume, fs_vnode* fromDir, const char* fromName,
606 	fs_vnode* toDir, const char* toName)
607 {
608 	VnodeToInode* fromVti
609 		= reinterpret_cast<VnodeToInode*>(fromDir->private_node);
610 	VnodeToInode* toVti = reinterpret_cast<VnodeToInode*>(toDir->private_node);
611 	TRACE("volume = %p, fromDir = %" B_PRIi64 ", toDir = %" B_PRIi64 ","	\
612 		" fromName = %s, toName = %s", volume, fromVti->ID(), toVti->ID(),	\
613 		fromName, toName);
614 
615 	VnodeToInodeLocker _from(fromVti);
616 	Inode* fromInode = fromVti->Get();
617 	if (fromInode == NULL)
618 		return B_ENTRY_NOT_FOUND;
619 
620 
621 	VnodeToInodeLocker _to(toVti);
622 	Inode* toInode = toVti->Get();
623 	if (toInode == NULL)
624 		return B_ENTRY_NOT_FOUND;
625 
626 	ino_t id;
627 	ino_t oldID;
628 	status_t result = Inode::Rename(fromInode, toInode, fromName, toName, false,
629 		&id, &oldID);
630 	if (result != B_OK)
631 		return result;
632 
633 	VnodeToInode* vti;
634 
635 	if (oldID != 0) {
636 		// we have overriden an inode
637 		result = acquire_vnode(volume, oldID);
638 		if (result == B_OK) {
639 			result = get_vnode(volume, oldID, reinterpret_cast<void**>(&vti));
640 			ASSERT(result == B_OK);
641 			if (vti->Unlink(toInode->fInfo.fNames, toName))
642 				remove_vnode(volume, oldID);
643 
644 			put_vnode(volume, oldID);
645 			put_vnode(volume, oldID);
646 		}
647 	}
648 
649 	result = get_vnode(volume, id, reinterpret_cast<void**>(&vti));
650 	if (result == B_OK) {
651 		Inode* child = vti->Get();
652 		if (child == NULL) {
653 			put_vnode(volume, id);
654 			return B_ENTRY_NOT_FOUND;
655 		}
656 
657 		unremove_vnode(volume, id);
658 		child->fInfo.fNames->RemoveName(fromInode->fInfo.fNames, fromName);
659 		child->fInfo.fNames->AddName(toInode->fInfo.fNames, toName);
660 		put_vnode(volume, id);
661 	}
662 
663 	return B_OK;
664 }
665 
666 
667 static status_t
668 nfs4_access(fs_volume* volume, fs_vnode* vnode, int mode)
669 {
670 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
671 	TRACE("volume = %p, vnode = %" B_PRIi64 ", mode = %d", volume, vti->ID(),
672 		mode);
673 
674 	VnodeToInodeLocker _(vti);
675 	Inode* inode = vti->Get();
676 	if (inode == NULL)
677 		return B_ENTRY_NOT_FOUND;
678 
679 	return inode->Access(mode);
680 }
681 
682 
683 static status_t
684 nfs4_read_stat(fs_volume* volume, fs_vnode* vnode, struct stat* stat)
685 {
686 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
687 	TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID());
688 
689 	VnodeToInodeLocker _(vti);
690 	Inode* inode = vti->Get();
691 	if (inode == NULL)
692 		return B_ENTRY_NOT_FOUND;
693 
694 	status_t result = inode->Stat(stat);
695 	if (inode->GetOpenState() != NULL)
696 		stat->st_size = inode->MaxFileSize();
697 	return result;
698 }
699 
700 
701 static status_t
702 nfs4_write_stat(fs_volume* volume, fs_vnode* vnode, const struct stat* stat,
703 	uint32 statMask)
704 {
705 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
706 	TRACE("volume = %p, vnode = %" B_PRIi64 ", statMask = %" B_PRIu32, volume,
707 		vti->ID(), statMask);
708 
709 	VnodeToInodeLocker _(vti);
710 	Inode* inode = vti->Get();
711 	if (inode == NULL)
712 		return B_ENTRY_NOT_FOUND;
713 
714 	return inode->WriteStat(stat, statMask);
715 }
716 
717 
718 static status_t
719 get_new_vnode(fs_volume* volume, ino_t id, VnodeToInode** _vti)
720 {
721 	FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
722 	Inode* inode;
723 	VnodeToInode* vti;
724 
725 	status_t result = acquire_vnode(volume, id);
726 	if (result == B_OK) {
727 		ASSERT(get_vnode(volume, id, reinterpret_cast<void**>(_vti)) == B_OK);
728 		unremove_vnode(volume, id);
729 
730 		// Release after acquire
731 		put_vnode(volume, id);
732 
733 		vti = *_vti;
734 
735 		if (vti->Get() == NULL) {
736 			result = fs->GetInode(id, &inode);
737 			if (result != B_OK) {
738 				put_vnode(volume, id);
739 				return result;
740 			}
741 
742 			vti->Replace(inode);
743 		}
744 		return B_OK;
745 	}
746 
747 	return get_vnode(volume, id, reinterpret_cast<void**>(_vti));
748 }
749 
750 
751 static status_t
752 nfs4_create(fs_volume* volume, fs_vnode* dir, const char* name, int openMode,
753 	int perms, void** _cookie, ino_t* _newVnodeID)
754 {
755 	FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
756 
757 	OpenFileCookie* cookie = new OpenFileCookie(fs);
758 	if (cookie == NULL)
759 		return B_NO_MEMORY;
760 	*_cookie = cookie;
761 
762 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(dir->private_node);
763 	TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s, openMode = %d,"	\
764 		" perms = %d", volume, vti->ID(), name, openMode, perms);
765 
766 	VnodeToInodeLocker _(vti);
767 	Inode* inode = vti->Get();
768 	if (inode == NULL)
769 		return B_ENTRY_NOT_FOUND;
770 
771 	MutexLocker createLocker(fs->CreateFileLock());
772 
773 	OpenDelegationData data;
774 	status_t result = inode->Create(name, openMode, perms, cookie, &data,
775 		_newVnodeID);
776 	if (result != B_OK) {
777 		delete cookie;
778 		return result;
779 	}
780 
781 	result = get_new_vnode(volume, *_newVnodeID, &vti);
782 	if (result != B_OK) {
783 		delete cookie;
784 		return result;
785 	}
786 
787 	VnodeToInodeLocker _child(vti);
788 	Inode* child = vti->Get();
789 	if (child == NULL) {
790 		delete cookie;
791 		put_vnode(volume, *_newVnodeID);
792 		return B_ENTRY_NOT_FOUND;
793 	}
794 
795 	child->SetOpenState(cookie->fOpenState);
796 
797 	if (data.fType != OPEN_DELEGATE_NONE) {
798 		Delegation* delegation
799 			= new(std::nothrow) Delegation(data, child,
800 				cookie->fOpenState->fClientID);
801 		if (delegation != NULL) {
802 			delegation->fInfo = cookie->fOpenState->fInfo;
803 			delegation->fFileSystem = child->GetFileSystem();
804 			child->SetDelegation(delegation);
805 		}
806 	}
807 
808 	TRACE("*cookie = %p, *newVnodeID = %" B_PRIi64, *_cookie, *_newVnodeID);
809 	return result;
810 }
811 
812 
813 static status_t
814 nfs4_open(fs_volume* volume, fs_vnode* vnode, int openMode, void** _cookie)
815 {
816 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
817 	TRACE("volume = %p, vnode = %" B_PRIi64 ", openMode = %d", volume,
818 		vti->ID(), openMode);
819 
820 	VnodeToInodeLocker _(vti);
821 	Inode* inode = vti->Get();
822 	if (inode == NULL)
823 		return B_ENTRY_NOT_FOUND;
824 
825 	if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK) {
826 		*_cookie = NULL;
827 		return B_OK;
828 	}
829 
830 	FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
831 	OpenFileCookie* cookie = new OpenFileCookie(fs);
832 	if (cookie == NULL)
833 		return B_NO_MEMORY;
834 	*_cookie = cookie;
835 
836 	status_t result = inode->Open(openMode, cookie);
837 	if (result != B_OK)
838 		delete cookie;
839 
840 	TRACE("*cookie = %p", *_cookie);
841 
842 	return result;
843 }
844 
845 
846 static status_t
847 nfs4_close(fs_volume* volume, fs_vnode* vnode, void* _cookie)
848 {
849 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
850 
851 	TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume, vti->ID(),
852 		_cookie);
853 
854 	VnodeToInodeLocker _(vti);
855 	Inode* inode = vti->Get();
856 	if (inode == NULL)
857 		return B_ENTRY_NOT_FOUND;
858 
859 
860 	if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK)
861 		return B_OK;
862 
863 	Cookie* cookie = reinterpret_cast<Cookie*>(_cookie);
864 	return cookie->CancelAll();
865 }
866 
867 
868 static status_t
869 nfs4_free_cookie(fs_volume* volume, fs_vnode* vnode, void* _cookie)
870 {
871 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
872 
873 	TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume, vti->ID(),
874 		_cookie);
875 
876 	VnodeToInodeLocker _(vti);
877 	Inode* inode = vti->Get();
878 	if (inode == NULL)
879 		return B_ENTRY_NOT_FOUND;
880 
881 	if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK)
882 		return B_OK;
883 
884 	OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
885 
886 	inode->Close(cookie);
887 	delete cookie;
888 
889 	return B_OK;
890 }
891 
892 
893 static status_t
894 nfs4_read(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos,
895 	void* buffer, size_t* length)
896 {
897 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
898 	TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \
899 		", length = %lu", volume, vti->ID(), _cookie, pos, *length);
900 
901 	VnodeToInodeLocker _(vti);
902 	Inode* inode = vti->Get();
903 	if (inode == NULL)
904 		return B_ENTRY_NOT_FOUND;
905 
906 	if (inode->Type() == S_IFDIR)
907 		return B_IS_A_DIRECTORY;
908 
909 	if (inode->Type() == S_IFLNK)
910 		return B_BAD_VALUE;
911 
912 	OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
913 
914 	return inode->Read(cookie, pos, buffer, length);;
915 }
916 
917 
918 static status_t
919 nfs4_write(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos,
920 	const void* _buffer, size_t* length)
921 {
922 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
923 	TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \
924 		", length = %lu", volume, vti->ID(), _cookie, pos, *length);
925 
926 	VnodeToInodeLocker _(vti);
927 	Inode* inode = vti->Get();
928 	if (inode == NULL)
929 		return B_ENTRY_NOT_FOUND;
930 
931 	if (inode->Type() == S_IFDIR)
932 		return B_IS_A_DIRECTORY;
933 
934 	if (inode->Type() == S_IFLNK)
935 		return B_BAD_VALUE;
936 
937 	OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
938 
939 	return inode->Write(cookie, pos, _buffer, length);
940 }
941 
942 
943 static status_t
944 nfs4_create_dir(fs_volume* volume, fs_vnode* parent, const char* name,
945 	int mode)
946 {
947 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(parent->private_node);
948 	TRACE("volume = %p, parent = %" B_PRIi64 ", mode = %d", volume, vti->ID(),
949 		mode);
950 
951 	VnodeToInodeLocker _(vti);
952 	Inode* inode = vti->Get();
953 	if (inode == NULL)
954 		return B_ENTRY_NOT_FOUND;
955 
956 	ino_t id;
957 	status_t result = inode->CreateDir(name, mode, &id);
958 	if (result != B_OK)
959 		return result;
960 
961 	result = get_vnode(volume, id, reinterpret_cast<void**>(&vti));
962 	if (result == B_OK) {
963 		unremove_vnode(volume, id);
964 		vti->Clear();
965 		put_vnode(volume, id);
966 	}
967 
968 	return B_OK;
969 }
970 
971 
972 static status_t
973 nfs4_remove_dir(fs_volume* volume, fs_vnode* parent, const char* name)
974 {
975 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(parent->private_node);
976 	TRACE("volume = %p, parent = %" B_PRIi64 ", name = %s", volume, vti->ID(),
977 		name);
978 
979 	VnodeToInodeLocker _(vti);
980 	Inode* inode = vti->Get();
981 	if (inode == NULL)
982 		return B_ENTRY_NOT_FOUND;
983 
984 	ino_t id;
985 	status_t result = inode->Remove(name, NF4DIR, &id);
986 	if (result != B_OK)
987 		return result;
988 
989 	result = acquire_vnode(volume, id);
990 	if (result == B_OK) {
991 		result = get_vnode(volume, id, reinterpret_cast<void**>(&vti));
992 		ASSERT(result == B_OK);
993 
994 		if (vti->Unlink(inode->fInfo.fNames, name))
995 			remove_vnode(volume, id);
996 
997 		put_vnode(volume, id);
998 		put_vnode(volume, id);
999 	}
1000 
1001 	return B_OK;
1002 }
1003 
1004 
1005 static status_t
1006 nfs4_open_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie)
1007 {
1008 	FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
1009 	OpenDirCookie* cookie = new(std::nothrow) OpenDirCookie(fs);
1010 	if (cookie == NULL)
1011 		return B_NO_MEMORY;
1012 	*_cookie = cookie;
1013 
1014 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1015 	TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID());
1016 
1017 	VnodeToInodeLocker _(vti);
1018 	Inode* inode = vti->Get();
1019 	if (inode == NULL)
1020 		return B_ENTRY_NOT_FOUND;
1021 
1022 	status_t result = inode->OpenDir(cookie);
1023 	if (result != B_OK)
1024 		delete cookie;
1025 
1026 	TRACE("*cookie = %p", *_cookie);
1027 
1028 	return result;
1029 }
1030 
1031 
1032 static status_t
1033 nfs4_close_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie)
1034 {
1035 	TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume,
1036 		reinterpret_cast<VnodeToInode*>(vnode->private_node)->ID(), _cookie);
1037 
1038 	Cookie* cookie = reinterpret_cast<Cookie*>(_cookie);
1039 	return cookie->CancelAll();
1040 }
1041 
1042 
1043 static status_t
1044 nfs4_free_dir_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie)
1045 {
1046 	TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume,
1047 		reinterpret_cast<VnodeToInode*>(vnode->private_node)->ID(), cookie);
1048 
1049 	delete reinterpret_cast<OpenDirCookie*>(cookie);
1050 	return B_OK;
1051 }
1052 
1053 
1054 static status_t
1055 nfs4_read_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie,
1056 				struct dirent* buffer, size_t bufferSize, uint32* _num)
1057 {
1058 	OpenDirCookie* cookie = reinterpret_cast<OpenDirCookie*>(_cookie);
1059 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1060 	TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume, vti->ID(),
1061 		_cookie);
1062 
1063 	VnodeToInodeLocker _(vti);
1064 	Inode* inode = vti->Get();
1065 	if (inode == NULL)
1066 		return B_ENTRY_NOT_FOUND;
1067 
1068 	return inode->ReadDir(buffer, bufferSize, _num, cookie);
1069 }
1070 
1071 
1072 static status_t
1073 nfs4_rewind_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie)
1074 {
1075 	TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume,
1076 		reinterpret_cast<VnodeToInode*>(vnode->private_node)->ID(), _cookie);
1077 
1078 	OpenDirCookie* cookie = reinterpret_cast<OpenDirCookie*>(_cookie);
1079 	cookie->fSpecial = 0;
1080 	if (cookie->fSnapshot != NULL)
1081 		cookie->fSnapshot->ReleaseReference();
1082 	cookie->fSnapshot = NULL;
1083 	cookie->fCurrent = NULL;
1084 	cookie->fEOF = false;
1085 
1086 	return B_OK;
1087 }
1088 
1089 
1090 static status_t
1091 nfs4_open_attr_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie)
1092 {
1093 	FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
1094 	OpenDirCookie* cookie = new(std::nothrow) OpenDirCookie(fs);
1095 	if (cookie == NULL)
1096 		return B_NO_MEMORY;
1097 	*_cookie = cookie;
1098 
1099 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1100 	TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID());
1101 
1102 	VnodeToInodeLocker _(vti);
1103 	Inode* inode = vti->Get();
1104 	if (inode == NULL)
1105 		return B_ENTRY_NOT_FOUND;
1106 
1107 	status_t result = inode->OpenAttrDir(cookie);
1108 	if (result != B_OK)
1109 		delete cookie;
1110 
1111 	return result;
1112 }
1113 
1114 
1115 static status_t
1116 nfs4_close_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie)
1117 {
1118 	return nfs4_close_dir(volume, vnode, cookie);
1119 }
1120 
1121 
1122 static status_t
1123 nfs4_free_attr_dir_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie)
1124 {
1125 	return nfs4_free_dir_cookie(volume, vnode, cookie);
1126 }
1127 
1128 
1129 static status_t
1130 nfs4_read_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie,
1131 	struct dirent* buffer, size_t bufferSize, uint32* _num)
1132 {
1133 	return nfs4_read_dir(volume, vnode, cookie, buffer, bufferSize, _num);
1134 }
1135 
1136 
1137 static status_t
1138 nfs4_rewind_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie)
1139 {
1140 	return nfs4_rewind_dir(volume, vnode, cookie);
1141 }
1142 
1143 
1144 static status_t
1145 nfs4_create_attr(fs_volume* volume, fs_vnode* vnode, const char* name,
1146 	uint32 type, int openMode, void** _cookie)
1147 {
1148 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1149 
1150 	VnodeToInodeLocker _(vti);
1151 	Inode* inode = vti->Get();
1152 	if (inode == NULL)
1153 		return B_ENTRY_NOT_FOUND;
1154 
1155 	FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
1156 	OpenAttrCookie* cookie = new OpenAttrCookie(fs);
1157 	if (cookie == NULL)
1158 		return B_NO_MEMORY;
1159 	*_cookie = cookie;
1160 
1161 	status_t result = inode->OpenAttr(name, openMode, cookie, true, type);
1162 	if (result != B_OK)
1163 		delete cookie;
1164 
1165 	return result;
1166 }
1167 
1168 
1169 static status_t
1170 nfs4_open_attr(fs_volume* volume, fs_vnode* vnode, const char* name,
1171 	int openMode, void** _cookie)
1172 {
1173 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1174 
1175 	VnodeToInodeLocker _(vti);
1176 	Inode* inode = vti->Get();
1177 	if (inode == NULL)
1178 		return B_ENTRY_NOT_FOUND;
1179 
1180 	FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
1181 	OpenAttrCookie* cookie = new OpenAttrCookie(fs);
1182 	if (cookie == NULL)
1183 		return B_NO_MEMORY;
1184 	*_cookie = cookie;
1185 
1186 	status_t result = inode->OpenAttr(name, openMode, cookie, false);
1187 	if (result != B_OK)
1188 		delete cookie;
1189 
1190 	return result;
1191 }
1192 
1193 
1194 static status_t
1195 nfs4_close_attr(fs_volume* volume, fs_vnode* vnode, void* _cookie)
1196 {
1197 	Cookie* cookie = reinterpret_cast<Cookie*>(_cookie);
1198 	return cookie->CancelAll();
1199 }
1200 
1201 
1202 static status_t
1203 nfs4_free_attr_cookie(fs_volume* volume, fs_vnode* vnode, void* _cookie)
1204 {
1205 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1206 
1207 	VnodeToInodeLocker _(vti);
1208 	Inode* inode = vti->Get();
1209 	if (inode == NULL)
1210 		return B_ENTRY_NOT_FOUND;
1211 
1212 	OpenAttrCookie* cookie = reinterpret_cast<OpenAttrCookie*>(_cookie);
1213 	inode->CloseAttr(cookie);
1214 	delete cookie;
1215 
1216 	return B_OK;
1217 }
1218 
1219 
1220 static status_t
1221 nfs4_read_attr(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos,
1222 	void* buffer, size_t* length)
1223 {
1224 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1225 	OpenAttrCookie* cookie = reinterpret_cast<OpenAttrCookie*>(_cookie);
1226 	bool eof;
1227 
1228 	VnodeToInodeLocker _(vti);
1229 	Inode* inode = vti->Get();
1230 	if (inode == NULL)
1231 		return B_ENTRY_NOT_FOUND;
1232 
1233 	return inode->ReadDirect(cookie, pos, buffer, length, &eof);
1234 }
1235 
1236 
1237 static status_t
1238 nfs4_write_attr(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos,
1239 	const void* buffer, size_t* length)
1240 {
1241 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1242 	OpenAttrCookie* cookie = reinterpret_cast<OpenAttrCookie*>(_cookie);
1243 
1244 	VnodeToInodeLocker _(vti);
1245 	Inode* inode = vti->Get();
1246 	if (inode == NULL)
1247 		return B_ENTRY_NOT_FOUND;
1248 
1249 	return inode->WriteDirect(cookie, pos, buffer, length);
1250 }
1251 
1252 
1253 static status_t
1254 nfs4_read_attr_stat(fs_volume* volume, fs_vnode* vnode, void* _cookie,
1255 	struct stat* stat)
1256 {
1257 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1258 	OpenAttrCookie* cookie = reinterpret_cast<OpenAttrCookie*>(_cookie);
1259 
1260 	VnodeToInodeLocker _(vti);
1261 	Inode* inode = vti->Get();
1262 	if (inode == NULL)
1263 		return B_ENTRY_NOT_FOUND;
1264 
1265 	return inode->Stat(stat, cookie);
1266 }
1267 
1268 
1269 static status_t
1270 nfs4_write_attr_stat(fs_volume* volume, fs_vnode* vnode, void* _cookie,
1271 	const struct stat* stat, int statMask)
1272 {
1273 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1274 	OpenAttrCookie* cookie = reinterpret_cast<OpenAttrCookie*>(_cookie);
1275 
1276 	VnodeToInodeLocker _(vti);
1277 	Inode* inode = vti->Get();
1278 	if (inode == NULL)
1279 		return B_ENTRY_NOT_FOUND;
1280 
1281 	return inode->WriteStat(stat, statMask, cookie);
1282 }
1283 
1284 
1285 static status_t
1286 nfs4_rename_attr(fs_volume* volume, fs_vnode* fromVnode, const char* fromName,
1287 	fs_vnode* toVnode, const char* toName)
1288 {
1289 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(toVnode->private_node);
1290 	VnodeToInodeLocker to(vti);
1291 	Inode* toInode = vti->Get();
1292 	if (toInode == NULL)
1293 		return B_ENTRY_NOT_FOUND;
1294 
1295 	vti = reinterpret_cast<VnodeToInode*>(fromVnode->private_node);
1296 	VnodeToInodeLocker from(vti);
1297 	Inode* fromInode = vti->Get();
1298 	if (fromInode == NULL)
1299 		return B_ENTRY_NOT_FOUND;
1300 
1301 	return Inode::Rename(fromInode, toInode, fromName, toName, true);
1302 }
1303 
1304 
1305 static status_t
1306 nfs4_remove_attr(fs_volume* volume, fs_vnode* vnode, const char* name)
1307 {
1308 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1309 
1310 	VnodeToInodeLocker _(vti);
1311 	Inode* inode = vti->Get();
1312 	if (inode == NULL)
1313 		return B_ENTRY_NOT_FOUND;
1314 
1315 	return inode->Remove(name, NF4NAMEDATTR, NULL);
1316 }
1317 
1318 
1319 static status_t
1320 nfs4_test_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie,
1321 	struct flock* lock)
1322 {
1323 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1324 	OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
1325 	TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, lock = %p", volume,
1326 		vti->ID(), _cookie, lock);
1327 
1328 	VnodeToInodeLocker _(vti);
1329 	Inode* inode = vti->Get();
1330 	if (inode == NULL)
1331 		return B_ENTRY_NOT_FOUND;
1332 
1333 	return inode->TestLock(cookie, lock);
1334 }
1335 
1336 
1337 static status_t
1338 nfs4_acquire_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie,
1339 			const struct flock* lock, bool wait)
1340 {
1341 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1342 	OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
1343 	TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, lock = %p", volume,
1344 		vti->ID(), _cookie, lock);
1345 
1346 
1347 	VnodeToInodeLocker _(vti);
1348 	Inode* inode = vti->Get();
1349 	if (inode == NULL)
1350 		return B_ENTRY_NOT_FOUND;
1351 
1352 	inode->RevalidateFileCache();
1353 	return inode->AcquireLock(cookie, lock, wait);
1354 }
1355 
1356 
1357 static status_t
1358 nfs4_release_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie,
1359 			const struct flock* lock)
1360 {
1361 	VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1362 	TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, lock = %p", volume,
1363 		vti->ID(), _cookie, lock);
1364 
1365 	VnodeToInodeLocker _(vti);
1366 	Inode* inode = vti->Get();
1367 	if (inode == NULL)
1368 		return B_ENTRY_NOT_FOUND;
1369 
1370 	if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK)
1371 		return B_OK;
1372 
1373 	OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
1374 
1375 	if (lock != NULL)
1376 		return inode->ReleaseLock(cookie, lock);
1377 	else
1378 		return inode->ReleaseAllLocks(cookie);
1379 }
1380 
1381 
1382 status_t
1383 nfs4_init()
1384 {
1385 	gRPCServerManager = new(std::nothrow) RPC::ServerManager;
1386 	if (gRPCServerManager == NULL)
1387 		return B_NO_MEMORY;
1388 
1389 	mutex_init(&gIdMapperLock, "idmapper Init Lock");
1390 	gIdMapper = NULL;
1391 
1392 	gWorkQueue = new(std::nothrow) WorkQueue;
1393 	if (gWorkQueue == NULL || gWorkQueue->InitStatus() != B_OK) {
1394 		delete gWorkQueue;
1395 		mutex_destroy(&gIdMapperLock);
1396 		delete gRPCServerManager;
1397 		return B_NO_MEMORY;
1398 	}
1399 
1400 	return B_OK;
1401 }
1402 
1403 
1404 status_t
1405 nfs4_uninit()
1406 {
1407 	RPC::CallbackServer::ShutdownAll();
1408 
1409 	delete gIdMapper;
1410 	delete gWorkQueue;
1411 	delete gRPCServerManager;
1412 
1413 	mutex_destroy(&gIdMapperLock);
1414 
1415 	return B_OK;
1416 }
1417 
1418 
1419 static status_t
1420 nfs4_std_ops(int32 op, ...)
1421 {
1422 	switch (op) {
1423 		case B_MODULE_INIT:
1424 			return nfs4_init();
1425 		case B_MODULE_UNINIT:
1426 			return nfs4_uninit();
1427 		default:
1428 			return B_ERROR;
1429 	}
1430 }
1431 
1432 
1433 fs_volume_ops gNFSv4VolumeOps = {
1434 	nfs4_unmount,
1435 	nfs4_read_fs_info,
1436 	NULL,
1437 	NULL,
1438 	nfs4_get_vnode,
1439 };
1440 
1441 fs_vnode_ops gNFSv4VnodeOps = {
1442 	nfs4_lookup,
1443 	NULL,	// get_vnode_name()
1444 	nfs4_put_vnode,
1445 	nfs4_remove_vnode,
1446 
1447 	/* VM file access */
1448 	NULL,	// can_page()
1449 	nfs4_read_pages,
1450 	nfs4_write_pages,
1451 
1452 	nfs4_io,
1453 	NULL,	// cancel_io()
1454 
1455 	nfs4_get_file_map,
1456 
1457 	NULL,	// ioctl()
1458 	nfs4_set_flags,
1459 	NULL,	// fs_select()
1460 	NULL,	// fs_deselect()
1461 	nfs4_fsync,
1462 
1463 	nfs4_read_symlink,
1464 	nfs4_create_symlink,
1465 
1466 	nfs4_link,
1467 	nfs4_unlink,
1468 	nfs4_rename,
1469 
1470 	nfs4_access,
1471 	nfs4_read_stat,
1472 	nfs4_write_stat,
1473 	NULL,	// fs_preallocate()
1474 
1475 	/* file operations */
1476 	nfs4_create,
1477 	nfs4_open,
1478 	nfs4_close,
1479 	nfs4_free_cookie,
1480 	nfs4_read,
1481 	nfs4_write,
1482 
1483 	/* directory operations */
1484 	nfs4_create_dir,
1485 	nfs4_remove_dir,
1486 	nfs4_open_dir,
1487 	nfs4_close_dir,
1488 	nfs4_free_dir_cookie,
1489 	nfs4_read_dir,
1490 	nfs4_rewind_dir,
1491 
1492 	/* attribute directory operations */
1493 	nfs4_open_attr_dir,
1494 	nfs4_close_attr_dir,
1495 	nfs4_free_attr_dir_cookie,
1496 	nfs4_read_attr_dir,
1497 	nfs4_rewind_attr_dir,
1498 
1499 	/* attribute operations */
1500 	nfs4_create_attr,
1501 	nfs4_open_attr,
1502 	nfs4_close_attr,
1503 	nfs4_free_attr_cookie,
1504 	nfs4_read_attr,
1505 	nfs4_write_attr,
1506 
1507 	nfs4_read_attr_stat,
1508 	nfs4_write_attr_stat,
1509 	nfs4_rename_attr,
1510 	nfs4_remove_attr,
1511 
1512 	/* support for node and FS layers */
1513 	NULL,	// create_special_node
1514 	NULL,	// get_super_vnode
1515 
1516 	/* lock operations */
1517 	nfs4_test_lock,
1518 	nfs4_acquire_lock,
1519 	nfs4_release_lock,
1520 };
1521 
1522 static file_system_module_info sNFSv4ModuleInfo = {
1523 	{
1524 		"file_systems/nfs4" B_CURRENT_FS_API_VERSION,
1525 		0,
1526 		nfs4_std_ops,
1527 	},
1528 
1529 	"nfs4",								// short_name
1530 	"Network File System version 4",	// pretty_name
1531 
1532 	// DDM flags
1533 	0,
1534 
1535 	// scanning
1536 	NULL,	// identify_partition()
1537 	NULL,	// scan_partition()
1538 	NULL,	// free_identify_partition_cookie()
1539 	NULL,	// free_partition_content_cookie()
1540 
1541 	nfs4_mount,
1542 };
1543 
1544 module_info* modules[] = {
1545 	(module_info*)&sNFSv4ModuleInfo,
1546 	NULL,
1547 };
1548 
1549