]>
Commit | Line | Data |
---|---|---|
52ad194e | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
663996b3 | 2 | |
db2df898 | 3 | #include <ctype.h> |
663996b3 | 4 | #include <errno.h> |
663996b3 | 5 | |
6e866b33 | 6 | #include "sd-device.h" |
db2df898 | 7 | |
db2df898 | 8 | #include "device-nodes.h" |
6e866b33 MB |
9 | #include "libudev-util.h" |
10 | #include "string-util.h" | |
11 | #include "strxcpyx.h" | |
14228c0d | 12 | #include "utf8.h" |
663996b3 MS |
13 | |
14 | /** | |
15 | * SECTION:libudev-util | |
16 | * @short_description: utils | |
17 | * | |
18 | * Utilities useful when dealing with devices and device node names. | |
19 | */ | |
20 | ||
663996b3 | 21 | /* handle "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */ |
6e866b33 MB |
22 | int util_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, bool read_value) { |
23 | char temp[UTIL_PATH_SIZE], *subsys, *sysname, *attr; | |
24 | _cleanup_(sd_device_unrefp) sd_device *dev = NULL; | |
25 | const char *val; | |
26 | int r; | |
663996b3 MS |
27 | |
28 | if (string[0] != '[') | |
6e866b33 | 29 | return -EINVAL; |
663996b3 MS |
30 | |
31 | strscpy(temp, sizeof(temp), string); | |
32 | ||
33 | subsys = &temp[1]; | |
34 | ||
35 | sysname = strchr(subsys, '/'); | |
6e866b33 MB |
36 | if (!sysname) |
37 | return -EINVAL; | |
663996b3 MS |
38 | sysname[0] = '\0'; |
39 | sysname = &sysname[1]; | |
40 | ||
41 | attr = strchr(sysname, ']'); | |
6e866b33 MB |
42 | if (!attr) |
43 | return -EINVAL; | |
663996b3 MS |
44 | attr[0] = '\0'; |
45 | attr = &attr[1]; | |
46 | if (attr[0] == '/') | |
47 | attr = &attr[1]; | |
48 | if (attr[0] == '\0') | |
49 | attr = NULL; | |
50 | ||
6e866b33 MB |
51 | if (read_value && !attr) |
52 | return -EINVAL; | |
663996b3 | 53 | |
6e866b33 MB |
54 | r = sd_device_new_from_subsystem_sysname(&dev, subsys, sysname); |
55 | if (r < 0) | |
56 | return r; | |
663996b3 MS |
57 | |
58 | if (read_value) { | |
6e866b33 MB |
59 | r = sd_device_get_sysattr_value(dev, attr, &val); |
60 | if (r < 0 && r != -ENOENT) | |
61 | return r; | |
62 | if (r == -ENOENT) | |
663996b3 | 63 | result[0] = '\0'; |
6e866b33 MB |
64 | else |
65 | strscpy(result, maxsize, val); | |
f47781d8 | 66 | log_debug("value '[%s/%s]%s' is '%s'", subsys, sysname, attr, result); |
663996b3 | 67 | } else { |
6e866b33 MB |
68 | r = sd_device_get_syspath(dev, &val); |
69 | if (r < 0) | |
70 | return r; | |
663996b3 | 71 | |
6e866b33 MB |
72 | strscpyl(result, maxsize, val, attr ? "/" : NULL, attr ?: NULL, NULL); |
73 | log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, strempty(attr), result); | |
f47781d8 | 74 | } |
6e866b33 | 75 | return 0; |
663996b3 MS |
76 | } |
77 | ||
6e866b33 | 78 | size_t util_path_encode(const char *src, char *dest, size_t size) { |
663996b3 MS |
79 | size_t i, j; |
80 | ||
6e866b33 MB |
81 | assert(src); |
82 | assert(dest); | |
83 | ||
663996b3 MS |
84 | for (i = 0, j = 0; src[i] != '\0'; i++) { |
85 | if (src[i] == '/') { | |
86 | if (j+4 >= size) { | |
87 | j = 0; | |
88 | break; | |
89 | } | |
90 | memcpy(&dest[j], "\\x2f", 4); | |
91 | j += 4; | |
92 | } else if (src[i] == '\\') { | |
93 | if (j+4 >= size) { | |
94 | j = 0; | |
95 | break; | |
96 | } | |
97 | memcpy(&dest[j], "\\x5c", 4); | |
98 | j += 4; | |
99 | } else { | |
100 | if (j+1 >= size) { | |
101 | j = 0; | |
102 | break; | |
103 | } | |
104 | dest[j] = src[i]; | |
105 | j++; | |
106 | } | |
107 | } | |
108 | dest[j] = '\0'; | |
109 | return j; | |
110 | } | |
111 | ||
2897b343 MP |
112 | /* |
113 | * Copy from 'str' to 'to', while removing all leading and trailing whitespace, | |
114 | * and replacing each run of consecutive whitespace with a single underscore. | |
115 | * The chars from 'str' are copied up to the \0 at the end of the string, or | |
116 | * at most 'len' chars. This appends \0 to 'to', at the end of the copied | |
117 | * characters. | |
118 | * | |
119 | * If 'len' chars are copied into 'to', the final \0 is placed at len+1 | |
120 | * (i.e. 'to[len] = \0'), so the 'to' buffer must have at least len+1 | |
121 | * chars available. | |
122 | * | |
123 | * Note this may be called with 'str' == 'to', i.e. to replace whitespace | |
124 | * in-place in a buffer. This function can handle that situation. | |
125 | */ | |
6e866b33 MB |
126 | size_t util_replace_whitespace(const char *str, char *to, size_t len) { |
127 | bool is_space = false; | |
128 | const char *p = str; | |
129 | size_t j; | |
663996b3 | 130 | |
6e866b33 MB |
131 | assert(str); |
132 | assert(to); | |
663996b3 | 133 | |
6e866b33 MB |
134 | p += strspn(p, WHITESPACE); |
135 | ||
136 | for (j = 0; j < len && *p != '\0'; p++) { | |
137 | if (isspace(*p)) { | |
138 | is_space = true; | |
139 | continue; | |
140 | } | |
141 | ||
142 | if (is_space) { | |
143 | if (j + 1 >= len) | |
144 | break; | |
663996b3 | 145 | |
663996b3 | 146 | to[j++] = '_'; |
6e866b33 | 147 | is_space = false; |
663996b3 | 148 | } |
6e866b33 | 149 | to[j++] = *p; |
663996b3 | 150 | } |
6e866b33 | 151 | |
663996b3 | 152 | to[j] = '\0'; |
2897b343 | 153 | return j; |
663996b3 MS |
154 | } |
155 | ||
663996b3 | 156 | /* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */ |
6e866b33 MB |
157 | size_t util_replace_chars(char *str, const char *white) { |
158 | size_t i = 0, replaced = 0; | |
159 | ||
160 | assert(str); | |
663996b3 MS |
161 | |
162 | while (str[i] != '\0') { | |
163 | int len; | |
164 | ||
14228c0d | 165 | if (whitelisted_char_for_devnode(str[i], white)) { |
663996b3 MS |
166 | i++; |
167 | continue; | |
168 | } | |
169 | ||
170 | /* accept hex encoding */ | |
171 | if (str[i] == '\\' && str[i+1] == 'x') { | |
172 | i += 2; | |
173 | continue; | |
174 | } | |
175 | ||
176 | /* accept valid utf8 */ | |
177 | len = utf8_encoded_valid_unichar(&str[i]); | |
178 | if (len > 1) { | |
179 | i += len; | |
180 | continue; | |
181 | } | |
182 | ||
183 | /* if space is allowed, replace whitespace with ordinary space */ | |
6e866b33 | 184 | if (isspace(str[i]) && white && strchr(white, ' ')) { |
663996b3 MS |
185 | str[i] = ' '; |
186 | i++; | |
187 | replaced++; | |
188 | continue; | |
189 | } | |
190 | ||
191 | /* everything else is replaced with '_' */ | |
192 | str[i] = '_'; | |
193 | i++; | |
194 | replaced++; | |
195 | } | |
196 | return replaced; | |
197 | } | |
198 | ||
199 | /** | |
200 | * udev_util_encode_string: | |
201 | * @str: input string to be encoded | |
202 | * @str_enc: output string to store the encoded input string | |
203 | * @len: maximum size of the output string, which may be | |
204 | * four times as long as the input string | |
205 | * | |
206 | * Encode all potentially unsafe characters of a string to the | |
207 | * corresponding 2 char hex value prefixed by '\x'. | |
208 | * | |
209 | * Returns: 0 if the entire string was copied, non-zero otherwise. | |
210 | **/ | |
6e866b33 | 211 | _public_ int udev_util_encode_string(const char *str, char *str_enc, size_t len) { |
14228c0d | 212 | return encode_devnode_name(str, str_enc, len); |
663996b3 | 213 | } |