BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / file_systems / nfs4 / kernel_interface.cpp
blob52c943a59f906a4292474a5f27700ee1c85fb4e5
1 /*
2 * Copyright 2012 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Paweł Dziepak, pdziepak@quarnos.org
7 */
10 #include <stdio.h>
12 #include <AutoDeleter.h>
13 #include <fs_cache.h>
14 #include <fs_interface.h>
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"
29 #ifdef DEBUG
30 #define TRACE_NFS4
31 #endif
33 #ifdef TRACE_NFS4
34 static mutex gTraceLock = MUTEX_INITIALIZER(NULL);
36 #define TRACE(x...) \
37 { \
38 mutex_lock(&gTraceLock); \
39 dprintf("nfs4: %s(): ", __FUNCTION__); \
40 dprintf(x); \
41 dprintf("\n"); \
42 mutex_unlock(&gTraceLock); \
44 #else
45 #define TRACE(x...) (void)0
46 #endif
48 extern fs_volume_ops gNFSv4VolumeOps;
49 extern fs_vnode_ops gNFSv4VnodeOps;
52 RPC::ServerManager* gRPCServerManager;
55 RPC::ProgramData*
56 CreateNFS4Server(RPC::Server* serv)
58 return new NFS4Server(serv);
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** _server,
78 char** _path, MountConfiguration* conf)
80 if (_args == NULL)
81 return B_BAD_VALUE;
83 char* args = strdup(_args);
84 if (args == NULL)
85 return B_NO_MEMORY;
86 MemoryDeleter argsDeleter(args);
88 char* options = strchr(args, ' ');
89 if (options != NULL)
90 *options++ = '\0';
92 char* path = strrchr(args, ':');
93 if (path == NULL)
94 return B_MISMATCHED_VALUES;
95 *path++ = '\0';
97 *_server = strdup(args);
98 if (*_server == NULL)
99 return B_NO_MEMORY;
100 *address = new AddressResolver(args);
101 if (*address == NULL) {
102 free(*_server);
103 return B_NO_MEMORY;
106 *_path = strdup(path);
107 if (*_path == NULL) {
108 free(*_server);
109 delete *address;
110 return B_NO_MEMORY;
113 conf->fHard = false;
114 conf->fRetryLimit = 5;
115 conf->fRequestTimeout = sSecToBigTime(60);
116 conf->fEmulateNamedAttrs = false;
117 conf->fCacheMetadata = true;
118 conf->fDirectoryCacheTime = sSecToBigTime(5);
120 char* optionsEnd = NULL;
121 if (options != NULL)
122 optionsEnd = strchr(options, ' ');
123 while (options != NULL && *options != '\0') {
124 if (optionsEnd != NULL)
125 *optionsEnd++ = '\0';
127 if (strcmp(options, "hard") == 0)
128 conf->fHard = true;
129 else if (strncmp(options, "retrans=", 8) == 0) {
130 options += strlen("retrans=");
131 conf->fRetryLimit = atoi(options);
132 } else if (strncmp(options, "timeo=", 6) == 0) {
133 options += strlen("timeo=");
134 conf->fRequestTimeout = atoi(options);
135 } else if (strcmp(options, "noac") == 0)
136 conf->fCacheMetadata = false;
137 else if (strcmp(options, "xattr-emu") == 0)
138 conf->fEmulateNamedAttrs = true;
139 else if (strncmp(options, "port=", 5) == 0) {
140 options += strlen("port=");
141 (*address)->ForcePort(atoi(options));
142 } else if (strncmp(options, "proto=", 6) == 0) {
143 options += strlen("proto=");
144 (*address)->ForceProtocol(options);
145 } else if (strncmp(options, "dirtime=", 8) == 0) {
146 options += strlen("dirtime=");
147 conf->fDirectoryCacheTime = sSecToBigTime(atoi(options));
150 options = optionsEnd;
151 if (options != NULL)
152 optionsEnd = strchr(options, ' ');
155 return B_OK;
159 static status_t
160 nfs4_mount(fs_volume* volume, const char* device, uint32 flags,
161 const char* args, ino_t* _rootVnodeID)
163 TRACE("volume = %p, device = %s, flags = %" B_PRIu32 ", args = %s", volume,
164 device, flags, args);
166 status_t result;
168 /* prepare idmapper server */
169 MutexLocker locker(gIdMapperLock);
170 if (gIdMapper == NULL) {
171 gIdMapper = new(std::nothrow) IdMap;
172 if (gIdMapper == NULL)
173 return B_NO_MEMORY;
175 result = gIdMapper->InitStatus();
176 if (result != B_OK) {
177 delete gIdMapper;
178 gIdMapper = NULL;
179 return result;
182 locker.Unlock();
184 AddressResolver* resolver;
185 MountConfiguration config;
186 char* path;
187 char* serverName;
188 result = ParseArguments(args, &resolver, &serverName, &path, &config);
189 if (result != B_OK)
190 return result;
191 MemoryDeleter pathDeleter(path);
192 MemoryDeleter serverNameDeleter(serverName);
194 RPC::Server* server;
195 result = gRPCServerManager->Acquire(&server, resolver, CreateNFS4Server);
196 delete resolver;
197 if (result != B_OK)
198 return result;
200 FileSystem* fs;
201 result = FileSystem::Mount(&fs, server, serverName, path, volume->id,
202 config);
203 if (result != B_OK) {
204 gRPCServerManager->Release(server);
205 return result;
208 Inode* inode = fs->Root();
209 if (inode == NULL) {
210 delete fs;
211 gRPCServerManager->Release(server);
213 return B_IO_ERROR;
216 volume->private_volume = fs;
217 volume->ops = &gNFSv4VolumeOps;
219 VnodeToInode* vti = new VnodeToInode(inode->ID(), fs);
220 if (vti == NULL) {
221 delete fs;
222 gRPCServerManager->Release(server);
223 return B_NO_MEMORY;
226 vti->Replace(inode);
227 result = publish_vnode(volume, inode->ID(), vti, &gNFSv4VnodeOps,
228 inode->Type(), 0);
229 if (result != B_OK)
230 return result;
232 *_rootVnodeID = inode->ID();
234 TRACE("*_rootVnodeID = %" B_PRIi64, inode->ID());
236 return B_OK;
240 static status_t
241 nfs4_get_vnode(fs_volume* volume, ino_t id, fs_vnode* vnode, int* _type,
242 uint32* _flags, bool reenter)
244 FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
245 TRACE("volume = %p, id = %" B_PRIi64, volume, id);
247 VnodeToInode* vnodeToInode = new VnodeToInode(id, fs);
248 if (vnodeToInode == NULL)
249 return B_NO_MEMORY;
251 Inode* inode;
252 status_t result = fs->GetInode(id, &inode);
253 if (result != B_OK) {
254 delete vnodeToInode;
255 return result;
258 vnodeToInode->Replace(inode);
259 vnode->ops = &gNFSv4VnodeOps;
260 vnode->private_node = vnodeToInode;
262 *_type = inode->Type();
263 *_flags = 0;
265 return B_OK;
269 static status_t
270 nfs4_unmount(fs_volume* volume)
272 TRACE("volume = %p", volume);
273 FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
274 RPC::Server* server = fs->Server();
276 delete fs;
277 gRPCServerManager->Release(server);
279 return B_OK;
283 static status_t
284 nfs4_read_fs_info(fs_volume* volume, struct fs_info* info)
286 TRACE("volume = %p", volume);
288 FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
289 RootInode* inode = reinterpret_cast<RootInode*>(fs->Root());
290 return inode->ReadInfo(info);
294 static status_t
295 nfs4_lookup(fs_volume* volume, fs_vnode* dir, const char* name, ino_t* _id)
297 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(dir->private_node);
299 if (!strcmp(name, ".")) {
300 *_id = vti->ID();
301 void* ptr;
302 return get_vnode(volume, *_id, &ptr);
305 VnodeToInodeLocker locker(vti);
307 Inode* inode = vti->Get();
308 if (inode == NULL)
309 return B_ENTRY_NOT_FOUND;
311 TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s", volume, vti->ID(),
312 name);
314 status_t result = inode->LookUp(name, _id);
315 if (result != B_OK)
316 return result;
317 locker.Unlock();
319 TRACE("*_id = %" B_PRIi64, *_id);
321 // If VTI holds an outdated Inode next operation performed on it will
322 // return either ERR_STALE or ERR_FHEXPIRED. Both of these error codes
323 // will cause FileInfo data to be updated (the former will also cause Inode
324 // object to be recreated). We are taking an optimistic (an lazy) approach
325 // here. The following code just ensures VTI won't be removed too soon.
326 void* ptr;
327 result = get_vnode(volume, *_id, &ptr);
328 if (result == B_OK)
329 unremove_vnode(volume, *_id);
331 return result;
335 static status_t
336 nfs4_put_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
338 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
339 TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID());
341 delete vti;
342 return B_OK;
346 static status_t
347 nfs4_remove_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter)
349 FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
350 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
351 TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID());
353 if (fs->Root() == vti->GetPointer())
354 return B_OK;
356 ASSERT(vti->GetPointer() == NULL);
357 delete vti;
359 return B_OK;
363 static status_t
364 nfs4_read_pages(fs_volume* _volume, fs_vnode* vnode, void* _cookie, off_t pos,
365 const iovec* vecs, size_t count, size_t* _numBytes)
367 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
368 TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \
369 ", count = %lu, numBytes = %lu", _volume, vti->ID(), _cookie, pos,
370 count, *_numBytes);
372 VnodeToInodeLocker _(vti);
373 Inode* inode = vti->Get();
374 if (inode == NULL)
375 return B_ENTRY_NOT_FOUND;
377 OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
379 status_t result;
380 size_t totalRead = 0;
381 bool eof = false;
382 for (size_t i = 0; i < count && !eof; i++) {
383 size_t bytesLeft = vecs[i].iov_len;
384 char* buffer = reinterpret_cast<char*>(vecs[i].iov_base);
386 do {
387 size_t bytesRead = bytesLeft;
388 result = inode->ReadDirect(cookie, pos, buffer, &bytesRead, &eof);
389 if (result != B_OK)
390 return result;
392 totalRead += bytesRead;
393 pos += bytesRead;
394 buffer += bytesRead;
395 bytesLeft -= bytesRead;
396 } while (bytesLeft > 0 && !eof);
399 *_numBytes = totalRead;
401 TRACE("*numBytes = %lu", totalRead);
403 return B_OK;
407 static status_t
408 nfs4_write_pages(fs_volume* _volume, fs_vnode* vnode, void* _cookie, off_t pos,
409 const iovec* vecs, size_t count, size_t* _numBytes)
411 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
412 TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \
413 ", count = %lu, numBytes = %lu", _volume, vti->ID(), _cookie, pos,
414 count, *_numBytes);
416 VnodeToInodeLocker _(vti);
417 Inode* inode = vti->Get();
418 if (inode == NULL)
419 return B_ENTRY_NOT_FOUND;
421 OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
423 status_t result;
424 for (size_t i = 0; i < count; i++) {
425 uint64 bytesLeft = vecs[i].iov_len;
426 if (pos + bytesLeft > inode->MaxFileSize())
427 bytesLeft = inode->MaxFileSize() - pos;
429 char* buffer = reinterpret_cast<char*>(vecs[i].iov_base);
431 do {
432 size_t bytesWritten = bytesLeft;
434 result = inode->WriteDirect(cookie, pos, buffer, &bytesWritten);
435 if (result != B_OK)
436 return result;
438 bytesLeft -= bytesWritten;
439 pos += bytesWritten;
440 buffer += bytesWritten;
441 } while (bytesLeft > 0);
444 return B_OK;
448 static status_t
449 nfs4_io(fs_volume* volume, fs_vnode* vnode, void* cookie, io_request* request)
451 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
452 TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume, vti->ID(),
453 cookie);
455 VnodeToInodeLocker _(vti);
456 Inode* inode = vti->Get();
457 if (inode == NULL)
458 return B_ENTRY_NOT_FOUND;
460 IORequestArgs* args = new(std::nothrow) IORequestArgs;
461 if (args == NULL) {
462 notify_io_request(request, B_NO_MEMORY);
463 return B_NO_MEMORY;
465 args->fRequest = request;
466 args->fInode = inode;
468 status_t result = gWorkQueue->EnqueueJob(IORequest, args);
469 if (result != B_OK)
470 notify_io_request(request, result);
472 return result;
476 static status_t
477 nfs4_get_file_map(fs_volume* volume, fs_vnode* vnode, off_t _offset,
478 size_t size, struct file_io_vec* vecs, size_t* _count)
480 return B_ERROR;
484 static status_t
485 nfs4_set_flags(fs_volume* volume, fs_vnode* vnode, void* _cookie, int flags)
487 TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, flags = %d", volume,
488 reinterpret_cast<VnodeToInode*>(vnode->private_node)->ID(), _cookie,
489 flags);
491 OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
492 cookie->fMode = (cookie->fMode & ~(O_APPEND | O_NONBLOCK)) | flags;
493 return B_OK;
497 static status_t
498 nfs4_fsync(fs_volume* volume, fs_vnode* vnode)
500 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
501 TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID());
503 VnodeToInodeLocker _(vti);
504 Inode* inode = vti->Get();
505 if (inode == NULL)
506 return B_ENTRY_NOT_FOUND;
508 return inode->SyncAndCommit();
512 static status_t
513 nfs4_read_symlink(fs_volume* volume, fs_vnode* link, char* buffer,
514 size_t* _bufferSize)
516 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(link->private_node);
517 TRACE("volume = %p, link = %" B_PRIi64, volume, vti->ID());
519 VnodeToInodeLocker _(vti);
520 Inode* inode = vti->Get();
521 if (inode == NULL)
522 return B_ENTRY_NOT_FOUND;
524 return inode->ReadLink(buffer, _bufferSize);
528 static status_t
529 nfs4_create_symlink(fs_volume* volume, fs_vnode* dir, const char* name,
530 const char* path, int mode)
532 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(dir->private_node);
533 TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s, path = %s, mode = %d",
534 volume, vti->ID(), name, path, mode);
536 VnodeToInodeLocker _(vti);
537 Inode* inode = vti->Get();
538 if (inode == NULL)
539 return B_ENTRY_NOT_FOUND;
541 ino_t id;
542 status_t result = inode->CreateLink(name, path, mode, &id);
543 if (result != B_OK)
544 return result;
546 result = get_vnode(volume, id, reinterpret_cast<void**>(&vti));
547 if (result == B_OK) {
548 unremove_vnode(volume, id);
549 vti->Clear();
550 put_vnode(volume, id);
553 return B_OK;
557 static status_t
558 nfs4_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* vnode)
560 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
561 VnodeToInode* dirVti = reinterpret_cast<VnodeToInode*>(dir->private_node);
562 TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s, vnode = %" B_PRIi64,
563 volume, dirVti->ID(), name, vti->ID());
565 VnodeToInodeLocker _dir(dirVti);
566 Inode* dirInode = dirVti->Get();
567 if (dirInode == NULL)
568 return B_ENTRY_NOT_FOUND;
571 VnodeToInodeLocker _(vti);
572 Inode* inode = vti->Get();
573 if (inode == NULL)
574 return B_ENTRY_NOT_FOUND;
576 return inode->Link(dirInode, name);
580 static status_t
581 nfs4_unlink(fs_volume* volume, fs_vnode* dir, const char* name)
583 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(dir->private_node);
585 VnodeToInodeLocker locker(vti);
586 Inode* inode = vti->Get();
587 if (inode == NULL)
588 return B_ENTRY_NOT_FOUND;
590 TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s", volume, vti->ID(),
591 name);
593 ino_t id;
594 status_t result = inode->Remove(name, NF4REG, &id);
595 if (result != B_OK)
596 return result;
597 locker.Unlock();
599 result = acquire_vnode(volume, id);
600 if (result == B_OK) {
601 result = get_vnode(volume, id, reinterpret_cast<void**>(&vti));
602 ASSERT(result == B_OK);
604 if (vti->Unlink(inode->fInfo.fNames, name))
605 remove_vnode(volume, id);
607 put_vnode(volume, id);
608 put_vnode(volume, id);
611 return B_OK;
615 static status_t
616 nfs4_rename(fs_volume* volume, fs_vnode* fromDir, const char* fromName,
617 fs_vnode* toDir, const char* toName)
619 VnodeToInode* fromVti
620 = reinterpret_cast<VnodeToInode*>(fromDir->private_node);
621 VnodeToInode* toVti = reinterpret_cast<VnodeToInode*>(toDir->private_node);
622 TRACE("volume = %p, fromDir = %" B_PRIi64 ", toDir = %" B_PRIi64 "," \
623 " fromName = %s, toName = %s", volume, fromVti->ID(), toVti->ID(), \
624 fromName, toName);
626 VnodeToInodeLocker _from(fromVti);
627 Inode* fromInode = fromVti->Get();
628 if (fromInode == NULL)
629 return B_ENTRY_NOT_FOUND;
632 VnodeToInodeLocker _to(toVti);
633 Inode* toInode = toVti->Get();
634 if (toInode == NULL)
635 return B_ENTRY_NOT_FOUND;
637 ino_t id;
638 ino_t oldID;
639 status_t result = Inode::Rename(fromInode, toInode, fromName, toName, false,
640 &id, &oldID);
641 if (result != B_OK)
642 return result;
644 VnodeToInode* vti;
646 if (oldID != 0) {
647 // we have overriden an inode
648 result = acquire_vnode(volume, oldID);
649 if (result == B_OK) {
650 result = get_vnode(volume, oldID, reinterpret_cast<void**>(&vti));
651 ASSERT(result == B_OK);
652 if (vti->Unlink(toInode->fInfo.fNames, toName))
653 remove_vnode(volume, oldID);
655 put_vnode(volume, oldID);
656 put_vnode(volume, oldID);
660 result = get_vnode(volume, id, reinterpret_cast<void**>(&vti));
661 if (result == B_OK) {
662 Inode* child = vti->Get();
663 if (child == NULL) {
664 put_vnode(volume, id);
665 return B_ENTRY_NOT_FOUND;
668 unremove_vnode(volume, id);
669 child->fInfo.fNames->RemoveName(fromInode->fInfo.fNames, fromName);
670 child->fInfo.fNames->AddName(toInode->fInfo.fNames, toName);
671 put_vnode(volume, id);
674 return B_OK;
678 static status_t
679 nfs4_access(fs_volume* volume, fs_vnode* vnode, int mode)
681 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
682 TRACE("volume = %p, vnode = %" B_PRIi64 ", mode = %d", volume, vti->ID(),
683 mode);
685 VnodeToInodeLocker _(vti);
686 Inode* inode = vti->Get();
687 if (inode == NULL)
688 return B_ENTRY_NOT_FOUND;
690 return inode->Access(mode);
694 static status_t
695 nfs4_read_stat(fs_volume* volume, fs_vnode* vnode, struct stat* stat)
697 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
698 TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID());
700 VnodeToInodeLocker _(vti);
701 Inode* inode = vti->Get();
702 if (inode == NULL)
703 return B_ENTRY_NOT_FOUND;
705 status_t result = inode->Stat(stat);
706 if (inode->GetOpenState() != NULL)
707 stat->st_size = inode->MaxFileSize();
708 return result;
712 static status_t
713 nfs4_write_stat(fs_volume* volume, fs_vnode* vnode, const struct stat* stat,
714 uint32 statMask)
716 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
717 TRACE("volume = %p, vnode = %" B_PRIi64 ", statMask = %" B_PRIu32, volume,
718 vti->ID(), statMask);
720 VnodeToInodeLocker _(vti);
721 Inode* inode = vti->Get();
722 if (inode == NULL)
723 return B_ENTRY_NOT_FOUND;
725 return inode->WriteStat(stat, statMask);
729 static status_t
730 get_new_vnode(fs_volume* volume, ino_t id, VnodeToInode** _vti)
732 FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
733 Inode* inode;
734 VnodeToInode* vti;
736 status_t result = acquire_vnode(volume, id);
737 if (result == B_OK) {
738 ASSERT(get_vnode(volume, id, reinterpret_cast<void**>(_vti)) == B_OK);
739 unremove_vnode(volume, id);
741 // Release after acquire
742 put_vnode(volume, id);
744 vti = *_vti;
746 if (vti->Get() == NULL) {
747 result = fs->GetInode(id, &inode);
748 if (result != B_OK) {
749 put_vnode(volume, id);
750 return result;
753 vti->Replace(inode);
755 return B_OK;
758 return get_vnode(volume, id, reinterpret_cast<void**>(_vti));
762 static status_t
763 nfs4_create(fs_volume* volume, fs_vnode* dir, const char* name, int openMode,
764 int perms, void** _cookie, ino_t* _newVnodeID)
766 FileSystem* fs = reinterpret_cast<FileSystem*>(volume->private_volume);
768 OpenFileCookie* cookie = new OpenFileCookie(fs);
769 if (cookie == NULL)
770 return B_NO_MEMORY;
771 *_cookie = cookie;
773 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(dir->private_node);
774 TRACE("volume = %p, dir = %" B_PRIi64 ", name = %s, openMode = %d," \
775 " perms = %d", volume, vti->ID(), name, openMode, perms);
777 VnodeToInodeLocker _(vti);
778 Inode* inode = vti->Get();
779 if (inode == NULL)
780 return B_ENTRY_NOT_FOUND;
782 MutexLocker createLocker(fs->CreateFileLock());
784 OpenDelegationData data;
785 status_t result = inode->Create(name, openMode, perms, cookie, &data,
786 _newVnodeID);
787 if (result != B_OK) {
788 delete cookie;
789 return result;
792 result = get_new_vnode(volume, *_newVnodeID, &vti);
793 if (result != B_OK) {
794 delete cookie;
795 return result;
798 VnodeToInodeLocker _child(vti);
799 Inode* child = vti->Get();
800 if (child == NULL) {
801 delete cookie;
802 put_vnode(volume, *_newVnodeID);
803 return B_ENTRY_NOT_FOUND;
806 child->SetOpenState(cookie->fOpenState);
808 if (data.fType != OPEN_DELEGATE_NONE) {
809 Delegation* delegation
810 = new(std::nothrow) Delegation(data, child,
811 cookie->fOpenState->fClientID);
812 if (delegation != NULL) {
813 delegation->fInfo = cookie->fOpenState->fInfo;
814 delegation->fFileSystem = child->GetFileSystem();
815 child->SetDelegation(delegation);
819 TRACE("*cookie = %p, *newVnodeID = %" B_PRIi64, *_cookie, *_newVnodeID);
820 return result;
824 static status_t
825 nfs4_open(fs_volume* volume, fs_vnode* vnode, int openMode, void** _cookie)
827 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
828 TRACE("volume = %p, vnode = %" B_PRIi64 ", openMode = %d", volume,
829 vti->ID(), openMode);
831 VnodeToInodeLocker _(vti);
832 Inode* inode = vti->Get();
833 if (inode == NULL)
834 return B_ENTRY_NOT_FOUND;
836 if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK) {
837 *_cookie = NULL;
838 return B_OK;
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;
847 status_t result = inode->Open(openMode, cookie);
848 if (result != B_OK)
849 delete cookie;
851 TRACE("*cookie = %p", *_cookie);
853 return result;
857 static status_t
858 nfs4_close(fs_volume* volume, fs_vnode* vnode, void* _cookie)
860 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
862 TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume, vti->ID(),
863 _cookie);
865 VnodeToInodeLocker _(vti);
866 Inode* inode = vti->Get();
867 if (inode == NULL)
868 return B_ENTRY_NOT_FOUND;
871 if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK)
872 return B_OK;
874 Cookie* cookie = reinterpret_cast<Cookie*>(_cookie);
875 return cookie->CancelAll();
879 static status_t
880 nfs4_free_cookie(fs_volume* volume, fs_vnode* vnode, void* _cookie)
882 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
884 TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume, vti->ID(),
885 _cookie);
887 VnodeToInodeLocker _(vti);
888 Inode* inode = vti->Get();
889 if (inode == NULL)
890 return B_ENTRY_NOT_FOUND;
892 if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK)
893 return B_OK;
895 OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
897 inode->Close(cookie);
898 delete cookie;
900 return B_OK;
904 static status_t
905 nfs4_read(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos,
906 void* buffer, size_t* length)
908 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
909 TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \
910 ", length = %lu", volume, vti->ID(), _cookie, pos, *length);
912 VnodeToInodeLocker _(vti);
913 Inode* inode = vti->Get();
914 if (inode == NULL)
915 return B_ENTRY_NOT_FOUND;
917 if (inode->Type() == S_IFDIR)
918 return B_IS_A_DIRECTORY;
920 if (inode->Type() == S_IFLNK)
921 return B_BAD_VALUE;
923 OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
925 return inode->Read(cookie, pos, buffer, length);;
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)
933 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
934 TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, pos = %" B_PRIi64 \
935 ", length = %lu", volume, vti->ID(), _cookie, pos, *length);
937 VnodeToInodeLocker _(vti);
938 Inode* inode = vti->Get();
939 if (inode == NULL)
940 return B_ENTRY_NOT_FOUND;
942 if (inode->Type() == S_IFDIR)
943 return B_IS_A_DIRECTORY;
945 if (inode->Type() == S_IFLNK)
946 return B_BAD_VALUE;
948 OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
950 return inode->Write(cookie, pos, _buffer, length);
954 static status_t
955 nfs4_create_dir(fs_volume* volume, fs_vnode* parent, const char* name,
956 int mode)
958 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(parent->private_node);
959 TRACE("volume = %p, parent = %" B_PRIi64 ", mode = %d", volume, vti->ID(),
960 mode);
962 VnodeToInodeLocker _(vti);
963 Inode* inode = vti->Get();
964 if (inode == NULL)
965 return B_ENTRY_NOT_FOUND;
967 ino_t id;
968 status_t result = inode->CreateDir(name, mode, &id);
969 if (result != B_OK)
970 return result;
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);
979 return B_OK;
983 static status_t
984 nfs4_remove_dir(fs_volume* volume, fs_vnode* parent, const char* name)
986 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(parent->private_node);
987 TRACE("volume = %p, parent = %" B_PRIi64 ", name = %s", volume, vti->ID(),
988 name);
990 VnodeToInodeLocker _(vti);
991 Inode* inode = vti->Get();
992 if (inode == NULL)
993 return B_ENTRY_NOT_FOUND;
995 ino_t id;
996 status_t result = inode->Remove(name, NF4DIR, &id);
997 if (result != B_OK)
998 return result;
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);
1005 if (vti->Unlink(inode->fInfo.fNames, name))
1006 remove_vnode(volume, id);
1008 put_vnode(volume, id);
1009 put_vnode(volume, id);
1012 return B_OK;
1016 static status_t
1017 nfs4_open_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie)
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;
1025 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1026 TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID());
1028 VnodeToInodeLocker _(vti);
1029 Inode* inode = vti->Get();
1030 if (inode == NULL)
1031 return B_ENTRY_NOT_FOUND;
1033 status_t result = inode->OpenDir(cookie);
1034 if (result != B_OK)
1035 delete cookie;
1037 TRACE("*cookie = %p", *_cookie);
1039 return result;
1043 static status_t
1044 nfs4_close_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie)
1046 TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume,
1047 reinterpret_cast<VnodeToInode*>(vnode->private_node)->ID(), _cookie);
1049 Cookie* cookie = reinterpret_cast<Cookie*>(_cookie);
1050 return cookie->CancelAll();
1054 static status_t
1055 nfs4_free_dir_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie)
1057 TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume,
1058 reinterpret_cast<VnodeToInode*>(vnode->private_node)->ID(), cookie);
1060 delete reinterpret_cast<OpenDirCookie*>(cookie);
1061 return B_OK;
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)
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", volume, vti->ID(),
1072 _cookie);
1074 VnodeToInodeLocker _(vti);
1075 Inode* inode = vti->Get();
1076 if (inode == NULL)
1077 return B_ENTRY_NOT_FOUND;
1079 return inode->ReadDir(buffer, bufferSize, _num, cookie);
1083 static status_t
1084 nfs4_rewind_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie)
1086 TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p", volume,
1087 reinterpret_cast<VnodeToInode*>(vnode->private_node)->ID(), _cookie);
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;
1097 return B_OK;
1101 static status_t
1102 nfs4_open_attr_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie)
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;
1110 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1111 TRACE("volume = %p, vnode = %" B_PRIi64, volume, vti->ID());
1113 VnodeToInodeLocker _(vti);
1114 Inode* inode = vti->Get();
1115 if (inode == NULL)
1116 return B_ENTRY_NOT_FOUND;
1118 status_t result = inode->OpenAttrDir(cookie);
1119 if (result != B_OK)
1120 delete cookie;
1122 return result;
1126 static status_t
1127 nfs4_close_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie)
1129 return nfs4_close_dir(volume, vnode, cookie);
1133 static status_t
1134 nfs4_free_attr_dir_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie)
1136 return nfs4_free_dir_cookie(volume, vnode, cookie);
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)
1144 return nfs4_read_dir(volume, vnode, cookie, buffer, bufferSize, _num);
1148 static status_t
1149 nfs4_rewind_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie)
1151 return nfs4_rewind_dir(volume, vnode, cookie);
1155 static status_t
1156 nfs4_create_attr(fs_volume* volume, fs_vnode* vnode, const char* name,
1157 uint32 type, int openMode, void** _cookie)
1159 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1161 VnodeToInodeLocker _(vti);
1162 Inode* inode = vti->Get();
1163 if (inode == NULL)
1164 return B_ENTRY_NOT_FOUND;
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;
1172 status_t result = inode->OpenAttr(name, openMode, cookie, true, type);
1173 if (result != B_OK)
1174 delete cookie;
1176 return result;
1180 static status_t
1181 nfs4_open_attr(fs_volume* volume, fs_vnode* vnode, const char* name,
1182 int openMode, void** _cookie)
1184 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1186 VnodeToInodeLocker _(vti);
1187 Inode* inode = vti->Get();
1188 if (inode == NULL)
1189 return B_ENTRY_NOT_FOUND;
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;
1197 status_t result = inode->OpenAttr(name, openMode, cookie, false);
1198 if (result != B_OK)
1199 delete cookie;
1201 return result;
1205 static status_t
1206 nfs4_close_attr(fs_volume* volume, fs_vnode* vnode, void* _cookie)
1208 Cookie* cookie = reinterpret_cast<Cookie*>(_cookie);
1209 return cookie->CancelAll();
1213 static status_t
1214 nfs4_free_attr_cookie(fs_volume* volume, fs_vnode* vnode, void* _cookie)
1216 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1218 VnodeToInodeLocker _(vti);
1219 Inode* inode = vti->Get();
1220 if (inode == NULL)
1221 return B_ENTRY_NOT_FOUND;
1223 OpenAttrCookie* cookie = reinterpret_cast<OpenAttrCookie*>(_cookie);
1224 inode->CloseAttr(cookie);
1225 delete cookie;
1227 return B_OK;
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)
1235 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1236 OpenAttrCookie* cookie = reinterpret_cast<OpenAttrCookie*>(_cookie);
1237 bool eof;
1239 VnodeToInodeLocker _(vti);
1240 Inode* inode = vti->Get();
1241 if (inode == NULL)
1242 return B_ENTRY_NOT_FOUND;
1244 return inode->ReadDirect(cookie, pos, buffer, length, &eof);
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)
1252 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1253 OpenAttrCookie* cookie = reinterpret_cast<OpenAttrCookie*>(_cookie);
1255 VnodeToInodeLocker _(vti);
1256 Inode* inode = vti->Get();
1257 if (inode == NULL)
1258 return B_ENTRY_NOT_FOUND;
1260 return inode->WriteDirect(cookie, pos, buffer, length);
1264 static status_t
1265 nfs4_read_attr_stat(fs_volume* volume, fs_vnode* vnode, void* _cookie,
1266 struct stat* stat)
1268 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1269 OpenAttrCookie* cookie = reinterpret_cast<OpenAttrCookie*>(_cookie);
1271 VnodeToInodeLocker _(vti);
1272 Inode* inode = vti->Get();
1273 if (inode == NULL)
1274 return B_ENTRY_NOT_FOUND;
1276 return inode->Stat(stat, cookie);
1280 static status_t
1281 nfs4_write_attr_stat(fs_volume* volume, fs_vnode* vnode, void* _cookie,
1282 const struct stat* stat, int statMask)
1284 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1285 OpenAttrCookie* cookie = reinterpret_cast<OpenAttrCookie*>(_cookie);
1287 VnodeToInodeLocker _(vti);
1288 Inode* inode = vti->Get();
1289 if (inode == NULL)
1290 return B_ENTRY_NOT_FOUND;
1292 return inode->WriteStat(stat, statMask, cookie);
1296 static status_t
1297 nfs4_rename_attr(fs_volume* volume, fs_vnode* fromVnode, const char* fromName,
1298 fs_vnode* toVnode, const char* toName)
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;
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;
1312 return Inode::Rename(fromInode, toInode, fromName, toName, true);
1316 static status_t
1317 nfs4_remove_attr(fs_volume* volume, fs_vnode* vnode, const char* name)
1319 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1321 VnodeToInodeLocker _(vti);
1322 Inode* inode = vti->Get();
1323 if (inode == NULL)
1324 return B_ENTRY_NOT_FOUND;
1326 return inode->Remove(name, NF4NAMEDATTR, NULL);
1330 static status_t
1331 nfs4_test_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie,
1332 struct flock* lock)
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", volume,
1337 vti->ID(), _cookie, lock);
1339 VnodeToInodeLocker _(vti);
1340 Inode* inode = vti->Get();
1341 if (inode == NULL)
1342 return B_ENTRY_NOT_FOUND;
1344 return inode->TestLock(cookie, lock);
1348 static status_t
1349 nfs4_acquire_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie,
1350 const struct flock* lock, bool wait)
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", volume,
1355 vti->ID(), _cookie, lock);
1358 VnodeToInodeLocker _(vti);
1359 Inode* inode = vti->Get();
1360 if (inode == NULL)
1361 return B_ENTRY_NOT_FOUND;
1363 inode->RevalidateFileCache();
1364 return inode->AcquireLock(cookie, lock, wait);
1368 static status_t
1369 nfs4_release_lock(fs_volume* volume, fs_vnode* vnode, void* _cookie,
1370 const struct flock* lock)
1372 VnodeToInode* vti = reinterpret_cast<VnodeToInode*>(vnode->private_node);
1373 TRACE("volume = %p, vnode = %" B_PRIi64 ", cookie = %p, lock = %p", volume,
1374 vti->ID(), _cookie, lock);
1376 VnodeToInodeLocker _(vti);
1377 Inode* inode = vti->Get();
1378 if (inode == NULL)
1379 return B_ENTRY_NOT_FOUND;
1381 if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK)
1382 return B_OK;
1384 OpenFileCookie* cookie = reinterpret_cast<OpenFileCookie*>(_cookie);
1386 if (lock != NULL)
1387 return inode->ReleaseLock(cookie, lock);
1388 else
1389 return inode->ReleaseAllLocks(cookie);
1393 status_t
1394 nfs4_init()
1396 gRPCServerManager = new(std::nothrow) RPC::ServerManager;
1397 if (gRPCServerManager == NULL)
1398 return B_NO_MEMORY;
1400 mutex_init(&gIdMapperLock, "idmapper Init Lock");
1401 gIdMapper = NULL;
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;
1411 return B_OK;
1415 status_t
1416 nfs4_uninit()
1418 RPC::CallbackServer::ShutdownAll();
1420 delete gIdMapper;
1421 delete gWorkQueue;
1422 delete gRPCServerManager;
1424 mutex_destroy(&gIdMapperLock);
1426 return B_OK;
1430 static status_t
1431 nfs4_std_ops(int32 op, ...)
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;
1444 fs_volume_ops gNFSv4VolumeOps = {
1445 nfs4_unmount,
1446 nfs4_read_fs_info,
1447 NULL,
1448 NULL,
1449 nfs4_get_vnode,
1452 fs_vnode_ops gNFSv4VnodeOps = {
1453 nfs4_lookup,
1454 NULL, // get_vnode_name()
1455 nfs4_put_vnode,
1456 nfs4_remove_vnode,
1458 /* VM file access */
1459 NULL, // can_page()
1460 nfs4_read_pages,
1461 nfs4_write_pages,
1463 nfs4_io,
1464 NULL, // cancel_io()
1466 nfs4_get_file_map,
1468 NULL, // ioctl()
1469 nfs4_set_flags,
1470 NULL, // fs_select()
1471 NULL, // fs_deselect()
1472 nfs4_fsync,
1474 nfs4_read_symlink,
1475 nfs4_create_symlink,
1477 nfs4_link,
1478 nfs4_unlink,
1479 nfs4_rename,
1481 nfs4_access,
1482 nfs4_read_stat,
1483 nfs4_write_stat,
1484 NULL, // fs_preallocate()
1486 /* file operations */
1487 nfs4_create,
1488 nfs4_open,
1489 nfs4_close,
1490 nfs4_free_cookie,
1491 nfs4_read,
1492 nfs4_write,
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,
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,
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,
1518 nfs4_read_attr_stat,
1519 nfs4_write_attr_stat,
1520 nfs4_rename_attr,
1521 nfs4_remove_attr,
1523 /* support for node and FS layers */
1524 NULL, // create_special_node
1525 NULL, // get_super_vnode
1527 /* lock operations */
1528 nfs4_test_lock,
1529 nfs4_acquire_lock,
1530 nfs4_release_lock,
1533 static file_system_module_info sNFSv4ModuleInfo = {
1535 "file_systems/nfs4" B_CURRENT_FS_API_VERSION,
1537 nfs4_std_ops,
1540 "nfs4", // short_name
1541 "Network File System version 4", // pretty_name
1543 // DDM flags
1546 // scanning
1547 NULL, // identify_partition()
1548 NULL, // scan_partition()
1549 NULL, // free_identify_partition_cookie()
1550 NULL, // free_partition_content_cookie()
1552 nfs4_mount,
1555 module_info* modules[] = {
1556 (module_info*)&sNFSv4ModuleInfo,
1557 NULL,