chiark / gitweb /
libudev: path_encode - always return 0 if encoded string does not fit into size
[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 }