Home | History | Annotate | Download | only in nfs
      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