]> git.ipfire.org Git - thirdparty/systemd.git/blame - udev/lib/libudev-util.c
cdrom_id: add and use ID_CDROM_MEDIA to decide if we run vol_id
[thirdparty/systemd.git] / udev / lib / libudev-util.c
CommitLineData
eb1f0e66
KS
1/*
2 * libudev - interface to udev device information
3 *
4 * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
5 *
4061ab9f
KS
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.
eb1f0e66
KS
10 */
11
eb1f0e66
KS
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>
75250977 19#include <ctype.h>
93b0f384 20#include <fcntl.h>
eb1f0e66
KS
21#include <sys/stat.h>
22
23#include "libudev.h"
24#include "libudev-private.h"
eb1f0e66 25
5c5cad79 26static ssize_t get_sys_link(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size)
eb1f0e66 27{
3eb46ec6 28 char path[UTIL_PATH_SIZE];
eb1f0e66
KS
29 ssize_t len;
30 const char *pos;
31
8753fadf 32 util_strlcpy(path, syspath, sizeof(path));
3eb46ec6
KS
33 util_strlcat(path, "/", sizeof(path));
34 util_strlcat(path, slink, sizeof(path));
eb1f0e66
KS
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];
86b57788 43 dbg(udev, "resolved link to: '%s'\n", pos);
5c5cad79 44 return util_strlcpy(value, pos, size);
eb1f0e66 45}
95d90c4f 46
8753fadf 47ssize_t util_get_sys_subsystem(struct udev *udev, const char *syspath, char *subsystem, size_t size)
95d90c4f 48{
8753fadf 49 return get_sys_link(udev, "subsystem", syspath, subsystem, size);
95d90c4f
KS
50}
51
8753fadf 52ssize_t util_get_sys_driver(struct udev *udev, const char *syspath, char *driver, size_t size)
95d90c4f 53{
8753fadf 54 return get_sys_link(udev, "driver", syspath, driver, size);
95d90c4f
KS
55}
56
8753fadf 57int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size)
b21b95d7 58{
3eb46ec6
KS
59 char link_target[UTIL_PATH_SIZE];
60
b21b95d7
KS
61 int len;
62 int i;
63 int back;
64
8753fadf 65 len = readlink(syspath, link_target, sizeof(link_target));
b21b95d7
KS
66 if (len <= 0)
67 return -1;
68 link_target[len] = '\0';
8753fadf 69 dbg(udev, "path link '%s' points to '%s'\n", syspath, link_target);
b21b95d7
KS
70
71 for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++)
72 ;
8753fadf 73 dbg(udev, "base '%s', tail '%s', back %i\n", syspath, &link_target[back * 3], back);
b21b95d7 74 for (i = 0; i <= back; i++) {
8753fadf 75 char *pos = strrchr(syspath, '/');
b21b95d7
KS
76
77 if (pos == NULL)
78 return -1;
79 pos[0] = '\0';
80 }
8753fadf
KS
81 dbg(udev, "after moving back '%s'\n", syspath);
82 util_strlcat(syspath, "/", size);
83 util_strlcat(syspath, &link_target[back * 3], size);
b21b95d7
KS
84 return 0;
85}
7a01f11a 86
7a01f11a
KS
87int 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
3e5bafc9 104size_t util_path_encode(char *s, size_t size)
7a01f11a 105{
3e5bafc9 106 char t[(size * 4)+1];
7a01f11a
KS
107 size_t i, j;
108
3e5bafc9 109 for (i = 0, j = 0; s[i] != '\0' && i < size; i++) {
7a01f11a
KS
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 }
3e5bafc9
KS
121 if (i >= size)
122 return 0;
123 if (j >= size)
124 return 0;
125 memcpy(s, t, j);
126 s[j] = '\0';
7a01f11a
KS
127 return j;
128}
129
130size_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;
3e5bafc9 138 } else if (memcmp(&s[i], "\\x5c", 4) == 0) {
7a01f11a
KS
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
150void 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}
3eb46ec6
KS
160
161size_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
180size_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}
75250977
KS
203
204/* count of characters used to encode one unicode char */
205static 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 */
225static 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 */
265static 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 */
281static 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 */
295static 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
92f43136
KS
327int 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
355static 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
75250977 366/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
92f43136 367int udev_util_replace_chars(char *str, const char *white)
75250977
KS
368{
369 size_t i = 0;
370 int replaced = 0;
371
372 while (str[i] != '\0') {
373 int len;
374
92f43136 375 if (is_whitelisted(str[i], white)) {
75250977
KS
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 */
bbfaec2b 394 if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) {
75250977
KS
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 }
75250977
KS
406 return replaced;
407}
92f43136
KS
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 **/
421int 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;
449err:
450 return -1;
451}
4b09a2fc
AJ
452
453void 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}