xref: /illumos-gate/usr/src/stand/lib/fs/nfs/nfsops.c (revision 2a8d6eba033e4713ab12b61178f0513f1f075482)
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 2007 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 #include "socket_inet.h"
49 #include "mac.h"
50 #include <sys/mode.h>
51 
52 ushort_t vttoif_tab[] = {
53 	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFIFO,
54 	S_IFDOOR, 0, S_IFSOCK, 0
55 };
56 
57 static int file_desc = 1;
58 static struct nfs_files {
59 	struct nfs_file file;
60 	int	desc;
61 	struct nfs_files *next;
62 } nfs_files[1] = {
63 	{0, 0, 0},
64 };
65 
66 #define	dprintf	if (boothowto & RB_DEBUG) printf
67 
68 static int	boot_nfs_open(char *filename, int flags);
69 static int	boot_nfs_close(int fd);
70 static ssize_t	boot_nfs_read(int fd, caddr_t buf, size_t size);
71 static off_t	boot_nfs_lseek(int, off_t, int);
72 static int	boot_nfs_fstat(int fd, struct bootstat *stp);
73 static void	boot_nfs_closeall(int flag);
74 static int	boot_nfs_getdents(int fd, struct dirent *dep, unsigned size);
75 
76 struct boot_fs_ops boot_nfs_ops = {
77 	"nfs",
78 	boot_nfs_mountroot,
79 	boot_nfs_unmountroot,
80 	boot_nfs_open,
81 	boot_nfs_close,
82 	boot_nfs_read,
83 	boot_nfs_lseek,
84 	boot_nfs_fstat,
85 	boot_nfs_closeall,
86 	boot_nfs_getdents
87 };
88 
89 /*
90  * bootops.c calls a closeall() function to close all open files. Since
91  * we only permit one open file at a time (not counting the device), this
92  * is simple to implement.
93  */
94 
95 /*ARGSUSED*/
96 static void
97 boot_nfs_closeall(int flag)
98 {
99 	struct nfs_files	*filep;
100 
101 #ifdef NFS_OPS_DEBUG
102 	if ((boothowto & DBFLAGS) == DBFLAGS)
103 		printf("boot_nfs_closeall(%x)\n", flag);
104 #endif
105 
106 	if (nfs_files->file.version == 0 &&
107 	    nfs_files->desc == 0 &&
108 	    nfs_files->next == NULL)
109 		return;
110 
111 	/* delete any dynamically allocated entries */
112 	while ((filep = nfs_files->next) != NULL) {
113 		nfs_files->next = filep->next;
114 		bkmem_free((caddr_t)filep, sizeof (struct  nfs_files));
115 	}
116 
117 	/* clear the first, static file */
118 	bzero((caddr_t)nfs_files, sizeof (struct nfs_files));
119 
120 	/* Close device */
121 	release_cache(mac_get_dev());
122 
123 	mac_fini();
124 }
125 
126 /*
127  * Get a file pointer given a file descriptor.  Return 0 on error
128  */
129 static struct nfs_files *
130 get_filep(int fd)
131 {
132 	struct nfs_files *filep;
133 
134 	for (filep = nfs_files; filep; filep = filep->next) {
135 		if (fd == filep->desc)
136 			return (filep);
137 	}
138 	return (NULL);
139 }
140 
141 /*
142  * Unmount the root fs -- not supported for this fstype.
143  */
144 
145 int
146 boot_nfs_unmountroot(void)
147 {
148 	return (-1);
149 }
150 
151 /*
152  * open a file for reading. Note: writing is NOT supported.
153  */
154 
155 static int
156 boot_nfs_open(char *path, int flags)
157 {
158 	struct nfs_files *filep, *newfilep;
159 	int got_filep;
160 
161 #ifdef NFS_OPS_DEBUG
162 	if ((boothowto & DBFLAGS) == DBFLAGS)
163 		printf("boot_nfs_open(%s, %x)\n", path, flags);
164 #endif
165 
166 	/* file can only be opened readonly. */
167 	if (flags & ~O_RDONLY) {
168 		dprintf("boot_nfs_open: files can only be opened O_RDONLY.\n");
169 		return (-1);
170 	}
171 
172 	if (path == NULL || *path == '\0') {
173 		dprintf("boot_nfs_open: NULL or EMPTY pathname argument.\n");
174 		return (-1);
175 	}
176 
177 	/* Try and find a vacant file pointer */
178 	filep = nfs_files;
179 	got_filep = FALSE;
180 	do {
181 		if (filep->desc == 0) {
182 			filep->desc = file_desc++;
183 			got_filep = TRUE;
184 			break;		/* We've got a file pointer */
185 		}
186 		/* Get next entry if not at end of list */
187 		if (filep->next)
188 			filep = filep->next;
189 	} while (filep->next);
190 
191 	/* If a a vacant file pointer cannot be found, make one */
192 	if (!got_filep) {
193 		if ((newfilep = (struct nfs_files *)
194 		    bkmem_zalloc(sizeof (struct nfs_files))) == 0) {
195 			dprintf("open: Cannot allocate file pointer\n");
196 			return (-1);
197 		}
198 		filep->next = newfilep;
199 		filep = newfilep;
200 		filep->desc = file_desc++;
201 	}
202 
203 	if (lookup(path, &filep->file, FALSE) != 0) {
204 #ifdef NFS_OPS_DEBUG
205 		if ((boothowto & DBFLAGS) == DBFLAGS)
206 			printf("boot_nfs_open(): Cannot open '%s'.\n", path);
207 #endif
208 		/* zero file pointer */
209 		bzero((caddr_t)filep, sizeof (struct nfs_file));
210 		filep->desc = 0;
211 		return (-1);
212 	}
213 	bzero(&filep->file.cookie, sizeof (filep->file.cookie));
214 
215 #ifdef NFS_OPS_DEBUG
216 	if ((boothowto & DBFLAGS) == DBFLAGS)
217 		printf("boot_nfs_open(): '%s' successful, fd = 0x%x\n",
218 		    path, filep->desc);
219 #endif
220 	return (filep->desc);
221 }
222 
223 /*
224  * close a previously opened file.
225  */
226 static int
227 boot_nfs_close(int fd)
228 {
229 	struct nfs_files *filep;
230 
231 #ifdef NFS_OPS_DEBUG
232 	if ((boothowto & DBFLAGS) == DBFLAGS)
233 		printf("boot_nfs_close(%d)\n", fd);
234 #endif
235 	if ((filep = get_filep(fd)) == 0)
236 		return (0);
237 
238 	/*
239 	 * zero file pointer
240 	 */
241 	bzero((caddr_t)&filep->file, sizeof (struct nfs_file));
242 
243 	/*
244 	 * "close" the fd.
245 	 */
246 	filep->desc = 0;
247 
248 	return (0);
249 }
250 
251 /*
252  * read from a file.
253  */
254 static ssize_t
255 boot_nfs_read(int fd, char *buf, size_t size)
256 {
257 	struct nfs_files	*filep;
258 	int			count = 0;
259 
260 	if (fd == 0) {
261 		dprintf("boot_nfs_read: Bad file number.\n");
262 		return (-1);
263 	}
264 	if (buf == NULL) {
265 		dprintf("boot_nfs_read: Bad address.\n");
266 		return (-1);
267 	}
268 
269 #ifdef NFS_OPS_DEBUG
270 	if ((boothowto & DBFLAGS) == DBFLAGS)
271 		printf("boot_nfs_read(%d, %x, 0x%x)\n", fd, buf, size);
272 #endif
273 
274 	/* initialize for read */
275 	if ((filep = get_filep(fd)) == 0)
276 		return (-1);
277 
278 	switch (filep->file.version) {
279 	case NFS_VERSION:
280 		count = nfsread(&filep->file, buf, size);
281 		break;
282 	case NFS_V3:
283 		count = nfs3read(&filep->file, buf, size);
284 		break;
285 	case NFS_V4:
286 		count = nfs4read(&filep->file, buf, size);
287 		break;
288 	default:
289 		printf("boot_nfs_read: NFS Version %d not supported\n",
290 		    filep->file.version);
291 		count = -1;
292 		break;
293 	}
294 
295 #ifdef NFS_OPS_DEBUG
296 	if ((boothowto & DBFLAGS) == DBFLAGS)
297 		printf("boot_nfs_read(): 0x%x bytes.\n", count);
298 #endif
299 	return (count);
300 }
301 
302 /*
303  * lseek - move read file pointer.
304  */
305 
306 static off_t
307 boot_nfs_lseek(int fd, off_t offset, int whence)
308 {
309 	struct nfs_files *filep;
310 
311 #ifdef NFS_OPS_DEBUG
312 	if ((boothowto & DBFLAGS) == DBFLAGS)
313 		printf("boot_nfs_lseek(%d, 0x%x, %d)\n", fd, offset, whence);
314 #endif
315 
316 	if (fd == 0) {
317 		dprintf("boot_nfs_lseek: Bad file number.\n");
318 		return (-1);
319 	}
320 
321 	if ((filep = get_filep(fd)) == 0)
322 		return (-1);
323 
324 	switch (whence) {
325 
326 	case SEEK_SET:
327 		/*
328 		 * file ptr is set to offset from beginning of file
329 		 */
330 		filep->file.offset = offset;
331 		break;
332 	case SEEK_CUR:
333 		/*
334 		 * file ptr is set to offset from current position
335 		 */
336 		filep->file.offset += offset;
337 		break;
338 	case SEEK_END:
339 		/*
340 		 * file ptr is set to current size of file plus offset.
341 		 * But since we only support reading, this is illegal.
342 		 */
343 	default:
344 		/*
345 		 * invalid offset origin
346 		 */
347 		dprintf("boot_nfs_lseek: invalid whence value.\n");
348 		return (-1);
349 	}
350 
351 #ifdef notyet
352 	return (filep->file.offset);
353 #else
354 	/*
355 	 * BROKE - lseek should return the offset seeked to on a
356 	 * successful seek, not zero - This must be fixed in the
357 	 * kernel before It can be fixed here.
358 	 */
359 	return (0);
360 #endif /* notyet */
361 }
362 
363 /*
364  * This version of fstat supports mode, size, inode #, and times only.
365  * It can be enhanced if more is required,
366  */
367 
368 static int
369 boot_nfs_fstat(int fd, struct bootstat *stp)
370 {
371 	struct vattr va;
372 	struct nfs_files *filep;
373 	int status;
374 
375 #ifdef NFS_OPS_DEBUG
376 	if ((boothowto & DBFLAGS) == DBFLAGS) {
377 		printf("boot_nfs_fstat(%d, 0x%x)\n", fd, stp);
378 	}
379 #endif
380 	if (fd == 0) {
381 		dprintf("boot_nfs_fstat(): Bad file number 0.\n");
382 		return (-1);
383 	}
384 
385 	if ((filep = get_filep(fd)) == 0)
386 		return (-1);
387 
388 	bzero((char *)&va, sizeof (va));
389 	va.va_mask = AT_TYPE | AT_SIZE | AT_MODE | AT_NODEID |
390 	    AT_ATIME | AT_CTIME | AT_MTIME;
391 
392 	switch (filep->file.version) {
393 	case NFS_VERSION:
394 		status = nfsgetattr(&filep->file, &va);
395 		break;
396 	case NFS_V3:
397 		status = nfs3getattr(&filep->file, &va);
398 		break;
399 	case NFS_V4:
400 		status = nfs4getattr(&filep->file, &va);
401 		break;
402 	default:
403 		printf("boot_nfs_fstat: NFS Version %d not supported\n",
404 		    filep->file.version);
405 		status = -1;
406 		break;
407 	}
408 
409 	if (status != 0)
410 		return (-1);
411 
412 	if (va.va_size > (u_offset_t)MAXOFF_T) {
413 		dprintf("boot_nfs_fstat(): File too large.\n");
414 		return (-1);
415 	}
416 	stp->st_size = (off_t)va.va_size;
417 	stp->st_mode = VTTOIF(va.va_type) | va.va_mode;
418 	stp->st_atim.tv_sec = va.va_atime.tv_sec;
419 	stp->st_atim.tv_nsec = va.va_atime.tv_nsec;
420 	stp->st_ctim.tv_sec = va.va_ctime.tv_sec;
421 	stp->st_ctim.tv_nsec = va.va_ctime.tv_nsec;
422 	stp->st_mtim.tv_sec = va.va_mtime.tv_sec;
423 	stp->st_mtim.tv_nsec = va.va_mtime.tv_nsec;
424 	stp->st_ino = (ino_t)va.va_nodeid;
425 
426 #ifdef NFS_OPS_DEBUG
427 	if ((boothowto & DBFLAGS) == DBFLAGS)
428 		printf("boot_nfs_fstat(): done.\n");
429 #endif
430 	return (0);
431 }
432 
433 static int
434 boot_nfs_getdents(int fd, struct dirent *dep, unsigned size)
435 {
436 	struct nfs_files *filep;
437 	int status;
438 
439 #ifdef NFS_OPS_DEBUG
440 	if ((boothowto & DBFLAGS) == DBFLAGS) {
441 		printf("boot_nfs_getdents(%d, 0x%x, 0x%x)\n", fd, dep, size);
442 	}
443 #endif
444 
445 	if (fd == 0) {
446 		dprintf("boot_nfs_getdents(): Bad file number 0.\n");
447 		return (-1);
448 	}
449 
450 	if ((filep = get_filep(fd)) == 0)
451 		return (-1);
452 
453 	switch (filep->file.version) {
454 	case NFS_VERSION:
455 		status = nfsgetdents(&filep->file, dep, size);
456 		break;
457 	case NFS_V3:
458 		status = nfs3getdents(&filep->file, dep, size);
459 		break;
460 	default:
461 		printf("boot_nfs_getdents: NFS Version %d not supported\n",
462 		    filep->file.version);
463 		status = -1;
464 	}
465 
466 #ifdef NFS_OPS_DEBUG
467 	if ((boothowto & DBFLAGS) == DBFLAGS)
468 		printf("boot_nfs_getdents(): done.\n");
469 #endif
470 	return (status);
471 }
472