[hacktree/wip/import: 2/3] wip import
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [hacktree/wip/import: 2/3] wip import
- Date: Fri, 14 Oct 2011 14:21:49 +0000 (UTC)
commit c7969ce318f01fc4416e80238b86c2f529f4c67e
Author: Colin Walters <walters verbum org>
Date: Thu Oct 13 17:11:01 2011 -0400
wip import
TODO | 4 +-
src/libhacktree/hacktree-core.c | 73 +++++++++--
src/libhacktree/hacktree-core.h | 43 ++++++-
src/libhacktree/hacktree-repo.c | 264 ++++++++++++++++++++++++++++++++++++++-
src/libhacktree/hacktree-repo.h | 16 +++
src/libhtutil/ht-unix-utils.c | 63 +++++++++
src/libhtutil/ht-unix-utils.h | 6 +
7 files changed, 452 insertions(+), 17 deletions(-)
---
diff --git a/TODO b/TODO
index aeff7f7..dd4bc1d 100644
--- a/TODO
+++ b/TODO
@@ -1,5 +1,5 @@
-* hacktree import-tar
- - Accepts tarball on stdin, stores the objects
* tree and commit objects
* hacktree import
+* hacktree import-tar
+ - Accepts tarball on stdin, stores the objects
* branches
diff --git a/src/libhacktree/hacktree-core.c b/src/libhacktree/hacktree-core.c
index ddda1b5..a499985 100644
--- a/src/libhacktree/hacktree-core.c
+++ b/src/libhacktree/hacktree-core.c
@@ -27,7 +27,10 @@
char *
stat_to_string (struct stat *stbuf)
{
- return g_strdup_printf ("%d:%d:%d", stbuf->st_mode, stbuf->st_uid, stbuf->st_gid);
+ return g_strdup_printf ("%u:%u:%u",
+ (guint32)(stbuf->st_mode & ~S_IFMT),
+ (guint32)stbuf->st_uid,
+ (guint32)stbuf->st_gid);
}
static char *
@@ -56,6 +59,52 @@ canonicalize_xattrs (char *xattr_string, size_t len)
}
gboolean
+hacktree_get_xattrs_for_directory (const char *path,
+ char **out_xattrs, /* out */
+ gsize *out_len, /* out */
+ GError **error)
+{
+ gboolean ret = FALSE;
+ char *xattrs = NULL;
+ ssize_t bytes_read;
+
+ bytes_read = llistxattr (path, NULL, 0);
+
+ if (bytes_read < 0)
+ {
+ if (errno != ENOTSUP)
+ {
+ ht_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ else if (bytes_read > 0)
+ {
+ xattrs = g_malloc (bytes_read);
+ if (!llistxattr (path, xattrs, bytes_read))
+ {
+ ht_util_set_error_from_errno (error, errno);
+ g_free (xattrs);
+ xattrs = NULL;
+ goto out;
+ }
+
+ *out_xattrs = canonicalize_xattrs (xattrs, bytes_read);
+ *out_len = (gsize)bytes_read;
+ }
+ else
+ {
+ *out_xattrs = NULL;
+ *out_len = 0;
+ }
+
+ ret = TRUE;
+ out:
+ g_free (xattrs);
+ return ret;
+}
+
+gboolean
hacktree_stat_and_checksum_file (int dir_fd, const char *path,
GChecksum **out_checksum,
struct stat *out_stbuf,
@@ -73,6 +122,7 @@ hacktree_stat_and_checksum_file (int dir_fd, const char *path,
gboolean ret = FALSE;
char *symlink_target = NULL;
char *device_id = NULL;
+ struct stat stbuf;
basename = g_path_get_basename (path);
@@ -89,13 +139,13 @@ hacktree_stat_and_checksum_file (int dir_fd, const char *path,
dir_fd = dirfd (temp_dir);
}
- if (fstatat (dir_fd, basename, out_stbuf, AT_SYMLINK_NOFOLLOW) < 0)
+ if (fstatat (dir_fd, basename, &stbuf, AT_SYMLINK_NOFOLLOW) < 0)
{
ht_util_set_error_from_errno (error, errno);
goto out;
}
- if (!S_ISLNK(out_stbuf->st_mode))
+ if (!S_ISLNK(&stbuf->st_mode))
{
fd = ht_util_open_file_read_at (dir_fd, basename, error);
if (fd < 0)
@@ -105,10 +155,10 @@ hacktree_stat_and_checksum_file (int dir_fd, const char *path,
}
}
- stat_string = stat_to_string (out_stbuf);
+ stat_string = stat_to_string (&stbuf);
/* FIXME - Add llistxattrat */
- if (!S_ISLNK(out_stbuf->st_mode))
+ if (!S_ISLNK(&stbuf->st_mode))
bytes_read = flistxattr (fd, NULL, 0);
else
bytes_read = llistxattr (path, NULL, 0);
@@ -121,12 +171,12 @@ hacktree_stat_and_checksum_file (int dir_fd, const char *path,
goto out;
}
}
- if (errno != ENOTSUP)
+ else if (bytes_read > 0)
{
gboolean tmp;
xattrs = g_malloc (bytes_read);
/* FIXME - Add llistxattrat */
- if (!S_ISLNK(out_stbuf->st_mode))
+ if (!S_ISLNK(&stbuf->st_mode))
tmp = flistxattr (fd, xattrs, bytes_read);
else
tmp = llistxattr (path, xattrs, bytes_read);
@@ -142,7 +192,7 @@ hacktree_stat_and_checksum_file (int dir_fd, const char *path,
content_sha256 = g_checksum_new (G_CHECKSUM_SHA256);
- if (S_ISREG(out_stbuf->st_mode))
+ if (S_ISREG(&stbuf->st_mode))
{
guint8 buf[8192];
@@ -154,7 +204,7 @@ hacktree_stat_and_checksum_file (int dir_fd, const char *path,
goto out;
}
}
- else if (S_ISLNK(out_stbuf->st_mode))
+ else if (S_ISLNK(&stbuf->st_mode))
{
symlink_target = g_malloc (PATH_MAX);
@@ -165,9 +215,9 @@ hacktree_stat_and_checksum_file (int dir_fd, const char *path,
}
g_checksum_update (content_sha256, symlink_target, strlen (symlink_target));
}
- else if (S_ISCHR(out_stbuf->st_mode) || S_ISBLK(out_stbuf->st_mode))
+ else if (S_ISCHR(&stbuf->st_mode) || S_ISBLK(&stbuf->st_mode))
{
- device_id = g_strdup_printf ("%d", out_stbuf->st_rdev);
+ device_id = g_strdup_printf ("%d", &stbuf->st_rdev);
g_checksum_update (content_sha256, device_id, strlen (device_id));
}
else
@@ -184,6 +234,7 @@ hacktree_stat_and_checksum_file (int dir_fd, const char *path,
g_checksum_update (content_and_meta_sha256, stat_string, strlen (stat_string));
g_checksum_update (content_and_meta_sha256, xattrs_canonicalized, strlen (stat_string));
+ *out_stbuf = stbuf;
*out_checksum = content_and_meta_sha256;
ret = TRUE;
out:
diff --git a/src/libhacktree/hacktree-core.h b/src/libhacktree/hacktree-core.h
index 124a90a..cc10926 100644
--- a/src/libhacktree/hacktree-core.h
+++ b/src/libhacktree/hacktree-core.h
@@ -18,7 +18,6 @@
*
* Author: Colin Walters <walters verbum org>
*/
-/* hacktree-repo.h */
#ifndef _HACKTREE_CORE
#define _HACKTREE_CORE
@@ -27,6 +26,48 @@
G_BEGIN_DECLS
+#define HACKTREE_EMPTY_STRING_SHA256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
+
+typedef enum {
+ HACKTREE_SERIALIZED_TREE_VARIANT = 1,
+ HACKTREE_SERIALIZED_COMMIT_VARIANT = 2
+ HACKTREE_SERIALIZED_XATTRS_VARIANT = 3
+} HacktreeSerializedVariantType;
+
+#define HACKTREE_SERIALIZED_VARIANT_FORMAT G_VARIANT_TYPE("(uv)")
+
+/*
+ * Tree objects:
+ * u - Version
+ * a{sv} - Metadata
+ * a(ss) - array of (checksum, filename) for files
+ * as - array of tree checksums for directories
+ * a(ssuay) - array of (dirname, uid, gid, mode, xattr_checksum) for directories
+ */
+#define HACKTREE_TREE_GVARIANT_FORMAT G_VARIANT_TYPE("(ua{sv}a(ss)asa(suuus)")
+/*
+ * Commit objects:
+ * u - Version
+ * a{sv} - Metadata
+ * s - subject
+ * s - body
+ * t - Timestamp in seconds since the epoch (UTC)
+ * s - Tree SHA256
+ */
+#define HACKTREE_COMMIT_GVARIANT_FORMAT G_VARIANT_TYPE("(ua{sv}ssts)")
+
+/*
+ * xattr objects:
+ * u - Version
+ * ay - data
+ */
+#define HACKTREE_XATTR_GVARIANT_FORMAT G_VARIANT_TYPE("(uay)")
+
+gboolean hacktree_get_xattrs_for_directory (const char *path,
+ char **out_xattrs,
+ gsize *out_len,
+ GError **error);
+
gboolean hacktree_stat_and_checksum_file (int dirfd, const char *path,
GChecksum **out_checksum,
struct stat *out_stbuf,
diff --git a/src/libhacktree/hacktree-repo.c b/src/libhacktree/hacktree-repo.c
index 7f6fce0..11e64b4 100644
--- a/src/libhacktree/hacktree-repo.c
+++ b/src/libhacktree/hacktree-repo.c
@@ -39,6 +39,7 @@ typedef struct _HacktreeRepoPrivate HacktreeRepoPrivate;
struct _HacktreeRepoPrivate {
char *path;
+ char *index_path;
char *objects_path;
gboolean inited;
@@ -51,6 +52,7 @@ hacktree_repo_finalize (GObject *object)
HacktreeRepoPrivate *priv = GET_PRIVATE (self);
g_free (priv->path);
+ g_free (priv->index_path);
g_free (priv->objects_path);
G_OBJECT_CLASS (hacktree_repo_parent_class)->finalize (object);
@@ -113,6 +115,7 @@ hacktree_repo_constructor (GType gtype,
g_assert (priv->path != NULL);
priv->objects_path = g_build_filename (priv->path, HACKTREE_REPO_DIR, "objects", NULL);
+ priv->index_path = g_build_filename (priv->path, HACKTREE_REPO_DIR, "index", NULL);
return object;
}
@@ -142,7 +145,6 @@ static void
hacktree_repo_init (HacktreeRepo *self)
{
HacktreeRepoPrivate *priv = GET_PRIVATE (self);
-
}
HacktreeRepo*
@@ -167,6 +169,121 @@ hacktree_repo_check (HacktreeRepo *self, GError **error)
return TRUE;
}
+static gboolean
+import_gvariant (HacktreeRepo *repo,
+ HacktreeSerializedVariantType type,
+ GVariant *variant,
+ GChecksum **out_checksum,
+ GError **error)
+{
+ HacktreeRepoPrivate *priv = GET_PRIVATE (self);
+ GVariantBuilder builder;
+ GVariant *serialized = NULL;
+ struct stat stbuf;
+ gboolean ret = FALSE;
+ gsize i, n_items;
+ gsize size;
+ char *tmp_name = NULL;
+ int fd = -1;
+ GUnixOutputStream *stream = NULL;
+
+ g_variant_builder_init (&builder, HACKTREE_SERIALIZED_VARIANT_FORMAT);
+ g_variant_builder_add (&builder, "uv", (guint32)type, variant);
+ serialized = g_variant_builder_end (&builder);
+
+ tmp_name = g_build_filename (priv->objects_path, "variant-XXXXXX.tmp", NULL);
+ fd = mkstemp (tmp_name);
+ if (fd < 0)
+ {
+ ht_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ stream = g_unix_output_stream_new (fd, FALSE);
+ if (!g_output_stream_write_all ((GOutputStream*)stream,
+ g_variant_get_data (serialized),
+ g_variant_get_size (serialized),
+ NULL,
+ error))
+ goto out;
+ if (!g_output_stream_close ((GOutputStream*)stream,
+ NULL, error))
+ goto out;
+
+ if (!link_one_file (repo, tmp_name, FALSE, FALSE, out_checksum, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ /* Unconditionally unlink; if we suceeded, there's a new link, if not, clean up. */
+ (void) unlink (tmp_name);
+ if (fd != -1)
+ close (fd);
+ if (serialized != NULL)
+ g_variant_unref (serialized);
+ g_free (tmp_name);
+ g_clear_object (&stream);
+ return ret;
+}
+
+static GVariant *
+import_directory (HacktreeRepo *self,
+ const char *path,
+ GError **error)
+{
+ GVariant *ret = NULL;
+ struct stat stbuf;
+ char *basename = NULL;
+ GChecksum *xattr_checksum = NULL;
+ const char *xattr_checksum_string;
+ GVariant *xattr_variant = NULL;
+ char *xattrs = NULL;
+ gsize xattr_len;
+
+ if (lstat (path, &stbuf) < 0)
+ {
+ ht_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ if (!S_ISDIR(stbuf->st_mode))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Not a directory: '%s'", path);
+ goto out;
+ }
+
+ if (!hacktree_get_xattrs_for_directory (path, &xattrs, &xattr_len, error))
+ goto out;
+
+ if (xattrs != NULL)
+ {
+ xattr_variant = g_variant_new_fixed_array (G_VARIANT_TYPE ("y"),
+ xattrs, xattr_len, 1);
+ g_variant_ref_sink (xattr_variant);
+ if (!import_gvariant (self, HACKTREE_SERIALIZED_XATTRS_VARIANT, xattr_variant, &xattr_checksum, error))
+ goto out;
+ xattr_checksum_string = g_checksum_get_string (xattr_checksum);
+ }
+ else
+ xattr_checksum_string = HACKTREE_EMPTY_STRING_SHA256;
+
+ basename = g_path_get_basename (path);
+
+ ret = g_variant_new (G_VARIANT_TYPE ("(suuus)"),
+ basename,
+ (guint32)stbuf.st_uid,
+ (guint32)stbuf.st_gid,
+ (guint32)(stbuf.st_mode & ~S_IFMT),
+ xattr_checksum_string);
+
+ out:
+ if (xattr_checksum != NULL)
+ g_checksum_free (xattr_checksum);
+ g_free (xattrs);
+ g_free (basename);
+ return ret;
+}
static char *
prepare_dir_for_checksum_get_object_path (HacktreeRepo *self,
@@ -195,7 +312,9 @@ prepare_dir_for_checksum_get_object_path (HacktreeRepo *self,
static gboolean
link_one_file (HacktreeRepo *self, const char *path,
- gboolean ignore_exists, gboolean force, GError **error)
+ gboolean ignore_exists, gboolean force,
+ GChecksum **out_checksum,
+ GError **error)
{
HacktreeRepoPrivate *priv = GET_PRIVATE (self);
char *src_basename = NULL;
@@ -265,6 +384,8 @@ link_one_file (HacktreeRepo *self, const char *path,
(void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
}
+ *out_checksum = id;
+ id = NULL;
ret = TRUE;
out:
if (id != NULL)
@@ -289,10 +410,147 @@ hacktree_repo_link_file (HacktreeRepo *self,
GError **error)
{
HacktreeRepoPrivate *priv = GET_PRIVATE (self);
+ GChecksum *checksum = NULL;
+
+ g_return_val_if_fail (priv->inited, FALSE);
+
+ if (!link_one_file (self, path, ignore_exists, force, &checksum, error))
+ return FALSE;
+ g_checksum_free (checksum);
+ return TRUE;
+}
+
+typedef struct {
+ GChecksum *checksum;
+ struct stat stbuf;
+} CommitData;
+
+static void
+commit_data_free (CommitData *data)
+{
+ if (!data)
+ return;
+ g_checksum_free (data->checksum);
+}
+
+gboolean
+hacktree_repo_commit_files (HacktreeRepo *repo,
+ const char *subject,
+ const char *body,
+ GVariant *metadata,
+ const char *base,
+ GPtrArray *files,
+ GError **error)
+{
+ char *abspath = NULL;
+ gboolean ret = FALSE;
+ int i;
+ int current_tree_depth = -1;
+ GPtrArray *sorted_files = NULL;
+ GHashTable *filename_to_commit_data = NULL;
+
+ filename_to_commit_data = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify)commit_data_free);
+ sorted_files = ht_util_sort_filenames_by_component_length (files);
+
+ for (i = 0; i < sorted_files->len; i++)
+ {
+ const char *filename = files->pdata[i];
+ CommitData *data = NULL;
+ int n_components;
+
+ if (ht_util_filename_has_dotdot (filename))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Path uplink '..' in filename '%s' not allowed (yet)", filename);
+ goto out;
+ }
+
+ if (g_path_is_absolute (filename))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Abolute filename '%s' not allowed (yet)", filename);
+ goto out;
+ }
+
+ n_components = ht_util_count_filename_components (filename);
+ if (current_tree_depth == -1)
+ current_tree_depth = n_components;
+ else if (n_components < current_tree_depth)
+ {
+
+ }
+
+ g_free (abspath);
+ abspath = g_build_filename (base, filename, NULL);
+ commitdata = g_new0 (CommitData, 1);
+
+ /* Takes ownership of commitdata */
+ g_hash_table_insert (filename_to_checksum, filename, commitdata);
+
+ if (lstat (abspath, &commitdata->stbuf) < 0)
+ {
+ ht_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ if (S_ISDIR (stbuf.st_mode))
+ {
+ GVariant *dirdata;
+
+ dirdata = create_directory_data (abspath, error);
+ if (!dirdata)
+ goto out;
+ g_variant_ref_sink (dirdata);
+
+ if (!import_gvariant (self, dirdata, error))
+ {
+ g_variant_unref (dirdata);
+ goto out;
+ }
+ g_variant_unref (dirdata);
+ }
+ else
+ {
+ if (!link_one_file (self, abspath, TRUE, FALSE, error))
+ goto out;
+ }
+ }
+
+ out:
+ if (sorted_files != NULL)
+ g_ptr_array_free (sorted_files);
+ if (filename_to_checksum != NULL)
+ g_hash_table_destroy (filename_to_checksum);
+ g_free (abspath);
+ return ret;
+}
+
+
+gboolean
+hacktree_repo_import_tree (HacktreeRepo *self,
+ GVariant *variant,
+ GError **error)
+{
+ HacktreeRepoPrivate *priv = GET_PRIVATE (self);
+
+ g_return_val_if_fail (priv->inited, FALSE);
+ g_return_val_if_fail (g_variant_is_of_type (variant, HACKTREE_TREE_GVARIANT_FORMAT), FALSE);
+
+ return import_gvariant (self, tree_variant, error);
+}
+
+gboolean
+hacktree_repo_import_commit (HacktreeRepo *self,
+ GVariant *variant,
+ GError **error)
+{
+ HacktreeRepoPrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (priv->inited, FALSE);
+ g_return_val_if_fail (g_variant_is_of_type (variant, HACKTREE_COMMIT_GVARIANT_FORMAT), FALSE);
- return link_one_file (self, path, ignore_exists, force, error);
+ return import_gvariant (self, variant, error);
}
static gboolean
diff --git a/src/libhacktree/hacktree-repo.h b/src/libhacktree/hacktree-repo.h
index 633771c..0c97422 100644
--- a/src/libhacktree/hacktree-repo.h
+++ b/src/libhacktree/hacktree-repo.h
@@ -59,6 +59,22 @@ gboolean hacktree_repo_link_file (HacktreeRepo *repo,
gboolean force,
GError **error);
+gboolean hacktree_repo_commit_files (HacktreeRepo *repo,
+ const char *subject,
+ const char *body,
+ GVariant *metadata,
+ const char *base,
+ GPtrArray *files,
+ GError **error);
+
+gboolean hacktree_repo_import_tree (HacktreeRepo *repo,
+ GVariant *tree_variant,
+ GError **error);
+
+gboolean hacktree_repo_import_commit (HacktreeRepo *repo,
+ GVariant *commit_variant,
+ GError **error);
+
typedef void (*HacktreeRepoObjectIter) (HacktreeRepo *repo, const char *path,
GFileInfo *fileinfo, gpointer user_data);
diff --git a/src/libhtutil/ht-unix-utils.c b/src/libhtutil/ht-unix-utils.c
index 4637c7d..10c38df 100644
--- a/src/libhtutil/ht-unix-utils.c
+++ b/src/libhtutil/ht-unix-utils.c
@@ -30,6 +30,69 @@
#include <sys/types.h>
#include <dirent.h>
+static int
+compare_filenames_by_component_length (const char *a,
+ const char *b)
+{
+ char *a_slash, *b_slash;
+
+ a_slash = strchr (a, '/');
+ b_slash = strchr (b, '/');
+ while (a_slash && b_slash)
+ {
+ a = a_slash + 1;
+ b = b_slash + 1;
+ a_slash = strchr (a, '/');
+ b_slash = strchr (b, '/');
+ }
+ if (a_slash)
+ return -1;
+ else if (b_slash)
+ return 1;
+ else
+ return 0;
+}
+
+GPtrArray *
+ht_util_sort_filenames_by_component_length (GPtrArray *files)
+{
+ GPtrArray *array = g_ptr_array_sized_new (files->len);
+ memcpy (array->pdata, files->pdata, sizeof (gpointer) * files->len);
+ g_ptr_array_sort (array, (GCompareFunc) compare_filenames_by_component_length);
+ return array;
+}
+
+int
+ht_util_count_filename_components (const char *path)
+{
+ int i = 0;
+ char *p;
+
+ while (path)
+ {
+ i++;
+ path = strchr (path, '/');
+ if (path)
+ path++;
+ }
+ return i;
+}
+
+gboolean
+ht_util_filename_has_dotdot (const char *path)
+{
+ char *p;
+ char last;
+
+ if (strcmp (path, "..") == 0)
+ return TRUE;
+ if (g_str_has_prefix (path, "../"))
+ return TRUE;
+ p = strstr (path, "/..");
+ last = *(p + 1);
+ return last == '\0' || last == '/';
+}
+
void
ht_util_set_error_from_errno (GError **error,
gint saved_errno)
diff --git a/src/libhtutil/ht-unix-utils.h b/src/libhtutil/ht-unix-utils.h
index ae3731b..1c69b09 100644
--- a/src/libhtutil/ht-unix-utils.h
+++ b/src/libhtutil/ht-unix-utils.h
@@ -34,6 +34,12 @@
G_BEGIN_DECLS
+gboolean ht_util_filename_has_dotdot (const char *path);
+
+GPtrArray *ht_util_sort_filenames_by_component_length (GPtrArray *files);
+
+int ht_util_count_filename_components (const char *path);
+
int ht_util_open_file_read (const char *path, GError **error);
int ht_util_open_file_read_at (int dirfd, const char *name, GError **error);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]