1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Simple nfs ops - open, close, read, and lseek.
26 */
27
28#pragma ident	"%Z%%M%	%I%	%E% SMI"
29
30#include <rpc/types.h>
31#include <rpc/auth.h>
32#include <sys/t_lock.h>
33#include "clnt.h"
34#include <sys/fcntl.h>
35#include <sys/vfs.h>
36#include <errno.h>
37#include <sys/promif.h>
38#include <rpc/xdr.h>
39#include "nfs_inet.h"
40#include <sys/stat.h>
41#include <sys/bootvfs.h>
42#include <sys/bootdebug.h>
43#include <sys/salib.h>
44#include <sys/sacache.h>
45#include <rpc/rpc.h>
46#include "brpc.h"
47#include <rpcsvc/nfs_prot.h>
48
49#define	dprintf	if (boothowto & RB_DEBUG) printf
50
51static struct timeval zero_timeout = {0, 0};	/* default */
52
53/*
54 * NFS Version 2 specific functions
55 */
56
57ssize_t
58nfsread(struct nfs_file *filep, char *buf, size_t size)
59{
60	readargs		read_args;
61	readres			read_res;
62	enum clnt_stat		read_stat;
63	uint_t			readcnt = 0;	/* # bytes read by nfs */
64	uint_t			count = 0;	/* # bytes transferred to buf */
65	int			done = FALSE;	/* last block has come in */
66	int			framing_errs = 0;	/* stack errors */
67	char			*buf_offset;	/* current buffer offset */
68	struct timeval		timeout;
69	static uint_t		pos;		/* progress indicator counter */
70	static char		ind[] = "|/-\\";	/* progress indicator */
71	static int		blks_read;
72
73	read_args.file = filep->fh.fh2;		/* structure copy */
74	read_args.offset = filep->offset;
75	buf_offset = buf;
76
77	/* Optimize for reads of less than one block size */
78
79	if (nfs_readsize == 0)
80		nfs_readsize = READ_SIZE;
81
82	if (size < nfs_readsize)
83		read_args.count = size;
84	else
85		read_args.count = nfs_readsize;
86
87	do {
88		/* use the user's buffer to stuff the data into. */
89		read_res.readres_u.reply.data.data_val = buf_offset;
90
91		/*
92		 * Handle the case where the file does not end
93		 * on a block boundary.
94		 */
95		if ((count + read_args.count) > size)
96			read_args.count = size - count;
97
98		timeout.tv_sec = NFS_REXMIT_MIN; /* Total wait for call */
99		timeout.tv_usec = 0;
100		do {
101			read_stat = CLNT_CALL(root_CLIENT, NFSPROC_READ,
102			    xdr_readargs, (caddr_t)&read_args,
103			    xdr_readres, (caddr_t)&read_res, timeout);
104
105			if (read_stat == RPC_TIMEDOUT) {
106				dprintf("NFS read(%d) timed out. Retrying...\n",
107				    read_args.count);
108				/*
109				 * If the remote is there and trying to respond,
110				 * but our stack is having trouble reassembling
111				 * the reply, reduce the read size in an
112				 * attempt to compensate. Reset the
113				 * transmission and reply wait timers.
114				 */
115				if (errno == ETIMEDOUT)
116					framing_errs++;
117
118				if (framing_errs > NFS_MAX_FERRS &&
119				    read_args.count > NFS_READ_DECR) {
120					read_args.count -= NFS_READ_DECR;
121					nfs_readsize -= NFS_READ_DECR;
122					dprintf("NFS Read size now %d.\n",
123					    nfs_readsize);
124					timeout.tv_sec = NFS_REXMIT_MIN;
125					framing_errs = 0;
126				} else {
127					if (timeout.tv_sec < NFS_REXMIT_MAX)
128						timeout.tv_sec++;
129					else
130						timeout.tv_sec = 0;
131							/* default RPC */
132				}
133			}
134		} while (read_stat == RPC_TIMEDOUT);
135
136		if (read_stat != RPC_SUCCESS)
137			return (-1);
138
139		readcnt = read_res.readres_u.reply.data.data_len;
140		/*
141		 * Handle the case where the file is simply empty, and
142		 * nothing could be read.
143		 */
144		if (readcnt == 0)
145			break; /* eof */
146
147		/*
148		 * Handle the case where the file is smaller than
149		 * the size of the read request, thus the request
150		 * couldn't be completely filled.
151		 */
152		if (readcnt < read_args.count) {
153#ifdef NFS_OPS_DEBUG
154		if ((boothowto & DBFLAGS) == DBFLAGS)
155			printf("nfsread(): partial read %d"
156			    " instead of %d\n",
157			    readcnt, read_args.count);
158#endif
159		done = TRUE; /* update the counts and exit */
160		}
161
162		/* update various offsets */
163		count += readcnt;
164		filep->offset += readcnt;
165		buf_offset += readcnt;
166		read_args.offset += readcnt;
167		/*
168		 * round and round she goes (though not on every block..
169		 * - OBP's take a fair bit of time to actually print stuff)
170		 */
171		if ((blks_read++ & 0x3) == 0)
172			printf("%c\b", ind[pos++ & 3]);
173	} while (count < size && !done);
174
175	return (count);
176}
177
178static vtype_t nf_to_vt[] = {
179	VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK
180};
181
182int
183nfsgetattr(struct nfs_file *nfp, struct vattr *vap)
184{
185	enum clnt_stat getattr_stat;
186	attrstat getattr_res;
187	fattr *na;
188	struct timeval timeout = {0, 0};	/* default */
189
190	getattr_stat = CLNT_CALL(root_CLIENT, NFSPROC_GETATTR,
191	    xdr_nfs_fh, (caddr_t)&(nfp->fh.fh2),
192	    xdr_attrstat, (caddr_t)&getattr_res, timeout);
193
194	if (getattr_stat != RPC_SUCCESS) {
195		dprintf("nfs_getattr: RPC error %d\n", getattr_stat);
196		return (-1);
197	}
198	if (getattr_res.status != NFS_OK) {
199		nfs_error(getattr_res.status);
200		return (getattr_res.status);
201	}
202
203	/* adapted from nattr_to_vattr() in nfs_client.c */
204
205	na = &getattr_res.attrstat_u.attributes;
206	if (vap->va_mask & AT_TYPE) {
207		if (na->type < NFNON || na->type > NFSOCK)
208			vap->va_type = VBAD;
209		else
210			vap->va_type = nf_to_vt[na->type];
211	}
212	if (vap->va_mask & AT_MODE)
213		vap->va_mode = na->mode;
214	if (vap->va_mask & AT_SIZE)
215		vap->va_size = na->size;
216	if (vap->va_mask & AT_NODEID)
217		vap->va_nodeid = na->fileid;
218	if (vap->va_mask & AT_ATIME) {
219		vap->va_atime.tv_sec  = na->atime.seconds;
220		vap->va_atime.tv_nsec = na->atime.useconds * 1000;
221	}
222	if (vap->va_mask & AT_CTIME) {
223		vap->va_ctime.tv_sec  = na->ctime.seconds;
224		vap->va_ctime.tv_nsec = na->ctime.useconds * 1000;
225	}
226	if (vap->va_mask & AT_MTIME) {
227		vap->va_mtime.tv_sec  = na->mtime.seconds;
228		vap->va_mtime.tv_nsec = na->mtime.useconds * 1000;
229	}
230
231#ifdef NFS_OPS_DEBUG
232	if ((boothowto & DBFLAGS) == DBFLAGS)
233		printf("nfs_getattr(): done.\n");
234#endif
235	return (getattr_res.status);
236}
237
238/*
239 * Display nfs error messages.
240 */
241/*ARGSUSED*/
242void
243nfs_error(enum nfsstat status)
244{
245	if (!(boothowto & RB_DEBUG))
246		return;
247
248	switch (status) {
249	case NFSERR_PERM:
250		printf("NFS: Not owner.\n");
251		break;
252	case NFSERR_NOENT:
253#ifdef	NFS_OPS_DEBUG
254		printf("NFS: No such file or directory.\n");
255#endif	/* NFS_OPS_DEBUG */
256		break;
257	case NFSERR_IO:
258		printf("NFS: IO ERROR occurred on NFS server.\n");
259		break;
260	case NFSERR_NXIO:
261		printf("NFS: No such device or address.\n");
262		break;
263	case NFSERR_ACCES:
264		printf("NFS: Permission denied.\n");
265		break;
266	case NFSERR_EXIST:
267		printf("NFS: File exists.\n");
268		break;
269	case NFSERR_NODEV:
270		printf("NFS: No such device.\n");
271		break;
272	case NFSERR_NOTDIR:
273		printf("NFS: Not a directory.\n");
274		break;
275	case NFSERR_ISDIR:
276		printf("NFS: Is a directory.\n");
277		break;
278	case NFSERR_FBIG:
279		printf("NFS: File too large.\n");
280		break;
281	case NFSERR_NOSPC:
282		printf("NFS: No space left on device.\n");
283		break;
284	case NFSERR_ROFS:
285		printf("NFS: Read-only filesystem.\n");
286		break;
287	case NFSERR_NAMETOOLONG:
288		printf("NFS: File name too long.\n");
289		break;
290	case NFSERR_NOTEMPTY:
291		printf("NFS: Directory not empty.\n");
292		break;
293	case NFSERR_DQUOT:
294		printf("NFS: Disk quota exceeded.\n");
295		break;
296	case NFSERR_STALE:
297		printf("NFS: Stale file handle.\n");
298		break;
299	case NFSERR_WFLUSH:
300		printf("NFS: server's write cache has been flushed.\n");
301		break;
302	default:
303		printf("NFS: unknown error.\n");
304		break;
305	}
306}
307
308struct nfs_file *
309nfslookup(struct nfs_file *dir, char *name, int *nstat)
310{
311	static struct nfs_file cd;
312	diropargs dirop;
313	diropres res_lookup;
314	enum clnt_stat status;
315
316	*nstat = (int)NFS_OK;
317
318	bcopy(&dir->fh.fh2, &dirop.dir, NFS_FHSIZE);
319	dirop.name = name;
320
321	status = CLNT_CALL(root_CLIENT, NFSPROC_LOOKUP, xdr_diropargs,
322	    (caddr_t)&dirop, xdr_diropres, (caddr_t)&res_lookup,
323	    zero_timeout);
324	if (status != RPC_SUCCESS) {
325		dprintf("lookup: RPC error.\n");
326		return (NULL);
327	}
328	if (res_lookup.status != NFS_OK) {
329		nfs_error(res_lookup.status);
330		*nstat = (int)res_lookup.status;
331		return (NULL);
332	}
333
334	bzero((caddr_t)&cd, sizeof (struct nfs_file));
335	cd.version = NFS_VERSION;
336	cd.ftype.type2 = res_lookup.diropres_u.diropres.attributes.type;
337	bcopy(&res_lookup.diropres_u.diropres.file, &cd.fh.fh2, NFS_FHSIZE);
338	return (&cd);
339}
340
341/*
342 * Gets symbolic link into pathname.
343 */
344int
345nfsgetsymlink(struct nfs_file *cfile, char **path)
346{
347	enum clnt_stat status;
348	struct readlinkres linkres;
349	static char symlink_path[NFS_MAXPATHLEN];
350
351	/*
352	 * linkres needs a zeroed buffer to place path data into:
353	 */
354	bzero(symlink_path, NFS_MAXPATHLEN);
355	linkres.readlinkres_u.data = &symlink_path[0];
356
357	status = CLNT_CALL(root_CLIENT, NFSPROC_READLINK,
358	    xdr_nfs_fh, (caddr_t)&cfile->fh.fh2,
359	    xdr_readlinkres, (caddr_t)&linkres, zero_timeout);
360	if (status != RPC_SUCCESS) {
361		dprintf("nfsgetsymlink: RPC call failed.\n");
362		return (-1);
363	}
364	if (linkres.status != NFS_OK) {
365		nfs_error(linkres.status);
366		return (linkres.status);
367	}
368
369	*path = linkres.readlinkres_u.data;
370
371	return (NFS_OK);
372}
373