summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Walters <walters@verbum.org>2014-09-15 09:29:26 -0400
committerColin Walters <walters@verbum.org>2014-09-15 09:29:26 -0400
commite993678c1208c545c0d1014b6a09019841e0dc8d (patch)
treea206df10b53f217b14cc7cf985642c8b41940a82
parentdfeb27eca55d923c57735e491e438ae54f8cc201 (diff)
downloadostree-wip/deploy-inherit-parent-dirs.tar.gz
wip deploy: copy parent dirswip/deploy-inherit-parent-dirs
-rw-r--r--src/libostree/ostree-sysroot-deploy.c206
1 files changed, 144 insertions, 62 deletions
diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c
index ca12a3a9..0d88df0d 100644
--- a/src/libostree/ostree-sysroot-deploy.c
+++ b/src/libostree/ostree-sysroot-deploy.c
@@ -52,6 +52,15 @@ dfd_and_name_get_all_xattrs (int dfd,
cancellable, error);
}
+static int
+xopendirat (int dfd, const char *path, gboolean follow)
+{
+ int flags = O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY;
+ if (!follow)
+ flags |= O_NOFOLLOW;
+ return openat (dfd, path, flags);
+}
+
static gboolean
copy_one_file_fsync_at (int src_parent_dfd,
int dest_parent_dfd,
@@ -156,6 +165,50 @@ copy_one_file_fsync_at (int src_parent_dfd,
}
static gboolean
+dirfd_copy_attributes_and_xattrs (int src_parent_dfd,
+ const char *src_name,
+ int src_dfd,
+ int dest_dfd,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ struct stat src_stbuf;
+ gs_unref_variant GVariant *xattrs = NULL;
+
+ /* Clone all xattrs first, so we get the SELinux security context
+ * right. This will allow other users access if they have ACLs, but
+ * oh well.
+ */
+ if (!dfd_and_name_get_all_xattrs (src_parent_dfd, src_name,
+ &xattrs, cancellable, error))
+ goto out;
+ if (!gs_fd_set_all_xattrs (dest_dfd, xattrs,
+ cancellable, error))
+ goto out;
+
+ if (fstat (src_dfd, &src_stbuf) != 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ if (fchown (dest_dfd, src_stbuf.st_uid, src_stbuf.st_gid) != 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ if (fchmod (dest_dfd, src_stbuf.st_mode) != 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+static gboolean
copy_dir_recurse_fsync (int src_parent_dfd,
int dest_parent_dfd,
const char *name,
@@ -163,14 +216,12 @@ copy_dir_recurse_fsync (int src_parent_dfd,
GError **error)
{
gboolean ret = FALSE;
- struct stat src_stbuf;
int src_dfd = -1;
int dest_dfd = -1;
DIR *srcd = NULL;
struct dirent *dent;
- gs_unref_variant GVariant *xattrs = NULL;
- src_dfd = openat (src_parent_dfd, name, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC);
+ src_dfd = xopendirat (src_parent_dfd, name, TRUE);
if (src_dfd == -1)
{
ot_util_set_error_from_errno (error, errno);
@@ -184,25 +235,17 @@ copy_dir_recurse_fsync (int src_parent_dfd,
goto out;
}
- dest_dfd = openat (dest_parent_dfd, name, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC);
+ dest_dfd = xopendirat (dest_parent_dfd, name, TRUE);
if (dest_dfd == -1)
{
ot_util_set_error_from_errno (error, errno);
goto out;
}
- /* Clone all xattrs first, so we get the SELinux security context
- * right. This will allow other users access if they have ACLs, but
- * oh well.
- */
- if (!dfd_and_name_get_all_xattrs (src_parent_dfd, name,
- &xattrs,
- cancellable, error))
- goto out;
- if (!gs_fd_set_all_xattrs (dest_dfd, xattrs,
- cancellable, error))
+ if (!dirfd_copy_attributes_and_xattrs (src_parent_dfd, name, dest_dfd,
+ cancellable, error))
goto out;
-
+
srcd = fdopendir (src_dfd);
if (!srcd)
{
@@ -241,22 +284,6 @@ copy_dir_recurse_fsync (int src_parent_dfd,
}
}
- if (fstat (src_dfd, &src_stbuf) != 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- if (fchown (dest_dfd, src_stbuf.st_uid, src_stbuf.st_gid) != 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- if (fchmod (dest_dfd, src_stbuf.st_mode) != 0)
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
-
/* And finally, fsync the fd */
if (fsync (dest_dfd) != 0)
{
@@ -279,62 +306,72 @@ copy_dir_recurse_fsync (int src_parent_dfd,
return ret;
}
-/**
- * copy_modified_config_file:
- *
- * Copy @file from @modified_etc to @new_etc, overwriting any existing
- * file there. The @file may refer to a regular file, a symbolic
- * link, or a directory. Directories will be copied recursively.
- *
- * Note this function does not (yet) handle the case where a directory
- * needed by a modified file is deleted in a newer tree.
- */
static gboolean
-copy_modified_config_file (int orig_etc_fd,
- int modified_etc_fd,
- int new_etc_fd,
- const char *path,
- GCancellable *cancellable,
- GError **error)
+ensure_parent_directory (int orig_etc_fd,
+ int modified_etc_fd,
+ int new_etc_fd,
+ const char *path,
+ int *out_dfd,
+ GCancellable *cancellable,
+ GError **error)
{
gboolean ret = FALSE;
- struct stat modified_stbuf;
- struct stat new_stbuf;
const char *parent_slash;
+ const char *name;
gs_free char *parent_path = NULL;
+ int mod_parent_dfd = -1;
int dest_parent_dfd = -1;
-
- if (fstatat (modified_etc_fd, path, &modified_stbuf, AT_SYMLINK_NOFOLLOW) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- g_prefix_error (error, "Failed to read modified config file '%s': ", path);
- goto out;
- }
+ int sub_dest_parent_dfd = -1;
parent_slash = strrchr (path, '/');
if (parent_slash != NULL)
{
+ name = parent_slash + 1;
+ g_assert (*name != '\0');
parent_path = g_strndup (path, parent_slash - path);
- dest_parent_dfd = openat (new_etc_fd, parent_path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW | O_NOCTTY);
+ dest_parent_dfd = xopendirat (new_etc_fd, parent_path, TRUE);
if (dest_parent_dfd == -1)
{
if (errno == ENOENT)
{
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
- "New tree removes parent directory '%s', cannot merge",
- parent_path);
+ if (!ensure_parent_directory (orig_etc_fd, modified_etc_fd, new_etc_fd,
+ parent_path, &sub_dest_parent_dfd, cancellable, error))
+ goto out;
+ mod_parent_dfd = xopendirat (modified_etc_fd, parent_path, TRUE);
+ if (mod_parent_dfd == -1)
+ {
+ g_prefix_error (error, "openat: ");
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ /* Create with mode 0700, we'll fchmod/fchown later */
+ if (mkdirat (sub_dest_parent_dfd, name, 0700) != 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ dest_parent_dfd = xopendirat (new_etc_fd, parent_path, TRUE);
+ if (dest_parent_dfd == -1)
+ {
+ g_prefix_error (error, "openat: ");
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ if (!dirfd_copy_attributes_and_xattrs (modified_etc_fd, parent_path, mod_parent_dfd,
+ dest_parent_dfd, cancellable, error))
+ goto out;
}
else
{
g_prefix_error (error, "openat: ");
ot_util_set_error_from_errno (error, errno);
+ goto out;
}
- goto out;
}
}
else
{
- parent_path = NULL;
dest_parent_dfd = dup (new_etc_fd);
if (dest_parent_dfd == -1)
{
@@ -343,6 +380,51 @@ copy_modified_config_file (int orig_etc_fd,
}
}
+ ret = TRUE;
+ *out_dfd = dest_parent_dfd;
+ dest_parent_dfd = -1
+ out:
+ if (mod_parent_dfd != -1)
+ (void) close (mod_parent_dfd);
+ if (dest_parent_dfd != -1)
+ (void) close (dest_parent_dfd);
+ return ret;
+}
+
+/**
+ * copy_modified_config_file:
+ *
+ * Copy @file from @modified_etc to @new_etc, overwriting any existing
+ * file there. The @file may refer to a regular file, a symbolic
+ * link, or a directory. Directories will be copied recursively.
+ *
+ * Note this function does not (yet) handle the case where a directory
+ * needed by a modified file is deleted in a newer tree.
+ */
+static gboolean
+copy_modified_config_file (int orig_etc_fd,
+ int modified_etc_fd,
+ int new_etc_fd,
+ const char *path,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ struct stat modified_stbuf;
+ struct stat new_stbuf;
+ int dest_parent_dfd = -1;
+
+ if (fstatat (modified_etc_fd, path, &modified_stbuf, AT_SYMLINK_NOFOLLOW) < 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ g_prefix_error (error, "Failed to read modified config file '%s': ", path);
+ goto out;
+ }
+
+ if (!ensure_parent_directory (orig_etc_fd, modified_etc_fd, new_etc_fd,
+ path, &dest_parent_dfd, cancellable, error))
+ goto out;
+
if (fstatat (new_etc_fd, path, &new_stbuf, AT_SYMLINK_NOFOLLOW) < 0)
{
if (errno == ENOENT)