1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2018 by Delphix. All rights reserved.
14  */
15 
16 #include <stdio.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <assert.h>
22 #include <sys/param.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <errno.h>
26 
27 static void
usage(const char * msg,int exit_value)28 usage(const char *msg, int exit_value)
29 {
30 	(void) fprintf(stderr, "usage: get_diff file redacted_file\n%s\n", msg);
31 	exit(exit_value);
32 }
33 
34 /*
35  * This utility compares two files, an original and its redacted counterpart
36  * (in that order). It compares the files 512 bytes at a time, printing out
37  * any ranges (as offset and length) where the redacted file does not match
38  * the original. This output is used to verify that the expected ranges of
39  * a redacted file do not contain the original data.
40  */
41 int
main(int argc,char * argv[])42 main(int argc, char *argv[])
43 {
44 	off_t		diff_off = 0, diff_len = 0, off = 0;
45 	int		fd1, fd2;
46 	char		*fname1, *fname2;
47 	char		buf1[DEV_BSIZE], buf2[DEV_BSIZE];
48 	ssize_t		bytes;
49 
50 	if (argc != 3)
51 		usage("Incorrect number of arguments.", 1);
52 
53 	if ((fname1 = argv[1]) == NULL)
54 		usage("Filename missing.", 1);
55 	if ((fd1 = open(fname1, O_LARGEFILE | O_RDONLY)) < 0) {
56 		perror("open1 failed");
57 		exit(1);
58 	}
59 
60 	if ((fname2 = argv[2]) == NULL)
61 		usage("Redacted filename missing.", 1);
62 	if ((fd2 = open(fname2, O_LARGEFILE | O_RDONLY)) < 0) {
63 		perror("open2 failed");
64 		exit(1);
65 	}
66 
67 	while ((bytes = pread(fd1, buf1, DEV_BSIZE, off)) > 0) {
68 		if (pread(fd2, buf2, DEV_BSIZE, off) < 0) {
69 			if (errno == EIO) {
70 				/*
71 				 * A read in a redacted section of a file will
72 				 * fail with EIO. If we get EIO, continue on
73 				 * but ensure that a comparison of buf1 and
74 				 * buf2 will fail, indicating a redacted block.
75 				 */
76 				buf2[0] = ~buf1[0];
77 			} else {
78 				perror("pread failed");
79 				exit(1);
80 			}
81 		}
82 		if (memcmp(buf1, buf2, bytes) == 0) {
83 			if (diff_len != 0) {
84 				(void) fprintf(stdout, "%lld,%lld\n",
85 				    (long long)diff_off, (long long)diff_len);
86 				assert(off == diff_off + diff_len);
87 				diff_len = 0;
88 			}
89 			diff_off = 0;
90 		} else {
91 			if (diff_len == 0)
92 				diff_off = off;
93 			assert(off == diff_off + diff_len);
94 			diff_len += bytes;
95 		}
96 		off += bytes;
97 	}
98 
99 	if (diff_len != 0) {
100 		(void) fprintf(stdout, "%lld,%lld\n", (long long)diff_off,
101 		    (long long)diff_len);
102 	}
103 
104 	(void) close(fd1);
105 	(void) close(fd2);
106 
107 	return (0);
108 }
109