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