From 8b1a53011b3aa56594e785c425b318c21fba8684 Mon Sep 17 00:00:00 2001
From: Timo Teras <timo.teras@iki.fi>
Date: Tue, 11 Aug 2009 19:56:11 +0300
Subject: [PATCH] abuild-tar: embed checksum for symlinks

and relocate the place of the symlink in tar header, as symlink
use the linkname field. now it's split to devmajor, devminor and
padding fields.
---
 abuild-tar.c | 44 +++++++++++++++++++++++++++++++-------------
 1 file changed, 31 insertions(+), 13 deletions(-)

diff --git a/abuild-tar.c b/abuild-tar.c
index 1293ddc..011fff6 100644
--- a/abuild-tar.c
+++ b/abuild-tar.c
@@ -42,6 +42,8 @@ struct tar_header {
 	char padding[12];   /* 500-512 */
 };
 
+#define MAX_MD_SIZE 20
+
 #define GET_OCTAL(s)	get_octal(s, sizeof(s))
 #define PUT_OCTAL(s,v)	put_octal(s, sizeof(s), v)
 
@@ -94,10 +96,10 @@ static int usage(void)
 	fprintf(stderr,
 "abuild-tar " VERSION "\n"
 "\n"
-"usage: abuild-tar [--hash [<algorithm>]] [--cut]\n"
+"usage: abuild-tar [--hash[=<algorithm>]] [--cut]\n"
 "\n"
 "options:\n"
-"  --hash [sha1|md5]	Read tar archive from stdin, precalculate hash for \n"
+"  --hash[=sha1|md5]	Read tar archive from stdin, precalculate hash for \n"
 "			regular	entries and output tar archive on stdout\n"
 "  --cut		Remove the end of file tar record\n"
 "\n");
@@ -173,7 +175,7 @@ static int do_it(const EVP_MD *md, int cut)
 	struct tar_header hdr;
 	size_t size, aligned_size;
 	void *ptr;
-	int dohash = 0, r;
+	int r;
 	struct {
 		char id[4];
 		uint16_t nid;
@@ -193,22 +195,34 @@ static int do_it(const EVP_MD *md, int cut)
 		if (cut && hdr.name[0] == 0)
 			return 0;
 
+		ptr = NULL;
 		size = GET_OCTAL(hdr.size);
 		aligned_size = (size + 511) & ~511;
 
-		if (md != NULL)
-			dohash = (hdr.typeflag == '0' || hdr.typeflag == '7');
-		if (dohash) {
+		if (md != NULL &&
+		    (hdr.typeflag == '0' || hdr.typeflag == '2' ||
+		     hdr.typeflag == '7')) {
 			const unsigned char *src;
+			unsigned char csdata[MAX_MD_SIZE];
 			int chksum, i;
 
-			ptr = malloc(aligned_size);
-			if (full_read(STDIN_FILENO, ptr, aligned_size) != aligned_size)
-				return 1;
+			if (hdr.typeflag == '0' || hdr.typeflag == '7') {
+				ptr = malloc(aligned_size);
+				if (full_read(STDIN_FILENO, ptr, aligned_size)
+				    != aligned_size)
+					return 1;
 
-			memcpy(&hdr.linkname[3], &mdinfo, sizeof(mdinfo));
-			EVP_Digest(ptr, size, &hdr.linkname[3+sizeof(mdinfo)],
-				   NULL, md, NULL);
+				memcpy(&hdr.linkname[3], &mdinfo, sizeof(mdinfo));
+				EVP_Digest(ptr, size, csdata, NULL, md, NULL);
+			} else {
+				EVP_Digest(hdr.linkname, strlen(hdr.linkname),
+					   csdata, NULL, md, NULL);
+			}
+
+			/* Embed to header */
+			memcpy(&hdr.devmajor, &mdinfo, sizeof(hdr.devmajor));
+			memcpy(&hdr.devminor, &csdata[0], sizeof(hdr.devminor));
+			memcpy(&hdr.padding,  &csdata[sizeof(hdr.devminor)], sizeof(hdr.padding));
 
 			/* Recalculate checksum */
 			memset(hdr.chksum, ' ', sizeof(hdr.chksum));
@@ -221,7 +235,7 @@ static int do_it(const EVP_MD *md, int cut)
 		if (full_write(STDOUT_FILENO, &hdr, sizeof(hdr)) != sizeof(hdr))
 			return 2;
 
-		if (dohash) {
+		if (ptr != NULL) {
 			if (full_write(STDOUT_FILENO, ptr, aligned_size) != aligned_size)
 				return 2;
 			free(ptr);
@@ -271,6 +285,10 @@ int main(int argc, char **argv)
 		md = EVP_get_digestbyname(digest);
 		if (md == NULL)
 			return usage();
+		if (EVP_MD_size(md) > MAX_MD_SIZE) {
+			fprintf(stderr, "digest size is too large\n");
+			return -1;
+		}
 	}
 
 	return do_it(md, cut);