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