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