xref: /titanic_52/usr/src/stand/lib/fs/nfs/nfs2ops.c (revision 4a5d661a82b942b6538acd26209d959ce98b593a)
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 
51 static struct timeval zero_timeout = {0, 0};	/* default */
52 
53 /*
54  * NFS Version 2 specific functions
55  */
56 
57 ssize_t
58 nfsread(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 
178 static vtype_t nf_to_vt[] = {
179 	VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK
180 };
181 
182 int
183 nfsgetattr(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*/
242 void
243 nfs_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 
308 struct nfs_file *
309 nfslookup(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  */
344 int
345 nfsgetsymlink(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