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