logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/

sha1sum.c (6088B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #include "../config.h"
  6. #include "../lib/sha1.h" // sha1_*
  7. #include "../lib/strconv.h" // bytes2hex
  8. #include "../libutils/getopt_nolong.h"
  9. #include <ctype.h> // isxdigit
  10. #include <errno.h>
  11. #include <fcntl.h> // open, O_*
  12. #include <stdbool.h>
  13. #include <stdio.h> // fprintf
  14. #include <stdlib.h> // free
  15. #include <string.h> // strerror
  16. #include <unistd.h> // read, write, close, getopt
  17. #define SHA1SUM_LEN SHA1_DIGEST_LENGTH * 2 + 1
  18. const char *argv0 = "sha1sum";
  19. bool quiet = false;
  20. static int
  21. sha1sum(int fd, const char *fdname, char sum[SHA1SUM_LEN])
  22. {
  23. struct sha1 ctx;
  24. sha1_init(&ctx);
  25. uint8_t buf[BUFSIZ];
  26. ssize_t nread = -1;
  27. while((nread = read(fd, buf, BUFSIZ)) > 0)
  28. {
  29. sha1_update(&ctx, buf, nread);
  30. }
  31. if(nread < 0)
  32. {
  33. fprintf(stderr,
  34. "%s: error: Failed reading file '%s': %s\n",
  35. argv0,
  36. fdname ? fdname : "<stdin>",
  37. strerror(errno));
  38. return -1;
  39. }
  40. uint8_t res[SHA1_DIGEST_LENGTH] = "";
  41. sha1_sum(&ctx, res);
  42. bytes2hex(res, SHA1_DIGEST_LENGTH, sum, SHA1SUM_LEN);
  43. return 0;
  44. }
  45. #define STR(s) #s
  46. #define XSTR(s) STR(s)
  47. static int
  48. check(FILE *file, const char *filename)
  49. {
  50. int err = 0;
  51. ssize_t nread = -1;
  52. char *line = NULL;
  53. size_t len = 0;
  54. errno = 0;
  55. while((nread = getline(&line, &len, file)) > 0)
  56. {
  57. if(line[nread - 1] == '\n') line[nread - 1] = '\0';
  58. // Checksum + 2 characters + filename
  59. #define SHA1SUM_MIN_LINE_LEN SHA1SUM_LEN + 3
  60. if(nread < SHA1SUM_MIN_LINE_LEN)
  61. {
  62. fprintf(stderr,
  63. "%s: error: Expected at least %zd characters, got %zd for line: %s\n",
  64. argv0,
  65. (size_t)SHA1SUM_MIN_LINE_LEN,
  66. nread,
  67. line);
  68. free(line);
  69. return -1;
  70. }
  71. ssize_t i = 0;
  72. for(; i < nread; i++)
  73. {
  74. if(isxdigit(line[i])) continue;
  75. if(line[i] == ' ') line[i] = '\0';
  76. if(line[i] == '\0') break;
  77. fprintf(stderr,
  78. "%s: error: Invalid character '%c' while reading hash in line: %s\n",
  79. argv0,
  80. line[i],
  81. line);
  82. free(line);
  83. return -1;
  84. }
  85. if(i != SHA1SUM_LEN - 1)
  86. {
  87. fprintf(stderr,
  88. "%s: error: Got %zd hexadecimal digits while expected %d for a SHA1\n",
  89. argv0,
  90. i,
  91. SHA1SUM_LEN - 1);
  92. free(line);
  93. return -1;
  94. }
  95. if(line[i] != '\0')
  96. {
  97. fprintf(stderr, "%s: error: Invalid line: %s\n", argv0, line);
  98. free(line);
  99. return -1;
  100. }
  101. if(i < nread) i++;
  102. while(i < nread && line[i] == ' ')
  103. i++;
  104. if(i < nread && line[i] == '*') i++;
  105. char *target = line + i;
  106. int fd = open(target, O_RDONLY | O_NOCTTY);
  107. if(fd < 0)
  108. {
  109. fprintf(stderr, "%s: error: Failed opening file '%s': %s\n", argv0, target, strerror(errno));
  110. free(line);
  111. return -1;
  112. }
  113. int ret = posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
  114. if(ret != 0)
  115. fprintf(stderr,
  116. "%s: warning: posix_fadvise failed on file '%s': %s\n",
  117. argv0,
  118. target,
  119. strerror(ret));
  120. char got[SHA1SUM_LEN] = "";
  121. if(sha1sum(fd, target, got) < 0) err = 1;
  122. if(memcmp(line, got, SHA1SUM_LEN) == 0)
  123. {
  124. if(!quiet) printf("%s: OK\n", target);
  125. }
  126. else
  127. {
  128. err = 1;
  129. printf("%s: FAILED\n", target);
  130. }
  131. if(close(fd) < 0)
  132. {
  133. fprintf(
  134. stderr, "%s: error: Failed closing file '%s': %s\n", argv0, filename, strerror(errno));
  135. free(line);
  136. return -1;
  137. }
  138. }
  139. if(nread < 0 && errno != 0)
  140. {
  141. err = 1;
  142. fprintf(stderr,
  143. "%s: error: Failed reading line from file '%s': %s\n",
  144. argv0,
  145. filename,
  146. strerror(errno));
  147. }
  148. free(line);
  149. return err;
  150. }
  151. int
  152. main(int argc, char *argv[])
  153. {
  154. bool opt_c = false;
  155. for(int c = -1; (c = getopt_nolong(argc, argv, ":cq")) != -1;)
  156. {
  157. switch(c)
  158. {
  159. case 'c':
  160. opt_c = true;
  161. break;
  162. case 'q':
  163. quiet = true;
  164. break;
  165. case ':':
  166. fprintf(stderr, "%s: error: Missing operand for option: '-%c'\n", argv0, optopt);
  167. return 1;
  168. case '?':
  169. GETOPT_UNKNOWN_OPT
  170. return 1;
  171. default:
  172. abort();
  173. }
  174. }
  175. argc -= optind;
  176. argv += optind;
  177. if(opt_c)
  178. {
  179. if(argc == 0)
  180. {
  181. if(check(stdin, "<stdin>") != 0) return 1;
  182. return 0;
  183. }
  184. int err = 0;
  185. for(int i = 0; i < argc; i++)
  186. {
  187. FILE *file = NULL;
  188. const char *filename = argv[i];
  189. if(filename[0] == '-' && filename[1] == '\0')
  190. {
  191. filename = "<stdin>";
  192. file = stdin;
  193. }
  194. else
  195. {
  196. file = fopen(filename, "rb");
  197. if(file == NULL)
  198. {
  199. fprintf(stderr,
  200. "%s: error: Failed opening file '%s': %s\n",
  201. argv0,
  202. filename,
  203. strerror(errno));
  204. return 1;
  205. }
  206. }
  207. if(check(file, filename) != 0) err = 1;
  208. if(fclose(file) < 0)
  209. {
  210. fprintf(
  211. stderr, "%s: error: Failed closing file '%s': %s\n", argv0, filename, strerror(errno));
  212. return 1;
  213. }
  214. }
  215. return err;
  216. }
  217. if(argc == 0)
  218. {
  219. char sum[SHA1SUM_LEN] = "";
  220. if(sha1sum(STDIN_FILENO, NULL, sum) < 0) return 1;
  221. puts(sum);
  222. return 0;
  223. }
  224. for(int i = 0; i < argc; i++)
  225. {
  226. int fd = -1;
  227. const char *filename = argv[i];
  228. if(filename[0] == '-' && filename[1] == '\0')
  229. {
  230. filename = "<stdin>";
  231. fd = STDIN_FILENO;
  232. }
  233. else
  234. {
  235. fd = open(filename, O_RDONLY | O_NOCTTY);
  236. if(fd < 0)
  237. {
  238. fprintf(
  239. stderr, "%s: error: Failed opening file '%s': %s\n", argv0, filename, strerror(errno));
  240. return 1;
  241. }
  242. int ret = posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
  243. if(ret != 0)
  244. fprintf(stderr,
  245. "%s: warning: posix_fadvise failed on file '%s': %s\n",
  246. argv0,
  247. filename,
  248. strerror(ret));
  249. }
  250. int err = 0;
  251. char sum[SHA1SUM_LEN] = "";
  252. if(sha1sum(fd, filename, sum) < 0) err = 1;
  253. printf("%s %s\n", sum, filename);
  254. if(close(fd) < 0)
  255. {
  256. fprintf(
  257. stderr, "%s: error: Failed closing file '%s': %s\n", argv0, filename, strerror(errno));
  258. return 1;
  259. }
  260. if(err == 1) return 1;
  261. }
  262. return 0;
  263. }