1
2
3
4
5
6
7#include <linux/bug.h>
8#include <linux/kernel.h>
9#include <linux/math64.h>
10#include <linux/export.h>
11#include <linux/ctype.h>
12#include <linux/errno.h>
13#include <linux/fs.h>
14#include <linux/limits.h>
15#include <linux/mm.h>
16#include <linux/slab.h>
17#include <linux/string.h>
18#include <linux/string_helpers.h>
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33void string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
34 char *buf, int len)
35{
36 static const char *const units_10[] = {
37 "B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
38 };
39 static const char *const units_2[] = {
40 "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"
41 };
42 static const char *const *const units_str[] = {
43 [STRING_UNITS_10] = units_10,
44 [STRING_UNITS_2] = units_2,
45 };
46 static const unsigned int divisor[] = {
47 [STRING_UNITS_10] = 1000,
48 [STRING_UNITS_2] = 1024,
49 };
50 static const unsigned int rounding[] = { 500, 50, 5 };
51 int i = 0, j;
52 u32 remainder = 0, sf_cap;
53 char tmp[8];
54 const char *unit;
55
56 tmp[0] = '\0';
57
58 if (blk_size == 0)
59 size = 0;
60 if (size == 0)
61 goto out;
62
63
64
65
66
67
68
69
70
71
72
73
74 while (blk_size >> 32) {
75 do_div(blk_size, divisor[units]);
76 i++;
77 }
78
79 while (size >> 32) {
80 do_div(size, divisor[units]);
81 i++;
82 }
83
84
85
86 size *= blk_size;
87
88
89 while (size >= divisor[units]) {
90 remainder = do_div(size, divisor[units]);
91 i++;
92 }
93
94
95
96 sf_cap = size;
97 for (j = 0; sf_cap*10 < 1000; j++)
98 sf_cap *= 10;
99
100 if (units == STRING_UNITS_2) {
101
102
103
104 remainder *= 1000;
105 remainder >>= 10;
106 }
107
108
109
110 remainder += rounding[j];
111 if (remainder >= 1000) {
112 remainder -= 1000;
113 size += 1;
114 }
115
116 if (j) {
117 snprintf(tmp, sizeof(tmp), ".%03u", remainder);
118 tmp[j+1] = '\0';
119 }
120
121 out:
122 if (i >= ARRAY_SIZE(units_2))
123 unit = "UNK";
124 else
125 unit = units_str[units][i];
126
127 snprintf(buf, len, "%u%s %s", (u32)size,
128 tmp, unit);
129}
130EXPORT_SYMBOL(string_get_size);
131
132static bool unescape_space(char **src, char **dst)
133{
134 char *p = *dst, *q = *src;
135
136 switch (*q) {
137 case 'n':
138 *p = '\n';
139 break;
140 case 'r':
141 *p = '\r';
142 break;
143 case 't':
144 *p = '\t';
145 break;
146 case 'v':
147 *p = '\v';
148 break;
149 case 'f':
150 *p = '\f';
151 break;
152 default:
153 return false;
154 }
155 *dst += 1;
156 *src += 1;
157 return true;
158}
159
160static bool unescape_octal(char **src, char **dst)
161{
162 char *p = *dst, *q = *src;
163 u8 num;
164
165 if (isodigit(*q) == 0)
166 return false;
167
168 num = (*q++) & 7;
169 while (num < 32 && isodigit(*q) && (q - *src < 3)) {
170 num <<= 3;
171 num += (*q++) & 7;
172 }
173 *p = num;
174 *dst += 1;
175 *src = q;
176 return true;
177}
178
179static bool unescape_hex(char **src, char **dst)
180{
181 char *p = *dst, *q = *src;
182 int digit;
183 u8 num;
184
185 if (*q++ != 'x')
186 return false;
187
188 num = digit = hex_to_bin(*q++);
189 if (digit < 0)
190 return false;
191
192 digit = hex_to_bin(*q);
193 if (digit >= 0) {
194 q++;
195 num = (num << 4) | digit;
196 }
197 *p = num;
198 *dst += 1;
199 *src = q;
200 return true;
201}
202
203static bool unescape_special(char **src, char **dst)
204{
205 char *p = *dst, *q = *src;
206
207 switch (*q) {
208 case '\"':
209 *p = '\"';
210 break;
211 case '\\':
212 *p = '\\';
213 break;
214 case 'a':
215 *p = '\a';
216 break;
217 case 'e':
218 *p = '\e';
219 break;
220 default:
221 return false;
222 }
223 *dst += 1;
224 *src += 1;
225 return true;
226}
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266int string_unescape(char *src, char *dst, size_t size, unsigned int flags)
267{
268 char *out = dst;
269
270 while (*src && --size) {
271 if (src[0] == '\\' && src[1] != '\0' && size > 1) {
272 src++;
273 size--;
274
275 if (flags & UNESCAPE_SPACE &&
276 unescape_space(&src, &out))
277 continue;
278
279 if (flags & UNESCAPE_OCTAL &&
280 unescape_octal(&src, &out))
281 continue;
282
283 if (flags & UNESCAPE_HEX &&
284 unescape_hex(&src, &out))
285 continue;
286
287 if (flags & UNESCAPE_SPECIAL &&
288 unescape_special(&src, &out))
289 continue;
290
291 *out++ = '\\';
292 }
293 *out++ = *src++;
294 }
295 *out = '\0';
296
297 return out - dst;
298}
299EXPORT_SYMBOL(string_unescape);
300
301static bool escape_passthrough(unsigned char c, char **dst, char *end)
302{
303 char *out = *dst;
304
305 if (out < end)
306 *out = c;
307 *dst = out + 1;
308 return true;
309}
310
311static bool escape_space(unsigned char c, char **dst, char *end)
312{
313 char *out = *dst;
314 unsigned char to;
315
316 switch (c) {
317 case '\n':
318 to = 'n';
319 break;
320 case '\r':
321 to = 'r';
322 break;
323 case '\t':
324 to = 't';
325 break;
326 case '\v':
327 to = 'v';
328 break;
329 case '\f':
330 to = 'f';
331 break;
332 default:
333 return false;
334 }
335
336 if (out < end)
337 *out = '\\';
338 ++out;
339 if (out < end)
340 *out = to;
341 ++out;
342
343 *dst = out;
344 return true;
345}
346
347static bool escape_special(unsigned char c, char **dst, char *end)
348{
349 char *out = *dst;
350 unsigned char to;
351
352 switch (c) {
353 case '\\':
354 to = '\\';
355 break;
356 case '\a':
357 to = 'a';
358 break;
359 case '\e':
360 to = 'e';
361 break;
362 default:
363 return false;
364 }
365
366 if (out < end)
367 *out = '\\';
368 ++out;
369 if (out < end)
370 *out = to;
371 ++out;
372
373 *dst = out;
374 return true;
375}
376
377static bool escape_null(unsigned char c, char **dst, char *end)
378{
379 char *out = *dst;
380
381 if (c)
382 return false;
383
384 if (out < end)
385 *out = '\\';
386 ++out;
387 if (out < end)
388 *out = '0';
389 ++out;
390
391 *dst = out;
392 return true;
393}
394
395static bool escape_octal(unsigned char c, char **dst, char *end)
396{
397 char *out = *dst;
398
399 if (out < end)
400 *out = '\\';
401 ++out;
402 if (out < end)
403 *out = ((c >> 6) & 0x07) + '0';
404 ++out;
405 if (out < end)
406 *out = ((c >> 3) & 0x07) + '0';
407 ++out;
408 if (out < end)
409 *out = ((c >> 0) & 0x07) + '0';
410 ++out;
411
412 *dst = out;
413 return true;
414}
415
416static bool escape_hex(unsigned char c, char **dst, char *end)
417{
418 char *out = *dst;
419
420 if (out < end)
421 *out = '\\';
422 ++out;
423 if (out < end)
424 *out = 'x';
425 ++out;
426 if (out < end)
427 *out = hex_asc_hi(c);
428 ++out;
429 if (out < end)
430 *out = hex_asc_lo(c);
431 ++out;
432
433 *dst = out;
434 return true;
435}
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,
494 unsigned int flags, const char *only)
495{
496 char *p = dst;
497 char *end = p + osz;
498 bool is_dict = only && *only;
499
500 while (isz--) {
501 unsigned char c = *src++;
502
503
504
505
506
507
508
509
510
511
512
513
514 if ((flags & ESCAPE_NP && isprint(c)) ||
515 (is_dict && !strchr(only, c))) {
516
517 } else {
518 if (flags & ESCAPE_SPACE && escape_space(c, &p, end))
519 continue;
520
521 if (flags & ESCAPE_SPECIAL && escape_special(c, &p, end))
522 continue;
523
524 if (flags & ESCAPE_NULL && escape_null(c, &p, end))
525 continue;
526
527
528 if (flags & ESCAPE_OCTAL && escape_octal(c, &p, end))
529 continue;
530
531 if (flags & ESCAPE_HEX && escape_hex(c, &p, end))
532 continue;
533 }
534
535 escape_passthrough(c, &p, end);
536 }
537
538 return p - dst;
539}
540EXPORT_SYMBOL(string_escape_mem);
541
542
543
544
545
546char *kstrdup_quotable(const char *src, gfp_t gfp)
547{
548 size_t slen, dlen;
549 char *dst;
550 const int flags = ESCAPE_HEX;
551 const char esc[] = "\f\n\r\t\v\a\e\\\"";
552
553 if (!src)
554 return NULL;
555 slen = strlen(src);
556
557 dlen = string_escape_mem(src, slen, NULL, 0, flags, esc);
558 dst = kmalloc(dlen + 1, gfp);
559 if (!dst)
560 return NULL;
561
562 WARN_ON(string_escape_mem(src, slen, dst, dlen, flags, esc) != dlen);
563 dst[dlen] = '\0';
564
565 return dst;
566}
567EXPORT_SYMBOL_GPL(kstrdup_quotable);
568
569
570
571
572
573
574char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp)
575{
576 char *buffer, *quoted;
577 int i, res;
578
579 buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
580 if (!buffer)
581 return NULL;
582
583 res = get_cmdline(task, buffer, PAGE_SIZE - 1);
584 buffer[res] = '\0';
585
586
587 while (--res >= 0 && buffer[res] == '\0')
588 ;
589
590
591 for (i = 0; i <= res; i++)
592 if (buffer[i] == '\0')
593 buffer[i] = ' ';
594
595
596 quoted = kstrdup_quotable(buffer, gfp);
597 kfree(buffer);
598 return quoted;
599}
600EXPORT_SYMBOL_GPL(kstrdup_quotable_cmdline);
601
602
603
604
605
606
607char *kstrdup_quotable_file(struct file *file, gfp_t gfp)
608{
609 char *temp, *pathname;
610
611 if (!file)
612 return kstrdup("<unknown>", gfp);
613
614
615 temp = kmalloc(PATH_MAX + 11, GFP_KERNEL);
616 if (!temp)
617 return kstrdup("<no_memory>", gfp);
618
619 pathname = file_path(file, temp, PATH_MAX + 11);
620 if (IS_ERR(pathname))
621 pathname = kstrdup("<too_long>", gfp);
622 else
623 pathname = kstrdup_quotable(pathname, gfp);
624
625 kfree(temp);
626 return pathname;
627}
628EXPORT_SYMBOL_GPL(kstrdup_quotable_file);
629