Subject: bin/24477: install(1) metalog: fix digest of /dev/null, and add size field
To: None <gnats-bugs@gnats.NetBSD.org>
From: None <apb@cequrux.com>
List: netbsd-bugs
Date: 02/18/2004 18:11:44
>Number:         24477
>Category:       bin
>Synopsis:       install(1) metalog: fix digest of /dev/null, and add size field
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Wed Feb 18 16:13:00 UTC 2004
>Closed-Date:
>Last-Modified:
>Originator:     Alan Barrett
>Release:        NetBSD 1.6ZK
>Organization:
Not much
>Environment:
NetBSD 1.6ZK
>Description:
When install(1) is used to install an empty file in metalog mode,
using a command like this:

	install -M ${DESTDIR}/METALOG -h sha1 /dev/null ${DESTDIR}/dir/file

the relevant line in the metalog does not contain the sha1 hash of the
empty file.  This happens because /dev/null is recognised as a special
case, and the code that would have calculated the hash is bypassed.

The appended patch gives install(1) knowledge of the md5, sha1 and rmd160
hashes of an empty file.  It also adds a "size=" field to the metalog.

>How-To-Repeat:
>Fix:
Apply the following patch.

Index: src/usr.bin/xinstall/xinstall.c
--- xinstall.c	2 Feb 2004 23:25:36 -0000	1.86
+++ xinstall.c	10 Feb 2004 21:09:54 -0000
@@ -101,6 +101,7 @@
 	DIGEST_SHA1,
 } digesttype = DIGEST_NONE;
 char	*digest;
+char	*nulldigest;	/* the digest of an empty file */
 
 #define LN_ABSOLUTE	0x01
 #define LN_RELATIVE	0x02
@@ -123,7 +124,7 @@
 int	main(int, char *[]);
 void	makelink(char *, char *);
 void	metadata_log(const char *, const char *, struct timeval *,
-	    const char *, const char *);
+	    const char *, const char *, off_t);
 int	parseid(char *, id_t *);
 void	strip(char *);
 void	usage(void);
@@ -281,12 +282,16 @@
 		if (0) {
 		} else if (strcmp(digest, "none") == 0) {
 			digesttype = DIGEST_NONE;
+			nulldigest = NULL;
 		} else if (strcmp(digest, "md5") == 0) {
 			digesttype = DIGEST_MD5;
+			nulldigest = "d41d8cd98f00b204e9800998ecf8427e";
 		} else if (strcmp(digest, "rmd160") == 0) {
 			digesttype = DIGEST_RMD160;
+			nulldigest = "9c1185a5c5e9fc54612808977ee8f548b2258d31";
 		} else if (strcmp(digest, "sha1") == 0) {
 			digesttype = DIGEST_SHA1;
+			nulldigest = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
 		} else {
 			warnx("unknown digest `%s'", digest);
 			usage();
@@ -489,7 +494,8 @@
 				default:
 					dres = NULL;
 				}
-				metadata_log(to_name, "file", NULL, NULL, dres);
+				metadata_log(to_name, "file", NULL, NULL,
+				    dres, to_sb.st_size);
 				free(dres);
 				mode = omode;
 				owner = oowner;
@@ -506,7 +512,7 @@
 		if (realpath(from_name, src) == NULL)
 			err(1, "%s: realpath", from_name);
 		do_symlink(src, to_name);
-		metadata_log(to_name, "link", NULL, src, NULL);
+		metadata_log(to_name, "link", NULL, src, NULL, 0);
 		return;
 	}
 
@@ -547,7 +553,7 @@
 		(void)strlcat(lnk, ++s, sizeof(lnk));
 
 		do_symlink(lnk, dst);
-		metadata_log(dst, "link", NULL, lnk, NULL);
+		metadata_log(dst, "link", NULL, lnk, NULL, 0);
 		return;
 	}
 
@@ -556,7 +562,7 @@
 	 * try the names the user provided
 	 */
 	do_symlink(from_name, to_name);
-	metadata_log(to_name, "link", NULL, from_name, NULL);
+	metadata_log(to_name, "link", NULL, from_name, NULL, 0);
 }
 
 /*
@@ -734,7 +740,9 @@
 	}
 #endif
 
-	metadata_log(to_name, "file", tv, NULL, digestresult);
+	metadata_log(to_name, "file", tv, NULL,
+	    (devnull ? nulldigest : digestresult),
+	    (devnull ? 0 : from_sb.st_size));
 	free(digestresult);
 }
 
@@ -1009,17 +1017,18 @@
 	    || chmod(path, mode) == -1 )) {
                 warn("%s: chown/chmod", path);
 	}
-	metadata_log(path, "dir", NULL, NULL, NULL);
+	metadata_log(path, "dir", NULL, NULL, NULL, 0);
 }
 
 /*
  * metadata_log --
  *	if metafp is not NULL, output mtree(8) full path name and settings to
- *	metafp, to allow permissions to be set correctly by other tools.
+ *	metafp, to allow permissions to be set correctly by other tools,
+ *	or to allow integrity checks to be performed.
  */
 void
 metadata_log(const char *path, const char *type, struct timeval *tv,
-	const char *link, const char *digestresult)
+	const char *link, const char *digestresult, off_t size)
 {
 	static const char	extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
 	char		*buf, *p;
@@ -1067,6 +1076,8 @@
 		fprintf(metafp, " tags=%s", tags);
 	if (tv != NULL && dopreserve)
 		fprintf(metafp, " time=%ld.%ld", tv[1].tv_sec, tv[1].tv_usec);
+	if (*type == 'f') /* type=file */
+		fprintf(metafp, " size=%lld", (long long)size);
 	if (digestresult && digest)
 		fprintf(metafp, " %s=%s", digest, digestresult);
 	fputc('\n', metafp);
>Release-Note:
>Audit-Trail:
>Unformatted: