[PATCH] lib: vsprintf: add printf format conversion %M for errno strings

From: Enrico Weigelt, metux IT consult
Date: Sun Jun 25 2017 - 13:12:44 EST


Adding a new format conversion for *printf() and friends.

If CONFIG_ERRNO_PRINTF_VERBOSE is enabled, prints human-readable
strerror()-like texts, otherwise just the number.
---
lib/Kconfig | 19 +++++++
lib/vsprintf.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 189 insertions(+), 2 deletions(-)

diff --git a/lib/Kconfig b/lib/Kconfig
index 0c8b78a9ae2e..b28ab2162435 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -7,6 +7,25 @@ config BINARY_PRINTF

menu "Library routines"

+config ERRNO_PRINTF
+ bool "printf conversion %M for errno codes"
+ default n
+ help
+ This option adds an %M modifier for *printf() for errno values.
+ (and callers like printk() etc)
+
+ In conjunction with ERRNO_PRINTF_VERBOSE, it prints human readable
+ strerror()-like textsm, otherwise just numeric values
+
+config ERRNO_PRINTF_VERBOSE
+ bool "Verbose errno strings"
+ default y
+ depends on ERRNO_PRINTF
+ help
+ Enable verbose error strings for ERRNO_PRINTF.
+
+ Small embedded systems might disable it for reducing kernel size.
+
config RAID6_PQ
tristate

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 2d41de3f98a1..9778e17fc178 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -382,7 +382,8 @@ enum format_type {
FORMAT_TYPE_UINT,
FORMAT_TYPE_INT,
FORMAT_TYPE_SIZE_T,
- FORMAT_TYPE_PTRDIFF
+ FORMAT_TYPE_PTRDIFF,
+ FORMAT_TYPE_ERRNO,
};

struct printf_spec {
@@ -600,6 +601,164 @@ char *string(char *buf, char *end, const char *s, struct printf_spec spec)
return widen_string(buf, len, end, spec);
}

+#if IS_ENABLED(CONFIG_ERRNO_PRINTF_VERBOSE)
+static noinline_for_stack
+const char *errno_to_str(int no)
+{
+ switch (no) {
+ case 0: return "Success";
+ case EPERM: return "Operation not permitted";
+ case ENOENT: return "No such file or directory";
+ case ESRCH: return "No such process";
+ case EINTR: return "Interrupted system call";
+ case EIO: return "I/O error";
+ case ENXIO: return "No such device or address";
+ case E2BIG: return "Argument list too long";
+ case ENOEXEC: return "Exec format error";
+ case EBADF: return "Bad file number";
+ case ECHILD: return "No child processes";
+ case EAGAIN: return "Try again";
+ case ENOMEM: return "Out of memory";
+ case EACCES: return "Permission denied";
+ case EFAULT: return "Bad address";
+ case ENOTBLK: return "Block device required";
+ case EBUSY: return "Device or resource busy";
+ case EEXIST: return "File exists";
+ case EXDEV: return "Cross-device link";
+ case ENODEV: return "No such device";
+ case ENOTDIR: return "Not a directory";
+ case EISDIR: return "Is a directory";
+ case EINVAL: return "Invalid argument";
+ case ENFILE: return "File table overflow";
+ case EMFILE: return "Too many open files";
+ case ENOTTY: return "Not a typewriter";
+ case ETXTBSY: return "Text file busy";
+ case EFBIG: return "File too large";
+ case ENOSPC: return "No space left on device";
+ case ESPIPE: return "Illegal seek";
+ case EROFS: return "Read-only file system";
+ case EMLINK: return "Too many links";
+ case EPIPE: return "Broken pipe";
+ case EDOM: return "Math argument out of domain of func";
+ case ERANGE: return "Math result not representable";
+ case EDEADLK: return "Resource deadlock would occur";
+ case ENAMETOOLONG: return "File name too long";
+ case ENOLCK: return "No record locks available";
+ case ENOSYS: return "Invalid system call number";
+ case ENOTEMPTY: return "Directory not empty";
+ case ELOOP: return "Too many symbolic links encountered";
+ case ENOMSG: return "No message of desired type";
+ case EIDRM: return "Identifier removed";
+ case ECHRNG: return "Channel number out of range";
+ case EL2NSYNC: return "Level 2 not synchronized";
+ case EL3HLT: return "Level 3 halted";
+ case EL3RST: return "Level 3 reset";
+ case ELNRNG: return "Link number out of range";
+ case EUNATCH: return "Protocol driver not attached";
+ case ENOCSI: return "No CSI structure available";
+ case EL2HLT: return "Level 2 halted";
+ case EBADE: return "Invalid exchange";
+ case EBADR: return "Invalid request descriptor";
+ case EXFULL: return "Exchange full";
+ case ENOANO: return "No anode";
+ case EBADRQC: return "Invalid request code";
+ case EBADSLT: return "Invalid slot";
+ case EBFONT: return "Bad font file format";
+ case ENOSTR: return "Device not a stream";
+ case ENODATA: return "No data available";
+ case ETIME: return "Timer expired";
+ case ENOSR: return "Out of streams resources";
+ case ENONET: return "Machine is not on the network";
+ case ENOPKG: return "Package not installed";
+ case EREMOTE: return "Object is remote";
+ case ENOLINK: return "Link has been severed";
+ case EADV: return "Advertise error";
+ case ESRMNT: return "Srmount error";
+ case ECOMM: return "Communication error on send";
+ case EPROTO: return "Protocol error";
+ case EMULTIHOP: return "Multihop attempted";
+ case EDOTDOT: return "RFS specific error";
+ case EBADMSG: return "Not a data message";
+ case EOVERFLOW: return "Value too large for defined data type";
+ case ENOTUNIQ: return "Name not unique on network";
+ case EBADFD: return "File descriptor in bad state";
+ case EREMCHG: return "Remote address changed";
+ case ELIBACC: return "Can not access a needed shared library";
+ case ELIBBAD: return "Accessing a corrupted shared library";
+ case ELIBSCN: return ".lib section in a.out corrupted";
+ case ELIBMAX: return "Attempting to link in too many shared libraries";
+ case ELIBEXEC: return "Cannot exec a shared library directly";
+ case EILSEQ: return "Illegal byte sequence";
+ case ERESTART: return "Interrupted system call should be restarted";
+ case ESTRPIPE: return "Streams pipe error";
+ case EUSERS: return "Too many users";
+ case EDESTADDRREQ: return "Destination address required";
+ case EMSGSIZE: return "Message too long";
+ case EPROTOTYPE: return "Protocol wrong type for socket";
+ case ENOPROTOOPT: return "Protocol not available";
+ case EPROTONOSUPPORT: return "Protocol not supported";
+ case ESOCKTNOSUPPORT: return "Socket type not supported";
+ case EOPNOTSUPP: return "Operation not supported on transport endpoint";
+ case EPFNOSUPPORT: return "Protocol family not supported";
+ case EAFNOSUPPORT: return "Address family not supported by protocol";
+ case EADDRINUSE: return "Address already in use";
+ case EADDRNOTAVAIL: return "Cannot assign requested address";
+ case ENETDOWN: return "Network is down";
+ case ENETUNREACH: return "Network is unreachable";
+ case ENETRESET: return "Network dropped connection because of reset";
+ case ECONNABORTED: return "Software caused connection abort";
+ case ECONNRESET: return "Connection reset by peer";
+ case ENOBUFS: return "No buffer space available";
+ case EISCONN: return "Transport endpoint is already connected";
+ case ENOTCONN: return "Transport endpoint is not connected";
+ case ESHUTDOWN: return "Cannot send after transport endpoint shutdown";
+ case ETOOMANYREFS: return "Too many references: cannot splice";
+ case ETIMEDOUT: return "Connection timed out";
+ case ECONNREFUSED: return "Connection refused";
+ case EHOSTDOWN: return "Host is down";
+ case EHOSTUNREACH: return "No route to host";
+ case EALREADY: return "Operation already in progress";
+ case EINPROGRESS: return "Operation now in progress";
+ case ESTALE: return "Stale file handle";
+ case EUCLEAN: return "Structure needs cleaning";
+ case ENOTNAM: return "Not a XENIX named type file";
+ case ENAVAIL: return "No XENIX semaphores available";
+ case EISNAM: return "Is a named type file";
+ case EREMOTEIO: return "Remote I/O error";
+ case EDQUOT: return "Quota exceeded";
+ case ENOMEDIUM: return "No medium found";
+ case EMEDIUMTYPE: return "Wrong medium type";
+ case ECANCELED: return "Operation Canceled";
+ case ENOKEY: return "Required key not available";
+ case EKEYEXPIRED: return "Key has expired";
+ case EKEYREVOKED: return "Key has been revoked";
+ case EKEYREJECTED: return "Key was rejected by service";
+ case EOWNERDEAD: return "Owner died";
+ case ENOTRECOVERABLE: return "State not recoverable";
+ case ERFKILL: return "Operation not possible due to RF-kill";
+ case EHWPOISON: return "Memory page has hardware error";
+ }
+ return NULL;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_ERRNO_PRINTF)
+static noinline_for_stack
+char *errno_string(char *buf, char *end, int errcode, struct printf_spec spec)
+{
+ char buffer[32];
+
+#if IS_ENABLED(CONFIG_ERRNO_PRINTF_VERBOSE)
+ const char *estr = errno_to_str(errcode);
+ if (estr != NULL)
+ return string(buf, end, estr, spec);
+#endif
+
+ snprintf(buffer, sizeof(buffer), "error %d", errcode);
+ return string(buf, end, buffer, spec);
+}
+#endif
+
static noinline_for_stack
char *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_spec spec,
const char *fmt)
@@ -1744,6 +1903,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
* 'z' changed to 'Z' --davidm 1/25/99
* 'Z' changed to 'z' --adobriyan 2017-01-25
* 't' added for ptrdiff_t
+ * 'M' added for strerror()-like output --mtx 2017-06-25
*
* @fmt: the format string
* @type of the token returned
@@ -1866,6 +2026,10 @@ int format_decode(const char *fmt, struct printf_spec *spec)
spec->type = FORMAT_TYPE_STR;
return ++fmt - start;

+ case 'M':
+ spec->type = FORMAT_TYPE_ERRNO;
+ return ++fmt - start;
+
case 'p':
spec->type = FORMAT_TYPE_PTR;
return ++fmt - start;
@@ -2047,7 +2211,11 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
case FORMAT_TYPE_STR:
str = string(str, end, va_arg(args, char *), spec);
break;
-
+#if IS_ENABLED(CONFIG_ERRNO_PRINTF)
+ case FORMAT_TYPE_ERRNO:
+ str = errno_string(str, end, va_arg(args, int), spec);
+ break;
+#endif
case FORMAT_TYPE_PTR:
str = pointer(fmt, str, end, va_arg(args, void *),
spec);
--
2.11.0.rc0.7.gbe5a750