Skip to content

Commit

Permalink
lib/vsprintf: add %*pE[achnops] format specifier
Browse files Browse the repository at this point in the history
This allows user to print a given buffer as an escaped string.  The
rules are applied according to an optional mix of flags provided by
additional format letters.

For example, if the given buffer is:

    1b 62 20 5c 43 07 22 90 0d 5d

The result strings would be:
    %*pE            "\eb \C\a"\220\r]"
    %*pEhp          "\x1bb \C\x07"\x90\x0d]"
    %*pEa           "\e\142\040\\\103\a\042\220\r\135"

Please, read Documentation/printk-formats.txt and lib/string_helpers.c
kernel documentation to get further information.

[[email protected]: tidy up comment layout, per Joe]
Signed-off-by: Andy Shevchenko <[email protected]>
Suggested-by: Joe Perches <[email protected]>
Cc: "John W . Linville" <[email protected]>
Cc: Johannes Berg <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
andy-shev authored and torvalds committed Oct 14, 2014
1 parent c825038 commit 71dca95
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 0 deletions.
32 changes: 32 additions & 0 deletions Documentation/printk-formats.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,38 @@ DMA addresses types dma_addr_t:
For printing a dma_addr_t type which can vary based on build options,
regardless of the width of the CPU data path. Passed by reference.

Raw buffer as an escaped string:

%*pE[achnops]

For printing raw buffer as an escaped string. For the following buffer

1b 62 20 5c 43 07 22 90 0d 5d

few examples show how the conversion would be done (the result string
without surrounding quotes):

%*pE "\eb \C\a"\220\r]"
%*pEhp "\x1bb \C\x07"\x90\x0d]"
%*pEa "\e\142\040\\\103\a\042\220\r\135"

The conversion rules are applied according to an optional combination
of flags (see string_escape_mem() kernel documentation for the
details):
a - ESCAPE_ANY
c - ESCAPE_SPECIAL
h - ESCAPE_HEX
n - ESCAPE_NULL
o - ESCAPE_OCTAL
p - ESCAPE_NP
s - ESCAPE_SPACE
By default ESCAPE_ANY_NP is used.

ESCAPE_ANY_NP is the sane choice for many cases, in particularly for
printing SSIDs.

If field width is omitted the 1 byte only will be escaped.

Raw buffer as a hex string:
%*ph 00 01 02 ... 3f
%*phC 00:01:02: ... :3f
Expand Down
71 changes: 71 additions & 0 deletions lib/vsprintf.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <asm/page.h> /* for PAGE_SIZE */
#include <asm/sections.h> /* for dereference_function_descriptor() */

#include <linux/string_helpers.h>
#include "kstrtox.h"

/**
Expand Down Expand Up @@ -1100,6 +1101,62 @@ char *ip4_addr_string_sa(char *buf, char *end, const struct sockaddr_in *sa,
return string(buf, end, ip4_addr, spec);
}

static noinline_for_stack
char *escaped_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
const char *fmt)
{
bool found = true;
int count = 1;
unsigned int flags = 0;
int len;

if (spec.field_width == 0)
return buf; /* nothing to print */

if (ZERO_OR_NULL_PTR(addr))
return string(buf, end, NULL, spec); /* NULL pointer */


do {
switch (fmt[count++]) {
case 'a':
flags |= ESCAPE_ANY;
break;
case 'c':
flags |= ESCAPE_SPECIAL;
break;
case 'h':
flags |= ESCAPE_HEX;
break;
case 'n':
flags |= ESCAPE_NULL;
break;
case 'o':
flags |= ESCAPE_OCTAL;
break;
case 'p':
flags |= ESCAPE_NP;
break;
case 's':
flags |= ESCAPE_SPACE;
break;
default:
found = false;
break;
}
} while (found);

if (!flags)
flags = ESCAPE_ANY_NP;

len = spec.field_width < 0 ? 1 : spec.field_width;

/* Ignore the error. We print as many characters as we can */
string_escape_mem(addr, len, &buf, end - buf, flags, NULL);

return buf;
}

static noinline_for_stack
char *uuid_string(char *buf, char *end, const u8 *addr,
struct printf_spec spec, const char *fmt)
Expand Down Expand Up @@ -1221,6 +1278,17 @@ int kptr_restrict __read_mostly;
* - '[Ii][4S][hnbl]' IPv4 addresses in host, network, big or little endian order
* - 'I[6S]c' for IPv6 addresses printed as specified by
* http://tools.ietf.org/html/rfc5952
* - 'E[achnops]' For an escaped buffer, where rules are defined by combination
* of the following flags (see string_escape_mem() for the
* details):
* a - ESCAPE_ANY
* c - ESCAPE_SPECIAL
* h - ESCAPE_HEX
* n - ESCAPE_NULL
* o - ESCAPE_OCTAL
* p - ESCAPE_NP
* s - ESCAPE_SPACE
* By default ESCAPE_ANY_NP is used.
* - 'U' For a 16 byte UUID/GUID, it prints the UUID/GUID in the form
* "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
* Options for %pU are:
Expand Down Expand Up @@ -1321,6 +1389,8 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
}}
}
break;
case 'E':
return escaped_string(buf, end, ptr, spec, fmt);
case 'U':
return uuid_string(buf, end, ptr, spec, fmt);
case 'V':
Expand Down Expand Up @@ -1633,6 +1703,7 @@ int format_decode(const char *fmt, struct printf_spec *spec)
* %piS depending on sa_family of 'struct sockaddr *' print IPv4/IPv6 address
* %pU[bBlL] print a UUID/GUID in big or little endian using lower or upper
* case.
* %*pE[achnops] print an escaped buffer
* %*ph[CDN] a variable-length hex string with a separator (supports up to 64
* bytes of the input)
* %n is ignored
Expand Down

0 comments on commit 71dca95

Please sign in to comment.