xref: /illumos-gate/usr/src/stand/lib/fs/nfs/nfs3ops.c (revision 4a634bb8)
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 
55 ssize_t
nfs3read(struct nfs_file * filep,char * buf,size_t size)56 nfs3read(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 
182 int
nfs3getattr(struct nfs_file * nfp,struct vattr * vap)183 nfs3getattr(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*/
246 void
nfs3_error(enum nfsstat3 status)247 nfs3_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 
348 struct nfs_file *
nfs3lookup(struct nfs_file * dir,char * name,int * nstat)349 nfs3lookup(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  */
406 int
nfs3getsymlink(struct nfs_file * cfile,char ** path)407 nfs3getsymlink(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