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

Reply via email to