mirror of
https://github.com/systemd/systemd
synced 2024-10-15 20:45:09 +00:00
machined/machinectl: add logic to show list of available images
This adds a new bus call to machined that enumerates /var/lib/container and returns all trees stored in it, distuingishing three types: - GPT disk images, which are files suffixed with ".gpt" - directory trees - btrfs subvolumes
This commit is contained in:
parent
8eebf6ad55
commit
cd61c3bfd7
|
@ -5034,9 +5034,11 @@ rootlibexec_PROGRAMS += \
|
|||
systemd-machined
|
||||
|
||||
libsystemd_machine_core_la_SOURCES = \
|
||||
src/machine/machined-dbus.c \
|
||||
src/machine/machine.c \
|
||||
src/machine/machine.h \
|
||||
src/machine/image.c \
|
||||
src/machine/image.h \
|
||||
src/machine/machined-dbus.c \
|
||||
src/machine/machine-dbus.c
|
||||
|
||||
libsystemd_machine_core_la_LIBADD = \
|
||||
|
|
239
src/machine/image.c
Normal file
239
src/machine/image.c
Normal file
|
@ -0,0 +1,239 @@
|
|||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2013 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <sys/statfs.h>
|
||||
|
||||
#include "strv.h"
|
||||
#include "utf8.h"
|
||||
#include "btrfs-util.h"
|
||||
#include "image.h"
|
||||
#include "bus-label.h"
|
||||
|
||||
Image *image_unref(Image *i) {
|
||||
if (!i)
|
||||
return NULL;
|
||||
|
||||
free(i->name);
|
||||
free(i->path);
|
||||
free(i);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int add_image(
|
||||
Hashmap *h,
|
||||
ImageType t,
|
||||
const char *name,
|
||||
const char *path,
|
||||
bool read_only,
|
||||
usec_t mtime,
|
||||
usec_t btime) {
|
||||
|
||||
_cleanup_(image_unrefp) Image *i = NULL;
|
||||
int r;
|
||||
|
||||
assert(h);
|
||||
assert(t >= 0);
|
||||
assert(t < _IMAGE_TYPE_MAX);
|
||||
assert(name);
|
||||
|
||||
i = new(Image, 1);
|
||||
if (!i)
|
||||
return -ENOMEM;
|
||||
|
||||
i->type = t;
|
||||
i->read_only = read_only;
|
||||
i->mtime = mtime;
|
||||
i->btime = btime;
|
||||
|
||||
i->name = strdup(name);
|
||||
if (!i->name)
|
||||
return -ENOMEM;
|
||||
|
||||
if (path) {
|
||||
i->path = strdup(path);
|
||||
if (!i->path)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = hashmap_put(h, i->name, i);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
i = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int image_discover(Hashmap *h) {
|
||||
const char *path;
|
||||
int r;
|
||||
|
||||
assert(h);
|
||||
|
||||
FOREACH_STRING(path, "/var/lib/container", "/var/lib/machine") {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
struct dirent *de;
|
||||
|
||||
d = opendir(path);
|
||||
if (!d) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
FOREACH_DIRENT_ALL(de, d, return -errno) {
|
||||
struct stat st;
|
||||
|
||||
if (STR_IN_SET(de->d_name, ".", ".."))
|
||||
continue;
|
||||
|
||||
/* Temporary files for atomically creating new files */
|
||||
if (startswith(de->d_name, ".#"))
|
||||
continue;
|
||||
|
||||
if (string_has_cc(de->d_name, NULL))
|
||||
continue;
|
||||
|
||||
if (!utf8_is_valid(de->d_name))
|
||||
continue;
|
||||
|
||||
if (hashmap_contains(h, de->d_name))
|
||||
continue;
|
||||
|
||||
/* We explicitly *do* follow symlinks here,
|
||||
* since we want to allow symlinking trees
|
||||
* into /var/lib/container/, and treat them
|
||||
* normally. */
|
||||
if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) {
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
|
||||
/* btrfs subvolumes have inode 256 */
|
||||
if (st.st_ino == 256) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
struct statfs sfs;
|
||||
|
||||
fd = openat(dirfd(d), de->d_name, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
|
||||
if (fd < 0) {
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (fstatfs(fd, &sfs) < 0)
|
||||
return -errno;
|
||||
|
||||
if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) {
|
||||
usec_t btime = 0;
|
||||
int ro;
|
||||
|
||||
/* It's a btrfs subvolume */
|
||||
|
||||
ro = btrfs_subvol_is_read_only_fd(fd);
|
||||
if (ro < 0)
|
||||
return ro;
|
||||
|
||||
/* r = btrfs_subvol_get_btime(fd, &btime); */
|
||||
/* if (r < 0) */
|
||||
/* return r; */
|
||||
|
||||
r = add_image(h,
|
||||
IMAGE_SUBVOLUME,
|
||||
de->d_name,
|
||||
path,
|
||||
ro,
|
||||
0,
|
||||
btime);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* It's just a normal directory. */
|
||||
|
||||
r = add_image(h,
|
||||
IMAGE_DIRECTORY,
|
||||
de->d_name,
|
||||
path,
|
||||
false,
|
||||
0,
|
||||
0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
} else if (S_ISREG(st.st_mode) &&
|
||||
endswith(de->d_name, ".gpt")) {
|
||||
|
||||
/* It's a GPT block device */
|
||||
|
||||
r = add_image(h,
|
||||
IMAGE_GPT,
|
||||
de->d_name,
|
||||
path,
|
||||
!!(st.st_mode & 0111),
|
||||
timespec_load(&st.st_mtim),
|
||||
0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void image_hashmap_free(Hashmap *map) {
|
||||
Image *i;
|
||||
|
||||
while ((i = hashmap_steal_first(map)))
|
||||
image_unref(i);
|
||||
|
||||
hashmap_free(map);
|
||||
}
|
||||
|
||||
char *image_bus_path(const char *name) {
|
||||
_cleanup_free_ char *e = NULL;
|
||||
|
||||
assert(name);
|
||||
|
||||
e = bus_label_escape(name);
|
||||
if (!e)
|
||||
return NULL;
|
||||
|
||||
return strappend("/org/freedesktop/machine1/image/", e);
|
||||
}
|
||||
|
||||
static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
|
||||
[IMAGE_DIRECTORY] = "directory",
|
||||
[IMAGE_SUBVOLUME] = "subvolume",
|
||||
[IMAGE_GPT] = "gpt",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);
|
57
src/machine/image.h
Normal file
57
src/machine/image.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2013 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "time-util.h"
|
||||
#include "hashmap.h"
|
||||
|
||||
typedef enum ImageType {
|
||||
IMAGE_DIRECTORY,
|
||||
IMAGE_SUBVOLUME,
|
||||
IMAGE_GPT,
|
||||
_IMAGE_TYPE_MAX,
|
||||
_IMAGE_TYPE_INVALID = -1
|
||||
} ImageType;
|
||||
|
||||
typedef struct Image {
|
||||
ImageType type;
|
||||
char *name;
|
||||
char *path;
|
||||
bool read_only;
|
||||
|
||||
usec_t mtime;
|
||||
usec_t btime;
|
||||
} Image;
|
||||
|
||||
Image *image_unref(Image *i);
|
||||
|
||||
void image_hashmap_free(Hashmap *map);
|
||||
|
||||
int image_discover(Hashmap *map);
|
||||
|
||||
char *image_bus_path(const char *name);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Image*, image_unref);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, image_hashmap_free);
|
||||
|
||||
const char* image_type_to_string(ImageType t) _const_;
|
||||
ImageType image_type_from_string(const char *s) _pure_;
|
|
@ -32,6 +32,7 @@
|
|||
#include "fileio.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "local-addresses.h"
|
||||
#include "image.h"
|
||||
#include "machine.h"
|
||||
|
||||
static int property_get_id(
|
||||
|
@ -475,9 +476,11 @@ char *machine_bus_path(Machine *m) {
|
|||
}
|
||||
|
||||
int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
|
||||
_cleanup_(image_hashmap_freep) Hashmap *images = NULL;
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
Machine *machine = NULL;
|
||||
Manager *m = userdata;
|
||||
Image *image;
|
||||
Iterator i;
|
||||
int r;
|
||||
|
||||
|
@ -497,6 +500,26 @@ int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char
|
|||
return r;
|
||||
}
|
||||
|
||||
images = hashmap_new(&string_hash_ops);
|
||||
if (!images)
|
||||
return -ENOMEM;
|
||||
|
||||
r = image_discover(images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
HASHMAP_FOREACH(image, images, i) {
|
||||
char *p;
|
||||
|
||||
p = image_bus_path(image->name);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_consume(&l, p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*nodes = l;
|
||||
l = NULL;
|
||||
|
||||
|
|
|
@ -120,6 +120,98 @@ static int list_machines(sd_bus *bus, char **args, unsigned n) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
typedef struct ImageInfo {
|
||||
const char *name;
|
||||
const char *type;
|
||||
bool read_only;
|
||||
} ImageInfo;
|
||||
|
||||
static int compare_image_info(const void *a, const void *b) {
|
||||
const ImageInfo *x = a, *y = b;
|
||||
|
||||
return strcmp(x->name, y->name);
|
||||
}
|
||||
|
||||
static int list_images(sd_bus *bus, char **args, unsigned n) {
|
||||
|
||||
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
size_t max_name = strlen("NAME"), max_type = strlen("TYPE");
|
||||
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
|
||||
_cleanup_free_ ImageInfo *images = NULL;
|
||||
size_t n_images = 0, n_allocated = 0, j;
|
||||
const char *name, *type, *object;
|
||||
int read_only;
|
||||
int r;
|
||||
|
||||
pager_open_if_enabled();
|
||||
|
||||
r = sd_bus_call_method(
|
||||
bus,
|
||||
"org.freedesktop.machine1",
|
||||
"/org/freedesktop/machine1",
|
||||
"org.freedesktop.machine1.Manager",
|
||||
"ListImages",
|
||||
&error,
|
||||
&reply,
|
||||
"");
|
||||
if (r < 0) {
|
||||
log_error("Could not get images: %s", bus_error_message(&error, -r));
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbo)");
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
while ((r = sd_bus_message_read(reply, "(ssbo)", &name, &type, &read_only, &object)) > 0) {
|
||||
|
||||
if (name[0] == '.' && !arg_all)
|
||||
continue;
|
||||
|
||||
if (!GREEDY_REALLOC(images, n_allocated, n_images + 1))
|
||||
return log_oom();
|
||||
|
||||
images[n_images].name = name;
|
||||
images[n_images].type = type;
|
||||
images[n_images].read_only = read_only;
|
||||
|
||||
if (strlen(name) > max_name)
|
||||
max_name = strlen(name);
|
||||
|
||||
if (strlen(type) > max_type)
|
||||
max_type = strlen(type);
|
||||
|
||||
n_images++;
|
||||
}
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
r = sd_bus_message_exit_container(reply);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
|
||||
|
||||
if (arg_legend)
|
||||
printf("%-*s %-*s %-3s\n", (int) max_name, "NAME", (int) max_type, "TYPE", "RO");
|
||||
|
||||
for (j = 0; j < n_images; j++) {
|
||||
printf("%-*s %-*s %-3s\n",
|
||||
(int) max_name, images[j].name,
|
||||
(int) max_type, images[j].type,
|
||||
yes_no(images[j].read_only));
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
|
||||
if (arg_legend)
|
||||
printf("\n%zu images listed.\n", n_images);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
|
||||
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
|
||||
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
@ -1106,7 +1198,7 @@ static void help(void) {
|
|||
" -s --signal=SIGNAL Which signal to send\n"
|
||||
" --read-only Create read-only bind mount\n"
|
||||
" --mkdir Create directory before bind mounting, if missing\n\n"
|
||||
"Commands:\n"
|
||||
"Machine Commands:\n"
|
||||
" list List running VMs and containers\n"
|
||||
" status NAME... Show VM/container status\n"
|
||||
" show NAME... Show properties of one or more VMs/containers\n"
|
||||
|
@ -1117,7 +1209,9 @@ static void help(void) {
|
|||
" terminate NAME... Terminate one or more VMs/containers\n"
|
||||
" bind NAME PATH [PATH] Bind mount a path from the host into a container\n"
|
||||
" copy-to NAME PATH [PATH] Copy files from the host to a container\n"
|
||||
" copy-from NAME PATH [PATH] Copy files from a container to the host\n",
|
||||
" copy-from NAME PATH [PATH] Copy files from a container to the host\n\n"
|
||||
"Image commands:\n"
|
||||
" list-images Show available images\n",
|
||||
program_invocation_short_name);
|
||||
}
|
||||
|
||||
|
@ -1247,6 +1341,7 @@ static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
|
|||
int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
|
||||
} verbs[] = {
|
||||
{ "list", LESS, 1, list_machines },
|
||||
{ "list-images", LESS, 1, list_images },
|
||||
{ "status", MORE, 2, show },
|
||||
{ "show", MORE, 1, show },
|
||||
{ "terminate", MORE, 2, terminate_machine },
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "bus-common-errors.h"
|
||||
#include "time-util.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "image.h"
|
||||
#include "machined.h"
|
||||
|
||||
static int method_get_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
|
@ -436,11 +437,63 @@ static int method_get_machine_os_release(sd_bus *bus, sd_bus_message *message, v
|
|||
return bus_machine_method_get_os_release(bus, message, machine, error);
|
||||
}
|
||||
|
||||
static int method_list_images(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
|
||||
_cleanup_(image_hashmap_freep) Hashmap *images = NULL;
|
||||
Manager *m = userdata;
|
||||
Image *image;
|
||||
Iterator i;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(message);
|
||||
assert(m);
|
||||
|
||||
images = hashmap_new(&string_hash_ops);
|
||||
if (!images)
|
||||
return -ENOMEM;
|
||||
|
||||
r = image_discover(images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_new_method_return(message, &reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "(ssbo)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
HASHMAP_FOREACH(image, images, i) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
p = image_bus_path(image->name);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
r = sd_bus_message_append(reply, "(ssbo)",
|
||||
image->name,
|
||||
image_type_to_string(image->type),
|
||||
image->read_only,
|
||||
p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_bus_send(bus, reply, NULL);
|
||||
}
|
||||
|
||||
const sd_bus_vtable manager_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("ListImages", NULL, "a(ssbo)", method_list_images, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0),
|
||||
SD_BUS_METHOD("CreateMachineWithNetwork", "sayssusaia(sv)", "o", method_create_machine_with_network, 0),
|
||||
SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0),
|
||||
|
|
|
@ -40,6 +40,10 @@
|
|||
send_interface="org.freedesktop.machine1.Manager"
|
||||
send_member="ListMachines"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.machine1"
|
||||
send_interface="org.freedesktop.machine1.Manager"
|
||||
send_member="ListImages"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.machine1"
|
||||
send_interface="org.freedesktop.machine1.Manager"
|
||||
send_member="GetMachine"/>
|
||||
|
|
|
@ -84,18 +84,18 @@ int btrfs_is_snapshot(int fd) {
|
|||
struct stat st;
|
||||
struct statfs sfs;
|
||||
|
||||
if (fstatfs(fd, &sfs) < 0)
|
||||
return -errno;
|
||||
|
||||
if (!F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
|
||||
return 0;
|
||||
/* On btrfs subvolumes always have the inode 256 */
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
/* On btrfs subvolumes always have the inode 256 */
|
||||
if (!S_ISDIR(st.st_mode) || st.st_ino != 256)
|
||||
return 0;
|
||||
|
||||
return S_ISDIR(st.st_mode) && st.st_ino == 256;
|
||||
if (fstatfs(fd, &sfs) < 0)
|
||||
return -errno;
|
||||
|
||||
return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
|
||||
}
|
||||
|
||||
int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy) {
|
||||
|
@ -232,6 +232,15 @@ int btrfs_subvol_read_only(const char *path, bool b) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int btrfs_subvol_is_read_only_fd(int fd) {
|
||||
uint64_t flags;
|
||||
|
||||
if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
|
||||
return -errno;
|
||||
|
||||
return !!(flags & BTRFS_SUBVOL_RDONLY);
|
||||
}
|
||||
|
||||
int btrfs_reflink(int infd, int outfd) {
|
||||
int r;
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ int btrfs_subvol_make(const char *path);
|
|||
int btrfs_subvol_remove(const char *path);
|
||||
int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy);
|
||||
int btrfs_subvol_read_only(const char *path, bool b);
|
||||
int btrfs_subvol_is_read_only_fd(int fd);
|
||||
|
||||
int btrfs_reflink(int infd, int outfd);
|
||||
|
||||
|
|
|
@ -775,6 +775,15 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root
|
|||
continue; \
|
||||
else
|
||||
|
||||
#define FOREACH_DIRENT_ALL(de, d, on_error) \
|
||||
for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \
|
||||
if (!de) { \
|
||||
if (errno > 0) { \
|
||||
on_error; \
|
||||
} \
|
||||
break; \
|
||||
} else
|
||||
|
||||
static inline void *mempset(void *s, int c, size_t n) {
|
||||
memset(s, c, n);
|
||||
return (uint8_t*)s + n;
|
||||
|
|
|
@ -15,7 +15,7 @@ After=machine.slice
|
|||
[Service]
|
||||
ExecStart=@rootlibexecdir@/systemd-machined
|
||||
BusName=org.freedesktop.machine1
|
||||
CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT
|
||||
CapabilityBoundingSet=CAP_KILL CAP_SYS_PTRACE CAP_SYS_ADMIN CAP_SETGID CAP_SYS_CHROOT CAP_DAC_READ_SEARCH
|
||||
WatchdogSec=1min
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
|
|
Loading…
Reference in a new issue