/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include "chase-symlinks.h" #include "devnum-util.h" #include "parse-util.h" #include "path-util.h" #include "string-util.h" int parse_devnum(const char *s, dev_t *ret) { const char *major; unsigned x, y; size_t n; int r; n = strspn(s, DIGITS); if (n == 0) return -EINVAL; if (n > DECIMAL_STR_MAX(dev_t)) return -EINVAL; if (s[n] != ':') return -EINVAL; major = strndupa_safe(s, n); r = safe_atou(major, &x); if (r < 0) return r; r = safe_atou(s + n + 1, &y); if (r < 0) return r; if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y)) return -ERANGE; *ret = makedev(x, y); return 0; } int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret) { const char *t; /* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */ if (S_ISCHR(mode)) t = "char"; else if (S_ISBLK(mode)) t = "block"; else return -ENODEV; if (asprintf(ret, "/dev/%s/" DEVNUM_FORMAT_STR, t, DEVNUM_FORMAT_VAL(devnum)) < 0) return -ENOMEM; return 0; } int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret) { _cleanup_free_ char *p = NULL; int r; /* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */ assert(ret); if (major(devnum) == 0 && minor(devnum) == 0) { char *s; /* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in * /dev/block/ and /dev/char/, hence we handle them specially here. */ if (S_ISCHR(mode)) s = strdup("/run/systemd/inaccessible/chr"); else if (S_ISBLK(mode)) s = strdup("/run/systemd/inaccessible/blk"); else return -ENODEV; if (!s) return -ENOMEM; *ret = s; return 0; } r = device_path_make_major_minor(mode, devnum, &p); if (r < 0) return r; return chase_symlinks(p, NULL, 0, ret, NULL); } int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devnum) { mode_t mode; dev_t devnum; int r; /* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/ * paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device * path cannot be parsed like this. */ if (path_equal(path, "/run/systemd/inaccessible/chr")) { mode = S_IFCHR; devnum = makedev(0, 0); } else if (path_equal(path, "/run/systemd/inaccessible/blk")) { mode = S_IFBLK; devnum = makedev(0, 0); } else { const char *w; w = path_startswith(path, "/dev/block/"); if (w) mode = S_IFBLK; else { w = path_startswith(path, "/dev/char/"); if (!w) return -ENODEV; mode = S_IFCHR; } r = parse_devnum(w, &devnum); if (r < 0) return r; } if (ret_mode) *ret_mode = mode; if (ret_devnum) *ret_devnum = devnum; return 0; }