Commit 3ee5fc2109eca572a496f0da1a80fecfc8b56aea

Stefan Sperling 2018-01-17T22:25:01

add support for extracting non-deltified packed objects

diff --git a/include/got_error.h b/include/got_error.h
index ebe18b5..b85bd54 100644
--- a/include/got_error.h
+++ b/include/got_error.h
@@ -34,6 +34,7 @@
 #define GOT_ERR_BAD_PACKFILE	16
 #define GOT_ERR_NO_OBJ		17
 #define GOT_ERR_NOT_IMPL	18
+#define GOT_ERR_OBJ_NOT_PACKED	19
 
 static const struct got_error {
 	int code;
@@ -58,6 +59,7 @@ static const struct got_error {
 	{ GOT_ERR_BAD_PACKFILE,	"bad pack file" },
 	{ GOT_ERR_NO_OBJ,	"object not found" },
 	{ GOT_ERR_NOT_IMPL,	"feature not implemented" },
+	{ GOT_ERR_OBJ_NOT_PACKED,"object is not packed" },
 };
 
 const struct got_error * got_error(int code);
diff --git a/lib/object.c b/lib/object.c
index f3581b6..96d336a 100644
--- a/lib/object.c
+++ b/lib/object.c
@@ -274,9 +274,8 @@ open_object(FILE **f, struct got_object *obj, struct got_repository *repo)
 	const struct got_error *err = NULL;
 	char *path;
 
-	if (obj->flags & GOT_OBJ_FLAG_PACKED) {
-		return got_error(GOT_ERR_NOT_IMPL);
-	}
+	if (obj->flags & GOT_OBJ_FLAG_PACKED)
+		return got_packfile_extract_object(f, obj, repo);
 
 	err = object_path(&path, &obj->id, repo);
 	if (err)
diff --git a/lib/pack.c b/lib/pack.c
index 7e7325a..32b5af0 100644
--- a/lib/pack.c
+++ b/lib/pack.c
@@ -327,7 +327,7 @@ read_packfile_hdr(FILE *f, struct got_packidx_v2_hdr *packidx)
 }
 
 static const struct got_error *
-decode_type_and_size(uint8_t *type, uint64_t *size, FILE *packfile)
+decode_type_and_size(uint8_t *type, uint64_t *size, size_t *len, FILE *packfile)
 {
 	uint8_t t = 0;
 	uint64_t s = 0;
@@ -357,6 +357,7 @@ decode_type_and_size(uint8_t *type, uint64_t *size, FILE *packfile)
 
 	*type = t;
 	*size = s;
+	*len = i * sizeof(sizeN);
 	return NULL;
 }
 
@@ -374,6 +375,7 @@ open_packed_object(struct got_object **obj, struct got_repository *repo,
 	FILE *packfile;
 	uint8_t type;
 	uint64_t size;
+	size_t tslen;
 
 	*obj = NULL;
 	if (idx == -1) /* object not found in pack index */
@@ -407,7 +409,7 @@ open_packed_object(struct got_object **obj, struct got_repository *repo,
 		goto done;
 	}
 
-	err = decode_type_and_size(&type, &size, packfile);
+	err = decode_type_and_size(&type, &size, &tslen, packfile);
 	if (err)
 		goto done;
 
@@ -431,7 +433,7 @@ open_packed_object(struct got_object **obj, struct got_repository *repo,
 		(*obj)->hdrlen = 0;
 		(*obj)->size = size;
 		memcpy(&(*obj)->id, id, sizeof((*obj)->id));
-		(*obj)->pack_offset = offset;
+		(*obj)->pack_offset = offset + tslen;
 		break;
 	case GOT_OBJ_TYPE_REF_DELTA:
 	case GOT_OBJ_TYPE_TAG:
@@ -500,3 +502,75 @@ done:
 		err = got_error_from_errno();
 	return err;
 }
+
+static const struct got_error *
+dump_plain_object(FILE *infile, uint8_t type, size_t size, FILE *outfile)
+{
+	size_t n;
+
+	while (size > 0) {
+		uint8_t data[2048];
+		size_t len = MIN(size, sizeof(data));
+
+		n = fread(data, len, 1, infile);
+		if (n != 1)
+			return got_ferror(infile, GOT_ERR_BAD_PACKIDX);
+
+		n = fwrite(data, len, 1, outfile);
+		if (n != 1)
+			return got_ferror(outfile, GOT_ERR_BAD_PACKIDX);
+
+		size -= len;
+	}
+
+	rewind(outfile);
+	return NULL;
+}
+
+const struct got_error *
+got_packfile_extract_object(FILE **f, struct got_object *obj,
+    struct got_repository *repo)
+{
+	const struct got_error *err = NULL;
+	FILE *packfile = NULL;
+
+	if ((obj->flags & GOT_OBJ_FLAG_PACKED) == 0)
+		return got_error(GOT_ERR_OBJ_NOT_PACKED);
+
+	*f = got_opentemp();
+	if (*f == NULL) {
+		err = got_error(GOT_ERR_FILE_OPEN);
+		goto done;
+	}
+
+	packfile = fopen(obj->path_packfile, "rb");
+	if (packfile == NULL) {
+		err = got_error_from_errno();
+		goto done;
+	}
+
+	if (fseeko(packfile, obj->pack_offset, SEEK_SET) != 0) {
+		err = got_error_from_errno();
+		goto done;
+	}
+
+	switch (obj->type) {
+	case GOT_OBJ_TYPE_COMMIT:
+	case GOT_OBJ_TYPE_TREE:
+	case GOT_OBJ_TYPE_BLOB:
+		err = dump_plain_object(packfile, obj->type, obj->size, *f);
+		break;
+	case GOT_OBJ_TYPE_REF_DELTA:
+	case GOT_OBJ_TYPE_TAG:
+	case GOT_OBJ_TYPE_OFFSET_DELTA:
+	default:
+		err = got_error(GOT_ERR_NOT_IMPL);
+		goto done;
+	}
+done:
+	if (packfile)
+		fclose(packfile);
+	if (err && *f)
+		fclose(*f);
+	return err;
+}
diff --git a/lib/pack.h b/lib/pack.h
index e26be48..18a6071 100644
--- a/lib/pack.h
+++ b/lib/pack.h
@@ -123,3 +123,5 @@ void got_packidx_close(struct got_packidx_v2_hdr *);
 
 const struct got_error *got_packfile_open_object(struct got_object **,
     struct got_object_id *, struct got_repository *);
+const struct got_error *got_packfile_extract_object(FILE **,
+    struct got_object *, struct got_repository *);