nfs.c revision 124811
1170076Skmacy/*	$NetBSD: nfs.c,v 1.2 1998/01/24 12:43:09 drochner Exp $	*/
2170076Skmacy
3170076Skmacy/*-
4170076Skmacy *  Copyright (c) 1993 John Brezak
5170076Skmacy *  All rights reserved.
6174686Skmacy *
7174686Skmacy *  Redistribution and use in source and binary forms, with or without
8174637Skmacy *  modification, are permitted provided that the following conditions
9174637Skmacy *  are met:
10170076Skmacy *  1. Redistributions of source code must retain the above copyright
11170076Skmacy *     notice, this list of conditions and the following disclaimer.
12170076Skmacy *  2. Redistributions in binary form must reproduce the above copyright
13170076Skmacy *     notice, this list of conditions and the following disclaimer in the
14170076Skmacy *     documentation and/or other materials provided with the distribution.
15170076Skmacy *  3. The name of the author may not be used to endorse or promote products
16170076Skmacy *     derived from this software without specific prior written permission.
17172109Skmacy *
18172109Skmacy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19170076Skmacy * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20170076Skmacy * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21174626Skmacy * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22170076Skmacy * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23170076Skmacy * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24170076Skmacy * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25170076Skmacy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26170076Skmacy * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27170076Skmacy * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28170076Skmacy * POSSIBILITY OF SUCH DAMAGE.
29170076Skmacy */
30170076Skmacy
31170076Skmacy#include <sys/cdefs.h>
32170076Skmacy__FBSDID("$FreeBSD: head/lib/libstand/nfs.c 124811 2004-01-21 20:12:23Z jhb $");
33170076Skmacy
34174626Skmacy#include <sys/param.h>
35170076Skmacy#include <sys/time.h>
36174626Skmacy#include <sys/socket.h>
37174626Skmacy#include <sys/stat.h>
38#include <string.h>
39
40#include <netinet/in.h>
41#include <netinet/in_systm.h>
42
43#include "rpcv2.h"
44#include "nfsv2.h"
45
46#include "stand.h"
47#include "net.h"
48#include "netif.h"
49#include "rpc.h"
50
51#define NFS_DEBUGxx
52
53/* Define our own NFS attributes without NQNFS stuff. */
54struct 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
72struct 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
81struct 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
89struct nfs_readlnk_repl {
90	n_long	errno;
91	n_long	len;
92	char	path[NFS_MAXPATHLEN];
93};
94#endif
95
96struct nfs_readdir_args {
97	u_char	fh[NFS_FHSIZE];
98	n_long	cookie;
99	n_long	count;
100};
101
102struct nfs_readdir_data {
103	n_long	fileid;
104	n_long	len;
105	char	name[0];
106};
107
108struct nfs_readdir_off {
109	n_long	cookie;
110	n_long	follows;
111};
112
113struct nfs_iodesc {
114	struct	iodesc	*iodesc;
115	off_t	off;
116	u_char	fh[NFS_FHSIZE];
117	struct nfsv2_fattrs fa;	/* all in network order */
118};
119
120/*
121 * XXX interactions with tftp? See nfswrapper.c for a confusing
122 *     issue.
123 */
124int		nfs_open(const char *path, struct open_file *f);
125static int	nfs_close(struct open_file *f);
126static int	nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
127static int	nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
128static off_t	nfs_seek(struct open_file *f, off_t offset, int where);
129static int	nfs_stat(struct open_file *f, struct stat *sb);
130static int	nfs_readdir(struct open_file *f, struct dirent *d);
131
132struct	nfs_iodesc nfs_root_node;
133
134struct fs_ops nfs_fsops = {
135	"nfs",
136	nfs_open,
137	nfs_close,
138	nfs_read,
139	nfs_write,
140	nfs_seek,
141	nfs_stat,
142	nfs_readdir
143};
144
145/*
146 * Fetch the root file handle (call mount daemon)
147 * Return zero or error number.
148 */
149int
150nfs_getrootfh(d, path, fhp)
151	struct iodesc *d;
152	char *path;
153	u_char *fhp;
154{
155	int len;
156	struct args {
157		n_long	len;
158		char	path[FNAME_SIZE];
159	} *args;
160	struct repl {
161		n_long	errno;
162		u_char	fh[NFS_FHSIZE];
163	} *repl;
164	struct {
165		n_long	h[RPC_HEADER_WORDS];
166		struct args d;
167	} sdata;
168	struct {
169		n_long	h[RPC_HEADER_WORDS];
170		struct repl d;
171	} rdata;
172	size_t cc;
173
174#ifdef NFS_DEBUG
175	if (debug)
176		printf("nfs_getrootfh: %s\n", path);
177#endif
178
179	args = &sdata.d;
180	repl = &rdata.d;
181
182	bzero(args, sizeof(*args));
183	len = strlen(path);
184	if (len > sizeof(args->path))
185		len = sizeof(args->path);
186	args->len = htonl(len);
187	bcopy(path, args->path, len);
188	len = 4 + roundup(len, 4);
189
190	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
191	    args, len, repl, sizeof(*repl));
192	if (cc == -1) {
193		/* errno was set by rpc_call */
194		return (errno);
195	}
196	if (cc < 4)
197		return (EBADRPC);
198	if (repl->errno)
199		return (ntohl(repl->errno));
200	bcopy(repl->fh, fhp, sizeof(repl->fh));
201	return (0);
202}
203
204/*
205 * Lookup a file.  Store handle and attributes.
206 * Return zero or error number.
207 */
208int
209nfs_lookupfh(d, name, newfd)
210	struct nfs_iodesc *d;
211	const char *name;
212	struct nfs_iodesc *newfd;
213{
214	int len, rlen;
215	struct args {
216		u_char	fh[NFS_FHSIZE];
217		n_long	len;
218		char	name[FNAME_SIZE];
219	} *args;
220	struct repl {
221		n_long	errno;
222		u_char	fh[NFS_FHSIZE];
223		struct	nfsv2_fattrs fa;
224	} *repl;
225	struct {
226		n_long	h[RPC_HEADER_WORDS];
227		struct args d;
228	} sdata;
229	struct {
230		n_long	h[RPC_HEADER_WORDS];
231		struct repl d;
232	} rdata;
233	ssize_t cc;
234
235#ifdef NFS_DEBUG
236	if (debug)
237		printf("lookupfh: called\n");
238#endif
239
240	args = &sdata.d;
241	repl = &rdata.d;
242
243	bzero(args, sizeof(*args));
244	bcopy(d->fh, args->fh, sizeof(args->fh));
245	len = strlen(name);
246	if (len > sizeof(args->name))
247		len = sizeof(args->name);
248	bcopy(name, args->name, len);
249	args->len = htonl(len);
250	len = 4 + roundup(len, 4);
251	len += NFS_FHSIZE;
252
253	rlen = sizeof(*repl);
254
255	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
256	    args, len, repl, rlen);
257	if (cc == -1)
258		return (errno);		/* XXX - from rpc_call */
259	if (cc < 4)
260		return (EIO);
261	if (repl->errno) {
262		/* saerrno.h now matches NFS error numbers. */
263		return (ntohl(repl->errno));
264	}
265	bcopy( repl->fh, &newfd->fh, sizeof(newfd->fh));
266	bcopy(&repl->fa, &newfd->fa, sizeof(newfd->fa));
267	return (0);
268}
269
270#ifndef NFS_NOSYMLINK
271/*
272 * Get the destination of a symbolic link.
273 */
274int
275nfs_readlink(d, buf)
276	struct nfs_iodesc *d;
277	char *buf;
278{
279	struct {
280		n_long	h[RPC_HEADER_WORDS];
281		u_char fh[NFS_FHSIZE];
282	} sdata;
283	struct {
284		n_long	h[RPC_HEADER_WORDS];
285		struct nfs_readlnk_repl d;
286	} rdata;
287	ssize_t cc;
288
289#ifdef NFS_DEBUG
290	if (debug)
291		printf("readlink: called\n");
292#endif
293
294	bcopy(d->fh, sdata.fh, NFS_FHSIZE);
295	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READLINK,
296		      sdata.fh, NFS_FHSIZE,
297		      &rdata.d, sizeof(rdata.d));
298	if (cc == -1)
299		return (errno);
300
301	if (cc < 4)
302		return (EIO);
303
304	if (rdata.d.errno)
305		return (ntohl(rdata.d.errno));
306
307	rdata.d.len = ntohl(rdata.d.len);
308	if (rdata.d.len > NFS_MAXPATHLEN)
309		return (ENAMETOOLONG);
310
311	bcopy(rdata.d.path, buf, rdata.d.len);
312	buf[rdata.d.len] = 0;
313	return (0);
314}
315#endif
316
317/*
318 * Read data from a file.
319 * Return transfer count or -1 (and set errno)
320 */
321ssize_t
322nfs_readdata(d, off, addr, len)
323	struct nfs_iodesc *d;
324	off_t off;
325	void *addr;
326	size_t len;
327{
328	struct nfs_read_args *args;
329	struct nfs_read_repl *repl;
330	struct {
331		n_long	h[RPC_HEADER_WORDS];
332		struct nfs_read_args d;
333	} sdata;
334	struct {
335		n_long	h[RPC_HEADER_WORDS];
336		struct nfs_read_repl d;
337	} rdata;
338	size_t cc;
339	long x;
340	int hlen, rlen;
341
342	args = &sdata.d;
343	repl = &rdata.d;
344
345	bcopy(d->fh, args->fh, NFS_FHSIZE);
346	args->off = htonl((n_long)off);
347	if (len > NFSREAD_SIZE)
348		len = NFSREAD_SIZE;
349	args->len = htonl((n_long)len);
350	args->xxx = htonl((n_long)0);
351	hlen = sizeof(*repl) - NFSREAD_SIZE;
352
353	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READ,
354	    args, sizeof(*args),
355	    repl, sizeof(*repl));
356	if (cc == -1) {
357		/* errno was already set by rpc_call */
358		return (-1);
359	}
360	if (cc < hlen) {
361		errno = EBADRPC;
362		return (-1);
363	}
364	if (repl->errno) {
365		errno = ntohl(repl->errno);
366		return (-1);
367	}
368	rlen = cc - hlen;
369	x = ntohl(repl->count);
370	if (rlen < x) {
371		printf("nfsread: short packet, %d < %ld\n", rlen, x);
372		errno = EBADRPC;
373		return(-1);
374	}
375	bcopy(repl->data, addr, x);
376	return (x);
377}
378
379/*
380 * Open a file.
381 * return zero or error number
382 */
383int
384nfs_open(upath, f)
385	const char *upath;
386	struct open_file *f;
387{
388	struct iodesc *desc;
389	struct nfs_iodesc *currfd;
390	char buf[2 * NFS_FHSIZE + 3];
391	u_char *fh;
392	char *cp;
393	int i;
394#ifndef NFS_NOSYMLINK
395	struct nfs_iodesc *newfd;
396	struct nfsv2_fattrs *fa;
397	char *ncp;
398	int c;
399	char namebuf[NFS_MAXPATHLEN + 1];
400	char linkbuf[NFS_MAXPATHLEN + 1];
401	int nlinks = 0;
402#endif
403	int error;
404	char *path;
405
406#ifdef NFS_DEBUG
407 	if (debug)
408 	    printf("nfs_open: %s (rootpath=%s)\n", path, rootpath);
409#endif
410	if (!rootpath[0]) {
411		printf("no rootpath, no nfs\n");
412		return (ENXIO);
413	}
414
415#ifndef __i386__
416	if (strcmp(f->f_dev->dv_name, "net") != 0)
417		return(EINVAL);
418#endif
419
420	if (!(desc = socktodesc(*(int *)(f->f_devdata))))
421		return(EINVAL);
422
423	/* Bind to a reserved port. */
424	desc->myport = htons(--rpc_port);
425	desc->destip = rootip;
426	if ((error = nfs_getrootfh(desc, rootpath, nfs_root_node.fh)))
427		return (error);
428	nfs_root_node.iodesc = desc;
429
430	fh = &nfs_root_node.fh[0];
431	buf[0] = 'X';
432	cp = &buf[1];
433	for (i = 0; i < NFS_FHSIZE; i++, cp += 2)
434		sprintf(cp, "%02x", fh[i]);
435	sprintf(cp, "X");
436	setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
437	setenv("boot.nfsroot.path", rootpath, 1);
438	setenv("boot.nfsroot.nfshandle", buf, 1);
439
440#ifndef NFS_NOSYMLINK
441	/* Fake up attributes for the root dir. */
442	fa = &nfs_root_node.fa;
443	fa->fa_type  = htonl(NFDIR);
444	fa->fa_mode  = htonl(0755);
445	fa->fa_nlink = htonl(2);
446
447	currfd = &nfs_root_node;
448	newfd = 0;
449
450	cp = path = strdup(upath);
451	if (path == NULL) {
452	    error = ENOMEM;
453	    goto out;
454	}
455	while (*cp) {
456		/*
457		 * Remove extra separators
458		 */
459		while (*cp == '/')
460			cp++;
461
462		if (*cp == '\0')
463			break;
464		/*
465		 * Check that current node is a directory.
466		 */
467		if (currfd->fa.fa_type != htonl(NFDIR)) {
468			error = ENOTDIR;
469			goto out;
470		}
471
472		/* allocate file system specific data structure */
473		newfd = malloc(sizeof(*newfd));
474		newfd->iodesc = currfd->iodesc;
475		newfd->off = 0;
476
477		/*
478		 * Get next component of path name.
479		 */
480		{
481			int len = 0;
482
483			ncp = cp;
484			while ((c = *cp) != '\0' && c != '/') {
485				if (++len > NFS_MAXNAMLEN) {
486					error = ENOENT;
487					goto out;
488				}
489				cp++;
490			}
491			*cp = '\0';
492		}
493
494		/* lookup a file handle */
495		error = nfs_lookupfh(currfd, ncp, newfd);
496		*cp = c;
497		if (error)
498			goto out;
499
500		/*
501		 * Check for symbolic link
502		 */
503		if (newfd->fa.fa_type == htonl(NFLNK)) {
504			int link_len, len;
505
506			error = nfs_readlink(newfd, linkbuf);
507			if (error)
508				goto out;
509
510			link_len = strlen(linkbuf);
511			len = strlen(cp);
512
513			if (link_len + len > MAXPATHLEN
514			    || ++nlinks > MAXSYMLINKS) {
515				error = ENOENT;
516				goto out;
517			}
518
519			bcopy(cp, &namebuf[link_len], len + 1);
520			bcopy(linkbuf, namebuf, link_len);
521
522			/*
523			 * If absolute pathname, restart at root.
524			 * If relative pathname, restart at parent directory.
525			 */
526			cp = namebuf;
527			if (*cp == '/') {
528				if (currfd != &nfs_root_node)
529					free(currfd);
530				currfd = &nfs_root_node;
531			}
532
533			free(newfd);
534			newfd = 0;
535
536			continue;
537		}
538
539		if (currfd != &nfs_root_node)
540			free(currfd);
541		currfd = newfd;
542		newfd = 0;
543	}
544
545	error = 0;
546
547out:
548	if (newfd)
549		free(newfd);
550	if (path)
551		free(path);
552#else
553        /* allocate file system specific data structure */
554        currfd = malloc(sizeof(*currfd));
555        currfd->iodesc = desc;
556        currfd->off = 0;
557
558        error = nfs_lookupfh(&nfs_root_node, upath, currfd);
559#endif
560	if (!error) {
561		f->f_fsdata = (void *)currfd;
562		return (0);
563	}
564
565#ifdef NFS_DEBUG
566	if (debug)
567		printf("nfs_open: %s lookupfh failed: %s\n",
568		    path, strerror(error));
569#endif
570#ifndef NFS_NOSYMLINK
571	if (currfd != &nfs_root_node)
572#endif
573		free(currfd);
574
575	return (error);
576}
577
578int
579nfs_close(f)
580	struct open_file *f;
581{
582	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
583
584#ifdef NFS_DEBUG
585	if (debug)
586		printf("nfs_close: fp=0x%lx\n", (u_long)fp);
587#endif
588
589	if (fp != &nfs_root_node && fp)
590		free(fp);
591	f->f_fsdata = (void *)0;
592
593	return (0);
594}
595
596/*
597 * read a portion of a file
598 */
599int
600nfs_read(f, buf, size, resid)
601	struct open_file *f;
602	void *buf;
603	size_t size;
604	size_t *resid;	/* out */
605{
606	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
607	ssize_t cc;
608	char *addr = buf;
609
610#ifdef NFS_DEBUG
611	if (debug)
612		printf("nfs_read: size=%lu off=%d\n", (u_long)size,
613		       (int)fp->off);
614#endif
615	while ((int)size > 0) {
616		twiddle();
617		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
618		/* XXX maybe should retry on certain errors */
619		if (cc == -1) {
620#ifdef NFS_DEBUG
621			if (debug)
622				printf("nfs_read: read: %s", strerror(errno));
623#endif
624			return (errno);	/* XXX - from nfs_readdata */
625		}
626		if (cc == 0) {
627#ifdef NFS_DEBUG
628			if (debug)
629				printf("nfs_read: hit EOF unexpectantly");
630#endif
631			goto ret;
632		}
633		fp->off += cc;
634		addr += cc;
635		size -= cc;
636	}
637ret:
638	if (resid)
639		*resid = size;
640
641	return (0);
642}
643
644/*
645 * Not implemented.
646 */
647int
648nfs_write(f, buf, size, resid)
649	struct open_file *f;
650	void *buf;
651	size_t size;
652	size_t *resid;	/* out */
653{
654	return (EROFS);
655}
656
657off_t
658nfs_seek(f, offset, where)
659	struct open_file *f;
660	off_t offset;
661	int where;
662{
663	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
664	n_long size = ntohl(d->fa.fa_size);
665
666	switch (where) {
667	case SEEK_SET:
668		d->off = offset;
669		break;
670	case SEEK_CUR:
671		d->off += offset;
672		break;
673	case SEEK_END:
674		d->off = size - offset;
675		break;
676	default:
677		errno = EINVAL;
678		return (-1);
679	}
680
681	return (d->off);
682}
683
684/* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
685int nfs_stat_types[8] = {
686	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
687
688int
689nfs_stat(f, sb)
690	struct open_file *f;
691	struct stat *sb;
692{
693	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
694	n_long ftype, mode;
695
696	ftype = ntohl(fp->fa.fa_type);
697	mode  = ntohl(fp->fa.fa_mode);
698	mode |= nfs_stat_types[ftype & 7];
699
700	sb->st_mode  = mode;
701	sb->st_nlink = ntohl(fp->fa.fa_nlink);
702	sb->st_uid   = ntohl(fp->fa.fa_uid);
703	sb->st_gid   = ntohl(fp->fa.fa_gid);
704	sb->st_size  = ntohl(fp->fa.fa_size);
705
706	return (0);
707}
708
709static int
710nfs_readdir(struct open_file *f, struct dirent *d)
711{
712	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
713	struct nfs_readdir_args *args;
714	struct nfs_readdir_data *rd;
715	struct nfs_readdir_off  *roff = NULL;
716	static char *buf;
717	static n_long cookie = 0;
718	size_t cc;
719	n_long eof;
720
721	struct {
722		n_long h[RPC_HEADER_WORDS];
723		struct nfs_readdir_args d;
724	} sdata;
725	static struct {
726		n_long h[RPC_HEADER_WORDS];
727		u_char d[NFS_READDIRSIZE];
728	} rdata;
729
730	if (cookie == 0) {
731	refill:
732		args = &sdata.d;
733		bzero(args, sizeof(*args));
734
735		bcopy(fp->fh, args->fh, NFS_FHSIZE);
736		args->cookie = htonl(cookie);
737		args->count  = htonl(NFS_READDIRSIZE);
738
739		cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READDIR,
740			      args, sizeof(*args),
741			      rdata.d, sizeof(rdata.d));
742		buf  = rdata.d;
743		roff = (struct nfs_readdir_off *)buf;
744		if (ntohl(roff->cookie) != 0)
745			return EIO;
746	}
747	roff = (struct nfs_readdir_off *)buf;
748
749	if (ntohl(roff->follows) == 0) {
750		eof = ntohl((roff+1)->cookie);
751		if (eof) {
752			cookie = 0;
753			return ENOENT;
754		}
755		goto refill;
756	}
757
758	buf += sizeof(struct nfs_readdir_off);
759	rd = (struct nfs_readdir_data *)buf;
760	d->d_namlen = ntohl(rd->len);
761	bcopy(rd->name, d->d_name, d->d_namlen);
762	d->d_name[d->d_namlen] = '\0';
763
764	buf += (sizeof(struct nfs_readdir_data) + roundup(htonl(rd->len),4));
765	roff = (struct nfs_readdir_off *)buf;
766	cookie = ntohl(roff->cookie);
767	return 0;
768}
769