From d1950da1a04f7f501bc9a3432f8732d681b858e5 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 2 Dec 2011 11:22:32 -0500 Subject: [PATCH] core: Further unify API to create files We now have just one place which writes to the filesystem. Wrap a temporary file allocation API on top of that. --- src/libostree/ostree-core.c | 210 +++++++++++++++++++++++++++++++++-- src/libostree/ostree-core.h | 21 ++++ src/libostree/ostree-repo.c | 12 +- src/libotutil/ot-gio-utils.c | 129 --------------------- src/libotutil/ot-gio-utils.h | 18 --- 5 files changed, 224 insertions(+), 166 deletions(-) diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c index da84fec5..8e6b8540 100644 --- a/src/libostree/ostree-core.c +++ b/src/libostree/ostree-core.c @@ -881,6 +881,7 @@ ostree_create_file_from_input (GFile *dest_file, GFileInfo *finfo, GVariant *xattrs, GInputStream *input, + OstreeObjectType objtype, GChecksum **out_checksum, GCancellable *cancellable, GError **error) @@ -891,14 +892,22 @@ ostree_create_file_from_input (GFile *dest_file, guint32 uid, gid, mode; GChecksum *ret_checksum = NULL; - uid = g_file_info_get_attribute_uint32 (finfo, "unix::uid"); - gid = g_file_info_get_attribute_uint32 (finfo, "unix::gid"); - mode = g_file_info_get_attribute_uint32 (finfo, "unix::mode"); + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + + if (finfo != NULL) + { + mode = g_file_info_get_attribute_uint32 (finfo, "unix::mode"); + } + else + { + mode = S_IFREG | 0666; + } dest_path = ot_gfile_get_path_cached (dest_file); if (S_ISREG (mode)) { - out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error); + out = g_file_create (dest_file, 0, cancellable, error); if (!out) goto out; @@ -913,6 +922,7 @@ ostree_create_file_from_input (GFile *dest_file, else if (S_ISLNK (mode)) { const char *target = g_file_info_get_attribute_byte_string (finfo, "standard::symlink-target"); + g_assert (objtype == OSTREE_OBJECT_TYPE_FILE); if (ret_checksum) g_checksum_update (ret_checksum, (guint8*)target, strlen (target)); if (symlink (target, dest_path) < 0) @@ -924,6 +934,7 @@ ostree_create_file_from_input (GFile *dest_file, else if (S_ISCHR (mode) || S_ISBLK (mode)) { guint32 dev = g_file_info_get_attribute_uint32 (finfo, "unix::rdev"); + g_assert (objtype == OSTREE_OBJECT_TYPE_FILE); if (ret_checksum) g_checksum_update (ret_checksum, (guint8*)&dev, 4); if (mknod (dest_path, mode, dev) < 0) @@ -934,6 +945,7 @@ ostree_create_file_from_input (GFile *dest_file, } else if (S_ISFIFO (mode)) { + g_assert (objtype == OSTREE_OBJECT_TYPE_FILE); if (mkfifo (dest_path, mode) < 0) { ot_util_set_error_from_errno (error, errno); @@ -947,10 +959,21 @@ ostree_create_file_from_input (GFile *dest_file, goto out; } - if (lchown (dest_path, uid, gid) < 0) + if (finfo != NULL) { - ot_util_set_error_from_errno (error, errno); - goto out; + uid = g_file_info_get_attribute_uint32 (finfo, "unix::uid"); + gid = g_file_info_get_attribute_uint32 (finfo, "unix::gid"); + + if (lchown (dest_path, uid, gid) < 0) + { + ot_util_set_error_from_errno (error, errno); + goto out; + } + } + else + { + uid = geteuid (); + gid = getegid (); } if (!S_ISLNK (mode)) @@ -962,13 +985,18 @@ ostree_create_file_from_input (GFile *dest_file, } } - if (!ostree_set_xattrs (dest_file, xattrs, cancellable, error)) - goto out; + if (xattrs != NULL) + { + g_assert (objtype == OSTREE_OBJECT_TYPE_FILE); + if (!ostree_set_xattrs (dest_file, xattrs, cancellable, error)) + goto out; + } - if (ret_checksum) + if (ret_checksum && objtype == OSTREE_OBJECT_TYPE_FILE) { ostree_checksum_update_stat (ret_checksum, uid, gid, mode); - g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs)); + if (xattrs) + g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs)); } ret = TRUE; @@ -985,6 +1013,165 @@ ostree_create_file_from_input (GFile *dest_file, return ret; } +static GString * +create_tmp_string (const char *dirpath, + const char *prefix, + const char *suffix) +{ + GString *tmp_name = NULL; + + if (!prefix) + prefix = "tmp-"; + if (!suffix) + suffix = ".tmp"; + + tmp_name = g_string_new (dirpath); + g_string_append_c (tmp_name, '/'); + g_string_append (tmp_name, prefix); + g_string_append (tmp_name, "XXXXXX"); + g_string_append (tmp_name, suffix); + + return tmp_name; +} + +static char * +subst_xxxxxx (GRand *rand, + const char *string) +{ + static const char table[] = "ABCEDEFGHIJKLMNOPQRSTUVWXYZabcedefghijklmnopqrstuvwxyz0123456789"; + char *ret = g_strdup (string); + guint8 *xxxxxx = (guint8*)strstr (ret, "XXXXXX"); + + g_assert (xxxxxx != NULL); + + while (*xxxxxx == 'X') + { + int offset = g_random_int_range (0, sizeof (table)); + *xxxxxx = (guint8)table[offset]; + xxxxxx++; + } + + return ret; +} + +gboolean +ostree_create_temp_file_from_input (GFile *dir, + const char *prefix, + const char *suffix, + GFileInfo *finfo, + GVariant *xattrs, + GInputStream *input, + OstreeObjectType objtype, + GFile **out_file, + GChecksum **out_checksum, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + GChecksum *ret_checksum = NULL; + GRand *rand = NULL; + GString *tmp_name = NULL; + char *possible_name = NULL; + GFile *possible_file = NULL; + GError *temp_error = NULL; + int i = 0; + + rand = g_rand_new (); + + tmp_name = create_tmp_string (ot_gfile_get_path_cached (dir), + prefix, suffix); + + /* 128 attempts seems reasonable... */ + for (i = 0; i < 128; i++) + { + g_free (possible_name); + possible_name = subst_xxxxxx (rand, tmp_name->str); + g_clear_object (&possible_file); + possible_file = ot_gfile_new_for_path (possible_name); + + if (!ostree_create_file_from_input (possible_file, finfo, xattrs, input, + objtype, + out_checksum ? &ret_checksum : NULL, + cancellable, &temp_error)) + { + if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_EXISTS)) + { + g_clear_error (&temp_error); + continue; + } + else + { + g_propagate_error (error, temp_error); + goto out; + } + } + else + { + break; + } + } + if (i == 128) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Exhausted 128 attempts to create a temporary file"); + goto out; + } + + ret = TRUE; + if (out_checksum) + { + *out_checksum = ret_checksum; + ret_checksum = NULL; + } + if (out_file) + { + *out_file = possible_file; + possible_file = NULL; + } + out: + if (rand) + g_rand_free (rand); + if (tmp_name) + g_string_free (tmp_name, TRUE); + g_free (possible_name); + g_clear_object (&possible_file); + ot_clear_checksum (&ret_checksum); + return ret; +} + +gboolean +ostree_create_temp_regular_file (GFile *dir, + const char *prefix, + const char *suffix, + GFile **out_file, + GOutputStream **out_stream, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + GFile *ret_file = NULL; + GOutputStream *ret_stream = NULL; + + if (!ostree_create_temp_file_from_input (dir, prefix, suffix, NULL, NULL, NULL, + OSTREE_OBJECT_TYPE_FILE, &ret_file, + NULL, cancellable, error)) + goto out; + + ret_stream = (GOutputStream*)g_file_append_to (ret_file, 0, cancellable, error); + if (ret_stream == NULL) + goto out; + + ret = TRUE; + *out_file = ret_file; + ret_file = NULL; + *out_stream = ret_stream; + ret_stream = NULL; + out: + g_clear_object (&ret_file); + g_clear_object (&ret_stream); + return ret; +} + static gboolean unpack_file (GFile *file, GFile *dest_file, @@ -1003,6 +1190,7 @@ unpack_file (GFile *file, goto out; if (!ostree_create_file_from_input (dest_file, finfo, xattrs, in, + OSTREE_OBJECT_TYPE_FILE, out_checksum ? &ret_checksum : NULL, cancellable, error)) goto out; diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h index 64bfca77..454889fc 100644 --- a/src/libostree/ostree-core.h +++ b/src/libostree/ostree-core.h @@ -166,10 +166,31 @@ gboolean ostree_create_file_from_input (GFile *file, GFileInfo *finfo, GVariant *xattrs, GInputStream *input, + OstreeObjectType objtype, GChecksum **out_checksum, GCancellable *cancellable, GError **error); +gboolean ostree_create_temp_file_from_input (GFile *dir, + const char *prefix, + const char *suffix, + GFileInfo *finfo, + GVariant *xattrs, + GInputStream *input, + OstreeObjectType objtype, + GFile **out_file, + GChecksum **out_checksum, + GCancellable *cancellable, + GError **error); + +gboolean ostree_create_temp_regular_file (GFile *dir, + const char *prefix, + const char *suffix, + GFile **out_file, + GOutputStream **out_stream, + GCancellable *cancellable, + GError **error); + gboolean ostree_parse_packed_file (GFile *file, GFileInfo **out_file_info, GVariant **out_xattrs, diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index e4a6c928..0beef27f 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -592,14 +592,10 @@ stage_and_checksum (OstreeRepo *self, break; } - if (!ot_gfile_create_tmp (priv->tmp_dir, prefix, NULL, 0666, - &tmp_f, &stream, cancellable, error)) - goto out; - - if (!ot_gio_splice_and_checksum (stream, input, &ret_checksum, cancellable, error)) - goto out; - - if (!g_output_stream_close ((GOutputStream*)stream, NULL, error)) + if (!ostree_create_temp_file_from_input (priv->tmp_dir, prefix, NULL, NULL, + NULL, input, OSTREE_OBJECT_TYPE_META, + &tmp_f, &ret_checksum, + cancellable, error)) goto out; ret_tmpname = g_file_get_child (priv->tmp_dir, g_checksum_get_string (ret_checksum)); diff --git a/src/libotutil/ot-gio-utils.c b/src/libotutil/ot-gio-utils.c index 0e8f4c2c..ed730e77 100644 --- a/src/libotutil/ot-gio-utils.c +++ b/src/libotutil/ot-gio-utils.c @@ -196,135 +196,6 @@ ot_gio_splice_and_checksum (GOutputStream *out, return ret; } -static GString * -create_tmp_string (const char *dirpath, - const char *prefix, - const char *suffix) -{ - GString *tmp_name = NULL; - - if (!prefix) - prefix = "tmp-"; - if (!suffix) - suffix = ".tmp"; - - tmp_name = g_string_new (dirpath); - g_string_append_c (tmp_name, '/'); - g_string_append (tmp_name, prefix); - g_string_append (tmp_name, "XXXXXX"); - g_string_append (tmp_name, suffix); - - return tmp_name; -} - -gboolean -ot_gfile_create_tmp (GFile *dir, - const char *prefix, - const char *suffix, - int mode, - GFile **out_file, - GOutputStream **out_stream, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - GString *tmp_name = NULL; - int tmpfd = -1; - GFile *ret_file = NULL; - GOutputStream *ret_stream = NULL; - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return FALSE; - - tmp_name = create_tmp_string (ot_gfile_get_path_cached (dir), prefix, suffix); - - tmpfd = g_mkstemp_full (tmp_name->str, O_WRONLY | O_BINARY, mode); - if (tmpfd == -1) - { - ot_util_set_error_from_errno (error, errno); - goto out; - } - - ret_file = ot_gfile_new_for_path (tmp_name->str); - ret_stream = g_unix_output_stream_new (tmpfd, TRUE); - - ret = TRUE; - if (out_file) - { - *out_file = ret_file; - ret_file = NULL; - } - if (out_stream) - { - *out_stream = ret_stream; - ret_stream = NULL; - } - out: - g_clear_object (&ret_file); - g_clear_object (&ret_stream); - g_string_free (tmp_name, TRUE); - return ret; -} - -static char * -subst_xxxxxx (GRand *rand, - const char *string) -{ - char *ret = g_strdup (string); - guint8 *xxxxxx = (guint8*)strstr (ret, "XXXXXX"); - - g_assert (xxxxxx != NULL); - - while (*xxxxxx == 'X') - { - *xxxxxx = (guint8)g_random_int_range (0, 255); - xxxxxx++; - } - - return ret; -} - -gboolean -ot_gfile_create_tmp_symlink (const char *target, - GFile *dir, - const char *prefix, - const char *suffix, - GFile **out_file, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - GRand *rand = NULL; - GString *tmp_name = NULL; - char *possible_name = NULL; - - rand = g_rand_new (); - - tmp_name = create_tmp_string (ot_gfile_get_path_cached (dir), - prefix, suffix); - - while (TRUE) - { - g_free (possible_name); - possible_name = subst_xxxxxx (rand, tmp_name->str); - if (symlink (target, possible_name) < 0) - { - if (errno == EEXIST) - continue; - ot_util_set_error_from_errno (error, errno); - goto out; - } - } - - *out_file = ot_gfile_new_for_path (possible_name); - out: - g_string_free (tmp_name, TRUE); - g_free (possible_name); - if (rand) - g_rand_free (rand); - return ret; -} - gboolean ot_gfile_merge_dirs (GFile *destination, GFile *src, diff --git a/src/libotutil/ot-gio-utils.h b/src/libotutil/ot-gio-utils.h index de131df6..a8cc080a 100644 --- a/src/libotutil/ot-gio-utils.h +++ b/src/libotutil/ot-gio-utils.h @@ -51,24 +51,6 @@ gboolean ot_gio_splice_and_checksum (GOutputStream *out, GCancellable *cancellable, GError **error); - -gboolean ot_gfile_create_tmp (GFile *dir, - const char *prefix, - const char *suffix, - int mode, - GFile **out_file, - GOutputStream **out_stream, - GCancellable *cancellable, - GError **error); - -gboolean ot_gfile_create_tmp_symlink (const char *target, - GFile *dir, - const char *prefix, - const char *suffix, - GFile **out_file, - GCancellable *cancellable, - GError **error); - gboolean ot_gfile_merge_dirs (GFile *destination, GFile *src, GCancellable *cancellable,