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 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 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 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 * 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