if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0)
return -errno;
+ if (FLAGS_SET(flags, BTRFS_SNAPSHOT_LOCK_BSD)) {
+ subvolume_fd = xopenat_lock(new_fd, subvolume,
+ O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW,
+ /* xopen_flags = */ 0,
+ /* mode = */ 0,
+ LOCK_BSD,
+ LOCK_EX);
+ if (subvolume_fd < 0)
+ return subvolume_fd;
+
+ r = btrfs_is_subvol_fd(subvolume_fd);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EEXIST;
+ }
+
if (!(flags & BTRFS_SNAPSHOT_RECURSIVE) &&
!(flags & BTRFS_SNAPSHOT_QUOTA))
- return 0;
+ return flags & BTRFS_SNAPSHOT_LOCK_BSD ? TAKE_FD(subvolume_fd) : 0;
if (old_subvol_id == 0) {
r = btrfs_subvol_get_id_fd(old_fd, &old_subvol_id);
if (flags & BTRFS_SNAPSHOT_QUOTA)
(void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id);
- return 0;
+ return flags & BTRFS_SNAPSHOT_LOCK_BSD ? TAKE_FD(subvolume_fd) : 0;
}
args.key.min_offset = args.key.max_offset = old_subvol_id;
return k;
}
- r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
+ r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid,
+ flags & ~(BTRFS_SNAPSHOT_FALLBACK_COPY|BTRFS_SNAPSHOT_LOCK_BSD));
/* Restore the readonly flag */
if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
if (flags & BTRFS_SNAPSHOT_QUOTA)
(void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id);
- return 0;
+ return flags & BTRFS_SNAPSHOT_LOCK_BSD ? TAKE_FD(subvolume_fd) : 0;
}
int btrfs_subvol_snapshot_at_full(
void *userdata) {
_cleanup_free_ char *subvolume = NULL;
- _cleanup_close_ int old_fd = -EBADF, new_fd = -EBADF;
+ _cleanup_close_ int old_fd = -EBADF, new_fd = -EBADF, subvolume_fd = -EBADF;
int r;
assert(dir_fdf >= 0 || dir_fdf == AT_FDCWD);
} else if (r < 0)
return r;
+ if (FLAGS_SET(flags, BTRFS_SNAPSHOT_LOCK_BSD)) {
+ subvolume_fd = xopenat_lock(new_fd, subvolume,
+ O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW,
+ /* xopen_flags = */ 0,
+ /* mode = */ 0,
+ LOCK_BSD,
+ LOCK_EX);
+ if (subvolume_fd < 0)
+ return subvolume_fd;
+
+ if (!plain_directory) {
+ r = btrfs_is_subvol_fd(subvolume_fd);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EEXIST;
+ }
+ }
+
r = copy_directory_at_full(
dir_fdf, from,
new_fd, subvolume,
}
}
- return 0;
+ return flags & BTRFS_SNAPSHOT_LOCK_BSD ? TAKE_FD(subvolume_fd) : 0;
fallback_fail:
(void) rm_rf_at(new_fd, subvolume, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE = 1 << 5, /* When we can't create a subvolume, use the FS_IMMUTABLE attribute for indicating read-only */
BTRFS_SNAPSHOT_SIGINT = 1 << 6, /* Check for SIGINT regularly, and return EINTR if seen */
BTRFS_SNAPSHOT_SIGTERM = 1 << 7, /* Ditto, but for SIGTERM */
+ BTRFS_SNAPSHOT_LOCK_BSD = 1 << 8, /* Return a BSD exclusively locked file descriptor referring to snapshot subvolume/directory. */
} BtrfsSnapshotFlags;
typedef enum BtrfsRemoveFlags {
#include "btrfs-util.h"
#include "fd-util.h"
+#include "fs-util.h"
#include "fileio.h"
#include "format-util.h"
#include "log.h"
if (r < 0)
log_error_errno(r, "Failed to make snapshot: %m");
+ r = btrfs_subvol_snapshot_at(AT_FDCWD, "/xxxtest", AT_FDCWD, "/xxxtest4", BTRFS_SNAPSHOT_LOCK_BSD);
+ if (r < 0)
+ log_error_errno(r, "Failed to make snapshot: %m");
+ if (r >= 0)
+ assert_se(xopenat_lock(AT_FDCWD, "/xxxtest4", 0, 0, 0, LOCK_BSD, LOCK_EX|LOCK_NB) == -EAGAIN);
+
+ safe_close(r);
+
r = btrfs_subvol_remove("/xxxtest", BTRFS_REMOVE_QUOTA);
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
if (r < 0)
log_error_errno(r, "Failed to remove subvolume: %m");
+ r = btrfs_subvol_remove("/xxxtest4", BTRFS_REMOVE_QUOTA);
+ if (r < 0)
+ log_error_errno(r, "Failed to remove subvolume: %m");
+
r = btrfs_subvol_snapshot_at(AT_FDCWD, "/etc", AT_FDCWD, "/etc2",
BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_FALLBACK_COPY);
if (r < 0)
if (r < 0)
log_error_errno(r, "Failed to query quota: %m");
- assert_se(quota.referenced_max == 4ULL * 1024 * 1024 * 1024);
+ if (r >= 0)
+ assert_se(quota.referenced_max == 4ULL * 1024 * 1024 * 1024);
r = btrfs_subvol_get_subtree_quota("/xxxquotatest2", 0, "a);
if (r < 0)
log_error_errno(r, "Failed to query quota: %m");
- assert_se(quota.referenced_max == 5ULL * 1024 * 1024 * 1024);
+ if (r >= 0)
+ assert_se(quota.referenced_max == 5ULL * 1024 * 1024 * 1024);
r = btrfs_subvol_remove("/xxxquotatest", BTRFS_REMOVE_QUOTA|BTRFS_REMOVE_RECURSIVE);
if (r < 0)