Commit 114fc1af authored by Andy Shevchenko's avatar Andy Shevchenko Committed by Linus Torvalds

hexdump: make it return number of bytes placed in buffer

This patch makes hexdump return the number of bytes placed in the buffer
excluding trailing NUL.  In the case of overflow it returns the desired
amount of bytes to produce the entire dump.  Thus, it mimics snprintf().

This will be useful for users that would like to repeat with a bigger
buffer.

[akpm@linux-foundation.org: fix printk warning]
Signed-off-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 5d909c8d
...@@ -417,9 +417,9 @@ enum { ...@@ -417,9 +417,9 @@ enum {
DUMP_PREFIX_ADDRESS, DUMP_PREFIX_ADDRESS,
DUMP_PREFIX_OFFSET DUMP_PREFIX_OFFSET
}; };
extern void hex_dump_to_buffer(const void *buf, size_t len, extern int hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
int rowsize, int groupsize, int groupsize, char *linebuf, size_t linebuflen,
char *linebuf, size_t linebuflen, bool ascii); bool ascii);
#ifdef CONFIG_PRINTK #ifdef CONFIG_PRINTK
extern void print_hex_dump(const char *level, const char *prefix_str, extern void print_hex_dump(const char *level, const char *prefix_str,
int prefix_type, int rowsize, int groupsize, int prefix_type, int rowsize, int groupsize,
......
...@@ -97,22 +97,26 @@ EXPORT_SYMBOL(bin2hex); ...@@ -97,22 +97,26 @@ EXPORT_SYMBOL(bin2hex);
* *
* example output buffer: * example output buffer:
* 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
*
* Return:
* The amount of bytes placed in the buffer without terminating NUL. If the
* output was truncated, then the return value is the number of bytes
* (excluding the terminating NUL) which would have been written to the final
* string if enough space had been available.
*/ */
void hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize,
int groupsize, char *linebuf, size_t linebuflen, char *linebuf, size_t linebuflen, bool ascii)
bool ascii)
{ {
const u8 *ptr = buf; const u8 *ptr = buf;
int ngroups; int ngroups;
u8 ch; u8 ch;
int j, lx = 0; int j, lx = 0;
int ascii_column; int ascii_column;
int ret;
if (rowsize != 16 && rowsize != 32) if (rowsize != 16 && rowsize != 32)
rowsize = 16; rowsize = 16;
if (!len)
goto nil;
if (len > rowsize) /* limit to one line at a time */ if (len > rowsize) /* limit to one line at a time */
len = rowsize; len = rowsize;
if (!is_power_of_2(groupsize) || groupsize > 8) if (!is_power_of_2(groupsize) || groupsize > 8)
...@@ -122,27 +126,50 @@ void hex_dump_to_buffer(const void *buf, size_t len, int rowsize, ...@@ -122,27 +126,50 @@ void hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
ngroups = len / groupsize; ngroups = len / groupsize;
ascii_column = rowsize * 2 + rowsize / groupsize + 1; ascii_column = rowsize * 2 + rowsize / groupsize + 1;
if (!linebuflen)
goto overflow1;
if (!len)
goto nil;
if (groupsize == 8) { if (groupsize == 8) {
const u64 *ptr8 = buf; const u64 *ptr8 = buf;
for (j = 0; j < ngroups; j++) for (j = 0; j < ngroups; j++) {
lx += scnprintf(linebuf + lx, linebuflen - lx, ret = snprintf(linebuf + lx, linebuflen - lx,
"%s%16.16llx", j ? " " : "", "%s%16.16llx", j ? " " : "",
(unsigned long long)*(ptr8 + j)); (unsigned long long)*(ptr8 + j));
if (ret >= linebuflen - lx)
goto overflow1;
lx += ret;
}
} else if (groupsize == 4) { } else if (groupsize == 4) {
const u32 *ptr4 = buf; const u32 *ptr4 = buf;
for (j = 0; j < ngroups; j++) for (j = 0; j < ngroups; j++) {
lx += scnprintf(linebuf + lx, linebuflen - lx, ret = snprintf(linebuf + lx, linebuflen - lx,
"%s%8.8x", j ? " " : "", *(ptr4 + j)); "%s%8.8x", j ? " " : "",
*(ptr4 + j));
if (ret >= linebuflen - lx)
goto overflow1;
lx += ret;
}
} else if (groupsize == 2) { } else if (groupsize == 2) {
const u16 *ptr2 = buf; const u16 *ptr2 = buf;
for (j = 0; j < ngroups; j++) for (j = 0; j < ngroups; j++) {
lx += scnprintf(linebuf + lx, linebuflen - lx, ret = snprintf(linebuf + lx, linebuflen - lx,
"%s%4.4x", j ? " " : "", *(ptr2 + j)); "%s%4.4x", j ? " " : "",
*(ptr2 + j));
if (ret >= linebuflen - lx)
goto overflow1;
lx += ret;
}
} else { } else {
for (j = 0; (j < len) && (lx + 3) <= linebuflen; j++) { for (j = 0; j < len; j++) {
if (linebuflen < lx + 3)
goto overflow2;
ch = ptr[j]; ch = ptr[j];
linebuf[lx++] = hex_asc_hi(ch); linebuf[lx++] = hex_asc_hi(ch);
linebuf[lx++] = hex_asc_lo(ch); linebuf[lx++] = hex_asc_lo(ch);
...@@ -154,14 +181,24 @@ void hex_dump_to_buffer(const void *buf, size_t len, int rowsize, ...@@ -154,14 +181,24 @@ void hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
if (!ascii) if (!ascii)
goto nil; goto nil;
while (lx < (linebuflen - 1) && lx < ascii_column) while (lx < ascii_column) {
if (linebuflen < lx + 2)
goto overflow2;
linebuf[lx++] = ' '; linebuf[lx++] = ' ';
for (j = 0; (j < len) && (lx + 2) < linebuflen; j++) { }
for (j = 0; j < len; j++) {
if (linebuflen < lx + 2)
goto overflow2;
ch = ptr[j]; ch = ptr[j];
linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.'; linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
} }
nil: nil:
linebuf[lx] = '\0';
return lx;
overflow2:
linebuf[lx++] = '\0'; linebuf[lx++] = '\0';
overflow1:
return ascii ? ascii_column + len : (groupsize * 2 + 1) * ngroups - 1;
} }
EXPORT_SYMBOL(hex_dump_to_buffer); EXPORT_SYMBOL(hex_dump_to_buffer);
......
...@@ -114,6 +114,45 @@ static void __init test_hexdump_set(int rowsize, bool ascii) ...@@ -114,6 +114,45 @@ static void __init test_hexdump_set(int rowsize, bool ascii)
test_hexdump(len, rowsize, 1, ascii); test_hexdump(len, rowsize, 1, ascii);
} }
static void __init test_hexdump_overflow(bool ascii)
{
char buf[56];
const char *t = test_data_1_le[0];
size_t l = get_random_int() % sizeof(buf);
bool a;
int e, r;
memset(buf, ' ', sizeof(buf));
r = hex_dump_to_buffer(data_b, 1, 16, 1, buf, l, ascii);
if (ascii)
e = 50;
else
e = 2;
buf[e + 2] = '\0';
if (!l) {
a = r == e && buf[0] == ' ';
} else if (l < 3) {
a = r == e && buf[0] == '\0';
} else if (l < 4) {
a = r == e && !strcmp(buf, t);
} else if (ascii) {
if (l < 51)
a = r == e && buf[l - 1] == '\0' && buf[l - 2] == ' ';
else
a = r == e && buf[50] == '\0' && buf[49] == '.';
} else {
a = r == e && buf[e] == '\0';
}
if (!a) {
pr_err("Len: %zu rc: %u strlen: %zu\n", l, r, strlen(buf));
pr_err("Result: '%s'\n", buf);
}
}
static int __init test_hexdump_init(void) static int __init test_hexdump_init(void)
{ {
unsigned int i; unsigned int i;
...@@ -129,6 +168,12 @@ static int __init test_hexdump_init(void) ...@@ -129,6 +168,12 @@ static int __init test_hexdump_init(void)
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
test_hexdump_set(rowsize, true); test_hexdump_set(rowsize, true);
for (i = 0; i < 16; i++)
test_hexdump_overflow(false);
for (i = 0; i < 16; i++)
test_hexdump_overflow(true);
return -EINVAL; return -EINVAL;
} }
module_init(test_hexdump_init); module_init(test_hexdump_init);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment