Mirror of CollapseOS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

187 lines
4.6KB

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <dirent.h>
  5. #include <string.h>
  6. #include <fnmatch.h>
  7. #include <libgen.h>
  8. #include <sys/stat.h>
  9. #define BLKSIZE 0x100
  10. #define HEADERSIZE 0x20
  11. #define MAX_FN_LEN 25 // 26 - null char
  12. #define MAX_FILE_SIZE (BLKSIZE * 0x100) - HEADERSIZE
  13. int is_regular_file(char *path)
  14. {
  15. struct stat path_stat;
  16. stat(path, &path_stat);
  17. return S_ISREG(path_stat.st_mode);
  18. }
  19. void spitempty()
  20. {
  21. putchar('C');
  22. putchar('F');
  23. putchar('S');
  24. for (int i=0; i<0x20-3; i++) {
  25. putchar(0);
  26. }
  27. }
  28. int spitblock(char *fullpath, char *fn)
  29. {
  30. FILE *fp = fopen(fullpath, "r");
  31. fseek(fp, 0, SEEK_END);
  32. long fsize = ftell(fp);
  33. if (fsize > MAX_FILE_SIZE) {
  34. fclose(fp);
  35. fprintf(stderr, "File too big: %s %ld\n", fullpath, fsize);
  36. return 1;
  37. }
  38. /* Compute block count.
  39. * We always have at least one, which contains 0x100 bytes - 0x20, which is
  40. * metadata. The rest of the blocks have a steady 0x100.
  41. */
  42. unsigned char blockcount = 1;
  43. int fsize2 = fsize - (BLKSIZE - HEADERSIZE);
  44. if (fsize2 > 0) {
  45. blockcount += (fsize2 / BLKSIZE);
  46. }
  47. if (blockcount * BLKSIZE < fsize + HEADERSIZE) {
  48. blockcount++;
  49. }
  50. putchar('C');
  51. putchar('F');
  52. putchar('S');
  53. putchar(blockcount);
  54. // file size is little endian
  55. putchar(fsize & 0xff);
  56. putchar((fsize >> 8) & 0xff);
  57. int fnlen = strlen(fn);
  58. for (int i=0; i<MAX_FN_LEN; i++) {
  59. if (i < fnlen) {
  60. putchar(fn[i]);
  61. } else {
  62. putchar(0);
  63. }
  64. }
  65. // And the last FN char which is always null
  66. putchar(0);
  67. char buf[MAX_FILE_SIZE] = {0};
  68. rewind(fp);
  69. fread(buf, fsize, 1, fp);
  70. fclose(fp);
  71. fwrite(buf, (blockcount * BLKSIZE) - HEADERSIZE, 1, stdout);
  72. fflush(stdout);
  73. return 0;
  74. }
  75. int spitdir(char *path, char *prefix, char **patterns)
  76. {
  77. DIR *dp;
  78. struct dirent *ep;
  79. int prefixlen = strlen(prefix);
  80. dp = opendir(path);
  81. if (dp == NULL) {
  82. fprintf(stderr, "Couldn't open directory.\n");
  83. return 1;
  84. }
  85. while (ep = readdir(dp)) {
  86. if ((strcmp(ep->d_name, ".") == 0) || strcmp(ep->d_name, "..") == 0) {
  87. continue;
  88. }
  89. if (ep->d_type != DT_DIR && ep->d_type != DT_REG) {
  90. fprintf(stderr, "Only regular file or directories are supported\n");
  91. return 1;
  92. }
  93. int slen = strlen(ep->d_name);
  94. if (prefixlen + slen> MAX_FN_LEN) {
  95. fprintf(stderr, "Filename too long: %s/%s\n", prefix, ep->d_name);
  96. return 1;
  97. }
  98. char fullpath[0x1000];
  99. strcpy(fullpath, path);
  100. strcat(fullpath, "/");
  101. strcat(fullpath, ep->d_name);
  102. char newprefix[MAX_FN_LEN];
  103. strcpy(newprefix, prefix);
  104. if (prefixlen > 0) {
  105. strcat(newprefix, "/");
  106. }
  107. strcat(newprefix, ep->d_name);
  108. if (ep->d_type == DT_DIR) {
  109. int r = spitdir(fullpath, newprefix, patterns);
  110. if (r != 0) {
  111. return r;
  112. }
  113. } else {
  114. char **p = patterns;
  115. // if we have no pattern, we match all
  116. int matches = (*p) == NULL ? 1 : 0;
  117. while (*p) {
  118. if (fnmatch(*p, ep->d_name, 0) == 0) {
  119. matches = 1;
  120. break;
  121. }
  122. p++;
  123. }
  124. if (!matches) {
  125. continue;
  126. }
  127. int r = spitblock(fullpath, newprefix);
  128. if (r != 0) {
  129. return r;
  130. }
  131. }
  132. }
  133. closedir(dp);
  134. return 0;
  135. }
  136. void usage()
  137. {
  138. fprintf(stderr, "Usage: cfspack [-p pattern] [/path/to/dir...]\n");
  139. }
  140. int main(int argc, char *argv[])
  141. {
  142. int patterncount = 0;
  143. char **patterns = malloc(sizeof(char**));
  144. patterns[0] = NULL;
  145. while (1) {
  146. int c = getopt(argc, argv, "p:");
  147. if (c < 0) {
  148. break;
  149. }
  150. switch (c) {
  151. case 'p':
  152. patterns[patterncount] = optarg;
  153. patterncount++;
  154. patterns = realloc(patterns, sizeof(char**)*(patterncount+1));
  155. patterns[patterncount] = NULL;
  156. break;
  157. default:
  158. usage();
  159. return 1;
  160. }
  161. }
  162. int res = 0;
  163. for (int i=optind; i<argc; i++) {
  164. if (is_regular_file(argv[i])) {
  165. // special case: just one file
  166. res = spitblock(argv[i], basename(argv[i]));
  167. } else {
  168. res = spitdir(argv[i], "", patterns);
  169. }
  170. }
  171. if (res == 0) {
  172. spitempty();
  173. }
  174. free(patterns);
  175. return res;
  176. }