1#include <block/block.h>
2#include <console/printf.h>
3#include <fs/iso9660.h>
4#include <math/align.h>
5#include <mem/alloc.h>
6#include <stdbool.h>
7#include <stdint.h>
8#include <string.h>
9
10#include <time/time.h>
11
12static bool iso9660_read_file(struct iso9660_fs *fs, uint32_t lba,
13 uint32_t size, void *out_buf) {
14 uint32_t num_blocks = DIV_ROUND_UP(size, fs->block_size);
15
16 if (!fs->disk->read_sector(fs->disk, lba + fs->partition->start_lba,
17 out_buf, num_blocks)) {
18 return false;
19 }
20
21 return true;
22}
23
24static bool iso9660_parse_pvd(struct partition *p,
25 struct iso9660_pvd *out_pvd) {
26 uint8_t *buffer = valloc(ISO9660_SECTOR_SIZE);
27 if (!buffer)
28 return false;
29
30 struct block_device *disk = p->disk;
31
32 if (!disk->read_sector(disk, ISO9660_PVD_SECTOR + p->start_lba, buffer,
33 1)) {
34 printf(format: "Failed to read ISO9660 PVD sector\n");
35 vfree(buffer);
36 return false;
37 }
38
39 struct iso9660_pvd *pvd = (struct iso9660_pvd *) buffer;
40
41 if (pvd->type != 1 || strncmp(s1: pvd->id, s2: "CD001", n: 5) != 0 ||
42 pvd->version != 1) {
43 printf(format: "Not a valid Primary Volume Descriptor\n");
44 vfree(buffer);
45 return false;
46 }
47
48 memcpy(out_pvd, pvd, sizeof(struct iso9660_pvd));
49
50 vfree(buffer);
51 return true;
52}
53
54static void print_str(const char *label, const char *src, uint64_t len) {
55 char buf[65] = {0};
56 memcpy(buf, src, len);
57 buf[len] = '\0';
58 printf(format: "%s: \"%s\"\n", label, buf);
59}
60
61static void iso9660_pvd_print(const struct iso9660_pvd *pvd) {
62 printf(format: "=== ISO9660 Primary Volume Descriptor ===\n");
63 printf(format: "Descriptor Type: %u\n", pvd->type);
64 printf(format: "Identifier: %-5s\n", pvd->id);
65 printf(format: "Version: %u\n", pvd->version);
66
67 print_str(label: "System Identifier", src: pvd->system_id, len: 32);
68 print_str(label: "Volume Identifier", src: pvd->volume_id, len: 32);
69
70 printf(format: "Volume Space Size: %u blocks\n", pvd->volume_space_le);
71 printf(format: "Logical Block Size: %u bytes\n", pvd->logical_block_size_le);
72
73 printf(format: "Volume Set Size: %u\n", pvd->vol_set_size_le);
74 printf(format: "Volume Sequence Number: %u\n", pvd->vol_seq_num_le);
75 printf(format: "Path Table Size: %u bytes\n", pvd->path_table_size_le);
76
77 printf(format: "L Path Table Location: 0x%08X\n", pvd->l_path_table_loc);
78 printf(format: "Optional L Path Table Location: 0x%08X\n",
79 pvd->opt_l_path_table_loc);
80 printf(format: "M Path Table Location: 0x%08X\n", pvd->m_path_table_loc);
81 printf(format: "Optional M Path Table Location: 0x%08X\n",
82 pvd->opt_m_path_table_loc);
83}
84
85struct vfs_node *iso9660_mount(struct partition *p) {
86 struct iso9660_pvd pvd;
87 struct block_device *disk = p->disk;
88 if (iso9660_parse_pvd(p, out_pvd: &pvd)) {
89 struct iso9660_fs *fs = vnew(fs, ALLOC_ZERO);
90 struct iso9660_pvd *new_pvd = vnew(new_pvd, ALLOC_ZERO);
91 if (!fs || !new_pvd)
92 goto err;
93
94 fs->pvd = new_pvd;
95 memcpy(fs->pvd, &pvd, sizeof(struct iso9660_pvd));
96 fs->root_lba = pvd.root_dir_record.extent_lba_le;
97 fs->root_size = pvd.root_dir_record.size_le;
98 fs->disk = disk;
99 fs->partition = p;
100 fs->block_size = pvd.logical_block_size_le;
101 disk->fs_data = fs;
102 p->fs_data = fs;
103 return NULL;
104
105 err:
106 vfree(new_pvd);
107 vfree(fs);
108 }
109 return NULL;
110}
111
112static void iso9660_ls(struct iso9660_fs *fs, uint32_t lba, uint32_t size) {
113 uint32_t num_blocks = DIV_ROUND_UP(size, fs->block_size);
114 uint8_t *dir_data = valloc(num_blocks * fs->block_size);
115 if (!dir_data)
116 return;
117
118 if (!fs->disk->read_sector(fs->disk, lba + fs->partition->start_lba,
119 dir_data, num_blocks)) {
120 vfree(dir_data);
121 return;
122 }
123
124 uint64_t offset = 0;
125 while (offset < size) {
126 struct iso9660_dir_record *rec =
127 (struct iso9660_dir_record *) (dir_data + offset);
128
129 if (rec->length == 0) {
130 offset = ((offset / fs->block_size) + 1) * fs->block_size;
131 continue;
132 }
133
134 if (rec->name_len == 1 && (rec->name[0] == 0 || rec->name[0] == 1)) {
135 offset += rec->length;
136 continue;
137 }
138
139 char name[256] = {0};
140 memcpy(name, rec->name, rec->name_len);
141 name[rec->name_len] = '\0';
142
143 printf(format: " %s (LBA: %u, Size: %u bytes, %s)\n", name,
144 rec->extent_lba_le, rec->size_le,
145 (rec->flags & 0x02) ? "Directory" : "File");
146 if (rec->flags & 0x02) {
147 printf(format: "--Listing %s--\n", name);
148 iso9660_ls(fs, lba: rec->extent_lba_le, size: rec->size_le);
149 }
150
151 offset += rec->length;
152 }
153 vfree(dir_data);
154}
155
156void iso9660_print(struct partition *disk) {
157 struct iso9660_pvd pvd;
158 struct iso9660_fs *fs = disk->fs_data;
159 if (!iso9660_parse_pvd(p: disk, out_pvd: &pvd)) {
160 return;
161 }
162
163 iso9660_pvd_print(pvd: &pvd);
164
165 iso9660_ls(fs, lba: fs->root_lba, size: fs->root_size);
166}
167
168static struct iso9660_dir_record *iso9660_find(struct iso9660_fs *fs,
169 const char *target_name,
170 uint32_t lba, uint32_t size) {
171 uint32_t num_blocks = DIV_ROUND_UP(size, fs->block_size);
172 uint8_t *dir_data = valloc(num_blocks * fs->block_size);
173 if (!dir_data)
174 return NULL;
175
176 if (!fs->disk->read_sector(fs->disk, lba + fs->partition->start_lba,
177 dir_data, num_blocks)) {
178 vfree(dir_data);
179 return NULL;
180 }
181
182 uint64_t offset = 0;
183 while (offset < size) {
184 struct iso9660_dir_record *rec =
185 (struct iso9660_dir_record *) (dir_data + offset);
186 if (rec->length == 0) {
187 offset = ((offset / fs->block_size) + 1) * fs->block_size;
188 continue;
189 }
190
191 if (!(rec->name_len == 1 && (rec->name[0] == 0 || rec->name[0] == 1))) {
192 char name[256] = {0};
193 memcpy(name, rec->name, rec->name_len);
194 name[rec->name_len] = '\0';
195
196 if (strcmp(str1: name, str2: target_name) == 0) {
197 struct iso9660_dir_record *found = vnew(found);
198 if (!found)
199 return NULL;
200
201 memcpy(found, rec, sizeof(struct iso9660_dir_record));
202 vfree(dir_data);
203 return found;
204 }
205 }
206
207 offset += rec->length;
208 }
209
210 vfree(dir_data);
211 return NULL;
212}
213
214void iso9660_read_and_print_file(struct iso9660_fs *fs, const char *name) {
215 struct iso9660_dir_record *rec =
216 iso9660_find(fs, target_name: name, lba: fs->root_lba, size: fs->root_size);
217 if (!rec) {
218 printf(format: "File '%s' not found\n", name);
219 return;
220 }
221
222 if (rec->flags & 0x02) {
223 printf(format: "'%s' is a directory, not a file\n", name);
224 vfree(rec);
225 return;
226 }
227
228 void *buf = valloc(rec->size_le);
229 if (!buf)
230 return;
231
232 if (!iso9660_read_file(fs, lba: rec->extent_lba_le, size: rec->size_le, out_buf: buf)) {
233 printf(format: "Failed to read file contents\n");
234 vfree(buf);
235 vfree(rec);
236 return;
237 }
238
239 printf(format: "Contents of '%s':\n%.*s\n", name, rec->size_le, (char *) buf);
240
241 vfree(buf);
242 vfree(rec);
243}
244
245struct iso9660_datetime iso9660_get_current_date(void) {
246 struct iso9660_datetime dt;
247
248 dt.year = (time_get_century() * 100 + time_get_year()) - 1900;
249 dt.month = time_get_month();
250 dt.day = time_get_day();
251 dt.hour = time_get_hour();
252 dt.minute = time_get_minute();
253 dt.second = time_get_second();
254
255 dt.gmt_offset =
256 0; // TODO: once we have better stuff up and going - alter this
257
258 return dt;
259}
260