nfs.c revision 197178
1/*	$NetBSD: nfs.c,v 1.2 1998/01/24 12:43:09 drochner 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/cdefs.h>
32__FBSDID("$FreeBSD: head/lib/libstand/nfs.c 197178 2009-09-13 21:51:01Z emaste $");
33
34#include <sys/param.h>
35#include <sys/time.h>
36#include <sys/socket.h>
37#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(struct iodesc *d, char *path, u_char *fhp)
151{
152	int len;
153	struct args {
154		n_long	len;
155		char	path[FNAME_SIZE];
156	} *args;
157	struct repl {
158		n_long	errno;
159		u_char	fh[NFS_FHSIZE];
160	} *repl;
161	struct {
162		n_long	h[RPC_HEADER_WORDS];
163		struct args d;
164	} sdata;
165	struct {
166		n_long	h[RPC_HEADER_WORDS];
167		struct repl d;
168	} rdata;
169	size_t cc;
170
171#ifdef NFS_DEBUG
172	if (debug)
173		printf("nfs_getrootfh: %s\n", path);
174#endif
175
176	args = &sdata.d;
177	repl = &rdata.d;
178
179	bzero(args, sizeof(*args));
180	len = strlen(path);
181	if (len > sizeof(args->path))
182		len = sizeof(args->path);
183	args->len = htonl(len);
184	bcopy(path, args->path, len);
185	len = 4 + roundup(len, 4);
186
187	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
188	    args, len, repl, sizeof(*repl));
189	if (cc == -1) {
190		/* errno was set by rpc_call */
191		return (errno);
192	}
193	if (cc < 4)
194		return (EBADRPC);
195	if (repl->errno)
196		return (ntohl(repl->errno));
197	bcopy(repl->fh, fhp, sizeof(repl->fh));
198	return (0);
199}
200
201/*
202 * Lookup a file.  Store handle and attributes.
203 * Return zero or error number.
204 */
205int
206nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
207{
208	int len, rlen;
209	struct args {
210		u_char	fh[NFS_FHSIZE];
211		n_long	len;
212		char	name[FNAME_SIZE];
213	} *args;
214	struct repl {
215		n_long	errno;
216		u_char	fh[NFS_FHSIZE];
217		struct	nfsv2_fattrs fa;
218	} *repl;
219	struct {
220		n_long	h[RPC_HEADER_WORDS];
221		struct args d;
222	} sdata;
223	struct {
224		n_long	h[RPC_HEADER_WORDS];
225		struct repl d;
226	} rdata;
227	ssize_t cc;
228
229#ifdef NFS_DEBUG
230	if (debug)
231		printf("lookupfh: called\n");
232#endif
233
234	args = &sdata.d;
235	repl = &rdata.d;
236
237	bzero(args, sizeof(*args));
238	bcopy(d->fh, args->fh, sizeof(args->fh));
239	len = strlen(name);
240	if (len > sizeof(args->name))
241		len = sizeof(args->name);
242	bcopy(name, args->name, len);
243	args->len = htonl(len);
244	len = 4 + roundup(len, 4);
245	len += NFS_FHSIZE;
246
247	rlen = sizeof(*repl);
248
249	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
250	    args, len, repl, rlen);
251	if (cc == -1)
252		return (errno);		/* XXX - from rpc_call */
253	if (cc < 4)
254		return (EIO);
255	if (repl->errno) {
256		/* saerrno.h now matches NFS error numbers. */
257		return (ntohl(repl->errno));
258	}
259	bcopy( repl->fh, &newfd->fh, sizeof(newfd->fh));
260	bcopy(&repl->fa, &newfd->fa, sizeof(newfd->fa));
261	return (0);
262}
263
264#ifndef NFS_NOSYMLINK
265/*
266 * Get the destination of a symbolic link.
267 */
268int
269nfs_readlink(struct nfs_iodesc *d, char *buf)
270{
271	struct {
272		n_long	h[RPC_HEADER_WORDS];
273		u_char fh[NFS_FHSIZE];
274	} sdata;
275	struct {
276		n_long	h[RPC_HEADER_WORDS];
277		struct nfs_readlnk_repl d;
278	} rdata;
279	ssize_t cc;
280
281#ifdef NFS_DEBUG
282	if (debug)
283		printf("readlink: called\n");
284#endif
285
286	bcopy(d->fh, sdata.fh, NFS_FHSIZE);
287	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READLINK,
288		      sdata.fh, NFS_FHSIZE,
289		      &rdata.d, sizeof(rdata.d));
290	if (cc == -1)
291		return (errno);
292
293	if (cc < 4)
294		return (EIO);
295
296	if (rdata.d.errno)
297		return (ntohl(rdata.d.errno));
298
299	rdata.d.len = ntohl(rdata.d.len);
300	if (rdata.d.len > NFS_MAXPATHLEN)
301		return (ENAMETOOLONG);
302
303	bcopy(rdata.d.path, buf, rdata.d.len);
304	buf[rdata.d.len] = 0;
305	return (0);
306}
307#endif
308
309/*
310 * Read data from a file.
311 * Return transfer count or -1 (and set errno)
312 */
313ssize_t
314nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
315{
316	struct nfs_read_args *args;
317	struct nfs_read_repl *repl;
318	struct {
319		n_long	h[RPC_HEADER_WORDS];
320		struct nfs_read_args d;
321	} sdata;
322	struct {
323		n_long	h[RPC_HEADER_WORDS];
324		struct nfs_read_repl d;
325	} rdata;
326	size_t cc;
327	long x;
328	int hlen, rlen;
329
330	args = &sdata.d;
331	repl = &rdata.d;
332
333	bcopy(d->fh, args->fh, NFS_FHSIZE);
334	args->off = htonl((n_long)off);
335	if (len > NFSREAD_SIZE)
336		len = NFSREAD_SIZE;
337	args->len = htonl((n_long)len);
338	args->xxx = htonl((n_long)0);
339	hlen = sizeof(*repl) - NFSREAD_SIZE;
340
341	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READ,
342	    args, sizeof(*args),
343	    repl, sizeof(*repl));
344	if (cc == -1) {
345		/* errno was already set by rpc_call */
346		return (-1);
347	}
348	if (cc < hlen) {
349		errno = EBADRPC;
350		return (-1);
351	}
352	if (repl->errno) {
353		errno = ntohl(repl->errno);
354		return (-1);
355	}
356	rlen = cc - hlen;
357	x = ntohl(repl->count);
358	if (rlen < x) {
359		printf("nfsread: short packet, %d < %ld\n", rlen, x);
360		errno = EBADRPC;
361		return(-1);
362	}
363	bcopy(repl->data, addr, x);
364	return (x);
365}
366
367/*
368 * Open a file.
369 * return zero or error number
370 */
371int
372nfs_open(const char *upath, struct open_file *f)
373{
374	struct iodesc *desc;
375	struct nfs_iodesc *currfd;
376	char buf[2 * NFS_FHSIZE + 3];
377	u_char *fh;
378	char *cp;
379	int i;
380#ifndef NFS_NOSYMLINK
381	struct nfs_iodesc *newfd;
382	struct nfsv2_fattrs *fa;
383	char *ncp;
384	int c;
385	char namebuf[NFS_MAXPATHLEN + 1];
386	char linkbuf[NFS_MAXPATHLEN + 1];
387	int nlinks = 0;
388#endif
389	int error;
390	char *path;
391
392#ifdef NFS_DEBUG
393 	if (debug)
394 	    printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
395#endif
396	if (!rootpath[0]) {
397		printf("no rootpath, no nfs\n");
398		return (ENXIO);
399	}
400
401	/*
402	 * This is silly - we should look at dv_type but that value is
403	 * arch dependant and we can't use it here.
404	 */
405#ifndef __i386__
406	if (strcmp(f->f_dev->dv_name, "net") != 0)
407		return(EINVAL);
408#else
409	if (strcmp(f->f_dev->dv_name, "pxe") != 0)
410		return(EINVAL);
411#endif
412
413	if (!(desc = socktodesc(*(int *)(f->f_devdata))))
414		return(EINVAL);
415
416	/* Bind to a reserved port. */
417	desc->myport = htons(--rpc_port);
418	desc->destip = rootip;
419	if ((error = nfs_getrootfh(desc, rootpath, nfs_root_node.fh)))
420		return (error);
421	nfs_root_node.iodesc = desc;
422
423	fh = &nfs_root_node.fh[0];
424	buf[0] = 'X';
425	cp = &buf[1];
426	for (i = 0; i < NFS_FHSIZE; i++, cp += 2)
427		sprintf(cp, "%02x", fh[i]);
428	sprintf(cp, "X");
429	setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
430	setenv("boot.nfsroot.path", rootpath, 1);
431	setenv("boot.nfsroot.nfshandle", buf, 1);
432
433#ifndef NFS_NOSYMLINK
434	/* Fake up attributes for the root dir. */
435	fa = &nfs_root_node.fa;
436	fa->fa_type  = htonl(NFDIR);
437	fa->fa_mode  = htonl(0755);
438	fa->fa_nlink = htonl(2);
439
440	currfd = &nfs_root_node;
441	newfd = 0;
442
443	cp = path = strdup(upath);
444	if (path == NULL) {
445	    error = ENOMEM;
446	    goto out;
447	}
448	while (*cp) {
449		/*
450		 * Remove extra separators
451		 */
452		while (*cp == '/')
453			cp++;
454
455		if (*cp == '\0')
456			break;
457		/*
458		 * Check that current node is a directory.
459		 */
460		if (currfd->fa.fa_type != htonl(NFDIR)) {
461			error = ENOTDIR;
462			goto out;
463		}
464
465		/* allocate file system specific data structure */
466		newfd = malloc(sizeof(*newfd));
467		newfd->iodesc = currfd->iodesc;
468		newfd->off = 0;
469
470		/*
471		 * Get next component of path name.
472		 */
473		{
474			int len = 0;
475
476			ncp = cp;
477			while ((c = *cp) != '\0' && c != '/') {
478				if (++len > NFS_MAXNAMLEN) {
479					error = ENOENT;
480					goto out;
481				}
482				cp++;
483			}
484			*cp = '\0';
485		}
486
487		/* lookup a file handle */
488		error = nfs_lookupfh(currfd, ncp, newfd);
489		*cp = c;
490		if (error)
491			goto out;
492
493		/*
494		 * Check for symbolic link
495		 */
496		if (newfd->fa.fa_type == htonl(NFLNK)) {
497			int link_len, len;
498
499			error = nfs_readlink(newfd, linkbuf);
500			if (error)
501				goto out;
502
503			link_len = strlen(linkbuf);
504			len = strlen(cp);
505
506			if (link_len + len > MAXPATHLEN
507			    || ++nlinks > MAXSYMLINKS) {
508				error = ENOENT;
509				goto out;
510			}
511
512			bcopy(cp, &namebuf[link_len], len + 1);
513			bcopy(linkbuf, namebuf, link_len);
514
515			/*
516			 * If absolute pathname, restart at root.
517			 * If relative pathname, restart at parent directory.
518			 */
519			cp = namebuf;
520			if (*cp == '/') {
521				if (currfd != &nfs_root_node)
522					free(currfd);
523				currfd = &nfs_root_node;
524			}
525
526			free(newfd);
527			newfd = 0;
528
529			continue;
530		}
531
532		if (currfd != &nfs_root_node)
533			free(currfd);
534		currfd = newfd;
535		newfd = 0;
536	}
537
538	error = 0;
539
540out:
541	if (newfd)
542		free(newfd);
543	if (path)
544		free(path);
545#else
546        /* allocate file system specific data structure */
547        currfd = malloc(sizeof(*currfd));
548        currfd->iodesc = desc;
549        currfd->off = 0;
550
551        error = nfs_lookupfh(&nfs_root_node, upath, currfd);
552#endif
553	if (!error) {
554		f->f_fsdata = (void *)currfd;
555		return (0);
556	}
557
558#ifdef NFS_DEBUG
559	if (debug)
560		printf("nfs_open: %s lookupfh failed: %s\n",
561		    path, strerror(error));
562#endif
563#ifndef NFS_NOSYMLINK
564	if (currfd != &nfs_root_node)
565#endif
566		free(currfd);
567
568	return (error);
569}
570
571int
572nfs_close(struct open_file *f)
573{
574	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
575
576#ifdef NFS_DEBUG
577	if (debug)
578		printf("nfs_close: fp=0x%lx\n", (u_long)fp);
579#endif
580
581	if (fp != &nfs_root_node && fp)
582		free(fp);
583	f->f_fsdata = (void *)0;
584
585	return (0);
586}
587
588/*
589 * read a portion of a file
590 */
591int
592nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
593{
594	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
595	ssize_t cc;
596	char *addr = buf;
597
598#ifdef NFS_DEBUG
599	if (debug)
600		printf("nfs_read: size=%lu off=%d\n", (u_long)size,
601		       (int)fp->off);
602#endif
603	while ((int)size > 0) {
604		twiddle();
605		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
606		/* XXX maybe should retry on certain errors */
607		if (cc == -1) {
608#ifdef NFS_DEBUG
609			if (debug)
610				printf("nfs_read: read: %s", strerror(errno));
611#endif
612			return (errno);	/* XXX - from nfs_readdata */
613		}
614		if (cc == 0) {
615#ifdef NFS_DEBUG
616			if (debug)
617				printf("nfs_read: hit EOF unexpectantly");
618#endif
619			goto ret;
620		}
621		fp->off += cc;
622		addr += cc;
623		size -= cc;
624	}
625ret:
626	if (resid)
627		*resid = size;
628
629	return (0);
630}
631
632/*
633 * Not implemented.
634 */
635int
636nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
637{
638	return (EROFS);
639}
640
641off_t
642nfs_seek(struct open_file *f, off_t offset, 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		errno = EINVAL;
659		return (-1);
660	}
661
662	return (d->off);
663}
664
665/* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
666int nfs_stat_types[8] = {
667	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
668
669int
670nfs_stat(struct open_file *f, struct stat *sb)
671{
672	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
673	n_long ftype, mode;
674
675	ftype = ntohl(fp->fa.fa_type);
676	mode  = ntohl(fp->fa.fa_mode);
677	mode |= nfs_stat_types[ftype & 7];
678
679	sb->st_mode  = mode;
680	sb->st_nlink = ntohl(fp->fa.fa_nlink);
681	sb->st_uid   = ntohl(fp->fa.fa_uid);
682	sb->st_gid   = ntohl(fp->fa.fa_gid);
683	sb->st_size  = ntohl(fp->fa.fa_size);
684
685	return (0);
686}
687
688static int
689nfs_readdir(struct open_file *f, struct dirent *d)
690{
691	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
692	struct nfs_readdir_args *args;
693	struct nfs_readdir_data *rd;
694	struct nfs_readdir_off  *roff = NULL;
695	static char *buf;
696	static n_long cookie = 0;
697	size_t cc;
698	n_long eof;
699
700	struct {
701		n_long h[RPC_HEADER_WORDS];
702		struct nfs_readdir_args d;
703	} sdata;
704	static struct {
705		n_long h[RPC_HEADER_WORDS];
706		u_char d[NFS_READDIRSIZE];
707	} rdata;
708
709	if (cookie == 0) {
710	refill:
711		args = &sdata.d;
712		bzero(args, sizeof(*args));
713
714		bcopy(fp->fh, args->fh, NFS_FHSIZE);
715		args->cookie = htonl(cookie);
716		args->count  = htonl(NFS_READDIRSIZE);
717
718		cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READDIR,
719			      args, sizeof(*args),
720			      rdata.d, sizeof(rdata.d));
721		buf  = rdata.d;
722		roff = (struct nfs_readdir_off *)buf;
723		if (ntohl(roff->cookie) != 0)
724			return EIO;
725	}
726	roff = (struct nfs_readdir_off *)buf;
727
728	if (ntohl(roff->follows) == 0) {
729		eof = ntohl((roff+1)->cookie);
730		if (eof) {
731			cookie = 0;
732			return ENOENT;
733		}
734		goto refill;
735	}
736
737	buf += sizeof(struct nfs_readdir_off);
738	rd = (struct nfs_readdir_data *)buf;
739	d->d_namlen = ntohl(rd->len);
740	bcopy(rd->name, d->d_name, d->d_namlen);
741	d->d_name[d->d_namlen] = '\0';
742
743	buf += (sizeof(struct nfs_readdir_data) + roundup(htonl(rd->len),4));
744	roff = (struct nfs_readdir_off *)buf;
745	cookie = ntohl(roff->cookie);
746	return 0;
747}
748