use the -newos toolchain even if -elf is present.
[newos.git] / kernel / addons / fs / nfs / nfs.c
blob42da0802ab5a3e587ed7fef70e7c9b399542547a
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, uint8 *attrstatbuf, nfs_attrstat *attrstat);
35 #if NFS_TRACE
36 static void dump_fhandle(const nfs_fhandle *handle)
38 unsigned int i;
40 for(i=0; i<sizeof(nfs_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 nfs_fhandle *handle = (nfs_fhandle *)key;
102 return memcmp(&v->nfs_handle, handle, sizeof(nfs_fhandle));
105 static unsigned int nfs_handle_hash(void *a, const void *key, unsigned int range)
107 const nfs_fhandle *hashit;
108 unsigned int hash;
109 unsigned int i;
111 if (key)
112 hashit = (const nfs_fhandle *)key;
113 else
114 hashit = (const nfs_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) / sizeof(unsigned int); i++) {
123 hash += ((unsigned int *)hashit)[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 nfs_status_to_error(nfs_status status)
138 switch (status) {
139 case NFS_OK:
140 return NO_ERROR;
141 case NFSERR_PERM:
142 return ERR_PERMISSION_DENIED;
143 case NFSERR_NOENT:
144 return ERR_VFS_PATH_NOT_FOUND;
145 case NFSERR_IO:
146 return ERR_IO_ERROR;
147 case NFSERR_NXIO:
148 return ERR_NOT_FOUND;
149 case NFSERR_ACCES:
150 return ERR_PERMISSION_DENIED;
151 case NFSERR_EXIST:
152 return ERR_VFS_ALREADY_EXISTS;
153 case NFSERR_NODEV:
154 return ERR_NOT_FOUND;
155 case NFSERR_NOTDIR:
156 return ERR_VFS_NOT_DIR;
157 case NFSERR_ISDIR:
158 return ERR_VFS_IS_DIR;
159 case NFSERR_FBIG:
160 return ERR_TOO_BIG;
161 case NFSERR_NOSPC:
162 return ERR_VFS_OUT_OF_SPACE;
163 case NFSERR_ROFS:
164 return ERR_VFS_READONLY_FS;
165 case NFSERR_NAMETOOLONG:
166 return ERR_VFS_PATH_TOO_LONG;
167 case NFSERR_NOTEMPTY:
168 return ERR_VFS_DIR_NOT_EMPTY;
169 case NFSERR_DQUOT:
170 return ERR_VFS_EXCEEDED_QUOTA;
171 case NFSERR_STALE:
172 return ERR_INVALID_HANDLE;
173 default:
174 return ERR_GENERAL;
178 static int parse_ipv4_addr_str(ipv4_addr *ip_addr, char *ip_addr_string)
180 int a, b;
182 *ip_addr = 0;
184 // walk through the first number
185 a = 0;
186 b = 0;
187 for(; ip_addr_string[b] != 0 && ip_addr_string[b] != '.'; b++)
189 if(ip_addr_string[b] == 0)
190 return ERR_NOT_FOUND;
191 ip_addr_string[b] = 0;
192 *ip_addr = atoi(&ip_addr_string[a]) << 24;
193 b++;
195 // second digit
196 a = b;
197 for(; ip_addr_string[b] != 0 && ip_addr_string[b] != '.'; b++)
199 if(ip_addr_string[b] == 0)
200 return ERR_NOT_FOUND;
201 ip_addr_string[b] = 0;
202 *ip_addr |= atoi(&ip_addr_string[a]) << 16;
203 b++;
205 // third digit
206 a = b;
207 for(; ip_addr_string[b] != 0 && ip_addr_string[b] != '.'; b++)
209 if(ip_addr_string[b] == 0)
210 return ERR_NOT_FOUND;
211 ip_addr_string[b] = 0;
212 *ip_addr |= atoi(&ip_addr_string[a]) << 8;
213 b++;
215 // last digit
216 a = b;
217 for(; ip_addr_string[b] != 0 && ip_addr_string[b] != '.'; b++)
219 ip_addr_string[b] = 0;
220 *ip_addr |= atoi(&ip_addr_string[a]);
222 return NO_ERROR;
225 static int nfs_mount_fs(nfs_fs *nfs, const char *server_path)
227 uint8 sendbuf[256];
228 char buf[128];
229 nfs_mountargs args;
230 size_t arglen;
231 int err;
233 rpc_set_port(&nfs->rpc, nfs->mount_port);
235 args.dirpath = server_path;
236 arglen = nfs_pack_mountargs(sendbuf, &args);
238 err = rpc_call(&nfs->rpc, MOUNTPROG, MOUNTVERS, MOUNTPROC_MNT, sendbuf, arglen, buf, sizeof(buf));
239 if(err < 0)
240 return err;
242 #if NFS_TRACE
243 TRACE("nfs_mount_fs: have root fhandle: ");
244 dump_fhandle((const nfs_fhandle *)&buf[4]);
245 TRACE("\n");
246 #endif
247 // we should have the root handle now
248 memcpy(&nfs->root_vnode->nfs_handle, &buf[4], sizeof(nfs->root_vnode->nfs_handle));
250 // set the rpc port to the nfs server
251 rpc_set_port(&nfs->rpc, nfs->nfs_port);
253 return 0;
256 static int nfs_unmount_fs(nfs_fs *nfs)
258 uint8 sendbuf[256];
259 size_t arglen;
260 nfs_mountargs args;
261 int err;
263 rpc_set_port(&nfs->rpc, nfs->mount_port);
265 args.dirpath = nfs->server_path;
266 arglen = nfs_pack_mountargs(sendbuf, &args);
268 err = rpc_call(&nfs->rpc, MOUNTPROG, MOUNTVERS, MOUNTPROC_UMNT, sendbuf, arglen, NULL, 0);
270 return err;
273 int nfs_mount(fs_cookie *fs, fs_id id, const char *device, void *args, vnode_id *root_vnid)
275 nfs_fs *nfs;
276 int err;
277 char ip_addr_str[128];
278 ipv4_addr ip_addr;
280 TRACE("nfs_mount: fsid 0x%x, device '%s'\n", id, device);
282 /* create the fs structure */
283 nfs = kmalloc(sizeof(nfs_fs));
284 if(!nfs) {
285 err = ERR_NO_MEMORY;
286 goto err;
288 memset(nfs, 0, sizeof(nfs_fs));
290 mutex_init(&nfs->lock, "nfs lock");
292 err = parse_mount(device, ip_addr_str, sizeof(ip_addr_str), nfs->server_path, sizeof(nfs->server_path));
293 if(err < 0) {
294 err = ERR_NET_BAD_ADDRESS;
295 goto err1;
298 err = parse_ipv4_addr_str(&ip_addr, ip_addr_str);
299 if(err < 0) {
300 err = ERR_NET_BAD_ADDRESS;
301 goto err1;
304 nfs->id = id;
305 nfs->server_addr.type = ADDR_TYPE_IP;
306 nfs->server_addr.len = 4;
307 NETADDR_TO_IPV4(nfs->server_addr) = ip_addr;
309 // set up the rpc state
310 rpc_init_state(&nfs->rpc);
311 rpc_open_socket(&nfs->rpc, &nfs->server_addr);
313 // look up the port numbers for mount and nfs
314 rpc_pmap_lookup(&nfs->server_addr, MOUNTPROG, MOUNTVERS, IP_PROT_UDP, &nfs->mount_port);
315 rpc_pmap_lookup(&nfs->server_addr, NFSPROG, NFSVERS, IP_PROT_UDP, &nfs->nfs_port);
317 nfs->root_vnode = new_vnode_struct(nfs);
318 nfs->root_vnode->st = STREAM_TYPE_DIR;
320 // try to mount the filesystem
321 err = nfs_mount_fs(nfs, nfs->server_path);
322 if(err < 0)
323 goto err2;
325 // build the vnode hash table and stick the root vnode in it
326 nfs->handle_hash = hash_init(1024, offsetof(struct nfs_vnode, hash_next),
327 nfs_handle_hash_compare,
328 nfs_handle_hash);
329 if (!nfs->handle_hash) {
330 err = ERR_NO_MEMORY;
331 goto err2;
333 hash_insert(nfs->handle_hash, nfs->root_vnode);
335 *fs = nfs;
336 *root_vnid = VNODETOVNID(nfs->root_vnode);
338 return 0;
340 err2:
341 destroy_vnode_struct(nfs->root_vnode);
342 rpc_destroy_state(&nfs->rpc);
343 err1:
344 mutex_destroy(&nfs->lock);
345 kfree(nfs);
346 err:
347 return err;
350 int nfs_unmount(fs_cookie fs)
352 nfs_fs *nfs = (nfs_fs *)fs;
354 TRACE("nfs_unmount: fsid 0x%x\n", nfs->id);
356 // put_vnode on the root to release the ref to it
357 vfs_put_vnode(nfs->id, VNODETOVNID(nfs->root_vnode));
359 nfs_unmount_fs(nfs);
361 hash_uninit(nfs->handle_hash);
363 rpc_destroy_state(&nfs->rpc);
365 mutex_destroy(&nfs->lock);
366 kfree(nfs);
368 return 0;
371 int nfs_sync(fs_cookie fs)
373 nfs_fs *nfs = (nfs_fs *)fs;
375 TOUCH(nfs);
377 TRACE("nfs_sync: fsid 0x%x\n", nfs->id);
378 return 0;
381 int nfs_lookup(fs_cookie fs, fs_vnode _dir, const char *name, vnode_id *id)
383 nfs_fs *nfs = (nfs_fs *)fs;
384 nfs_vnode *dir = (nfs_vnode *)_dir;
385 int err;
387 TRACE("nfs_lookup: fsid 0x%x, dirvnid 0x%Lx, name '%s'\n", nfs->id, VNODETOVNID(dir), name);
389 mutex_lock(&dir->lock);
392 uint8 sendbuf[NFS_DIROPARGS_MAXLEN];
393 nfs_diropargs args;
394 size_t arglen;
396 uint8 resbuf[NFS_DIROPRES_MAXLEN];
397 nfs_diropres res;
399 /* set up the args structure */
400 args.dir = &dir->nfs_handle;
401 args.name.name = name;
402 arglen = nfs_pack_diropargs(sendbuf, &args);
404 err = rpc_call(&nfs->rpc, NFSPROG, NFSVERS, NFSPROC_LOOKUP, sendbuf, arglen, resbuf, sizeof(resbuf));
405 if(err < 0) {
406 err = ERR_NOT_FOUND;
407 goto out;
410 nfs_unpack_diropres(resbuf, &res);
412 /* see if the lookup was successful */
413 if(res.status == NFS_OK) {
414 nfs_vnode *v;
415 nfs_vnode *v2;
416 bool newvnode;
418 /* successful lookup */
419 #if NFS_TRACE
420 dprintf("nfs_lookup: result of lookup of '%s'\n", name);
421 dprintf("\tfhandle: "); dump_fhandle(res.file); dprintf("\n");
422 dprintf("\tsize: %d\n", res.attributes->size);
423 nfs_handle_hash(NULL, res.file, 1024);
424 #endif
426 /* see if the vnode already exists */
427 newvnode = false;
428 v = hash_lookup(nfs->handle_hash, res.file);
429 if (v == NULL) {
430 /* didn't find it, create a new one */
431 v = new_vnode_struct(nfs);
432 if(v == NULL) {
433 err = ERR_NO_MEMORY;
434 goto out;
437 /* copy the file handle over */
438 memcpy(&v->nfs_handle, res.file, sizeof(v->nfs_handle));
440 /* figure out the stream type from the return value and cache it */
441 switch(res.attributes->ftype) {
442 case NFREG:
443 v->st = STREAM_TYPE_FILE;
444 break;
445 case NFDIR:
446 v->st = STREAM_TYPE_DIR;
447 break;
448 default:
449 v->st = -1;
452 /* add it to the handle -> vnode lookup table */
453 mutex_lock(&nfs->lock);
454 hash_insert(nfs->handle_hash, v);
455 mutex_unlock(&nfs->lock);
456 newvnode = true;
459 /* request that the vfs layer look it up */
460 err = vfs_get_vnode(nfs->id, VNODETOVNID(v), (fs_vnode *)(void *)&v2);
461 if(err < 0) {
462 if (newvnode) {
463 mutex_lock(&nfs->lock);
464 hash_remove(nfs->handle_hash, v);
465 mutex_unlock(&nfs->lock);
466 destroy_vnode_struct(v);
468 err = ERR_NOT_FOUND;
469 goto out;
472 ASSERT(v == v2);
474 *id = VNODETOVNID(v);
475 } else {
476 TRACE("nfs_lookup: '%s' not found\n", name);
477 err = ERR_NOT_FOUND;
478 goto out;
482 err = NO_ERROR;
484 out:
485 mutex_unlock(&dir->lock);
487 return err;
490 int nfs_getvnode(fs_cookie fs, vnode_id id, fs_vnode *v, bool r)
492 nfs_fs *nfs = (nfs_fs *)fs;
494 TOUCH(nfs);
496 TRACE("nfs_getvnode: fsid 0x%x, vnid 0x%Lx\n", nfs->id, id);
498 *v = VNIDTOVNODE(id);
500 return NO_ERROR;
503 int nfs_putvnode(fs_cookie fs, fs_vnode _v, bool r)
505 nfs_fs *nfs = (nfs_fs *)fs;
506 nfs_vnode *v = (nfs_vnode *)_v;
508 TOUCH(nfs);
510 TRACE("nfs_putvnode: fsid 0x%x, vnid 0x%Lx\n", nfs->id, VNODETOVNID(v));
512 mutex_lock(&nfs->lock);
513 hash_remove(nfs->handle_hash, v);
514 mutex_unlock(&nfs->lock);
515 destroy_vnode_struct(v);
517 return NO_ERROR;
520 int nfs_removevnode(fs_cookie fs, fs_vnode _v, bool r)
522 nfs_fs *nfs = (nfs_fs *)fs;
523 nfs_vnode *v = (nfs_vnode *)_v;
525 TOUCH(nfs);TOUCH(v);
527 TRACE("nfs_removevnode: fsid 0x%x, vnid 0x%Lx\n", nfs->id, VNODETOVNID(v));
529 return ERR_UNIMPLEMENTED;
532 static int nfs_opendir(fs_cookie _fs, fs_vnode _v, dir_cookie *_cookie)
534 struct nfs_vnode *v = (struct nfs_vnode *)_v;
535 struct nfs_cookie *cookie;
536 int err = 0;
538 TRACE("nfs_opendir: vnode %p\n", v);
540 if(v->st != STREAM_TYPE_DIR)
541 return ERR_VFS_NOT_DIR;
543 cookie = kmalloc(sizeof(struct nfs_cookie));
544 if(cookie == NULL)
545 return ERR_NO_MEMORY;
547 cookie->v = v;
548 cookie->u.dir.nfscookie = 0;
549 cookie->u.dir.at_end = false;
551 *_cookie = cookie;
553 return err;
556 static int nfs_closedir(fs_cookie _fs, fs_vnode _v, dir_cookie _cookie)
558 struct nfs_fs *fs = _fs;
559 struct nfs_vnode *v = _v;
560 struct nfs_cookie *cookie = _cookie;
562 TOUCH(fs);TOUCH(v);TOUCH(cookie);
564 TRACE("nfs_closedir: entry vnode %p, cookie %p\n", v, cookie);
566 if(v->st != STREAM_TYPE_DIR)
567 return ERR_VFS_NOT_DIR;
569 if(cookie)
570 kfree(cookie);
572 return 0;
575 static int nfs_rewinddir(fs_cookie _fs, fs_vnode _v, dir_cookie _cookie)
577 struct nfs_vnode *v = _v;
578 struct nfs_cookie *cookie = _cookie;
579 int err = 0;
581 TOUCH(v);
583 TRACE("nfs_rewinddir: vnode %p, cookie %p\n", v, cookie);
585 if(v->st != STREAM_TYPE_DIR)
586 return ERR_VFS_NOT_DIR;
588 mutex_lock(&v->lock);
590 cookie->u.dir.nfscookie = 0;
591 cookie->u.dir.at_end = false;
593 mutex_unlock(&v->lock);
595 return err;
598 #define READDIR_BUF_SIZE (MAXNAMLEN + 64)
600 static ssize_t _nfs_readdir(nfs_fs *nfs, nfs_vnode *v, nfs_cookie *cookie, void *buf, ssize_t len)
602 uint8 resbuf[READDIR_BUF_SIZE];
603 nfs_readdirres *res = (nfs_readdirres *)resbuf;
604 uint8 argbuf[NFS_READDIRARGS_MAXLEN];
605 nfs_readdirargs args;
606 size_t arglen;
607 ssize_t err = 0;
608 int i;
609 int namelen;
611 if(len < MAXNAMLEN)
612 return ERR_VFS_INSUFFICIENT_BUF; // XXX not quite accurate
614 /* see if we've already hit the end */
615 if(cookie->u.dir.at_end)
616 return 0;
618 /* put together the message */
619 args.dir = &v->nfs_handle;
620 args.cookie = cookie->u.dir.nfscookie;
621 args.count = min(len, READDIR_BUF_SIZE);
622 arglen = nfs_pack_readdirargs(argbuf, &args);
624 err = rpc_call(&nfs->rpc, NFSPROG, NFSVERS, NFSPROC_READDIR, argbuf, arglen, resbuf, sizeof(resbuf));
625 if(err < 0)
626 return err;
628 /* get response */
629 if(ntohl(res->status) != NFS_OK)
630 return 0;
632 /* walk into the buffer, looking for the first entry */
633 if(ntohl(res->data[0]) == 0) {
634 // end of list
635 cookie->u.dir.at_end = true;
636 return 0;
638 i = ntohl(res->data[0]);
640 /* copy the data out of the first entry */
641 strlcpy(buf, (char const *)&res->data[i + 2], ntohl(res->data[i + 1]) + 1);
643 namelen = ROUNDUP(ntohl(res->data[i + 1]), 4);
645 /* update the cookie */
646 cookie->u.dir.nfscookie = res->data[i + namelen / 4 + 2];
648 return ntohl(res->data[i + 1]);
651 static int nfs_readdir(fs_cookie _fs, fs_vnode _v, dir_cookie _cookie, void *buf, size_t len)
653 struct nfs_fs *fs = _fs;
654 struct nfs_vnode *v = _v;
655 struct nfs_cookie *cookie = _cookie;
656 int err = 0;
658 TOUCH(v);
660 TRACE("nfs_readdir: vnode %p, cookie %p, len 0x%x\n", v, cookie, len);
662 if(v->st != STREAM_TYPE_DIR)
663 return ERR_VFS_NOT_DIR;
665 mutex_lock(&v->lock);
667 err = _nfs_readdir(fs, v, cookie, buf, len);
669 mutex_unlock(&v->lock);
671 return err;
674 int nfs_open(fs_cookie fs, fs_vnode _v, file_cookie *_cookie, int oflags)
676 nfs_fs *nfs = (nfs_fs *)fs;
677 nfs_vnode *v = (nfs_vnode *)_v;
678 nfs_cookie *cookie;
679 int err;
681 TOUCH(nfs);
683 TRACE("nfs_open: fsid 0x%x, vnid 0x%Lx, oflags 0x%x\n", nfs->id, VNODETOVNID(v), oflags);
685 if(v->st == STREAM_TYPE_DIR) {
686 err = ERR_VFS_IS_DIR;
687 goto err;
690 cookie = kmalloc(sizeof(nfs_cookie));
691 if(cookie == NULL) {
692 err = ERR_NO_MEMORY;
693 goto err;
695 cookie->v = v;
697 cookie->u.file.pos = 0;
698 cookie->u.file.oflags = oflags;
700 *_cookie = (file_cookie)cookie;
701 err = NO_ERROR;
703 err:
704 return err;
707 int nfs_close(fs_cookie fs, fs_vnode _v, file_cookie _cookie)
709 nfs_fs *nfs = (nfs_fs *)fs;
710 nfs_vnode *v = (nfs_vnode *)_v;
712 TOUCH(nfs);TOUCH(v);
714 TRACE("nfs_close: fsid 0x%x, vnid 0x%Lx\n", nfs->id, VNODETOVNID(v));
716 if(v->st == STREAM_TYPE_DIR)
717 return ERR_VFS_IS_DIR;
719 return NO_ERROR;
722 int nfs_freecookie(fs_cookie fs, fs_vnode _v, file_cookie _cookie)
724 nfs_fs *nfs = (nfs_fs *)fs;
725 nfs_vnode *v = (nfs_vnode *)_v;
726 nfs_cookie *cookie = (nfs_cookie *)_cookie;
728 TOUCH(nfs);TOUCH(v);
730 TRACE("nfs_freecookie: fsid 0x%x, vnid 0x%Lx\n", nfs->id, VNODETOVNID(v));
732 if(v->st == STREAM_TYPE_DIR)
733 return ERR_VFS_IS_DIR;
735 kfree(cookie);
737 return NO_ERROR;
740 int nfs_fsync(fs_cookie fs, fs_vnode _v)
742 nfs_fs *nfs = (nfs_fs *)fs;
743 nfs_vnode *v = (nfs_vnode *)_v;
745 TOUCH(nfs);TOUCH(v);
747 TRACE("nfs_fsync: fsid 0x%x, vnid 0x%Lx\n", nfs->id, VNODETOVNID(v));
749 return NO_ERROR;
752 #define READ_BUF_SIZE 1024
754 static ssize_t nfs_readfile(nfs_fs *nfs, nfs_vnode *v, nfs_cookie *cookie, void *buf, off_t pos, ssize_t len, bool updatecookiepos)
756 uint8 resbuf[NFS_READRES_MAXLEN + READ_BUF_SIZE];
757 nfs_readres res;
759 uint8 argbuf[NFS_READARGS_MAXLEN];
760 nfs_readargs args;
761 size_t arglen;
762 int err;
763 ssize_t total_read = 0;
765 TRACE("nfs_readfile: v %p, buf %p, pos %Ld, len %d\n", v, buf, pos, len);
767 /* check args */
768 if(pos < 0)
769 pos = cookie->u.file.pos;
770 /* can't do more than 32-bit offsets right now */
771 if(pos > 0xffffffff)
772 return 0;
773 /* negative or zero length means nothing */
774 if(len <= 0)
775 return 0;
777 while(len > 0) {
778 ssize_t to_read = min(len, READ_BUF_SIZE);
780 /* put together the message */
781 args.file = &v->nfs_handle;
782 args.offset = pos;
783 args.count = to_read;
784 args.totalcount = 0; // unused
785 arglen = nfs_pack_readargs(argbuf, &args);
787 err = rpc_call(&nfs->rpc, NFSPROG, NFSVERS, NFSPROC_READ, argbuf, arglen, resbuf, sizeof(resbuf));
788 if(err < 0)
789 break;
791 nfs_unpack_readres(resbuf, &res);
793 /* get response */
794 if(res.status != NFS_OK)
795 break;
797 /* see how much we read */
798 err = user_memcpy((uint8 *)buf + total_read, res.data, res.len);
799 if(err < 0) {
800 total_read = err; // bad user give me bad buffer
801 break;
804 pos += res.len;
805 len -= res.len;
806 total_read += res.len;
808 /* short read, we're done */
809 if((ssize_t)res.len != to_read)
810 break;
813 if (updatecookiepos)
814 cookie->u.file.pos = pos;
816 return total_read;
819 ssize_t nfs_read(fs_cookie fs, fs_vnode _v, file_cookie _cookie, void *buf, off_t pos, ssize_t len)
821 nfs_fs *nfs = (nfs_fs *)fs;
822 nfs_vnode *v = (nfs_vnode *)_v;
823 nfs_cookie *cookie = (nfs_cookie *)_cookie;
824 ssize_t err;
826 TRACE("nfs_read: fsid 0x%x, vnid 0x%Lx, buf %p, pos 0x%Lx, len %ld\n", nfs->id, VNODETOVNID(v), buf, pos, len);
828 if(v->st == STREAM_TYPE_DIR)
829 return ERR_VFS_IS_DIR;
831 mutex_lock(&v->lock);
833 err = nfs_readfile(nfs, v, cookie, buf, pos, len, true);
835 mutex_unlock(&v->lock);
837 return err;
840 #define WRITE_BUF_SIZE 1024
842 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)
844 #if 0
845 uint8 abuf[sizeof(nfs_writeargs) + WRITE_BUF_SIZE];
846 nfs_writeargs *args = (nfs_writeargs *)abuf;
847 nfs_attrstat *res = (nfs_attrstat *)abuf;
848 int err;
849 ssize_t total_written = 0;
851 /* check args */
852 if(pos < 0)
853 pos = cookie->u.file.pos;
854 /* can't do more than 32-bit offsets right now */
855 if(pos > 0xffffffff)
856 return 0;
857 /* negative or zero length means nothing */
858 if(len <= 0)
859 return 0;
861 while(len > 0) {
862 ssize_t to_write = min(len, WRITE_BUF_SIZE);
864 /* put together the message */
865 memcpy(&args->file, &v->nfs_handle, sizeof(args->file));
866 args->beginoffset = 0; // unused
867 args->offset = htonl(pos);
868 args->totalcount = htonl(to_write);
869 err = user_memcpy(args->data, (const uint8 *)buf + total_written, to_write);
870 if(err < 0) {
871 total_written = err;
872 break;
875 err = rpc_call(&nfs->rpc, NFSPROG, NFSVERS, NFSPROC_WRITE, args, sizeof(*args) + to_write, abuf, sizeof(abuf));
876 if(err < 0)
877 break;
879 /* get response */
880 if(ntohl(res->status) != NFS_OK)
881 break;
883 pos += to_write;
884 len -= to_write;
885 total_written += to_write;
888 if (updatecookiepos)
889 cookie->u.file.pos = pos;
891 return total_written;
892 #endif
893 return ERR_UNIMPLEMENTED;
896 ssize_t nfs_write(fs_cookie fs, fs_vnode _v, file_cookie _cookie, const void *buf, off_t pos, ssize_t len)
898 nfs_fs *nfs = (nfs_fs *)fs;
899 nfs_vnode *v = (nfs_vnode *)_v;
900 nfs_cookie *cookie = (nfs_cookie *)_cookie;
901 ssize_t err;
903 TRACE("nfs_write: fsid 0x%x, vnid 0x%Lx, buf %p, pos 0x%Lx, len %ld\n", nfs->id, VNODETOVNID(v), buf, pos, len);
905 mutex_lock(&v->lock);
907 switch(v->st) {
908 case STREAM_TYPE_FILE:
909 err = nfs_writefile(nfs, v, cookie, buf, pos, len, true);
910 break;
911 case STREAM_TYPE_DIR:
912 err = ERR_NOT_ALLOWED;
913 break;
914 default:
915 err = ERR_GENERAL;
918 mutex_unlock(&v->lock);
920 return err;
923 int nfs_seek(fs_cookie fs, fs_vnode _v, file_cookie _cookie, off_t pos, seek_type st)
925 nfs_fs *nfs = (nfs_fs *)fs;
926 nfs_vnode *v = (nfs_vnode *)_v;
927 nfs_cookie *cookie = (nfs_cookie *)_cookie;
928 int err = NO_ERROR;
929 uint8 attrstatbuf[NFS_ATTRSTAT_MAXLEN];
930 nfs_attrstat attrstat;
931 off_t file_len;
933 TRACE("nfs_seek: fsid 0x%x, vnid 0x%Lx, pos 0x%Lx, seek_type %d\n", nfs->id, VNODETOVNID(v), pos, st);
935 if(v->st == STREAM_TYPE_DIR)
936 return ERR_VFS_IS_DIR;
938 mutex_lock(&v->lock);
940 err = nfs_getattr(nfs, v, attrstatbuf, &attrstat);
941 if(err < 0)
942 goto out;
944 file_len = attrstat.attributes->size;
946 switch(st) {
947 case _SEEK_SET:
948 if(pos < 0)
949 pos = 0;
950 if(pos > file_len)
951 pos = file_len;
952 cookie->u.file.pos = pos;
953 break;
954 case _SEEK_CUR:
955 if(pos + cookie->u.file.pos > file_len)
956 cookie->u.file.pos = file_len;
957 else if(pos + cookie->u.file.pos < 0)
958 cookie->u.file.pos = 0;
959 else
960 cookie->u.file.pos += pos;
961 break;
962 case _SEEK_END:
963 if(pos > 0)
964 cookie->u.file.pos = file_len;
965 else if(pos + file_len < 0)
966 cookie->u.file.pos = 0;
967 else
968 cookie->u.file.pos = pos + file_len;
969 break;
970 default:
971 err = ERR_INVALID_ARGS;
974 out:
975 mutex_unlock(&v->lock);
977 return err;
980 int nfs_ioctl(fs_cookie fs, fs_vnode _v, file_cookie cookie, int op, void *buf, size_t len)
982 nfs_fs *nfs = (nfs_fs *)fs;
983 nfs_vnode *v = (nfs_vnode *)_v;
985 TOUCH(nfs);TOUCH(v);
987 TRACE("nfs_ioctl: fsid 0x%x, vnid 0x%Lx, op %d, buf %p, len %ld\n", nfs->id, VNODETOVNID(v), op, buf, len);
989 return ERR_UNIMPLEMENTED;
992 int nfs_canpage(fs_cookie fs, fs_vnode _v)
994 nfs_fs *nfs = (nfs_fs *)fs;
995 nfs_vnode *v = (nfs_vnode *)_v;
997 TOUCH(nfs);TOUCH(v);
999 TRACE("nfs_canpage: fsid 0x%x, vnid 0x%Lx\n", nfs->id, VNODETOVNID(v));
1001 if(v->st == STREAM_TYPE_FILE)
1002 return 1;
1003 else
1004 return 0;
1007 ssize_t nfs_readpage(fs_cookie fs, fs_vnode _v, iovecs *vecs, off_t pos)
1009 nfs_fs *nfs = (nfs_fs *)fs;
1010 nfs_vnode *v = (nfs_vnode *)_v;
1011 unsigned int i;
1012 ssize_t readfile_return;
1013 ssize_t total_bytes_read = 0;
1015 TOUCH(nfs);TOUCH(v);
1017 TRACE("nfs_readpage: fsid 0x%x, vnid 0x%Lx, vecs %p, pos 0x%Lx\n", nfs->id, VNODETOVNID(v), vecs, pos);
1019 if(v->st == STREAM_TYPE_DIR)
1020 return ERR_VFS_IS_DIR;
1022 mutex_lock(&v->lock);
1024 for (i=0; i < vecs->num; i++) {
1025 readfile_return = nfs_readfile(nfs, v, NULL, vecs->vec[i].start, pos, vecs->vec[i].len, false);
1026 TRACE("nfs_readpage: nfs_readfile returns %d\n", readfile_return);
1027 if (readfile_return < 0)
1028 goto out;
1030 pos += readfile_return;
1031 total_bytes_read += readfile_return;
1033 if ((size_t)readfile_return < vecs->vec[i].len) {
1034 /* we have hit the end of file, zero out the rest */
1035 for (; i < vecs->num; i++) {
1036 memset(vecs->vec[i].start + readfile_return, 0, vecs->vec[i].len - readfile_return);
1037 total_bytes_read += vecs->vec[i].len - readfile_return;
1039 readfile_return = 0; // after the first pass, wipe out entire pages
1044 out:
1045 mutex_unlock(&v->lock);
1047 return total_bytes_read;
1050 ssize_t nfs_writepage(fs_cookie fs, fs_vnode _v, iovecs *vecs, off_t pos)
1052 nfs_fs *nfs = (nfs_fs *)fs;
1053 nfs_vnode *v = (nfs_vnode *)_v;
1055 TOUCH(nfs);TOUCH(v);
1057 TRACE("nfs_writepage: fsid 0x%x, vnid 0x%Lx, vecs %p, pos 0x%Lx\n", nfs->id, VNODETOVNID(v), vecs, pos);
1059 if(v->st == STREAM_TYPE_DIR)
1060 return ERR_VFS_IS_DIR;
1062 return ERR_UNIMPLEMENTED;
1065 static int _nfs_create(nfs_fs *nfs, nfs_vnode *dir, const char *name, stream_type type, vnode_id *new_vnid)
1067 int err;
1068 uint8 argbuf[NFS_CREATEARGS_MAXLEN];
1069 nfs_createargs args;
1070 size_t arglen;
1071 uint8 resbuf[NFS_DIROPRES_MAXLEN];
1072 nfs_diropres res;
1073 nfs_vnode *v, *v2;
1074 int proc;
1076 /* start building the args */
1077 args.where.dir = &dir->nfs_handle;
1078 args.where.name.name = name;
1079 args.attributes.mode = 0777;
1080 args.attributes.uid = 0;
1081 args.attributes.gid = 0;
1082 args.attributes.size = 0;
1083 args.attributes.atime.seconds = 0;
1084 args.attributes.atime.useconds = 0;
1085 args.attributes.mtime.seconds = 0;
1086 args.attributes.mtime.useconds = 0;
1087 arglen = nfs_pack_createopargs(argbuf, &args);
1089 switch (type) {
1090 case STREAM_TYPE_FILE:
1091 proc = NFSPROC_CREATE;
1092 break;
1093 case STREAM_TYPE_DIR:
1094 proc = NFSPROC_MKDIR;
1095 break;
1096 default:
1097 panic("_nfs_create asked to make file type it doesn't understand\n");
1100 err = rpc_call(&nfs->rpc, NFSPROG, NFSVERS, proc, argbuf, arglen, resbuf, sizeof(resbuf));
1101 if (err < 0)
1102 return err;
1104 nfs_unpack_diropres(resbuf, &res);
1106 if (res.status != NFS_OK) {
1107 err = nfs_status_to_error(res.status);
1108 goto err;
1111 /* if new_vnid is null, the layers above us aren't requesting that we bring the vnode into existence */
1112 if (new_vnid == NULL) {
1113 err = 0;
1114 goto out;
1117 /* create a new vnode */
1118 v = new_vnode_struct(nfs);
1119 if (v == NULL) {
1120 err = ERR_NO_MEMORY;
1121 /* weird state here. we've created a file but failed */
1122 goto err;
1125 /* copy the file handle over */
1126 memcpy(&v->nfs_handle, res.file, sizeof(v->nfs_handle));
1128 /* figure out the stream type from the return value and cache it */
1129 switch (res.attributes->ftype) {
1130 case NFREG:
1131 v->st = STREAM_TYPE_FILE;
1132 break;
1133 case NFDIR:
1134 v->st = STREAM_TYPE_DIR;
1135 break;
1136 default:
1137 v->st = -1;
1140 /* add it to the handle -> vnode lookup table */
1141 mutex_lock(&nfs->lock);
1142 hash_insert(nfs->handle_hash, v);
1143 mutex_unlock(&nfs->lock);
1145 /* request that the vfs layer look it up */
1146 err = vfs_get_vnode(nfs->id, VNODETOVNID(v), (fs_vnode *)(void *)&v2);
1147 if (err < 0) {
1148 mutex_lock(&nfs->lock);
1149 hash_remove(nfs->handle_hash, v);
1150 mutex_unlock(&nfs->lock);
1151 destroy_vnode_struct(v);
1152 err = ERR_NOT_FOUND;
1153 goto err;
1156 *new_vnid = VNODETOVNID(v);
1158 err = 0;
1160 out:
1161 err:
1162 return err;
1165 int nfs_create(fs_cookie fs, fs_vnode _dir, const char *name, void *create_args, vnode_id *new_vnid)
1167 nfs_fs *nfs = (nfs_fs *)fs;
1168 nfs_vnode *dir = (nfs_vnode *)_dir;
1169 int err;
1171 TOUCH(nfs);TOUCH(dir);
1173 TRACE("nfs_create: fsid 0x%x, vnid 0x%Lx, name '%s'\n", nfs->id, VNODETOVNID(dir), name);
1175 mutex_lock(&dir->lock);
1177 err = _nfs_create(nfs, dir, name, STREAM_TYPE_FILE, new_vnid);
1179 mutex_unlock(&dir->lock);
1181 return err;
1184 static int _nfs_unlink(nfs_fs *nfs, nfs_vnode *dir, const char *name, stream_type type)
1186 int err;
1187 uint8 argbuf[NFS_DIROPARGS_MAXLEN];
1188 nfs_diropargs args;
1189 size_t arglen;
1190 nfs_status res;
1191 int proc;
1193 /* start building the args */
1194 args.dir = &dir->nfs_handle;
1195 args.name.name = name;
1196 arglen = nfs_pack_diropargs(argbuf, &args);
1198 switch (type) {
1199 case STREAM_TYPE_FILE:
1200 proc = NFSPROC_REMOVE;
1201 break;
1202 case STREAM_TYPE_DIR:
1203 proc = NFSPROC_RMDIR;
1204 break;
1205 default:
1206 panic("_nfs_unlink asked to remove file type it doesn't understand\n");
1209 err = rpc_call(&nfs->rpc, NFSPROG, NFSVERS, proc, argbuf, arglen, &res, sizeof(res));
1210 if (err < 0)
1211 return err;
1213 return nfs_status_to_error(ntohl(res));
1216 int nfs_unlink(fs_cookie fs, fs_vnode _dir, const char *name)
1218 nfs_fs *nfs = (nfs_fs *)fs;
1219 nfs_vnode *dir = (nfs_vnode *)_dir;
1220 int err;
1222 TOUCH(nfs);TOUCH(dir);
1224 TRACE("nfs_unlink: fsid 0x%x, vnid 0x%Lx, name '%s'\n", nfs->id, VNODETOVNID(dir), name);
1226 mutex_lock(&dir->lock);
1228 err = _nfs_unlink(nfs, dir, name, STREAM_TYPE_FILE);
1230 mutex_unlock(&dir->lock);
1232 return err;
1235 int nfs_rename(fs_cookie fs, fs_vnode _olddir, const char *oldname, fs_vnode _newdir, const char *newname)
1237 nfs_fs *nfs = (nfs_fs *)fs;
1238 nfs_vnode *olddir = (nfs_vnode *)_olddir;
1239 nfs_vnode *newdir = (nfs_vnode *)_newdir;
1241 TOUCH(nfs);TOUCH(olddir);TOUCH(newdir);
1243 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);
1245 return ERR_UNIMPLEMENTED;
1248 int nfs_mkdir(fs_cookie _fs, fs_vnode _base_dir, const char *name)
1250 nfs_fs *nfs = (nfs_fs *)_fs;
1251 nfs_vnode *dir = (nfs_vnode *)_base_dir;
1252 int err;
1254 TOUCH(nfs);TOUCH(dir);
1256 TRACE("nfs_mkdir: fsid 0x%x, vnid 0x%Lx, name '%s'\n", nfs->id, VNODETOVNID(dir), name);
1258 mutex_lock(&dir->lock);
1260 err = _nfs_create(nfs, dir, name, STREAM_TYPE_DIR, NULL);
1262 mutex_unlock(&dir->lock);
1264 return err;
1267 int nfs_rmdir(fs_cookie _fs, fs_vnode _base_dir, const char *name)
1269 nfs_fs *nfs = (nfs_fs *)_fs;
1270 nfs_vnode *dir = (nfs_vnode *)_base_dir;
1271 int err;
1273 TOUCH(nfs);TOUCH(dir);
1275 TRACE("nfs_rmdir: fsid 0x%x, vnid 0x%Lx, name '%s'\n", nfs->id, VNODETOVNID(dir), name);
1277 mutex_lock(&dir->lock);
1279 err = _nfs_unlink(nfs, dir, name, STREAM_TYPE_DIR);
1281 mutex_unlock(&dir->lock);
1283 return err;
1286 static int nfs_getattr(nfs_fs *nfs, nfs_vnode *v, uint8 *buf, nfs_attrstat *attrstat)
1288 int err;
1290 err = rpc_call(&nfs->rpc, NFSPROG, NFSVERS, NFSPROC_GETATTR, &v->nfs_handle, sizeof(v->nfs_handle), buf, NFS_ATTRSTAT_MAXLEN);
1291 if (err < 0)
1292 return err;
1294 nfs_unpack_attrstat(buf, attrstat);
1296 return nfs_status_to_error(attrstat->status);
1299 int nfs_rstat(fs_cookie fs, fs_vnode _v, struct file_stat *stat)
1301 nfs_fs *nfs = (nfs_fs *)fs;
1302 nfs_vnode *v = (nfs_vnode *)_v;
1303 uint8 attrstatbuf[NFS_ATTRSTAT_MAXLEN];
1304 nfs_attrstat attrstat;
1305 int err;
1307 TRACE("nfs_rstat: fsid 0x%x, vnid 0x%Lx, stat %p\n", nfs->id, VNODETOVNID(v), stat);
1309 mutex_lock(&v->lock);
1311 err = nfs_getattr(nfs, v, attrstatbuf, &attrstat);
1312 if(err < 0)
1313 goto out;
1315 if(attrstat.status != NFS_OK) {
1316 err = ERR_IO_ERROR;
1317 goto out;
1320 /* copy the stat over from the nfs attrstat */
1321 stat->vnid = VNODETOVNID(v);
1322 stat->size = attrstat.attributes->size;
1323 switch(attrstat.attributes->ftype) {
1324 case NFREG:
1325 stat->type = STREAM_TYPE_FILE;
1326 break;
1327 case NFDIR:
1328 stat->type = STREAM_TYPE_DIR;
1329 break;
1330 default:
1331 stat->type = STREAM_TYPE_DEVICE; // XXX should have unknown type
1332 break;
1335 err = NO_ERROR;
1337 out:
1338 mutex_unlock(&v->lock);
1340 return err;
1343 int nfs_wstat(fs_cookie fs, fs_vnode _v, struct file_stat *stat, int stat_mask)
1345 nfs_fs *nfs = (nfs_fs *)fs;
1346 nfs_vnode *v = (nfs_vnode *)_v;
1348 TOUCH(nfs);TOUCH(v);
1350 TRACE("nfs_wstat: fsid 0x%x, vnid 0x%Lx, stat %p, stat_mask 0x%x\n", nfs->id, VNODETOVNID(v), stat, stat_mask);
1352 return ERR_UNIMPLEMENTED;
1355 static struct fs_calls nfs_calls = {
1356 &nfs_mount,
1357 &nfs_unmount,
1358 &nfs_sync,
1360 &nfs_lookup,
1362 &nfs_getvnode,
1363 &nfs_putvnode,
1364 &nfs_removevnode,
1366 &nfs_opendir,
1367 &nfs_closedir,
1368 &nfs_rewinddir,
1369 &nfs_readdir,
1371 &nfs_open,
1372 &nfs_close,
1373 &nfs_freecookie,
1374 &nfs_fsync,
1376 &nfs_read,
1377 &nfs_write,
1378 &nfs_seek,
1379 &nfs_ioctl,
1381 &nfs_canpage,
1382 &nfs_readpage,
1383 &nfs_writepage,
1385 &nfs_create,
1386 &nfs_unlink,
1387 &nfs_rename,
1389 &nfs_mkdir,
1390 &nfs_rmdir,
1392 &nfs_rstat,
1393 &nfs_wstat
1396 int fs_bootstrap(void);
1397 int fs_bootstrap(void)
1399 dprintf("bootstrap_nfs: entry\n");
1400 return vfs_register_filesystem("nfs", &nfs_calls);