Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <errno.h>
4 : : #include <stddef.h>
5 : : #include <stdint.h>
6 : : #include <stdlib.h>
7 : : #include <string.h>
8 : :
9 : : #include "alloc-util.h"
10 : : #include "glob-util.h"
11 : : #include "hexdecoct.h"
12 : : #include "path-util.h"
13 : : #include "special.h"
14 : : #include "string-util.h"
15 : : #include "strv.h"
16 : : #include "unit-name.h"
17 : :
18 : : /* Characters valid in a unit name. */
19 : : #define VALID_CHARS \
20 : : DIGITS \
21 : : LETTERS \
22 : : ":-_.\\"
23 : :
24 : : /* The same, but also permits the single @ character that may appear */
25 : : #define VALID_CHARS_WITH_AT \
26 : : "@" \
27 : : VALID_CHARS
28 : :
29 : : /* All chars valid in a unit name glob */
30 : : #define VALID_CHARS_GLOB \
31 : : VALID_CHARS_WITH_AT \
32 : : "[]!-*?"
33 : :
34 : 1123120 : bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
35 : : const char *e, *i, *at;
36 : :
37 [ - + ]: 1123120 : assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
38 : :
39 [ - + ]: 1123120 : if (_unlikely_(flags == 0))
40 : 0 : return false;
41 : :
42 [ + + ]: 1123120 : if (isempty(n))
43 : 20 : return false;
44 : :
45 [ - + ]: 1123100 : if (strlen(n) >= UNIT_NAME_MAX)
46 : 0 : return false;
47 : :
48 : 1123100 : e = strrchr(n, '.');
49 [ + + + + ]: 1123100 : if (!e || e == n)
50 : 1584 : return false;
51 : :
52 [ + + ]: 1121516 : if (unit_type_from_string(e + 1) < 0)
53 : 892 : return false;
54 : :
55 [ + + ]: 19126392 : for (i = n, at = NULL; i < e; i++) {
56 : :
57 [ + + + + ]: 18005796 : if (*i == '@' && !at)
58 : 90316 : at = i;
59 : :
60 [ + + ]: 18005796 : if (!strchr("@" VALID_CHARS, *i))
61 : 28 : return false;
62 : : }
63 : :
64 [ + + ]: 1120596 : if (at == n)
65 : 20 : return false;
66 : :
67 [ + + ]: 1120576 : if (flags & UNIT_NAME_PLAIN)
68 [ + + ]: 936476 : if (!at)
69 : 859708 : return true;
70 : :
71 [ + + ]: 260868 : if (flags & UNIT_NAME_INSTANCE)
72 [ + + + + ]: 228628 : if (at && e > at + 1)
73 : 20716 : return true;
74 : :
75 [ + + ]: 240152 : if (flags & UNIT_NAME_TEMPLATE)
76 [ + + + + ]: 134208 : if (at && e == at + 1)
77 : 68552 : return true;
78 : :
79 : 171600 : return false;
80 : : }
81 : :
82 : 148512 : bool unit_prefix_is_valid(const char *p) {
83 : :
84 : : /* We don't allow additional @ in the prefix string */
85 : :
86 [ + + ]: 148512 : if (isempty(p))
87 : 4 : return false;
88 : :
89 : 148508 : return in_charset(p, VALID_CHARS);
90 : : }
91 : :
92 : 320 : bool unit_instance_is_valid(const char *i) {
93 : :
94 : : /* The max length depends on the length of the string, so we
95 : : * don't really check this here. */
96 : :
97 [ + + ]: 320 : if (isempty(i))
98 : 4 : return false;
99 : :
100 : : /* We allow additional @ in the instance string, we do not
101 : : * allow them in the prefix! */
102 : :
103 : 316 : return in_charset(i, "@" VALID_CHARS);
104 : : }
105 : :
106 : 12096 : bool unit_suffix_is_valid(const char *s) {
107 [ - + ]: 12096 : if (isempty(s))
108 : 0 : return false;
109 : :
110 [ - + ]: 12096 : if (s[0] != '.')
111 : 0 : return false;
112 : :
113 [ + + ]: 12096 : if (unit_type_from_string(s + 1) < 0)
114 : 4 : return false;
115 : :
116 : 12092 : return true;
117 : : }
118 : :
119 : 100680 : int unit_name_to_prefix(const char *n, char **ret) {
120 : : const char *p;
121 : : char *s;
122 : :
123 [ - + ]: 100680 : assert(n);
124 [ - + ]: 100680 : assert(ret);
125 : :
126 [ + + ]: 100680 : if (!unit_name_is_valid(n, UNIT_NAME_ANY))
127 : 28 : return -EINVAL;
128 : :
129 : 100652 : p = strchr(n, '@');
130 [ + + ]: 100652 : if (!p)
131 : 100620 : p = strrchr(n, '.');
132 : :
133 [ - + ]: 100652 : assert_se(p);
134 : :
135 : 100652 : s = strndup(n, p - n);
136 [ - + ]: 100652 : if (!s)
137 : 0 : return -ENOMEM;
138 : :
139 : 100652 : *ret = s;
140 : 100652 : return 0;
141 : : }
142 : :
143 : 27080 : int unit_name_to_instance(const char *n, char **ret) {
144 : : const char *p, *d;
145 : :
146 [ - + ]: 27080 : assert(n);
147 : :
148 [ + + ]: 27080 : if (!unit_name_is_valid(n, UNIT_NAME_ANY))
149 : 12 : return -EINVAL;
150 : :
151 : : /* Everything past the first @ and before the last . is the instance */
152 : 27068 : p = strchr(n, '@');
153 [ + + ]: 27068 : if (!p) {
154 [ + - ]: 26876 : if (ret)
155 : 26876 : *ret = NULL;
156 : 26876 : return UNIT_NAME_PLAIN;
157 : : }
158 : :
159 : 192 : p++;
160 : :
161 : 192 : d = strrchr(p, '.');
162 [ - + ]: 192 : if (!d)
163 : 0 : return -EINVAL;
164 : :
165 [ + - ]: 192 : if (ret) {
166 : 192 : char *i = strndup(p, d-p);
167 [ - + ]: 192 : if (!i)
168 : 0 : return -ENOMEM;
169 : :
170 : 192 : *ret = i;
171 : : }
172 [ + + ]: 192 : return d > p ? UNIT_NAME_INSTANCE : UNIT_NAME_TEMPLATE;
173 : : }
174 : :
175 : 20 : int unit_name_to_prefix_and_instance(const char *n, char **ret) {
176 : : const char *d;
177 : : char *s;
178 : :
179 [ - + ]: 20 : assert(n);
180 [ - + ]: 20 : assert(ret);
181 : :
182 [ - + ]: 20 : if (!unit_name_is_valid(n, UNIT_NAME_ANY))
183 : 0 : return -EINVAL;
184 : :
185 : 20 : d = strrchr(n, '.');
186 [ - + ]: 20 : if (!d)
187 : 0 : return -EINVAL;
188 : :
189 : 20 : s = strndup(n, d - n);
190 [ - + ]: 20 : if (!s)
191 : 0 : return -ENOMEM;
192 : :
193 : 20 : *ret = s;
194 : 20 : return 0;
195 : : }
196 : :
197 : 134784 : UnitType unit_name_to_type(const char *n) {
198 : : const char *e;
199 : :
200 [ - + ]: 134784 : assert(n);
201 : :
202 [ + + ]: 134784 : if (!unit_name_is_valid(n, UNIT_NAME_ANY))
203 : 376 : return _UNIT_TYPE_INVALID;
204 : :
205 [ - + ]: 134408 : assert_se(e = strrchr(n, '.'));
206 : :
207 : 134408 : return unit_type_from_string(e + 1);
208 : : }
209 : :
210 : 32 : int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
211 : : char *e, *s;
212 : : size_t a, b;
213 : :
214 [ - + ]: 32 : assert(n);
215 [ - + ]: 32 : assert(suffix);
216 [ - + ]: 32 : assert(ret);
217 : :
218 [ - + ]: 32 : if (!unit_name_is_valid(n, UNIT_NAME_ANY))
219 : 0 : return -EINVAL;
220 : :
221 [ - + ]: 32 : if (!unit_suffix_is_valid(suffix))
222 : 0 : return -EINVAL;
223 : :
224 [ - + ]: 32 : assert_se(e = strrchr(n, '.'));
225 : :
226 : 32 : a = e - n;
227 : 32 : b = strlen(suffix);
228 : :
229 : 32 : s = new(char, a + b + 1);
230 [ - + ]: 32 : if (!s)
231 : 0 : return -ENOMEM;
232 : :
233 : 32 : strcpy(mempcpy(s, n, a), suffix);
234 : 32 : *ret = s;
235 : :
236 : 32 : return 0;
237 : : }
238 : :
239 : 24 : int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
240 : : UnitType type;
241 : :
242 [ - + ]: 24 : assert(prefix);
243 [ - + ]: 24 : assert(suffix);
244 [ - + ]: 24 : assert(ret);
245 : :
246 [ - + ]: 24 : if (suffix[0] != '.')
247 : 0 : return -EINVAL;
248 : :
249 : 24 : type = unit_type_from_string(suffix + 1);
250 [ - + ]: 24 : if (type < 0)
251 : 0 : return -EINVAL;
252 : :
253 : 24 : return unit_name_build_from_type(prefix, instance, type, ret);
254 : : }
255 : :
256 : 74224 : int unit_name_build_from_type(const char *prefix, const char *instance, UnitType type, char **ret) {
257 : : const char *ut;
258 : : char *s;
259 : :
260 [ - + ]: 74224 : assert(prefix);
261 [ - + ]: 74224 : assert(type >= 0);
262 [ - + ]: 74224 : assert(type < _UNIT_TYPE_MAX);
263 [ - + ]: 74224 : assert(ret);
264 : :
265 [ - + ]: 74224 : if (!unit_prefix_is_valid(prefix))
266 : 0 : return -EINVAL;
267 : :
268 [ + + - + ]: 74224 : if (instance && !unit_instance_is_valid(instance))
269 : 0 : return -EINVAL;
270 : :
271 : 74224 : ut = unit_type_to_string(type);
272 : :
273 [ + + ]: 74224 : if (!instance)
274 : 74204 : s = strjoin(prefix, ".", ut);
275 : : else
276 : 20 : s = strjoin(prefix, "@", instance, ".", ut);
277 [ - + ]: 74224 : if (!s)
278 : 0 : return -ENOMEM;
279 : :
280 : 74224 : *ret = s;
281 : 74224 : return 0;
282 : : }
283 : :
284 : 12196 : static char *do_escape_char(char c, char *t) {
285 [ - + ]: 12196 : assert(t);
286 : :
287 : 12196 : *(t++) = '\\';
288 : 12196 : *(t++) = 'x';
289 : 12196 : *(t++) = hexchar(c >> 4);
290 : 12196 : *(t++) = hexchar(c);
291 : :
292 : 12196 : return t;
293 : : }
294 : :
295 : 10476 : static char *do_escape(const char *f, char *t) {
296 [ - + ]: 10476 : assert(f);
297 [ - + ]: 10476 : assert(t);
298 : :
299 : : /* do not create units with a leading '.', like for "/.dotdir" mount points */
300 [ - + ]: 10476 : if (*f == '.') {
301 : 0 : t = do_escape_char(*f, t);
302 : 0 : f++;
303 : : }
304 : :
305 [ + + ]: 292864 : for (; *f; f++) {
306 [ + + ]: 282388 : if (*f == '/')
307 : 25480 : *(t++) = '-';
308 [ + + + + : 256908 : else if (IN_SET(*f, '-', '\\') || !strchr(VALID_CHARS, *f))
+ + ]
309 : 12108 : t = do_escape_char(*f, t);
310 : : else
311 : 244800 : *(t++) = *f;
312 : : }
313 : :
314 : 10476 : return t;
315 : : }
316 : :
317 : 10476 : char *unit_name_escape(const char *f) {
318 : : char *r, *t;
319 : :
320 [ - + ]: 10476 : assert(f);
321 : :
322 : 10476 : r = new(char, strlen(f)*4+1);
323 [ - + ]: 10476 : if (!r)
324 : 0 : return NULL;
325 : :
326 : 10476 : t = do_escape(f, r);
327 : 10476 : *t = 0;
328 : :
329 : 10476 : return r;
330 : : }
331 : :
332 : 136 : int unit_name_unescape(const char *f, char **ret) {
333 : 136 : _cleanup_free_ char *r = NULL;
334 : : char *t;
335 : :
336 [ - + ]: 136 : assert(f);
337 : :
338 : 136 : r = strdup(f);
339 [ - + ]: 136 : if (!r)
340 : 0 : return -ENOMEM;
341 : :
342 [ + + ]: 1100 : for (t = r; *f; f++) {
343 [ + + ]: 964 : if (*f == '-')
344 : 124 : *(t++) = '/';
345 [ + + ]: 840 : else if (*f == '\\') {
346 : : int a, b;
347 : :
348 [ - + ]: 20 : if (f[1] != 'x')
349 : 0 : return -EINVAL;
350 : :
351 : 20 : a = unhexchar(f[2]);
352 [ - + ]: 20 : if (a < 0)
353 : 0 : return -EINVAL;
354 : :
355 : 20 : b = unhexchar(f[3]);
356 [ - + ]: 20 : if (b < 0)
357 : 0 : return -EINVAL;
358 : :
359 : 20 : *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
360 : 20 : f += 3;
361 : : } else
362 : 820 : *(t++) = *f;
363 : : }
364 : :
365 : 136 : *t = 0;
366 : :
367 : 136 : *ret = TAKE_PTR(r);
368 : :
369 : 136 : return 0;
370 : : }
371 : :
372 : 11592 : int unit_name_path_escape(const char *f, char **ret) {
373 : : char *p, *s;
374 : :
375 [ - + ]: 11592 : assert(f);
376 [ - + ]: 11592 : assert(ret);
377 : :
378 : 11592 : p = strdupa(f);
379 [ - + ]: 11592 : if (!p)
380 : 0 : return -ENOMEM;
381 : :
382 : 11592 : path_simplify(p, false);
383 : :
384 [ + + ]: 11592 : if (empty_or_root(p))
385 : 1108 : s = strdup("-");
386 : : else {
387 [ + + ]: 10484 : if (!path_is_normalized(p))
388 : 12 : return -EINVAL;
389 : :
390 : : /* Truncate trailing slashes */
391 : 10472 : delete_trailing_chars(p, "/");
392 : :
393 : : /* Truncate leading slashes */
394 : 10472 : p = skip_leading_chars(p, "/");
395 : :
396 : 10472 : s = unit_name_escape(p);
397 : : }
398 [ - + ]: 11580 : if (!s)
399 : 0 : return -ENOMEM;
400 : :
401 : 11580 : *ret = s;
402 : 11580 : return 0;
403 : : }
404 : :
405 : 132 : int unit_name_path_unescape(const char *f, char **ret) {
406 : 132 : _cleanup_free_ char *s = NULL;
407 : : int r;
408 : :
409 [ - + ]: 132 : assert(f);
410 : :
411 [ + + ]: 132 : if (isempty(f))
412 : 4 : return -EINVAL;
413 : :
414 [ + + ]: 128 : if (streq(f, "-")) {
415 : 28 : s = strdup("/");
416 [ - + ]: 28 : if (!s)
417 : 0 : return -ENOMEM;
418 : : } else {
419 [ + + ]: 100 : _cleanup_free_ char *w = NULL;
420 : :
421 : 100 : r = unit_name_unescape(f, &w);
422 [ - + ]: 100 : if (r < 0)
423 : 0 : return r;
424 : :
425 : : /* Don't accept trailing or leading slashes */
426 [ + + + + ]: 100 : if (startswith(w, "/") || endswith(w, "/"))
427 : 24 : return -EINVAL;
428 : :
429 : : /* Prefix a slash again */
430 : 76 : s = strjoin("/", w);
431 [ - + ]: 76 : if (!s)
432 : 0 : return -ENOMEM;
433 : :
434 [ + + ]: 76 : if (!path_is_normalized(s))
435 : 16 : return -EINVAL;
436 : : }
437 : :
438 [ + - ]: 88 : if (ret)
439 : 88 : *ret = TAKE_PTR(s);
440 : :
441 : 88 : return 0;
442 : : }
443 : :
444 : 192 : int unit_name_replace_instance(const char *f, const char *i, char **ret) {
445 : : const char *p, *e;
446 : : char *s;
447 : : size_t a, b;
448 : :
449 [ - + ]: 192 : assert(f);
450 [ - + ]: 192 : assert(i);
451 [ - + ]: 192 : assert(ret);
452 : :
453 [ + + ]: 192 : if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
454 : 24 : return -EINVAL;
455 [ - + ]: 168 : if (!unit_instance_is_valid(i))
456 : 0 : return -EINVAL;
457 : :
458 [ - + ]: 168 : assert_se(p = strchr(f, '@'));
459 [ - + ]: 168 : assert_se(e = strrchr(f, '.'));
460 : :
461 : 168 : a = p - f;
462 : 168 : b = strlen(i);
463 : :
464 : 168 : s = new(char, a + 1 + b + strlen(e) + 1);
465 [ - + ]: 168 : if (!s)
466 : 0 : return -ENOMEM;
467 : :
468 : 168 : strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
469 : :
470 : 168 : *ret = s;
471 : 168 : return 0;
472 : : }
473 : :
474 : 384 : int unit_name_template(const char *f, char **ret) {
475 : : const char *p, *e;
476 : : char *s;
477 : : size_t a;
478 : :
479 [ - + ]: 384 : assert(f);
480 [ - + ]: 384 : assert(ret);
481 : :
482 [ + + ]: 384 : if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
483 : 48 : return -EINVAL;
484 : :
485 [ - + ]: 336 : assert_se(p = strchr(f, '@'));
486 [ - + ]: 336 : assert_se(e = strrchr(f, '.'));
487 : :
488 : 336 : a = p - f;
489 : :
490 : 336 : s = new(char, a + 1 + strlen(e) + 1);
491 [ - + ]: 336 : if (!s)
492 : 0 : return -ENOMEM;
493 : :
494 : 336 : strcpy(mempcpy(s, f, a + 1), e);
495 : :
496 : 336 : *ret = s;
497 : 336 : return 0;
498 : : }
499 : :
500 : 11564 : int unit_name_from_path(const char *path, const char *suffix, char **ret) {
501 : 11564 : _cleanup_free_ char *p = NULL;
502 : 11564 : char *s = NULL;
503 : : int r;
504 : :
505 [ - + ]: 11564 : assert(path);
506 [ - + ]: 11564 : assert(suffix);
507 [ - + ]: 11564 : assert(ret);
508 : :
509 [ - + ]: 11564 : if (!unit_suffix_is_valid(suffix))
510 : 0 : return -EINVAL;
511 : :
512 : 11564 : r = unit_name_path_escape(path, &p);
513 [ + + ]: 11564 : if (r < 0)
514 : 8 : return r;
515 : :
516 : 11556 : s = strjoin(p, suffix);
517 [ - + ]: 11556 : if (!s)
518 : 0 : return -ENOMEM;
519 : :
520 : 11556 : *ret = s;
521 : 11556 : return 0;
522 : : }
523 : :
524 : 32 : int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
525 : 32 : _cleanup_free_ char *p = NULL;
526 : : char *s;
527 : : int r;
528 : :
529 [ - + ]: 32 : assert(prefix);
530 [ - + ]: 32 : assert(path);
531 [ - + ]: 32 : assert(suffix);
532 [ - + ]: 32 : assert(ret);
533 : :
534 [ - + ]: 32 : if (!unit_prefix_is_valid(prefix))
535 : 0 : return -EINVAL;
536 : :
537 [ + + ]: 32 : if (!unit_suffix_is_valid(suffix))
538 : 4 : return -EINVAL;
539 : :
540 : 28 : r = unit_name_path_escape(path, &p);
541 [ + + ]: 28 : if (r < 0)
542 : 4 : return r;
543 : :
544 : 24 : s = strjoin(prefix, "@", p, suffix);
545 [ - + ]: 24 : if (!s)
546 : 0 : return -ENOMEM;
547 : :
548 : 24 : *ret = s;
549 : 24 : return 0;
550 : : }
551 : :
552 : 64 : int unit_name_to_path(const char *name, char **ret) {
553 : 64 : _cleanup_free_ char *prefix = NULL;
554 : : int r;
555 : :
556 [ - + ]: 64 : assert(name);
557 : :
558 : 64 : r = unit_name_to_prefix(name, &prefix);
559 [ + + ]: 64 : if (r < 0)
560 : 8 : return r;
561 : :
562 : 56 : return unit_name_path_unescape(prefix, ret);
563 : : }
564 : :
565 : 396 : static bool do_escape_mangle(const char *f, bool allow_globs, char *t) {
566 : : const char *valid_chars;
567 : 396 : bool mangled = false;
568 : :
569 [ - + ]: 396 : assert(f);
570 [ - + ]: 396 : assert(t);
571 : :
572 : : /* We'll only escape the obvious characters here, to play safe.
573 : : *
574 : : * Returns true if any characters were mangled, false otherwise.
575 : : */
576 : :
577 [ + + ]: 396 : valid_chars = allow_globs ? VALID_CHARS_GLOB : VALID_CHARS_WITH_AT;
578 : :
579 [ + + ]: 2232 : for (; *f; f++)
580 [ + + ]: 1836 : if (*f == '/') {
581 : 20 : *(t++) = '-';
582 : 20 : mangled = true;
583 [ + + ]: 1816 : } else if (!strchr(valid_chars, *f)) {
584 : 88 : t = do_escape_char(*f, t);
585 : 88 : mangled = true;
586 : : } else
587 : 1728 : *(t++) = *f;
588 : 396 : *t = 0;
589 : :
590 : 396 : return mangled;
591 : : }
592 : :
593 : : /**
594 : : * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
595 : : * /blah/blah is converted to blah-blah.mount, anything else is left alone,
596 : : * except that @suffix is appended if a valid unit suffix is not present.
597 : : *
598 : : * If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
599 : : */
600 : 472 : int unit_name_mangle_with_suffix(const char *name, UnitNameMangle flags, const char *suffix, char **ret) {
601 : : char *s;
602 : : int r;
603 : : bool mangled;
604 : :
605 [ - + ]: 472 : assert(name);
606 [ - + ]: 472 : assert(suffix);
607 [ - + ]: 472 : assert(ret);
608 : :
609 [ + + ]: 472 : if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
610 : 4 : return -EINVAL;
611 : :
612 [ - + ]: 468 : if (!unit_suffix_is_valid(suffix))
613 : 0 : return -EINVAL;
614 : :
615 : : /* Already a fully valid unit name? If so, no mangling is necessary... */
616 [ + + ]: 468 : if (unit_name_is_valid(name, UNIT_NAME_ANY))
617 : 52 : goto good;
618 : :
619 : : /* Already a fully valid globbing expression? If so, no mangling is necessary either... */
620 [ + + ]: 416 : if ((flags & UNIT_NAME_MANGLE_GLOB) &&
621 [ + + ]: 20 : string_is_glob(name) &&
622 [ + + ]: 16 : in_charset(name, VALID_CHARS_GLOB))
623 : 12 : goto good;
624 : :
625 [ + + ]: 404 : if (is_device_path(name)) {
626 : 4 : r = unit_name_from_path(name, ".device", ret);
627 [ + - ]: 4 : if (r >= 0)
628 : 4 : return 1;
629 [ # # ]: 0 : if (r != -EINVAL)
630 : 0 : return r;
631 : : }
632 : :
633 [ + + ]: 400 : if (path_is_absolute(name)) {
634 : 4 : r = unit_name_from_path(name, ".mount", ret);
635 [ + - ]: 4 : if (r >= 0)
636 : 4 : return 1;
637 [ # # ]: 0 : if (r != -EINVAL)
638 : 0 : return r;
639 : : }
640 : :
641 : 396 : s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
642 [ - + ]: 396 : if (!s)
643 : 0 : return -ENOMEM;
644 : :
645 : 396 : mangled = do_escape_mangle(name, flags & UNIT_NAME_MANGLE_GLOB, s);
646 [ + + ]: 396 : if (mangled)
647 [ + + + - ]: 28 : log_full(flags & UNIT_NAME_MANGLE_WARN ? LOG_NOTICE : LOG_DEBUG,
648 : : "Invalid unit name \"%s\" was escaped as \"%s\" (maybe you should use systemd-escape?)",
649 : : name, s);
650 : :
651 : : /* Append a suffix if it doesn't have any, but only if this is not a glob, so that we can allow "foo.*" as a
652 : : * valid glob. */
653 [ + + + + : 396 : if ((!(flags & UNIT_NAME_MANGLE_GLOB) || !string_is_glob(s)) && unit_name_to_type(s) < 0)
+ + ]
654 : 376 : strcat(s, suffix);
655 : :
656 : 396 : *ret = s;
657 : 396 : return 1;
658 : :
659 : 64 : good:
660 : 64 : s = strdup(name);
661 [ - + ]: 64 : if (!s)
662 : 0 : return -ENOMEM;
663 : :
664 : 64 : *ret = s;
665 : 64 : return 0;
666 : : }
667 : :
668 : 208 : int slice_build_parent_slice(const char *slice, char **ret) {
669 : : char *s, *dash;
670 : : int r;
671 : :
672 [ - + ]: 208 : assert(slice);
673 [ - + ]: 208 : assert(ret);
674 : :
675 [ + + ]: 208 : if (!slice_name_is_valid(slice))
676 : 48 : return -EINVAL;
677 : :
678 [ + + ]: 160 : if (streq(slice, SPECIAL_ROOT_SLICE)) {
679 : 92 : *ret = NULL;
680 : 92 : return 0;
681 : : }
682 : :
683 : 68 : s = strdup(slice);
684 [ - + ]: 68 : if (!s)
685 : 0 : return -ENOMEM;
686 : :
687 : 68 : dash = strrchr(s, '-');
688 [ + + ]: 68 : if (dash)
689 : 40 : strcpy(dash, ".slice");
690 : : else {
691 : 28 : r = free_and_strdup(&s, SPECIAL_ROOT_SLICE);
692 [ - + ]: 28 : if (r < 0) {
693 : 0 : free(s);
694 : 0 : return r;
695 : : }
696 : : }
697 : :
698 : 68 : *ret = s;
699 : 68 : return 1;
700 : : }
701 : :
702 : 24 : int slice_build_subslice(const char *slice, const char *name, char **ret) {
703 : : char *subslice;
704 : :
705 [ - + ]: 24 : assert(slice);
706 [ - + ]: 24 : assert(name);
707 [ - + ]: 24 : assert(ret);
708 : :
709 [ + + ]: 24 : if (!slice_name_is_valid(slice))
710 : 8 : return -EINVAL;
711 : :
712 [ - + ]: 16 : if (!unit_prefix_is_valid(name))
713 : 0 : return -EINVAL;
714 : :
715 [ + + ]: 16 : if (streq(slice, SPECIAL_ROOT_SLICE))
716 : 4 : subslice = strjoin(name, ".slice");
717 : : else {
718 : : char *e;
719 : :
720 [ - + ]: 12 : assert_se(e = endswith(slice, ".slice"));
721 : :
722 : 12 : subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
723 [ - + ]: 12 : if (!subslice)
724 : 0 : return -ENOMEM;
725 : :
726 : 12 : stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
727 : : }
728 : :
729 : 16 : *ret = subslice;
730 : 16 : return 0;
731 : : }
732 : :
733 : 404 : bool slice_name_is_valid(const char *name) {
734 : : const char *p, *e;
735 : 404 : bool dash = false;
736 : :
737 [ + + ]: 404 : if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
738 : 96 : return false;
739 : :
740 [ + + ]: 308 : if (streq(name, SPECIAL_ROOT_SLICE))
741 : 144 : return true;
742 : :
743 : 164 : e = endswith(name, ".slice");
744 [ + + ]: 164 : if (!e)
745 : 12 : return false;
746 : :
747 [ + + ]: 1332 : for (p = name; p < e; p++) {
748 : :
749 [ + + ]: 1204 : if (*p == '-') {
750 : :
751 : : /* Don't allow initial dash */
752 [ + + ]: 148 : if (p == name)
753 : 12 : return false;
754 : :
755 : : /* Don't allow multiple dashes */
756 [ + + ]: 136 : if (dash)
757 : 12 : return false;
758 : :
759 : 124 : dash = true;
760 : : } else
761 : 1056 : dash = false;
762 : : }
763 : :
764 : : /* Don't allow trailing hash */
765 [ + + ]: 128 : if (dash)
766 : 8 : return false;
767 : :
768 : 120 : return true;
769 : : }
|