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