chiark / gitweb /
libudev: GPL -> LGPL
[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 len)
105 {
106         char t[(len * 3)+1];
107         size_t i, j;
108
109         for (i = 0, j = 0; s[i] != '\0'; 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 (len == 0)
122                 return j;
123         i = (j < len - 1) ? j : len - 1;
124         memcpy(s, t, i);
125         s[i] = '\0';
126         return j;
127 }
128
129 size_t util_path_decode(char *s)
130 {
131         size_t i, j;
132
133         for (i = 0, j = 0; s[i] != '\0'; j++) {
134                 if (memcmp(&s[i], "\\x2f", 4) == 0) {
135                         s[j] = '/';
136                         i += 4;
137                 }else if (memcmp(&s[i], "\\x5c", 4) == 0) {
138                         s[j] = '\\';
139                         i += 4;
140                 } else {
141                         s[j] = s[i];
142                         i++;
143                 }
144         }
145         s[j] = '\0';
146         return j;
147 }
148
149 void util_remove_trailing_chars(char *path, char c)
150 {
151         size_t len;
152
153         if (path == NULL)
154                 return;
155         len = strlen(path);
156         while (len > 0 && path[len-1] == c)
157                 path[--len] = '\0';
158 }
159
160 size_t util_strlcpy(char *dst, const char *src, size_t size)
161 {
162         size_t bytes = 0;
163         char *q = dst;
164         const char *p = src;
165         char ch;
166
167         while ((ch = *p++)) {
168                 if (bytes+1 < size)
169                         *q++ = ch;
170                 bytes++;
171         }
172
173         /* If size == 0 there is no space for a final null... */
174         if (size)
175                 *q = '\0';
176         return bytes;
177 }
178
179 size_t util_strlcat(char *dst, const char *src, size_t size)
180 {
181         size_t bytes = 0;
182         char *q = dst;
183         const char *p = src;
184         char ch;
185
186         while (bytes < size && *q) {
187                 q++;
188                 bytes++;
189         }
190         if (bytes == size)
191                 return (bytes + strlen(src));
192
193         while ((ch = *p++)) {
194                 if (bytes+1 < size)
195                 *q++ = ch;
196                 bytes++;
197         }
198
199         *q = '\0';
200         return bytes;
201 }
202
203 /* count of characters used to encode one unicode char */
204 static int utf8_encoded_expected_len(const char *str)
205 {
206         unsigned char c = (unsigned char)str[0];
207
208         if (c < 0x80)
209                 return 1;
210         if ((c & 0xe0) == 0xc0)
211                 return 2;
212         if ((c & 0xf0) == 0xe0)
213                 return 3;
214         if ((c & 0xf8) == 0xf0)
215                 return 4;
216         if ((c & 0xfc) == 0xf8)
217                 return 5;
218         if ((c & 0xfe) == 0xfc)
219                 return 6;
220         return 0;
221 }
222
223 /* decode one unicode char */
224 static int utf8_encoded_to_unichar(const char *str)
225 {
226         int unichar;
227         int len;
228         int i;
229
230         len = utf8_encoded_expected_len(str);
231         switch (len) {
232         case 1:
233                 return (int)str[0];
234         case 2:
235                 unichar = str[0] & 0x1f;
236                 break;
237         case 3:
238                 unichar = (int)str[0] & 0x0f;
239                 break;
240         case 4:
241                 unichar = (int)str[0] & 0x07;
242                 break;
243         case 5:
244                 unichar = (int)str[0] & 0x03;
245                 break;
246         case 6:
247                 unichar = (int)str[0] & 0x01;
248                 break;
249         default:
250                 return -1;
251         }
252
253         for (i = 1; i < len; i++) {
254                 if (((int)str[i] & 0xc0) != 0x80)
255                         return -1;
256                 unichar <<= 6;
257                 unichar |= (int)str[i] & 0x3f;
258         }
259
260         return unichar;
261 }
262
263 /* expected size used to encode one unicode char */
264 static int utf8_unichar_to_encoded_len(int unichar)
265 {
266         if (unichar < 0x80)
267                 return 1;
268         if (unichar < 0x800)
269                 return 2;
270         if (unichar < 0x10000)
271                 return 3;
272         if (unichar < 0x200000)
273                 return 4;
274         if (unichar < 0x4000000)
275                 return 5;
276         return 6;
277 }
278
279 /* check if unicode char has a valid numeric range */
280 static int utf8_unichar_valid_range(int unichar)
281 {
282         if (unichar > 0x10ffff)
283                 return 0;
284         if ((unichar & 0xfffff800) == 0xd800)
285                 return 0;
286         if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
287                 return 0;
288         if ((unichar & 0xffff) == 0xffff)
289                 return 0;
290         return 1;
291 }
292
293 /* validate one encoded unicode char and return its length */
294 static int utf8_encoded_valid_unichar(const char *str)
295 {
296         int len;
297         int unichar;
298         int i;
299
300         len = utf8_encoded_expected_len(str);
301         if (len == 0)
302                 return -1;
303
304         /* ascii is valid */
305         if (len == 1)
306                 return 1;
307
308         /* check if expected encoded chars are available */
309         for (i = 0; i < len; i++)
310                 if ((str[i] & 0x80) != 0x80)
311                         return -1;
312
313         unichar = utf8_encoded_to_unichar(str);
314
315         /* check if encoded length matches encoded value */
316         if (utf8_unichar_to_encoded_len(unichar) != len)
317                 return -1;
318
319         /* check if value has valid range */
320         if (!utf8_unichar_valid_range(unichar))
321                 return -1;
322
323         return len;
324 }
325
326 int udev_util_replace_whitespace(const char *str, char *to, size_t len)
327 {
328         size_t i, j;
329
330         /* strip trailing whitespace */
331         len = strnlen(str, len);
332         while (len && isspace(str[len-1]))
333                 len--;
334
335         /* strip leading whitespace */
336         i = 0;
337         while (isspace(str[i]) && (i < len))
338                 i++;
339
340         j = 0;
341         while (i < len) {
342                 /* substitute multiple whitespace with a single '_' */
343                 if (isspace(str[i])) {
344                         while (isspace(str[i]))
345                                 i++;
346                         to[j++] = '_';
347                 }
348                 to[j++] = str[i++];
349         }
350         to[j] = '\0';
351         return 0;
352 }
353
354 static int is_whitelisted(char c, const char *white)
355 {
356         if ((c >= '0' && c <= '9') ||
357             (c >= 'A' && c <= 'Z') ||
358             (c >= 'a' && c <= 'z') ||
359             strchr("#+-.:=@_", c) != NULL ||
360             (white != NULL && strchr(white, c) != NULL))
361                 return 1;
362         return 0;
363 }
364
365 /* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
366 int udev_util_replace_chars(char *str, const char *white)
367 {
368         size_t i = 0;
369         int replaced = 0;
370
371         while (str[i] != '\0') {
372                 int len;
373
374                 if (is_whitelisted(str[i], white)) {
375                         i++;
376                         continue;
377                 }
378
379                 /* accept hex encoding */
380                 if (str[i] == '\\' && str[i+1] == 'x') {
381                         i += 2;
382                         continue;
383                 }
384
385                 /* accept valid utf8 */
386                 len = utf8_encoded_valid_unichar(&str[i]);
387                 if (len > 1) {
388                         i += len;
389                         continue;
390                 }
391
392                 /* if space is allowed, replace whitespace with ordinary space */
393                 if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) {
394                         str[i] = ' ';
395                         i++;
396                         replaced++;
397                         continue;
398                 }
399
400                 /* everything else is replaced with '_' */
401                 str[i] = '_';
402                 i++;
403                 replaced++;
404         }
405         return replaced;
406 }
407
408 /**
409  * util_encode_string:
410  * @str: input string to be encoded
411  * @str_enc: output string to store the encoded input string
412  * @len: maximum size of the output string, which may be
413  *       four times as long as the input string
414  *
415  * Encode all potentially unsafe characters of a string to the
416  * corresponding hex value prefixed by '\x'.
417  *
418  * Returns: 0 if the entire string was copied, non-zero otherwise.
419  **/
420 int udev_util_encode_string(const char *str, char *str_enc, size_t len)
421 {
422         size_t i, j;
423
424         if (str == NULL || str_enc == NULL || len == 0)
425                 return -1;
426
427         str_enc[0] = '\0';
428         for (i = 0, j = 0; str[i] != '\0'; i++) {
429                 int seqlen;
430
431                 seqlen = utf8_encoded_valid_unichar(&str[i]);
432                 if (seqlen > 1) {
433                         memcpy(&str_enc[j], &str[i], seqlen);
434                         j += seqlen;
435                         i += (seqlen-1);
436                 } else if (str[i] == '\\' || !is_whitelisted(str[i], NULL)) {
437                         sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
438                         j += 4;
439                 } else {
440                         str_enc[j] = str[i];
441                         j++;
442                 }
443                 if (j+3 >= len)
444                         goto err;
445         }
446         str_enc[j] = '\0';
447         return 0;
448 err:
449         return -1;
450 }