xref: /minix3/sys/lib/libsa/nfs.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: nfs.c,v 1.48 2014/03/20 03:13:18 christos Exp $	*/
258a2b000SEvgeniy Ivanov 
358a2b000SEvgeniy Ivanov /*-
458a2b000SEvgeniy Ivanov  *  Copyright (c) 1993 John Brezak
558a2b000SEvgeniy Ivanov  *  All rights reserved.
658a2b000SEvgeniy Ivanov  *
758a2b000SEvgeniy Ivanov  *  Redistribution and use in source and binary forms, with or without
858a2b000SEvgeniy Ivanov  *  modification, are permitted provided that the following conditions
958a2b000SEvgeniy Ivanov  *  are met:
1058a2b000SEvgeniy Ivanov  *  1. Redistributions of source code must retain the above copyright
1158a2b000SEvgeniy Ivanov  *     notice, this list of conditions and the following disclaimer.
1258a2b000SEvgeniy Ivanov  *  2. Redistributions in binary form must reproduce the above copyright
1358a2b000SEvgeniy Ivanov  *     notice, this list of conditions and the following disclaimer in the
1458a2b000SEvgeniy Ivanov  *     documentation and/or other materials provided with the distribution.
1558a2b000SEvgeniy Ivanov  *  3. The name of the author may not be used to endorse or promote products
1658a2b000SEvgeniy Ivanov  *     derived from this software without specific prior written permission.
1758a2b000SEvgeniy Ivanov  *
1858a2b000SEvgeniy Ivanov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
1958a2b000SEvgeniy Ivanov  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2058a2b000SEvgeniy Ivanov  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2158a2b000SEvgeniy Ivanov  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2258a2b000SEvgeniy Ivanov  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2358a2b000SEvgeniy Ivanov  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2458a2b000SEvgeniy Ivanov  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2558a2b000SEvgeniy Ivanov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2658a2b000SEvgeniy Ivanov  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2758a2b000SEvgeniy Ivanov  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2858a2b000SEvgeniy Ivanov  * POSSIBILITY OF SUCH DAMAGE.
2958a2b000SEvgeniy Ivanov  */
3058a2b000SEvgeniy Ivanov 
3158a2b000SEvgeniy Ivanov /*
3258a2b000SEvgeniy Ivanov  * XXX Does not currently implement:
3358a2b000SEvgeniy Ivanov  * XXX
3458a2b000SEvgeniy Ivanov  * XXX LIBSA_NO_FS_CLOSE
3558a2b000SEvgeniy Ivanov  * XXX LIBSA_NO_FS_SEEK
3658a2b000SEvgeniy Ivanov  * XXX LIBSA_NO_FS_WRITE
3758a2b000SEvgeniy Ivanov  * XXX LIBSA_NO_FS_SYMLINK (does this even make sense?)
3858a2b000SEvgeniy Ivanov  * XXX LIBSA_FS_SINGLECOMPONENT (does this even make sense?)
3958a2b000SEvgeniy Ivanov  */
4058a2b000SEvgeniy Ivanov 
4158a2b000SEvgeniy Ivanov #include <sys/param.h>
4258a2b000SEvgeniy Ivanov #include <sys/time.h>
4358a2b000SEvgeniy Ivanov #include <sys/socket.h>
4458a2b000SEvgeniy Ivanov #include <sys/stat.h>
4558a2b000SEvgeniy Ivanov #ifdef _STANDALONE
4658a2b000SEvgeniy Ivanov #include <lib/libkern/libkern.h>
4758a2b000SEvgeniy Ivanov #else
4858a2b000SEvgeniy Ivanov #include <string.h>
4958a2b000SEvgeniy Ivanov #endif
5058a2b000SEvgeniy Ivanov 
5158a2b000SEvgeniy Ivanov #include <netinet/in.h>
5258a2b000SEvgeniy Ivanov #include <netinet/in_systm.h>
5358a2b000SEvgeniy Ivanov 
5458a2b000SEvgeniy Ivanov #include "rpcv2.h"
5558a2b000SEvgeniy Ivanov #include "nfsv2.h"
5658a2b000SEvgeniy Ivanov 
5758a2b000SEvgeniy Ivanov #include "stand.h"
5858a2b000SEvgeniy Ivanov #include "net.h"
5958a2b000SEvgeniy Ivanov #include "nfs.h"
6058a2b000SEvgeniy Ivanov #include "rpc.h"
6158a2b000SEvgeniy Ivanov 
6258a2b000SEvgeniy Ivanov /* Define our own NFS attributes */
6358a2b000SEvgeniy Ivanov struct nfsv2_fattrs {
6458a2b000SEvgeniy Ivanov 	n_long	fa_type;
6558a2b000SEvgeniy Ivanov 	n_long	fa_mode;
6658a2b000SEvgeniy Ivanov 	n_long	fa_nlink;
6758a2b000SEvgeniy Ivanov 	n_long	fa_uid;
6858a2b000SEvgeniy Ivanov 	n_long	fa_gid;
6958a2b000SEvgeniy Ivanov 	n_long	fa_size;
7058a2b000SEvgeniy Ivanov 	n_long	fa_blocksize;
7158a2b000SEvgeniy Ivanov 	n_long	fa_rdev;
7258a2b000SEvgeniy Ivanov 	n_long	fa_blocks;
7358a2b000SEvgeniy Ivanov 	n_long	fa_fsid;
7458a2b000SEvgeniy Ivanov 	n_long	fa_fileid;
7558a2b000SEvgeniy Ivanov 	struct nfsv2_time fa_atime;
7658a2b000SEvgeniy Ivanov 	struct nfsv2_time fa_mtime;
7758a2b000SEvgeniy Ivanov 	struct nfsv2_time fa_ctime;
7858a2b000SEvgeniy Ivanov };
7958a2b000SEvgeniy Ivanov 
8058a2b000SEvgeniy Ivanov 
8158a2b000SEvgeniy Ivanov struct nfs_read_args {
8258a2b000SEvgeniy Ivanov 	u_char	fh[NFS_FHSIZE];
8358a2b000SEvgeniy Ivanov 	n_long	off;
8458a2b000SEvgeniy Ivanov 	n_long	len;
8558a2b000SEvgeniy Ivanov 	n_long	xxx;			/* XXX what's this for? */
8658a2b000SEvgeniy Ivanov };
8758a2b000SEvgeniy Ivanov 
8858a2b000SEvgeniy Ivanov /* Data part of nfs rpc reply (also the largest thing we receive) */
8958a2b000SEvgeniy Ivanov #define NFSREAD_SIZE 1024
9058a2b000SEvgeniy Ivanov struct nfs_read_repl {
9158a2b000SEvgeniy Ivanov 	n_long	errno;
9258a2b000SEvgeniy Ivanov 	struct	nfsv2_fattrs fa;
9358a2b000SEvgeniy Ivanov 	n_long	count;
9458a2b000SEvgeniy Ivanov 	u_char	data[NFSREAD_SIZE];
9558a2b000SEvgeniy Ivanov };
9658a2b000SEvgeniy Ivanov 
9758a2b000SEvgeniy Ivanov #ifndef NFS_NOSYMLINK
9858a2b000SEvgeniy Ivanov struct nfs_readlnk_repl {
9958a2b000SEvgeniy Ivanov 	n_long	errno;
10058a2b000SEvgeniy Ivanov 	n_long	len;
10158a2b000SEvgeniy Ivanov 	char	path[NFS_MAXPATHLEN];
10258a2b000SEvgeniy Ivanov };
10358a2b000SEvgeniy Ivanov #endif
10458a2b000SEvgeniy Ivanov 
10558a2b000SEvgeniy Ivanov struct nfs_iodesc {
10658a2b000SEvgeniy Ivanov 	struct	iodesc	*iodesc;
10758a2b000SEvgeniy Ivanov 	off_t	off;
10858a2b000SEvgeniy Ivanov 	u_char	fh[NFS_FHSIZE];
10958a2b000SEvgeniy Ivanov 	struct nfsv2_fattrs fa;	/* all in network order */
11058a2b000SEvgeniy Ivanov };
11158a2b000SEvgeniy Ivanov 
11258a2b000SEvgeniy Ivanov struct nfs_iodesc nfs_root_node;
11358a2b000SEvgeniy Ivanov 
11458a2b000SEvgeniy Ivanov int	nfs_getrootfh(struct iodesc *, char *, u_char *);
11558a2b000SEvgeniy Ivanov int	nfs_lookupfh(struct nfs_iodesc *, const char *, int,
11658a2b000SEvgeniy Ivanov 	    struct nfs_iodesc *);
11758a2b000SEvgeniy Ivanov int	nfs_readlink(struct nfs_iodesc *, char *);
11858a2b000SEvgeniy Ivanov ssize_t	nfs_readdata(struct nfs_iodesc *, off_t, void *, size_t);
11958a2b000SEvgeniy Ivanov 
12058a2b000SEvgeniy Ivanov /*
12158a2b000SEvgeniy Ivanov  * Fetch the root file handle (call mount daemon)
12258a2b000SEvgeniy Ivanov  * On error, return non-zero and set errno.
12358a2b000SEvgeniy Ivanov  */
12458a2b000SEvgeniy Ivanov int
nfs_getrootfh(struct iodesc * d,char * path,u_char * fhp)12558a2b000SEvgeniy Ivanov nfs_getrootfh(struct iodesc *d, char *path, u_char *fhp)
12658a2b000SEvgeniy Ivanov {
12758a2b000SEvgeniy Ivanov 	int len;
12858a2b000SEvgeniy Ivanov 	struct args {
12958a2b000SEvgeniy Ivanov 		n_long	len;
13058a2b000SEvgeniy Ivanov 		char	path[FNAME_SIZE];
13158a2b000SEvgeniy Ivanov 	} *args;
13258a2b000SEvgeniy Ivanov 	struct repl {
13358a2b000SEvgeniy Ivanov 		n_long	errno;
13458a2b000SEvgeniy Ivanov 		u_char	fh[NFS_FHSIZE];
13558a2b000SEvgeniy Ivanov 	} *repl;
13658a2b000SEvgeniy Ivanov 	struct {
13758a2b000SEvgeniy Ivanov 		n_long	h[RPC_HEADER_WORDS];
13858a2b000SEvgeniy Ivanov 		struct args d;
13958a2b000SEvgeniy Ivanov 	} sdata;
14058a2b000SEvgeniy Ivanov 	struct {
14158a2b000SEvgeniy Ivanov 		n_long	h[RPC_HEADER_WORDS];
14258a2b000SEvgeniy Ivanov 		struct repl d;
14358a2b000SEvgeniy Ivanov 	} rdata;
14458a2b000SEvgeniy Ivanov 	ssize_t cc;
14558a2b000SEvgeniy Ivanov 
14658a2b000SEvgeniy Ivanov #ifdef NFS_DEBUG
14758a2b000SEvgeniy Ivanov 	if (debug)
14858a2b000SEvgeniy Ivanov 		printf("nfs_getrootfh: %s\n", path);
14958a2b000SEvgeniy Ivanov #endif
15058a2b000SEvgeniy Ivanov 
15158a2b000SEvgeniy Ivanov 	args = &sdata.d;
15258a2b000SEvgeniy Ivanov 	repl = &rdata.d;
15358a2b000SEvgeniy Ivanov 
15458a2b000SEvgeniy Ivanov 	(void)memset(args, 0, sizeof(*args));
15558a2b000SEvgeniy Ivanov 	len = strlen(path);
15658a2b000SEvgeniy Ivanov 	if ((size_t)len > sizeof(args->path))
15758a2b000SEvgeniy Ivanov 		len = sizeof(args->path);
15858a2b000SEvgeniy Ivanov 	args->len = htonl(len);
15958a2b000SEvgeniy Ivanov 	(void)memcpy(args->path, path, len);
16058a2b000SEvgeniy Ivanov 	len = 4 + roundup(len, 4);
16158a2b000SEvgeniy Ivanov 
16258a2b000SEvgeniy Ivanov 	cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
16358a2b000SEvgeniy Ivanov 	    args, len, repl, sizeof(*repl));
16458a2b000SEvgeniy Ivanov 	if (cc == -1) {
16558a2b000SEvgeniy Ivanov 		/* errno was set by rpc_call */
16658a2b000SEvgeniy Ivanov 		return -1;
16758a2b000SEvgeniy Ivanov 	}
16858a2b000SEvgeniy Ivanov 	if (cc < 4) {
16958a2b000SEvgeniy Ivanov 		errno = EBADRPC;
17058a2b000SEvgeniy Ivanov 		return -1;
17158a2b000SEvgeniy Ivanov 	}
17258a2b000SEvgeniy Ivanov 	if (repl->errno) {
17358a2b000SEvgeniy Ivanov 		errno = ntohl(repl->errno);
17458a2b000SEvgeniy Ivanov 		return -1;
17558a2b000SEvgeniy Ivanov 	}
17658a2b000SEvgeniy Ivanov 	(void)memcpy(fhp, repl->fh, sizeof(repl->fh));
17758a2b000SEvgeniy Ivanov 	return 0;
17858a2b000SEvgeniy Ivanov }
17958a2b000SEvgeniy Ivanov 
18058a2b000SEvgeniy Ivanov /*
18158a2b000SEvgeniy Ivanov  * Lookup a file.  Store handle and attributes.
18258a2b000SEvgeniy Ivanov  * Return zero or error number.
18358a2b000SEvgeniy Ivanov  */
18458a2b000SEvgeniy Ivanov int
nfs_lookupfh(struct nfs_iodesc * d,const char * name,int len,struct nfs_iodesc * newfd)18558a2b000SEvgeniy Ivanov nfs_lookupfh(struct nfs_iodesc *d, const char *name, int len,
18658a2b000SEvgeniy Ivanov 	struct nfs_iodesc *newfd)
18758a2b000SEvgeniy Ivanov {
18858a2b000SEvgeniy Ivanov 	int rlen;
18958a2b000SEvgeniy Ivanov 	struct args {
19058a2b000SEvgeniy Ivanov 		u_char	fh[NFS_FHSIZE];
19158a2b000SEvgeniy Ivanov 		n_long	len;
19258a2b000SEvgeniy Ivanov 		char	name[FNAME_SIZE];
19358a2b000SEvgeniy Ivanov 	} *args;
19458a2b000SEvgeniy Ivanov 	struct repl {
19558a2b000SEvgeniy Ivanov 		n_long	errno;
19658a2b000SEvgeniy Ivanov 		u_char	fh[NFS_FHSIZE];
19758a2b000SEvgeniy Ivanov 		struct	nfsv2_fattrs fa;
19858a2b000SEvgeniy Ivanov 	} *repl;
19958a2b000SEvgeniy Ivanov 	struct {
20058a2b000SEvgeniy Ivanov 		n_long	h[RPC_HEADER_WORDS];
20158a2b000SEvgeniy Ivanov 		struct args d;
20258a2b000SEvgeniy Ivanov 	} sdata;
20358a2b000SEvgeniy Ivanov 	struct {
20458a2b000SEvgeniy Ivanov 		n_long	h[RPC_HEADER_WORDS];
20558a2b000SEvgeniy Ivanov 		struct repl d;
20658a2b000SEvgeniy Ivanov 	} rdata;
20758a2b000SEvgeniy Ivanov 	ssize_t cc;
20858a2b000SEvgeniy Ivanov 
20958a2b000SEvgeniy Ivanov #ifdef NFS_DEBUG
21058a2b000SEvgeniy Ivanov 	if (debug)
21158a2b000SEvgeniy Ivanov 		printf("lookupfh: called\n");
21258a2b000SEvgeniy Ivanov #endif
21358a2b000SEvgeniy Ivanov 
21458a2b000SEvgeniy Ivanov 	args = &sdata.d;
21558a2b000SEvgeniy Ivanov 	repl = &rdata.d;
21658a2b000SEvgeniy Ivanov 
21758a2b000SEvgeniy Ivanov 	(void)memset(args, 0, sizeof(*args));
21858a2b000SEvgeniy Ivanov 	(void)memcpy(args->fh, d->fh, sizeof(args->fh));
21958a2b000SEvgeniy Ivanov 	if ((size_t)len > sizeof(args->name))
22058a2b000SEvgeniy Ivanov 		len = sizeof(args->name);
22158a2b000SEvgeniy Ivanov 	(void)memcpy(args->name, name, len);
22258a2b000SEvgeniy Ivanov 	args->len = htonl(len);
22358a2b000SEvgeniy Ivanov 	len = 4 + roundup(len, 4);
22458a2b000SEvgeniy Ivanov 	len += NFS_FHSIZE;
22558a2b000SEvgeniy Ivanov 
22658a2b000SEvgeniy Ivanov 	rlen = sizeof(*repl);
22758a2b000SEvgeniy Ivanov 
22858a2b000SEvgeniy Ivanov 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
22958a2b000SEvgeniy Ivanov 	    args, len, repl, rlen);
23058a2b000SEvgeniy Ivanov 	if (cc == -1)
23158a2b000SEvgeniy Ivanov 		return errno;		/* XXX - from rpc_call */
23258a2b000SEvgeniy Ivanov 	if (cc < 4)
23358a2b000SEvgeniy Ivanov 		return EIO;
23458a2b000SEvgeniy Ivanov 	if (repl->errno) {
23558a2b000SEvgeniy Ivanov 		/* saerrno.h now matches NFS error numbers. */
23658a2b000SEvgeniy Ivanov 		return ntohl(repl->errno);
23758a2b000SEvgeniy Ivanov 	}
23858a2b000SEvgeniy Ivanov 	(void)memcpy(&newfd->fh, repl->fh, sizeof(newfd->fh));
23958a2b000SEvgeniy Ivanov 	(void)memcpy(&newfd->fa, &repl->fa, sizeof(newfd->fa));
24058a2b000SEvgeniy Ivanov 	return 0;
24158a2b000SEvgeniy Ivanov }
24258a2b000SEvgeniy Ivanov 
24358a2b000SEvgeniy Ivanov #ifndef NFS_NOSYMLINK
24458a2b000SEvgeniy Ivanov /*
24558a2b000SEvgeniy Ivanov  * Get the destination of a symbolic link.
24658a2b000SEvgeniy Ivanov  */
24758a2b000SEvgeniy Ivanov int
nfs_readlink(struct nfs_iodesc * d,char * buf)24858a2b000SEvgeniy Ivanov nfs_readlink(struct nfs_iodesc *d, char *buf)
24958a2b000SEvgeniy Ivanov {
25058a2b000SEvgeniy Ivanov 	struct {
25158a2b000SEvgeniy Ivanov 		n_long	h[RPC_HEADER_WORDS];
25258a2b000SEvgeniy Ivanov 		u_char fh[NFS_FHSIZE];
25358a2b000SEvgeniy Ivanov 	} sdata;
25458a2b000SEvgeniy Ivanov 	struct {
25558a2b000SEvgeniy Ivanov 		n_long	h[RPC_HEADER_WORDS];
25658a2b000SEvgeniy Ivanov 		struct nfs_readlnk_repl d;
25758a2b000SEvgeniy Ivanov 	} rdata;
25858a2b000SEvgeniy Ivanov 	ssize_t cc;
25958a2b000SEvgeniy Ivanov 
26058a2b000SEvgeniy Ivanov #ifdef NFS_DEBUG
26158a2b000SEvgeniy Ivanov 	if (debug)
26258a2b000SEvgeniy Ivanov 		printf("readlink: called\n");
26358a2b000SEvgeniy Ivanov #endif
26458a2b000SEvgeniy Ivanov 
26558a2b000SEvgeniy Ivanov 	(void)memcpy(sdata.fh, d->fh, NFS_FHSIZE);
26658a2b000SEvgeniy Ivanov 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READLINK,
26758a2b000SEvgeniy Ivanov 	              sdata.fh, NFS_FHSIZE,
26858a2b000SEvgeniy Ivanov 	              &rdata.d, sizeof(rdata.d));
26958a2b000SEvgeniy Ivanov 	if (cc == -1)
27058a2b000SEvgeniy Ivanov 		return errno;
27158a2b000SEvgeniy Ivanov 
27258a2b000SEvgeniy Ivanov 	if (cc < 4)
27358a2b000SEvgeniy Ivanov 		return EIO;
27458a2b000SEvgeniy Ivanov 
27558a2b000SEvgeniy Ivanov 	if (rdata.d.errno)
27658a2b000SEvgeniy Ivanov 		return ntohl(rdata.d.errno);
27758a2b000SEvgeniy Ivanov 
27858a2b000SEvgeniy Ivanov 	rdata.d.len = ntohl(rdata.d.len);
27958a2b000SEvgeniy Ivanov 	if (rdata.d.len > NFS_MAXPATHLEN)
28058a2b000SEvgeniy Ivanov 		return ENAMETOOLONG;
28158a2b000SEvgeniy Ivanov 
28258a2b000SEvgeniy Ivanov 	(void)memcpy(buf, rdata.d.path, rdata.d.len);
28358a2b000SEvgeniy Ivanov 	buf[rdata.d.len] = 0;
28458a2b000SEvgeniy Ivanov 	return 0;
28558a2b000SEvgeniy Ivanov }
28658a2b000SEvgeniy Ivanov #endif
28758a2b000SEvgeniy Ivanov 
28858a2b000SEvgeniy Ivanov /*
28958a2b000SEvgeniy Ivanov  * Read data from a file.
29058a2b000SEvgeniy Ivanov  * Return transfer count or -1 (and set errno)
29158a2b000SEvgeniy Ivanov  */
29258a2b000SEvgeniy Ivanov ssize_t
nfs_readdata(struct nfs_iodesc * d,off_t off,void * addr,size_t len)29358a2b000SEvgeniy Ivanov nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
29458a2b000SEvgeniy Ivanov {
29558a2b000SEvgeniy Ivanov 	struct nfs_read_args *args;
29658a2b000SEvgeniy Ivanov 	struct nfs_read_repl *repl;
29758a2b000SEvgeniy Ivanov 	struct {
29858a2b000SEvgeniy Ivanov 		n_long	h[RPC_HEADER_WORDS];
29958a2b000SEvgeniy Ivanov 		struct nfs_read_args d;
30058a2b000SEvgeniy Ivanov 	} sdata;
30158a2b000SEvgeniy Ivanov 	struct {
30258a2b000SEvgeniy Ivanov 		n_long	h[RPC_HEADER_WORDS];
30358a2b000SEvgeniy Ivanov 		struct nfs_read_repl d;
30458a2b000SEvgeniy Ivanov 	} rdata;
30558a2b000SEvgeniy Ivanov 	ssize_t cc;
30658a2b000SEvgeniy Ivanov 	long x;
30758a2b000SEvgeniy Ivanov 	size_t hlen, rlen;
30858a2b000SEvgeniy Ivanov 
30958a2b000SEvgeniy Ivanov 	args = &sdata.d;
31058a2b000SEvgeniy Ivanov 	repl = &rdata.d;
31158a2b000SEvgeniy Ivanov 
31258a2b000SEvgeniy Ivanov 	(void)memcpy(args->fh, d->fh, NFS_FHSIZE);
31358a2b000SEvgeniy Ivanov 	args->off = htonl((n_long)off);
31458a2b000SEvgeniy Ivanov 	if (len > NFSREAD_SIZE)
31558a2b000SEvgeniy Ivanov 		len = NFSREAD_SIZE;
31658a2b000SEvgeniy Ivanov 	args->len = htonl((n_long)len);
31758a2b000SEvgeniy Ivanov 	args->xxx = htonl((n_long)0);
31858a2b000SEvgeniy Ivanov 	hlen = sizeof(*repl) - NFSREAD_SIZE;
31958a2b000SEvgeniy Ivanov 
32058a2b000SEvgeniy Ivanov 	cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_READ,
32158a2b000SEvgeniy Ivanov 	    args, sizeof(*args),
32258a2b000SEvgeniy Ivanov 	    repl, sizeof(*repl));
32358a2b000SEvgeniy Ivanov 	if (cc == -1) {
32458a2b000SEvgeniy Ivanov 		/* errno was already set by rpc_call */
32558a2b000SEvgeniy Ivanov 		return -1;
32658a2b000SEvgeniy Ivanov 	}
32758a2b000SEvgeniy Ivanov 	if (cc < (ssize_t)hlen) {
32858a2b000SEvgeniy Ivanov 		errno = EBADRPC;
32958a2b000SEvgeniy Ivanov 		return -1;
33058a2b000SEvgeniy Ivanov 	}
33158a2b000SEvgeniy Ivanov 	if (repl->errno) {
33258a2b000SEvgeniy Ivanov 		errno = ntohl(repl->errno);
33358a2b000SEvgeniy Ivanov 		return -1;
33458a2b000SEvgeniy Ivanov 	}
33558a2b000SEvgeniy Ivanov 	rlen = cc - hlen;
33658a2b000SEvgeniy Ivanov 	x = ntohl(repl->count);
33758a2b000SEvgeniy Ivanov 	if (rlen < (size_t)x) {
33858a2b000SEvgeniy Ivanov 		printf("nfsread: short packet, %lu < %ld\n", (u_long) rlen, x);
33958a2b000SEvgeniy Ivanov 		errno = EBADRPC;
34058a2b000SEvgeniy Ivanov 		return -1;
34158a2b000SEvgeniy Ivanov 	}
34258a2b000SEvgeniy Ivanov 	(void)memcpy(addr, repl->data, x);
34358a2b000SEvgeniy Ivanov 	return x;
34458a2b000SEvgeniy Ivanov }
34558a2b000SEvgeniy Ivanov 
34658a2b000SEvgeniy Ivanov /*
34758a2b000SEvgeniy Ivanov  * nfs_mount - mount this nfs filesystem to a host
34858a2b000SEvgeniy Ivanov  * On error, return non-zero and set errno.
34958a2b000SEvgeniy Ivanov  */
35058a2b000SEvgeniy Ivanov int
nfs_mount(int sock,struct in_addr ip,char * path)35158a2b000SEvgeniy Ivanov nfs_mount(int sock, struct in_addr ip, char *path)
35258a2b000SEvgeniy Ivanov {
35358a2b000SEvgeniy Ivanov 	struct iodesc *desc;
35458a2b000SEvgeniy Ivanov 	struct nfsv2_fattrs *fa;
35558a2b000SEvgeniy Ivanov 
35658a2b000SEvgeniy Ivanov 	if (!(desc = socktodesc(sock))) {
35758a2b000SEvgeniy Ivanov 		errno = EINVAL;
35858a2b000SEvgeniy Ivanov 		return -1;
35958a2b000SEvgeniy Ivanov 	}
36058a2b000SEvgeniy Ivanov 
36158a2b000SEvgeniy Ivanov 	/* Bind to a reserved port. */
36258a2b000SEvgeniy Ivanov 	desc->myport = htons(--rpc_port);
36358a2b000SEvgeniy Ivanov 	desc->destip = ip;
36458a2b000SEvgeniy Ivanov 	if (nfs_getrootfh(desc, path, nfs_root_node.fh))
36558a2b000SEvgeniy Ivanov 		return -1;
36658a2b000SEvgeniy Ivanov 	nfs_root_node.iodesc = desc;
36758a2b000SEvgeniy Ivanov 	/* Fake up attributes for the root dir. */
36858a2b000SEvgeniy Ivanov 	fa = &nfs_root_node.fa;
36958a2b000SEvgeniy Ivanov 	fa->fa_type  = htonl(NFDIR);
37058a2b000SEvgeniy Ivanov 	fa->fa_mode  = htonl(0755);
37158a2b000SEvgeniy Ivanov 	fa->fa_nlink = htonl(2);
37258a2b000SEvgeniy Ivanov 
37358a2b000SEvgeniy Ivanov #ifdef NFS_DEBUG
37458a2b000SEvgeniy Ivanov 	if (debug)
37558a2b000SEvgeniy Ivanov 		printf("nfs_mount: got fh for %s\n", path);
37658a2b000SEvgeniy Ivanov #endif
37758a2b000SEvgeniy Ivanov 
37858a2b000SEvgeniy Ivanov 	return 0;
37958a2b000SEvgeniy Ivanov }
38058a2b000SEvgeniy Ivanov 
38158a2b000SEvgeniy Ivanov /*
38258a2b000SEvgeniy Ivanov  * Open a file.
38358a2b000SEvgeniy Ivanov  * return zero or error number
38458a2b000SEvgeniy Ivanov  */
38558a2b000SEvgeniy Ivanov __compactcall int
nfs_open(const char * path,struct open_file * f)38658a2b000SEvgeniy Ivanov nfs_open(const char *path, struct open_file *f)
38758a2b000SEvgeniy Ivanov {
38858a2b000SEvgeniy Ivanov 	struct nfs_iodesc *newfd, *currfd;
38958a2b000SEvgeniy Ivanov 	const char *cp;
39058a2b000SEvgeniy Ivanov #ifndef NFS_NOSYMLINK
39158a2b000SEvgeniy Ivanov 	const char *ncp;
39258a2b000SEvgeniy Ivanov 	int c;
39358a2b000SEvgeniy Ivanov 	char namebuf[NFS_MAXPATHLEN + 1];
39458a2b000SEvgeniy Ivanov 	char linkbuf[NFS_MAXPATHLEN + 1];
39558a2b000SEvgeniy Ivanov 	int nlinks = 0;
39658a2b000SEvgeniy Ivanov #endif
39758a2b000SEvgeniy Ivanov 	int error = 0;
39858a2b000SEvgeniy Ivanov 
39958a2b000SEvgeniy Ivanov #ifdef NFS_DEBUG
40058a2b000SEvgeniy Ivanov  	if (debug)
40158a2b000SEvgeniy Ivanov 		printf("nfs_open: %s\n", path);
40258a2b000SEvgeniy Ivanov #endif
40358a2b000SEvgeniy Ivanov 	if (nfs_root_node.iodesc == NULL) {
40458a2b000SEvgeniy Ivanov 		printf("nfs_open: must mount first.\n");
40558a2b000SEvgeniy Ivanov 		return ENXIO;
40658a2b000SEvgeniy Ivanov 	}
40758a2b000SEvgeniy Ivanov 
40858a2b000SEvgeniy Ivanov 	currfd = &nfs_root_node;
40958a2b000SEvgeniy Ivanov 	newfd = 0;
41058a2b000SEvgeniy Ivanov 
41158a2b000SEvgeniy Ivanov #ifndef NFS_NOSYMLINK
41258a2b000SEvgeniy Ivanov 	cp = path;
41358a2b000SEvgeniy Ivanov 	while (*cp) {
41458a2b000SEvgeniy Ivanov 		/*
41558a2b000SEvgeniy Ivanov 		 * Remove extra separators
41658a2b000SEvgeniy Ivanov 		 */
41758a2b000SEvgeniy Ivanov 		while (*cp == '/')
41858a2b000SEvgeniy Ivanov 			cp++;
41958a2b000SEvgeniy Ivanov 
42058a2b000SEvgeniy Ivanov 		if (*cp == '\0')
42158a2b000SEvgeniy Ivanov 			break;
42258a2b000SEvgeniy Ivanov 		/*
42358a2b000SEvgeniy Ivanov 		 * Check that current node is a directory.
42458a2b000SEvgeniy Ivanov 		 */
42558a2b000SEvgeniy Ivanov 		if (currfd->fa.fa_type != htonl(NFDIR)) {
42658a2b000SEvgeniy Ivanov 			error = ENOTDIR;
42758a2b000SEvgeniy Ivanov 			goto out;
42858a2b000SEvgeniy Ivanov 		}
42958a2b000SEvgeniy Ivanov 
43058a2b000SEvgeniy Ivanov 		/* allocate file system specific data structure */
43158a2b000SEvgeniy Ivanov 		newfd = alloc(sizeof(*newfd));
43258a2b000SEvgeniy Ivanov 		newfd->iodesc = currfd->iodesc;
43358a2b000SEvgeniy Ivanov 		newfd->off = 0;
43458a2b000SEvgeniy Ivanov 
43558a2b000SEvgeniy Ivanov 		/*
43658a2b000SEvgeniy Ivanov 		 * Get next component of path name.
43758a2b000SEvgeniy Ivanov 		 */
43858a2b000SEvgeniy Ivanov 		{
43958a2b000SEvgeniy Ivanov 			int len = 0;
44058a2b000SEvgeniy Ivanov 
44158a2b000SEvgeniy Ivanov 			ncp = cp;
44258a2b000SEvgeniy Ivanov 			while ((c = *cp) != '\0' && c != '/') {
44358a2b000SEvgeniy Ivanov 				if (++len > NFS_MAXNAMLEN) {
44458a2b000SEvgeniy Ivanov 					error = ENOENT;
44558a2b000SEvgeniy Ivanov 					goto out;
44658a2b000SEvgeniy Ivanov 				}
44758a2b000SEvgeniy Ivanov 				cp++;
44858a2b000SEvgeniy Ivanov 			}
44958a2b000SEvgeniy Ivanov 		}
45058a2b000SEvgeniy Ivanov 
45158a2b000SEvgeniy Ivanov 		/* lookup a file handle */
45258a2b000SEvgeniy Ivanov 		error = nfs_lookupfh(currfd, ncp, cp - ncp, newfd);
45358a2b000SEvgeniy Ivanov 		if (error)
45458a2b000SEvgeniy Ivanov 			goto out;
45558a2b000SEvgeniy Ivanov 
45658a2b000SEvgeniy Ivanov 		/*
45758a2b000SEvgeniy Ivanov 		 * Check for symbolic link
45858a2b000SEvgeniy Ivanov 		 */
45958a2b000SEvgeniy Ivanov 		if (newfd->fa.fa_type == htonl(NFLNK)) {
46058a2b000SEvgeniy Ivanov 			int link_len, len;
46158a2b000SEvgeniy Ivanov 
46258a2b000SEvgeniy Ivanov 			error = nfs_readlink(newfd, linkbuf);
46358a2b000SEvgeniy Ivanov 			if (error)
46458a2b000SEvgeniy Ivanov 				goto out;
46558a2b000SEvgeniy Ivanov 
46658a2b000SEvgeniy Ivanov 			link_len = strlen(linkbuf);
46758a2b000SEvgeniy Ivanov 			len = strlen(cp);
46858a2b000SEvgeniy Ivanov 
46958a2b000SEvgeniy Ivanov 			if (link_len + len > MAXPATHLEN
47058a2b000SEvgeniy Ivanov 			    || ++nlinks > MAXSYMLINKS) {
47158a2b000SEvgeniy Ivanov 				error = ENOENT;
47258a2b000SEvgeniy Ivanov 				goto out;
47358a2b000SEvgeniy Ivanov 			}
47458a2b000SEvgeniy Ivanov 
47558a2b000SEvgeniy Ivanov 			(void)memcpy(&namebuf[link_len], cp, len + 1);
47658a2b000SEvgeniy Ivanov 			(void)memcpy(namebuf, linkbuf, link_len);
47758a2b000SEvgeniy Ivanov 
47858a2b000SEvgeniy Ivanov 			/*
47958a2b000SEvgeniy Ivanov 			 * If absolute pathname, restart at root.
48058a2b000SEvgeniy Ivanov 			 * If relative pathname, restart at parent directory.
48158a2b000SEvgeniy Ivanov 			 */
48258a2b000SEvgeniy Ivanov 			cp = namebuf;
48358a2b000SEvgeniy Ivanov 			if (*cp == '/') {
48458a2b000SEvgeniy Ivanov 				if (currfd != &nfs_root_node)
48558a2b000SEvgeniy Ivanov 					dealloc(currfd, sizeof(*currfd));
48658a2b000SEvgeniy Ivanov 				currfd = &nfs_root_node;
48758a2b000SEvgeniy Ivanov 			}
48858a2b000SEvgeniy Ivanov 
48958a2b000SEvgeniy Ivanov 			dealloc(newfd, sizeof(*newfd));
49058a2b000SEvgeniy Ivanov 			newfd = 0;
49158a2b000SEvgeniy Ivanov 
49258a2b000SEvgeniy Ivanov 			continue;
49358a2b000SEvgeniy Ivanov 		}
49458a2b000SEvgeniy Ivanov 
49558a2b000SEvgeniy Ivanov 		if (currfd != &nfs_root_node)
49658a2b000SEvgeniy Ivanov 			dealloc(currfd, sizeof(*currfd));
49758a2b000SEvgeniy Ivanov 		currfd = newfd;
49858a2b000SEvgeniy Ivanov 		newfd = 0;
49958a2b000SEvgeniy Ivanov 	}
50058a2b000SEvgeniy Ivanov 
50158a2b000SEvgeniy Ivanov 	error = 0;
50258a2b000SEvgeniy Ivanov 
50358a2b000SEvgeniy Ivanov out:
50458a2b000SEvgeniy Ivanov #else
50558a2b000SEvgeniy Ivanov 	/* allocate file system specific data structure */
50658a2b000SEvgeniy Ivanov 	currfd = alloc(sizeof(*currfd));
50758a2b000SEvgeniy Ivanov 	currfd->iodesc = nfs_root_node.iodesc;
50858a2b000SEvgeniy Ivanov 	currfd->off = 0;
50958a2b000SEvgeniy Ivanov 
51058a2b000SEvgeniy Ivanov 	cp = path;
51158a2b000SEvgeniy Ivanov 	/*
51258a2b000SEvgeniy Ivanov 	 * Remove extra separators
51358a2b000SEvgeniy Ivanov 	 */
51458a2b000SEvgeniy Ivanov 	while (*cp == '/')
51558a2b000SEvgeniy Ivanov 		cp++;
51658a2b000SEvgeniy Ivanov 
51758a2b000SEvgeniy Ivanov 	/* XXX: Check for empty path here? */
51858a2b000SEvgeniy Ivanov 
51958a2b000SEvgeniy Ivanov 	error = nfs_lookupfh(&nfs_root_node, cp, strlen(cp), currfd);
52058a2b000SEvgeniy Ivanov #endif
52158a2b000SEvgeniy Ivanov 	if (!error) {
52258a2b000SEvgeniy Ivanov 		f->f_fsdata = (void *)currfd;
52358a2b000SEvgeniy Ivanov 		fsmod = "nfs";
52458a2b000SEvgeniy Ivanov 		return 0;
52558a2b000SEvgeniy Ivanov 	}
52658a2b000SEvgeniy Ivanov 
52758a2b000SEvgeniy Ivanov #ifdef NFS_DEBUG
52858a2b000SEvgeniy Ivanov 	if (debug)
52958a2b000SEvgeniy Ivanov 		printf("nfs_open: %s lookupfh failed: %s\n",
53058a2b000SEvgeniy Ivanov 		    path, strerror(error));
53158a2b000SEvgeniy Ivanov #endif
53258a2b000SEvgeniy Ivanov 	if (currfd != &nfs_root_node)
53358a2b000SEvgeniy Ivanov 		dealloc(currfd, sizeof(*currfd));
53458a2b000SEvgeniy Ivanov 	if (newfd)
53558a2b000SEvgeniy Ivanov 		dealloc(newfd, sizeof(*newfd));
53658a2b000SEvgeniy Ivanov 
53758a2b000SEvgeniy Ivanov 	return error;
53858a2b000SEvgeniy Ivanov }
53958a2b000SEvgeniy Ivanov 
54058a2b000SEvgeniy Ivanov __compactcall int
nfs_close(struct open_file * f)54158a2b000SEvgeniy Ivanov nfs_close(struct open_file *f)
54258a2b000SEvgeniy Ivanov {
54358a2b000SEvgeniy Ivanov 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
54458a2b000SEvgeniy Ivanov 
54558a2b000SEvgeniy Ivanov #ifdef NFS_DEBUG
54658a2b000SEvgeniy Ivanov 	if (debug)
54758a2b000SEvgeniy Ivanov 		printf("nfs_close: fp=0x%lx\n", (u_long)fp);
54858a2b000SEvgeniy Ivanov #endif
54958a2b000SEvgeniy Ivanov 
55058a2b000SEvgeniy Ivanov 	if (fp)
55158a2b000SEvgeniy Ivanov 		dealloc(fp, sizeof(struct nfs_iodesc));
55258a2b000SEvgeniy Ivanov 	f->f_fsdata = (void *)0;
55358a2b000SEvgeniy Ivanov 
55458a2b000SEvgeniy Ivanov 	return 0;
55558a2b000SEvgeniy Ivanov }
55658a2b000SEvgeniy Ivanov 
55758a2b000SEvgeniy Ivanov /*
55858a2b000SEvgeniy Ivanov  * read a portion of a file
55958a2b000SEvgeniy Ivanov  */
56058a2b000SEvgeniy Ivanov __compactcall int
nfs_read(struct open_file * f,void * buf,size_t size,size_t * resid)56158a2b000SEvgeniy Ivanov nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
56258a2b000SEvgeniy Ivanov {
56358a2b000SEvgeniy Ivanov 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
56458a2b000SEvgeniy Ivanov 	ssize_t cc;
56558a2b000SEvgeniy Ivanov 	char *addr = buf;
56658a2b000SEvgeniy Ivanov 
56758a2b000SEvgeniy Ivanov #ifdef NFS_DEBUG
56858a2b000SEvgeniy Ivanov 	if (debug)
56958a2b000SEvgeniy Ivanov 		printf("nfs_read: size=%lu off=%d\n", (u_long)size,
57058a2b000SEvgeniy Ivanov 		    (int)fp->off);
57158a2b000SEvgeniy Ivanov #endif
57258a2b000SEvgeniy Ivanov 	while ((int)size > 0) {
57358a2b000SEvgeniy Ivanov #if !defined(LIBSA_NO_TWIDDLE)
57458a2b000SEvgeniy Ivanov 		twiddle();
57558a2b000SEvgeniy Ivanov #endif
57658a2b000SEvgeniy Ivanov 		cc = nfs_readdata(fp, fp->off, (void *)addr, size);
57758a2b000SEvgeniy Ivanov 		/* XXX maybe should retry on certain errors */
57858a2b000SEvgeniy Ivanov 		if (cc == -1) {
57958a2b000SEvgeniy Ivanov #ifdef NFS_DEBUG
58058a2b000SEvgeniy Ivanov 			if (debug)
58158a2b000SEvgeniy Ivanov 				printf("nfs_read: read: %s\n",
58258a2b000SEvgeniy Ivanov 				       strerror(errno));
58358a2b000SEvgeniy Ivanov #endif
58458a2b000SEvgeniy Ivanov 			return errno;	/* XXX - from nfs_readdata */
58558a2b000SEvgeniy Ivanov 		}
58658a2b000SEvgeniy Ivanov 		if (cc == 0) {
58758a2b000SEvgeniy Ivanov #ifdef NFS_DEBUG
58858a2b000SEvgeniy Ivanov 			if (debug)
58958a2b000SEvgeniy Ivanov 				printf("nfs_read: hit EOF unexpectantly\n");
59058a2b000SEvgeniy Ivanov #endif
59158a2b000SEvgeniy Ivanov 			goto ret;
59258a2b000SEvgeniy Ivanov 		}
59358a2b000SEvgeniy Ivanov 		fp->off += cc;
59458a2b000SEvgeniy Ivanov 		addr += cc;
59558a2b000SEvgeniy Ivanov 		size -= cc;
59658a2b000SEvgeniy Ivanov 	}
59758a2b000SEvgeniy Ivanov ret:
59858a2b000SEvgeniy Ivanov 	if (resid)
59958a2b000SEvgeniy Ivanov 		*resid = size;
60058a2b000SEvgeniy Ivanov 
60158a2b000SEvgeniy Ivanov 	return 0;
60258a2b000SEvgeniy Ivanov }
60358a2b000SEvgeniy Ivanov 
60458a2b000SEvgeniy Ivanov /*
60558a2b000SEvgeniy Ivanov  * Not implemented.
60658a2b000SEvgeniy Ivanov  */
60758a2b000SEvgeniy Ivanov __compactcall int
nfs_write(struct open_file * f,void * buf,size_t size,size_t * resid)60858a2b000SEvgeniy Ivanov nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
60958a2b000SEvgeniy Ivanov {
61058a2b000SEvgeniy Ivanov 	return EROFS;
61158a2b000SEvgeniy Ivanov }
61258a2b000SEvgeniy Ivanov 
61358a2b000SEvgeniy Ivanov __compactcall off_t
nfs_seek(struct open_file * f,off_t offset,int where)61458a2b000SEvgeniy Ivanov nfs_seek(struct open_file *f, off_t offset, int where)
61558a2b000SEvgeniy Ivanov {
61658a2b000SEvgeniy Ivanov 	struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
61758a2b000SEvgeniy Ivanov 	n_long size = ntohl(d->fa.fa_size);
61858a2b000SEvgeniy Ivanov 
61958a2b000SEvgeniy Ivanov 	switch (where) {
62058a2b000SEvgeniy Ivanov 	case SEEK_SET:
62158a2b000SEvgeniy Ivanov 		d->off = offset;
62258a2b000SEvgeniy Ivanov 		break;
62358a2b000SEvgeniy Ivanov 	case SEEK_CUR:
62458a2b000SEvgeniy Ivanov 		d->off += offset;
62558a2b000SEvgeniy Ivanov 		break;
62658a2b000SEvgeniy Ivanov 	case SEEK_END:
62758a2b000SEvgeniy Ivanov 		d->off = size - offset;
62858a2b000SEvgeniy Ivanov 		break;
62958a2b000SEvgeniy Ivanov 	default:
63058a2b000SEvgeniy Ivanov 		return -1;
63158a2b000SEvgeniy Ivanov 	}
63258a2b000SEvgeniy Ivanov 
63358a2b000SEvgeniy Ivanov 	return d->off;
63458a2b000SEvgeniy Ivanov }
63558a2b000SEvgeniy Ivanov 
63658a2b000SEvgeniy Ivanov /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
63758a2b000SEvgeniy Ivanov const int nfs_stat_types[8] = {
63858a2b000SEvgeniy Ivanov 	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
63958a2b000SEvgeniy Ivanov 
64058a2b000SEvgeniy Ivanov __compactcall int
nfs_stat(struct open_file * f,struct stat * sb)64158a2b000SEvgeniy Ivanov nfs_stat(struct open_file *f, struct stat *sb)
64258a2b000SEvgeniy Ivanov {
64358a2b000SEvgeniy Ivanov 	struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
64458a2b000SEvgeniy Ivanov 	n_long ftype, mode;
64558a2b000SEvgeniy Ivanov 
64658a2b000SEvgeniy Ivanov 	ftype = ntohl(fp->fa.fa_type);
64758a2b000SEvgeniy Ivanov 	mode  = ntohl(fp->fa.fa_mode);
64858a2b000SEvgeniy Ivanov 	mode |= nfs_stat_types[ftype & 7];
64958a2b000SEvgeniy Ivanov 
65058a2b000SEvgeniy Ivanov 	sb->st_mode  = mode;
65158a2b000SEvgeniy Ivanov 	sb->st_nlink = ntohl(fp->fa.fa_nlink);
65258a2b000SEvgeniy Ivanov 	sb->st_uid   = ntohl(fp->fa.fa_uid);
65358a2b000SEvgeniy Ivanov 	sb->st_gid   = ntohl(fp->fa.fa_gid);
65458a2b000SEvgeniy Ivanov 	sb->st_size  = ntohl(fp->fa.fa_size);
65558a2b000SEvgeniy Ivanov 
65658a2b000SEvgeniy Ivanov 	return 0;
65758a2b000SEvgeniy Ivanov }
65858a2b000SEvgeniy Ivanov 
65958a2b000SEvgeniy Ivanov #if defined(LIBSA_ENABLE_LS_OP)
660*0a6a1f1dSLionel Sambuc #include "ls.h"
66158a2b000SEvgeniy Ivanov __compactcall void
nfs_ls(struct open_file * f,const char * pattern)662*0a6a1f1dSLionel Sambuc nfs_ls(struct open_file *f, const char *pattern)
66358a2b000SEvgeniy Ivanov {
664*0a6a1f1dSLionel Sambuc 	lsunsup("nfs");
66558a2b000SEvgeniy Ivanov }
66658a2b000SEvgeniy Ivanov #endif
667