1#include <block/block.h>
2#include <compiler/core.h>
3#include <console/printf.h>
4#include <fs/detect.h>
5#include <fs/ext2.h>
6#include <fs/fat.h>
7#include <fs/gpt.h>
8#include <fs/iso9660.h>
9#include <fs/mbr.h>
10#include <math/units.h>
11#include <mem/alloc.h>
12#include <stdbool.h>
13#include <stdint.h>
14#include <string.h>
15
16LOG_HANDLE_DEFINE_PRINT(fs_detect);
17LOG_SITE_DEFINE_PRINT(fs_detect);
18
19/* there is no point in using the bcache for these operations */
20const char *detect_fstr(enum fs_type type) {
21 switch (type) {
22 case FS_FAT12: return "FAT12";
23 case FS_FAT16: return "FAT16";
24 case FS_FAT32: return "FAT32";
25 case FS_EXFAT: return "exFAT";
26 case FS_EXT2: return "EXT2";
27 case FS_EXT3: return "EXT3";
28 case FS_EXT4: return "EXT4";
29 case FS_NTFS: return "NTFS";
30 case FS_ISO9660: return "ISO9660";
31 case FS_TMPFS: return "TMPFS";
32 case FS_DEVTMPFS: return "DEVTMPFS";
33 default: return "Unknown";
34 }
35}
36
37static struct vfs_node *dummy_mount(struct partition *p) {
38 cc_ignore(p);
39 return NULL;
40}
41
42void dummy_print(struct partition *p) {
43 printf(format: "error: filesystem \"%s\" not implemented\n",
44 detect_fstr(type: p->disk->fs_type));
45}
46
47static void make_partition(struct partition *part, struct block_device *disk,
48 uint64_t start_lba, uint64_t sector_count,
49 uint8_t idx) {
50 part->disk = disk;
51 part->start_lba = start_lba;
52 part->sector_count = sector_count;
53 part->fs_type = FS_UNKNOWN;
54 part->fs_data = NULL;
55 part->mounted = false;
56 snprintf(part->name, sizeof(part->name), "%sp%d", disk->name, idx);
57
58 part->mount = NULL;
59}
60
61static enum err detect_mbr_partitions(struct block_device *disk,
62 uint8_t *sector) {
63 struct mbr *mbr = (struct mbr *) sector;
64
65 /* no idea what to return here */
66 if (mbr->signature != 0xAA55)
67 return ERR_IO;
68
69 int count = 0;
70 for (int i = 0; i < 4; i++) {
71 if (mbr->partitions[i].type != 0)
72 count++;
73 }
74
75 if (count == 0)
76 return ERR_IO;
77
78 disk->partition_count = count;
79 disk->partitions = vnews(disk->partitions, count, ALLOC_ZERO);
80 if (cc_unlikely(!disk->partitions))
81 return ERR_NO_MEM;
82
83 int idx = 0;
84 for (int i = 0; i < 4; i++) {
85 struct mbr_partition_entry *p = &mbr->partitions[i];
86 if (p->type == 0)
87 continue;
88
89 struct partition *part = &disk->partitions[idx++];
90 make_partition(part, disk, start_lba: p->lba_start, sector_count: p->sector_count, idx);
91 }
92 return ERR_OK;
93}
94
95static enum err detect_gpt_partitions(struct block_device *disk,
96 uint8_t *sector) {
97 if (!disk->read_sector(disk, 1, sector, 1))
98 return ERR_NO_MEM;
99
100 struct gpt_header *gpt = (struct gpt_header *) sector;
101 if (gpt->signature != 0x5452415020494645ULL)
102 return ERR_NO_ENT;
103
104 uint32_t count = gpt->num_partition_entries;
105 uint32_t size = gpt->size_of_partition_entry;
106 uint64_t entry_lba = gpt->partition_entry_lba;
107 uint32_t entries_per_sector = disk->sector_size / size;
108
109 int valid_count = 0;
110 for (uint32_t i = 0; i < count; i++) {
111 uint64_t lba = entry_lba + (i * size) / disk->sector_size;
112 if (!disk->read_sector(disk, lba, sector, 1))
113 break;
114
115 struct gpt_partition_entry *entry;
116 entry = (void *) (sector + (i % entries_per_sector) * size);
117
118 if (entry->first_lba && entry->last_lba)
119 valid_count++;
120 }
121
122 if (valid_count == 0)
123 return ERR_NO_ENT;
124
125 disk->partition_count = valid_count;
126 disk->partitions = vnews(disk->partitions, valid_count, ALLOC_ZERO);
127
128 int idx = 0;
129 for (uint32_t i = 0; i < count; i++) {
130 uint64_t lba = entry_lba + (i * size) / disk->sector_size;
131 if (!disk->read_sector(disk, lba, sector, 1))
132 break;
133
134 struct gpt_partition_entry *entry;
135 entry = (void *) (sector + (i % entries_per_sector) * size);
136
137 if (entry->first_lba && entry->last_lba) {
138 struct partition *part = &disk->partitions[idx++];
139 uint64_t sector_count = entry->last_lba - entry->first_lba + 1;
140 make_partition(part, disk, start_lba: entry->first_lba, sector_count, idx);
141 }
142 }
143 return ERR_OK;
144}
145
146static enum fs_type detect_partition_fs(struct block_device *disk,
147 struct partition *part,
148 uint8_t *sector) {
149 if (!disk->read_sector(disk, part->start_lba, sector, 1))
150 return FS_UNKNOWN;
151
152 if (memcmp(&sector[0x36], "FAT12", 5) == 0)
153 return FS_FAT12;
154 if (memcmp(&sector[0x36], "FAT16", 5) == 0)
155 return FS_FAT16;
156 if (memcmp(&sector[0x52], "FAT32", 5) == 0)
157 return FS_FAT32;
158 if (memcmp(&sector[3], "EXFAT ", 8) == 0)
159 return FS_EXFAT;
160 if (memcmp(&sector[3], "NTFS ", 8) == 0)
161 return FS_NTFS;
162
163 uint64_t ext_sb_offset = KIB(1);
164 uint64_t ext_sector_offset =
165 part->start_lba + (ext_sb_offset / disk->sector_size);
166 uint64_t ext_offset_within_sector = ext_sb_offset % disk->sector_size;
167
168 if (disk->read_sector(disk, ext_sector_offset, sector, 1)) {
169 uint16_t magic = *(uint16_t *) (sector + ext_offset_within_sector + 56);
170 if (magic == 0xEF53)
171 return FS_EXT2;
172 }
173
174 if (disk->read_sector(disk, part->start_lba + 16, sector, 1)) {
175 if (memcmp(&sector[1], "CD001", 5) == 0)
176 return FS_ISO9660;
177 }
178
179 if (disk->read_sector(disk, 16, sector, 1)) {
180 if (memcmp(&sector[1], "CD001", 5) == 0) {
181 part->start_lba = 0;
182 disk->partition_count = 1;
183 return FS_ISO9660;
184 }
185 }
186
187 return FS_UNKNOWN;
188}
189
190static void assign_fs_ops(struct partition *part) {
191 switch (part->fs_type) {
192 case FS_EXT2: part->mount = ext2_g_mount; break;
193 case FS_FAT12:
194 case FS_FAT16:
195 case FS_FAT32: part->mount = fat_g_mount; break;
196 case FS_ISO9660: part->mount = iso9660_mount; break;
197 case FS_EXT3:
198 case FS_EXT4:
199 default: part->mount = dummy_mount; break;
200 }
201}
202
203enum fs_type detect_fs(struct block_device *disk) {
204 uint8_t *sector = valloc_aligned(PAGE_SIZE, PAGE_SIZE);
205 if (!sector)
206 return FS_UNKNOWN;
207
208 fs_detect_info("attempting to detect %s's filesystem(s)", disk->name);
209
210 if (!disk->read_sector(disk, 0, sector, 1)) {
211 fs_detect_info("%s has an unknown filesystem - read failed",
212 disk->name);
213 vfree_aligned(sector);
214 return FS_UNKNOWN;
215 }
216
217 fs_detect_info("read sector 0 of %s", disk->name);
218
219 enum err found_partitions_err = ERR_NO_ENT;
220 struct mbr *mbr = (struct mbr *) sector;
221
222 if (mbr->signature == 0xAA55) {
223 if (mbr->partitions[0].type == 0xEE) {
224 found_partitions_err = detect_gpt_partitions(disk, sector);
225 } else {
226 found_partitions_err = detect_mbr_partitions(disk, sector);
227 }
228 }
229
230 if (found_partitions_err < 0) {
231 /* No partition table - create one big partition spanning the disk */
232 disk->partition_count = 1;
233 disk->partitions = vnew(disk->partitions, ALLOC_ZERO);
234 if (!disk->partitions)
235 return FS_UNKNOWN;
236
237 struct partition *part = &disk->partitions[0];
238 make_partition(part, disk, start_lba: 0, sector_count: disk->total_sectors, idx: 1);
239 }
240
241 for (uint64_t i = 0; i < disk->partition_count; i++) {
242 struct partition *part = &disk->partitions[i];
243 part->disk = disk;
244 part->fs_type = detect_partition_fs(disk, part, sector);
245 assign_fs_ops(part);
246 fs_detect_info("%s has a(n) %s filesystem", part->name,
247 detect_fstr(part->fs_type));
248 }
249
250 vfree_aligned(sector);
251
252 return disk->partition_count > 0 ? disk->partitions[0].fs_type : FS_UNKNOWN;
253}
254