chiark / gitweb /
builtin: blkid - add missing ID_ prefix for PART_ENTRY_* keys
[elogind.git] / src / libudev-util.c
1 /*
2  * libudev - interface to udev device information
3  *
4  * Copyright (C) 2008-2011 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stddef.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <dirent.h>
19 #include <ctype.h>
20 #include <fcntl.h>
21 #include <time.h>
22 #include <sys/stat.h>
23
24 #include "libudev.h"
25 #include "libudev-private.h"
26
27 /**
28  * SECTION:libudev-util
29  * @short_description: utils
30  */
31
32 ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size)
33 {
34         char path[UTIL_PATH_SIZE];
35         char target[UTIL_PATH_SIZE];
36         ssize_t len;
37         const char *pos;
38
39         util_strscpyl(path, sizeof(path), syspath, "/", slink, NULL);
40         len = readlink(path, target, sizeof(target));
41         if (len <= 0 || len == (ssize_t)sizeof(target))
42                 return -1;
43         target[len] = '\0';
44         pos = strrchr(target, '/');
45         if (pos == NULL)
46                 return -1;
47         pos = &pos[1];
48         dbg(udev, "resolved link to: '%s'\n", pos);
49         return util_strscpy(value, size, pos);
50 }
51
52 int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size)
53 {
54         char link_target[UTIL_PATH_SIZE];
55
56         ssize_t len;
57         int i;
58         int back;
59         char *base = NULL;
60
61         len = readlink(syspath, link_target, sizeof(link_target));
62         if (len <= 0 || len == (ssize_t)sizeof(link_target))
63                 return -1;
64         link_target[len] = '\0';
65         dbg(udev, "path link '%s' points to '%s'\n", syspath, link_target);
66
67         for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++)
68                 ;
69         dbg(udev, "base '%s', tail '%s', back %i\n", syspath, &link_target[back * 3], back);
70         for (i = 0; i <= back; i++) {
71                 base = strrchr(syspath, '/');
72                 if (base == NULL)
73                         return -EINVAL;
74                 base[0] = '\0';
75         }
76         if (base == NULL)
77                 return -EINVAL;
78         dbg(udev, "after moving back '%s'\n", syspath);
79         util_strscpyl(base, size - (base - syspath), "/", &link_target[back * 3], NULL);
80         return 0;
81 }
82
83 int util_log_priority(const char *priority)
84 {
85         char *endptr;
86         int prio;
87
88         prio = strtol(priority, &endptr, 10);
89         if (endptr[0] == '\0' || isspace(endptr[0]))
90                 return prio;
91         if (strncmp(priority, "err", 3) == 0)
92                 return LOG_ERR;
93         if (strncmp(priority, "info", 4) == 0)
94                 return LOG_INFO;
95         if (strncmp(priority, "debug", 5) == 0)
96                 return LOG_DEBUG;
97         return 0;
98 }
99
100 size_t util_path_encode(const char *src, char *dest, size_t size)
101 {
102         size_t i, j;
103
104         for (i = 0, j = 0; src[i] != '\0'; i++) {
105                 if (src[i] == '/') {
106                         if (j+4 >= size) {
107                                 j = 0;
108                                 break;
109                         }
110                         memcpy(&dest[j], "\\x2f", 4);
111                         j += 4;
112                 } else if (src[i] == '\\') {
113                         if (j+4 >= size) {
114                                 j = 0;
115                                 break;
116                         }
117                         memcpy(&dest[j], "\\x5c", 4);
118                         j += 4;
119                 } else {
120                         if (j+1 >= size) {
121                                 j = 0;
122                                 break;
123                         }
124                         dest[j] = src[i];
125                         j++;
126                 }
127         }
128         dest[j] = '\0';
129         return j;
130 }
131
132 size_t util_path_decode(char *s)
133 {
134         size_t i, j;
135
136         for (i = 0, j = 0; s[i] != '\0'; j++) {
137                 if (memcmp(&s[i], "\\x2f", 4) == 0) {
138                         s[j] = '/';
139                         i += 4;
140                 } else if (memcmp(&s[i], "\\x5c", 4) == 0) {
141                         s[j] = '\\';
142                         i += 4;
143                 } else {
144                         s[j] = s[i];
145                         i++;
146                 }
147         }
148         s[j] = '\0';
149         return j;
150 }
151
152 void util_remove_trailing_chars(char *path, char c)
153 {
154         size_t len;
155
156         if (path == NULL)
157                 return;
158         len = strlen(path);
159         while (len > 0 && path[len-1] == c)
160                 path[--len] = '\0';
161 }
162
163 /*
164  * Concatenates strings. In any case, terminates in _all_ cases with '\0'
165  * and moves the @dest pointer forward to the added '\0'. Returns the
166  * remaining size, and 0 if the string was truncated.
167  */
168 size_t util_strpcpy(char **dest, size_t size, const char *src)
169 {
170         size_t len;
171
172         len = strlen(src);
173         if (len >= size) {
174                 if (size > 1)
175                         *dest = mempcpy(*dest, src, size-1);
176                 size = 0;
177                 *dest[0] = '\0';
178         } else {
179                 if (len > 0) {
180                         *dest = mempcpy(*dest, src, len);
181                         size -= len;
182                 }
183                 *dest[0] = '\0';
184         }
185         return size;
186 }
187
188 /* concatenates list of strings, moves dest forward */
189 size_t util_strpcpyl(char **dest, size_t size, const char *src, ...)
190 {
191         va_list va;
192
193         va_start(va, src);
194         do {
195                 size = util_strpcpy(dest, size, src);
196                 src = va_arg(va, char *);
197         } while (src != NULL);
198         va_end(va);
199
200         return size;
201 }
202
203 /* copies string */
204 size_t util_strscpy(char *dest, size_t size, const char *src)
205 {
206         char *s;
207
208         s = dest;
209         return util_strpcpy(&s, size, src);
210 }
211
212 /* concatenates list of strings */
213 size_t util_strscpyl(char *dest, size_t size, const char *src, ...)
214 {
215         va_list va;
216         char *s;
217
218         va_start(va, src);
219         s = dest;
220         do {
221                 size = util_strpcpy(&s, size, src);
222                 src = va_arg(va, char *);
223         } while (src != NULL);
224         va_end(va);
225
226         return size;
227 }
228
229 /* count of characters used to encode one unicode char */
230 static int utf8_encoded_expected_len(const char *str)
231 {
232         unsigned char c = (unsigned char)str[0];
233
234         if (c < 0x80)
235                 return 1;
236         if ((c & 0xe0) == 0xc0)
237                 return 2;
238         if ((c & 0xf0) == 0xe0)
239                 return 3;
240         if ((c & 0xf8) == 0xf0)
241                 return 4;
242         if ((c & 0xfc) == 0xf8)
243                 return 5;
244         if ((c & 0xfe) == 0xfc)
245                 return 6;
246         return 0;
247 }
248
249 /* decode one unicode char */
250 static int utf8_encoded_to_unichar(const char *str)
251 {
252         int unichar;
253         int len;
254         int i;
255
256         len = utf8_encoded_expected_len(str);
257         switch (len) {
258         case 1:
259                 return (int)str[0];
260         case 2:
261                 unichar = str[0] & 0x1f;
262                 break;
263         case 3:
264                 unichar = (int)str[0] & 0x0f;
265                 break;
266         case 4:
267                 unichar = (int)str[0] & 0x07;
268                 break;
269         case 5:
270                 unichar = (int)str[0] & 0x03;
271                 break;
272         case 6:
273                 unichar = (int)str[0] & 0x01;
274                 break;
275         default:
276                 return -1;
277         }
278
279         for (i = 1; i < len; i++) {
280                 if (((int)str[i] & 0xc0) != 0x80)
281                         return -1;
282                 unichar <<= 6;
283                 unichar |= (int)str[i] & 0x3f;
284         }
285
286         return unichar;
287 }
288
289 /* expected size used to encode one unicode char */
290 static int utf8_unichar_to_encoded_len(int unichar)
291 {
292         if (unichar < 0x80)
293                 return 1;
294         if (unichar < 0x800)
295                 return 2;
296         if (unichar < 0x10000)
297                 return 3;
298         if (unichar < 0x200000)
299                 return 4;
300         if (unichar < 0x4000000)
301                 return 5;
302         return 6;
303 }
304
305 /* check if unicode char has a valid numeric range */
306 static int utf8_unichar_valid_range(int unichar)
307 {
308         if (unichar > 0x10ffff)
309                 return 0;
310         if ((unichar & 0xfffff800) == 0xd800)
311                 return 0;
312         if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
313                 return 0;
314         if ((unichar & 0xffff) == 0xffff)
315                 return 0;
316         return 1;
317 }
318
319 /* validate one encoded unicode char and return its length */
320 static int utf8_encoded_valid_unichar(const char *str)
321 {
322         int len;
323         int unichar;
324         int i;
325
326         len = utf8_encoded_expected_len(str);
327         if (len == 0)
328                 return -1;
329
330         /* ascii is valid */
331         if (len == 1)
332                 return 1;
333
334         /* check if expected encoded chars are available */
335         for (i = 0; i < len; i++)
336                 if ((str[i] & 0x80) != 0x80)
337                         return -1;
338
339         unichar = utf8_encoded_to_unichar(str);
340
341         /* check if encoded length matches encoded value */
342         if (utf8_unichar_to_encoded_len(unichar) != len)
343                 return -1;
344
345         /* check if value has valid range */
346         if (!utf8_unichar_valid_range(unichar))
347                 return -1;
348
349         return len;
350 }
351
352 int util_replace_whitespace(const char *str, char *to, size_t len)
353 {
354         size_t i, j;
355
356         /* strip trailing whitespace */
357         len = strnlen(str, len);
358         while (len && isspace(str[len-1]))
359                 len--;
360
361         /* strip leading whitespace */
362         i = 0;
363         while (isspace(str[i]) && (i < len))
364                 i++;
365
366         j = 0;
367         while (i < len) {
368                 /* substitute multiple whitespace with a single '_' */
369                 if (isspace(str[i])) {
370                         while (isspace(str[i]))
371                                 i++;
372                         to[j++] = '_';
373                 }
374                 to[j++] = str[i++];
375         }
376         to[j] = '\0';
377         return 0;
378 }
379
380 static int is_whitelisted(char c, const char *white)
381 {
382         if ((c >= '0' && c <= '9') ||
383             (c >= 'A' && c <= 'Z') ||
384             (c >= 'a' && c <= 'z') ||
385             strchr("#+-.:=@_", c) != NULL ||
386             (white != NULL && strchr(white, c) != NULL))
387                 return 1;
388         return 0;
389 }
390
391 /* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
392 int util_replace_chars(char *str, const char *white)
393 {
394         size_t i = 0;
395         int replaced = 0;
396
397         while (str[i] != '\0') {
398                 int len;
399
400                 if (is_whitelisted(str[i], white)) {
401                         i++;
402                         continue;
403                 }
404
405                 /* accept hex encoding */
406                 if (str[i] == '\\' && str[i+1] == 'x') {
407                         i += 2;
408                         continue;
409                 }
410
411                 /* accept valid utf8 */
412                 len = utf8_encoded_valid_unichar(&str[i]);
413                 if (len > 1) {
414                         i += len;
415                         continue;
416                 }
417
418                 /* if space is allowed, replace whitespace with ordinary space */
419                 if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) {
420                         str[i] = ' ';
421                         i++;
422                         replaced++;
423                         continue;
424                 }
425
426                 /* everything else is replaced with '_' */
427                 str[i] = '_';
428                 i++;
429                 replaced++;
430         }
431         return replaced;
432 }
433
434 /**
435  * udev_util_encode_string:
436  * @str: input string to be encoded
437  * @str_enc: output string to store the encoded input string
438  * @len: maximum size of the output string, which may be
439  *       four times as long as the input string
440  *
441  * Encode all potentially unsafe characters of a string to the
442  * corresponding 2 char hex value prefixed by '\x'.
443  *
444  * Returns: 0 if the entire string was copied, non-zero otherwise.
445  **/
446 UDEV_EXPORT int udev_util_encode_string(const char *str, char *str_enc, size_t len)
447 {
448         size_t i, j;
449
450         if (str == NULL || str_enc == NULL)
451                 return -1;
452
453         for (i = 0, j = 0; str[i] != '\0'; i++) {
454                 int seqlen;
455
456                 seqlen = utf8_encoded_valid_unichar(&str[i]);
457                 if (seqlen > 1) {
458                         if (len-j < (size_t)seqlen)
459                                 goto err;
460                         memcpy(&str_enc[j], &str[i], seqlen);
461                         j += seqlen;
462                         i += (seqlen-1);
463                 } else if (str[i] == '\\' || !is_whitelisted(str[i], NULL)) {
464                         if (len-j < 4)
465                                 goto err;
466                         sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
467                         j += 4;
468                 } else {
469                         if (len-j < 1)
470                                 goto err;
471                         str_enc[j] = str[i];
472                         j++;
473                 }
474         }
475         if (len-j < 1)
476                 goto err;
477         str_enc[j] = '\0';
478         return 0;
479 err:
480         return -1;
481 }
482
483 /*
484  * http://sites.google.com/site/murmurhash/
485  *
486  * All code is released to the public domain. For business purposes,
487  * Murmurhash is under the MIT license.
488  *
489  */
490 static unsigned int murmur_hash2(const char *key, int len, unsigned int seed)
491 {
492         /*
493          *  'm' and 'r' are mixing constants generated offline.
494          *  They're not really 'magic', they just happen to work well.
495          */
496         const unsigned int m = 0x5bd1e995;
497         const int r = 24;
498
499         /* initialize the hash to a 'random' value */
500         unsigned int h = seed ^ len;
501
502         /* mix 4 bytes at a time into the hash */
503         const unsigned char * data = (const unsigned char *)key;
504
505         while(len >= 4) {
506                 unsigned int k = *(unsigned int *)data;
507
508                 k *= m;
509                 k ^= k >> r;
510                 k *= m;
511                 h *= m;
512                 h ^= k;
513
514                 data += 4;
515                 len -= 4;
516         }
517
518         /* handle the last few bytes of the input array */
519         switch(len) {
520         case 3:
521                 h ^= data[2] << 16;
522         case 2:
523                 h ^= data[1] << 8;
524         case 1:
525                 h ^= data[0];
526                 h *= m;
527         };
528
529         /* do a few final mixes of the hash to ensure the last few bytes are well-incorporated */
530         h ^= h >> 13;
531         h *= m;
532         h ^= h >> 15;
533
534         return h;
535 }
536
537 unsigned int util_string_hash32(const char *str)
538 {
539         return murmur_hash2(str, strlen(str), 0);
540 }
541
542 /* get a bunch of bit numbers out of the hash, and set the bits in our bit field */
543 uint64_t util_string_bloom64(const char *str)
544 {
545         uint64_t bits = 0;
546         unsigned int hash = util_string_hash32(str);
547
548         bits |= 1LLU << (hash & 63);
549         bits |= 1LLU << ((hash >> 6) & 63);
550         bits |= 1LLU << ((hash >> 12) & 63);
551         bits |= 1LLU << ((hash >> 18) & 63);
552         return bits;
553 }
554
555 #define USEC_PER_SEC  1000000ULL
556 #define NSEC_PER_USEC 1000ULL
557 unsigned long long ts_usec(const struct timespec *ts)
558 {
559         return (unsigned long long) ts->tv_sec * USEC_PER_SEC +
560                (unsigned long long) ts->tv_nsec / NSEC_PER_USEC;
561 }
562
563 unsigned long long now_usec(void)
564 {
565         struct timespec ts;
566
567         if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
568                 return 0;
569         return ts_usec(&ts);
570 }