xref: /minix/sys/arch/i386/stand/libsa/nfs.c (revision f14fb602)
1 /*	$NetBSD: nfs.c,v 1.19 2011/12/25 06:09:09 tsutsui Exp $	*/
2 
3 /*-
4  *  Copyright (c) 1993 John Brezak
5  *  All rights reserved.
6  *
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions
9  *  are met:
10  *  1. Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  *  2. Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  *  3. The name of the author may not be used to endorse or promote products
16  *     derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/param.h>
32 #include <sys/time.h>
33 #include <sys/socket.h>
34 #include <sys/stat.h>
35 #ifdef _STANDALONE
36 #include <lib/libkern/libkern.h>
37 #else
38 #include <string.h>
39 #endif
40 
41 #include <netinet/in.h>
42 #include <netinet/in_systm.h>
43 
44 #include "rpcv2.h"
45 #include "nfsv2.h"
46 
47 #include <lib/libsa/stand.h>
48 #include "net.h"
49 #include "netif.h"
50 #include "nfs.h"
51 #include "rpc.h"
52 
53 /* Define our own NFS attributes. */
54 struct nfsv2_fattrs {
55 	n_long	fa_type;
56 	n_long	fa_mode;
57 	n_long	fa_nlink;
58 	n_long	fa_uid;
59 	n_long	fa_gid;
60 	n_long	fa_size;
61 	n_long	fa_blocksize;
62 	n_long	fa_rdev;
63 	n_long	fa_blocks;
64 	n_long	fa_fsid;
65 	n_long	fa_fileid;
66 	struct nfsv2_time fa_atime;
67 	struct nfsv2_time fa_mtime;
68 	struct nfsv2_time fa_ctime;
69 };
70 
71 
72 struct nfs_read_args {
73 	u_char	fh[NFS_FHSIZE];
74 	n_long	off;
75 	n_long	len;
76 	n_long	xxx;			/* XXX what's this for? */
77 };
78 
79 /* Data part of nfs rpc reply (also the largest thing we receive) */
80 #define NFSREAD_SIZE 1024
81 struct nfs_read_repl {
82 	n_long	errno;
83 	struct	nfsv2_fattrs fa;
84 	n_long	count;
85 	u_char	data[NFSREAD_SIZE];
86 };
87 
88 #ifndef NFS_NOSYMLINK
89 struct nfs_readlnk_repl {
90 	n_long	errno;
91 	n_long	len;
92 	char	path[NFS_MAXPATHLEN];
93 };
94 #endif
95 
96 struct nfs_iodesc {
97 	struct	iodesc	*iodesc;
98 	off_t	off;
99 	u_char	fh[NFS_FHSIZE];
100 	struct nfsv2_fattrs fa;	/* all in network order */
101 };
102 
103 int	nfs_getrootfh(struct iodesc *, char *, u_char *);
104 int	nfs_lookupfh(struct nfs_iodesc *, const char *, int,
105 	    struct nfs_iodesc *);
106 #ifndef NFS_NOSYMLINK
107 int	nfs_readlink(struct nfs_iodesc *, char *);
108 #endif
109 ssize_t	nfs_readdata(struct nfs_iodesc *, off_t, void *, size_t);
110 
111 /*
112  * Fetch the root file handle (call mount daemon)
113  * Return zero or error number.
114  */
115 int
nfs_getrootfh(struct iodesc * d,char * path,u_char * fhp)116 nfs_getrootfh(struct iodesc *d, char *path, u_char *fhp)
117 {
118 	size_t len;
119 	struct args {
120 		n_long	len;
121 		char	path[FNAME_SIZE];
122 	} *args;
123 	struct repl {
124 		n_long	errno;
125 		u_char	fh[NFS_FHSIZE];
126 	} *repl;
127 	struct {
128 		n_long	h[RPC_HEADER_WORDS];
129 		struct args d;
130 	} sdata;
131 	struct {
132 		n_long	h[RPC_HEADER_WORDS];
133 		struct repl d;
134 	} rdata;
135 	ssize_t cc;
136 
137 #ifdef NFS_DEBUG
138 	if (debug)
139 		printf("nfs_getrootfh: %s\n", path);
140 #endif
141 
142 	args = &sdata.d;
143 	repl = &rdata.d;
144 
145 	memset(args, 0, sizeof(*args));
146 	len = strlen(path);
147 	if (len > sizeof(args->path))
148 		len = sizeof(args->path);
149 	args->len = htonl(len);
150 	memcpy(args->path, path, len);
151 	len = 4 + roundup(len, 4);
152 
153 	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
154 	    args, len, repl, sizeof(*repl));
155 	if (cc == -1) {
156 		/* errno was set by rpc_call */
157 		return (errno);
158 	}
159 	if (cc < 4)
160 		return (EBADRPC);
161 	if (repl->errno)
162 		return (ntohl(repl->errno));
163 	memcpy(fhp, repl->fh, sizeof(repl->fh));
164 	return (0);
165 }
166 
167 /*
168  * Lookup a file.  Store handle and attributes.
169  * Return zero or error number.
170  */
171 int
nfs_lookupfh(struct nfs_iodesc * d,const char * name,int len,struct nfs_iodesc * newfd)172 nfs_lookupfh(struct nfs_iodesc *d, const char *name, int len, struct nfs_iodesc *newfd)
173 {
174 	int rlen;
175 	struct args {
176 		u_char	fh[NFS_FHSIZE];
177 		n_long	len;
178 		char	name[FNAME_SIZE];
179 	} *args;
180 	struct repl {
181 		n_long	errno;
182 		u_char	fh[NFS_FHSIZE];
183 		struct	nfsv2_fattrs fa;
184 	} *repl;
185 	struct {
186 		n_long	h[RPC_HEADER_WORDS];
187 		struct args d;
188 	} sdata;
189 	struct {
190 		n_long	h[RPC_HEADER_WORDS];
191 		struct repl d;
192 	} rdata;
193 	ssize_t cc;
194 
195 #ifdef NFS_DEBUG
196 	if (debug)
197 		printf("lookupfh: called\n");
198 #endif
199 
200 	args = &sdata.d;
201 	repl = &rdata.d;
202 
203 	memset(args, 0, sizeof(*args));
204 	memcpy(args->fh, d->fh, sizeof(args->fh));
205 	if ((size_t)len > sizeof(args->name))
206 		len = sizeof(args->name);
207 	memcpy(args->name, name, len);
208 	args->len = htonl(len);
209 	len = 4 + roundup(len, 4);
210 	len += NFS_FHSIZE;
211 
212 	rlen = sizeof(*repl);
213 
214 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
215 	    args, len, repl, rlen);
216 	if (cc == -1)
217 		return (errno);		/* XXX - from rpc_call */
218 	if (cc < 4)
219 		return (EIO);
220 	if (repl->errno) {
221 		/* saerrno.h now matches NFS error numbers. */
222 		return (ntohl(repl->errno));
223 	}
224 	memcpy(&newfd->fh, repl->fh, sizeof(newfd->fh));
225 	memcpy(&newfd->fa, &repl->fa, sizeof(newfd->fa));
226 	return (0);
227 }
228 
229 #ifndef NFS_NOSYMLINK
230 /*
231  * Get the destination of a symbolic link.
232  */
233 int
nfs_readlink(struct nfs_iodesc * d,char * buf)234 nfs_readlink(struct nfs_iodesc *d, char *buf)
235 {
236 	struct {
237 		n_long	h[RPC_HEADER_WORDS];
238 		u_char fh[NFS_FHSIZE];
239 	} sdata;
240 	struct {
241 		n_long	h[RPC_HEADER_WORDS];
242 		struct nfs_readlnk_repl d;
243 	} rdata;
244 	ssize_t cc;
245 
246 #ifdef NFS_DEBUG
247 	if (debug)
248 		printf("readlink: called\n");
249 #endif
250 
251 	memcpy(sdata.fh, d->fh, NFS_FHSIZE);
252 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READLINK,
253 		      sdata.fh, NFS_FHSIZE,
254 		      &rdata.d, sizeof(rdata.d));
255 	if (cc == -1)
256 		return (errno);
257 
258 	if (cc < 4)
259 		return (EIO);
260 
261 	if (rdata.d.errno)
262 		return (ntohl(rdata.d.errno));
263 
264 	rdata.d.len = ntohl(rdata.d.len);
265 	if (rdata.d.len > NFS_MAXPATHLEN)
266 		return (ENAMETOOLONG);
267 
268 	memcpy(buf, rdata.d.path, rdata.d.len);
269 	buf[rdata.d.len] = 0;
270 	return (0);
271 }
272 #endif
273 
274 /*
275  * Read data from a file.
276  * Return transfer count or -1 (and set errno)
277  */
278 ssize_t
nfs_readdata(struct nfs_iodesc * d,off_t off,void * addr,size_t len)279 nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
280 {
281 	struct nfs_read_args *args;
282 	struct nfs_read_repl *repl;
283 	struct {
284 		n_long	h[RPC_HEADER_WORDS];
285 		struct nfs_read_args d;
286 	} sdata;
287 	struct {
288 		n_long	h[RPC_HEADER_WORDS];
289 		struct nfs_read_repl d;
290 	} rdata;
291 	ssize_t cc;
292 	long x;
293 	int hlen, rlen;
294 
295 	args = &sdata.d;
296 	repl = &rdata.d;
297 
298 	memcpy(args->fh, d->fh, NFS_FHSIZE);
299 	args->off = htonl((n_long)off);
300 	if (len > NFSREAD_SIZE)
301 		len = NFSREAD_SIZE;
302 	args->len = htonl((n_long)len);
303 	args->xxx = htonl((n_long)0);
304 	hlen = sizeof(*repl) - NFSREAD_SIZE;
305 
306 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READ,
307 	    args, sizeof(*args),
308 	    repl, sizeof(*repl));
309 	if (cc == -1) {
310 		/* errno was already set by rpc_call */
311 		return (-1);
312 	}
313 	if (cc < hlen) {
314 		errno = EBADRPC;
315 		return (-1);
316 	}
317 	if (repl->errno) {
318 		errno = ntohl(repl->errno);
319 		return (-1);
320 	}
321 	rlen = cc - hlen;
322 	x = ntohl(repl->count);
323 	if (rlen < x) {
324 		printf("nfsread: short packet, %d < %ld\n", rlen, x);
325 		errno = EBADRPC;
326 		return (-1);
327 	}
328 	memcpy(addr, repl->data, x);
329 	return (x);
330 }
331 
332 /*
333  * Open a file.
334  * return zero or error number
335  */
336 __compactcall int
nfs_open(const char * path,struct open_file * f)337 nfs_open(const char *path, struct open_file *f)
338 {
339 	static struct nfs_iodesc nfs_root_node;
340 	struct iodesc *desc;
341 	struct nfs_iodesc *currfd;
342 	const char *cp;
343 #ifndef NFS_NOSYMLINK
344 	struct nfs_iodesc *newfd;
345 	struct nfsv2_fattrs *fa;
346 	const char *ncp;
347 	int c;
348 	char namebuf[NFS_MAXPATHLEN + 1];
349 	char linkbuf[NFS_MAXPATHLEN + 1];
350 	int nlinks = 0;
351 #endif
352 	int error;
353 
354 #ifdef NFS_DEBUG
355  	if (debug)
356  	    printf("nfs_open: %s\n", path);
357 #endif
358 	if (!rootpath[0]) {
359 		printf("no rootpath, no nfs\n");
360 		return (ENXIO);
361 	}
362 
363 	if (!(desc = socktodesc(*(int *)(f->f_devdata))))
364 		return (EINVAL);
365 
366 	/* Bind to a reserved port. */
367 	desc->myport = htons(--rpc_port);
368 	desc->destip = rootip;
369 	if ((error = nfs_getrootfh(desc, rootpath, nfs_root_node.fh)))
370 		return (error);
371 	nfs_root_node.iodesc = desc;
372 
373 #ifndef NFS_NOSYMLINK
374 	/* Fake up attributes for the root dir. */
375 	fa = &nfs_root_node.fa;
376 	fa->fa_type  = htonl(NFDIR);
377 	fa->fa_mode  = htonl(0755);
378 	fa->fa_nlink = htonl(2);
379 
380 	currfd = &nfs_root_node;
381 	newfd = 0;
382 
383 	cp = path;
384 	while (*cp) {
385 		/*
386 		 * Remove extra separators
387 		 */
388 		while (*cp == '/')
389 			cp++;
390 
391 		if (*cp == '\0')
392 			break;
393 		/*
394 		 * Check that current node is a directory.
395 		 */
396 		if (currfd->fa.fa_type != htonl(NFDIR)) {
397 			error = ENOTDIR;
398 			goto out;
399 		}
400 
401 		/* allocate file system specific data structure */
402 		newfd = alloc(sizeof(*newfd));
403 		newfd->iodesc = currfd->iodesc;
404 		newfd->off = 0;
405 
406 		/*
407 		 * Get next component of path name.
408 		 */
409 		{
410 			int len = 0;
411 
412 			ncp = cp;
413 			while ((c = *cp) != '\0' && c != '/') {
414 				if (++len > NFS_MAXNAMLEN) {
415 					error = ENOENT;
416 					goto out;
417 				}
418 				cp++;
419 			}
420 		}
421 
422 		/* lookup a file handle */
423 		error = nfs_lookupfh(currfd, ncp, cp - ncp, newfd);
424 		if (error)
425 			goto out;
426 
427 		/*
428 		 * Check for symbolic link
429 		 */
430 		if (newfd->fa.fa_type == htonl(NFLNK)) {
431 			int link_len, len;
432 
433 			error = nfs_readlink(newfd, linkbuf);
434 			if (error)
435 				goto out;
436 
437 			link_len = strlen(linkbuf);
438 			len = strlen(cp);
439 
440 			if (link_len + len > MAXPATHLEN
441 			    || ++nlinks > MAXSYMLINKS) {
442 				error = ENOENT;
443 				goto out;
444 			}
445 
446 			memcpy(&namebuf[link_len], cp, len + 1);
447 			memcpy(namebuf, linkbuf, link_len);
448 
449 			/*
450 			 * If absolute pathname, restart at root.
451 			 * If relative pathname, restart at parent directory.
452 			 */
453 			cp = namebuf;
454 			if (*cp == '/') {
455 				if (currfd != &nfs_root_node)
456 					dealloc(currfd, sizeof(*currfd));
457 				currfd = &nfs_root_node;
458 			}
459 
460 			dealloc(newfd, sizeof(*newfd));
461 			newfd = 0;
462 
463 			continue;
464 		}
465 
466 		if (currfd != &nfs_root_node)
467 			dealloc(currfd, sizeof(*currfd));
468 		currfd = newfd;
469 		newfd = 0;
470 	}
471 
472 	error = 0;
473 
474 out:
475 	if (newfd)
476 		dealloc(newfd, sizeof(*newfd));
477 #else
478         /* allocate file system specific data structure */
479         currfd = alloc(sizeof(*currfd));
480         currfd->iodesc = desc;
481         currfd->off = 0;
482 
483 	cp = path;
484 	/*
485 	 * Remove extra separators
486 	 */
487 	while (*cp == '/')
488 		cp++;
489 
490 	/* XXX: Check for empty path here? */
491 
492         error = nfs_lookupfh(&nfs_root_node, cp, strlen(cp), currfd);
493 #endif
494 	if (!error) {
495 		f->f_fsdata = (void *)currfd;
496 		fsmod = "nfs";
497 		return (0);
498 	}
499 
500 #ifdef NFS_DEBUG
501 	if (debug)
502 		printf("nfs_open: %s lookupfh failed: %s\n",
503 		    path, strerror(error));
504 #endif
505 #ifndef NFS_NOSYMLINK
506 	if (currfd != &nfs_root_node)
507 #endif
508 		dealloc(currfd, sizeof(*currfd));
509 
510 	return (error);
511 }
512 
513 __compactcall int
nfs_close(struct open_file * f)514 nfs_close(struct open_file *f)
515 {
516 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
517 
518 #ifdef NFS_DEBUG
519 	if (debug)
520 		printf("nfs_close: fp=0x%lx\n", (u_long)fp);
521 #endif
522 
523 	if (fp)
524 		dealloc(fp, sizeof(struct nfs_iodesc));
525 	f->f_fsdata = (void *)0;
526 
527 	return (0);
528 }
529 
530 /*
531  * read a portion of a file
532  */
533 __compactcall int
nfs_read(struct open_file * f,void * buf,size_t size,size_t * resid)534 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
535 	/* resid:	 out */
536 {
537 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
538 	ssize_t cc;
539 	char *addr = buf;
540 
541 #ifdef NFS_DEBUG
542 	if (debug)
543 		printf("nfs_read: size=%lu off=%d\n", (u_long)size,
544 		    (int)fp->off);
545 #endif
546 	while ((int)size > 0) {
547 #if !defined(LIBSA_NO_TWIDDLE)
548 		twiddle();
549 #endif
550 		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
551 		/* XXX maybe should retry on certain errors */
552 		if (cc == -1) {
553 #ifdef NFS_DEBUG
554 			if (debug)
555 				printf("nfs_read: read: %s", strerror(errno));
556 #endif
557 			return (errno);	/* XXX - from nfs_readdata */
558 		}
559 		if (cc == 0) {
560 #ifdef NFS_DEBUG
561 			if (debug)
562 				printf("nfs_read: hit EOF unexpectantly");
563 #endif
564 			goto ret;
565 		}
566 		fp->off += cc;
567 		addr += cc;
568 		size -= cc;
569 	}
570 ret:
571 	if (resid)
572 		*resid = size;
573 
574 	return (0);
575 }
576 
577 /*
578  * Not implemented.
579  */
580 __compactcall int
nfs_write(struct open_file * f,void * buf,size_t size,size_t * resid)581 nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
582 	/* resid:	 out */
583 {
584 
585 	return (EROFS);
586 }
587 
588 __compactcall off_t
nfs_seek(struct open_file * f,off_t offset,int where)589 nfs_seek(struct open_file *f, off_t offset, int where)
590 {
591 	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
592 	n_long size = ntohl(d->fa.fa_size);
593 
594 	switch (where) {
595 	case SEEK_SET:
596 		d->off = offset;
597 		break;
598 	case SEEK_CUR:
599 		d->off += offset;
600 		break;
601 	case SEEK_END:
602 		d->off = size - offset;
603 		break;
604 	default:
605 		return (-1);
606 	}
607 
608 	return (d->off);
609 }
610 
611 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
612 const int nfs_stat_types[8] = {
613 	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
614 
615 __compactcall int
nfs_stat(struct open_file * f,struct stat * sb)616 nfs_stat(struct open_file *f, struct stat *sb)
617 {
618 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
619 	n_long ftype, mode;
620 
621 	ftype = ntohl(fp->fa.fa_type);
622 	mode  = ntohl(fp->fa.fa_mode);
623 	mode |= nfs_stat_types[ftype & 7];
624 
625 	sb->st_mode  = mode;
626 	sb->st_nlink = ntohl(fp->fa.fa_nlink);
627 	sb->st_uid   = ntohl(fp->fa.fa_uid);
628 	sb->st_gid   = ntohl(fp->fa.fa_gid);
629 	sb->st_size  = ntohl(fp->fa.fa_size);
630 
631 	return (0);
632 }
633 
634 #if defined(LIBSA_ENABLE_LS_OP)
635 __compactcall void
nfs_ls(struct open_file * f,const char * pattern)636 nfs_ls(struct open_file *f, const char *pattern)
637 {
638 	printf("Currently ls command is unsupported by nfs\n");
639 	return;
640 }
641 #endif
642