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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * This file contains code to support better NFS error messages.  Death to
29  * integer codes in user error messages!
30  *
31  * XXX Ideally this code should be more general and available to the entire
32  * kernel (see RFE 1101936).  When this happens, this file can go away.
33  */
34 
35 #include <nfs/nfs.h>
36 #include <sys/null.h>
37 #include <sys/systm.h>
38 #include <sys/cmn_err.h>
39 #include <sys/errno.h>
40 #include <sys/varargs.h>
41 
42 /* size of a temporary printf format buffer. */
43 #define	FMT_BUF_SIZE	1024
44 
45 static void expand_format_string(int, const char *, char *, int);
46 static char *nfs_strerror(int);
47 
48 /*
49  * nfs_perror: Works like printf (format string and variable args) except
50  * that it will substitute an error message for a "%m" string (like
51  * syslog), using the given errno value.
52  */
53 
54 void
nfs_perror(int error,char * fmt,...)55 nfs_perror(int error, char *fmt, ...)
56 {
57 	va_list ap;
58 	char buf[FMT_BUF_SIZE];		/* massaged version of fmt */
59 
60 	/* Expand %m */
61 
62 	expand_format_string(error, fmt, buf, FMT_BUF_SIZE);
63 
64 	/*
65 	 * Now pass the massaged format string and its arguments off to
66 	 * printf.
67 	 */
68 
69 	va_start(ap, fmt);
70 	(void) vzprintf(getzoneid(), buf, ap);
71 	va_end(ap);
72 }
73 
74 /*
75  * nfs_cmn_err: Works like cmn_err (error level, format string, and
76  * variable args) except that it will substitute an error message for a
77  * "%m" string (like syslog), using the given errno value.
78  */
79 
80 void
nfs_cmn_err(int error,int level,char * fmt,...)81 nfs_cmn_err(int error, int level, char *fmt, ...)
82 {
83 	va_list ap;
84 	char buf[FMT_BUF_SIZE];		/* massaged version of fmt */
85 
86 	/* Expand %m */
87 
88 	expand_format_string(error, fmt, buf, FMT_BUF_SIZE);
89 
90 	/*
91 	 * Now pass the massaged format string and its arguments off to
92 	 * cmn_err.
93 	 */
94 
95 	va_start(ap, fmt);
96 	(void) vzcmn_err(getzoneid(), level, buf, ap);
97 	va_end(ap);
98 }
99 
100 /*
101  * expand_format_string: copy the printf format string from "fmt" to "buf",
102  * expanding %m to the error string for "error".
103  */
104 
105 static void
expand_format_string(int error,const char * fmt,char * buf,int buf_chars)106 expand_format_string(int error, const char *fmt, char *buf, int buf_chars)
107 {
108 	const char *from;		/* pointer into fmt */
109 	char *to;			/* pointer into buf */
110 	char *errmsg;			/* expansion for %m */
111 	char *trunc_msg = "Truncated NFS error message: ";
112 	zoneid_t zoneid = getzoneid();
113 
114 	/*
115 	 * Copy the given format string into the result buffer, expanding
116 	 * %m as we go.  If the result buffer is too short, complain and
117 	 * truncate the message.  (We don't expect this to ever happen,
118 	 * though.)
119 	 */
120 
121 	for (from = fmt, to = buf; *from; from++) {
122 		if (to >= buf + buf_chars - 1) {
123 			zprintf(zoneid, trunc_msg);
124 			break;
125 		}
126 		if (*from == '%' && *(from+1) == 'm') {
127 			errmsg = nfs_strerror(error);
128 			/*
129 			 * If there's an error message and room to display
130 			 * it, copy it in.  If there's no message or not
131 			 * enough room, try just printing an error number.
132 			 * (We assume that the error value is in a
133 			 * reasonable range.)  If there's no room for
134 			 * anything, bail out.
135 			 */
136 			if (errmsg != NULL &&
137 			    strlen(buf) + strlen(errmsg) < buf_chars) {
138 				(void) strcpy(to, errmsg);
139 				to += strlen(errmsg);
140 			} else if (strlen(buf) + strlen("error XXX") <
141 			    buf_chars) {
142 				(void) sprintf(to, "error %d", error);
143 				/*
144 				 * Don't try to guess how many characters
145 				 * were laid down.
146 				 */
147 				to = buf + strlen(buf);
148 			} else {
149 				zprintf(zoneid, trunc_msg);
150 				break;
151 			}
152 			from++;
153 		} else {
154 			*to++ = *from;
155 		}
156 	}
157 	*to = '\0';
158 }
159 
160 /*
161  * nfs_strerror: map an errno value to a string.  Not all possible errno
162  * values are supported.
163  *
164  * If there is no string for the given errno value, return NULL.
165  */
166 
167 static char *
nfs_strerror(int errcode)168 nfs_strerror(int errcode)
169 {
170 	char *result;
171 
172 	switch (errcode) {
173 	case EPERM:
174 		result = "Not owner";
175 		break;
176 	case ENOENT:
177 		result = "No such file or directory";
178 		break;
179 	case EIO:
180 		result = "I/O error";
181 		break;
182 	case EACCES:
183 		result = "Permission denied";
184 		break;
185 	case EEXIST:
186 		result = "File exists";
187 		break;
188 	case ENOTDIR:
189 		result = "Not a directory";
190 		break;
191 	case EISDIR:
192 		result = "Is a directory";
193 		break;
194 	case EINVAL:
195 		result = "Invalid argument";
196 		break;
197 	case EFBIG:
198 		result = "File too large";
199 		break;
200 	case ENOSPC:
201 		result = "No space left on device";
202 		break;
203 	case EROFS:
204 		result = "Read-only file system";
205 		break;
206 	case EDQUOT:
207 		result = "Disc quota exceeded";
208 		break;
209 	case ENOTEMPTY:
210 		result = "Directory not empty";
211 		break;
212 	case ESTALE:
213 		result = "Stale NFS file handle";
214 		break;
215 	case ENOMEM:
216 		result = "Not enough memory";
217 		break;
218 	default:
219 		result = NULL;
220 		break;
221 	}
222 
223 	return (result);
224 }
225