Advertisement
Guest User

ltree_patched.c

a guest
Apr 25th, 2021
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 9.27 KB | None | 0 0
  1. /*
  2.  * ltree.c
  3.  * Copyright (C) 2018 Adrian Perez <aperez@igalia.com>
  4.  *
  5.  * Distributed under terms of the MIT license.
  6.  */
  7.  
  8. #define _GNU_SOURCE 1
  9.  
  10. #include "autocleanup/autocleanup.h"
  11.  
  12. #include <archive.h>
  13. #include <archive_entry.h>
  14. #include <errno.h>
  15. #include <fcntl.h>
  16. #include <stdbool.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <unistd.h>
  20. #include <sys/stat.h>
  21. #include <sys/types.h>
  22.  
  23. #define length_of(_arr)  (sizeof (_arr) / sizeof ((_arr) [0]))
  24.  
  25. // These are set as result from command line parsing.
  26. static int opt_prefix_fd = AT_FDCWD;
  27. static int opt_print_separator = '\n';
  28. static int opt_dir_separator = '/';
  29.  
  30. typedef struct archive ArchiveRead;
  31. typedef struct archive_entry ArchiveEntry;
  32.  
  33. PTR_AUTO_DEFINE (ArchiveRead, archive_read_free)
  34.  
  35. typedef int DirFd;
  36. HANDLE_AUTO_DEFINE (DirFd, close, AT_FDCWD);
  37.  
  38.  
  39. enum {
  40.     ENTRY_OK = 0,
  41.     ENTRY_SKIP,
  42.     ENTRY_WARNING,
  43.     ENTRY_ERROR,
  44. };
  45.  
  46.  
  47. typedef int (*entry_func) (struct archive_entry*, void*);
  48.  
  49.  
  50. struct entry_action {
  51.     const char *name;
  52.     void       *data;
  53.     entry_func  check;
  54.     entry_func  action;
  55. };
  56.  
  57.  
  58. static int
  59. entry_check_exists (struct archive_entry *e, void *d)
  60. {
  61.     (void) d;
  62.  
  63.     const char *e_path = archive_entry_pathname (e);
  64.  
  65.     struct stat sb;
  66.     int ret = fstatat (opt_prefix_fd, e_path, &sb, AT_SYMLINK_NOFOLLOW);
  67.     if (ret == 0)
  68.         return ENTRY_OK;
  69.  
  70.     switch ((ret = errno)) {
  71.         case ENOENT:
  72.         case ENOTDIR:
  73.             return ENTRY_SKIP;
  74.         default:
  75.             return -errno;
  76.     }
  77. }
  78.  
  79.  
  80. static int
  81. entry_print (struct archive_entry *e, void *d)
  82. {
  83.     (void) d;
  84. //    int res = fputs (archive_entry_pathname_utf8 (e), stdout);
  85.     int res = fputs (archive_entry_pathname (e), stdout);
  86.  
  87.     if ((archive_entry_filetype(e) == AE_IFDIR) && res != EOF) {
  88.         res = putchar (opt_dir_separator);
  89.     }
  90.  
  91.     if (res == EOF || putchar (opt_print_separator) == EOF)
  92.     {
  93.         return errno;
  94.     }
  95.  
  96.     return ENTRY_OK;
  97. }
  98.  
  99.  
  100. static int
  101. entry_check (struct archive_entry *e, void *d)
  102. {
  103.     (void) d;
  104.  
  105.     struct stat sb;
  106.     const char *e_path = archive_entry_pathname (e);
  107.     if (fstatat (opt_prefix_fd, e_path, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
  108.         int ret = errno;
  109.         switch (ret) {
  110.             case ENOENT:
  111.             case ENOTDIR:
  112.                 fprintf (stderr, "%s: missing\n", e_path);
  113.                 return ENTRY_WARNING;
  114.             default:
  115.                 return -errno;
  116.         }
  117.     }
  118.  
  119.     __auto_type *e_sb = archive_entry_stat (e);
  120.     __auto_type ret = ENTRY_OK;
  121.  
  122.     if (e_sb->st_mode != sb.st_mode) {
  123.         // TODO: Print modes in a human-friendly format.
  124.         fprintf (stderr, "%s: expected mode %#lx (got %#lx)\n",
  125.                  e_path, (long) e_sb->st_mode, (long) sb.st_mode);
  126.         ret = ENTRY_WARNING;
  127.     }
  128.  
  129.     if (e_sb->st_uid != sb.st_uid) {
  130.         fprintf (stderr, "%s: expected uid %zu (got %zu)\n",
  131.                  e_path, (size_t) e_sb->st_uid, (size_t) sb.st_uid);
  132.         ret = ENTRY_WARNING;
  133.     }
  134.  
  135.     if (e_sb->st_gid != sb.st_gid) {
  136.         fprintf (stderr, "%s: expected gid %zd (got %zd)\n",
  137.                  e_path, (size_t) e_sb->st_gid, (size_t) sb.st_gid);
  138.         ret = ENTRY_WARNING;
  139.     }
  140.  
  141.     /*
  142.      * TODO: There should there be some specific checks for device nodes.
  143.      */
  144.     if (!S_ISREG(e_sb->st_mode))
  145.         return ret;
  146.  
  147.     if (e_sb->st_size != sb.st_size) {
  148.         fprintf (stderr, "%s: expected size %zu (got %zu)\n",
  149.                  e_path, e_sb->st_size, sb.st_size);
  150.         ret = ENTRY_WARNING;
  151.     }
  152.  
  153.     /*
  154.      * TODO: Honor subsecond time resolutions.
  155.      * Note that atime/ctime are not present in the mtree format.
  156.      */
  157.     if (e_sb->st_mtime != sb.st_mtime) {
  158.         fprintf (stderr, "%s: expected mtime %zu (got %zu)\n",
  159.                  e_path, e_sb->st_mtime, sb.st_mtime);
  160.         ret = ENTRY_WARNING;
  161.     }
  162.  
  163.     return ret;
  164. }
  165.  
  166.  
  167. static int
  168. entry_remove (struct archive_entry *e, void *d)
  169. {
  170.     (void) d;
  171.  
  172.     // TODO
  173.  
  174.     return ENTRY_OK;
  175. }
  176.  
  177.  
  178. enum action {
  179.     ACTION_PRINT = 0,
  180.     ACTION_CHECK,
  181.     ACTION_REMOVE,
  182.  
  183.     ACTION_UNKNOWN, // Leave always as last
  184. };
  185.  
  186. static const struct entry_action s_entry_actions[] = {
  187.     [ACTION_PRINT] = {
  188.         .name = "print",
  189.         .action = entry_print,
  190.     },
  191.     [ACTION_CHECK] = {
  192.         .name = "check",
  193.         .action = entry_check,
  194.     },
  195.     [ACTION_REMOVE] = {
  196.         .name = "remove",
  197.         .check = entry_check_exists,
  198.         .action = entry_remove,
  199.     },
  200. };
  201.  
  202.  
  203. static int
  204. entry_action_apply (const struct entry_action *ea, ArchiveEntry *e)
  205. {
  206.     int check = (ea->check) ? (*ea->check) (e, ea->data) : ENTRY_OK;
  207.     switch (check) {
  208.         case ENTRY_SKIP:
  209.             return ENTRY_OK;
  210.         case ENTRY_OK:
  211.             return (*ea->action) (e, ea->data);
  212.         default:
  213.             return check;
  214.     }
  215. }
  216.  
  217.  
  218. static inline _Bool
  219. set_action (enum action *act, enum action newact)
  220. {
  221.     if (*act == ACTION_UNKNOWN) {
  222.         *act = newact;
  223.         return true;
  224.     } else {
  225.         return false;
  226.     }
  227. }
  228.  
  229.  
  230. int
  231. main (int argc, char **argv)
  232. {
  233.     enum action opt_action = ACTION_UNKNOWN;
  234.     _Bool opt_verbose = false;
  235.     char *opt_prefix = NULL;
  236.     FILE *fd = stdin;
  237.  
  238.     ptr_auto(ArchiveRead) a = NULL;
  239.     handle_auto(DirFd) prefix_fd = AT_FDCWD;
  240.  
  241.     int opt;
  242.     while ((opt = getopt (argc, argv, "lCR0vpf:h")) != -1) {
  243.         switch (opt) {
  244.             case '0':
  245.                 opt_print_separator = '\0';
  246.                 break;
  247.  
  248.             case 'l':
  249.                 if (!set_action (&opt_action, ACTION_PRINT))
  250.                     goto getopt_action_err;
  251.                 break;
  252.  
  253.             case 'C':
  254.                 if (!set_action (&opt_action, ACTION_CHECK))
  255.                     goto getopt_action_err;
  256.                 break;
  257.  
  258.             case 'R':
  259.                 if (!set_action (&opt_action, ACTION_REMOVE))
  260.                     goto getopt_action_err;
  261.                 break;
  262.  
  263.             case 'v':
  264.                 opt_verbose = true;
  265.                 break;
  266.  
  267.             case 'p':
  268.                 opt_prefix = optarg;
  269.                 break;
  270.  
  271.             case 'f':
  272.                 fd = fopen(optarg, "r");
  273.                 if (fd == NULL) {
  274.                     fprintf (stderr, "Error opening '%s' file\n", optarg);
  275.                     return EXIT_FAILURE;
  276.                 }
  277.                 break;
  278.  
  279.             case 'h':
  280.                 fprintf (stderr, "Usage: %s [-lCR] [-0v] [-p path] < mtree\n", argv[0]);
  281.                 fprintf (stderr, "or     %s [-lCR] [-0v] [-f file] [-p path]\n", argv[0]);
  282.                 return EXIT_SUCCESS;
  283.  
  284.             default:
  285.                 return EXIT_FAILURE;
  286.         }
  287.     }
  288.  
  289.     if (opt_action == ACTION_UNKNOWN)
  290.         opt_action = ACTION_PRINT;
  291.  
  292.     if (opt_action != ACTION_PRINT && opt_prefix) {
  293.         opt_prefix_fd = prefix_fd = open (opt_prefix, O_PATH | O_DIRECTORY, 0);
  294.         if (opt_prefix_fd < 0) {
  295.             fprintf (stderr, "%s: error opening '%s': %s\n",
  296.                      argv[0], opt_prefix, strerror (errno));
  297.             return EXIT_FAILURE;
  298.         }
  299.     }
  300.  
  301.     const struct entry_action *entry_actions[2] = {
  302.         &s_entry_actions[opt_action],
  303.         (opt_verbose && opt_action != ACTION_PRINT) ?
  304.             &s_entry_actions[ACTION_PRINT] : NULL,
  305.     };
  306.  
  307.     a = archive_read_new ();
  308.     archive_read_support_format_mtree (a);
  309.  
  310.     if (archive_read_open_FILE (a, fd) != ARCHIVE_OK)
  311.         goto beach;
  312.  
  313.     int exit_code = EXIT_SUCCESS;
  314.     ArchiveEntry *e = NULL;
  315.  
  316.     for (;;) {
  317.         switch (archive_read_next_header (a, &e)) {
  318.             case ARCHIVE_WARN:
  319.                 fprintf (stderr, "Warning: %s\n", archive_error_string (a));
  320.                 exit_code = EXIT_FAILURE;
  321.                 // fall-through
  322.             case ARCHIVE_OK:
  323.                 for (unsigned i = 0; i < length_of (entry_actions); i++) {
  324.                     if (entry_actions[i]) {
  325.                         int ret = entry_action_apply (entry_actions[i], e);
  326.                         if (ret < 0) {
  327.                             fprintf (stderr, "%s: error: %s\n", argv[0], strerror (-ret));
  328.                             goto end;
  329.                         } else if (ret == ENTRY_ERROR) {
  330.                             ret = EXIT_FAILURE;
  331.                             goto end;
  332.                         } else if (ret != 0) {
  333.                             /* Warning, operation may continue. */
  334.                             ret = EXIT_FAILURE;
  335.                         }
  336.                     }
  337.                 }
  338.                 // fall-through
  339.             case ARCHIVE_RETRY:
  340.                 break;
  341.  
  342.             case ARCHIVE_FATAL:
  343.                 goto beach;
  344.  
  345.             case ARCHIVE_EOF:
  346.                 goto end;
  347.         }
  348.     }
  349. end:
  350.     if (fd != stdin)
  351.         fclose(fd);
  352.  
  353.     return exit_code;
  354.  
  355. beach:
  356.     fprintf (stderr, "%s: error: %s\n", argv[0], archive_error_string (a));
  357.  
  358.     if (fd != stdin)
  359.         fclose(fd);
  360.  
  361.     return EXIT_FAILURE;
  362.  
  363. getopt_action_err:
  364.     fprintf (stderr, "%s: Option '-%c' invalid in '%s' mode.\n", argv[0],
  365.              opt, s_entry_actions[opt_action].name);
  366.  
  367.     if (fd != stdin)
  368.         fclose(fd);
  369.  
  370.     return EXIT_FAILURE;
  371. }
  372.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement