chiark / gitweb /
018e2cdb0860d91722d0b68058f96e666c0bfbaf
[elogind.git] / udev / lib / libudev-util.c
1 /*
2  * libudev - interface to udev device information
3  *
4  * Copyright (C) 2008 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         ssize_t len;
30         const char *pos;
31
32         util_strlcpy(path, syspath, sizeof(path));
33         util_strlcat(path, "/", sizeof(path));
34         util_strlcat(path, slink, sizeof(path));
35         len = readlink(path, path, sizeof(path));
36         if (len < 0 || len >= (ssize_t) sizeof(path))
37                 return -1;
38         path[len] = '\0';
39         pos = strrchr(path, '/');
40         if (pos == NULL)
41                 return -1;
42         pos = &pos[1];
43         dbg(udev, "resolved link to: '%s'\n", pos);
44         return util_strlcpy(value, pos, size);
45 }
46
47 ssize_t util_get_sys_subsystem(struct udev *udev, const char *syspath, char *subsystem, size_t size)
48 {
49         return get_sys_link(udev, "subsystem", syspath, subsystem, size);
50 }
51
52 ssize_t util_get_sys_driver(struct udev *udev, const char *syspath, char *driver, size_t size)
53 {
54         return get_sys_link(udev, "driver", syspath, driver, size);
55 }
56
57 int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size)
58 {
59         char link_target[UTIL_PATH_SIZE];
60
61         int len;
62         int i;
63         int back;
64
65         len = readlink(syspath, link_target, sizeof(link_target));
66         if (len <= 0)
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                 char *pos = strrchr(syspath, '/');
76
77                 if (pos == NULL)
78                         return -1;
79                 pos[0] = '\0';
80         }
81         dbg(udev, "after moving back '%s'\n", syspath);
82         util_strlcat(syspath, "/", size);
83         util_strlcat(syspath, &link_target[back * 3], size);
84         return 0;
85 }
86
87 int util_log_priority(const char *priority)
88 {
89         char *endptr;
90         int prio;
91
92         prio = strtol(priority, &endptr, 10);
93         if (endptr[0] == '\0')
94                 return prio;
95         if (strncasecmp(priority, "err", 3) == 0)
96                 return LOG_ERR;
97         if (strcasecmp(priority, "info") == 0)
98                 return LOG_INFO;
99         if (strcasecmp(priority, "debug") == 0)
100                 return LOG_DEBUG;
101         return 0;
102 }
103
104 size_t util_path_encode(char *s, size_t size)
105 {
106         char t[(size * 4)+1];
107         size_t i, j;
108
109         for (i = 0, j = 0; s[i] != '\0' && i < size; i++) {
110                 if (s[i] == '/') {
111                         memcpy(&t[j], "\\x2f", 4);
112                         j += 4;
113                 } else if (s[i] == '\\') {
114                         memcpy(&t[j], "\\x5c", 4);
115                         j += 4;
116                 } else {
117                         t[j] = s[i];
118                         j++;
119                 }
120         }
121         if (i >= size)
122                 return 0;
123         if (j >= size)
124                 return 0;
125         memcpy(s, t, j);
126         s[j] = '\0';
127         return j;
128 }
129
130 size_t util_path_decode(char *s)
131 {
132         size_t i, j;
133
134         for (i = 0, j = 0; s[i] != '\0'; j++) {
135                 if (memcmp(&s[i], "\\x2f", 4) == 0) {
136                         s[j] = '/';
137                         i += 4;
138                 } else if (memcmp(&s[i], "\\x5c", 4) == 0) {
139                         s[j] = '\\';
140                         i += 4;
141                 } else {
142                         s[j] = s[i];
143                         i++;
144                 }
145         }
146         s[j] = '\0';
147         return j;
148 }
149
150 void util_remove_trailing_chars(char *path, char c)
151 {
152         size_t len;
153
154         if (path == NULL)
155                 return;
156         len = strlen(path);
157         while (len > 0 && path[len-1] == c)
158                 path[--len] = '\0';
159 }
160
161 size_t util_strlcpy(char *dst, const char *src, size_t size)
162 {
163         size_t bytes = 0;
164         char *q = dst;
165         const char *p = src;
166         char ch;
167
168         while ((ch = *p++)) {
169                 if (bytes+1 < size)
170                         *q++ = ch;
171                 bytes++;
172         }
173
174         /* If size == 0 there is no space for a final null... */
175         if (size)
176                 *q = '\0';
177         return bytes;
178 }
179
180 size_t util_strlcat(char *dst, const char *src, size_t size)
181 {
182         size_t bytes = 0;
183         char *q = dst;
184         const char *p = src;
185         char ch;
186
187         while (bytes < size && *q) {
188                 q++;
189                 bytes++;
190         }
191         if (bytes == size)
192                 return (bytes + strlen(src));
193
194         while ((ch = *p++)) {
195                 if (bytes+1 < size)
196                 *q++ = ch;
197                 bytes++;
198         }
199
200         *q = '\0';
201         return bytes;
202 }
203
204 /* count of characters used to encode one unicode char */
205 static int utf8_encoded_expected_len(const char *str)
206 {
207         unsigned char c = (unsigned char)str[0];
208
209         if (c < 0x80)
210                 return 1;
211         if ((c & 0xe0) == 0xc0)
212                 return 2;
213         if ((c & 0xf0) == 0xe0)
214                 return 3;
215         if ((c & 0xf8) == 0xf0)
216                 return 4;
217         if ((c & 0xfc) == 0xf8)
218                 return 5;
219         if ((c & 0xfe) == 0xfc)
220                 return 6;
221         return 0;
222 }
223
224 /* decode one unicode char */
225 static int utf8_encoded_to_unichar(const char *str)
226 {
227         int unichar;
228         int len;
229         int i;
230
231         len = utf8_encoded_expected_len(str);
232         switch (len) {
233         case 1:
234                 return (int)str[0];
235         case 2:
236                 unichar = str[0] & 0x1f;
237                 break;
238         case 3:
239                 unichar = (int)str[0] & 0x0f;
240                 break;
241         case 4:
242                 unichar = (int)str[0] & 0x07;
243                 break;
244         case 5:
245                 unichar = (int)str[0] & 0x03;
246                 break;
247         case 6:
248                 unichar = (int)str[0] & 0x01;
249                 break;
250         default:
251                 return -1;
252         }
253
254         for (i = 1; i < len; i++) {
255                 if (((int)str[i] & 0xc0) != 0x80)
256                         return -1;
257                 unichar <<= 6;
258                 unichar |= (int)str[i] & 0x3f;
259         }
260
261         return unichar;
262 }
263
264 /* expected size used to encode one unicode char */
265 static int utf8_unichar_to_encoded_len(int unichar)
266 {
267         if (unichar < 0x80)
268                 return 1;
269         if (unichar < 0x800)
270                 return 2;
271         if (unichar < 0x10000)
272                 return 3;
273         if (unichar < 0x200000)
274                 return 4;
275         if (unichar < 0x4000000)
276                 return 5;
277         return 6;
278 }
279
280 /* check if unicode char has a valid numeric range */
281 static int utf8_unichar_valid_range(int unichar)
282 {
283         if (unichar > 0x10ffff)
284                 return 0;
285         if ((unichar & 0xfffff800) == 0xd800)
286                 return 0;
287         if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
288                 return 0;
289         if ((unichar & 0xffff) == 0xffff)
290                 return 0;
291         return 1;
292 }
293
294 /* validate one encoded unicode char and return its length */
295 static int utf8_encoded_valid_unichar(const char *str)
296 {
297         int len;
298         int unichar;
299         int i;
300
301         len = utf8_encoded_expected_len(str);
302         if (len == 0)
303                 return -1;
304
305         /* ascii is valid */
306         if (len == 1)
307                 return 1;
308
309         /* check if expected encoded chars are available */
310         for (i = 0; i < len; i++)
311                 if ((str[i] & 0x80) != 0x80)
312                         return -1;
313
314         unichar = utf8_encoded_to_unichar(str);
315
316         /* check if encoded length matches encoded value */
317         if (utf8_unichar_to_encoded_len(unichar) != len)
318                 return -1;
319
320         /* check if value has valid range */
321         if (!utf8_unichar_valid_range(unichar))
322                 return -1;
323
324         return len;
325 }
326
327 int udev_util_replace_whitespace(const char *str, char *to, size_t len)
328 {
329         size_t i, j;
330
331         /* strip trailing whitespace */
332         len = strnlen(str, len);
333         while (len && isspace(str[len-1]))
334                 len--;
335
336         /* strip leading whitespace */
337         i = 0;
338         while (isspace(str[i]) && (i < len))
339                 i++;
340
341         j = 0;
342         while (i < len) {
343                 /* substitute multiple whitespace with a single '_' */
344                 if (isspace(str[i])) {
345                         while (isspace(str[i]))
346                                 i++;
347                         to[j++] = '_';
348                 }
349                 to[j++] = str[i++];
350         }
351         to[j] = '\0';
352         return 0;
353 }
354
355 static int is_whitelisted(char c, const char *white)
356 {
357         if ((c >= '0' && c <= '9') ||
358             (c >= 'A' && c <= 'Z') ||
359             (c >= 'a' && c <= 'z') ||
360             strchr("#+-.:=@_", c) != NULL ||
361             (white != NULL && strchr(white, c) != NULL))
362                 return 1;
363         return 0;
364 }
365
366 /* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
367 int udev_util_replace_chars(char *str, const char *white)
368 {
369         size_t i = 0;
370         int replaced = 0;
371
372         while (str[i] != '\0') {
373                 int len;
374
375                 if (is_whitelisted(str[i], white)) {
376                         i++;
377                         continue;
378                 }
379
380                 /* accept hex encoding */
381                 if (str[i] == '\\' && str[i+1] == 'x') {
382                         i += 2;
383                         continue;
384                 }
385
386                 /* accept valid utf8 */
387                 len = utf8_encoded_valid_unichar(&str[i]);
388                 if (len > 1) {
389                         i += len;
390                         continue;
391                 }
392
393                 /* if space is allowed, replace whitespace with ordinary space */
394                 if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) {
395                         str[i] = ' ';
396                         i++;
397                         replaced++;
398                         continue;
399                 }
400
401                 /* everything else is replaced with '_' */
402                 str[i] = '_';
403                 i++;
404                 replaced++;
405         }
406         return replaced;
407 }
408
409 /**
410  * util_encode_string:
411  * @str: input string to be encoded
412  * @str_enc: output string to store the encoded input string
413  * @len: maximum size of the output string, which may be
414  *       four times as long as the input string
415  *
416  * Encode all potentially unsafe characters of a string to the
417  * corresponding hex value prefixed by '\x'.
418  *
419  * Returns: 0 if the entire string was copied, non-zero otherwise.
420  **/
421 int udev_util_encode_string(const char *str, char *str_enc, size_t len)
422 {
423         size_t i, j;
424
425         if (str == NULL || str_enc == NULL || len == 0)
426                 return -1;
427
428         str_enc[0] = '\0';
429         for (i = 0, j = 0; str[i] != '\0'; i++) {
430                 int seqlen;
431
432                 seqlen = utf8_encoded_valid_unichar(&str[i]);
433                 if (seqlen > 1) {
434                         memcpy(&str_enc[j], &str[i], seqlen);
435                         j += seqlen;
436                         i += (seqlen-1);
437                 } else if (str[i] == '\\' || !is_whitelisted(str[i], NULL)) {
438                         sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
439                         j += 4;
440                 } else {
441                         str_enc[j] = str[i];
442                         j++;
443                 }
444                 if (j+3 >= len)
445                         goto err;
446         }
447         str_enc[j] = '\0';
448         return 0;
449 err:
450         return -1;
451 }
452
453 void util_set_fd_cloexec(int fd)
454 {
455         int flags;
456
457         flags = fcntl(fd, F_GETFD);
458         if (flags < 0)
459                 flags = FD_CLOEXEC;
460         else
461                 flags |= FD_CLOEXEC;
462         fcntl(fd, F_SETFD, flags);
463 }
464
465 unsigned int util_crc32(const unsigned char *buf, size_t len)
466 {
467         unsigned int crc;
468         const unsigned char *end;
469         static const unsigned int crc32_table[256] = {
470                 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
471                 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
472                 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
473                 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
474                 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
475                 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
476                 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
477                 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
478                 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
479                 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
480                 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
481                 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
482                 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
483                 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
484                 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
485                 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
486                 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
487                 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
488                 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
489                 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
490                 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
491                 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
492                 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
493                 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
494                 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
495                 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
496                 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
497                 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
498                 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
499                 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
500                 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
501                 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
502                 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
503                 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
504                 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
505                 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
506                 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
507                 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
508                 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
509                 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
510                 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
511                 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
512                 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
513                 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
514                 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
515                 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
516                 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
517                 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
518                 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
519                 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
520                 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
521                 0x2d02ef8d
522         };
523
524         crc = 0xffffffff;
525         for (end = buf + len; buf < end; ++buf)
526                 crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
527         return ~crc;
528 }