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
is_hex(const char * p,size_t l)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
from_hex(const char * p,size_t l)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
nlinks(const char * p)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
DEFINE_TEST(test_format_newc)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