"Fossies" - the Fresh Open Source Software Archive 
Member "schily-2021-09-18/tartest/tartest.c" (20 Aug 2021, 15540 Bytes) of package /linux/privat/schily-2021-09-18.tar.bz2:
1 /* @(#)tartest.c 1.24 21/08/20 Copyright 2002-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)tartest.c 1.24 21/08/20 Copyright 2002-2021 J. Schilling";
6 #endif
7 /*
8 * Copyright (c) 2002-2021 J. Schilling
9 */
10 /*
11 * The contents of this file are subject to the terms of the
12 * Common Development and Distribution License, Version 1.0 only
13 * (the "License"). You may not use this file except in compliance
14 * with the License.
15 *
16 * See the file CDDL.Schily.txt in this distribution for details.
17 * A copy of the CDDL is also available via the Internet at
18 * http://www.opensource.org/licenses/cddl1.txt
19 *
20 * When distributing Covered Code, include this CDDL HEADER in each
21 * file and include the License file CDDL.Schily.txt from this distribution.
22 */
23
24 #include <schily/stdio.h>
25 #include <schily/stdlib.h>
26
27 #include "star.h"
28 #include <schily/standard.h>
29 #include <schily/string.h>
30 #include <schily/getargs.h>
31 #define GT_COMERR /* #define comerr gtcomerr */
32 #define GT_ERROR /* #define error gterror */
33 #include <schily/schily.h>
34
35 #include <schily/fcntl.h> /* O_BINARY */
36 #include <schily/io.h> /* for setmode() prototype */
37 #include <schily/nlsdefs.h>
38
39 LOCAL void usage __PR((int ret));
40 EXPORT int main __PR((int ac, char *av[]));
41 LOCAL BOOL doit __PR((FILE *f));
42 LOCAL BOOL checkhdr __PR((TCB *ptb));
43 LOCAL BOOL checkoctal __PR((char *ptr, int len, char *text));
44 LOCAL BOOL checktype __PR((TCB *ptb));
45 LOCAL BOOL checkid __PR((char *ptr, char *text));
46 LOCAL BOOL checkmagic __PR((char *ptr));
47 LOCAL BOOL checkvers __PR((char *ptr));
48 EXPORT void stolli __PR((char *s, Ullong *ull, int len));
49 LOCAL Ulong checksum __PR((TCB *ptb));
50 LOCAL void pretty_char __PR((char *p, unsigned c));
51
52 LOCAL BOOL verbose;
53 LOCAL BOOL signedcksum;
54 LOCAL BOOL is_posix_2001;
55
56 LOCAL void
57 usage(ret)
58 int ret;
59 {
60 error("Usage:\t%s [options] < file\n", get_progname());
61 error("Options:\n");
62 error("\t-help\t\tprint this help\n");
63 error("\t-version\tPrint version number.\n");
64 error("\t-v\t\tprint all filenames during verification\n");
65 error("\n%s checks stdin fore compliance with the POSIX.1-1990 TAR standard\n", get_progname());
66 exit(ret);
67 /* NOTREACHED */
68 }
69
70 EXPORT int
71 main(ac, av)
72 int ac;
73 char *av[];
74 {
75 int cac = ac;
76 char *const *cav = av;
77 BOOL help = FALSE;
78 BOOL prversion = FALSE;
79
80 save_args(ac, av);
81
82 (void) setlocale(LC_ALL, "");
83
84 #ifdef USE_NLS
85 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
86 #define TEXT_DOMAIN "tartest" /* Use this only if it weren't */
87 #endif
88 { char *dir;
89 dir = searchfileinpath("share/locale", F_OK,
90 SIP_ANY_FILE|SIP_NO_PATH, NULL);
91 if (dir)
92 (void) bindtextdomain(TEXT_DOMAIN, dir);
93 else
94 #if defined(PROTOTYPES) && defined(INS_BASE)
95 (void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale");
96 #else
97 (void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale");
98 #endif
99 (void) textdomain(TEXT_DOMAIN);
100 }
101 #endif /* USE_NLS */
102 cac--;
103 cav++;
104 if (getallargs(&cac, &cav, "help,h,version,v", &help, &help,
105 &prversion, &verbose) < 0) {
106 errmsgno(EX_BAD, "Bad Option: '%s'.\n", cav[0]);
107 usage(EX_BAD);
108 }
109 if (help)
110 usage(0);
111
112 printf("tartest %s (%s-%s-%s)\n\n", "1.24",
113 HOST_CPU, HOST_VENDOR, HOST_OS);
114 gtprintf("Copyright (C) 2002-2021 %s\n", _("Jörg Schilling"));
115 gtprintf("This is free software; see the source for copying conditions. There is NO\n");
116 gtprintf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
117 if (prversion)
118 exit(0);
119
120 gtprintf("\nTesting for POSIX.1-1990 TAR compliance...\n");
121
122 setmode(fileno(stdin), O_BINARY);
123
124 if (!doit(stdin)) {
125 gtprintf(">>> Archive is not POSIX.1-1990 TAR standard compliant.\n");
126 return (1);
127 }
128 gtprintf("No deviations from POSIX.1-1990 TAR standard found.\n");
129 return (0);
130 }
131
132 LOCAL BOOL
133 doit(f)
134 FILE *f;
135 {
136 BOOL ret = TRUE;
137 BOOL r;
138 TCB tcb;
139 TCB *ptb;
140 char name[257];
141 char lname[101];
142 Ullong checks;
143 Ullong hsum;
144 Ullong size;
145 Ullong blockno = 0;
146 int i;
147
148 ptb = &tcb;
149 for (;;) {
150 r = TRUE;
151 fillbytes(ptb, TBLOCK, '\0');
152 if (fileread(f, ptb, TBLOCK) != TBLOCK) {
153 gtprintf("Hard EOF at block %lld\n", blockno);
154 return (FALSE);
155 }
156
157 stolli(ptb->ustar_dbuf.t_chksum, &checks, 8);
158 hsum = checksum(ptb);
159 if (hsum == 0) {
160 /*
161 * Check EOF
162 */
163 gtprintf("Found 1st EOF block at %lld\n", blockno);
164 blockno++;
165 if (fileread(f, ptb, TBLOCK) != TBLOCK) {
166 gtprintf(
167 "Hard EOF at block %lld (second EOF block missing)\n",
168 blockno);
169 return (FALSE);
170 }
171 hsum = checksum(ptb);
172 if (hsum != 0) {
173 gtprintf(
174 "Second EOF block missing at %lld\n",
175 blockno);
176 return (FALSE);
177 }
178 gtprintf("Found 2nd EOF block at %lld\n", blockno);
179 return (ret);
180 }
181 if (checks != hsum) {
182 gtprintf("Bad Checksum %0llo != %0llo at block %lld\n",
183 checks, hsum, blockno);
184 signedcksum = TRUE;
185 hsum = checksum(ptb);
186 if (checks == hsum) {
187 gtprintf("Warning: archive uses signed checksums.\n");
188 return (FALSE);
189 }
190 if (blockno != 0) {
191 if (is_posix_2001) {
192 gtprintf(
193 "The archive may either be corrupted or using the POSIX.1-2001 size field.\n");
194 } else {
195 gtprintf(
196 "Warning: Corrupted TAR archive.\n");
197 }
198 }
199 return (FALSE);
200 }
201 blockno++;
202
203 stolli(ptb->ustar_dbuf.t_size, &size, 12);
204
205 if (ptb->ustar_dbuf.t_prefix[0]) {
206 js_snprintf(name, sizeof (name), "%.155s/%.100s",
207 ptb->ustar_dbuf.t_prefix,
208 ptb->ustar_dbuf.t_name);
209 } else {
210 strncpy(name, ptb->ustar_dbuf.t_name, 100);
211 name[100] = '\0';
212 }
213 strncpy(lname, ptb->ustar_dbuf.t_linkname, 100);
214 lname[100] = '\0';
215
216 r = checkhdr(ptb);
217 if (!r)
218 ret = FALSE;
219
220 /*
221 * Handle the size field acording to POSIX.1-1990.
222 */
223 i = tarblocks(size);
224 switch ((int)(ptb->ustar_dbuf.t_typeflag & 0xFF)) {
225
226 case '\0': /* Old plain file */
227 case '0': /* Ustar plain file */
228 case '7': /* Contiguous file */
229 break;
230
231 case '1': /* Hard link */
232 case '2': /* Symbolic link */
233 if (i != 0) {
234 gtprintf(
235 "Warning: t_size field: %0llu, should be 0 for %s link\n",
236 size,
237 ptb->ustar_dbuf.t_typeflag == '1'?
238 "hard":"symbolic");
239 ret = r = FALSE;
240 }
241 i = 0;
242 break;
243 case '3': /* Character special */
244 case '4': /* Block special */
245 case '5': /* Directory */
246 case '6': /* FIFO (named pipe) */
247 i = 0;
248 break;
249 }
250
251 if (!r || verbose) {
252 gtprintf("*** %sFilename '%s'\n",
253 r == FALSE ?
254 "Failing ":"", name);
255 if (lname[0]) {
256 gtprintf("*** %sLinkname '%s'\n",
257 r == FALSE ?
258 "Failing ":"", lname);
259 }
260 }
261
262 /*
263 * Skip file content.
264 */
265 while (--i >= 0) {
266 if (fileread(f, ptb, TBLOCK) != TBLOCK) {
267 gtprintf("Hard EOF at block %lld\n", blockno);
268 return (FALSE);
269 }
270 blockno++;
271 }
272 }
273
274 }
275
276 LOCAL BOOL
277 checkhdr(ptb)
278 TCB *ptb;
279 {
280 BOOL ret = TRUE;
281 int errs = 0;
282 Ullong ll;
283
284 if (ptb->ustar_dbuf.t_name[ 0] == '\0') {
285 gtprintf("Warning: t_name[ 0] is a null character.\n");
286 errs++;
287 }
288 if (ptb->ustar_dbuf.t_name[ 0] != '\0' &&
289 ptb->ustar_dbuf.t_name[ 99] != '\0' &&
290 /* LINTED */
291 ptb->ndbuf.t_name[100] == '\0') {
292 gtprintf("Warning: t_name[100] is a null character.\n");
293 errs++;
294 }
295 if (ptb->ustar_dbuf.t_linkname[ 0] != '\0' &&
296 ptb->ustar_dbuf.t_linkname[ 99] != '\0' &&
297 /* LINTED */
298 ptb->ndbuf.t_linkname[100] == '\0') {
299 gtprintf("Warning: t_linkname[100] is a null character.\n");
300 errs++;
301 }
302
303 if (!checkoctal(ptb->ustar_dbuf.t_mode, 8, "t_mode"))
304 errs++;
305
306 stolli(ptb->ustar_dbuf.t_mode, &ll, 8);
307 if (ll & ~07777) {
308 gtprintf(
309 "Warning: too many bits in t_mode field: 0%llo, should be 0%llo\n",
310 ll, ll & 07777);
311 errs++;
312 }
313
314 if (!checkoctal(ptb->ustar_dbuf.t_uid, 8, "t_uid"))
315 errs++;
316
317 if (!checkoctal(ptb->ustar_dbuf.t_gid, 8, "t_gid"))
318 errs++;
319
320 if (!checkoctal(ptb->ustar_dbuf.t_size, 12, "t_size"))
321 errs++;
322
323 if (!checkoctal(ptb->ustar_dbuf.t_mtime, 12, "t_mtime"))
324 errs++;
325
326 if (!checkoctal(ptb->ustar_dbuf.t_chksum, 8, "t_chksum"))
327 errs++;
328
329 if (!checktype(ptb))
330 errs++;
331
332 if (!checkmagic(ptb->ustar_dbuf.t_magic))
333 errs++;
334
335 if (!checkvers(ptb->ustar_dbuf.t_version))
336 errs++;
337
338 if (!checkid(ptb->ustar_dbuf.t_uname, "t_uname"))
339 errs++;
340 if (!checkid(ptb->ustar_dbuf.t_gname, "t_gname"))
341 errs++;
342
343 if (!checkoctal(ptb->ustar_dbuf.t_devmajor, 8, "t_devmajor"))
344 errs++;
345
346 if (!checkoctal(ptb->ustar_dbuf.t_devminor, 8, "t_devminor"))
347 errs++;
348
349 #ifdef __needed__
350 /*
351 * The POSIX.1 TAR standard does not mention the last 12 bytes in the
352 * TAR header. They may have any value...
353 */
354 if (cmpnullbytes(ptb->ustar_dbuf.t_mfill, 12) < 12) {
355 gtprintf(
356 "Warning: non null character in last 12 bytes of header\n");
357 errs++;
358 }
359 #endif
360
361 if (errs)
362 ret = FALSE;
363 return (ret);
364 }
365
366 /*
367 * Check whether octal numeric fields are according to POSIX.1-1990.
368 */
369 LOCAL BOOL
370 checkoctal(ptr, len, text)
371 char *ptr;
372 int len;
373 char *text;
374 {
375 BOOL ret = TRUE;
376 BOOL foundoctal = FALSE;
377 int i;
378 int endoff = 0;
379 #ifdef END_ALL_THESAME
380 char endc = '\0';
381 #endif
382 char cs[4];
383
384 for (i = 0; i < len; i++) {
385 #ifdef CHECKOCTAL_DEBUG
386 error("%d '%c'\n", i, ptr[i]);
387 #endif
388
389 #ifdef END_ALL_THESAME
390 if (endoff > 0 && ptr[i] != endc) {
391 #else
392 /*
393 * Ugly, but the standard seems to allow mixins space and null
394 * characters at the end of an octal numeric field.
395 */
396 if (endoff > 0 && (ptr[i] != ' ' && ptr[i] != '\0')) {
397 #endif
398 pretty_char(cs, ptr[i] & 0xFF);
399 gtprintf(
400 "Warning: illegal end character '%s' (0x%02X) found in field '%s[%d]'\n",
401 cs,
402 ptr[i] & 0xFF,
403 text, i);
404 ret = FALSE;
405 }
406 if (endoff > 0)
407 continue;
408 if (ptr[i] == ' ' || ptr[i] == '\0') {
409 if (foundoctal) {
410 endoff = i;
411 #ifdef END_ALL_THESAME
412 endc = ptr[i];
413 #endif
414 continue;
415 }
416 }
417 if (!isoctal(ptr[i])) {
418 pretty_char(cs, ptr[i] & 0xFF);
419 gtprintf(
420 "Warning: non octal character '%s' (0x%02X) found in field '%s[%d]'\n",
421 cs,
422 ptr[i] & 0xFF,
423 text, i);
424 ret = FALSE;
425 } else {
426 foundoctal = TRUE;
427 }
428 }
429 if (foundoctal && endoff == 0) {
430 gtprintf("Warning: no end character found in field '%s'\n",
431 text);
432 ret = FALSE;
433 }
434 return (ret);
435 }
436
437 /*
438 * Check whether the POSIX.1-1990 'typeflag' field contains a valid character.
439 */
440 LOCAL BOOL
441 checktype(ptb)
442 TCB *ptb;
443 {
444 BOOL ret = TRUE;
445 char cs[4];
446
447 switch ((int)(ptb->ustar_dbuf.t_typeflag & 0xFF)) {
448
449 case '\0': /* Old plain file */
450 case '0': /* Ustar plain file */
451 case '1': /* Hard link */
452 case '2': /* Symbolic link */
453 case '3': /* Character special */
454 case '4': /* Block special */
455 case '5': /* Directory */
456 case '6': /* FIFO (named pipe) */
457 case '7': /* Contiguous file */
458 break;
459
460 case 'g':
461 case 'x':
462 if (!is_posix_2001) {
463 gtprintf("Warning: Archive uses POSIX.1-2001 extensions.\n");
464 gtprintf("Warning: The correctness of the size field cannot be checked for this reason.\n");
465 is_posix_2001 = TRUE;
466 }
467 break;
468
469 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
470 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
471 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
472 case 'V': case 'W': case 'X': case 'Y': case 'Z':
473 { static char vend[256];
474 if (vend[ptb->ustar_dbuf.t_typeflag & 0xFF] == 0) {
475 gtprintf(
476 "Warning: Archive uses Vendor specific extension file type '%c'.\n",
477 ptb->ustar_dbuf.t_typeflag & 0xFF);
478 vend[ptb->ustar_dbuf.t_typeflag & 0xFF] = 1;
479 }
480 }
481 break;
482
483 default:
484 pretty_char(cs, ptb->ustar_dbuf.t_typeflag & 0xFF);
485 gtprintf(
486 "Warning: Archive uses illegal file type '%s' (0x%02X).\n",
487 cs,
488 ptb->ustar_dbuf.t_typeflag & 0xFF);
489 ret = FALSE;
490 }
491 return (ret);
492 }
493
494 /*
495 * Check whether the POSIX.1-1990 'uid' or 'gid' field contains
496 * reasonable things.
497 */
498 LOCAL BOOL
499 checkid(ptr, text)
500 char *ptr;
501 char *text;
502 {
503 BOOL ret = TRUE;
504 char cs[4];
505 int len = 32;
506 int i;
507
508 if (ptr[0] == '\0') {
509 for (i = 0; i < len; i++) {
510 if (ptr[i] != '\0') {
511 pretty_char(cs, ptr[i] & 0xFF);
512 gtprintf(
513 "Warning: non null character '%s' (0x%02X) found in field '%s[%d]'\n",
514 cs,
515 ptr[i] & 0xFF,
516 text, i);
517 ret = FALSE;
518 }
519 }
520 return (ret);
521 }
522 i = len - 1;
523 if (ptr[i] != '\0') {
524 pretty_char(cs, ptr[i] & 0xFF);
525 gtprintf(
526 "Warning: non null string terminator character '%s' (0x%02X) found in field '%s[%d]'\n",
527 cs,
528 ptr[i] & 0xFF,
529 text, i);
530 ret = FALSE;
531 }
532 return (ret);
533 }
534
535 /*
536 * Check whether the POSIX.1-1990 'magic' field contains the
537 * two string "ustar" (5 characters and a null byte).
538 */
539 LOCAL BOOL
540 checkmagic(ptr)
541 char *ptr;
542 {
543 BOOL ret = TRUE;
544 char *mag = "ustar";
545 char cs[4];
546 int i;
547
548 for (i = 0; i < 6; i++) {
549 if (ptr[i] != mag[i]) {
550 pretty_char(cs, ptr[i] & 0xFF);
551 gtprintf(
552 "Warning: illegal character '%s' (0x%02X) found in field 't_magic[%d]'\n",
553 cs,
554 ptr[i] & 0xFF, i);
555 ret = FALSE;
556 }
557 }
558 return (ret);
559 }
560
561 /*
562 * Check whether the POSIX.1-1990 'version' field contains the
563 * two characters "00".
564 */
565 LOCAL BOOL
566 checkvers(ptr)
567 char *ptr;
568 {
569 BOOL ret = TRUE;
570 char *vers = "00";
571 char cs[4];
572 int i;
573
574 for (i = 0; i < 2; i++) {
575 if (ptr[i] != vers[i]) {
576 pretty_char(cs, ptr[i] & 0xFF);
577 gtprintf(
578 "Warning: illegal character '%s' (0x%02X) found in field 't_version[%d]'\n",
579 cs,
580 ptr[i] & 0xFF, i);
581 ret = FALSE;
582 }
583 }
584 return (ret);
585 }
586
587
588 /*
589 * Convert string -> long long int
590 * This is the debug version that stops at "len" size to be safe against
591 * field overflow.
592 */
593 EXPORT void
594 stolli(s, ull, len)
595 register char *s;
596 Ullong *ull;
597 int len;
598 {
599 register Ullong ret = (Ullong)0;
600 register char c;
601 register int t;
602
603 while (*s == ' ') {
604 if (--len < 0)
605 break;
606 s++;
607 }
608
609 for (;;) {
610 if (--len < 0)
611 break;
612 c = *s++;
613 #ifdef STOLLI_DEBUG
614 error("'%c'\n", c);
615 #endif
616 if (isoctal(c))
617 t = c - '0';
618 else
619 break;
620 ret *= 8;
621 ret += t;
622 }
623 *ull = ret;
624 #ifdef STOLLI_DEBUG
625 error("len: %d\n", len);
626 #endif
627 }
628
629 /*
630 * Checsum function.
631 * Returns 0 if the block contains nothing but null characters.
632 */
633 #define CHECKS sizeof (ptb->ustar_dbuf.t_chksum)
634 /*
635 * We know, that sizeof (TCP) is 512 and therefore has no
636 * reminder when dividing by 8
637 *
638 * CHECKS is known to be 8 too, use loop unrolling.
639 */
640 #define DO8(a) a; a; a; a; a; a; a; a;
641
642 LOCAL Ulong
643 checksum(ptb)
644 register TCB *ptb;
645 {
646 register int i;
647 register Ulong sum = 0;
648 register Uchar *us;
649
650 if (signedcksum) {
651 register char *ss;
652
653 ss = (char *)ptb;
654 for (i = sizeof (*ptb)/8; --i >= 0; ) {
655 DO8(sum += *ss++);
656 }
657 if (sum == 0L) /* Block containing 512 nul's */
658 return (sum);
659
660 ss = (char *)ptb->ustar_dbuf.t_chksum;
661 DO8(sum -= *ss++);
662 sum += CHECKS*' ';
663 } else {
664 us = (Uchar *)ptb;
665 for (i = sizeof (*ptb)/8; --i >= 0; ) {
666 DO8(sum += *us++);
667 }
668 if (sum == 0L) /* Block containing 512 nul's */
669 return (sum);
670
671 us = (Uchar *)ptb->ustar_dbuf.t_chksum;
672 DO8(sum -= *us++);
673 sum += CHECKS*' ';
674 }
675 return (sum);
676 }
677
678 /*
679 * Pretty print one character.
680 * Quote anything that is not a printable 7 bit ASCII character.
681 */
682 #define SP ' '
683 #define DEL '\177'
684 #define SP8 (SP | 0x80)
685 #define DEL8 (DEL | 0x80)
686
687 LOCAL void
688 pretty_char(p, c)
689 char *p;
690 unsigned c;
691 {
692 c &= 0xFF;
693
694 if (c < SP || c == DEL) { /* ctl char */
695 *p++ = '^';
696 *p++ = c ^ 0100;
697 } else if ((c > DEL && c < SP8) || c == DEL8) { /* 8 bit ctl */
698 *p++ = '~';
699 *p++ = '^';
700 *p++ = c ^ 0300;
701 } else if (c >= SP8) { /* 8 bit char */
702 *p++ = '~';
703 *p++ = c & 0177;
704 } else { /* normal char */
705 *p++ = c;
706 }
707 *p = '\0';
708 }