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 V3 ops
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
51/*
52 * NFS Version 3 specific functions
53 */
54
55ssize_t
56nfs3read(struct nfs_file *filep, char *buf, size_t size)
57{
58	READ3args		read_args;
59	READ3res		read_res;
60	enum clnt_stat		read_stat;
61	uint_t			readcnt = 0;	/* # bytes read by nfs */
62	uint_t			count = 0;	/* # bytes transferred to buf */
63	int			done = FALSE;	/* last block has come in */
64	int			framing_errs = 0;	/* stack errors */
65	char			*buf_offset;	/* current buffer offset */
66	struct timeval		timeout;
67	static uint_t		pos;		/* progress indicator counter */
68	static char		ind[] = "|/-\\";	/* progress indicator */
69	static int		blks_read;
70
71	read_args.file.data.data_len = filep->fh.fh3.len;
72	read_args.file.data.data_val = filep->fh.fh3.data;
73	read_args.offset = filep->offset;
74
75	bzero(&read_res, sizeof (read_res));
76
77	buf_offset = buf;
78
79	/* Optimize for reads of less than one block size */
80
81	if (nfs_readsize == 0)
82		nfs_readsize = READ3_SIZE;
83
84	if (size < nfs_readsize)
85		read_args.count = size;
86	else
87		read_args.count = nfs_readsize;
88
89	do {
90		/* use the user's buffer to stuff the data into. */
91		read_res.READ3res_u.resok.data.data_val = buf_offset;
92
93		/*
94		 * Handle the case where the file does not end
95		 * on a block boundary.
96		 */
97		if ((count + read_args.count) > size)
98			read_args.count = size - count;
99
100		timeout.tv_sec = NFS_REXMIT_MIN; /* Total wait for call */
101		timeout.tv_usec = 0;
102		do {
103			read_stat = CLNT_CALL(root_CLIENT, NFSPROC3_READ,
104			    xdr_READ3args, (caddr_t)&read_args,
105			    xdr_READ3res, (caddr_t)&read_res, timeout);
106
107			if (read_stat == RPC_TIMEDOUT) {
108				dprintf("NFS read(%d) timed out. Retrying...\n",
109				    read_args.count);
110				/*
111				 * If the remote is there and trying to respond,
112				 * but our stack is having trouble reassembling
113				 * the reply, reduce the read size in an
114				 * attempt to compensate. Reset the
115				 * transmission and reply wait timers.
116				 */
117				if (errno == ETIMEDOUT)
118					framing_errs++;
119
120				if (framing_errs > NFS_MAX_FERRS &&
121				    read_args.count > NFS_READ_DECR) {
122					read_args.count /= 2;
123					nfs_readsize /= 2;
124					dprintf("NFS Read size now %d.\n",
125					    nfs_readsize);
126					timeout.tv_sec = NFS_REXMIT_MIN;
127					framing_errs = 0;
128				} else {
129					if (timeout.tv_sec < NFS_REXMIT_MAX)
130						timeout.tv_sec++;
131					else
132						timeout.tv_sec = 0;
133							/* default RPC */
134				}
135			}
136		} while (read_stat == RPC_TIMEDOUT);
137
138		if (read_stat != RPC_SUCCESS)
139			return (-1);
140
141		if (read_res.status != NFS3_OK)
142			return (-1);
143
144		readcnt = read_res.READ3res_u.resok.data.data_len;
145		/*
146		 * If we are at EOF, update counts and exit
147		 */
148		if (read_res.READ3res_u.resok.eof == TRUE)
149			done = TRUE;
150
151		/*
152		 * Handle the case where the file is smaller than
153		 * the size of the read request, thus the request
154		 * couldn't be completely filled.
155		 */
156		if (readcnt < read_args.count) {
157#ifdef NFS_OPS_DEBUG
158			if ((boothowto & DBFLAGS) == DBFLAGS)
159				printf("nfs3read(): partial read %d"
160				    " instead of %d\n",
161				    readcnt, read_args.count);
162#endif
163			done = TRUE; /* update the counts and exit */
164		}
165
166		/* update various offsets */
167		count += readcnt;
168		filep->offset += readcnt;
169		buf_offset += readcnt;
170		read_args.offset += readcnt;
171		/*
172		 * round and round she goes (though not on every block..
173		 * - OBP's take a fair bit of time to actually print stuff)
174		 */
175		if ((blks_read++ & 0x3) == 0)
176			printf("%c\b", ind[pos++ & 3]);
177	} while (count < size && !done);
178
179	return (count);
180}
181
182int
183nfs3getattr(struct nfs_file *nfp, struct vattr *vap)
184{
185	enum clnt_stat getattr_stat;
186	GETATTR3args getattr_args;
187	GETATTR3res getattr_res;
188	fattr3 *na;
189	struct timeval timeout = {0, 0};	/* default */
190	vtype_t nf3_to_vt[] =
191			{ VBAD, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO };
192
193
194	bzero(&getattr_args, sizeof (getattr_args));
195	getattr_args.object.data.data_len = nfp->fh.fh3.len;
196	getattr_args.object.data.data_val = nfp->fh.fh3.data;
197
198	bzero(&getattr_res, sizeof (getattr_res));
199
200	getattr_stat = CLNT_CALL(root_CLIENT, NFSPROC3_GETATTR,
201	    xdr_GETATTR3args, (caddr_t)&getattr_args,
202	    xdr_GETATTR3res, (caddr_t)&getattr_res, timeout);
203
204	if (getattr_stat != RPC_SUCCESS) {
205		dprintf("nfs_getattr: RPC error %d\n", getattr_stat);
206		return (-1);
207	}
208	if (getattr_res.status != NFS3_OK) {
209		nfs3_error(getattr_res.status);
210		return (getattr_res.status);
211	}
212
213	na = &getattr_res.GETATTR3res_u.resok.obj_attributes;
214	if (vap->va_mask & AT_TYPE) {
215		if (na->type < NF3REG || na->type > NF3FIFO)
216			vap->va_type = VBAD;
217		else
218			vap->va_type = nf3_to_vt[na->type];
219	}
220	if (vap->va_mask & AT_MODE)
221		vap->va_mode = (mode_t)na->mode;
222	if (vap->va_mask & AT_SIZE)
223		vap->va_size = (u_offset_t)na->size;
224	if (vap->va_mask & AT_NODEID)
225		vap->va_nodeid = (u_longlong_t)na->fileid;
226	if (vap->va_mask & AT_ATIME) {
227		vap->va_atime.tv_sec  = na->atime.seconds;
228		vap->va_atime.tv_nsec = na->atime.nseconds;
229	}
230	if (vap->va_mask & AT_CTIME) {
231		vap->va_ctime.tv_sec  = na->ctime.seconds;
232		vap->va_ctime.tv_nsec = na->ctime.nseconds;
233	}
234	if (vap->va_mask & AT_MTIME) {
235		vap->va_mtime.tv_sec  = na->mtime.seconds;
236		vap->va_mtime.tv_nsec = na->mtime.nseconds;
237	}
238
239	return (NFS3_OK);
240}
241
242/*
243 * Display nfs error messages.
244 */
245/*ARGSUSED*/
246void
247nfs3_error(enum nfsstat3 status)
248{
249	if (!(boothowto & RB_DEBUG))
250		return;
251
252	switch (status) {
253	case NFS3_OK:
254		printf("NFS: No error.\n");
255		break;
256	case NFS3ERR_PERM:
257		printf("NFS: Not owner.\n");
258		break;
259	case NFS3ERR_NOENT:
260#ifdef	NFS_OPS_DEBUG
261		printf("NFS: No such file or directory.\n");
262#endif	/* NFS_OPS_DEBUG */
263		break;
264	case NFS3ERR_IO:
265		printf("NFS: IO ERROR occurred on NFS server.\n");
266		break;
267	case NFS3ERR_NXIO:
268		printf("NFS: No such device or address.\n");
269		break;
270	case NFS3ERR_ACCES:
271		printf("NFS: Permission denied.\n");
272		break;
273	case NFS3ERR_EXIST:
274		printf("NFS: File exists.\n");
275		break;
276	case NFS3ERR_XDEV:
277		printf("NFS: Cross device hard link.\n");
278		break;
279	case NFS3ERR_NODEV:
280		printf("NFS: No such device.\n");
281		break;
282	case NFS3ERR_NOTDIR:
283		printf("NFS: Not a directory.\n");
284		break;
285	case NFS3ERR_ISDIR:
286		printf("NFS: Is a directory.\n");
287		break;
288	case NFS3ERR_INVAL:
289		printf("NFS: Invalid argument.\n");
290		break;
291	case NFS3ERR_FBIG:
292		printf("NFS: File too large.\n");
293		break;
294	case NFS3ERR_NOSPC:
295		printf("NFS: No space left on device.\n");
296		break;
297	case NFS3ERR_ROFS:
298		printf("NFS: Read-only filesystem.\n");
299		break;
300	case NFS3ERR_MLINK:
301		printf("NFS: Too many hard links.\n");
302		break;
303	case NFS3ERR_NAMETOOLONG:
304		printf("NFS: File name too long.\n");
305		break;
306	case NFS3ERR_NOTEMPTY:
307		printf("NFS: Directory not empty.\n");
308		break;
309	case NFS3ERR_DQUOT:
310		printf("NFS: Disk quota exceeded.\n");
311		break;
312	case NFS3ERR_STALE:
313		printf("NFS: Stale file handle.\n");
314		break;
315	case NFS3ERR_REMOTE:
316		printf("NFS: Remote file in path.\n");
317		break;
318	case NFS3ERR_BADHANDLE:
319		printf("NFS: Illegal NFS file handle.\n");
320		break;
321	case NFS3ERR_NOT_SYNC:
322		printf("NFS: Synchronization mismatch.\n");
323		break;
324	case NFS3ERR_BAD_COOKIE:
325		printf("NFS: Stale Cookie.\n");
326		break;
327	case NFS3ERR_NOTSUPP:
328		printf("NFS: Operation is not supported.\n");
329		break;
330	case NFS3ERR_TOOSMALL:
331		printf("NFS: Buffer too small.\n");
332		break;
333	case NFS3ERR_SERVERFAULT:
334		printf("NFS: Server fault.\n");
335		break;
336	case NFS3ERR_BADTYPE:
337		printf("NFS: Unsupported object type.\n");
338		break;
339	case NFS3ERR_JUKEBOX:
340		printf("NFS: Resource temporarily unavailable.\n");
341		break;
342	default:
343		printf("NFS: unknown error.\n");
344		break;
345	}
346}
347
348struct nfs_file *
349nfs3lookup(struct nfs_file *dir, char *name, int *nstat)
350{
351	struct timeval zero_timeout = {0, 0};	/* default */
352	static struct nfs_file cd;
353	LOOKUP3args dirop;
354	LOOKUP3res res_lookup;
355	enum clnt_stat status;
356
357	*nstat = (int)NFS3_OK;
358
359	bzero((caddr_t)&dirop, sizeof (LOOKUP3args));
360	bzero((caddr_t)&res_lookup, sizeof (LOOKUP3res));
361
362	dirop.what.dir.data.data_len = dir->fh.fh3.len;
363	dirop.what.dir.data.data_val = dir->fh.fh3.data;
364	dirop.what.name = name;
365
366	status = CLNT_CALL(root_CLIENT, NFSPROC3_LOOKUP, xdr_LOOKUP3args,
367	    (caddr_t)&dirop, xdr_LOOKUP3res, (caddr_t)&res_lookup,
368	    zero_timeout);
369	if (status != RPC_SUCCESS) {
370		dprintf("lookup: RPC error.\n");
371		return (NULL);
372	}
373	if (res_lookup.status != NFS3_OK) {
374		nfs3_error(res_lookup.status);
375		*nstat = (int)res_lookup.status;
376		(void) CLNT_FREERES(root_CLIENT,
377		    xdr_LOOKUP3res, (caddr_t)&res_lookup);
378		return (NULL);
379	}
380
381	bzero((caddr_t)&cd, sizeof (struct nfs_file));
382	cd.version = NFS_V3;
383	/*
384	 * Server must supply post_op_attr's
385	 */
386	if (res_lookup.LOOKUP3res_u.resok.obj_attributes.attributes_follow ==
387	    FALSE) {
388		printf("nfs3lookup: server fails to return post_op_attr\n");
389		(void) CLNT_FREERES(root_CLIENT,
390		    xdr_LOOKUP3res, (caddr_t)&res_lookup);
391		return (NULL);
392	}
393
394	cd.ftype.type3 = res_lookup.LOOKUP3res_u.resok.obj_attributes
395	    .post_op_attr_u.attributes.type;
396	cd.fh.fh3.len = res_lookup.LOOKUP3res_u.resok.object.data.data_len;
397	bcopy(res_lookup.LOOKUP3res_u.resok.object.data.data_val,
398	    cd.fh.fh3.data, cd.fh.fh3.len);
399	(void) CLNT_FREERES(root_CLIENT, xdr_LOOKUP3res, (caddr_t)&res_lookup);
400	return (&cd);
401}
402
403/*
404 * Gets symbolic link into pathname.
405 */
406int
407nfs3getsymlink(struct nfs_file *cfile, char **path)
408{
409	struct timeval zero_timeout = {0, 0};	/* default */
410	enum clnt_stat status;
411	struct READLINK3res linkres;
412	struct READLINK3args linkargs;
413	static char symlink_path[NFS_MAXPATHLEN];
414
415	bzero(&linkargs, sizeof (linkargs));
416	linkargs.symlink.data.data_len = cfile->fh.fh3.len;
417	linkargs.symlink.data.data_val = cfile->fh.fh3.data;
418
419	/*
420	 * linkres needs a zeroed buffer to place path data into:
421	 */
422	bzero(&linkres, sizeof (linkres));
423	bzero(symlink_path, NFS_MAXPATHLEN);
424	linkres.READLINK3res_u.resok.data = symlink_path;
425
426	status = CLNT_CALL(root_CLIENT, NFSPROC3_READLINK,
427	    xdr_READLINK3args, (caddr_t)&linkargs,
428	    xdr_READLINK3res, (caddr_t)&linkres, zero_timeout);
429	if (status != RPC_SUCCESS) {
430		dprintf("nfs3getsymlink: RPC call failed.\n");
431		return (-1);
432	}
433	if (linkres.status != NFS3_OK) {
434		nfs3_error(linkres.status);
435		return (linkres.status);
436	}
437
438	*path = symlink_path;
439
440	return (NFS3_OK);
441}
442