mirror of
https://github.com/morgan9e/systemd
synced 2026-04-15 00:47:10 +09:00
139 lines
3.8 KiB
C
139 lines
3.8 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "chase.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_inaccessible(mode_t mode, char **ret) {
|
|
char *s;
|
|
|
|
assert(ret);
|
|
|
|
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;
|
|
}
|
|
|
|
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 (devnum_is_zero(devnum))
|
|
/* 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. */
|
|
return device_path_make_inaccessible(mode, ret);
|
|
|
|
r = device_path_make_major_minor(mode, devnum, &p);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return chase(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;
|
|
}
|