mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 16:37:19 +09:00
This part of the copyright blurb stems from the GPL use recommendations: https://www.gnu.org/licenses/gpl-howto.en.html The concept appears to originate in times where version control was per file, instead of per tree, and was a way to glue the files together. Ultimately, we nowadays don't live in that world anymore, and this information is entirely useless anyway, as people are very welcome to copy these files into any projects they like, and they shouldn't have to change bits that are part of our copyright header for that. hence, let's just get rid of this old cruft, and shorten our codebase a bit.
183 lines
5.4 KiB
C
183 lines
5.4 KiB
C
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
/***
|
|
Copyright 2010 Lennart Poettering
|
|
***/
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/statfs.h>
|
|
|
|
#include "alloc-util.h"
|
|
#include "blockdev-util.h"
|
|
#include "btrfs-util.h"
|
|
#include "dirent-util.h"
|
|
#include "fd-util.h"
|
|
#include "fileio.h"
|
|
#include "missing.h"
|
|
#include "stat-util.h"
|
|
|
|
int block_get_whole_disk(dev_t d, dev_t *ret) {
|
|
char p[SYS_BLOCK_PATH_MAX("/partition")];
|
|
_cleanup_free_ char *s = NULL;
|
|
unsigned n, m;
|
|
int r;
|
|
|
|
assert(ret);
|
|
|
|
/* If it has a queue this is good enough for us */
|
|
xsprintf_sys_block_path(p, "/queue", d);
|
|
if (access(p, F_OK) >= 0) {
|
|
*ret = d;
|
|
return 0;
|
|
}
|
|
|
|
/* If it is a partition find the originating device */
|
|
xsprintf_sys_block_path(p, "/partition", d);
|
|
if (access(p, F_OK) < 0)
|
|
return -ENOENT;
|
|
|
|
/* Get parent dev_t */
|
|
xsprintf_sys_block_path(p, "/../dev", d);
|
|
r = read_one_line_file(p, &s);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sscanf(s, "%u:%u", &m, &n);
|
|
if (r != 2)
|
|
return -EINVAL;
|
|
|
|
/* Only return this if it is really good enough for us. */
|
|
xsprintf_sys_block_path(p, "/queue", makedev(m, n));
|
|
if (access(p, F_OK) < 0)
|
|
return -ENOENT;
|
|
|
|
*ret = makedev(m, n);
|
|
return 0;
|
|
}
|
|
|
|
int get_block_device(const char *path, dev_t *dev) {
|
|
struct stat st;
|
|
struct statfs sfs;
|
|
|
|
assert(path);
|
|
assert(dev);
|
|
|
|
/* Get's the block device directly backing a file system. If
|
|
* the block device is encrypted, returns the device mapper
|
|
* block device. */
|
|
|
|
if (lstat(path, &st))
|
|
return -errno;
|
|
|
|
if (major(st.st_dev) != 0) {
|
|
*dev = st.st_dev;
|
|
return 1;
|
|
}
|
|
|
|
if (statfs(path, &sfs) < 0)
|
|
return -errno;
|
|
|
|
if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
|
|
return btrfs_get_block_device(path, dev);
|
|
|
|
*dev = 0;
|
|
return 0;
|
|
}
|
|
|
|
int block_get_originating(dev_t dt, dev_t *ret) {
|
|
_cleanup_closedir_ DIR *d = NULL;
|
|
_cleanup_free_ char *t = NULL;
|
|
char p[SYS_BLOCK_PATH_MAX("/slaves")];
|
|
struct dirent *de, *found = NULL;
|
|
unsigned maj, min;
|
|
const char *q;
|
|
int r;
|
|
|
|
/* For the specified block device tries to chase it through the layers, in case LUKS-style DM stacking is used,
|
|
* trying to find the next underlying layer. */
|
|
|
|
xsprintf_sys_block_path(p, "/slaves", dt);
|
|
d = opendir(p);
|
|
if (!d)
|
|
return -errno;
|
|
|
|
FOREACH_DIRENT_ALL(de, d, return -errno) {
|
|
|
|
if (dot_or_dot_dot(de->d_name))
|
|
continue;
|
|
|
|
if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
|
|
continue;
|
|
|
|
if (found) {
|
|
_cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL;
|
|
|
|
/* We found a device backed by multiple other devices. We don't really support automatic
|
|
* discovery on such setups, with the exception of dm-verity partitions. In this case there are
|
|
* two backing devices: the data partition and the hash partition. We are fine with such
|
|
* setups, however, only if both partitions are on the same physical device. Hence, let's
|
|
* verify this. */
|
|
|
|
u = strjoin(p, "/", de->d_name, "/../dev");
|
|
if (!u)
|
|
return -ENOMEM;
|
|
|
|
v = strjoin(p, "/", found->d_name, "/../dev");
|
|
if (!v)
|
|
return -ENOMEM;
|
|
|
|
r = read_one_line_file(u, &a);
|
|
if (r < 0)
|
|
return log_debug_errno(r, "Failed to read %s: %m", u);
|
|
|
|
r = read_one_line_file(v, &b);
|
|
if (r < 0)
|
|
return log_debug_errno(r, "Failed to read %s: %m", v);
|
|
|
|
/* Check if the parent device is the same. If not, then the two backing devices are on
|
|
* different physical devices, and we don't support that. */
|
|
if (!streq(a, b))
|
|
return -ENOTUNIQ;
|
|
}
|
|
|
|
found = de;
|
|
}
|
|
|
|
if (!found)
|
|
return -ENOENT;
|
|
|
|
q = strjoina(p, "/", found->d_name, "/dev");
|
|
|
|
r = read_one_line_file(q, &t);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (sscanf(t, "%u:%u", &maj, &min) != 2)
|
|
return -EINVAL;
|
|
|
|
if (maj == 0)
|
|
return -ENOENT;
|
|
|
|
*ret = makedev(maj, min);
|
|
return 1;
|
|
}
|
|
|
|
int get_block_device_harder(const char *path, dev_t *ret) {
|
|
int r;
|
|
|
|
assert(path);
|
|
assert(ret);
|
|
|
|
/* Gets the backing block device for a file system, and handles LUKS encrypted file systems, looking for its
|
|
* immediate parent, if there is one. */
|
|
|
|
r = get_block_device(path, ret);
|
|
if (r <= 0)
|
|
return r;
|
|
|
|
r = block_get_originating(*ret, ret);
|
|
if (r < 0)
|
|
log_debug_errno(r, "Failed to chase block device '%s', ignoring: %m", path);
|
|
|
|
return 1;
|
|
}
|