[hacktree] Don't include any timestamps in hash, add fsck command



commit cd3a56dd68d8caf365236f23c2f52cc709baae2b
Author: Colin Walters <walters verbum org>
Date:   Wed Oct 12 11:38:41 2011 -0400

    Don't include any timestamps in hash, add fsck command

 Makefile-hacktree.am            |    3 +
 src/ht-builtin-fsck.c           |  114 ++++++++++++++++
 src/ht-builtins.h               |    1 +
 src/libhacktree/hacktree-core.c |  203 +++++++++++++++++++++++++++
 src/libhacktree/hacktree-core.h |   36 +++++
 src/libhacktree/hacktree-repo.c |  287 ++++++++++++++++-----------------------
 src/libhacktree/hacktree-repo.h |    8 +
 src/libhacktree/hacktree.h      |    1 +
 src/libhtutil/ht-unix-utils.c   |    1 -
 src/main.c                      |    1 +
 10 files changed, 483 insertions(+), 172 deletions(-)
---
diff --git a/Makefile-hacktree.am b/Makefile-hacktree.am
index 308f3c0..77bf547 100644
--- a/Makefile-hacktree.am
+++ b/Makefile-hacktree.am
@@ -13,6 +13,8 @@ libhtutil_la_LIBADD = $(GIO_UNIX_LIBS)
 noinst_LTLIBRARIES += libhacktree.la
 
 libhacktree_la_SOURCES = src/libhacktree/hacktree.h \
+	src/libhacktree/hacktree-core.c \
+	src/libhacktree/hacktree-core.h \
 	src/libhacktree/hacktree-repo.c \
 	src/libhacktree/hacktree-repo.h \
 	src/libhacktree/hacktree-types.h \
@@ -26,6 +28,7 @@ hacktree_SOURCES = src/main.c \
 	src/ht-builtins.h \
 	src/ht-builtin-init.c \
 	src/ht-builtin-link-file.c \
+	src/ht-builtin-fsck.c \
 	$(NULL)
 hacktree_CFLAGS = -I$(srcdir)/src -I$(srcdir)/src/libhacktree -I$(srcdir)/src/libhtutil -DLOCALEDIR=\"$(datadir)/locale\" $(GIO_UNIX_CFLAGS)
 hacktree_LDADD = libhtutil.la libhacktree.la $(GIO_UNIX_LIBS)
diff --git a/src/ht-builtin-fsck.c b/src/ht-builtin-fsck.c
new file mode 100644
index 0000000..61737c5
--- /dev/null
+++ b/src/ht-builtin-fsck.c
@@ -0,0 +1,114 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#include "config.h"
+
+#include "ht-builtins.h"
+#include "hacktree.h"
+
+#include <glib/gi18n.h>
+
+static char *repo_path;
+
+static GOptionEntry options[] = {
+  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", NULL },
+  { NULL }
+};
+
+typedef struct {
+  guint n_objects;
+} HtFsckData;
+
+static void
+object_iter_callback (HacktreeRepo  *repo,
+                      const char    *path,
+                      GFileInfo     *file_info,
+                      gpointer       user_data)
+{
+  HtFsckData *data = user_data;
+  guint32 nlinks;
+  struct stat stbuf;
+  GChecksum *checksum = NULL;
+  GError *error = NULL;
+  char *dirname;
+  char *checksum_prefix;
+  char *checksum_string;
+
+  dirname = g_path_get_dirname (path);
+  checksum_prefix = g_path_get_basename (dirname);
+  
+  nlinks = g_file_info_get_attribute_uint32 (file_info, "unix::nlink");
+
+  if (nlinks < 2)
+    g_printerr ("note: floating object: %s\n", path);
+
+  if (!hacktree_stat_and_checksum_file (-1, path, &checksum, &stbuf, &error))
+    goto out;
+
+  checksum_string = g_strconcat (checksum_prefix, g_file_info_get_name (file_info), NULL);
+
+  if (strcmp (checksum_string, g_checksum_get_string (checksum)) != 0)
+    {
+      g_printerr ("ERROR: corrupted object '%s' expected checksum: %s\n",
+                  path, g_checksum_get_string (checksum));
+    }
+
+  data->n_objects++;
+
+ out:
+  if (checksum != NULL)
+    g_checksum_free (checksum);
+  g_free (dirname);
+  g_free (checksum_prefix);
+  if (error != NULL)
+    {
+      g_printerr ("%s\n", error->message);
+      g_clear_error (&error);
+    }
+}
+
+gboolean
+hacktree_builtin_fsck (int argc, const char **argv, const char *prefix, GError **error)
+{
+  HtFsckData data;
+  gboolean ret = FALSE;
+  HacktreeRepo *repo = NULL;
+  int i;
+
+  if (repo_path == NULL)
+    repo_path = ".";
+
+  data.n_objects = 0;
+
+  repo = hacktree_repo_new (repo_path);
+  if (!hacktree_repo_check (repo, error))
+    goto out;
+
+  if (!hacktree_repo_iter_objects (repo, object_iter_callback, &data, error))
+    goto out;
+
+  g_printerr ("Total Objects: %u\n", data.n_objects);
+
+  ret = TRUE;
+ out:
+  g_clear_object (&repo);
+  return ret;
+}
diff --git a/src/ht-builtins.h b/src/ht-builtins.h
index fee9777..58e6914 100644
--- a/src/ht-builtins.h
+++ b/src/ht-builtins.h
@@ -38,6 +38,7 @@ typedef struct {
 
 gboolean hacktree_builtin_init (int argc, const char **argv, const char *prefix, GError **error);
 gboolean hacktree_builtin_link_file (int argc, const char **argv, const char *prefix, GError **error);
+gboolean hacktree_builtin_fsck (int argc, const char **argv, const char *prefix, GError **error);
 
 G_END_DECLS
 
diff --git a/src/libhacktree/hacktree-core.c b/src/libhacktree/hacktree-core.c
new file mode 100644
index 0000000..ddda1b5
--- /dev/null
+++ b/src/libhacktree/hacktree-core.c
@@ -0,0 +1,203 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+
+#include "config.h"
+
+#include "hacktree.h"
+#include "htutil.h"
+
+char *
+stat_to_string (struct stat *stbuf)
+{
+  return g_strdup_printf ("%d:%d:%d", stbuf->st_mode, stbuf->st_uid, stbuf->st_gid);
+}
+
+static char *
+canonicalize_xattrs (char *xattr_string, size_t len)
+{
+  char *p;
+  GSList *xattrs = NULL;
+  GSList *iter;
+  GString *result;
+
+  result = g_string_new (0);
+
+  p = xattr_string;
+  while (p < xattr_string+len)
+    {
+      xattrs = g_slist_prepend (xattrs, p);
+      p += strlen (p) + 1;
+    }
+
+  xattrs = g_slist_sort (xattrs, (GCompareFunc) strcmp);
+  for (iter = xattrs; iter; iter = iter->next)
+    g_string_append (result, iter->data);
+
+  g_slist_free (xattrs);
+  return g_string_free (result, FALSE);
+}
+
+gboolean
+hacktree_stat_and_checksum_file (int dir_fd, const char *path,
+                                 GChecksum **out_checksum,
+                                 struct stat *out_stbuf,
+                                 GError **error)
+{
+  GChecksum *content_sha256 = NULL;
+  GChecksum *content_and_meta_sha256 = NULL;
+  char *stat_string = NULL;
+  ssize_t bytes_read;
+  char *xattrs = NULL;
+  char *xattrs_canonicalized = NULL;
+  int fd = -1;
+  DIR *temp_dir = NULL;
+  char *basename = NULL;
+  gboolean ret = FALSE;
+  char *symlink_target = NULL;
+  char *device_id = NULL;
+
+  basename = g_path_get_basename (path);
+
+  if (dir_fd == -1)
+    {
+      char *dirname = g_path_get_dirname (path);
+      temp_dir = opendir (dirname);
+      if (temp_dir == NULL)
+        {
+          ht_util_set_error_from_errno (error, errno);
+          g_free (dirname);
+        }
+      g_free (dirname);
+      dir_fd = dirfd (temp_dir);
+    }
+
+  if (fstatat (dir_fd, basename, out_stbuf, AT_SYMLINK_NOFOLLOW) < 0)
+    {
+      ht_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  if (!S_ISLNK(out_stbuf->st_mode))
+    {
+      fd = ht_util_open_file_read_at (dir_fd, basename, error);
+      if (fd < 0)
+        {
+          ht_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+
+  stat_string = stat_to_string (out_stbuf);
+
+  /* FIXME - Add llistxattrat */
+  if (!S_ISLNK(out_stbuf->st_mode))
+    bytes_read = flistxattr (fd, NULL, 0);
+  else
+    bytes_read = llistxattr (path, NULL, 0);
+
+  if (bytes_read < 0)
+    {
+      if (errno != ENOTSUP)
+        {
+          ht_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+  if (errno != ENOTSUP)
+    {
+      gboolean tmp;
+      xattrs = g_malloc (bytes_read);
+      /* FIXME - Add llistxattrat */
+      if (!S_ISLNK(out_stbuf->st_mode))
+        tmp = flistxattr (fd, xattrs, bytes_read);
+      else
+        tmp = llistxattr (path, xattrs, bytes_read);
+          
+      if (!tmp)
+        {
+          ht_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+
+      xattrs_canonicalized = canonicalize_xattrs (xattrs, bytes_read);
+    }
+
+  content_sha256 = g_checksum_new (G_CHECKSUM_SHA256);
+ 
+  if (S_ISREG(out_stbuf->st_mode))
+    {
+      guint8 buf[8192];
+
+      while ((bytes_read = read (fd, buf, sizeof (buf))) > 0)
+        g_checksum_update (content_sha256, buf, bytes_read);
+      if (bytes_read < 0)
+        {
+          ht_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+  else if (S_ISLNK(out_stbuf->st_mode))
+    {
+      symlink_target = g_malloc (PATH_MAX);
+
+      if (readlinkat (dir_fd, basename, symlink_target, PATH_MAX) < 0)
+        {
+          ht_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+      g_checksum_update (content_sha256, symlink_target, strlen (symlink_target));
+    }
+  else if (S_ISCHR(out_stbuf->st_mode) || S_ISBLK(out_stbuf->st_mode))
+    {
+      device_id = g_strdup_printf ("%d", out_stbuf->st_rdev);
+      g_checksum_update (content_sha256, device_id, strlen (device_id));
+    }
+  else
+    {
+      g_set_error (error, G_IO_ERROR,
+                   G_IO_ERROR_FAILED,
+                   "Unsupported file '%s' (must be regular, symbolic link, or device)",
+                   path);
+      goto out;
+    }
+
+  content_and_meta_sha256 = g_checksum_copy (content_sha256);
+
+  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_checksum = content_and_meta_sha256;
+  ret = TRUE;
+ out:
+  if (fd >= 0)
+    close (fd);
+  if (temp_dir != NULL)
+    closedir (temp_dir);
+  g_free (symlink_target);
+  g_free (basename);
+  g_free (stat_string);
+  g_free (xattrs);
+  g_free (xattrs_canonicalized);
+  if (content_sha256)
+    g_checksum_free (content_sha256);
+
+  return ret;
+}
diff --git a/src/libhacktree/hacktree-core.h b/src/libhacktree/hacktree-core.h
new file mode 100644
index 0000000..124a90a
--- /dev/null
+++ b/src/libhacktree/hacktree-core.h
@@ -0,0 +1,36 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters verbum org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters verbum org>
+ */
+/* hacktree-repo.h */
+
+#ifndef _HACKTREE_CORE
+#define _HACKTREE_CORE
+
+#include <htutil.h>
+
+G_BEGIN_DECLS
+
+gboolean hacktree_stat_and_checksum_file (int dirfd, const char *path,
+                                          GChecksum **out_checksum,
+                                          struct stat *out_stbuf,
+                                          GError **error);
+
+
+#endif /* _HACKTREE_REPO */
diff --git a/src/libhacktree/hacktree-repo.c b/src/libhacktree/hacktree-repo.c
index bb0a9b8..a95f92d 100644
--- a/src/libhacktree/hacktree-repo.c
+++ b/src/libhacktree/hacktree-repo.c
@@ -167,173 +167,6 @@ hacktree_repo_check (HacktreeRepo *self, GError **error)
   return TRUE;
 }
 
-char *
-stat_to_string (struct stat *stbuf)
-{
-  return g_strdup_printf ("%d:%d:%d:%" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT,
-                          stbuf->st_mode,
-                          stbuf->st_uid,
-                          stbuf->st_gid,
-                          stbuf->st_atime,
-                          stbuf->st_mtime,
-                          stbuf->st_ctime);
-}
-
-static char *
-canonicalize_xattrs (char *xattr_string, size_t len)
-{
-  char *p;
-  GSList *xattrs = NULL;
-  GSList *iter;
-  GString *result;
-
-  result = g_string_new (0);
-
-  p = xattr_string;
-  while (p < xattr_string+len)
-    {
-      xattrs = g_slist_prepend (xattrs, p);
-      p += strlen (p) + 1;
-    }
-
-  xattrs = g_slist_sort (xattrs, (GCompareFunc) strcmp);
-  for (iter = xattrs; iter; iter = iter->next)
-    g_string_append (result, iter->data);
-
-  g_slist_free (xattrs);
-  return g_string_free (result, FALSE);
-}
-
-static gboolean
-stat_and_compute_checksum (int dirfd, const char *path,
-                           GChecksum **out_checksum,
-                           struct stat *out_stbuf,
-                           GError **error)
-{
-  GChecksum *content_sha256 = NULL;
-  GChecksum *content_and_meta_sha256 = NULL;
-  char *stat_string = NULL;
-  ssize_t bytes_read;
-  char *xattrs = NULL;
-  char *xattrs_canonicalized = NULL;
-  int fd = -1;
-  char *basename = NULL;
-  gboolean ret = FALSE;
-  char *symlink_target = NULL;
-  char *device_id = NULL;
-
-  basename = g_path_get_basename (path);
-
-  if (fstatat (dirfd, basename, out_stbuf, AT_SYMLINK_NOFOLLOW) < 0)
-    {
-      ht_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-
-  if (!S_ISLNK(out_stbuf->st_mode))
-    {
-      fd = ht_util_open_file_read_at (dirfd, basename, error);
-      if (fd < 0)
-        {
-          ht_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-    }
-
-  stat_string = stat_to_string (out_stbuf);
-
-  /* FIXME - Add llistxattrat */
-  if (!S_ISLNK(out_stbuf->st_mode))
-    bytes_read = flistxattr (fd, NULL, 0);
-  else
-    bytes_read = llistxattr (path, NULL, 0);
-
-  if (bytes_read < 0)
-    {
-      if (errno != ENOTSUP)
-        {
-          ht_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-    }
-  if (errno != ENOTSUP)
-    {
-      gboolean tmp;
-      xattrs = g_malloc (bytes_read);
-      /* FIXME - Add llistxattrat */
-      if (!S_ISLNK(out_stbuf->st_mode))
-        tmp = flistxattr (fd, xattrs, bytes_read);
-      else
-        tmp = llistxattr (path, xattrs, bytes_read);
-          
-      if (!tmp)
-        {
-          ht_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-
-      xattrs_canonicalized = canonicalize_xattrs (xattrs, bytes_read);
-    }
-
-  content_sha256 = g_checksum_new (G_CHECKSUM_SHA256);
- 
-  if (S_ISREG(out_stbuf->st_mode))
-    {
-      guint8 buf[8192];
-
-      while ((bytes_read = read (fd, buf, sizeof (buf))) > 0)
-        g_checksum_update (content_sha256, buf, bytes_read);
-      if (bytes_read < 0)
-        {
-          ht_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-    }
-  else if (S_ISLNK(out_stbuf->st_mode))
-    {
-      symlink_target = g_malloc (PATH_MAX);
-
-      if (readlinkat (dirfd, basename, symlink_target, PATH_MAX) < 0)
-        {
-          ht_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-      g_checksum_update (content_sha256, symlink_target, strlen (symlink_target));
-    }
-  else if (S_ISCHR(out_stbuf->st_mode) || S_ISBLK(out_stbuf->st_mode))
-    {
-      device_id = g_strdup_printf ("%d", out_stbuf->st_rdev);
-      g_checksum_update (content_sha256, device_id, strlen (device_id));
-    }
-  else
-    {
-      g_set_error (error, G_IO_ERROR,
-                   G_IO_ERROR_FAILED,
-                   "Unsupported file '%s' (must be regular, symbolic link, or device)",
-                   path);
-      goto out;
-    }
-
-  content_and_meta_sha256 = g_checksum_copy (content_sha256);
-
-  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_checksum = content_and_meta_sha256;
-  ret = TRUE;
- out:
-  if (fd >= 0)
-    close (fd);
-  g_free (symlink_target);
-  g_free (basename);
-  g_free (stat_string);
-  g_free (xattrs);
-  g_free (xattrs_canonicalized);
-  if (content_sha256)
-    g_checksum_free (content_sha256);
-
-  return ret;
-}
 
 static char *
 prepare_dir_for_checksum_get_object_path (HacktreeRepo *self,
@@ -346,14 +179,14 @@ prepare_dir_for_checksum_get_object_path (HacktreeRepo *self,
   char *object_path = NULL;
   GError *temp_error = NULL;
 
-  checksum_prefix = g_strdup (g_checksum_get_string (checksum));
-  checksum_prefix[2] = '\0';
+  checksum_prefix = g_strndup (g_checksum_get_string (checksum), 2);
+  g_assert_cmpuint (strlen (checksum_prefix), ==, 2);
   checksum_dir = g_build_filename (priv->objects_path, checksum_prefix, NULL);
 
   if (!ht_util_ensure_directory (checksum_dir, FALSE, error))
     goto out;
   
-  object_path = g_build_filename (checksum_dir, checksum_prefix + 3, NULL);
+  object_path = g_build_filename (checksum_dir, g_checksum_get_string (checksum) + 2, NULL);
  out:
   g_free (checksum_prefix);
   g_free (checksum_dir);
@@ -387,7 +220,7 @@ link_one_file (HacktreeRepo *self, const char *path, GError **error)
       goto out;
     }
 
-  if (!stat_and_compute_checksum (dirfd (src_dir), path, &id, &stbuf, error))
+  if (!hacktree_stat_and_checksum_file (dirfd (src_dir), path, &id, &stbuf, error))
     goto out;
   dest_path = prepare_dir_for_checksum_get_object_path (self, id, error);
   if (!dest_path)
@@ -434,3 +267,115 @@ hacktree_repo_link_file (HacktreeRepo *self,
 
   return link_one_file (self, path, error);
 }
+
+static gboolean
+iter_object_dir (HacktreeRepo   *repo,
+                 GFile          *dir,
+                 HacktreeRepoObjectIter  callback,
+                 gpointer                user_data,
+                 GError                **error)
+{
+  gboolean ret = FALSE;
+  GError *temp_error = NULL;
+  GFileEnumerator *enumerator = NULL;
+  GFileInfo *file_info = NULL;
+  char *dirpath = NULL;
+
+  dirpath = g_file_get_path (dir);
+
+  enumerator = g_file_enumerate_children (dir, "standard::*,unix::*", 
+                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                          NULL, 
+                                          error);
+  if (!enumerator)
+    goto out;
+  
+  while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+    {
+      const char *name;
+      guint32 type;
+      name = g_file_info_get_attribute_byte_string (file_info, "standard::name"); 
+      type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+      
+      /* 64 - 2 */
+      if (strlen (name) == 62 && type != G_FILE_TYPE_DIRECTORY)
+        {
+          char *path = g_build_filename (dirpath, name, NULL);
+          callback (repo, path, file_info, user_data);
+          g_free (path);
+        }
+
+      g_object_unref (file_info);
+    }
+  if (file_info == NULL && temp_error != NULL)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+  if (!g_file_enumerator_close (enumerator, NULL, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_free (dirpath);
+  return ret;
+}
+
+gboolean
+hacktree_repo_iter_objects (HacktreeRepo  *self,
+                            HacktreeRepoObjectIter callback,
+                            gpointer       user_data,
+                            GError        **error)
+{
+  HacktreeRepoPrivate *priv = GET_PRIVATE (self);
+  GFile *objectdir = NULL;
+  GFileEnumerator *enumerator = NULL;
+  gboolean ret = FALSE;
+  GFileInfo *file_info = NULL;
+  GError *temp_error = NULL;
+
+  g_return_val_if_fail (priv->inited, FALSE);
+
+  objectdir = g_file_new_for_path (priv->objects_path);
+  enumerator = g_file_enumerate_children (objectdir, "standard::*,unix::*", 
+                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                          NULL, 
+                                          error);
+  if (!enumerator)
+    goto out;
+
+  while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+    {
+      const char *name;
+      guint32 type;
+
+      name = g_file_info_get_attribute_byte_string (file_info, "standard::name"); 
+      type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+      
+      if (strlen (name) == 2 && type == G_FILE_TYPE_DIRECTORY)
+        {
+          GFile *objdir = g_file_get_child (objectdir, name);
+          if (!iter_object_dir (self, objdir, callback, user_data, error))
+            {
+              g_object_unref (objdir);
+              goto out;
+            }
+          g_object_unref (objdir);
+        }
+      g_object_unref (file_info);
+    }
+  if (file_info == NULL && temp_error != NULL)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+  if (!g_file_enumerator_close (enumerator, NULL, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_clear_object (&file_info);
+  g_clear_object (&enumerator);
+  g_clear_object (&objectdir);
+  return ret;
+}
diff --git a/src/libhacktree/hacktree-repo.h b/src/libhacktree/hacktree-repo.h
index 715560e..0899295 100644
--- a/src/libhacktree/hacktree-repo.h
+++ b/src/libhacktree/hacktree-repo.h
@@ -57,6 +57,14 @@ gboolean      hacktree_repo_link_file (HacktreeRepo *repo,
                                        const char   *path,
                                        GError      **error);
 
+typedef void (*HacktreeRepoObjectIter) (HacktreeRepo *repo, const char *path,
+                                        GFileInfo *fileinfo, gpointer user_data);
+
+gboolean     hacktree_repo_iter_objects (HacktreeRepo  *repo,
+                                         HacktreeRepoObjectIter callback,
+                                         gpointer       user_data,
+                                         GError        **error);
+
 G_END_DECLS
 
 #endif /* _HACKTREE_REPO */
diff --git a/src/libhacktree/hacktree.h b/src/libhacktree/hacktree.h
index 11f0a89..0cf2f15 100644
--- a/src/libhacktree/hacktree.h
+++ b/src/libhacktree/hacktree.h
@@ -21,6 +21,7 @@
 
 #ifndef __HACKTREE_H__
 
+#include <hacktree-core.h>
 #include <hacktree-repo.h>
 #include <hacktree-types.h>
 
diff --git a/src/libhtutil/ht-unix-utils.c b/src/libhtutil/ht-unix-utils.c
index f0aebb2..4637c7d 100644
--- a/src/libhtutil/ht-unix-utils.c
+++ b/src/libhtutil/ht-unix-utils.c
@@ -30,7 +30,6 @@
 #include <sys/types.h>
 #include <dirent.h>
 
-
 void
 ht_util_set_error_from_errno (GError **error,
                               gint     saved_errno)
diff --git a/src/main.c b/src/main.c
index df4022e..7ff1c24 100644
--- a/src/main.c
+++ b/src/main.c
@@ -31,6 +31,7 @@
 static HacktreeBuiltin builtins[] = {
   { "init", hacktree_builtin_init, 0 },
   { "link-file", hacktree_builtin_link_file, 0 },
+  { "fsck", hacktree_builtin_fsck, 0 },
   { NULL }
 };
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]