NFS stuff:
[newos.git] / kernel / addons / fs / nfs / nfs.c
blobd9ee2c632ab7c64d895a00be6917749aa4cb5102
1 /*
2 ** Copyright 2002-2006, Travis Geiselbrecht. All rights reserved.
3 ** Distributed under the terms of the NewOS License.
4 */
5 #include <kernel/kernel.h>
6 #include <kernel/vfs.h>
7 #include <kernel/heap.h>
8 #include <kernel/khash.h>
9 #include <kernel/debug.h>
10 #include <kernel/lock.h>
11 #include <kernel/sem.h>
12 #include <kernel/vm.h>
13 #include <kernel/net/misc.h>
15 #include <newos/net.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include <stdlib.h>
21 #include "nfs.h"
22 #include "nfs_fs.h"
23 #include "rpc.h"
25 #define NFS_TRACE 0
27 #if NFS_TRACE
28 #define TRACE(x...) dprintf(x)
29 #else
30 #define TRACE(x...)
31 #endif
33 static int nfs_getattr(nfs_fs *nfs, nfs_vnode *v, nfs_attrstat *attrstat);
35 #if NFS_TRACE
36 static void dump_fhandle(const fhandle *handle)
38 unsigned int i;
40 for(i=0; i<sizeof(fhandle); i++)
41 dprintf("%02x", ((const uint8 *)handle)[i]);
43 #endif
45 static nfs_vnode *new_vnode_struct(nfs_fs *fs)
47 nfs_vnode *v;
49 v = (nfs_vnode *)kmalloc(sizeof(nfs_vnode));
50 if(!v)
51 return NULL;
53 if (mutex_init(&v->lock, "nfs vnode lock") < 0) {
54 kfree(v);
55 return NULL;
58 v->hash_next = NULL;
59 v->fs = fs;
61 return v;
64 static void destroy_vnode_struct(nfs_vnode *v)
66 mutex_destroy(&v->lock);
67 kfree(v);
70 /* ghetto x.x.x.x:/path parsing code */
71 static int parse_mount(const char *mount, char *address, int address_len, char *server_path, int path_len)
73 int a, b;
75 // trim the beginning
76 for(a = 0; mount[a] != 0 && isspace(mount[a]); a++)
78 if(mount[a] == 0)
79 return ERR_NOT_FOUND;
81 // search for the ':'
82 for(b = a; mount[b] != 0 && mount[b] != ':'; b++)
84 if(mount[b] == 0)
85 return ERR_NOT_FOUND;
87 // copy the address out
88 memcpy(address, &mount[a], b - a);
89 address[b - a] = 0;
91 // grab the path
92 strcpy(server_path, &mount[b+1]);
94 return NO_ERROR;
97 static int nfs_handle_hash_compare(void *a, const void *key)
99 struct nfs_vnode *v = (struct nfs_vnode *)a;
100 fhandle *handle = (fhandle *)key;
102 return memcmp(&v->nfs_handle, handle, sizeof(*handle));
105 static unsigned int nfs_handle_hash(void *a, const void *key, unsigned int range)
107 fhandle *hashit;
108 unsigned int hash;
109 unsigned int i;
111 if (key)
112 hashit = (fhandle *)key;
113 else
114 hashit = (fhandle *)&((nfs_vnode *)a)->nfs_handle;
116 #if NFS_TRACE
117 dprintf("nfs_handle_hash: hashit ");
118 dump_fhandle(hashit);
119 #endif
121 hash = 0;
122 for (i=0; i < sizeof(hashit->handle) / sizeof(unsigned int); i++) {
123 hash += ((unsigned int *)hashit->handle)[i];
125 hash += hash >> 16;
127 hash %= range;
129 #if NFS_TRACE
130 dprintf(" hash 0x%x\n", hash);
131 #endif
133 return hash;
136 static int parse_ipv4_addr_str(ipv4_addr *ip_addr, char *ip_addr_string)
138 int a, b;
140 *ip_addr = 0;
142 // walk through the first number
143 a = 0;
144 b = 0;
145 for(; ip_addr_string[b] != 0 && ip_addr_string[b] != '.'; b++)
147 if(ip_addr_string[b] == 0)
148 return ERR_NOT_FOUND;
149 ip_addr_string[b] = 0;
150 *ip_addr = atoi(&ip_addr_string[a]) << 24;
151 b++;
153 // second digit
154 a = b;
155 for(; ip_addr_string[b] != 0 && ip_addr_string[b] != '.'; b++)
157 if(ip_addr_string[b] == 0)
158 return ERR_NOT_FOUND;
159 ip_addr_string[b] = 0;
160 *ip_addr |= atoi(&ip_addr_string[a]) << 16;
161 b++;
163 // third digit
164 a = b;
165 for(; ip_addr_string[b] != 0 && ip_addr_string[b] != '.'; b++)
167 if(ip_addr_string[b] == 0)
168 return ERR_NOT_FOUND;
169 ip_addr_string[b] = 0;
170 *ip_addr |= atoi(&ip_addr_string[a]) << 8;
171 b++;
173 // last digit
174 a = b;
175 for(; ip_addr_string[b] != 0 && ip_addr_string[b] != '.'; b++)
177 ip_addr_string[b] = 0;
178 *ip_addr |= atoi(&ip_addr_string[a]);
180 return NO_ERROR;
183 static int nfs_mount_fs(nfs_fs *nfs, const char *server_path)
185 struct mount_args args;
186 char buf[128];
187 int err;
189 memset(&args, sizeof(args), 0);
190 strlcpy(args.dirpath, server_path, sizeof(args.dirpath));
191 args.len = htonl(strlen(args.dirpath));
193 rpc_set_port(&nfs->rpc, nfs->mount_port);
195 err = rpc_call(&nfs->rpc, MOUNTPROG, MOUNTVERS, MOUNTPROC_MNT, &args, ntohl(args.len) + 4, buf, sizeof(buf));
196 if(err < 0)
197 return err;
199 #if NFS_TRACE
200 TRACE("nfs_mount_fs: have root fhandle: ");
201 dump_fhandle((fhandle *)&buf[4]);
202 TRACE("\n");
203 #endif
204 // we should have the root handle now
205 memcpy(&nfs->root_vnode->nfs_handle, &buf[4], sizeof(nfs->root_vnode->nfs_handle));
207 // set the rpc port to the nfs server
208 rpc_set_port(&nfs->rpc, nfs->nfs_port);
210 return 0;
213 static int nfs_unmount_fs(nfs_fs *nfs)
215 struct mount_args args;
216 int err;
218 memset(&args, sizeof(args), 0);
219 strlcpy(args.dirpath, nfs->server_path, sizeof(args.dirpath));
220 args.len = htonl(strlen(args.dirpath));
222 rpc_set_port(&nfs->rpc, nfs->mount_port);
224 err = rpc_call(&nfs->rpc, MOUNTPROG, MOUNTVERS, MOUNTPROC_UMNT, &args, ntohl(args.len) + 4, NULL, 0);
226 return err;
229 int nfs_mount(fs_cookie *fs, fs_id id, const char *device, void *args, vnode_id *root_vnid)
231 nfs_fs *nfs;
232 int err;
233 char ip_addr_str[128];
234 ipv4_addr ip_addr;
236 TRACE("nfs_mount: fsid 0x%x, device '%s'\n", id, device);
238 /* create the fs structure */
239 nfs = kmalloc(sizeof(nfs_fs));
240 if(!nfs) {
241 err = ERR_NO_MEMORY;
242 goto err;
244 memset(nfs, 0, sizeof(nfs_fs));
246 mutex_init(&nfs->lock, "nfs lock");
248 err = parse_mount(device, ip_addr_str, sizeof(ip_addr_str), nfs->server_path, sizeof(nfs->server_path));
249 if(err < 0) {
250 err = ERR_NET_BAD_ADDRESS;
251 goto err1;
254 err = parse_ipv4_addr_str(&ip_addr, ip_addr_str);
255 if(err < 0) {
256 err = ERR_NET_BAD_ADDRESS;
257 goto err1;
260 nfs->id = id;
261 nfs->server_addr.type = ADDR_TYPE_IP;
262 nfs->server_addr.len = 4;
263 NETADDR_TO_IPV4(nfs->server_addr) = ip_addr;
265 // set up the rpc state
266 rpc_init_state(&nfs->rpc);
267 rpc_open_socket(&nfs->rpc, &nfs->server_addr);
269 // look up the port numbers for mount and nfs
270 rpc_pmap_lookup(&nfs->server_addr, MOUNTPROG, MOUNTVERS, IP_PROT_UDP, &nfs->mount_port);
271 rpc_pmap_lookup(&nfs->server_addr, NFSPROG, NFSVERS, IP_PROT_UDP, &nfs->nfs_port);
273 nfs->root_vnode = new_vnode_struct(nfs);
274 nfs->root_vnode->st = STREAM_TYPE_DIR;
276 // try to mount the filesystem
277 err = nfs_mount_fs(nfs, nfs->server_path);
278 if(err < 0)
279 goto err2;
281 // build the vnode hash table and stick the root vnode in it
282 nfs->handle_hash = hash_init(1024, offsetof(struct nfs_vnode, hash_next),
283 nfs_handle_hash_compare,
284 nfs_handle_hash);
285 if (!nfs->handle_hash) {
286 err = ERR_NO_MEMORY;
287 goto err2;
289 hash_insert(nfs->handle_hash, nfs->root_vnode);
291 *fs = nfs;
292 *root_vnid = VNODETOVNID(nfs->root_vnode);
294 return 0;
296 err2:
297 destroy_vnode_struct(nfs->root_vnode);
298 rpc_destroy_state(&nfs->rpc);
299 err1:
300 mutex_destroy(&nfs->lock);
301 kfree(nfs);
302 err:
303 return err;
306 int nfs_unmount(fs_cookie fs)
308 nfs_fs *nfs = (nfs_fs *)fs;
310 TRACE("nfs_unmount: fsid 0x%x\n", nfs->id);
312 // put_vnode on the root to release the ref to it
313 vfs_put_vnode(nfs->id, VNODETOVNID(nfs->root_vnode));
315 nfs_unmount_fs(nfs);
317 hash_uninit(nfs->handle_hash);
319 rpc_destroy_state(&nfs->rpc);
321 mutex_destroy(&nfs->lock);
322 kfree(nfs);
324 return 0;
327 int nfs_sync(fs_cookie fs)
329 nfs_fs *nfs = (nfs_fs *)fs;
331 TOUCH(nfs);
333 TRACE("nfs_sync: fsid 0x%x\n", nfs->id);
334 return 0;
337 int nfs_lookup(fs_cookie fs, fs_vnode _dir, const char *name, vnode_id *id)
339 nfs_fs *nfs = (nfs_fs *)fs;
340 nfs_vnode *dir = (nfs_vnode *)_dir;
341 int err;
343 TRACE("nfs_lookup: fsid 0x%x, dirvnid 0x%Lx, name '%s'\n", nfs->id, VNODETOVNID(dir), name);
345 mutex_lock(&dir->lock);
348 uint8 buf[sizeof(nfs_diropargs)];
349 nfs_diropargs *args = (nfs_diropargs *)buf;
350 nfs_diropres *res = (nfs_diropres *)buf;
351 int namelen = min(MAXNAMLEN, strlen(name));
353 /* set up the args structure */
354 memcpy(&args->dir, &dir->nfs_handle, sizeof(args->dir));
356 args->name.len = htonl(namelen);
357 memcpy(args->name.name, name, namelen);
359 err = rpc_call(&nfs->rpc, NFSPROG, NFSVERS, NFSPROC_LOOKUP, args, sizeof(nfs_diropargs), buf, sizeof(buf));
360 if(err < 0) {
361 err = ERR_NOT_FOUND;
362 goto out;
365 /* see if the lookup was successful */
366 if(htonl(res->status) == NFS_OK) {
367 nfs_vnode *v;
368 nfs_vnode *v2;
369 bool newvnode;
371 /* successful lookup */
372 #if NFS_TRACE
373 dprintf("nfs_lookup: result of lookup of '%s'\n", name);
374 dprintf("\tfhandle: "); dump_fhandle(&res->file); dprintf("\n");
375 dprintf("\tsize: %d\n", ntohl(res->attributes.size));
376 nfs_handle_hash(NULL, &res->file, 1024);
377 #endif
379 /* see if the vnode already exists */
380 newvnode = false;
381 v = hash_lookup(nfs->handle_hash, &res->file);
382 if (v == NULL) {
383 /* didn't find it, create a new one */
384 v = new_vnode_struct(nfs);
385 if(v == NULL) {
386 err = ERR_NO_MEMORY;
387 goto out;
390 /* copy the file handle over */
391 memcpy(&v->nfs_handle, &res->file, sizeof(v->nfs_handle));
393 /* figure out the stream type from the return value and cache it */
394 switch(ntohl(res->attributes.ftype)) {
395 case NFREG:
396 v->st = STREAM_TYPE_FILE;
397 break;
398 case NFDIR:
399 v->st = STREAM_TYPE_DIR;
400 break;
401 default:
402 v->st = -1;
405 /* add it to the handle -> vnode lookup table */
406 mutex_lock(&nfs->lock);
407 hash_insert(nfs->handle_hash, v);
408 mutex_unlock(&nfs->lock);
409 newvnode = true;
412 /* request that the vfs layer look it up */
413 err = vfs_get_vnode(nfs->id, VNODETOVNID(v), (fs_vnode *)(void *)&v2);
414 if(err < 0) {
415 if (newvnode) {
416 mutex_lock(&nfs->lock);
417 hash_remove(nfs->handle_hash, v);
418 mutex_unlock(&nfs->lock);
419 destroy_vnode_struct(v);
421 err = ERR_NOT_FOUND;
422 goto out;
425 ASSERT(v == v2);
427 *id = VNODETOVNID(v);
428 } else {
429 TRACE("nfs_lookup: '%s' not found\n", name);
430 err = ERR_NOT_FOUND;
431 goto out;
435 err = NO_ERROR;
437 out:
438 mutex_unlock(&dir->lock);
440 return err;
443 int nfs_getvnode(fs_cookie fs, vnode_id id, fs_vnode *v, bool r)
445 nfs_fs *nfs = (nfs_fs *)fs;
447 TOUCH(nfs);
449 TRACE("nfs_getvnode: fsid 0x%x, vnid 0x%Lx\n", nfs->id, id);
451 *v = VNIDTOVNODE(id);
453 return NO_ERROR;
456 int nfs_putvnode(fs_cookie fs, fs_vnode _v, bool r)
458 nfs_fs *nfs = (nfs_fs *)fs;
459 nfs_vnode *v = (nfs_vnode *)_v;
461 TOUCH(nfs);
463 TRACE("nfs_putvnode: fsid 0x%x, vnid 0x%Lx\n", nfs->id, VNODETOVNID(v));
465 mutex_lock(&nfs->lock);
466 hash_remove(nfs->handle_hash, v);
467 mutex_unlock(&nfs->lock);
468 destroy_vnode_struct(v);
470 return NO_ERROR;
473 int nfs_removevnode(fs_cookie fs, fs_vnode _v, bool r)
475 nfs_fs *nfs = (nfs_fs *)fs;
476 nfs_vnode *v = (nfs_vnode *)_v;
478 TOUCH(nfs);TOUCH(v);
480 TRACE("nfs_removevnode: fsid 0x%x, vnid 0x%Lx\n", nfs->id, VNODETOVNID(v));
482 return ERR_UNIMPLEMENTED;
485 static int nfs_opendir(fs_cookie _fs, fs_vnode _v, dir_cookie *_cookie)
487 struct nfs_vnode *v = (struct nfs_vnode *)_v;
488 struct nfs_cookie *cookie;
489 int err = 0;
491 TRACE("nfs_opendir: vnode %p\n", v);
493 if(v->st != STREAM_TYPE_DIR)
494 return ERR_VFS_NOT_DIR;
496 cookie = kmalloc(sizeof(struct nfs_cookie));
497 if(cookie == NULL)
498 return ERR_NO_MEMORY;
500 cookie->v = v;
501 cookie->u.dir.nfscookie = 0;
502 cookie->u.dir.at_end = false;
504 *_cookie = cookie;
506 return err;
509 static int nfs_closedir(fs_cookie _fs, fs_vnode _v, dir_cookie _cookie)
511 struct nfs_fs *fs = _fs;
512 struct nfs_vnode *v = _v;
513 struct nfs_cookie *cookie = _cookie;
515 TOUCH(fs);TOUCH(v);TOUCH(cookie);
517 TRACE("nfs_closedir: entry vnode %p, cookie %p\n", v, cookie);
519 if(v->st != STREAM_TYPE_DIR)
520 return ERR_VFS_NOT_DIR;
522 if(cookie)
523 kfree(cookie);
525 return 0;
528 static int nfs_rewinddir(fs_cookie _fs, fs_vnode _v, dir_cookie _cookie)
530 struct nfs_vnode *v = _v;
531 struct nfs_cookie *cookie = _cookie;
532 int err = 0;
534 TOUCH(v);
536 TRACE("nfs_rewinddir: vnode %p, cookie %p\n", v, cookie);
538 if(v->st != STREAM_TYPE_DIR)
539 return ERR_VFS_NOT_DIR;
541 mutex_lock(&v->lock);
543 cookie->u.dir.nfscookie = 0;
544 cookie->u.dir.at_end = false;
546 mutex_unlock(&v->lock);
548 return err;
551 #define READDIR_BUF_SIZE (MAXNAMLEN + 64)
553 static ssize_t _nfs_readdir(nfs_fs *nfs, nfs_vnode *v, nfs_cookie *cookie, void *buf, ssize_t len)
555 uint8 abuf[READDIR_BUF_SIZE];
556 nfs_readdirargs *args = (nfs_readdirargs *)abuf;
557 nfs_readdirres *res = (nfs_readdirres *)abuf;
558 ssize_t err = 0;
559 int i;
560 int namelen;
562 if(len < MAXNAMLEN)
563 return ERR_VFS_INSUFFICIENT_BUF; // XXX not quite accurate
565 /* see if we've already hit the end */
566 if(cookie->u.dir.at_end)
567 return 0;
569 /* put together the message */
570 memcpy(&args->dir, &v->nfs_handle, sizeof(args->dir));
571 args->cookie = cookie->u.dir.nfscookie;
572 args->count = htonl(min(len, READDIR_BUF_SIZE));
574 err = rpc_call(&nfs->rpc, NFSPROG, NFSVERS, NFSPROC_READDIR, args, sizeof(*args), abuf, sizeof(abuf));
575 if(err < 0)
576 return err;
578 /* get response */
579 if(ntohl(res->status) != NFS_OK)
580 return 0;
582 /* walk into the buffer, looking for the first entry */
583 if(ntohl(res->data[0]) == 0) {
584 // end of list
585 cookie->u.dir.at_end = true;
586 return 0;
588 i = ntohl(res->data[0]);
590 /* copy the data out of the first entry */
591 strlcpy(buf, (char const *)&res->data[i + 2], ntohl(res->data[i + 1]) + 1);
593 namelen = ROUNDUP(ntohl(res->data[i + 1]), 4);
595 /* update the cookie */
596 cookie->u.dir.nfscookie = res->data[i + namelen / 4 + 2];
598 return ntohl(res->data[i + 1]);
601 static int nfs_readdir(fs_cookie _fs, fs_vnode _v, dir_cookie _cookie, void *buf, size_t len)
603 struct nfs_fs *fs = _fs;
604 struct nfs_vnode *v = _v;
605 struct nfs_cookie *cookie = _cookie;
606 int err = 0;
608 TOUCH(v);
610 TRACE("nfs_readdir: vnode %p, cookie %p, len 0x%x\n", v, cookie, len);
612 if(v->st != STREAM_TYPE_DIR)
613 return ERR_VFS_NOT_DIR;
615 mutex_lock(&v->lock);
617 err = _nfs_readdir(fs, v, cookie, buf, len);
619 mutex_unlock(&v->lock);
621 return err;
624 int nfs_open(fs_cookie fs, fs_vnode _v, file_cookie *_cookie, int oflags)
626 nfs_fs *nfs = (nfs_fs *)fs;
627 nfs_vnode *v = (nfs_vnode *)_v;
628 nfs_cookie *cookie;
629 int err;
631 TOUCH(nfs);
633 TRACE("nfs_open: fsid 0x%x, vnid 0x%Lx, oflags 0x%x\n", nfs->id, VNODETOVNID(v), oflags);
635 if(v->st == STREAM_TYPE_DIR) {
636 err = ERR_VFS_IS_DIR;
637 goto err;
640 cookie = kmalloc(sizeof(nfs_cookie));
641 if(cookie == NULL) {
642 err = ERR_NO_MEMORY;
643 goto err;
645 cookie->v = v;
647 cookie->u.file.pos = 0;
648 cookie->u.file.oflags = oflags;
650 *_cookie = (file_cookie)cookie;
651 err = NO_ERROR;
653 err:
654 return err;
657 int nfs_close(fs_cookie fs, fs_vnode _v, file_cookie _cookie)
659 nfs_fs *nfs = (nfs_fs *)fs;
660 nfs_vnode *v = (nfs_vnode *)_v;
662 TOUCH(nfs);TOUCH(v);
664 TRACE("nfs_close: fsid 0x%x, vnid 0x%Lx\n", nfs->id, VNODETOVNID(v));
666 if(v->st == STREAM_TYPE_DIR)
667 return ERR_VFS_IS_DIR;
669 return NO_ERROR;
672 int nfs_freecookie(fs_cookie fs, fs_vnode _v, file_cookie _cookie)
674 nfs_fs *nfs = (nfs_fs *)fs;
675 nfs_vnode *v = (nfs_vnode *)_v;
676 nfs_cookie *cookie = (nfs_cookie *)_cookie;
678 TOUCH(nfs);TOUCH(v);
680 TRACE("nfs_freecookie: fsid 0x%x, vnid 0x%Lx\n", nfs->id, VNODETOVNID(v));
682 if(v->st == STREAM_TYPE_DIR)
683 return ERR_VFS_IS_DIR;
685 kfree(cookie);
687 return NO_ERROR;
690 int nfs_fsync(fs_cookie fs, fs_vnode _v)
692 nfs_fs *nfs = (nfs_fs *)fs;
693 nfs_vnode *v = (nfs_vnode *)_v;
695 TOUCH(nfs);TOUCH(v);
697 TRACE("nfs_fsync: fsid 0x%x, vnid 0x%Lx\n", nfs->id, VNODETOVNID(v));
699 return NO_ERROR;
702 #define READ_BUF_SIZE 1024
704 static ssize_t nfs_readfile(nfs_fs *nfs, nfs_vnode *v, nfs_cookie *cookie, void *buf, off_t pos, ssize_t len, bool updatecookiepos)
706 uint8 abuf[4 + sizeof(nfs_fattr) + READ_BUF_SIZE];
707 nfs_readargs *args = (nfs_readargs *)abuf;
708 nfs_readres *res = (nfs_readres *)abuf;
709 int err;
710 ssize_t total_read = 0;
712 TRACE("nfs_readfile: v %p, buf %p, pos %Ld, len %d\n", v, buf, pos, len);
714 /* check args */
715 if(pos < 0)
716 pos = cookie->u.file.pos;
717 /* can't do more than 32-bit offsets right now */
718 if(pos > 0xffffffff)
719 return 0;
720 /* negative or zero length means nothing */
721 if(len <= 0)
722 return 0;
724 while(len > 0) {
725 ssize_t to_read = min(len, READ_BUF_SIZE);
727 /* put together the message */
728 memcpy(&args->file, &v->nfs_handle, sizeof(args->file));
729 args->offset = htonl(pos);
730 args->count = htonl(to_read);
731 args->totalcount = 0; // unused
733 err = rpc_call(&nfs->rpc, NFSPROG, NFSVERS, NFSPROC_READ, args, sizeof(*args), abuf, sizeof(abuf));
734 if(err < 0)
735 break;
737 /* get response */
738 if(ntohl(res->status) != NFS_OK)
739 break;
741 /* see how much we read */
742 err = user_memcpy((uint8 *)buf + total_read, res->data, ntohl(res->len));
743 if(err < 0) {
744 total_read = err; // bad user give me bad buffer
745 break;
748 pos += ntohl(res->len);
749 len -= ntohl(res->len);
750 total_read += ntohl(res->len);
752 /* short read, we're done */
753 if((ssize_t)ntohl(res->len) != to_read)
754 break;
757 if (updatecookiepos)
758 cookie->u.file.pos = pos;
760 return total_read;
763 ssize_t nfs_read(fs_cookie fs, fs_vnode _v, file_cookie _cookie, void *buf, off_t pos, ssize_t len)
765 nfs_fs *nfs = (nfs_fs *)fs;
766 nfs_vnode *v = (nfs_vnode *)_v;
767 nfs_cookie *cookie = (nfs_cookie *)_cookie;
768 ssize_t err;
770 TRACE("nfs_read: fsid 0x%x, vnid 0x%Lx, buf %p, pos 0x%Lx, len %ld\n", nfs->id, VNODETOVNID(v), buf, pos, len);
772 if(v->st == STREAM_TYPE_DIR)
773 return ERR_VFS_IS_DIR;
775 mutex_lock(&v->lock);
777 err = nfs_readfile(nfs, v, cookie, buf, pos, len, true);
779 mutex_unlock(&v->lock);
781 return err;
784 #define WRITE_BUF_SIZE 1024
786 static ssize_t nfs_writefile(nfs_fs *nfs, nfs_vnode *v, nfs_cookie *cookie, const void *buf, off_t pos, ssize_t len, bool updatecookiepos)
788 uint8 abuf[sizeof(nfs_writeargs) + WRITE_BUF_SIZE];
789 nfs_writeargs *args = (nfs_writeargs *)abuf;
790 nfs_attrstat *res = (nfs_attrstat *)abuf;
791 int err;
792 ssize_t total_written = 0;
794 /* check args */
795 if(pos < 0)
796 pos = cookie->u.file.pos;
797 /* can't do more than 32-bit offsets right now */
798 if(pos > 0xffffffff)
799 return 0;
800 /* negative or zero length means nothing */
801 if(len <= 0)
802 return 0;
804 while(len > 0) {
805 ssize_t to_write = min(len, WRITE_BUF_SIZE);
807 /* put together the message */
808 memcpy(&args->file, &v->nfs_handle, sizeof(args->file));
809 args->beginoffset = 0; // unused
810 args->offset = htonl(pos);
811 args->totalcount = 0; // unused
812 args->len = htonl(to_write);
813 err = user_memcpy(args->data, (const uint8 *)buf + total_written, to_write);
814 if(err < 0) {
815 total_written = err;
816 break;
819 err = rpc_call(&nfs->rpc, NFSPROG, NFSVERS, NFSPROC_WRITE, args, sizeof(*args) + to_write, abuf, sizeof(abuf));
820 if(err < 0)
821 break;
823 /* get response */
824 if(ntohl(res->status) != NFS_OK)
825 break;
827 pos += to_write;
828 len -= to_write;
829 total_written += to_write;
832 if (updatecookiepos)
833 cookie->u.file.pos = pos;
835 return total_written;
838 ssize_t nfs_write(fs_cookie fs, fs_vnode _v, file_cookie _cookie, const void *buf, off_t pos, ssize_t len)
840 nfs_fs *nfs = (nfs_fs *)fs;
841 nfs_vnode *v = (nfs_vnode *)_v;
842 nfs_cookie *cookie = (nfs_cookie *)_cookie;
843 ssize_t err;
845 TRACE("nfs_write: fsid 0x%x, vnid 0x%Lx, buf %p, pos 0x%Lx, len %ld\n", nfs->id, VNODETOVNID(v), buf, pos, len);
847 mutex_lock(&v->lock);
849 switch(v->st) {
850 case STREAM_TYPE_FILE:
851 err = nfs_writefile(nfs, v, cookie, buf, pos, len, true);
852 break;
853 case STREAM_TYPE_DIR:
854 err = ERR_NOT_ALLOWED;
855 break;
856 default:
857 err = ERR_GENERAL;
860 mutex_unlock(&v->lock);
862 return err;
865 int nfs_seek(fs_cookie fs, fs_vnode _v, file_cookie _cookie, off_t pos, seek_type st)
867 nfs_fs *nfs = (nfs_fs *)fs;
868 nfs_vnode *v = (nfs_vnode *)_v;
869 nfs_cookie *cookie = (nfs_cookie *)_cookie;
870 int err = NO_ERROR;
871 nfs_attrstat attrstat;
872 off_t file_len;
874 TRACE("nfs_seek: fsid 0x%x, vnid 0x%Lx, pos 0x%Lx, seek_type %d\n", nfs->id, VNODETOVNID(v), pos, st);
876 if(v->st == STREAM_TYPE_DIR)
877 return ERR_VFS_IS_DIR;
879 mutex_lock(&v->lock);
881 err = nfs_getattr(nfs, v, &attrstat);
882 if(err < 0)
883 goto out;
885 file_len = ntohl(attrstat.attributes.size);
887 switch(st) {
888 case _SEEK_SET:
889 if(pos < 0)
890 pos = 0;
891 if(pos > file_len)
892 pos = file_len;
893 cookie->u.file.pos = pos;
894 break;
895 case _SEEK_CUR:
896 if(pos + cookie->u.file.pos > file_len)
897 cookie->u.file.pos = file_len;
898 else if(pos + cookie->u.file.pos < 0)
899 cookie->u.file.pos = 0;
900 else
901 cookie->u.file.pos += pos;
902 break;
903 case _SEEK_END:
904 if(pos > 0)
905 cookie->u.file.pos = file_len;
906 else if(pos + file_len < 0)
907 cookie->u.file.pos = 0;
908 else
909 cookie->u.file.pos = pos + file_len;
910 break;
911 default:
912 err = ERR_INVALID_ARGS;
915 out:
916 mutex_unlock(&v->lock);
918 return err;
921 int nfs_ioctl(fs_cookie fs, fs_vnode _v, file_cookie cookie, int op, void *buf, size_t len)
923 nfs_fs *nfs = (nfs_fs *)fs;
924 nfs_vnode *v = (nfs_vnode *)_v;
926 TOUCH(nfs);TOUCH(v);
928 TRACE("nfs_ioctl: fsid 0x%x, vnid 0x%Lx, op %d, buf %p, len %ld\n", nfs->id, VNODETOVNID(v), op, buf, len);
930 return ERR_UNIMPLEMENTED;
933 int nfs_canpage(fs_cookie fs, fs_vnode _v)
935 nfs_fs *nfs = (nfs_fs *)fs;
936 nfs_vnode *v = (nfs_vnode *)_v;
938 TOUCH(nfs);TOUCH(v);
940 TRACE("nfs_canpage: fsid 0x%x, vnid 0x%Lx\n", nfs->id, VNODETOVNID(v));
942 if(v->st == STREAM_TYPE_FILE)
943 return 1;
944 else
945 return 0;
948 ssize_t nfs_readpage(fs_cookie fs, fs_vnode _v, iovecs *vecs, off_t pos)
950 nfs_fs *nfs = (nfs_fs *)fs;
951 nfs_vnode *v = (nfs_vnode *)_v;
952 unsigned int i;
953 ssize_t readfile_return;
954 ssize_t total_bytes_read = 0;
956 TOUCH(nfs);TOUCH(v);
958 TRACE("nfs_readpage: fsid 0x%x, vnid 0x%Lx, vecs %p, pos 0x%Lx\n", nfs->id, VNODETOVNID(v), vecs, pos);
960 if(v->st == STREAM_TYPE_DIR)
961 return ERR_VFS_IS_DIR;
963 mutex_lock(&v->lock);
965 for (i=0; i < vecs->num; i++) {
966 readfile_return = nfs_readfile(nfs, v, NULL, vecs->vec[i].start, pos, vecs->vec[i].len, false);
967 TRACE("nfs_readpage: nfs_readfile returns %d\n", readfile_return);
968 if (readfile_return < 0)
969 goto out;
971 pos += readfile_return;
972 total_bytes_read += readfile_return;
974 if ((size_t)readfile_return < vecs->vec[i].len) {
975 /* we have hit the end of file, zero out the rest */
976 for (; i < vecs->num; i++) {
977 memset(vecs->vec[i].start + readfile_return, 0, vecs->vec[i].len - readfile_return);
978 total_bytes_read += vecs->vec[i].len - readfile_return;
980 readfile_return = 0; // after the first pass, wipe out entire pages
985 out:
986 mutex_unlock(&v->lock);
988 return total_bytes_read;
991 ssize_t nfs_writepage(fs_cookie fs, fs_vnode _v, iovecs *vecs, off_t pos)
993 nfs_fs *nfs = (nfs_fs *)fs;
994 nfs_vnode *v = (nfs_vnode *)_v;
996 TOUCH(nfs);TOUCH(v);
998 TRACE("nfs_writepage: fsid 0x%x, vnid 0x%Lx, vecs %p, pos 0x%Lx\n", nfs->id, VNODETOVNID(v), vecs, pos);
1000 if(v->st == STREAM_TYPE_DIR)
1001 return ERR_VFS_IS_DIR;
1003 return ERR_UNIMPLEMENTED;
1006 int nfs_create(fs_cookie fs, fs_vnode _dir, const char *name, void *create_args, vnode_id *new_vnid)
1008 nfs_fs *nfs = (nfs_fs *)fs;
1009 nfs_vnode *dir = (nfs_vnode *)_dir;
1011 TOUCH(nfs);TOUCH(dir);
1013 TRACE("nfs_create: fsid 0x%x, vnid 0x%Lx, name '%s'\n", nfs->id, VNODETOVNID(dir), name);
1015 return ERR_UNIMPLEMENTED;
1018 int nfs_unlink(fs_cookie fs, fs_vnode _dir, const char *name)
1020 nfs_fs *nfs = (nfs_fs *)fs;
1021 nfs_vnode *dir = (nfs_vnode *)_dir;
1023 TOUCH(nfs);TOUCH(dir);
1025 TRACE("nfs_unlink: fsid 0x%x, vnid 0x%Lx, name '%s'\n", nfs->id, VNODETOVNID(dir), name);
1027 return ERR_UNIMPLEMENTED;
1030 int nfs_rename(fs_cookie fs, fs_vnode _olddir, const char *oldname, fs_vnode _newdir, const char *newname)
1032 nfs_fs *nfs = (nfs_fs *)fs;
1033 nfs_vnode *olddir = (nfs_vnode *)_olddir;
1034 nfs_vnode *newdir = (nfs_vnode *)_newdir;
1036 TOUCH(nfs);TOUCH(olddir);TOUCH(newdir);
1038 TRACE("nfs_rename: fsid 0x%x, vnid 0x%Lx, oldname '%s', newdir 0x%Lx, newname '%s'\n", nfs->id, VNODETOVNID(olddir), oldname, VNODETOVNID(newdir), newname);
1040 return ERR_UNIMPLEMENTED;
1043 int nfs_mkdir(fs_cookie _fs, fs_vnode _base_dir, const char *name)
1045 nfs_fs *nfs = (nfs_fs *)_fs;
1046 nfs_vnode *dir = (nfs_vnode *)_base_dir;
1048 TOUCH(nfs);TOUCH(dir);
1050 TRACE("nfs_mkdir: fsid 0x%x, vnid 0x%Lx, name '%s'\n", nfs->id, VNODETOVNID(dir), name);
1052 return ERR_UNIMPLEMENTED;
1055 int nfs_rmdir(fs_cookie _fs, fs_vnode _base_dir, const char *name)
1057 nfs_fs *nfs = (nfs_fs *)_fs;
1058 nfs_vnode *dir = (nfs_vnode *)_base_dir;
1060 TOUCH(nfs);TOUCH(dir);
1062 TRACE("nfs_rmdir: fsid 0x%x, vnid 0x%Lx, name '%s'\n", nfs->id, VNODETOVNID(dir), name);
1064 return ERR_UNIMPLEMENTED;
1067 static int nfs_getattr(nfs_fs *nfs, nfs_vnode *v, nfs_attrstat *attrstat)
1069 return rpc_call(&nfs->rpc, NFSPROG, NFSVERS, NFSPROC_GETATTR, &v->nfs_handle, sizeof(v->nfs_handle), attrstat, sizeof(nfs_attrstat));
1072 int nfs_rstat(fs_cookie fs, fs_vnode _v, struct file_stat *stat)
1074 nfs_fs *nfs = (nfs_fs *)fs;
1075 nfs_vnode *v = (nfs_vnode *)_v;
1076 nfs_attrstat attrstat;
1077 int err;
1079 TRACE("nfs_rstat: fsid 0x%x, vnid 0x%Lx, stat %p\n", nfs->id, VNODETOVNID(v), stat);
1081 mutex_lock(&v->lock);
1083 err = nfs_getattr(nfs, v, &attrstat);
1084 if(err < 0)
1085 goto out;
1087 if(ntohl(attrstat.status) != NFS_OK) {
1088 err = ERR_IO_ERROR;
1089 goto out;
1092 /* copy the stat over from the nfs attrstat */
1093 stat->vnid = VNODETOVNID(v);
1094 stat->size = ntohl(attrstat.attributes.size);
1095 switch(ntohl(attrstat.attributes.ftype)) {
1096 case NFREG:
1097 stat->type = STREAM_TYPE_FILE;
1098 break;
1099 case NFDIR:
1100 stat->type = STREAM_TYPE_DIR;
1101 break;
1102 default:
1103 stat->type = STREAM_TYPE_DEVICE; // XXX should have unknown type
1104 break;
1107 err = NO_ERROR;
1109 out:
1110 mutex_unlock(&v->lock);
1112 return err;
1115 int nfs_wstat(fs_cookie fs, fs_vnode _v, struct file_stat *stat, int stat_mask)
1117 nfs_fs *nfs = (nfs_fs *)fs;
1118 nfs_vnode *v = (nfs_vnode *)_v;
1120 TOUCH(nfs);TOUCH(v);
1122 TRACE("nfs_wstat: fsid 0x%x, vnid 0x%Lx, stat %p, stat_mask 0x%x\n", nfs->id, VNODETOVNID(v), stat, stat_mask);
1124 return ERR_UNIMPLEMENTED;
1127 static struct fs_calls nfs_calls = {
1128 &nfs_mount,
1129 &nfs_unmount,
1130 &nfs_sync,
1132 &nfs_lookup,
1134 &nfs_getvnode,
1135 &nfs_putvnode,
1136 &nfs_removevnode,
1138 &nfs_opendir,
1139 &nfs_closedir,
1140 &nfs_rewinddir,
1141 &nfs_readdir,
1143 &nfs_open,
1144 &nfs_close,
1145 &nfs_freecookie,
1146 &nfs_fsync,
1148 &nfs_read,
1149 &nfs_write,
1150 &nfs_seek,
1151 &nfs_ioctl,
1153 &nfs_canpage,
1154 &nfs_readpage,
1155 &nfs_writepage,
1157 &nfs_create,
1158 &nfs_unlink,
1159 &nfs_rename,
1161 &nfs_mkdir,
1162 &nfs_rmdir,
1164 &nfs_rstat,
1165 &nfs_wstat
1168 int fs_bootstrap(void);
1169 int fs_bootstrap(void)
1171 dprintf("bootstrap_nfs: entry\n");
1172 return vfs_register_filesystem("nfs", &nfs_calls);