1/*	$NetBSD: nfs.c,v 1.18 2011/06/16 13:27:59 joerg 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. */
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_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
103int	nfs_getrootfh(struct iodesc *, char *, u_char *);
104int	nfs_lookupfh(struct nfs_iodesc *, const char *, int,
105	    struct nfs_iodesc *);
106#ifndef NFS_NOSYMLINK
107int	nfs_readlink(struct nfs_iodesc *, char *);
108#endif
109ssize_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 */
115int
116nfs_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 */
171int
172nfs_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 */
233int
234nfs_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 */
278ssize_t
279nfs_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
337nfs_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
474out:
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
514nfs_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
534nfs_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	}
570ret:
571	if (resid)
572		*resid = size;
573
574	return (0);
575}
576
577/*
578 * Not implemented.
579 */
580__compactcall int
581nfs_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
589nfs_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 */
612const int nfs_stat_types[8] = {
613	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
614
615__compactcall int
616nfs_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
636nfs_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