xref: /openbsd/regress/sys/ffs/fstest.c (revision 49a6e16f)
1 /*	$OpenBSD: fstest.c,v 1.7 2021/12/13 16:56:49 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 2006-2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD: src/tools/regression/fstest/fstest.c,v 1.1 2007/01/17 01:42:07 pjd Exp $
29  */
30 
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/sysctl.h>
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <grp.h>
40 #include <string.h>
41 #include <ctype.h>
42 #include <errno.h>
43 #include <assert.h>
44 
45 enum action {
46 	ACTION_OPEN,
47 	ACTION_CREATE,
48 	ACTION_UNLINK,
49 	ACTION_MKDIR,
50 	ACTION_RMDIR,
51 	ACTION_LINK,
52 	ACTION_SYMLINK,
53 	ACTION_RENAME,
54 	ACTION_MKFIFO,
55 	ACTION_CHMOD,
56 	ACTION_CHOWN,
57 	ACTION_LCHOWN,
58 	ACTION_CHFLAGS,
59 	ACTION_LCHFLAGS,
60 	ACTION_TRUNCATE,
61 	ACTION_STAT,
62 	ACTION_LSTAT,
63 };
64 
65 #define	TYPE_NONE	0x0000
66 #define	TYPE_STRING	0x0001
67 #define	TYPE_NUMBER	0x0002
68 #define	TYPE_OPTIONAL	0x0100
69 #define	MAX_ARGS	8
70 
71 struct syscall_desc {
72 	char *sd_name;
73 	enum action  sd_action;
74 	int sd_args[MAX_ARGS];
75 };
76 
77 static struct syscall_desc syscalls[] = {
78 	{ "open", ACTION_OPEN, { TYPE_STRING, TYPE_STRING,
79 				 TYPE_NUMBER | TYPE_OPTIONAL, TYPE_NONE } },
80 	{ "create", ACTION_CREATE, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
81 	{ "unlink", ACTION_UNLINK, { TYPE_STRING, TYPE_NONE } },
82 	{ "mkdir", ACTION_MKDIR, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
83 	{ "rmdir", ACTION_RMDIR, { TYPE_STRING, TYPE_NONE } },
84 	{ "link", ACTION_LINK, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
85 	{ "symlink", ACTION_SYMLINK, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
86 	{ "rename", ACTION_RENAME, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
87 	{ "mkfifo", ACTION_MKFIFO, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
88 	{ "chmod", ACTION_CHMOD, { TYPE_STRING, TYPE_NUMBER, TYPE_NONE } },
89 	{ "chown", ACTION_CHOWN, { TYPE_STRING, TYPE_NUMBER,
90 				   TYPE_NUMBER, TYPE_NONE } },
91 	{ "lchown", ACTION_LCHOWN, { TYPE_STRING, TYPE_NUMBER,
92 				     TYPE_NUMBER, TYPE_NONE } },
93 	{ "chflags", ACTION_CHFLAGS, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
94 	{ "lchflags", ACTION_LCHFLAGS, { TYPE_STRING, TYPE_STRING,
95 					 TYPE_NONE } },
96 	{ "truncate", ACTION_TRUNCATE, { TYPE_STRING, TYPE_NUMBER,
97 					 TYPE_NONE } },
98 	{ "stat", ACTION_STAT, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
99 	{ "lstat", ACTION_LSTAT, { TYPE_STRING, TYPE_STRING, TYPE_NONE } },
100 	{ NULL, -1, { TYPE_NONE } }
101 };
102 
103 struct flag {
104 	long long f_flag;
105 	char *f_str;
106 };
107 
108 static struct flag open_flags[] = {
109 	{ O_RDONLY, "O_RDONLY" },
110 	{ O_WRONLY, "O_WRONLY" },
111 	{ O_RDWR, "O_RDWR" },
112 	{ O_NONBLOCK, "O_NONBLOCK" },
113 	{ O_APPEND, "O_APPEND" },
114 	{ O_CREAT, "O_CREAT" },
115 	{ O_TRUNC, "O_TRUNC" },
116 	{ O_EXCL, "O_EXCL" },
117 	{ O_SHLOCK, "O_SHLOCK" },
118 	{ O_EXLOCK, "O_EXLOCK" },
119 	{ O_FSYNC, "O_FSYNC" },
120 	{ O_SYNC, "O_SYNC" },
121 	{ O_NOFOLLOW, "O_NOFOLLOW" },
122 	{ O_NOCTTY, "O_NOCTTY" },
123 	{ 0, NULL }
124 };
125 
126 static struct flag chflags_flags[] = {
127 	{ UF_NODUMP, "UF_NODUMP" },
128 	{ UF_IMMUTABLE, "UF_IMMUTABLE" },
129 	{ UF_APPEND, "UF_APPEND" },
130 	{ UF_OPAQUE, "UF_OPAQUE" },
131 	{ SF_ARCHIVED, "SF_ARCHIVED" },
132 	{ SF_IMMUTABLE, "SF_IMMUTABLE" },
133 	{ SF_APPEND, "SF_APPEND" },
134 	{ 0, NULL }
135 };
136 
137 static const char *err2str(int error);
138 int use_appimm;		/* use the SF_APPEND and SF_IMMUTABLE chflags */
139 
140 __dead static void
usage(void)141 usage(void)
142 {
143 	fprintf(stderr, "usage: fstest [-u uid] [-g gid1[,gid2[...]]] syscall "
144 	    "args ...\n");
145 	exit(1);
146 }
147 
148 static long long
str2flags(struct flag * tflags,char * sflags)149 str2flags(struct flag *tflags, char *sflags)
150 {
151 	long long flags = 0;
152 	unsigned int i;
153 	char *f;
154 
155 	for (f = strtok(sflags, ","); f != NULL; f = strtok(NULL, ",")) {
156 		/* Support magic 'none' flag which just reset all flags. */
157 		if (strcmp(f, "none") == 0)
158 			return (0);
159 		for (i = 0; tflags[i].f_str != NULL; i++) {
160 			if (strcmp(tflags[i].f_str, f) == 0)
161 				break;
162 		}
163 		if (tflags[i].f_str == NULL) {
164 			fprintf(stderr, "unknown flag '%s'\n", f);
165 			exit(1);
166 		}
167 		flags |= tflags[i].f_flag;
168 	}
169 	return (flags);
170 }
171 
172 static char *
flags2str(struct flag * tflags,long long flags)173 flags2str(struct flag *tflags, long long flags)
174 {
175 	static char sflags[1024];
176 	unsigned int i;
177 
178 	sflags[0] = '\0';
179 	for (i = 0; tflags[i].f_str != NULL; i++) {
180 		if (flags & tflags[i].f_flag) {
181 			if (sflags[0] != '\0')
182 				strlcat(sflags, ",", sizeof(sflags));
183 			strlcat(sflags, tflags[i].f_str, sizeof(sflags));
184 		}
185 	}
186 	if (sflags[0] == '\0')
187 		strlcpy(sflags, "none", sizeof(sflags));
188 	return (sflags);
189 }
190 
191 static struct syscall_desc *
find_syscall(const char * name)192 find_syscall(const char *name)
193 {
194 	int i;
195 
196 	for (i = 0; syscalls[i].sd_name != NULL; i++) {
197 		if (strcmp(syscalls[i].sd_name, name) == 0)
198 			return (&syscalls[i]);
199 	}
200 	return (NULL);
201 }
202 
203 static void
show_stat(struct stat * sp,const char * what)204 show_stat(struct stat *sp, const char *what)
205 {
206 
207 	if (strcmp(what, "mode") == 0)
208 		printf("0%o", (unsigned int)(sp->st_mode & ALLPERMS));
209 	else if (strcmp(what, "inode") == 0)
210 		printf("%llu", (unsigned long long)sp->st_ino);
211 	else if (strcmp(what, "nlink") == 0)
212 		printf("%lld", (long long)sp->st_nlink);
213 	else if (strcmp(what, "uid") == 0)
214 		printf("%d", (int)sp->st_uid);
215 	else if (strcmp(what, "gid") == 0)
216 		printf("%d", (int)sp->st_gid);
217 	else if (strcmp(what, "size") == 0)
218 		printf("%lld", (long long)sp->st_size);
219 	else if (strcmp(what, "blocks") == 0)
220 		printf("%lld", (long long)sp->st_blocks);
221 	else if (strcmp(what, "atime") == 0)
222 		printf("%lld", (long long)sp->st_atime);
223 	else if (strcmp(what, "mtime") == 0)
224 		printf("%lld", (long long)sp->st_mtime);
225 	else if (strcmp(what, "ctime") == 0)
226 		printf("%lld", (long long)sp->st_ctime);
227 	else if (strcmp(what, "flags") == 0)
228 		printf("%s", flags2str(chflags_flags, sp->st_flags));
229 	else if (strcmp(what, "type") == 0) {
230 		switch (sp->st_mode & S_IFMT) {
231 		case S_IFIFO:
232 			printf("fifo");
233 			break;
234 		case S_IFCHR:
235 			printf("char");
236 			break;
237 		case S_IFDIR:
238 			printf("dir");
239 			break;
240 		case S_IFBLK:
241 			printf("block");
242 			break;
243 		case S_IFREG:
244 			printf("regular");
245 			break;
246 		case S_IFLNK:
247 			printf("symlink");
248 			break;
249 		case S_IFSOCK:
250 			printf("socket");
251 			break;
252 		default:
253 			printf("unknown");
254 			break;
255 		}
256 	} else {
257 		printf("unknown");
258 	}
259 }
260 
261 static void
show_stats(struct stat * sp,char * what)262 show_stats(struct stat *sp, char *what)
263 {
264 	const char *s = "";
265 	char *w;
266 
267 	for (w = strtok(what, ","); w != NULL; w = strtok(NULL, ",")) {
268 		printf("%s", s);
269 		show_stat(sp, w);
270 		s = ",";
271 	}
272 	printf("\n");
273 }
274 
275 static unsigned int
call_syscall(struct syscall_desc * scall,char * argv[])276 call_syscall(struct syscall_desc *scall, char *argv[])
277 {
278 	struct stat sb;
279 	long long flags;
280 	unsigned int i;
281 	char *endp;
282 	int rval;
283 	union {
284 		char *str;
285 		long long num;
286 	} args[MAX_ARGS];
287 	unsigned int ch_flags;
288 
289 	/*
290 	 * Verify correctness of the arguments.
291 	 */
292 	for (i = 0; i < sizeof(args)/sizeof(args[0]); i++) {
293 		if (scall->sd_args[i] == TYPE_NONE) {
294 			if (argv[i] == NULL || strcmp(argv[i], ":") == 0)
295 				break;
296 			fprintf(stderr, "too many arguments [%s]\n", argv[i]);
297 			exit(1);
298 		} else {
299 			if (argv[i] == NULL || strcmp(argv[i], ":") == 0) {
300 				if (scall->sd_args[i] & TYPE_OPTIONAL)
301 					break;
302 				fprintf(stderr, "too few arguments\n");
303 				exit(1);
304 			}
305 			if (scall->sd_args[i] & TYPE_STRING) {
306 				if (strcmp(argv[i], "NULL") == 0)
307 					args[i].str = NULL;
308 				else if (strcmp(argv[i], "DEADCODE") == 0)
309 					args[i].str = (void *)0xdeadc0de;
310 				else
311 					args[i].str = argv[i];
312 			} else if (scall->sd_args[i] & TYPE_NUMBER) {
313 				args[i].num = strtoll(argv[i], &endp, 0);
314 				if (*endp != '\0' &&
315 				    !isspace((unsigned char)*endp)) {
316 					fprintf(stderr, "invalid argument %u, "
317 					    "number expected [%s]\n", i, endp);
318 					exit(1);
319 				}
320 			}
321 		}
322 	}
323 	/*
324 	 * Call the given syscall.
325 	 */
326 #define	NUM(n)	(args[(n)].num)
327 #define	STR(n)	(args[(n)].str)
328 	switch (scall->sd_action) {
329 	case ACTION_OPEN:
330 		flags = str2flags(open_flags, STR(1));
331 		if (flags & O_CREAT) {
332 			if (i == 2) {
333 				fprintf(stderr, "too few arguments\n");
334 				exit(1);
335 			}
336 			rval = open(STR(0), flags, (mode_t)NUM(2));
337 		} else {
338 			if (i == 3) {
339 				fprintf(stderr, "too many arguments\n");
340 				exit(1);
341 			}
342 			rval = open(STR(0), flags);
343 		}
344 		break;
345 	case ACTION_CREATE:
346 		rval = open(STR(0), O_CREAT | O_EXCL, NUM(1));
347 		if (rval >= 0)
348 			close(rval);
349 		break;
350 	case ACTION_UNLINK:
351 		rval = unlink(STR(0));
352 		break;
353 	case ACTION_MKDIR:
354 		rval = mkdir(STR(0), NUM(1));
355 		break;
356 	case ACTION_RMDIR:
357 		rval = rmdir(STR(0));
358 		break;
359 	case ACTION_LINK:
360 		rval = link(STR(0), STR(1));
361 		break;
362 	case ACTION_SYMLINK:
363 		rval = symlink(STR(0), STR(1));
364 		break;
365 	case ACTION_RENAME:
366 		rval = rename(STR(0), STR(1));
367 		break;
368 	case ACTION_MKFIFO:
369 		rval = mkfifo(STR(0), NUM(1));
370 		break;
371 	case ACTION_CHMOD:
372 		rval = chmod(STR(0), NUM(1));
373 		break;
374 	case ACTION_CHOWN:
375 		rval = chown(STR(0), NUM(1), NUM(2));
376 		break;
377 	case ACTION_LCHOWN:
378 		rval = lchown(STR(0), NUM(1), NUM(2));
379 		break;
380 	case ACTION_CHFLAGS:
381 		ch_flags = str2flags(chflags_flags, STR(1));
382 		if (!use_appimm)
383 			ch_flags &= ~(SF_APPEND|SF_IMMUTABLE);
384 		rval = chflags(STR(0), ch_flags);
385 		break;
386 	case ACTION_LCHFLAGS:
387 		ch_flags = str2flags(chflags_flags, STR(1));
388 		if (!use_appimm)
389 			ch_flags &= ~(SF_APPEND|SF_IMMUTABLE);
390 		rval = chflagsat(AT_FDCWD, STR(0), ch_flags,
391 		    AT_SYMLINK_NOFOLLOW);
392 		break;
393 	case ACTION_TRUNCATE:
394 		rval = truncate(STR(0), NUM(1));
395 		break;
396 	case ACTION_STAT:
397 		rval = stat(STR(0), &sb);
398 		if (rval == 0) {
399 			show_stats(&sb, STR(1));
400 			return (i);
401 		}
402 		break;
403 	case ACTION_LSTAT:
404 		rval = lstat(STR(0), &sb);
405 		if (rval == 0) {
406 			show_stats(&sb, STR(1));
407 			return (i);
408 		}
409 		break;
410 	default:
411 		fprintf(stderr, "unsupported syscall\n");
412 		exit(1);
413 	}
414 #undef STR
415 #undef NUM
416 	if (rval < 0) {
417 		printf("%s\n", err2str(errno));
418 		exit(1);
419 	}
420 	printf("0\n");
421 	return (i);
422 }
423 
424 static void
set_gids(char * gids)425 set_gids(char *gids)
426 {
427 	gid_t *gidset;
428 	long ngroups;
429 	char *g, *endp;
430 	unsigned i;
431 
432 	ngroups = sysconf(_SC_NGROUPS_MAX);
433 	assert(ngroups > 0);
434 	gidset = reallocarray(NULL, ngroups, sizeof(*gidset));
435 	assert(gidset != NULL);
436 	for (i = 0, g = strtok(gids, ","); g != NULL;
437 	    g = strtok(NULL, ","), i++) {
438 		if (i >= ngroups) {
439 			fprintf(stderr, "too many gids\n");
440 			exit(1);
441 		}
442 		gidset[i] = strtol(g, &endp, 0);
443 		if (*endp != '\0' && !isspace((unsigned char)*endp)) {
444 			fprintf(stderr, "invalid gid '%s' - number expected\n",
445 			    g);
446 			exit(1);
447 		}
448 	}
449 	if (setgroups(i, gidset) < 0) {
450 		fprintf(stderr, "cannot change groups: %s\n", strerror(errno));
451 		exit(1);
452 	}
453 	free(gidset);
454 }
455 
456 int
main(int argc,char * argv[])457 main(int argc, char *argv[])
458 {
459 	struct syscall_desc *scall;
460 	unsigned int n;
461 	char *gids, *endp;
462 	int uid, umsk, ch;
463 	int mib[2];
464 	size_t len;
465 	int securelevel;
466 
467 	uid = -1;
468 	gids = NULL;
469 	umsk = 0;
470 
471 	while ((ch = getopt(argc, argv, "g:u:U:")) != -1) {
472 		switch(ch) {
473 		case 'g':
474 			gids = optarg;
475 			break;
476 		case 'u':
477 			uid = (int)strtol(optarg, &endp, 0);
478 			if (*endp != '\0' && !isspace((unsigned char)*endp)) {
479 				fprintf(stderr, "invalid uid '%s' - number "
480 				    "expected\n", optarg);
481 				exit(1);
482 			}
483 			break;
484 		case 'U':
485 			umsk = (int)strtol(optarg, &endp, 0);
486 			if (*endp != '\0' && !isspace((unsigned char)*endp)) {
487 				fprintf(stderr, "invalid umask '%s' - number "
488 				    "expected\n", optarg);
489 				exit(1);
490 			}
491 			break;
492 		default:
493 			usage();
494 		}
495 	}
496 	argc -= optind;
497 	argv += optind;
498 
499 	if (argc < 1) {
500 		fprintf(stderr, "too few arguments\n");
501 		usage();
502 	}
503 
504 	if (gids != NULL)
505 		set_gids(gids);
506 	if (uid != -1) {
507 		if (setuid(uid) < 0) {
508 			fprintf(stderr, "cannot change uid: %s\n",
509 			    strerror(errno));
510 			exit(1);
511 		}
512 	}
513 
514 	/*
515 	 * Find out if we should use the SF_IMMUTABLE and SF_APPEND flags;
516 	 * Since we run by default on kern.securelevel=1 these cause false
517 	 * positives.
518 	 */
519 	mib[0] = CTL_KERN;
520 	mib[1] = KERN_SECURELVL;
521 	len = sizeof(securelevel);
522 	if (sysctl(mib, 2, &securelevel, &len, NULL, 0) == -1) {
523 		fprintf(stderr, "cannot get kernel securelevel\n");
524 		exit(1);
525 	}
526 	if (securelevel == 0 || securelevel == -1)
527 		use_appimm = 1;
528 	else
529 		use_appimm = 0;
530 
531 	/* Change umask to requested value or to 0, if not requested. */
532 	umask(umsk);
533 
534 	for (;;) {
535 		scall = find_syscall(argv[0]);
536 		if (scall == NULL) {
537 			fprintf(stderr, "syscall '%s' not supported\n",
538 			    argv[0]);
539 			exit(1);
540 		}
541 		argc++;
542 		argv++;
543 		n = call_syscall(scall, argv);
544 		argc += n;
545 		argv += n;
546 		if (argv[0] == NULL)
547 			break;
548 		argc++;
549 		argv++;
550 	}
551 
552 	exit(0);
553 }
554 
555 static const char *
err2str(int error)556 err2str(int error)
557 {
558 	static char errnum[8];
559 
560 	switch (error) {
561 	case EPERM:
562 		return ("EPERM");
563 	case ENOENT:
564 		return ("ENOENT");
565 	case ESRCH:
566 		return ("ESRCH");
567 	case EINTR:
568 		return ("EINTR");
569 	case EIO:
570 		return ("EIO");
571 	case ENXIO:
572 		return ("ENXIO");
573 	case E2BIG:
574 		return ("E2BIG");
575 	case ENOEXEC:
576 		return ("ENOEXEC");
577 	case EBADF:
578 		return ("EBADF");
579 	case ECHILD:
580 		return ("ECHILD");
581 	case EDEADLK:
582 		return ("EDEADLK");
583 	case ENOMEM:
584 		return ("ENOMEM");
585 	case EACCES:
586 		return ("EACCES");
587 	case EFAULT:
588 		return ("EFAULT");
589 	case ENOTBLK:
590 		return ("ENOTBLK");
591 	case EBUSY:
592 		return ("EBUSY");
593 	case EEXIST:
594 		return ("EEXIST");
595 	case EXDEV:
596 		return ("EXDEV");
597 	case ENODEV:
598 		return ("ENODEV");
599 	case ENOTDIR:
600 		return ("ENOTDIR");
601 	case EISDIR:
602 		return ("EISDIR");
603 	case EINVAL:
604 		return ("EINVAL");
605 	case ENFILE:
606 		return ("ENFILE");
607 	case EMFILE:
608 		return ("EMFILE");
609 	case ENOTTY:
610 		return ("ENOTTY");
611 	case ETXTBSY:
612 		return ("ETXTBSY");
613 	case EFBIG:
614 		return ("EFBIG");
615 	case ENOSPC:
616 		return ("ENOSPC");
617 	case ESPIPE:
618 		return ("ESPIPE");
619 	case EROFS:
620 		return ("EROFS");
621 	case EMLINK:
622 		return ("EMLINK");
623 	case EPIPE:
624 		return ("EPIPE");
625 	case EDOM:
626 		return ("EDOM");
627 	case ERANGE:
628 		return ("ERANGE");
629 	case EAGAIN:
630 		return ("EAGAIN");
631 	case EINPROGRESS:
632 		return ("EINPROGRESS");
633 	case EALREADY:
634 		return ("EALREADY");
635 	case ENOTSOCK:
636 		return ("ENOTSOCK");
637 	case EDESTADDRREQ:
638 		return ("EDESTADDRREQ");
639 	case EMSGSIZE:
640 		return ("EMSGSIZE");
641 	case EPROTOTYPE:
642 		return ("EPROTOTYPE");
643 	case ENOPROTOOPT:
644 		return ("ENOPROTOOPT");
645 	case EPROTONOSUPPORT:
646 		return ("EPROTONOSUPPORT");
647 	case ESOCKTNOSUPPORT:
648 		return ("ESOCKTNOSUPPORT");
649 	case EOPNOTSUPP:
650 		return ("EOPNOTSUPP");
651 	case EPFNOSUPPORT:
652 		return ("EPFNOSUPPORT");
653 	case EAFNOSUPPORT:
654 		return ("EAFNOSUPPORT");
655 	case EADDRINUSE:
656 		return ("EADDRINUSE");
657 	case EADDRNOTAVAIL:
658 		return ("EADDRNOTAVAIL");
659 	case ENETDOWN:
660 		return ("ENETDOWN");
661 	case ENETUNREACH:
662 		return ("ENETUNREACH");
663 	case ENETRESET:
664 		return ("ENETRESET");
665 	case ECONNABORTED:
666 		return ("ECONNABORTED");
667 	case ECONNRESET:
668 		return ("ECONNRESET");
669 	case ENOBUFS:
670 		return ("ENOBUFS");
671 	case EISCONN:
672 		return ("EISCONN");
673 	case ENOTCONN:
674 		return ("ENOTCONN");
675 	case ESHUTDOWN:
676 		return ("ESHUTDOWN");
677 	case ETOOMANYREFS:
678 		return ("ETOOMANYREFS");
679 	case ETIMEDOUT:
680 		return ("ETIMEDOUT");
681 	case ECONNREFUSED:
682 		return ("ECONNREFUSED");
683 	case ELOOP:
684 		return ("ELOOP");
685 	case ENAMETOOLONG:
686 		return ("ENAMETOOLONG");
687 	case EHOSTDOWN:
688 		return ("EHOSTDOWN");
689 	case EHOSTUNREACH:
690 		return ("EHOSTUNREACH");
691 	case ENOTEMPTY:
692 		return ("ENOTEMPTY");
693 	case EPROCLIM:
694 		return ("EPROCLIM");
695 	case EUSERS:
696 		return ("EUSERS");
697 	case EDQUOT:
698 		return ("EDQUOT");
699 	case ESTALE:
700 		return ("ESTALE");
701 	case EREMOTE:
702 		return ("EREMOTE");
703 	case EBADRPC:
704 		return ("EBADRPC");
705 	case ERPCMISMATCH:
706 		return ("ERPCMISMATCH");
707 	case EPROGUNAVAIL:
708 		return ("EPROGUNAVAIL");
709 	case EPROGMISMATCH:
710 		return ("EPROGMISMATCH");
711 	case EPROCUNAVAIL:
712 		return ("EPROCUNAVAIL");
713 	case ENOLCK:
714 		return ("ENOLCK");
715 	case ENOSYS:
716 		return ("ENOSYS");
717 	case EFTYPE:
718 		return ("EFTYPE");
719 	case EAUTH:
720 		return ("EAUTH");
721 	case ENEEDAUTH:
722 		return ("ENEEDAUTH");
723 	case EILSEQ:
724 		return ("EILSEQ");
725 	case ENOATTR:
726 		return ("ENOATTR");
727 	default:
728 		snprintf(errnum, sizeof(errnum), "%d", error);
729 		return (errnum);
730 	}
731 }
732