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