root/fs/nfs_common/common.c
// SPDX-License-Identifier: GPL-2.0-only

#include <linux/module.h>
#include <linux/nfs_common.h>
#include <linux/nfs4.h>

/*
 * We need to translate between nfs status return values and
 * the local errno values which may not be the same.
 */
static const struct {
        int stat;
        int errno;
} nfs_errtbl[] = {
        { NFS_OK,               0               },
        { NFSERR_PERM,          -EPERM          },
        { NFSERR_NOENT,         -ENOENT         },
        { NFSERR_IO,            -EIO            },
        { NFSERR_NXIO,          -ENXIO          },
        { NFSERR_ACCES,         -EACCES         },
        { NFSERR_EXIST,         -EEXIST         },
        { NFSERR_XDEV,          -EXDEV          },
        { NFSERR_NODEV,         -ENODEV         },
        { NFSERR_NOTDIR,        -ENOTDIR        },
        { NFSERR_ISDIR,         -EISDIR         },
        { NFSERR_INVAL,         -EINVAL         },
        { NFSERR_FBIG,          -EFBIG          },
        { NFSERR_NOSPC,         -ENOSPC         },
        { NFSERR_ROFS,          -EROFS          },
        { NFSERR_MLINK,         -EMLINK         },
        { NFSERR_NAMETOOLONG,   -ENAMETOOLONG   },
        { NFSERR_NOTEMPTY,      -ENOTEMPTY      },
        { NFSERR_DQUOT,         -EDQUOT         },
        { NFSERR_STALE,         -ESTALE         },
        { NFSERR_REMOTE,        -EREMOTE        },
#ifdef EWFLUSH
        { NFSERR_WFLUSH,        -EWFLUSH        },
#endif
        { NFSERR_BADHANDLE,     -EBADHANDLE     },
        { NFSERR_NOT_SYNC,      -ENOTSYNC       },
        { NFSERR_BAD_COOKIE,    -EBADCOOKIE     },
        { NFSERR_NOTSUPP,       -ENOTSUPP       },
        { NFSERR_TOOSMALL,      -ETOOSMALL      },
        { NFSERR_SERVERFAULT,   -EREMOTEIO      },
        { NFSERR_BADTYPE,       -EBADTYPE       },
        { NFSERR_JUKEBOX,       -EJUKEBOX       },
};

/**
 * nfs_stat_to_errno - convert an NFS status code to a local errno
 * @status: NFS status code to convert
 *
 * Returns a local errno value, or -EIO if the NFS status code is
 * not recognized.  This function is used jointly by NFSv2 and NFSv3.
 */
int nfs_stat_to_errno(enum nfs_stat status)
{
        int i;

        for (i = 0; i < ARRAY_SIZE(nfs_errtbl); i++) {
                if (nfs_errtbl[i].stat == (int)status)
                        return nfs_errtbl[i].errno;
        }
        return -EIO;
}
EXPORT_SYMBOL_GPL(nfs_stat_to_errno);

/*
 * We need to translate between nfs v4 status return values and
 * the local errno values which may not be the same.
 *
 * nfs4_errtbl_common[] is used before more specialized mappings
 * available in nfs4_errtbl[] or nfs4_errtbl_localio[].
 */
static const struct {
        int stat;
        int errno;
} nfs4_errtbl_common[] = {
        { NFS4_OK,              0               },
        { NFS4ERR_PERM,         -EPERM          },
        { NFS4ERR_NOENT,        -ENOENT         },
        { NFS4ERR_IO,           -EIO            },
        { NFS4ERR_NXIO,         -ENXIO          },
        { NFS4ERR_ACCESS,       -EACCES         },
        { NFS4ERR_EXIST,        -EEXIST         },
        { NFS4ERR_XDEV,         -EXDEV          },
        { NFS4ERR_NOTDIR,       -ENOTDIR        },
        { NFS4ERR_ISDIR,        -EISDIR         },
        { NFS4ERR_INVAL,        -EINVAL         },
        { NFS4ERR_FBIG,         -EFBIG          },
        { NFS4ERR_NOSPC,        -ENOSPC         },
        { NFS4ERR_ROFS,         -EROFS          },
        { NFS4ERR_MLINK,        -EMLINK         },
        { NFS4ERR_NAMETOOLONG,  -ENAMETOOLONG   },
        { NFS4ERR_NOTEMPTY,     -ENOTEMPTY      },
        { NFS4ERR_DQUOT,        -EDQUOT         },
        { NFS4ERR_STALE,        -ESTALE         },
        { NFS4ERR_BADHANDLE,    -EBADHANDLE     },
        { NFS4ERR_BAD_COOKIE,   -EBADCOOKIE     },
        { NFS4ERR_NOTSUPP,      -ENOTSUPP       },
        { NFS4ERR_TOOSMALL,     -ETOOSMALL      },
        { NFS4ERR_BADTYPE,      -EBADTYPE       },
        { NFS4ERR_SYMLINK,      -ELOOP          },
        { NFS4ERR_DEADLOCK,     -EDEADLK        },
};

static const struct {
        int stat;
        int errno;
} nfs4_errtbl[] = {
        { NFS4ERR_SERVERFAULT,  -EREMOTEIO      },
        { NFS4ERR_LOCKED,       -EAGAIN         },
        { NFS4ERR_OP_ILLEGAL,   -EOPNOTSUPP     },
        { NFS4ERR_NOXATTR,      -ENODATA        },
        { NFS4ERR_XATTR2BIG,    -E2BIG          },
};

/*
 * Convert an NFS error code to a local one.
 * This one is used by NFSv4.
 */
int nfs4_stat_to_errno(int stat)
{
        int i;

        /* First check nfs4_errtbl_common */
        for (i = 0; i < ARRAY_SIZE(nfs4_errtbl_common); i++) {
                if (nfs4_errtbl_common[i].stat == stat)
                        return nfs4_errtbl_common[i].errno;
        }
        /* Then check nfs4_errtbl */
        for (i = 0; i < ARRAY_SIZE(nfs4_errtbl); i++) {
                if (nfs4_errtbl[i].stat == stat)
                        return nfs4_errtbl[i].errno;
        }
        if (stat <= 10000 || stat > 10100) {
                /* The server is looney tunes. */
                return -EREMOTEIO;
        }
        /* If we cannot translate the error, the recovery routines should
         * handle it.
         * Note: remaining NFSv4 error codes have values > 10000, so should
         * not conflict with native Linux error codes.
         */
        return -stat;
}
EXPORT_SYMBOL_GPL(nfs4_stat_to_errno);

/*
 * This table is useful for conversion from local errno to NFS error.
 * It provides more logically correct mappings for use with LOCALIO
 * (which is focused on converting from errno to NFS status).
 */
static const struct {
        int stat;
        int errno;
} nfs4_errtbl_localio[] = {
        /* Map errors differently than nfs4_errtbl */
        { NFS4ERR_IO,           -EREMOTEIO      },
        { NFS4ERR_DELAY,        -EAGAIN         },
        { NFS4ERR_FBIG,         -E2BIG          },
        /* Map errors not handled by nfs4_errtbl */
        { NFS4ERR_STALE,        -EBADF          },
        { NFS4ERR_STALE,        -EOPENSTALE     },
        { NFS4ERR_DELAY,        -ETIMEDOUT      },
        { NFS4ERR_DELAY,        -ERESTARTSYS    },
        { NFS4ERR_DELAY,        -ENOMEM         },
        { NFS4ERR_IO,           -ETXTBSY        },
        { NFS4ERR_IO,           -EBUSY          },
        { NFS4ERR_SERVERFAULT,  -ESERVERFAULT   },
        { NFS4ERR_SERVERFAULT,  -ENFILE         },
        { NFS4ERR_IO,           -EUCLEAN        },
        { NFS4ERR_PERM,         -ENOKEY         },
};

/*
 * Convert an errno to an NFS error code for LOCALIO.
 */
__u32 nfs_localio_errno_to_nfs4_stat(int errno)
{
        int i;

        /* First check nfs4_errtbl_common */
        for (i = 0; i < ARRAY_SIZE(nfs4_errtbl_common); i++) {
                if (nfs4_errtbl_common[i].errno == errno)
                        return nfs4_errtbl_common[i].stat;
        }
        /* Then check nfs4_errtbl_localio */
        for (i = 0; i < ARRAY_SIZE(nfs4_errtbl_localio); i++) {
                if (nfs4_errtbl_localio[i].errno == errno)
                        return nfs4_errtbl_localio[i].stat;
        }
        /* If we cannot translate the error, the recovery routines should
         * handle it.
         * Note: remaining NFSv4 error codes have values > 10000, so should
         * not conflict with native Linux error codes.
         */
        return NFS4ERR_SERVERFAULT;
}
EXPORT_SYMBOL_GPL(nfs_localio_errno_to_nfs4_stat);