VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/md5sum.c@ 2113

最後變更 在這個檔案從2113是 2113,由 bird 提交於 16 年 前

kmkbuiltin: include config.h

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 22.7 KB
 
1/* $Id: md5sum.c 2113 2008-12-25 13:21:58Z bird $ */
2/** @file
3 * md5sum.
4 */
5
6/*
7 * Copyright (c) 2007-2008 knut st. osmundsen <[email protected]>
8 *
9 * This file is part of kBuild.
10 *
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
23 *
24 */
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29#include "config.h"
30#include <string.h>
31#include <stdio.h>
32#include <errno.h>
33#include <fcntl.h>
34#ifdef _MSC_VER
35# include <io.h>
36#else
37# include <unistd.h>
38#endif
39#include <sys/stat.h>
40#include "err.h"
41#include "kmkbuiltin.h"
42#include "../../lib/md5.h"
43
44/*#define MD5SUM_USE_STDIO*/
45
46
47/**
48 * Prints the usage and return 1.
49 */
50static int usage(FILE *pOut)
51{
52 fprintf(pOut,
53 "usage: md5sum [-bt] [-o list-file] file(s)\n"
54 " or: md5sum [-btwq] -c list-file(s)\n"
55 " or: md5sum [-btq] -C MD5 file\n"
56 "\n"
57 " -c, --check Check MD5 and files found in the specified list file(s).\n"
58 " The default is to compute MD5 sums of the specified files\n"
59 " and print them to stdout in list form.\n"
60 " -C, --check-file This is followed by an MD5 sum and the file to check.\n"
61 " -b, --binary Read files in binary mode. (default)\n"
62 " -t, --text Read files in text mode.\n"
63 " -p, --progress Show progress indicator on large files.\n"
64 " -o, --output Name of the output list file. Useful with -p.\n"
65 " -q, --status Be quiet.\n"
66 " -w, --warn Ignored. Always warn, unless quiet.\n"
67 " -h, --help This usage info.\n"
68 " -v, --version Show version information and exit.\n"
69 );
70 return 1;
71}
72
73
74/**
75 * Makes a string out of the given digest.
76 *
77 * @param pDigest The MD5 digest.
78 * @param pszDigest Where to put the digest string. Must be able to
79 * hold at least 33 bytes.
80 */
81static void digest_to_string(unsigned char pDigest[16], char *pszDigest)
82{
83 unsigned i;
84 for (i = 0; i < 16; i++)
85 {
86 static char s_achDigits[17] = "0123456789abcdef";
87 pszDigest[i*2] = s_achDigits[(pDigest[i] >> 4)];
88 pszDigest[i*2 + 1] = s_achDigits[(pDigest[i] & 0xf)];
89 }
90 pszDigest[i*2] = '\0';
91}
92
93
94/**
95 * Attempts to convert a string to a MD5 digest.
96 *
97 * @returns 0 on success, 1-based position of the failure first error.
98 * @param pszDigest The string to interpret.
99 * @param pDigest Where to put the MD5 digest.
100 */
101static int string_to_digest(const char *pszDigest, unsigned char pDigest[16])
102{
103 unsigned i;
104 unsigned iBase = 1;
105
106 /* skip blanks */
107 while ( *pszDigest == ' '
108 || *pszDigest == '\t'
109 || *pszDigest == '\n'
110 || *pszDigest == '\r')
111 pszDigest++, iBase++;
112
113 /* convert the digits. */
114 memset(pDigest, 0, 16);
115 for (i = 0; i < 32; i++, pszDigest++)
116 {
117 int iDigit;
118 if (*pszDigest >= '0' && *pszDigest <= '9')
119 iDigit = *pszDigest - '0';
120 else if (*pszDigest >= 'a' && *pszDigest <= 'f')
121 iDigit = *pszDigest - 'a' + 10;
122 else if (*pszDigest >= 'A' && *pszDigest <= 'F')
123 iDigit = *pszDigest - 'A' + 10;
124 else
125 return i + iBase;
126 if (i & 1)
127 pDigest[i >> 1] |= iDigit;
128 else
129 pDigest[i >> 1] |= iDigit << 4;
130 }
131
132 /* the rest of the string must now be blanks. */
133 while ( *pszDigest == ' '
134 || *pszDigest == '\t'
135 || *pszDigest == '\n'
136 || *pszDigest == '\r')
137 pszDigest++, i++;
138
139 return *pszDigest ? i + iBase : 0;
140}
141
142
143/**
144 * Opens the specified file for md5 sum calculation.
145 *
146 * @returns Opaque pointer on success, NULL and errno on failure.
147 * @param pszFilename The filename.
148 * @param fText Whether text or binary mode should be used.
149 */
150static void *open_file(const char *pszFilename, unsigned fText)
151{
152#if defined(MD5SUM_USE_STDIO)
153 FILE *pFile;
154
155 errno = 0;
156 pFile = fopen(pszFilename, fText ? "r" : "rb");
157 if (!pFile && errno == EINVAL && !fText)
158 pFile = fopen(pszFilename, "r");
159 return pFile;
160
161#else
162 int fd;
163 int fFlags;
164
165 /* figure out the appropriate flags. */
166 fFlags = O_RDONLY;
167#ifdef O_SEQUENTIAL
168 fFlags |= _O_SEQUENTIAL;
169#elif defined(_O_SEQUENTIAL)
170 fFlags |= _O_SEQUENTIAL;
171#endif
172#ifdef _O_BINARY
173 if (!fText) fFlags |= _O_BINARY;
174#elif defined(_O_BINARY)
175 if (!fText) fFlags |= _O_BINARY;
176#endif
177#ifdef O_TEXT
178 if (fText) fFlags |= O_TEXT;
179#elif defined(O_TEXT)
180 if (fText) fFlags |= _O_TEXT;
181#endif
182
183 errno = 0;
184 fd = open(pszFilename, fFlags, 0755);
185 if (fd >= 0)
186 {
187 int *pFd = malloc(sizeof(*pFd));
188 if (pFd)
189 {
190 *pFd = fd;
191 return pFd;
192 }
193 close(fd);
194 errno = ENOMEM;
195 }
196
197 return NULL;
198#endif
199}
200
201
202/**
203 * Closes a file opened by open_file.
204 *
205 * @param pvFile The opaque pointer returned by open_file.
206 */
207static void close_file(void *pvFile)
208{
209#if defined(MD5SUM_USE_STDIO)
210 fclose((FILE *)pvFile);
211#else
212 close(*(int *)pvFile);
213 free(pvFile);
214#endif
215}
216
217
218/**
219 * Reads from a file opened by open_file.
220 *
221 * @returns Number of bytes read on success.
222 * 0 on EOF.
223 * Negated errno on read error.
224 * @param pvFile The opaque pointer returned by open_file.
225 * @param pvBuf Where to put the number of read bytes.
226 * @param cbBuf The max number of bytes to read.
227 * Must be less than a INT_MAX.
228 */
229static int read_file(void *pvFile, void *pvBuf, size_t cbBuf)
230{
231#if defined(MD5SUM_USE_STDIO)
232 int cb;
233
234 errno = 0;
235 cb = (int)fread(pvBuf, 1, cbBuf, (FILE *)pvFile);
236 if (cb >= 0)
237 return (int)cb;
238 if (!errno)
239 return -EINVAL;
240 return -errno;
241#else
242 int cb;
243
244 errno = 0;
245 cb = (int)read(*(int *)pvFile, pvBuf, (int)cbBuf);
246 if (cb >= 0)
247 return (int)cb;
248 if (!errno)
249 return -EINVAL;
250 return -errno;
251#endif
252}
253
254
255/**
256 * Gets the size of the file.
257 * This is informational and not necessarily 100% accurate.
258 *
259 * @returns File size.
260 * @param pvFile The opaque pointer returned by open_file
261 */
262static double size_file(void *pvFile)
263{
264#if defined(_MSC_VER)
265 __int64 cb;
266# if defined(MD5SUM_USE_STDIO)
267 cb = _filelengthi64(fileno((FILE *)pvFile));
268# else
269 cb = _filelengthi64(*(int *)pvFile);
270# endif
271 if (cb >= 0)
272 return (double)cb;
273
274#elif defined(MD5SUM_USE_STDIO)
275 struct stat st;
276 if (!fstat(fileno((FILE *)pvFile), &st))
277 return st.st_size;
278
279#else
280 struct stat st;
281 if (!fstat(*(int *)pvFile, &st))
282 return st.st_size;
283#endif
284 return 1024;
285}
286
287
288/**
289 * Calculates the md5sum of the sepecified file stream.
290 *
291 * @returns errno on failure, 0 on success.
292 * @param pvFile The file stream.
293 * @param pDigest Where to store the MD5 digest.
294 * @param fProgress Whether to show a progress bar.
295 */
296static int calc_md5sum(void *pvFile, unsigned char pDigest[16], unsigned fProgress)
297{
298 int cb;
299 int rc = 0;
300 char abBuf[32*1024];
301 struct MD5Context Ctx;
302 unsigned uPercent = 0;
303 double cbFile = 0.0;
304 double off = 0.0;
305
306 if (fProgress)
307 {
308 cbFile = size_file(pvFile);
309 if (cbFile < 1024*1024)
310 fProgress = 0;
311 }
312
313 MD5Init(&Ctx);
314 for (;;)
315 {
316 /* process a chunk. */
317 cb = read_file(pvFile, abBuf, sizeof(abBuf));
318 if (cb > 0)
319 MD5Update(&Ctx, (unsigned char *)&abBuf[0], cb);
320 else if (!cb)
321 break;
322 else
323 {
324 rc = -cb;
325 break;
326 }
327
328 /* update the progress indicator. */
329 if (fProgress)
330 {
331 unsigned uNewPercent;
332 off += cb;
333 uNewPercent = (unsigned)((off / cbFile) * 100);
334 if (uNewPercent != uPercent)
335 {
336 if (uPercent)
337 printf("\b\b\b\b");
338 printf("%3d%%", uNewPercent);
339 fflush(stdout);
340 uPercent = uNewPercent;
341 }
342 }
343 }
344 MD5Final(pDigest, &Ctx);
345
346 if (fProgress)
347 printf("\b\b\b\b \b\b\b\b");
348
349 return rc;
350}
351
352
353/**
354 * Checks the if the specified digest matches the digest of the file stream.
355 *
356 * @returns 0 on match, -1 on mismatch, errno value (positive) on failure.
357 * @param pvFile The file stream.
358 * @param Digest The MD5 digest.
359 * @param fProgress Whether to show an progress indicator on large files.
360 */
361static int check_md5sum(void *pvFile, unsigned char Digest[16], unsigned fProgress)
362{
363 unsigned char DigestFile[16];
364 int rc;
365
366 rc = calc_md5sum(pvFile, DigestFile, fProgress);
367 if (!rc)
368 rc = memcmp(Digest, DigestFile, 16) ? -1 : 0;
369 return rc;
370}
371
372
373/**
374 * Checks if the specified file matches the given MD5 digest.
375 *
376 * @returns 0 if it matches, 1 if it doesn't or an error occurs.
377 * @param pszFilename The name of the file to check.
378 * @param pszDigest The MD5 digest string.
379 * @param fText Whether to open the file in text or binary mode.
380 * @param fQuiet Whether to go about this in a quiet fashion or not.
381 * @param fProgress Whether to show an progress indicator on large files.
382 */
383static int check_one_file(const char *pszFilename, const char *pszDigest, unsigned fText, unsigned fQuiet, unsigned fProgress)
384{
385 unsigned char Digest[16];
386 int rc;
387
388 rc = string_to_digest(pszDigest, Digest);
389 if (!rc)
390 {
391 void *pvFile;
392
393 pvFile = open_file(pszFilename, fText);
394 if (pvFile)
395 {
396 if (!fQuiet)
397 fprintf(stdout, "%s: ", pszFilename);
398 rc = check_md5sum(pvFile, Digest, fProgress);
399 close_file(pvFile);
400 if (!fQuiet)
401 {
402 fprintf(stdout, "%s\n", !rc ? "OK" : rc < 0 ? "FAILURE" : "ERROR");
403 fflush(stdout);
404 if (rc > 0)
405 errx(1, "Error reading '%s': %s", pszFilename, strerror(rc));
406 }
407 if (rc)
408 rc = 1;
409 }
410 else
411 {
412 if (!fQuiet)
413 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno));
414 rc = 1;
415 }
416 }
417 else
418 {
419 errx(1, "Malformed MD5 digest '%s'!", pszDigest);
420 errx(1, " %*s^", rc - 1, "");
421 rc = 1;
422 }
423
424 return rc;
425}
426
427
428/**
429 * Checks the specified md5.lst file.
430 *
431 * @returns 0 if all checks out file, 1 if one or more fails or there are read errors.
432 * @param pszFilename The name of the file.
433 * @param fText The default mode, text or binary. Only used when fBinaryTextOpt is true.
434 * @param fBinaryTextOpt Whether a -b or -t option was specified and should be used.
435 * @param fQuiet Whether to be quiet.
436 * @param fProgress Whether to show an progress indicator on large files.
437 */
438static int check_files(const char *pszFilename, int fText, int fBinaryTextOpt, int fQuiet, unsigned fProgress)
439{
440 int rc = 0;
441 FILE *pFile;
442
443 /*
444 * Try open the md5.lst file and process it line by line.
445 */
446 pFile = fopen(pszFilename, "r");
447 if (pFile)
448 {
449 int iLine = 0;
450 char szLine[8192];
451 while (fgets(szLine, sizeof(szLine), pFile))
452 {
453 const char *pszDigest;
454 int fLineText;
455 char *psz;
456 int rc2;
457
458 iLine++;
459 psz = szLine;
460
461 /* leading blanks */
462 while (*psz == ' ' || *psz == '\t' || *psz == '\n')
463 psz++;
464
465 /* skip blank or comment lines. */
466 if (!*psz || *psz == '#' || *psz == ';' || *psz == '/')
467 continue;
468
469 /* remove the trailing newline. */
470 rc2 = (int)strlen(psz);
471 if (psz[rc2 - 1] == '\n')
472 psz[rc2 - (rc2 >= 2 && psz[rc2 - 2] == '\r' ? 2 : 1)] = '\0';
473
474 /* skip to the end of the digest and terminate it. */
475 pszDigest = psz;
476 while (*psz != ' ' && *psz != '\t' && *psz)
477 psz++;
478 if (*psz)
479 {
480 *psz++ = '\0';
481
482 /* blanks */
483 while (*psz == ' ' || *psz == '\t' || *psz == '\n')
484 psz++;
485
486 /* check for binary asterix */
487 if (*psz != '*')
488 fLineText = fBinaryTextOpt ? fText : 0;
489 else
490 {
491 fLineText = 0;
492 psz++;
493 }
494 if (*psz)
495 {
496 unsigned char Digest[16];
497
498 /* the rest is filename. */
499 pszFilename = psz;
500
501 /*
502 * Do the job.
503 */
504 rc2 = string_to_digest(pszDigest, Digest);
505 if (!rc2)
506 {
507 void *pvFile = open_file(pszFilename, fLineText);
508 if (pvFile)
509 {
510 if (!fQuiet)
511 fprintf(stdout, "%s: ", pszFilename);
512 rc2 = check_md5sum(pvFile, Digest, fProgress);
513 close_file(pvFile);
514 if (!fQuiet)
515 {
516 fprintf(stdout, "%s\n", !rc2 ? "OK" : rc2 < 0 ? "FAILURE" : "ERROR");
517 fflush(stdout);
518 if (rc2 > 0)
519 errx(1, "Error reading '%s': %s", pszFilename, strerror(rc2));
520 }
521 if (rc2)
522 rc = 1;
523 }
524 else
525 {
526 if (!fQuiet)
527 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno));
528 rc = 1;
529 }
530 }
531 else if (!fQuiet)
532 {
533 errx(1, "%s (%d): Ignoring malformed digest '%s' (digest)", pszFilename, iLine, pszDigest);
534 errx(1, "%s (%d): %*s^", pszFilename, iLine, rc2 - 1, "");
535 }
536 }
537 else if (!fQuiet)
538 errx(1, "%s (%d): Ignoring malformed line!", pszFilename, iLine);
539 }
540 else if (!fQuiet)
541 errx(1, "%s (%d): Ignoring malformed line!", pszFilename, iLine);
542 } /* while more lines */
543
544 fclose(pFile);
545 }
546 else
547 {
548 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno));
549 rc = 1;
550 }
551
552 return rc;
553}
554
555
556/**
557 * Calculates the MD5 sum for one file and prints it.
558 *
559 * @returns 0 on success, 1 on any kind of failure.
560 * @param pszFilename The file to process.
561 * @param fText The mode to open the file in.
562 * @param fQuiet Whether to be quiet or verbose about errors.
563 * @param fProgress Whether to show an progress indicator on large files.
564 * @param pOutput Where to write the list. Progress is always written to stdout.
565 */
566static int md5sum_file(const char *pszFilename, unsigned fText, unsigned fQuiet, unsigned fProgress, FILE *pOutput)
567{
568 int rc;
569 void *pvFile;
570
571 /*
572 * Calcuate and print the MD5 sum for one file.
573 */
574 pvFile = open_file(pszFilename, fText);
575 if (pvFile)
576 {
577 unsigned char Digest[16];
578
579 if (fProgress && pOutput)
580 fprintf(stdout, "%s: ", pszFilename);
581
582 rc = calc_md5sum(pvFile, Digest, fProgress);
583 close_file(pvFile);
584
585 if (fProgress && pOutput)
586 {
587 size_t cch = strlen(pszFilename) + 2;
588 while (cch-- > 0)
589 fputc('\b', stdout);
590 }
591
592 if (!rc)
593 {
594 char szDigest[36];
595 digest_to_string(Digest, szDigest);
596 if (pOutput)
597 {
598 fprintf(pOutput, "%s %s%s\n", szDigest, fText ? "" : "*", pszFilename);
599 fflush(pOutput);
600 }
601 fprintf(stdout, "%s %s%s\n", szDigest, fText ? "" : "*", pszFilename);
602 fflush(stdout);
603 }
604 else
605 {
606 if (!fQuiet)
607 errx(1, "Failed to open '%s': %s", pszFilename, strerror(rc));
608 rc = 1;
609 }
610 }
611 else
612 {
613 if (!fQuiet)
614 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno));
615 rc = 1;
616 }
617 return rc;
618}
619
620
621
622/**
623 * md5sum, calculates and checks the md5sum of files.
624 * Somewhat similar to the GNU coreutil md5sum command.
625 */
626int kmk_builtin_md5sum(int argc, char **argv, char **envp)
627{
628 int i;
629 int rc = 0;
630 int fText = 0;
631 int fBinaryTextOpt = 0;
632 int fQuiet = 0;
633 int fNewLine = 0;
634 int fChecking = 0;
635 int fProgress = 0;
636 int fNoMoreOptions = 0;
637 const char *pszOutput = NULL;
638 FILE *pOutput = NULL;
639
640 g_progname = argv[0];
641
642 /*
643 * Print usage if no arguments.
644 */
645 if (argc <= 1)
646 return usage(stderr);
647
648 /*
649 * Process the arguments, FIFO style.
650 */
651 i = 1;
652 while (i < argc)
653 {
654 char *psz = argv[i];
655 if (!fNoMoreOptions && psz[0] == '-' && psz[1] == '-' && !psz[2])
656 fNoMoreOptions = 1;
657 else if (*psz == '-' && !fNoMoreOptions)
658 {
659 psz++;
660
661 /* convert long options for gnu just for fun */
662 if (*psz == '-')
663 {
664 if (!strcmp(psz, "-binary"))
665 psz = "b";
666 else if (!strcmp(psz, "-text"))
667 psz = "t";
668 else if (!strcmp(psz, "-check"))
669 psz = "c";
670 else if (!strcmp(psz, "-check-file"))
671 psz = "C";
672 else if (!strcmp(psz, "-output"))
673 psz = "o";
674 else if (!strcmp(psz, "-progress"))
675 psz = "p";
676 else if (!strcmp(psz, "-status"))
677 psz = "q";
678 else if (!strcmp(psz, "-warn"))
679 psz = "w";
680 else if (!strcmp(psz, "-help"))
681 psz = "h";
682 else if (!strcmp(psz, "-version"))
683 psz = "v";
684 }
685
686 /* short options */
687 do
688 {
689 switch (*psz)
690 {
691 case 'c':
692 fChecking = 1;
693 break;
694
695 case 'b':
696 fText = 0;
697 fBinaryTextOpt = 1;
698 break;
699
700 case 't':
701 fText = 1;
702 fBinaryTextOpt = 1;
703 break;
704
705 case 'p':
706 fProgress = 1;
707 break;
708
709 case 'q':
710 fQuiet = 1;
711 break;
712
713 case 'w':
714 /* ignored */
715 break;
716
717 case 'h':
718 usage(stdout);
719 return 0;
720
721 case 'v':
722 return kbuild_version(argv[0]);
723
724 /*
725 * -C md5 file
726 */
727 case 'C':
728 {
729 const char *pszFilename;
730 const char *pszDigest;
731
732 if (psz[1])
733 pszDigest = &psz[1];
734 else if (i + 1 < argc)
735 pszDigest = argv[++i];
736 else
737 {
738 errx(1, "'-C' is missing the MD5 sum!");
739 return 1;
740 }
741 if (i + 1 < argc)
742 pszFilename = argv[++i];
743 else
744 {
745 errx(1, "'-C' is missing the filename!");
746 return 1;
747 }
748
749 rc |= check_one_file(pszFilename, pszDigest, fText, fQuiet, fProgress && !fQuiet);
750 psz = "\0";
751 break;
752 }
753
754 /*
755 * Output file.
756 */
757 case 'o':
758 {
759 if (fChecking)
760 {
761 errx(1, "'-o' cannot be used with -c or -C!");
762 return 1;
763 }
764
765 if (psz[1])
766 pszOutput = &psz[1];
767 else if (i + 1 < argc)
768 pszOutput = argv[++i];
769 else
770 {
771 errx(1, "'-o' is missing the file name!");
772 return 1;
773 }
774
775 psz = "\0";
776 break;
777 }
778
779 default:
780 errx(1, "Invalid option '%c'! (%s)", *psz, argv[i]);
781 return usage(stderr);
782 }
783 } while (*++psz);
784 }
785 else if (fChecking)
786 rc |= check_files(argv[i], fText, fBinaryTextOpt, fQuiet, fProgress && !fQuiet);
787 else
788 {
789 /* lazily open the output if specified. */
790 if (pszOutput)
791 {
792 if (pOutput)
793 fclose(pOutput);
794 pOutput = fopen(pszOutput, "w");
795 if (!pOutput)
796 {
797 rc = err(1, "fopen(\"%s\", \"w\") failed", pszOutput);
798 break;
799 }
800 pszOutput = NULL;
801 }
802
803 rc |= md5sum_file(argv[i], fText, fQuiet, fProgress && !fQuiet, pOutput);
804 }
805 i++;
806 }
807
808 if (pOutput)
809 fclose(pOutput);
810 return rc;
811}
812
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette