Merge pull request #23070 from poettering/devnum-split
basic: some devnum handling tweaks and refactorings
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "stdio-util.h"
|
||||
|
||||
int parse_devnum(const char *s, dev_t *ret);
|
||||
|
||||
/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
|
||||
* specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
|
||||
* major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
|
||||
* comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as
|
||||
* such a test would be pointless in such a case.) */
|
||||
|
||||
#define DEVICE_MAJOR_VALID(x) \
|
||||
({ \
|
||||
typeof(x) _x = (x), _y = 0; \
|
||||
_x >= _y && _x < (UINT32_C(1) << 12); \
|
||||
\
|
||||
})
|
||||
|
||||
#define DEVICE_MINOR_VALID(x) \
|
||||
({ \
|
||||
typeof(x) _x = (x), _y = 0; \
|
||||
_x >= _y && _x < (UINT32_C(1) << 20); \
|
||||
})
|
||||
|
||||
int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret);
|
||||
int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret);
|
||||
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devnum);
|
||||
|
||||
static inline bool devnum_set_and_equal(dev_t a, dev_t b) {
|
||||
/* Returns true if a and b definitely refer to the same device. If either is zero, this means "don't
|
||||
* know" and we'll return false */
|
||||
return a == b && a != 0;
|
||||
}
|
||||
|
||||
/* Maximum string length for a major:minor string. (Note that DECIMAL_STR_MAX includes space for a trailing NUL) */
|
||||
#define DEVNUM_STR_MAX (DECIMAL_STR_MAX(dev_t)-1+1+DECIMAL_STR_MAX(dev_t))
|
||||
|
||||
#define DEVNUM_FORMAT_STR "%u:%u"
|
||||
#define DEVNUM_FORMAT_VAL(d) major(d), minor(d)
|
||||
|
||||
static inline char *format_devnum(dev_t d, char buf[static DEVNUM_STR_MAX]) {
|
||||
return ASSERT_PTR(snprintf_ok(buf, DEVNUM_STR_MAX, DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(d)));
|
||||
}
|
||||
|
||||
#define FORMAT_DEVNUM(d) format_devnum((d), (char[DEVNUM_STR_MAX]) {})
|
||||
@@ -32,6 +32,8 @@ basic_sources = files(
|
||||
'conf-files.c',
|
||||
'conf-files.h',
|
||||
'def.h',
|
||||
'devnum-util.c',
|
||||
'devnum-util.h',
|
||||
'dirent-util.c',
|
||||
'dirent-util.h',
|
||||
'dns-def.h',
|
||||
|
||||
@@ -692,34 +692,6 @@ int parse_ip_prefix_length(const char *s, int *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_dev(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 (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 parse_oom_score_adjust(const char *s, int *ret) {
|
||||
int r, v;
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
typedef unsigned long loadavg_t;
|
||||
|
||||
int parse_boolean(const char *v) _pure_;
|
||||
int parse_dev(const char *s, dev_t *ret);
|
||||
int parse_pid(const char *s, pid_t* ret_pid);
|
||||
int parse_mode(const char *s, mode_t *ret);
|
||||
int parse_ifindex(const char *s);
|
||||
|
||||
@@ -314,101 +314,6 @@ int fd_verify_directory(int fd) {
|
||||
return stat_verify_directory(&st);
|
||||
}
|
||||
|
||||
int device_path_make_major_minor(mode_t mode, dev_t devno, 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/%u:%u", t, major(devno), minor(devno)) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_path_make_canonical(mode_t mode, dev_t devno, 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(devno) == 0 && minor(devno) == 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, devno, &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_devno) {
|
||||
mode_t mode;
|
||||
dev_t devno;
|
||||
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;
|
||||
devno = makedev(0, 0);
|
||||
} else if (path_equal(path, "/run/systemd/inaccessible/blk")) {
|
||||
mode = S_IFBLK;
|
||||
devno = 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_dev(w, &devno);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (ret_mode)
|
||||
*ret_mode = mode;
|
||||
if (ret_devno)
|
||||
*ret_devno = devno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proc_mounted(void) {
|
||||
int r;
|
||||
|
||||
|
||||
@@ -71,29 +71,6 @@ int fd_verify_regular(int fd);
|
||||
int stat_verify_directory(const struct stat *st);
|
||||
int fd_verify_directory(int fd);
|
||||
|
||||
/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
|
||||
* specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
|
||||
* major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
|
||||
* comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as
|
||||
* such a test would be pointless in such a case.) */
|
||||
|
||||
#define DEVICE_MAJOR_VALID(x) \
|
||||
({ \
|
||||
typeof(x) _x = (x), _y = 0; \
|
||||
_x >= _y && _x < (UINT32_C(1) << 12); \
|
||||
\
|
||||
})
|
||||
|
||||
#define DEVICE_MINOR_VALID(x) \
|
||||
({ \
|
||||
typeof(x) _x = (x), _y = 0; \
|
||||
_x >= _y && _x < (UINT32_C(1) << 20); \
|
||||
})
|
||||
|
||||
int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret);
|
||||
int device_path_make_canonical(mode_t mode, dev_t devno, char **ret);
|
||||
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno);
|
||||
|
||||
int proc_mounted(void);
|
||||
|
||||
bool stat_inode_same(const struct stat *a, const struct stat *b);
|
||||
@@ -119,9 +96,3 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s
|
||||
struct new_statx nsx; \
|
||||
} var
|
||||
#endif
|
||||
|
||||
static inline bool devid_set_and_equal(dev_t a, dev_t b) {
|
||||
/* Returns true if a and b definitely refer to the same device. If either is zero, this means "don't
|
||||
* know" and we'll return false */
|
||||
return a == b && a != 0;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "def.h"
|
||||
#include "devnum-util.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
|
||||
Reference in New Issue
Block a user