xref: /netbsd-src/sys/lib/libsa/nfs.c (revision 001c68bd94f75ce9270b69227c4199fbf34ee396)
1 /*	$NetBSD: nfs.c,v 1.31 2003/03/18 19:20:09 mycroft 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 /*
32  * XXX Does not currently implement:
33  * XXX
34  * XXX LIBSA_NO_FS_CLOSE
35  * XXX LIBSA_NO_FS_SEEK
36  * XXX LIBSA_NO_FS_WRITE
37  * XXX LIBSA_NO_FS_SYMLINK (does this even make sense?)
38  * XXX LIBSA_FS_SINGLECOMPONENT (does this even make sense?)
39  */
40 
41 #include <sys/param.h>
42 #include <sys/time.h>
43 #include <sys/socket.h>
44 #include <sys/stat.h>
45 #ifdef _STANDALONE
46 #include <lib/libkern/libkern.h>
47 #else
48 #include <string.h>
49 #endif
50 
51 #include <netinet/in.h>
52 #include <netinet/in_systm.h>
53 
54 #include "rpcv2.h"
55 #include "nfsv2.h"
56 
57 #include "stand.h"
58 #include "net.h"
59 #include "nfs.h"
60 #include "rpc.h"
61 
62 /* Define our own NFS attributes without NQNFS stuff. */
63 struct nfsv2_fattrs {
64 	n_long	fa_type;
65 	n_long	fa_mode;
66 	n_long	fa_nlink;
67 	n_long	fa_uid;
68 	n_long	fa_gid;
69 	n_long	fa_size;
70 	n_long	fa_blocksize;
71 	n_long	fa_rdev;
72 	n_long	fa_blocks;
73 	n_long	fa_fsid;
74 	n_long	fa_fileid;
75 	struct nfsv2_time fa_atime;
76 	struct nfsv2_time fa_mtime;
77 	struct nfsv2_time fa_ctime;
78 };
79 
80 
81 struct nfs_read_args {
82 	u_char	fh[NFS_FHSIZE];
83 	n_long	off;
84 	n_long	len;
85 	n_long	xxx;			/* XXX what's this for? */
86 };
87 
88 /* Data part of nfs rpc reply (also the largest thing we receive) */
89 #define NFSREAD_SIZE 1024
90 struct nfs_read_repl {
91 	n_long	errno;
92 	struct	nfsv2_fattrs fa;
93 	n_long	count;
94 	u_char	data[NFSREAD_SIZE];
95 };
96 
97 #ifndef NFS_NOSYMLINK
98 struct nfs_readlnk_repl {
99 	n_long	errno;
100 	n_long	len;
101 	char	path[NFS_MAXPATHLEN];
102 };
103 #endif
104 
105 struct nfs_iodesc {
106 	struct	iodesc	*iodesc;
107 	off_t	off;
108 	u_char	fh[NFS_FHSIZE];
109 	struct nfsv2_fattrs fa;	/* all in network order */
110 };
111 
112 struct nfs_iodesc nfs_root_node;
113 
114 int	nfs_getrootfh __P((struct iodesc *, char *, u_char *));
115 int	nfs_lookupfh __P((struct nfs_iodesc *, char *, struct nfs_iodesc *));
116 int	nfs_readlink __P((struct nfs_iodesc *, char *));
117 ssize_t	nfs_readdata __P((struct nfs_iodesc *, off_t, void *, size_t));
118 
119 /*
120  * Fetch the root file handle (call mount daemon)
121  * On error, return non-zero and set errno.
122  */
123 int
124 nfs_getrootfh(d, path, fhp)
125 	struct iodesc *d;
126 	char *path;
127 	u_char *fhp;
128 {
129 	int len;
130 	struct args {
131 		n_long	len;
132 		char	path[FNAME_SIZE];
133 	} *args;
134 	struct repl {
135 		n_long	errno;
136 		u_char	fh[NFS_FHSIZE];
137 	} *repl;
138 	struct {
139 		n_long	h[RPC_HEADER_WORDS];
140 		struct args d;
141 	} sdata;
142 	struct {
143 		n_long	h[RPC_HEADER_WORDS];
144 		struct repl d;
145 	} rdata;
146 	size_t cc;
147 
148 #ifdef NFS_DEBUG
149 	if (debug)
150 		printf("nfs_getrootfh: %s\n", path);
151 #endif
152 
153 	args = &sdata.d;
154 	repl = &rdata.d;
155 
156 	bzero(args, sizeof(*args));
157 	len = strlen(path);
158 	if (len > sizeof(args->path))
159 		len = sizeof(args->path);
160 	args->len = htonl(len);
161 	bcopy(path, args->path, len);
162 	len = 4 + roundup(len, 4);
163 
164 	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
165 	    args, len, repl, sizeof(*repl));
166 	if (cc == -1) {
167 		/* errno was set by rpc_call */
168 		return (-1);
169 	}
170 	if (cc < 4) {
171 		errno = EBADRPC;
172 		return (-1);
173 	}
174 	if (repl->errno) {
175 		errno = ntohl(repl->errno);
176 		return (-1);
177 	}
178 	bcopy(repl->fh, fhp, sizeof(repl->fh));
179 	return (0);
180 }
181 
182 /*
183  * Lookup a file.  Store handle and attributes.
184  * Return zero or error number.
185  */
186 int
187 nfs_lookupfh(d, name, newfd)
188 	struct nfs_iodesc *d;
189 	char *name;
190 	struct nfs_iodesc *newfd;
191 {
192 	int len, rlen;
193 	struct args {
194 		u_char	fh[NFS_FHSIZE];
195 		n_long	len;
196 		char	name[FNAME_SIZE];
197 	} *args;
198 	struct repl {
199 		n_long	errno;
200 		u_char	fh[NFS_FHSIZE];
201 		struct	nfsv2_fattrs fa;
202 	} *repl;
203 	struct {
204 		n_long	h[RPC_HEADER_WORDS];
205 		struct args d;
206 	} sdata;
207 	struct {
208 		n_long	h[RPC_HEADER_WORDS];
209 		struct repl d;
210 	} rdata;
211 	ssize_t cc;
212 
213 #ifdef NFS_DEBUG
214 	if (debug)
215 		printf("lookupfh: called\n");
216 #endif
217 
218 	args = &sdata.d;
219 	repl = &rdata.d;
220 
221 	bzero(args, sizeof(*args));
222 	bcopy(d->fh, args->fh, sizeof(args->fh));
223 	len = strlen(name);
224 	if (len > sizeof(args->name))
225 		len = sizeof(args->name);
226 	bcopy(name, args->name, len);
227 	args->len = htonl(len);
228 	len = 4 + roundup(len, 4);
229 	len += NFS_FHSIZE;
230 
231 	rlen = sizeof(*repl);
232 
233 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
234 	    args, len, repl, rlen);
235 	if (cc == -1)
236 		return (errno);		/* XXX - from rpc_call */
237 	if (cc < 4)
238 		return (EIO);
239 	if (repl->errno) {
240 		/* saerrno.h now matches NFS error numbers. */
241 		return (ntohl(repl->errno));
242 	}
243 	bcopy( repl->fh, &newfd->fh, sizeof(newfd->fh));
244 	bcopy(&repl->fa, &newfd->fa, sizeof(newfd->fa));
245 	return (0);
246 }
247 
248 #ifndef NFS_NOSYMLINK
249 /*
250  * Get the destination of a symbolic link.
251  */
252 int
253 nfs_readlink(d, buf)
254 	struct nfs_iodesc *d;
255 	char *buf;
256 {
257 	struct {
258 		n_long	h[RPC_HEADER_WORDS];
259 		u_char fh[NFS_FHSIZE];
260 	} sdata;
261 	struct {
262 		n_long	h[RPC_HEADER_WORDS];
263 		struct nfs_readlnk_repl d;
264 	} rdata;
265 	ssize_t cc;
266 
267 #ifdef NFS_DEBUG
268 	if (debug)
269 		printf("readlink: called\n");
270 #endif
271 
272 	bcopy(d->fh, sdata.fh, NFS_FHSIZE);
273 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READLINK,
274 		      sdata.fh, NFS_FHSIZE,
275 		      &rdata.d, sizeof(rdata.d));
276 	if (cc == -1)
277 		return (errno);
278 
279 	if (cc < 4)
280 		return (EIO);
281 
282 	if (rdata.d.errno)
283 		return (ntohl(rdata.d.errno));
284 
285 	rdata.d.len = ntohl(rdata.d.len);
286 	if (rdata.d.len > NFS_MAXPATHLEN)
287 		return (ENAMETOOLONG);
288 
289 	bcopy(rdata.d.path, buf, rdata.d.len);
290 	buf[rdata.d.len] = 0;
291 	return (0);
292 }
293 #endif
294 
295 /*
296  * Read data from a file.
297  * Return transfer count or -1 (and set errno)
298  */
299 ssize_t
300 nfs_readdata(d, off, addr, len)
301 	struct nfs_iodesc *d;
302 	off_t off;
303 	void *addr;
304 	size_t len;
305 {
306 	struct nfs_read_args *args;
307 	struct nfs_read_repl *repl;
308 	struct {
309 		n_long	h[RPC_HEADER_WORDS];
310 		struct nfs_read_args d;
311 	} sdata;
312 	struct {
313 		n_long	h[RPC_HEADER_WORDS];
314 		struct nfs_read_repl d;
315 	} rdata;
316 	size_t cc;
317 	long x;
318 	int hlen, rlen;
319 
320 	args = &sdata.d;
321 	repl = &rdata.d;
322 
323 	bcopy(d->fh, args->fh, NFS_FHSIZE);
324 	args->off = htonl((n_long)off);
325 	if (len > NFSREAD_SIZE)
326 		len = NFSREAD_SIZE;
327 	args->len = htonl((n_long)len);
328 	args->xxx = htonl((n_long)0);
329 	hlen = sizeof(*repl) - NFSREAD_SIZE;
330 
331 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READ,
332 	    args, sizeof(*args),
333 	    repl, sizeof(*repl));
334 	if (cc == -1) {
335 		/* errno was already set by rpc_call */
336 		return (-1);
337 	}
338 	if (cc < hlen) {
339 		errno = EBADRPC;
340 		return (-1);
341 	}
342 	if (repl->errno) {
343 		errno = ntohl(repl->errno);
344 		return (-1);
345 	}
346 	rlen = cc - hlen;
347 	x = ntohl(repl->count);
348 	if (rlen < x) {
349 		printf("nfsread: short packet, %d < %ld\n", rlen, x);
350 		errno = EBADRPC;
351 		return(-1);
352 	}
353 	bcopy(repl->data, addr, x);
354 	return (x);
355 }
356 
357 /*
358  * nfs_mount - mount this nfs filesystem to a host
359  * On error, return non-zero and set errno.
360  */
361 int
362 nfs_mount(sock, ip, path)
363 	int sock;
364 	struct in_addr ip;
365 	char *path;
366 {
367 	struct iodesc *desc;
368 	struct nfsv2_fattrs *fa;
369 
370 	if (!(desc = socktodesc(sock))) {
371 		errno = EINVAL;
372 		return(-1);
373 	}
374 
375 	/* Bind to a reserved port. */
376 	desc->myport = htons(--rpc_port);
377 	desc->destip = ip;
378 	if (nfs_getrootfh(desc, path, nfs_root_node.fh))
379 		return (-1);
380 	nfs_root_node.iodesc = desc;
381 	/* Fake up attributes for the root dir. */
382 	fa = &nfs_root_node.fa;
383 	fa->fa_type  = htonl(NFDIR);
384 	fa->fa_mode  = htonl(0755);
385 	fa->fa_nlink = htonl(2);
386 
387 #ifdef NFS_DEBUG
388 	if (debug)
389 		printf("nfs_mount: got fh for %s\n", path);
390 #endif
391 
392 	return(0);
393 }
394 
395 /*
396  * Open a file.
397  * return zero or error number
398  */
399 int
400 nfs_open(path, f)
401 	char *path;
402 	struct open_file *f;
403 {
404 	struct nfs_iodesc *newfd, *currfd;
405 	char *cp;
406 #ifndef NFS_NOSYMLINK
407 	char *ncp;
408 	int c;
409 	char namebuf[NFS_MAXPATHLEN + 1];
410 	char linkbuf[NFS_MAXPATHLEN + 1];
411 	int nlinks = 0;
412 #endif
413 	int error = 0;
414 
415 #ifdef NFS_DEBUG
416  	if (debug)
417  	    printf("nfs_open: %s\n", path);
418 #endif
419 	if (nfs_root_node.iodesc == NULL) {
420 		printf("nfs_open: must mount first.\n");
421 		return (ENXIO);
422 	}
423 
424 	currfd = &nfs_root_node;
425 	newfd = 0;
426 
427 #ifndef NFS_NOSYMLINK
428 	cp = path;
429 	while (*cp) {
430 		/*
431 		 * Remove extra separators
432 		 */
433 		while (*cp == '/')
434 			cp++;
435 
436 		if (*cp == '\0')
437 			break;
438 		/*
439 		 * Check that current node is a directory.
440 		 */
441 		if (currfd->fa.fa_type != htonl(NFDIR)) {
442 			error = ENOTDIR;
443 			goto out;
444 		}
445 
446 		/* allocate file system specific data structure */
447 		newfd = alloc(sizeof(*newfd));
448 		newfd->iodesc = currfd->iodesc;
449 		newfd->off = 0;
450 
451 		/*
452 		 * Get next component of path name.
453 		 */
454 		{
455 			int len = 0;
456 
457 			ncp = cp;
458 			while ((c = *cp) != '\0' && c != '/') {
459 				if (++len > NFS_MAXNAMLEN) {
460 					error = ENOENT;
461 					goto out;
462 				}
463 				cp++;
464 			}
465 			*cp = '\0';
466 		}
467 
468 		/* lookup a file handle */
469 		error = nfs_lookupfh(currfd, ncp, newfd);
470 		*cp = c;
471 		if (error)
472 			goto out;
473 
474 		/*
475 		 * Check for symbolic link
476 		 */
477 		if (newfd->fa.fa_type == htonl(NFLNK)) {
478 			int link_len, len;
479 
480 			error = nfs_readlink(newfd, linkbuf);
481 			if (error)
482 				goto out;
483 
484 			link_len = strlen(linkbuf);
485 			len = strlen(cp);
486 
487 			if (link_len + len > MAXPATHLEN
488 			    || ++nlinks > MAXSYMLINKS) {
489 				error = ENOENT;
490 				goto out;
491 			}
492 
493 			bcopy(cp, &namebuf[link_len], len + 1);
494 			bcopy(linkbuf, namebuf, link_len);
495 
496 			/*
497 			 * If absolute pathname, restart at root.
498 			 * If relative pathname, restart at parent directory.
499 			 */
500 			cp = namebuf;
501 			if (*cp == '/') {
502 				if (currfd != &nfs_root_node)
503 					free(currfd, sizeof(*currfd));
504 				currfd = &nfs_root_node;
505 			}
506 
507 			free(newfd, sizeof(*newfd));
508 			newfd = 0;
509 
510 			continue;
511 		}
512 
513 		if (currfd != &nfs_root_node)
514 			free(currfd, sizeof(*currfd));
515 		currfd = newfd;
516 		newfd = 0;
517 	}
518 
519 	error = 0;
520 
521 out:
522 #else
523 	/* allocate file system specific data structure */
524 	currfd = alloc(sizeof(*currfd));
525 	currfd->iodesc = nfs_root_node.iodesc;
526 	currfd->off = 0;
527 
528 	cp = path;
529 	/*
530 	 * Remove extra separators
531 	 */
532 	while (*cp == '/')
533 		cp++;
534 
535 	/* XXX: Check for empty path here? */
536 
537 	error = nfs_lookupfh(&nfs_root_node, cp, currfd);
538 #endif
539 	if (!error) {
540 		f->f_fsdata = (void *)currfd;
541 		return (0);
542 	}
543 
544 #ifdef NFS_DEBUG
545 	if (debug)
546 		printf("nfs_open: %s lookupfh failed: %s\n",
547 		    path, strerror(error));
548 #endif
549 	if (currfd != &nfs_root_node)
550 		free(currfd, sizeof(*currfd));
551 	if (newfd)
552 		free(newfd, sizeof(*newfd));
553 
554 	return (error);
555 }
556 
557 int
558 nfs_close(f)
559 	struct open_file *f;
560 {
561 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
562 
563 #ifdef NFS_DEBUG
564 	if (debug)
565 		printf("nfs_close: fp=0x%lx\n", (u_long)fp);
566 #endif
567 
568 	if (fp)
569 		free(fp, sizeof(struct nfs_iodesc));
570 	f->f_fsdata = (void *)0;
571 
572 	return (0);
573 }
574 
575 /*
576  * read a portion of a file
577  */
578 int
579 nfs_read(f, buf, size, resid)
580 	struct open_file *f;
581 	void *buf;
582 	size_t size;
583 	size_t *resid;	/* out */
584 {
585 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
586 	ssize_t cc;
587 	char *addr = buf;
588 
589 #ifdef NFS_DEBUG
590 	if (debug)
591 		printf("nfs_read: size=%lu off=%d\n", (u_long)size,
592 		    (int)fp->off);
593 #endif
594 	while ((int)size > 0) {
595 #if !defined(LIBSA_NO_TWIDDLE)
596 		twiddle();
597 #endif
598 		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
599 		/* XXX maybe should retry on certain errors */
600 		if (cc == -1) {
601 #ifdef NFS_DEBUG
602 			if (debug)
603 				printf("nfs_read: read: %s", strerror(errno));
604 #endif
605 			return (errno);	/* XXX - from nfs_readdata */
606 		}
607 		if (cc == 0) {
608 #ifdef NFS_DEBUG
609 			if (debug)
610 				printf("nfs_read: hit EOF unexpectantly");
611 #endif
612 			goto ret;
613 		}
614 		fp->off += cc;
615 		addr += cc;
616 		size -= cc;
617 	}
618 ret:
619 	if (resid)
620 		*resid = size;
621 
622 	return (0);
623 }
624 
625 /*
626  * Not implemented.
627  */
628 int
629 nfs_write(f, buf, size, resid)
630 	struct open_file *f;
631 	void *buf;
632 	size_t size;
633 	size_t *resid;	/* out */
634 {
635 	return (EROFS);
636 }
637 
638 off_t
639 nfs_seek(f, offset, where)
640 	struct open_file *f;
641 	off_t offset;
642 	int where;
643 {
644 	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
645 	n_long size = ntohl(d->fa.fa_size);
646 
647 	switch (where) {
648 	case SEEK_SET:
649 		d->off = offset;
650 		break;
651 	case SEEK_CUR:
652 		d->off += offset;
653 		break;
654 	case SEEK_END:
655 		d->off = size - offset;
656 		break;
657 	default:
658 		return (-1);
659 	}
660 
661 	return (d->off);
662 }
663 
664 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
665 const int nfs_stat_types[8] = {
666 	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
667 
668 int
669 nfs_stat(f, sb)
670 	struct open_file *f;
671 	struct stat *sb;
672 {
673 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
674 	n_long ftype, mode;
675 
676 	ftype = ntohl(fp->fa.fa_type);
677 	mode  = ntohl(fp->fa.fa_mode);
678 	mode |= nfs_stat_types[ftype & 7];
679 
680 	sb->st_mode  = mode;
681 	sb->st_nlink = ntohl(fp->fa.fa_nlink);
682 	sb->st_uid   = ntohl(fp->fa.fa_uid);
683 	sb->st_gid   = ntohl(fp->fa.fa_gid);
684 	sb->st_size  = ntohl(fp->fa.fa_size);
685 
686 	return (0);
687 }
688