struct btrfs_ioctl_vol_args vol_args = {};
_cleanup_close_ int subvol_fd = -1;
struct stat st;
+ bool made_writable = false;
int r;
assert(fd >= 0);
if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
return -errno;
+ if (!made_writable) {
+ r = btrfs_subvol_set_read_only_fd(subvol_fd, false);
+ if (r < 0)
+ return r;
+
+ made_writable = true;
+ }
+
if (isempty(ino_args.name))
/* Subvolume is in the top-level
* directory of the subvolume. */
.fd = old_fd,
};
int r;
+ _cleanup_close_ int subvolume_fd = -1;
assert(old_fd >= 0);
assert(new_fd >= 0);
if (new_child_fd < 0)
return -errno;
+ if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
+ /* If the snapshot is read-only we
+ * need to mark it writable
+ * temporarily, to put the subsnapshot
+ * into place. */
+
+ if (subvolume_fd < 0) {
+ subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (subvolume_fd < 0)
+ return -errno;
+ }
+
+ r = btrfs_subvol_set_read_only_fd(subvolume_fd, false);
+ if (r < 0)
+ return r;
+ }
+
/* When btrfs clones the subvolumes, child
* subvolumes appear as directories. Remove
* them, so that we can create a new snapshot
* in their place */
- if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0)
- return -errno;
+ if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) {
+ int k = -errno;
+
+ if (flags & BTRFS_SNAPSHOT_READ_ONLY)
+ (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true);
+
+ return k;
+ }
r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY);
+
+ /* Restore the readonly flag */
+ if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
+ int k;
+
+ k = btrfs_subvol_set_read_only_fd(subvolume_fd, true);
+ if (r >= 0 && k < 0)
+ return k;
+ }
+
if (r < 0)
return r;
}