/* * error.c -- Common error handling functions * * Copyright (C) 2007 Oracle. All rights reserved. * Copyright (C) 2007 Chuck Lever * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * * To Do: * + Proper support for internationalization */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include "xcommon.h" #include "nls.h" #include "mount.h" #include "error.h" #ifdef HAVE_RPCSVC_NFS_PROT_H #include #else #include #define nfsstat nfs_stat #endif extern char *progname; static char errbuf[BUFSIZ]; static char *erreob = &errbuf[BUFSIZ]; /* Convert RPC errors into strings */ static int rpc_strerror(int spos) { int cf_stat = rpc_createerr.cf_stat; int pos = 0, cf_errno = rpc_createerr.cf_error.re_errno; char *ptr, *estr = clnt_sperrno(cf_stat); char *tmp; if (estr) { if ((ptr = index(estr, ':'))) estr = ++ptr; tmp = &errbuf[spos]; if (cf_stat == RPC_SYSTEMERROR) pos = snprintf(tmp, (erreob - tmp), _("System Error: %s"), strerror(cf_errno)); else { if (cf_errno) pos = snprintf(tmp, (erreob - tmp), _("RPC Error:%s; errno = %s"), estr, strerror(cf_errno)); else pos = snprintf(tmp, (erreob - tmp), _("RPC Error:%s"), estr); } } return pos; } /** * rpc_mount_errors - log an RPC error that occurred during a user-space mount * @server: C string containing name of server we are attempting to mount * @will_retry: one indicates mount will retry at some later point * @bg: one indicates this is a background mount * * Extracts the error code from the user-space RPC library, and reports it * on stderr (fg mount) or in the system log (bg mount). */ void rpc_mount_errors(char *server, int will_retry, int bg) { int pos = 0; char *tmp; static int onlyonce = 0; tmp = &errbuf[pos]; if (bg) pos = snprintf(tmp, (erreob - tmp), _("mount to NFS server '%s' failed: "), server); else pos = snprintf(tmp, (erreob - tmp), _("%s: mount to NFS server '%s' failed: "), progname, server); tmp = &errbuf[pos]; if (rpc_createerr.cf_stat == RPC_TIMEDOUT) { if (will_retry) pos = snprintf(tmp, (erreob - tmp), _("timed out, retrying")); else pos = snprintf(tmp, (erreob - tmp), _("timed out, giving up")); } else { pos += rpc_strerror(pos); tmp = &errbuf[pos]; if (bg) { if (will_retry) pos = snprintf(tmp, (erreob - tmp), _(", retrying")); else pos = snprintf(tmp, (erreob - tmp), _(", giving up")); } } if (bg) { if (onlyonce++ < 1) openlog("mount", LOG_CONS|LOG_PID, LOG_AUTH); syslog(LOG_ERR, "%s", errbuf); } else fprintf(stderr, "%s\n", errbuf); } /** * sys_mount_errors - log an error that occurred during a mount system call * @server: C string containing name of server we are attempting to mount * @error: errno value to report * @will_retry: one indicates mount will retry at some later point * @bg: one indicates this is a background mount * * Passed an errno value generated by a mount system call, and reports it * on stderr (fg mount) or in the system log (bg mount). */ void sys_mount_errors(char *server, int error, int will_retry, int bg) { int pos = 0; char *tmp; static int onlyonce = 0; tmp = &errbuf[pos]; if (bg) pos = snprintf(tmp, (erreob - tmp), _("mount to NFS server '%s' failed: "), server); else pos = snprintf(tmp, (erreob - tmp), _("%s: mount to NFS server '%s' failed: "), progname, server); tmp = &errbuf[pos]; if (error == ETIMEDOUT) { if (will_retry) pos = snprintf(tmp, (erreob - tmp), _("timed out, retrying")); else pos = snprintf(tmp, (erreob - tmp), _("timed out, giving up")); } else { if (bg) { if (will_retry) pos = snprintf(tmp, (erreob - tmp), _("%s, retrying"), strerror(error)); else pos = snprintf(tmp, (erreob - tmp), _("%s, giving up"), strerror(error)); } } if (bg) { if (onlyonce++ < 1) openlog("mount", LOG_CONS|LOG_PID, LOG_AUTH); syslog(LOG_ERR, "%s", errbuf); } else fprintf(stderr, "%s\n", errbuf); } /** * mount_error - report a foreground mount error * @spec: C string containing the device name being mounted * @mount_point: C string containing the pathname of the local mounted on dir * @error: errno value to report * */ void mount_error(const char *spec, const char *mount_point, int error) { switch(error) { case EACCES: nfs_error(_("%s: access denied by server while mounting %s"), progname, spec); break; case EINVAL: nfs_error(_("%s: an incorrect mount option was specified"), progname); break; case EOPNOTSUPP: nfs_error(_("%s: requested NFS version or transport" " protocol is not supported"), progname); break; case ENOTDIR: nfs_error(_("%s: mount point %s is not a directory"), progname, mount_point); break; case EBUSY: nfs_error(_("%s: %s is busy or already mounted"), progname, mount_point); break; case ENOENT: if (spec) nfs_error(_("%s: mounting %s failed, " "reason given by server: %s"), progname, spec, strerror(error)); else nfs_error(_("%s: mount point %s does not exist"), progname, mount_point); break; case ESPIPE: rpc_mount_errors((char *)spec, 0, 0); break; case EIO: nfs_error(_("%s: mount system call failed"), progname); break; case EFAULT: nfs_error(_("%s: encountered unexpected error condition."), progname); nfs_error(_("%s: please report the error to" PACKAGE_BUGREPORT), progname); break; default: nfs_error(_("%s: %s"), progname, strerror(error)); } } /* * umount_error - report a failed umount request * @err: errno value to report * @dev: C string containing the pathname of the local mounted on dir * */ void umount_error(int err, const char *dev) { switch (err) { case ENXIO: nfs_error(_("%s: %s: invalid block device"), progname, dev); break; case EINVAL: nfs_error(_("%s: %s: not mounted"), progname, dev); break; case EIO: nfs_error(_("%s: %s: can't write superblock"), progname, dev); break; case EBUSY: nfs_error(_("%s: %s: device is busy"), progname, dev); break; case ENOENT: nfs_error(_("%s: %s: not found"), progname, dev); break; case EPERM: nfs_error(_("%s: %s: must be superuser to umount"), progname, dev); break; case EACCES: nfs_error(_("%s: %s: block devices not permitted on fs"), progname, dev); break; default: nfs_error(_("%s: %s: %s"), progname, dev, strerror(err)); break; } } /* * We need to translate between nfs status return values and * the local errno values which may not be the same. * * Andreas Schwab : change errno: * "after #include the symbol errno is reserved for any use, * it cannot even be used as a struct tag or field name". */ #ifndef EDQUOT #define EDQUOT ENOSPC #endif #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) static struct { enum nfsstat stat; int errnum; } nfs_errtbl[] = { { NFS_OK, 0 }, { NFSERR_PERM, EPERM }, { NFSERR_NOENT, ENOENT }, { NFSERR_IO, EIO }, { NFSERR_NXIO, ENXIO }, { NFSERR_ACCES, EACCES }, { NFSERR_EXIST, EEXIST }, { NFSERR_NODEV, ENODEV }, { NFSERR_NOTDIR, ENOTDIR }, { NFSERR_ISDIR, EISDIR }, #ifdef NFSERR_INVAL { NFSERR_INVAL, EINVAL }, /* that Sun forgot */ #endif { NFSERR_FBIG, EFBIG }, { NFSERR_NOSPC, ENOSPC }, { NFSERR_ROFS, EROFS }, { NFSERR_NAMETOOLONG, ENAMETOOLONG }, { NFSERR_NOTEMPTY, ENOTEMPTY }, { NFSERR_DQUOT, EDQUOT }, { NFSERR_STALE, ESTALE }, #ifdef EWFLUSH { NFSERR_WFLUSH, EWFLUSH }, #endif /* Throw in some NFSv3 values for even more fun (HP returns these) */ { 71, EREMOTE }, }; char *nfs_strerror(unsigned int stat) { unsigned int i; static char buf[256]; for (i = 0; i < ARRAY_SIZE(nfs_errtbl); i++) { if (nfs_errtbl[i].stat == stat) return strerror(nfs_errtbl[i].errnum); } sprintf(buf, _("unknown nfs status return value: %u"), stat); return buf; }