xref: /freebsd/contrib/libarchive/cpio/test/test_format_newc.c (revision b64c5a0ace59af62eff52bfe110a521dc73c937b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2003-2007 Tim Kientzle
5  * All rights reserved.
6  */
7 #include "test.h"
8 
9 /* Number of bytes needed to pad 'n' to multiple of 'block', assuming
10  * that 'block' is a power of two. This trick can be more easily
11  * remembered as -n & (block - 1), but many compilers quite reasonably
12  * warn about "-n" when n is an unsigned value.  (~(n) + 1) is the
13  * same thing, but written in a way that won't offend anyone. */
14 #define PAD(n, block)  ((~(n) + 1) & ((block) - 1))
15 
16 static int
17 is_hex(const char *p, size_t l)
18 {
19 	while (l > 0) {
20 		if ((*p >= '0' && *p <= '9')
21 		    || (*p >= 'a' && *p <= 'f')
22 		    || (*p >= 'A' && *p <= 'F'))
23 		{
24 			--l;
25 			++p;
26 		} else
27 			return (0);
28 
29 	}
30 	return (1);
31 }
32 
33 /* Convert up to 8 hex characters to unsigned 32-bit decimal integer */
34 static uint32_t
35 from_hex(const char *p, size_t l)
36 {
37 	uint32_t r = 0;
38 
39 	while (l > 0) {
40 		r *= 16;
41 		if (*p >= 'a' && *p <= 'f')
42 			r += *p + 10 - 'a';
43 		else if (*p >= 'A' && *p <= 'F')
44 			r += *p + 10 - 'A';
45 		else
46 			r += *p - '0';
47 		--l;
48 		++p;
49 	}
50 	return (r);
51 }
52 
53 #if !defined(_WIN32) || defined(__CYGWIN__)
54 static int
55 nlinks(const char *p)
56 {
57 	struct stat st;
58 	assertEqualInt(0, stat(p, &st));
59 	return st.st_nlink;
60 }
61 #endif
62 
63 DEFINE_TEST(test_format_newc)
64 {
65 	FILE *list;
66 	int r;
67 	uint32_t devmajor, devminor, ino, gid, uid;
68 	time_t t, t2, now;
69 	char *p, *e;
70 	size_t s;
71 	uint64_t fs, ns;
72 	char result[1024];
73 
74 	assertUmask(0);
75 
76 #if !defined(_WIN32)
77 	uid = getuid();
78 #endif
79 
80 	/*
81 	 * Create an assortment of files.
82 	 * TODO: Extend this to cover more filetypes.
83 	 */
84 	list = fopen("list", "w");
85 
86 	/* "file1" */
87 	assertMakeFile("file1", 0644, "1234567890");
88 	fprintf(list, "file1\n");
89 
90 	/* "hardlink" */
91 	assertMakeHardlink("hardlink", "file1");
92 	fprintf(list, "hardlink\n");
93 
94 	/* Another hardlink, but this one won't be archived. */
95 	assertMakeHardlink("hardlink2", "file1");
96 
97 	/* "symlink" */
98 	if (canSymlink()) {
99 		assertMakeSymlink("symlink", "file1", 0);
100 		fprintf(list, "symlink\n");
101 	}
102 
103 	/* "dir" */
104 	assertMakeDir("dir", 0775);
105 	fprintf(list, "dir\n");
106 
107 	/* Setup result message. */
108 	memset(result, 0, sizeof(result));
109 	if (is_LargeInode("file1")) {
110 		strncat(result,
111 		    "bsdcpio: file1: large inode number truncated: ",
112 		    sizeof(result) - strlen(result) -1);
113 		strncat(result, strerror(ERANGE),
114 		    sizeof(result) - strlen(result) -1);
115 		strncat(result, "\n",
116 		    sizeof(result) - strlen(result) -1);
117 	}
118 	if (canSymlink() && is_LargeInode("symlink")) {
119 		strncat(result,
120 		    "bsdcpio: symlink: large inode number truncated: ",
121 		    sizeof(result) - strlen(result) -1);
122 		strncat(result, strerror(ERANGE),
123 		    sizeof(result) - strlen(result) -1);
124 		strncat(result, "\n",
125 		    sizeof(result) - strlen(result) -1);
126 	}
127 	if (is_LargeInode("dir")) {
128 		strncat(result,
129 		    "bsdcpio: dir: large inode number truncated: ",
130 		    sizeof(result) - strlen(result) -1);
131 		strncat(result, strerror(ERANGE),
132 		    sizeof(result) - strlen(result) -1);
133 		strncat(result, "\n",
134 		    sizeof(result) - strlen(result) -1);
135 	}
136 	if (is_LargeInode("hardlink")) {
137 		strncat(result,
138 		    "bsdcpio: hardlink: large inode number truncated: ",
139 		    sizeof(result) - strlen(result) -1);
140 		strncat(result, strerror(ERANGE),
141 		    sizeof(result) - strlen(result) -1);
142 		strncat(result, "\n",
143 		    sizeof(result) - strlen(result) -1);
144 	}
145 
146 	/* Record some facts about what we just created: */
147 	now = time(NULL); /* They were all created w/in last two seconds. */
148 
149 	/* Use the cpio program to create an archive. */
150 	fclose(list);
151 	r = systemf("%s -o --format=newc <list >newc.out 2>newc.err",
152 	    testprog);
153 	if (!assertEqualInt(r, 0))
154 		return;
155 
156 	/* Verify that nothing went to stderr. */
157 	if (canSymlink()) {
158 		strncat(result, "2 blocks\n", sizeof(result) - strlen(result) -1);
159 	} else {
160 		strncat(result, "1 block\n", sizeof(result) - strlen(result) -1);
161 	}
162 	assertTextFileContents(result, "newc.err");
163 
164 	/* Verify that stdout is a well-formed cpio file in "newc" format. */
165 	p = slurpfile(&s, "newc.out");
166 	assertEqualInt(s, canSymlink() ? 1024 : 512);
167 	e = p;
168 
169 	/*
170 	 * Some of these assertions could be stronger, but it's
171 	 * a little tricky because they depend on the local environment.
172 	 */
173 
174 	/* First entry is "file1" */
175 	assert(is_hex(e, 110)); /* Entire header is octal digits. */
176 	assertEqualMem(e + 0, "070701", 6); /* Magic */
177 	ino = from_hex(e + 6, 8); /* ino */
178 #if defined(_WIN32) && !defined(__CYGWIN__)
179 	/* Group members bits and others bits do not work. */
180 	assertEqualInt(0x8180, from_hex(e + 14, 8) & 0xffc0); /* Mode */
181 #else
182 	assertEqualInt(0x81a4, from_hex(e + 14, 8)); /* Mode */
183 #endif
184 #if defined(_WIN32)
185 	uid = from_hex(e + 22, 8);
186 #else
187 	assertEqualInt(from_hex(e + 22, 8), uid); /* uid */
188 #endif
189 	gid = from_hex(e + 30, 8); /* gid */
190 	assertEqualMem(e + 38, "00000003", 8); /* nlink */
191 	t = from_hex(e + 46, 8); /* mtime */
192 	failure("t=%#08jx now=%#08jx=%jd", (intmax_t)t, (intmax_t)now,
193 	    (intmax_t)now);
194 	assert(t <= now); /* File wasn't created in future. */
195 	failure("t=%#08jx now - 2=%#08jx=%jd", (intmax_t)t, (intmax_t)now - 2,
196 	    (intmax_t)now - 2);
197 	assert(t >= now - 2); /* File was created w/in last 2 secs. */
198 	failure("newc format stores body only with last appearance of a link\n"
199 	    "       first appearance should be empty, so this file size\n"
200 	    "       field should be zero");
201 	assertEqualInt(0, from_hex(e + 54, 8)); /* File size */
202 	fs = (uint64_t)from_hex(e + 54, 8);
203 	fs += PAD(fs, 4);
204 	devmajor = from_hex(e + 62, 8); /* devmajor */
205 	devminor = from_hex(e + 70, 8); /* devminor */
206 	assert(is_hex(e + 78, 8)); /* rdevmajor */
207 	assert(is_hex(e + 86, 8)); /* rdevminor */
208 	assertEqualMem(e + 94, "00000006", 8); /* Name size */
209 	ns = (uint64_t)from_hex(e + 94, 8);
210 	ns += PAD(ns + 2, 4);
211 	assertEqualInt(0, from_hex(e + 102, 8)); /* check field */
212 	assertEqualMem(e + 110, "file1\0", 6); /* Name contents */
213 	/* Since there's another link, no file contents here. */
214 	/* But add in file size so that an error here doesn't cascade. */
215 	e += 110 + fs + ns;
216 
217 	if (canSymlink()) {
218 		/* "symlink" pointing to "file1" */
219 		assert(is_hex(e, 110));
220 		assertEqualMem(e + 0, "070701", 6); /* Magic */
221 		assert(is_hex(e + 6, 8)); /* ino */
222 #if defined(_WIN32) && !defined(CYGWIN)
223 		/* Mode: Group members bits and others bits do not work. */
224 		assertEqualInt(0xa180, from_hex(e + 14, 8) & 0xffc0);
225 #else
226 		assertEqualInt(0xa1ff, from_hex(e + 14, 8)); /* Mode */
227 #endif
228 		assertEqualInt(from_hex(e + 22, 8), uid); /* uid */
229 		assertEqualInt(gid, from_hex(e + 30, 8)); /* gid */
230 		assertEqualMem(e + 38, "00000001", 8); /* nlink */
231 		t2 = from_hex(e + 46, 8); /* mtime */
232 		failure("First entry created at t=%#08jx this entry created"
233 		    " at t2=%#08jx", (intmax_t)t, (intmax_t)t2);
234 		assert(t2 == t || t2 == t + 1); /* Almost same as first entry. */
235 		assertEqualMem(e + 54, "00000005", 8); /* File size */
236 		fs = (uint64_t)from_hex(e + 54, 8);
237 		fs += PAD(fs, 4);
238 		assertEqualInt(devmajor, from_hex(e + 62, 8)); /* devmajor */
239 		assertEqualInt(devminor, from_hex(e + 70, 8)); /* devminor */
240 		assert(is_hex(e + 78, 8)); /* rdevmajor */
241 		assert(is_hex(e + 86, 8)); /* rdevminor */
242 		assertEqualMem(e + 94, "00000008", 8); /* Name size */
243 		ns = (uint64_t)from_hex(e + 94, 8);
244 		ns += PAD(ns + 2, 4);
245 		assertEqualInt(0, from_hex(e + 102, 8)); /* check field */
246 		assertEqualMem(e + 110, "symlink\0\0\0", 10); /* Name contents */
247 		assertEqualMem(e + 110 + ns, "file1\0\0\0", 8); /* symlink target */
248 		e += 110 + fs + ns;
249 	}
250 
251 	/* "dir" */
252 	assert(is_hex(e, 110));
253 	assertEqualMem(e + 0, "070701", 6); /* Magic */
254 	assert(is_hex(e + 6, 8)); /* ino */
255 #if defined(_WIN32) && !defined(__CYGWIN__)
256 	/* Group members bits and others bits do not work. */
257 	assertEqualInt(0x41c0, from_hex(e + 14, 8) & 0xffc0); /* Mode */
258 #else
259 	/* Mode: sgid bit sometimes propagates from parent dirs, ignore it. */
260 	assertEqualInt(040775, from_hex(e + 14, 8) & ~02000);
261 #endif
262 	assertEqualInt(uid, from_hex(e + 22, 8)); /* uid */
263 	assertEqualInt(gid, from_hex(e + 30, 8)); /* gid */
264 #if !defined(_WIN32) || defined(__CYGWIN__)
265 	assertEqualInt(nlinks("dir"), from_hex(e + 38, 8)); /* nlinks */
266 #endif
267 	t2 = from_hex(e + 46, 8); /* mtime */
268 	failure("First entry created at t=%#08jx this entry created at"
269 	    "t2=%#08jx", (intmax_t)t, (intmax_t)t2);
270 	assert(t2 == t || t2 == t + 1); /* Almost same as first entry. */
271 	assertEqualMem(e + 54, "00000000", 8); /* File size */
272 	fs = (uint64_t)from_hex(e + 54, 8);
273 	fs += PAD(fs, 4);
274 	assertEqualInt(devmajor, from_hex(e + 62, 8)); /* devmajor */
275 	assertEqualInt(devminor, from_hex(e + 70, 8)); /* devminor */
276 	assert(is_hex(e + 78, 8)); /* rdevmajor */
277 	assert(is_hex(e + 86, 8)); /* rdevminor */
278 	assertEqualMem(e + 94, "00000004", 8); /* Name size */
279 	ns = (uint64_t)from_hex(e + 94, 8);
280 	ns += PAD(ns + 2, 4);
281 	assertEqualInt(0, from_hex(e + 102, 8)); /* check field */
282 	assertEqualMem(e + 110, "dir\0\0\0", 6); /* Name contents */
283 	e += 110 + fs + ns;
284 
285 	/* Hardlink identical to "file1" */
286 	/* Since we only wrote two of the three links to this
287 	 * file, this link should get deferred by the hardlink logic. */
288 	assert(is_hex(e, 110));
289 	assertEqualMem(e + 0, "070701", 6); /* Magic */
290 	failure("If these aren't the same, then the hardlink detection failed to match them.");
291 	assertEqualInt(ino, from_hex(e + 6, 8)); /* ino */
292 #if defined(_WIN32) && !defined(__CYGWIN__)
293 	/* Group members bits and others bits do not work. */
294 	assertEqualInt(0x8180, from_hex(e + 14, 8) & 0xffc0); /* Mode */
295 #else
296 	assertEqualInt(0x81a4, from_hex(e + 14, 8)); /* Mode */
297 #endif
298 	assertEqualInt(from_hex(e + 22, 8), uid); /* uid */
299 	assertEqualInt(gid, from_hex(e + 30, 8)); /* gid */
300 	assertEqualMem(e + 38, "00000003", 8); /* nlink */
301 	t2 = from_hex(e + 46, 8); /* mtime */
302 	failure("First entry created at t=%#08jx this entry created at"
303 	    "t2=%#08jx", (intmax_t)t, (intmax_t)t2);
304 	assert(t2 == t || t2 == t + 1); /* Almost same as first entry. */
305 	assertEqualInt(10, from_hex(e + 54, 8)); /* File size */
306 	fs = (uint64_t)from_hex(e + 54, 8);
307 	fs += PAD(fs, 4);
308 	assertEqualInt(devmajor, from_hex(e + 62, 8)); /* devmajor */
309 	assertEqualInt(devminor, from_hex(e + 70, 8)); /* devminor */
310 	assert(is_hex(e + 78, 8)); /* rdevmajor */
311 	assert(is_hex(e + 86, 8)); /* rdevminor */
312 	assertEqualMem(e + 94, "00000009", 8); /* Name size */
313 	ns = (uint64_t)from_hex(e + 94, 8);
314 	ns += PAD(ns + 2, 4);
315 	assertEqualInt(0, from_hex(e + 102, 8)); /* check field */
316 	assertEqualMem(e + 110, "hardlink\0\0", 10); /* Name contents */
317 	assertEqualMem(e + 110 + ns, "1234567890\0\0", 12); /* File contents */
318 	e += 110 + ns + fs;
319 
320 	/* Last entry is end-of-archive marker. */
321 	assert(is_hex(e, 110));
322 	assertEqualMem(e + 0, "070701", 6); /* Magic */
323 	assertEqualMem(e + 8, "00000000", 8); /* ino */
324 	assertEqualMem(e + 14, "00000000", 8); /* mode */
325 	assertEqualMem(e + 22, "00000000", 8); /* uid */
326 	assertEqualMem(e + 30, "00000000", 8); /* gid */
327 	assertEqualMem(e + 38, "00000001", 8); /* nlink */
328 	assertEqualMem(e + 46, "00000000", 8); /* mtime */
329 	assertEqualMem(e + 54, "00000000", 8); /* size */
330 	assertEqualMem(e + 62, "00000000", 8); /* devmajor */
331 	assertEqualMem(e + 70, "00000000", 8); /* devminor */
332 	assertEqualMem(e + 78, "00000000", 8); /* rdevmajor */
333 	assertEqualMem(e + 86, "00000000", 8); /* rdevminor */
334 	assertEqualInt(11, from_hex(e + 94, 8)); /* name size */
335 	assertEqualMem(e + 102, "00000000", 8); /* check field */
336 	assertEqualMem(e + 110, "TRAILER!!!\0\0", 12); /* Name */
337 
338 	free(p);
339 }
340