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