Commit c34b20a267f11d16c26b79b2979d91fc9a75132f

Stefan Sperling 2018-03-12T13:06:03

write file index after checking out files

diff --git a/lib/fileindex.c b/lib/fileindex.c
index 85fdbb1..8ca7884 100644
--- a/lib/fileindex.c
+++ b/lib/fileindex.c
@@ -21,26 +21,27 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sha1.h>
+#include <endian.h>
 
 #include "got_error.h"
 
 #include "got_fileindex_lib.h"
 
 const struct got_error *
-got_fileindex_entry_open(struct got_fileindex_entry **entry, const char *path,
-    uint8_t *blob_sha1)
+got_fileindex_entry_open(struct got_fileindex_entry **entry,
+    const char *ondisk_path, const char *relpath, uint8_t *blob_sha1)
 {
 	struct stat sb;
 	size_t len;
 
-	if (lstat(path, &sb) != 0)
+	if (lstat(ondisk_path, &sb) != 0)
 		return got_error_from_errno();
 
 	*entry = calloc(1, sizeof(**entry));
 	if (*entry == NULL)
 		return got_error(GOT_ERR_NO_MEM);
 
-	(*entry)->path = strdup(path);
+	(*entry)->path = strdup(relpath);
 	if ((*entry)->path == NULL) {
 		free(*entry);
 		*entry = NULL;
@@ -61,7 +62,7 @@ got_fileindex_entry_open(struct got_fileindex_entry **entry, const char *path,
 	(*entry)->mode |= ((sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) <<
 	    GOT_INDEX_ENTRY_MODE_PERMS_SHIFT);
 	memcpy((*entry)->blob_sha1, blob_sha1, SHA1_DIGEST_LENGTH);
-	len = strlen(path);
+	len = strlen(relpath);
 	if (len > GOT_INDEX_ENTRY_F_PATH_LEN)
 		len = GOT_INDEX_ENTRY_F_PATH_LEN;
 	(*entry)->flags |= len;
@@ -111,8 +112,155 @@ got_fileindex_close(struct got_fileindex *fileindex)
 	free(fileindex);
 }
 
+static const struct got_error *
+write_fileindex_val64(SHA1_CTX *ctx, uint64_t val, FILE *outfile)
+{
+	uint8_t buf[sizeof(uint64_t)];
+	size_t n;
+
+	val = htobe64(val);
+	memcpy(buf, &val, sizeof(val));
+	SHA1Update(ctx, buf, sizeof(val));
+	n = fwrite(buf, 1, sizeof(val), outfile);
+	if (n != sizeof(val))
+		return got_ferror(outfile, GOT_ERR_IO);
+	return NULL;
+}
+
+static const struct got_error *
+write_fileindex_val32(SHA1_CTX *ctx, uint32_t val, FILE *outfile)
+{
+	uint8_t buf[sizeof(uint32_t)];
+	size_t n;
+
+	val = htobe32(val);
+	memcpy(buf, &val, sizeof(val));
+	SHA1Update(ctx, buf, sizeof(val));
+	n = fwrite(buf, 1, sizeof(val), outfile);
+	if (n != sizeof(val))
+		return got_ferror(outfile, GOT_ERR_IO);
+	return NULL;
+}
+
+static const struct got_error *
+write_fileindex_val16(SHA1_CTX *ctx, uint16_t val, FILE *outfile)
+{
+	uint8_t buf[sizeof(uint16_t)];
+	size_t n;
+
+	val = htobe16(val);
+	memcpy(buf, &val, sizeof(val));
+	SHA1Update(ctx, buf, sizeof(val));
+	n = fwrite(buf, 1, sizeof(val), outfile);
+	if (n != sizeof(val))
+		return got_ferror(outfile, GOT_ERR_IO);
+	return NULL;
+}
+
+static const struct got_error *
+write_fileindex_path(SHA1_CTX *ctx, const char *path, FILE *outfile)
+{
+	size_t n, len, pad;
+	static const uint8_t zero[8] = { 0 };
+
+	len = strlen(path);
+	pad = (len % 8);
+
+	SHA1Update(ctx, path, len);
+	n = fwrite(path, 1, len, outfile);
+	if (n != len)
+		return got_ferror(outfile, GOT_ERR_IO);
+	if (pad == 0)
+		return NULL;
+	SHA1Update(ctx, zero, pad);
+	n = fwrite(zero, 1, pad, outfile);
+	if (n != pad)
+		return got_ferror(outfile, GOT_ERR_IO);
+	return NULL;
+}
+
+static const struct got_error *
+write_fileindex_entry(SHA1_CTX *ctx, struct got_fileindex_entry *entry,
+    FILE *outfile)
+{
+	const struct got_error *err;
+	size_t n, len, pad;
+
+	err = write_fileindex_val64(ctx, entry->ctime_sec, outfile);
+	if (err)
+		return err;
+	err = write_fileindex_val64(ctx, entry->ctime_nsec, outfile);
+	if (err)
+		return err;
+	err = write_fileindex_val64(ctx, entry->mtime_sec, outfile);
+	if (err)
+		return err;
+	err = write_fileindex_val64(ctx, entry->mtime_nsec, outfile);
+	if (err)
+		return err;
+
+	err = write_fileindex_val32(ctx, entry->uid, outfile);
+	if (err)
+		return err;
+	err = write_fileindex_val32(ctx, entry->gid, outfile);
+	if (err)
+		return err;
+	err = write_fileindex_val32(ctx, entry->size, outfile);
+	if (err)
+		return err;
+
+	err = write_fileindex_val16(ctx, entry->mode, outfile);
+	if (err)
+		return err;
+
+	SHA1Update(ctx, entry->blob_sha1, SHA1_DIGEST_LENGTH);
+	n = fwrite(entry->blob_sha1, 1, SHA1_DIGEST_LENGTH, outfile);
+	if (n != SHA1_DIGEST_LENGTH)
+		return got_ferror(outfile, GOT_ERR_IO);
+
+	err = write_fileindex_val32(ctx, entry->flags, outfile);
+	if (err)
+		return err;
+
+	err = write_fileindex_path(ctx, entry->path, outfile);
+	return err;
+}
+
 const struct got_error *
 got_fileindex_write(struct got_fileindex *fileindex, FILE *outfile)
 {
+	struct got_fileindex_hdr hdr;
+	struct got_fileindex_entry *entry;
+	SHA1_CTX ctx;
+	uint8_t sha1[SHA1_DIGEST_LENGTH];
+	size_t n;
+	const size_t len = sizeof(hdr.signature) + sizeof(hdr.version) +
+	    sizeof(hdr.nentries);
+	uint8_t buf[len];
+
+	SHA1Init(&ctx);
+
+	hdr.signature = htobe32(GOT_FILE_INDEX_SIGNATURE);
+	hdr.version = htobe32(GOT_FILE_INDEX_VERSION);
+	hdr.nentries = htobe32(fileindex->nentries);
+
+	memcpy(buf, &hdr, len);
+	SHA1Update(&ctx, buf, len);
+	n = fwrite(buf, 1, len, outfile);
+	if (n != len)
+		return got_ferror(outfile, GOT_ERR_IO);
+
+	TAILQ_FOREACH(entry, &fileindex->entries, entry) {
+		const struct got_error *err;
+		err = write_fileindex_entry(&ctx, entry, outfile);
+		if (err)
+			return err;
+	}
+
+	SHA1Final(sha1, &ctx);
+	n = fwrite(sha1, 1, sizeof(sha1), outfile);
+	if (n != sizeof(sha1))
+		return got_ferror(outfile, GOT_ERR_IO);
+
 	return NULL;
 }
diff --git a/lib/got_fileindex_lib.h b/lib/got_fileindex_lib.h
index 546f7dd..018f4f2 100644
--- a/lib/got_fileindex_lib.h
+++ b/lib/got_fileindex_lib.h
@@ -74,6 +74,7 @@ struct got_fileindex {
 /* On-disk file index header structure. */
 struct got_fileindex_hdr {
 	uint32_t signature;	/* big-endian */
+#define GOT_FILE_INDEX_SIGNATURE	0x676f7449 /* 'g', 'o', 't', 'I' */
 	uint32_t version;	/* big-endian */
 #define GOT_FILE_INDEX_VERSION	1
 	uint32_t nentries;	/* big-endian */
@@ -82,7 +83,7 @@ struct got_fileindex_hdr {
 };
 
 const struct got_error *got_fileindex_entry_open(struct got_fileindex_entry **,
-    const char *, uint8_t *);
+    const char *, const char *, uint8_t *);
 void got_fileindex_entry_close(struct got_fileindex_entry *);
 struct got_fileindex *got_fileindex_open(void);
 void got_fileindex_close(struct got_fileindex *);
diff --git a/lib/worktree.c b/lib/worktree.c
index c8140c7..e7ba544 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -377,22 +377,22 @@ add_file_on_disk(struct got_worktree *worktree, struct got_fileindex *fileindex,
    const char *path, struct got_blob_object *blob, struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
-	char *abspath;
+	char *ondisk_path;
 	int fd;
 	size_t len, hdrlen;
 	struct got_fileindex_entry *entry;
 
-	if (asprintf(&abspath, "%s/%s", worktree->root_path,
+	if (asprintf(&ondisk_path, "%s/%s", worktree->root_path,
 	    apply_path_prefix(worktree, path)) == -1)
 		return got_error(GOT_ERR_NO_MEM);
 
-	fd = open(abspath, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW,
+	fd = open(ondisk_path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW,
 	    GOT_DEFAULT_FILE_MODE);
 	if (fd == -1) {
 		err = got_error_from_errno();
 		if (errno == EEXIST) {
 			struct stat sb;
-			if (lstat(abspath, &sb) == -1) {
+			if (lstat(ondisk_path, &sb) == -1) {
 				err = got_error_from_errno();
 			} else if (!S_ISREG(sb.st_mode)) {
 				/* TODO file is obstructed; do something */
@@ -424,7 +424,8 @@ add_file_on_disk(struct got_worktree *worktree, struct got_fileindex *fileindex,
 
 	fsync(fd);
 
-	err = got_fileindex_entry_open(&entry, abspath, blob->id.sha1);
+	err = got_fileindex_entry_open(&entry, ondisk_path,
+	    apply_path_prefix(worktree, path), blob->id.sha1);
 	if (err)
 		goto done;
 
@@ -433,7 +434,7 @@ add_file_on_disk(struct got_worktree *worktree, struct got_fileindex *fileindex,
 		goto done;
 done:
 	close(fd);
-	free(abspath);
+	free(ondisk_path);
 	return err;
 }