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