iso9660.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  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) 2016-2018 K. Lange
  5. *
  6. * ISO 9660 filesystem driver (for CDs)
  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 ISO_SECTOR_SIZE 2048
  19. #define FLAG_HIDDEN 0x01
  20. #define FLAG_DIRECTORY 0x02
  21. #define FLAG_ASSOCIATED 0x04
  22. #define FLAG_EXTENDED 0x08
  23. #define FLAG_PERMISSIONS 0x10
  24. #define FLAG_CONTINUES 0x80
  25. typedef struct {
  26. fs_node_t * block_device;
  27. uint32_t block_size;
  28. hashmap_t * cache;
  29. list_t * lru;
  30. } iso_9660_fs_t;
  31. typedef struct {
  32. char year[4];
  33. char month[2];
  34. char day[2];
  35. char hour[2];
  36. char minute[2];
  37. char second[2];
  38. char hundredths[2];
  39. int8_t timezone;
  40. } __attribute__((packed)) iso_9660_datetime_t;
  41. typedef struct {
  42. uint8_t year;
  43. uint8_t month;
  44. uint8_t day;
  45. uint8_t hour;
  46. uint8_t minute;
  47. uint8_t second;
  48. int8_t timezone;
  49. } __attribute__((packed)) iso_9660_rec_date_t;
  50. typedef struct {
  51. uint8_t length;
  52. uint8_t ext_length;
  53. uint32_t extent_start_LSB;
  54. uint32_t extent_start_MSB;
  55. uint32_t extent_length_LSB;
  56. uint32_t extent_length_MSB;
  57. iso_9660_rec_date_t record_date;
  58. uint8_t flags;
  59. uint8_t interleave_units;
  60. uint8_t interleave_gap;
  61. uint16_t volume_seq_LSB;
  62. uint16_t volume_seq_MSB;
  63. uint8_t name_len;
  64. char name[];
  65. } __attribute__((packed)) iso_9660_directory_entry_t;
  66. typedef struct {
  67. uint8_t type; /* 0x01 */
  68. char id[5]; /* CD001 */
  69. uint8_t version;
  70. uint8_t _unused0;
  71. char system_id[32];
  72. char volume_id[32];
  73. uint8_t _unused1[8];
  74. uint32_t volume_space_LSB;
  75. uint32_t volume_space_MSB;
  76. uint8_t _unused2[32];
  77. uint16_t volume_set_LSB;
  78. uint16_t volume_set_MSB;
  79. uint16_t volume_seq_LSB;
  80. uint16_t volume_seq_MSB;
  81. uint16_t logical_block_size_LSB;
  82. uint16_t logical_block_size_MSB;
  83. uint32_t path_table_size_LSB;
  84. uint32_t path_table_size_MSB;
  85. uint32_t path_table_LSB;
  86. uint32_t optional_path_table_LSB;
  87. uint32_t path_table_MSB;
  88. uint32_t optional_path_table_MSB;
  89. /* iso_9660_directory_entry_t */
  90. char root[34];
  91. char volume_set_id[128];
  92. char volume_publisher[128];
  93. char data_preparer[128];
  94. char application_id[128];
  95. char copyright_file[38];
  96. char abstract_file[36];
  97. char bibliographic_file[37];
  98. iso_9660_datetime_t creation;
  99. iso_9660_datetime_t modification;
  100. iso_9660_datetime_t expiration;
  101. iso_9660_datetime_t effective;
  102. uint8_t file_structure_version;
  103. uint8_t _unused_3;
  104. char application_use[];
  105. } __attribute__((packed)) iso_9660_volume_descriptor_t;
  106. static void file_from_dir_entry(iso_9660_fs_t * this, size_t sector, iso_9660_directory_entry_t * dir, size_t offset, fs_node_t * fs);
  107. #define CACHE_SIZE 64
  108. static void read_sector(iso_9660_fs_t * this, uint32_t sector_id, char * buffer) {
  109. if (this->cache) {
  110. void * sector_id_v = (void *)sector_id;
  111. if (hashmap_has(this->cache, sector_id_v)) {
  112. memcpy(buffer,hashmap_get(this->cache, sector_id_v), this->block_size);
  113. node_t * me = list_find(this->lru, sector_id_v);
  114. list_delete(this->lru, me);
  115. list_append(this->lru, me);
  116. } else {
  117. if (this->lru->length > CACHE_SIZE) {
  118. node_t * l = list_dequeue(this->lru);
  119. free(hashmap_get(this->cache, l->value));
  120. hashmap_remove(this->cache, l->value);
  121. free(l);
  122. }
  123. read_fs(this->block_device, sector_id * this->block_size, this->block_size, (uint8_t *)buffer);
  124. char * buf = malloc(this->block_size);
  125. memcpy(buf, buffer, this->block_size);
  126. hashmap_set(this->cache, sector_id_v, buf);
  127. list_insert(this->lru, sector_id_v);
  128. }
  129. } else {
  130. read_fs(this->block_device, sector_id * this->block_size, this->block_size, (uint8_t *)buffer);
  131. }
  132. }
  133. static void inplace_lower(char * string) {
  134. while (*string) {
  135. if (*string >= 'A' && *string <= 'Z') {
  136. *string += ('a' - 'A');
  137. }
  138. string++;
  139. }
  140. }
  141. static void open_iso(fs_node_t *node, unsigned int flags) {
  142. /* Nothing to do here */
  143. }
  144. static void close_iso(fs_node_t *node) {
  145. /* Nothing to do here */
  146. }
  147. static struct dirent * readdir_iso(fs_node_t *node, uint32_t index) {
  148. if (index == 0) {
  149. struct dirent * out = malloc(sizeof(struct dirent));
  150. memset(out, 0x00, sizeof(struct dirent));
  151. out->ino = 0;
  152. strcpy(out->name, ".");
  153. return out;
  154. }
  155. if (index == 1) {
  156. struct dirent * out = malloc(sizeof(struct dirent));
  157. memset(out, 0x00, sizeof(struct dirent));
  158. out->ino = 0;
  159. strcpy(out->name, "..");
  160. return out;
  161. }
  162. iso_9660_fs_t * this = node->device;
  163. char * buffer = malloc(this->block_size);
  164. read_sector(this, node->inode, buffer);
  165. iso_9660_directory_entry_t * root_entry = (iso_9660_directory_entry_t *)(buffer + node->impl);
  166. debug_print(INFO, "[iso] Reading directory for readdir; sector = %d, offset = %d", node->inode, node->impl);
  167. uint8_t * root_data = malloc(root_entry->extent_length_LSB);
  168. uint8_t * offset = root_data;
  169. size_t sector_offset = 0;
  170. size_t length_to_read = root_entry->extent_length_LSB;
  171. while (length_to_read) {
  172. read_sector(this, root_entry->extent_start_LSB + sector_offset, (char*)offset);
  173. if (length_to_read >= this->block_size) {
  174. offset += this->block_size;
  175. sector_offset += 1;
  176. length_to_read -= this->block_size;
  177. } else {
  178. break;
  179. }
  180. }
  181. debug_print(INFO, "[iso] Done, want index = %d", index);
  182. /* Examine directory */
  183. offset = root_data;
  184. unsigned int i = 0;
  185. struct dirent *dirent = malloc(sizeof(struct dirent));
  186. fs_node_t * out = malloc(sizeof(fs_node_t));
  187. memset(dirent, 0, sizeof(struct dirent));
  188. while (1) {
  189. iso_9660_directory_entry_t * dir = (iso_9660_directory_entry_t *)offset;
  190. if (dir->length == 0) {
  191. debug_print(INFO, "dir->length = %d", dir->length);
  192. if ((size_t)(offset - root_data) < root_entry->extent_length_LSB) {
  193. offset += 1; // this->block_size - ((uintptr_t)offset % this->block_size);
  194. goto try_again;
  195. }
  196. break;
  197. }
  198. if (!(dir->flags & FLAG_HIDDEN)) {
  199. debug_print(INFO, "[iso] Found file %d", i);
  200. if (i == index) {
  201. file_from_dir_entry(this, (root_entry->extent_start_LSB)+(offset - root_data)/this->block_size, dir, (offset - root_data) % this->block_size, out);
  202. memcpy(&dirent->name, out->name, strlen(out->name)+1);
  203. dirent->ino = out->inode;
  204. goto cleanup;
  205. }
  206. i += 1;
  207. }
  208. offset += dir->length;
  209. try_again:
  210. if ((size_t)(offset - root_data) > root_entry->extent_length_LSB) break;
  211. }
  212. debug_print(INFO, "offset = %x; root_data = %x; extent = %x", offset, root_data, root_entry->extent_length_LSB);
  213. free(dirent);
  214. dirent = NULL;
  215. cleanup:
  216. free(root_data);
  217. free(buffer);
  218. free(out);
  219. return dirent;
  220. }
  221. static uint32_t read_iso(fs_node_t * node, uint32_t offset, uint32_t size, uint8_t * buffer) {
  222. iso_9660_fs_t * this = node->device;
  223. char * tmp = malloc(this->block_size);
  224. read_sector(this, node->inode, tmp);
  225. iso_9660_directory_entry_t * root_entry = (iso_9660_directory_entry_t *)(tmp + node->impl);
  226. uint32_t end;
  227. /* We can do this in a single underlying read to the filesystem */
  228. if (offset + size > root_entry->extent_length_LSB) {
  229. end = root_entry->extent_length_LSB;
  230. } else {
  231. end = offset + size;
  232. }
  233. uint32_t size_to_read = end - offset;
  234. read_fs(this->block_device, root_entry->extent_start_LSB * this->block_size + offset, size_to_read, (uint8_t *)buffer);
  235. free(tmp);
  236. return size_to_read;
  237. }
  238. static fs_node_t * finddir_iso(fs_node_t *node, char *name) {
  239. iso_9660_fs_t * this = node->device;
  240. char * buffer = malloc(this->block_size);
  241. read_sector(this, node->inode, buffer);
  242. iso_9660_directory_entry_t * root_entry = (iso_9660_directory_entry_t *)(buffer + node->impl);
  243. uint8_t * root_data = malloc(root_entry->extent_length_LSB);
  244. uint8_t * offset = root_data;
  245. size_t sector_offset = 0;
  246. size_t length_to_read = root_entry->extent_length_LSB;
  247. while (length_to_read) {
  248. read_sector(this, root_entry->extent_start_LSB + sector_offset, (char*)offset);
  249. if (length_to_read >= this->block_size) {
  250. offset += this->block_size;
  251. sector_offset += 1;
  252. length_to_read -= this->block_size;
  253. } else {
  254. break;
  255. }
  256. }
  257. /* Examine directory */
  258. offset = root_data;
  259. fs_node_t * out = malloc(sizeof(fs_node_t));
  260. while (1) {
  261. iso_9660_directory_entry_t * dir = (iso_9660_directory_entry_t *)offset;
  262. if (dir->length == 0) {
  263. if ((size_t)(offset - root_data) < root_entry->extent_length_LSB) {
  264. offset += 1; // this->block_size - ((uintptr_t)offset % this->block_size);
  265. goto try_next_finddir;
  266. }
  267. break;
  268. }
  269. if (!(dir->flags & FLAG_HIDDEN)) {
  270. memset(out, 0, sizeof(fs_node_t));
  271. file_from_dir_entry(this, (root_entry->extent_start_LSB)+(offset - root_data)/this->block_size, dir, (offset - root_data) % this->block_size, out);
  272. if (!strcmp(out->name, name)) {
  273. goto cleanup; /* found it */
  274. }
  275. }
  276. offset += dir->length;
  277. try_next_finddir:
  278. if ((size_t)(offset - root_data) > root_entry->extent_length_LSB) break;
  279. }
  280. free(out);
  281. out = NULL;
  282. cleanup:
  283. free(root_data);
  284. free(buffer);
  285. return out;
  286. }
  287. static void file_from_dir_entry(iso_9660_fs_t * this, size_t sector, iso_9660_directory_entry_t * dir, size_t offset, fs_node_t * fs) {
  288. fs->device = this;
  289. fs->inode = sector; /* Sector the file is in */
  290. fs->impl = offset; /* Offset */
  291. char * file_name = malloc(dir->name_len + 1);
  292. memcpy(file_name, dir->name, dir->name_len);
  293. file_name[dir->name_len] = 0;
  294. inplace_lower(file_name);
  295. char * dot = strchr(file_name, '.');
  296. if (!dot) {
  297. /* It's a directory. */
  298. } else {
  299. char * ext = dot + 1;
  300. char * semi = strchr(ext, ';');
  301. if (semi) {
  302. *semi = 0;
  303. }
  304. if (strlen(ext) == 0) {
  305. *dot = 0;
  306. } else {
  307. char * derp = ext;
  308. while (*derp == '.') derp++;
  309. if (derp != ext) {
  310. memmove(ext, derp, strlen(derp)+1);
  311. }
  312. }
  313. }
  314. memcpy(fs->name, file_name, strlen(file_name)+1);
  315. free(file_name);
  316. fs->uid = 0;
  317. fs->gid = 0;
  318. fs->length = dir->extent_length_LSB;
  319. fs->mask = 0444;
  320. fs->nlink = 0; /* Unsupported */
  321. if (dir->flags & FLAG_DIRECTORY) {
  322. fs->flags = FS_DIRECTORY;
  323. fs->readdir = readdir_iso;
  324. fs->finddir = finddir_iso;
  325. } else {
  326. fs->flags = FS_FILE;
  327. fs->read = read_iso;
  328. }
  329. /* Other things not supported */
  330. /* TODO actually get these from the CD into Unix time */
  331. fs->atime = now();
  332. fs->mtime = now();
  333. fs->ctime = now();
  334. fs->open = open_iso;
  335. fs->close = close_iso;
  336. }
  337. static fs_node_t * iso_fs_mount(char * device, char * mount_path) {
  338. char * arg = strdup(device);
  339. char * argv[10];
  340. int argc = tokenize(arg, ",", argv);
  341. fs_node_t * dev = kopen(argv[0], 0);
  342. if (!dev) {
  343. debug_print(ERROR, "failed to open %s", device);
  344. return NULL;
  345. }
  346. int cache = 1;
  347. for (int i = 1; i < argc; ++i) {
  348. if (!strcmp(argv[i],"nocache")) {
  349. cache = 0;
  350. } else {
  351. debug_print(WARNING, "Unrecognized option to iso driver: %s", argv[i]);
  352. }
  353. }
  354. if (!dev) {
  355. debug_print(ERROR, "failed to open %s", argv[0]);
  356. free(arg);
  357. return NULL;
  358. }
  359. iso_9660_fs_t * this = malloc(sizeof(iso_9660_fs_t));
  360. this->block_device = dev;
  361. this->block_size = ISO_SECTOR_SIZE;
  362. if (cache) {
  363. this->cache = hashmap_create_int(10);
  364. this->lru = list_create();
  365. } else {
  366. this->cache = NULL;
  367. }
  368. /* Probably want to put a block cache on this like EXT2 driver does; or do that in the ATAPI layer... */
  369. debug_print(WARNING, "ISO 9660 file system driver mounting %s to %s", device, mount_path);
  370. /* Read the volume descriptors */
  371. uint8_t * tmp = malloc(ISO_SECTOR_SIZE);
  372. int i = 0x10;
  373. int found = 0;
  374. while (1) {
  375. read_sector(this,i,(char*)tmp);
  376. if (tmp[0] == 0x00) {
  377. debug_print(WARNING, " Boot Record");
  378. } else if (tmp[0] == 0x01) {
  379. debug_print(WARNING, " Primary Volume Descriptor");
  380. found = 1;
  381. break;
  382. } else if (tmp[0] == 0x02) {
  383. debug_print(WARNING, " Secondary Volume Descriptor");
  384. } else if (tmp[0] == 0x03) {
  385. debug_print(WARNING, " Volume Partition Descriptor");
  386. }
  387. if (tmp[0] == 0xFF) break;
  388. i++;
  389. }
  390. if (!found) {
  391. debug_print(WARNING, "No primary volume descriptor?");
  392. free(arg);
  393. return NULL;
  394. }
  395. iso_9660_volume_descriptor_t * root = (iso_9660_volume_descriptor_t *)tmp;
  396. debug_print(WARNING, " Volume space: %d", root->volume_space_LSB);
  397. debug_print(WARNING, " Volume set: %d", root->volume_set_LSB);
  398. debug_print(WARNING, " Volume seq: %d", root->volume_seq_LSB);
  399. debug_print(WARNING, " Block size: %d", root->logical_block_size_LSB);
  400. debug_print(WARNING, " Path table size: %d", root->path_table_size_LSB);
  401. debug_print(WARNING, " Path table loc: %d", root->path_table_LSB);
  402. iso_9660_directory_entry_t * root_entry = (iso_9660_directory_entry_t *)&root->root;
  403. debug_print(WARNING, "ISO root info:");
  404. debug_print(WARNING, " Entry len: %d", root_entry->length);
  405. debug_print(WARNING, " File start: %d", root_entry->extent_start_LSB);
  406. debug_print(WARNING, " File len: %d", root_entry->extent_length_LSB);
  407. debug_print(WARNING, " Is a directory: %s", (root_entry->flags & FLAG_DIRECTORY) ? "yes" : "no?");
  408. debug_print(WARNING, " Interleave units: %d", root_entry->interleave_units);
  409. debug_print(WARNING, " Interleave gap: %d", root_entry->interleave_gap);
  410. debug_print(WARNING, " Volume Seq: %d", root_entry->volume_seq_LSB);
  411. fs_node_t * fs = malloc(sizeof(fs_node_t));
  412. memset(fs, 0, sizeof(fs_node_t));
  413. file_from_dir_entry(this, i, root_entry, 156, fs);
  414. free(arg);
  415. return fs;
  416. }
  417. static int init(void) {
  418. vfs_register("iso", iso_fs_mount);
  419. return 0;
  420. }
  421. static int fini(void) {
  422. return 0;
  423. }
  424. MODULE_DEF(iso9660, init, fini);