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
40extern fs_volume_ops gNFSv4VolumeOps;
41extern fs_vnode_ops gNFSv4VnodeOps;
42
43
44RPC::ServerManager* gRPCServerManager;
45
46
47RPC::ProgramData*
48CreateNFS4Server(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
68static status_t
69ParseArguments(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
151static status_t
152nfs4_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
239static status_t
240nfs4_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
268static status_t
269nfs4_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
282static status_t
283nfs4_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
293static status_t
294nfs4_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
334static status_t
335nfs4_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
345static status_t
346nfs4_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
362static status_t
363nfs4_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
406static status_t
407nfs4_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
447static status_t
448nfs4_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
475static status_t
476nfs4_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
483static status_t
484nfs4_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
496static status_t
497nfs4_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
511static status_t
512nfs4_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
527static status_t
528nfs4_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
556static status_t
557nfs4_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
579static status_t
580nfs4_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
614static status_t
615nfs4_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
677static status_t
678nfs4_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
693static status_t
694nfs4_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
711static status_t
712nfs4_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
728static status_t
729get_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
761static status_t
762nfs4_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
824static status_t
825nfs4_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
857static status_t
858nfs4_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
879static status_t
880nfs4_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
904static status_t
905nfs4_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
929static status_t
930nfs4_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
954static status_t
955nfs4_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
983static status_t
984nfs4_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
1016static status_t
1017nfs4_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
1043static status_t
1044nfs4_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
1054static status_t
1055nfs4_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
1065static status_t
1066nfs4_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
1083static status_t
1084nfs4_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
1101static status_t
1102nfs4_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
1126static status_t
1127nfs4_close_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie)
1128{
1129	return nfs4_close_dir(volume, vnode, cookie);
1130}
1131
1132
1133static status_t
1134nfs4_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
1140static status_t
1141nfs4_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
1148static status_t
1149nfs4_rewind_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie)
1150{
1151	return nfs4_rewind_dir(volume, vnode, cookie);
1152}
1153
1154
1155static status_t
1156nfs4_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
1180static status_t
1181nfs4_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
1205static status_t
1206nfs4_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
1213static status_t
1214nfs4_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
1231static status_t
1232nfs4_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
1248static status_t
1249nfs4_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
1264static status_t
1265nfs4_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
1280static status_t
1281nfs4_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
1296static status_t
1297nfs4_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
1316static status_t
1317nfs4_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
1330static status_t
1331nfs4_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
1348static status_t
1349nfs4_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
1368static status_t
1369nfs4_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
1393status_t
1394nfs4_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
1415status_t
1416nfs4_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
1430static status_t
1431nfs4_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
1444fs_volume_ops gNFSv4VolumeOps = {
1445	nfs4_unmount,
1446	nfs4_read_fs_info,
1447	NULL,
1448	NULL,
1449	nfs4_get_vnode,
1450};
1451
1452fs_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
1533static 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
1555module_info* modules[] = {
1556	(module_info*)&sNFSv4ModuleInfo,
1557	NULL,
1558};
1559
1560