diff options
| author | Lennart Poettering <lennart@poettering.net> | 2014-12-25 03:19:19 +0100 | 
|---|---|---|
| committer | Lennart Poettering <lennart@poettering.net> | 2014-12-25 03:19:19 +0100 | 
| commit | 10f9c75519671e7c7ab8993b54fe22da7c2d0c38 (patch) | |
| tree | 78777a123c0261f892af164581884f8a07756203 | |
| parent | 5fa89b2cb366d533e56a9b7a9ce548480776f973 (diff) | |
machined: beef up machined image listing with creation/modification times of subvolumes
We make use of the btrfs subvol crtime for this, and for gpt images of a
manually managed xattr, if we can.
| -rw-r--r-- | Makefile.am | 1 | ||||
| -rw-r--r-- | src/import/import-dkr.c | 2 | ||||
| -rw-r--r-- | src/import/import-gpt.c | 2 | ||||
| -rw-r--r-- | src/machine/image-dbus.c | 64 | ||||
| -rw-r--r-- | src/machine/image.c | 33 | ||||
| -rw-r--r-- | src/machine/image.h | 2 | ||||
| -rw-r--r-- | src/machine/machinectl.c | 53 | ||||
| -rw-r--r-- | src/machine/machined-dbus.c | 8 | ||||
| -rw-r--r-- | src/shared/btrfs-ctree.h | 68 | ||||
| -rw-r--r-- | src/shared/btrfs-util.c | 82 | ||||
| -rw-r--r-- | src/shared/btrfs-util.h | 19 | ||||
| -rw-r--r-- | src/shared/missing.h | 8 | ||||
| -rw-r--r-- | src/shared/util.c | 55 | ||||
| -rw-r--r-- | src/shared/util.h | 3 | ||||
| -rw-r--r-- | src/test/test-btrfs.c | 25 | 
15 files changed, 370 insertions, 55 deletions
| diff --git a/Makefile.am b/Makefile.am index 89f3af89f5..fd1a8a5ae9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -896,6 +896,7 @@ libsystemd_shared_la_SOURCES = \  	src/shared/nss-util.h \  	src/shared/btrfs-util.c \  	src/shared/btrfs-util.h \ +	src/shared/btrfs-ctree.h \  	src/shared/verbs.c \  	src/shared/verbs.h \  	src/shared/build.h diff --git a/src/import/import-dkr.c b/src/import/import-dkr.c index 8f26191c40..b290619305 100644 --- a/src/import/import-dkr.c +++ b/src/import/import-dkr.c @@ -780,7 +780,7 @@ static void dkr_import_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result                          goto fail;                  } -                r = btrfs_subvol_read_only(job->temp_path, true); +                r = btrfs_subvol_set_read_only(job->temp_path, true);                  if (r < 0) {                          log_error_errno(r, "Failed to mark snapshot read-only: %m");                          goto fail; diff --git a/src/import/import-gpt.c b/src/import/import-gpt.c index 503f1e64cf..a81efa2bba 100644 --- a/src/import/import-gpt.c +++ b/src/import/import-gpt.c @@ -171,6 +171,8 @@ static void gpt_import_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result                  ut[1] = ut[0];                  (void) futimens(f->disk_fd, ut); + +                fd_setcrtime(f->disk_fd, f->mtime);          }          if (fstat(f->disk_fd, &st) < 0) { diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index afb849b41a..8d9ad5589e 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -159,12 +159,68 @@ static int property_get_read_only(          return 1;  } +static int property_get_crtime( +                sd_bus *bus, +                const char *path, +                const char *interface, +                const char *property, +                sd_bus_message *reply, +                void *userdata, +                sd_bus_error *error) { + + +        _cleanup_(image_unrefp) Image *image = NULL; +        int r; + +        assert(bus); +        assert(reply); + +        r = image_find_by_bus_path_with_error(path, &image, error); +        if (r < 0) +                return r; + +        r = sd_bus_message_append(reply, "t", image->crtime); +        if (r < 0) +                return r; + +        return 1; +} + +static int property_get_mtime( +                sd_bus *bus, +                const char *path, +                const char *interface, +                const char *property, +                sd_bus_message *reply, +                void *userdata, +                sd_bus_error *error) { + + +        _cleanup_(image_unrefp) Image *image = NULL; +        int r; + +        assert(bus); +        assert(reply); + +        r = image_find_by_bus_path_with_error(path, &image, error); +        if (r < 0) +                return r; + +        r = sd_bus_message_append(reply, "t", image->mtime); +        if (r < 0) +                return r; + +        return 1; +} +  const sd_bus_vtable image_vtable[] = {          SD_BUS_VTABLE_START(0), -        SD_BUS_PROPERTY("Name",     "s", property_get_name,      0, 0), -        SD_BUS_PROPERTY("Path",     "s", property_get_path,      0, 0), -        SD_BUS_PROPERTY("Type",     "s", property_get_type,      0, 0), -        SD_BUS_PROPERTY("ReadOnly", "b", property_get_read_only, 0, 0), +        SD_BUS_PROPERTY("Name",                  "s", property_get_name,      0, 0), +        SD_BUS_PROPERTY("Path",                  "s", property_get_path,      0, 0), +        SD_BUS_PROPERTY("Type",                  "s", property_get_type,      0, 0), +        SD_BUS_PROPERTY("ReadOnly",              "b", property_get_read_only, 0, 0), +        SD_BUS_PROPERTY("CreationTimestamp",     "t", property_get_crtime,    0, 0), +        SD_BUS_PROPERTY("ModificationTimestamp", "t", property_get_mtime,     0, 0),          SD_BUS_VTABLE_END  }; diff --git a/src/machine/image.c b/src/machine/image.c index 2ffe9444e3..9f88b0551f 100644 --- a/src/machine/image.c +++ b/src/machine/image.c @@ -46,8 +46,8 @@ static int image_new(                  const char *name,                  const char *path,                  bool read_only, +                usec_t crtime,                  usec_t mtime, -                usec_t btime,                  Image **ret) {          _cleanup_(image_unrefp) Image *i = NULL; @@ -63,8 +63,8 @@ static int image_new(          i->type = t;          i->read_only = read_only; +        i->crtime = crtime;          i->mtime = mtime; -        i->btime = btime;          i->name = strdup(name);          if (!i->name) @@ -116,25 +116,20 @@ static int image_make(int dfd, const char *name, const char *path, Image **ret)                                  return -errno;                          if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) { -                                usec_t btime = 0; -                                int ro; +                                BtrfsSubvolInfo info;                                  /* 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 = btrfs_subvol_get_info_fd(fd, &info); +                                if (r < 0) +                                        return r;                                  r = image_new(IMAGE_SUBVOLUME,                                                name,                                                path, -                                              ro, +                                              info.read_only, +                                              info.otime,                                                0, -                                              btime,                                                ret);                                  if (r < 0)                                          return r; @@ -158,18 +153,24 @@ static int image_make(int dfd, const char *name, const char *path, Image **ret)                  return 1;          } else if (S_ISREG(st.st_mode) && endswith(name, ".gpt")) { +                const char *truncated; +                usec_t crtime = 0;                  /* It's a GPT block device */                  if (!ret)                          return 1; +                fd_getcrtime_at(dfd, name, &crtime, 0); + +                truncated = strndupa(name, strlen(name) - 4); +                  r = image_new(IMAGE_GPT, -                              name, +                              truncated,                                path, -                              !!(st.st_mode & 0222), +                              !(st.st_mode & 0222), +                              crtime,                                timespec_load(&st.st_mtim), -                              0,                                ret);                  if (r < 0)                          return r; diff --git a/src/machine/image.h b/src/machine/image.h index f04be239d3..2e8f78147c 100644 --- a/src/machine/image.h +++ b/src/machine/image.h @@ -39,8 +39,8 @@ typedef struct Image {          char *path;          bool read_only; +        usec_t crtime;          usec_t mtime; -        usec_t btime;  } Image;  Image *image_unref(Image *i); diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index d223e7895a..1cc5dfc68e 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -129,6 +129,8 @@ typedef struct ImageInfo {          const char *name;          const char *type;          bool read_only; +        usec_t crtime; +        usec_t mtime;  } ImageInfo;  static int compare_image_info(const void *a, const void *b) { @@ -140,14 +142,14 @@ static int compare_image_info(const void *a, const void *b) {  static int list_images(int argc, char *argv[], void *userdata) {          _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; -        size_t max_name = strlen("NAME"), max_type = strlen("TYPE"); +        size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");          _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;          sd_bus *bus = userdata; -        int read_only; -        int r; +        uint64_t crtime, mtime; +        int read_only, r;          assert(bus); @@ -167,11 +169,13 @@ static int list_images(int argc, char *argv[], void *userdata) {                  return r;          } -        r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbo)"); +        r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbtto)");          if (r < 0)                  return bus_log_parse_error(r); -        while ((r = sd_bus_message_read(reply, "(ssbo)", &name, &type, &read_only, &object)) > 0) { +        while ((r = sd_bus_message_read(reply, "(ssbtto)", &name, &type, &read_only, &crtime, &mtime, &object)) > 0) { +                char buf[FORMAT_TIMESTAMP_MAX]; +                size_t l;                  if (name[0] == '.' && !arg_all)                          continue; @@ -182,12 +186,28 @@ static int list_images(int argc, char *argv[], void *userdata) {                  images[n_images].name = name;                  images[n_images].type = type;                  images[n_images].read_only = read_only; +                images[n_images].crtime = crtime; +                images[n_images].mtime = mtime; + +                l = strlen(name); +                if (l > max_name) +                        max_name = l; + +                l = strlen(type); +                if (l > max_type) +                        max_type = l; -                if (strlen(name) > max_name) -                        max_name = strlen(name); +                if (crtime != 0) { +                        l = strlen(format_timestamp(buf, sizeof(buf), crtime)); +                        if (l > max_crtime) +                                max_crtime = l; +                } -                if (strlen(type) > max_type) -                        max_type = strlen(type); +                if (mtime != 0) { +                        l = strlen(format_timestamp(buf, sizeof(buf), mtime)); +                        if (l > max_mtime) +                                max_mtime = l; +                }                  n_images++;          } @@ -201,13 +221,22 @@ static int list_images(int argc, char *argv[], void *userdata) {          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"); +                printf("%-*s %-*s %-3s %*s %*s\n", +                       (int) max_name, "NAME", +                       (int) max_type, "TYPE", +                       "RO", +                       (int) max_crtime, "CREATED", +                       (int) max_mtime, "MODIFIED");          for (j = 0; j < n_images; j++) { -                printf("%-*s %-*s %-3s\n", +                char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX]; + +                printf("%-*s %-*s %-3s %*s %*s\n",                         (int) max_name, images[j].name,                         (int) max_type, images[j].type, -                       yes_no(images[j].read_only)); +                       yes_no(images[j].read_only), +                       (int) max_crtime, images[j].crtime != 0 ? format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime) : "-", +                       (int) max_mtime, images[j].mtime != 0 ? format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime) : "-");          }          if (r < 0) diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 0bf97e1ebb..e264dacbe6 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -488,7 +488,7 @@ static int method_list_images(sd_bus *bus, sd_bus_message *message, void *userda          if (r < 0)                  return r; -        r = sd_bus_message_open_container(reply, 'a', "(ssbo)"); +        r = sd_bus_message_open_container(reply, 'a', "(ssbtto)");          if (r < 0)                  return r; @@ -499,10 +499,12 @@ static int method_list_images(sd_bus *bus, sd_bus_message *message, void *userda                  if (!p)                          return -ENOMEM; -                r = sd_bus_message_append(reply, "(ssbo)", +                r = sd_bus_message_append(reply, "(ssbtto)",                                            image->name,                                            image_type_to_string(image->type),                                            image->read_only, +                                          image->crtime, +                                          image->mtime,                                            p);                  if (r < 0)                          return r; @@ -563,7 +565,7 @@ const sd_bus_vtable manager_vtable[] = {          SD_BUS_METHOD("GetImage", "s", "o", method_get_image, 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("ListImages", NULL, "a(ssbtto)", 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), diff --git a/src/shared/btrfs-ctree.h b/src/shared/btrfs-ctree.h new file mode 100644 index 0000000000..45b94cdbf6 --- /dev/null +++ b/src/shared/btrfs-ctree.h @@ -0,0 +1,68 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +#include "sparse-endian.h" + +/* Stolen from btrfs' ctree.h */ + +struct btrfs_timespec { +        le64_t sec; +        le32_t nsec; +} _packed_; + +struct btrfs_disk_key { +        le64_t objectid; +        uint8_t type; +        le64_t offset; +} _packed_; + +struct btrfs_inode_item { +        le64_t generation; +        le64_t transid; +        le64_t size; +        le64_t nbytes; +        le64_t block_group; +        le32_t nlink; +        le32_t uid; +        le32_t gid; +        le32_t mode; +        le64_t rdev; +        le64_t flags; +        le64_t sequence; +        le64_t reserved[4]; +        struct btrfs_timespec atime; +        struct btrfs_timespec ctime; +        struct btrfs_timespec mtime; +        struct btrfs_timespec otime; +} _packed_; + +struct btrfs_root_item { +        struct btrfs_inode_item inode; +        le64_t generation; +        le64_t root_dirid; +        le64_t bytenr; +        le64_t byte_limit; +        le64_t bytes_used; +        le64_t last_snapshot; +        le64_t flags; +        le32_t refs; +        struct btrfs_disk_key drop_progress; +        uint8_t drop_level; +        uint8_t level; +        le64_t generation_v2; +        uint8_t uuid[BTRFS_UUID_SIZE]; +        uint8_t parent_uuid[BTRFS_UUID_SIZE]; +        uint8_t received_uuid[BTRFS_UUID_SIZE]; +        le64_t ctransid; +        le64_t otransid; +        le64_t stransid; +        le64_t rtransid; +        struct btrfs_timespec ctime; +        struct btrfs_timespec otime; +        struct btrfs_timespec stime; +        struct btrfs_timespec rtime; +        le64_t reserved[8]; +} _packed_; + +#define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0) diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c index d685b3ecbd..84c81106fa 100644 --- a/src/shared/btrfs-util.c +++ b/src/shared/btrfs-util.c @@ -33,6 +33,7 @@  #include "macro.h"  #include "strv.h"  #include "copy.h" +#include "btrfs-ctree.h"  #include "btrfs-util.h"  static int validate_subvolume_name(const char *name) { @@ -129,7 +130,7 @@ int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_                          }                          if (read_only) { -                                r = btrfs_subvol_read_only(new_path, true); +                                r = btrfs_subvol_set_read_only(new_path, true);                                  if (r < 0) {                                          btrfs_subvol_remove(new_path);                                          return r; @@ -207,7 +208,7 @@ int btrfs_subvol_remove(const char *path) {          return 0;  } -int btrfs_subvol_read_only(const char *path, bool b) { +int btrfs_subvol_set_read_only(const char *path, bool b) {          _cleanup_close_ int fd = -1;          uint64_t flags, nflags; @@ -232,7 +233,7 @@ int btrfs_subvol_read_only(const char *path, bool b) {          return 0;  } -int btrfs_subvol_is_read_only_fd(int fd) { +int btrfs_subvol_get_read_only_fd(int fd) {          uint64_t flags;          if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0) @@ -301,3 +302,78 @@ int btrfs_get_block_device(const char *path, dev_t *dev) {          return -ENODEV;  } + +int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) { +        struct btrfs_ioctl_ino_lookup_args args = { +                .objectid = BTRFS_FIRST_FREE_OBJECTID +        }; + +        assert(fd >= 0); +        assert(ret); + +        if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0) +                return -errno; + +        *ret = args.treeid; +        return 0; +} + +int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) { +        struct btrfs_ioctl_search_args args = { +                /* Tree of tree roots */ +                .key.tree_id = 1, + +                /* Look precisely for the subvolume items */ +                .key.min_type = BTRFS_ROOT_ITEM_KEY, +                .key.max_type = BTRFS_ROOT_ITEM_KEY, + +                /* No restrictions on the other components */ +                .key.min_offset = 0, +                .key.max_offset = (uint64_t) -1, +                .key.min_transid = 0, +                .key.max_transid = (uint64_t) -1, + +                /* Some large value */ +                .key.nr_items = 2, +        }; + +        struct btrfs_ioctl_search_header *sh; +        struct btrfs_root_item *ri; +        uint64_t subvol_id; +        int r; + +        assert(fd >= 0); +        assert(ret); + +        r = btrfs_subvol_get_id_fd(fd, &subvol_id); +        if (r < 0) +                return r; + +        args.key.min_objectid = args.key.max_objectid = subvol_id; +        if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) +                return -errno; + +        if (args.key.nr_items != 1) +                return -EIO; + +        sh = (struct btrfs_ioctl_search_header*) args.buf; +        assert(sh->type == BTRFS_ROOT_ITEM_KEY); +        assert(sh->objectid == subvol_id); + +        if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec)) +                return -ENOTSUP; + +        ri = (struct btrfs_root_item *)(args.buf + sizeof(struct btrfs_ioctl_search_header)); + +        ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC + +                     (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC; + +        ret->subvol_id = subvol_id; +        ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY); + +        assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid)); +        memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid)); +        memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid)); + +        return 0; +} diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h index c8f798b6a6..f51f37a659 100644 --- a/src/shared/btrfs-util.h +++ b/src/shared/btrfs-util.h @@ -22,13 +22,28 @@  #include <stdbool.h>  #include <sys/types.h> +#include "time-util.h" + +typedef struct BtrfsSubvolInfo { +        uint64_t subvol_id; +        usec_t otime; + +        sd_id128_t uuid; +        sd_id128_t parent_uuid; + +        bool read_only; +} BtrfsSubvolInfo; +  int btrfs_is_snapshot(int fd);  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_subvol_set_read_only(const char *path, bool b); +int btrfs_subvol_get_read_only_fd(int fd); +int btrfs_subvol_get_id_fd(int fd, uint64_t *ret); +int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *info);  int btrfs_reflink(int infd, int outfd); diff --git a/src/shared/missing.h b/src/shared/missing.h index 91a6215226..dd7bef4d9d 100644 --- a/src/shared/missing.h +++ b/src/shared/missing.h @@ -250,6 +250,14 @@ struct btrfs_ioctl_fs_info_args {                                   struct btrfs_ioctl_vol_args)  #endif +#ifndef BTRFS_FIRST_FREE_OBJECTID +#define BTRFS_FIRST_FREE_OBJECTID 256 +#endif + +#ifndef BTRFS_ROOT_ITEM_KEY +#define BTRFS_ROOT_ITEM_KEY 132 +#endif +  #ifndef BTRFS_SUPER_MAGIC  #define BTRFS_SUPER_MAGIC 0x9123683E  #endif diff --git a/src/shared/util.c b/src/shared/util.c index 98b3465d4a..52e5df44a9 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -7561,8 +7561,37 @@ int openpt_in_namespace(pid_t pid, int flags) {          return -EIO;  } -int fd_getcrtime(int fd, usec_t *usec) { +ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags) { +        _cleanup_close_ int fd = -1; +        ssize_t l; + +        /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */ + +        fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOATIME|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0)); +        if (fd < 0) +                return -errno; + +        l = fgetxattr(fd, attribute, value, size); +        if (l < 0) +                return -errno; + +        return l; +} + +static int parse_crtime(le64_t le, usec_t *usec) {          uint64_t u; + +        assert(usec); + +        u = le64toh(le); +        if (u == 0 || u == (uint64_t) -1) +                return -EIO; + +        *usec = (usec_t) u; +        return 0; +} + +int fd_getcrtime(int fd, usec_t *usec) {          le64_t le;          ssize_t n; @@ -7578,16 +7607,23 @@ int fd_getcrtime(int fd, usec_t *usec) {          if (n != sizeof(le))                  return -EIO; -        u = le64toh(le); -        if (u == 0 || u == (uint64_t) -1) +        return parse_crtime(le, usec); +} + +int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags) { +        le64_t le; +        ssize_t n; + +        n = fgetxattrat_fake(dirfd, name, "user.crtime_usec", &le, sizeof(le), flags); +        if (n < 0) +                return -errno; +        if (n != sizeof(le))                  return -EIO; -        *usec = (usec_t) u; -        return 0; +        return parse_crtime(le, usec);  }  int path_getcrtime(const char *p, usec_t *usec) { -        uint64_t u;          le64_t le;          ssize_t n; @@ -7600,12 +7636,7 @@ int path_getcrtime(const char *p, usec_t *usec) {          if (n != sizeof(le))                  return -EIO; -        u = le64toh(le); -        if (u == 0 || u == (uint64_t) -1) -                return -EIO; - -        *usec = (usec_t) u; -        return 0; +        return parse_crtime(le, usec);  }  int fd_setcrtime(int fd, usec_t usec) { diff --git a/src/shared/util.h b/src/shared/util.h index 4d5b9824d9..a131a3c0f1 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -1063,6 +1063,9 @@ int ptsname_malloc(int fd, char **ret);  int openpt_in_namespace(pid_t pid, int flags); +ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags); +  int fd_setcrtime(int fd, usec_t usec);  int fd_getcrtime(int fd, usec_t *usec);  int path_getcrtime(const char *p, usec_t *usec); +int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags); diff --git a/src/test/test-btrfs.c b/src/test/test-btrfs.c index 7c4cc55442..4a08c72fbb 100644 --- a/src/test/test-btrfs.c +++ b/src/test/test-btrfs.c @@ -20,13 +20,36 @@  ***/  #include <stdlib.h> +#include <fcntl.h>  #include "log.h" -#include "btrfs-util.h"  #include "fileio.h" +#include "util.h" +#include "btrfs-util.h"  int main(int argc, char *argv[]) {          int r; +        BtrfsSubvolInfo info; +        char ts[FORMAT_TIMESTAMP_MAX]; +        int fd; + +        fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY); +        if (fd < 0) +                log_error_errno(errno, "Failed to open root directory: %m"); +        else { +                r = btrfs_subvol_get_info_fd(fd, &info); +                if (r < 0) +                        log_error_errno(r, "Failed to get subvolume info: %m"); +                else { +                        log_info("otime: %s", format_timestamp(ts, sizeof(ts), info.otime)); +                        log_info("read-only: %s", yes_no(info.read_only)); +                } + +                r = btrfs_subvol_get_read_only_fd(fd); +                assert_se(r >= 0); + +                log_info("read-only: %s", yes_no(r)); +        }          r = btrfs_subvol_make("/xxxtest");          if (r < 0) | 
