tarfs.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2018 K. Lange
  5. *
  6. * tarfs - Allows read-only mounting of ustar archives
  7. */
  8. #include <kernel/system.h>
  9. #include <kernel/types.h>
  10. #include <kernel/fs.h>
  11. #include <kernel/logging.h>
  12. #include <kernel/module.h>
  13. #include <kernel/args.h>
  14. #include <kernel/printf.h>
  15. #include <kernel/tokenize.h>
  16. #include <toaru/list.h>
  17. #include <toaru/hashmap.h>
  18. #define TARFS_LOG_LEVEL WARNING
  19. struct tarfs {
  20. fs_node_t * device;
  21. unsigned int length;
  22. };
  23. struct ustar {
  24. char filename[100];
  25. char mode[8];
  26. char ownerid[8];
  27. char groupid[8];
  28. char size[12];
  29. char mtime[12];
  30. char checksum[8];
  31. char type[1];
  32. char link[100];
  33. char ustar[6];
  34. char version[2];
  35. char owner[32];
  36. char group[32];
  37. char dev_major[8];
  38. char dev_minor[8];
  39. char prefix[155];
  40. };
  41. static unsigned int interpret_uid(struct ustar * file) {
  42. return
  43. ((file->ownerid[0] - '0') << 18) |
  44. ((file->ownerid[1] - '0') << 15) |
  45. ((file->ownerid[2] - '0') << 12) |
  46. ((file->ownerid[3] - '0') << 9) |
  47. ((file->ownerid[4] - '0') << 6) |
  48. ((file->ownerid[5] - '0') << 3) |
  49. ((file->ownerid[6] - '0') << 0);
  50. }
  51. static unsigned int interpret_gid(struct ustar * file) {
  52. return
  53. ((file->groupid[0] - '0') << 18) |
  54. ((file->groupid[1] - '0') << 15) |
  55. ((file->groupid[2] - '0') << 12) |
  56. ((file->groupid[3] - '0') << 9) |
  57. ((file->groupid[4] - '0') << 6) |
  58. ((file->groupid[5] - '0') << 3) |
  59. ((file->groupid[6] - '0') << 0);
  60. }
  61. static unsigned int interpret_mode(struct ustar * file) {
  62. return
  63. ((file->mode[0] - '0') << 18) |
  64. ((file->mode[1] - '0') << 15) |
  65. ((file->mode[2] - '0') << 12) |
  66. ((file->mode[3] - '0') << 9) |
  67. ((file->mode[4] - '0') << 6) |
  68. ((file->mode[5] - '0') << 3) |
  69. ((file->mode[6] - '0') << 0);
  70. }
  71. static unsigned int interpret_size(struct ustar * file) {
  72. return
  73. ((file->size[ 0] - '0') << 30) |
  74. ((file->size[ 1] - '0') << 27) |
  75. ((file->size[ 2] - '0') << 24) |
  76. ((file->size[ 3] - '0') << 21) |
  77. ((file->size[ 4] - '0') << 18) |
  78. ((file->size[ 5] - '0') << 15) |
  79. ((file->size[ 6] - '0') << 12) |
  80. ((file->size[ 7] - '0') << 9) |
  81. ((file->size[ 8] - '0') << 6) |
  82. ((file->size[ 9] - '0') << 3) |
  83. ((file->size[10] - '0') << 0);
  84. }
  85. static unsigned int round_to_512(unsigned int i) {
  86. unsigned int t = i % 512;
  87. if (!t) return i;
  88. return i + (512 - t);
  89. }
  90. static int ustar_from_offset(struct tarfs * self, unsigned int offset, struct ustar * out);
  91. static fs_node_t * file_from_ustar(struct tarfs * self, struct ustar * file, unsigned int offset);
  92. #ifndef strncat
  93. static char * strncat(char *dest, const char *src, size_t n) {
  94. char * end = dest;
  95. while (*end != '\0') {
  96. ++end;
  97. }
  98. size_t i = 0;
  99. while (*src && i < n) {
  100. *end = *src;
  101. end++;
  102. src++;
  103. i++;
  104. }
  105. *end = '\0';
  106. return dest;
  107. }
  108. #endif
  109. static int count_slashes(char * string) {
  110. int i = 0;
  111. char * s = strstr(string, "/");
  112. while (s) {
  113. if (*(s+1) == '\0') return i;
  114. i++;
  115. s = strstr(s+1,"/");
  116. }
  117. return i;
  118. }
  119. static struct dirent * readdir_tar_root(fs_node_t *node, uint32_t index) {
  120. if (index == 0) {
  121. struct dirent * out = malloc(sizeof(struct dirent));
  122. memset(out, 0x00, sizeof(struct dirent));
  123. out->ino = 0;
  124. strcpy(out->name, ".");
  125. return out;
  126. }
  127. if (index == 1) {
  128. struct dirent * out = malloc(sizeof(struct dirent));
  129. memset(out, 0x00, sizeof(struct dirent));
  130. out->ino = 0;
  131. strcpy(out->name, "..");
  132. return out;
  133. }
  134. index -= 2;
  135. struct tarfs * self = node->device;
  136. /* Go through each file and pick the ones are at the root */
  137. /* Root files will have no /, so this is easy */
  138. unsigned int offset = 0;
  139. struct ustar * file = malloc(sizeof(struct ustar));
  140. while (offset < self->length) {
  141. int status = ustar_from_offset(self, offset, file);
  142. if (!status) {
  143. free(file);
  144. return NULL;
  145. }
  146. char filename_workspace[256];
  147. memset(filename_workspace, 0, 256);
  148. strncat(filename_workspace, file->prefix, 155);
  149. strncat(filename_workspace, file->filename, 100);
  150. if (!count_slashes(filename_workspace)) {
  151. char * slash = strstr(filename_workspace,"/");
  152. if (slash) *slash = '\0'; /* remove trailing slash */
  153. if (strlen(filename_workspace)) {
  154. if (index == 0) {
  155. struct dirent * out = malloc(sizeof(struct dirent));
  156. memset(out, 0x00, sizeof(struct dirent));
  157. out->ino = offset;
  158. strcpy(out->name, filename_workspace);
  159. free(file);
  160. return out;
  161. } else {
  162. index--;
  163. }
  164. }
  165. }
  166. offset += 512;
  167. offset += round_to_512(interpret_size(file));
  168. }
  169. free(file);
  170. return NULL;
  171. }
  172. static uint32_t read_tarfs(fs_node_t * node, uint64_t offset, uint32_t size, uint8_t * buffer) {
  173. struct tarfs * self = node->device;
  174. struct ustar * file = malloc(sizeof(struct ustar));
  175. ustar_from_offset(self, node->inode, file);
  176. size_t file_size = interpret_size(file);
  177. if (offset > file_size) return 0;
  178. if (offset + size > file_size) {
  179. size = file_size - offset;
  180. }
  181. free(file);
  182. return read_fs(self->device, offset + node->inode + 512, size, buffer);
  183. }
  184. static struct dirent * readdir_tarfs(fs_node_t *node, uint32_t index) {
  185. if (index == 0) {
  186. struct dirent * out = malloc(sizeof(struct dirent));
  187. memset(out, 0x00, sizeof(struct dirent));
  188. out->ino = 0;
  189. strcpy(out->name, ".");
  190. return out;
  191. }
  192. if (index == 1) {
  193. struct dirent * out = malloc(sizeof(struct dirent));
  194. memset(out, 0x00, sizeof(struct dirent));
  195. out->ino = 0;
  196. strcpy(out->name, "..");
  197. return out;
  198. }
  199. index -= 2;
  200. struct tarfs * self = node->device;
  201. /* Go through each file and pick the ones are at the root */
  202. /* Root files will have no /, so this is easy */
  203. unsigned int offset = node->inode;
  204. /* Read myself */
  205. struct ustar * file = malloc(sizeof(struct ustar));
  206. int status = ustar_from_offset(self, node->inode, file);
  207. char my_filename[256];
  208. /* Figure out my own filename, with forward slash */
  209. memset(my_filename, 0, 256);
  210. strncat(my_filename, file->prefix, 155);
  211. strncat(my_filename, file->filename, 100);
  212. while (offset < self->length) {
  213. ustar_from_offset(self, offset, file);
  214. if (!status) {
  215. free(file);
  216. return NULL;
  217. }
  218. char filename_workspace[256];
  219. memset(filename_workspace, 0, 256);
  220. strncat(filename_workspace, file->prefix, 155);
  221. strncat(filename_workspace, file->filename, 100);
  222. if (startswith(filename_workspace, my_filename)) {
  223. if (!count_slashes(filename_workspace + strlen(my_filename))) {
  224. if (strlen(filename_workspace + strlen(my_filename))) {
  225. if (index == 0) {
  226. char * slash = strstr(filename_workspace+strlen(my_filename),"/");
  227. if (slash) *slash = '\0'; /* remove trailing slash */
  228. struct dirent * out = malloc(sizeof(struct dirent));
  229. memset(out, 0x00, sizeof(struct dirent));
  230. out->ino = offset;
  231. strcpy(out->name, filename_workspace+strlen(my_filename));
  232. free(file);
  233. return out;
  234. } else {
  235. index--;
  236. }
  237. }
  238. }
  239. }
  240. offset += 512;
  241. offset += round_to_512(interpret_size(file));
  242. }
  243. free(file);
  244. return NULL;
  245. }
  246. static fs_node_t * finddir_tarfs(fs_node_t *node, char *name) {
  247. struct tarfs * self = node->device;
  248. /* find my own filename */
  249. struct ustar * file = malloc(sizeof(struct ustar));
  250. ustar_from_offset(self, node->inode, file);
  251. char my_filename[256];
  252. /* Figure out my own filename, with forward slash */
  253. memset(my_filename, 0, 256);
  254. strncat(my_filename, file->prefix, 155);
  255. strncat(my_filename, file->filename, 100);
  256. /* Append name */
  257. strncat(my_filename, name, strlen(name));
  258. if (strlen(my_filename) > 255) {
  259. debug_print(CRITICAL, "what");
  260. }
  261. unsigned int offset = node->inode;
  262. while (offset < self->length) {
  263. int status = ustar_from_offset(self, offset, file);
  264. if (!status) {
  265. free(file);
  266. return NULL;
  267. }
  268. char filename_workspace[256];
  269. memset(filename_workspace, 0, 256);
  270. strncat(filename_workspace, file->prefix, 155);
  271. strncat(filename_workspace, file->filename, 100);
  272. if (filename_workspace[strlen(filename_workspace)-1] == '/') {
  273. filename_workspace[strlen(filename_workspace)-1] = '\0';
  274. }
  275. if (!strcmp(filename_workspace, my_filename)) {
  276. return file_from_ustar(self, file, offset);
  277. }
  278. offset += 512;
  279. offset += round_to_512(interpret_size(file));
  280. }
  281. free(file);
  282. return NULL;
  283. }
  284. static int readlink_tarfs(fs_node_t * node, char * buf, size_t size) {
  285. struct tarfs * self = node->device;
  286. struct ustar * file = malloc(sizeof(struct ustar));
  287. ustar_from_offset(self, node->inode, file);
  288. if (size < strlen(file->link) + 1) {
  289. debug_print(INFO, "Requested read size was only %d, need %d.", size, strlen(file->link)+1);
  290. memcpy(buf, file->link, size-1);
  291. buf[size-1] = '\0';
  292. free(file);
  293. return size-1;
  294. } else {
  295. debug_print(INFO, "Reading link target is [%s]", file->link);
  296. memcpy(buf, file->link, strlen(file->link) + 1);
  297. free(file);
  298. return strlen(file->link);
  299. }
  300. }
  301. static fs_node_t * file_from_ustar(struct tarfs * self, struct ustar * file, unsigned int offset) {
  302. fs_node_t * fs = malloc(sizeof(fs_node_t));
  303. memset(fs, 0, sizeof(fs_node_t));
  304. fs->device = self;
  305. fs->inode = offset;
  306. fs->impl = 0;
  307. char filename_workspace[256];
  308. memcpy(fs->name, filename_workspace, strlen(filename_workspace)+1);
  309. fs->uid = interpret_uid(file);
  310. fs->gid = interpret_gid(file);
  311. fs->length = interpret_size(file);
  312. fs->mask = interpret_mode(file);
  313. fs->nlink = 0; /* Unsupported */
  314. fs->flags = FS_FILE;
  315. if (file->type[0] == '5') {
  316. fs->flags = FS_DIRECTORY;
  317. fs->readdir = readdir_tarfs;
  318. fs->finddir = finddir_tarfs;
  319. } else if (file->type[0] == '1') {
  320. debug_print(ERROR, "Hardlink detected");
  321. /* go through file and find target, reassign inode to point to that */
  322. } else if (file->type[0] == '2') {
  323. fs->flags = FS_SYMLINK;
  324. fs->readlink = readlink_tarfs;
  325. } else {
  326. fs->flags = FS_FILE;
  327. fs->read = read_tarfs;
  328. }
  329. free(file);
  330. #if 0
  331. /* TODO times are also available from the file */
  332. fs->atime = now();
  333. fs->mtime = now();
  334. fs->ctime = now();
  335. #endif
  336. return fs;
  337. }
  338. static fs_node_t * finddir_tar_root(fs_node_t *node, char *name) {
  339. struct tarfs * self = node->device;
  340. unsigned int offset = 0;
  341. struct ustar * file = malloc(sizeof(struct ustar));
  342. while (offset < self->length) {
  343. int status = ustar_from_offset(self, offset, file);
  344. if (!status) {
  345. free(file);
  346. return NULL;
  347. }
  348. char filename_workspace[256];
  349. memset(filename_workspace, 0, 256);
  350. strncat(filename_workspace, file->prefix, 155);
  351. strncat(filename_workspace, file->filename, 100);
  352. if (count_slashes(filename_workspace)) {
  353. /* skip */
  354. } else {
  355. char * slash = strstr(filename_workspace,"/");
  356. if (slash) *slash = '\0';
  357. if (!strcmp(filename_workspace, name)) {
  358. return file_from_ustar(self, file, offset);
  359. }
  360. }
  361. offset += 512;
  362. offset += round_to_512(interpret_size(file));
  363. }
  364. free(file);
  365. return NULL;
  366. }
  367. static int ustar_from_offset(struct tarfs * self, unsigned int offset, struct ustar * out) {
  368. read_fs(self->device, offset, sizeof(struct ustar), (unsigned char*)out);
  369. if (out->ustar[0] != 'u' ||
  370. out->ustar[1] != 's' ||
  371. out->ustar[2] != 't' ||
  372. out->ustar[3] != 'a' ||
  373. out->ustar[4] != 'r') {
  374. return 0;
  375. }
  376. return 1;
  377. }
  378. static fs_node_t * tar_mount(char * device, char * mount_path) {
  379. char * arg = strdup(device);
  380. char * argv[10];
  381. int argc = tokenize(arg, ",", argv);
  382. if (argc > 1) {
  383. debug_print(WARNING, "tarfs driver takes no options");
  384. }
  385. fs_node_t * dev = kopen(argv[0], 0);
  386. free(arg); /* Shouldn't need the filename or args anymore */
  387. if (!dev) {
  388. debug_print(ERROR, "failed to open %s", device);
  389. return NULL;
  390. }
  391. /* Create a metadata struct for this mount */
  392. struct tarfs * self = malloc(sizeof(struct tarfs));
  393. self->device = dev;
  394. self->length = dev->length;
  395. fs_node_t * root = malloc(sizeof(fs_node_t));
  396. memset(root, 0, sizeof(fs_node_t));
  397. root->uid = 0;
  398. root->gid = 0;
  399. root->length = 0;
  400. root->mask = 0555;
  401. root->readdir = readdir_tar_root;
  402. root->finddir = finddir_tar_root;
  403. root->flags = FS_DIRECTORY;
  404. root->device = self;
  405. return root;
  406. }
  407. static int init(void) {
  408. vfs_register("tar", tar_mount);
  409. return 0;
  410. }
  411. static int fini(void) {
  412. return 0;
  413. }
  414. MODULE_DEF(tarfs, init, fini);