| /* |
| * error.c -- Common error handling functions |
| * |
| * Copyright (C) 2007 Oracle. All rights reserved. |
| * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com> |
| * |
| * 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., 59 Temple Place - Suite 330, |
| * Boston, MA 021110-1307, USA. |
| * |
| * To Do: |
| * + Proper support for internationalization |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <syslog.h> |
| #include <rpc/rpc.h> |
| |
| #include "xcommon.h" |
| #include "nls.h" |
| #include "mount.h" |
| #include "error.h" |
| |
| #ifdef HAVE_RPCSVC_NFS_PROT_H |
| #include <rpcsvc/nfs_prot.h> |
| #else |
| #include <linux/nfs.h> |
| #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:\n %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 <schwab@LS5.informatik.uni-dortmund.de>: change errno: |
| * "after #include <errno.h> 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; |
| } |