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