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