diff options
Diffstat (limited to 'lume-legacy/src/fs/fat32.cc')
| -rw-r--r-- | lume-legacy/src/fs/fat32.cc | 806 |
1 files changed, 806 insertions, 0 deletions
diff --git a/lume-legacy/src/fs/fat32.cc b/lume-legacy/src/fs/fat32.cc new file mode 100644 index 0000000..e4089d4 --- /dev/null +++ b/lume-legacy/src/fs/fat32.cc @@ -0,0 +1,806 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 Richard QIn + */ +#include "fs/fat32.h" +#include "lib/string.h" +#include "drivers/uart.h" +#include "kernel/mm.h" +#include "kernel/proc.h" + +FAT32FileSystem fat32_fs; + +// Helper function +static void generate_short_name(char *dst, const char *src) +{ + memset(dst, ' ', 11); + + int i = 0; + for (; *src && *src != '.' && i < 8; src++) + { + char c = *src; + if (c >= 'a' && c <= 'z') + c -= 32; + dst[i++] = c; + } + + while (*src && *src != '.') + src++; + if (*src == '.') + src++; + + i = 8; + for (; *src && i < 11; src++) + { + char c = *src; + if (c >= 'a' && c <= 'z') + c -= 32; + dst[i++] = c; + } +} + +// FAT32 FileSystem Implementation + +void FAT32FileSystem::init() +{ + struct Buf *b = BufferCache::bread(0, 0); + uint8 *data = (uint8 *)b->data; + struct FAT32_BPB *bpb = (struct FAT32_BPB *)data; + + uint32 partition_lba = 0; + + bool is_valid_bpb = (bpb->bytesPerSector == 512) && (bpb->rootClus != 0); + + if (!is_valid_bpb) + { + Drivers::uart_puts("[FAT32] Sector 0 is not a valid BPB. Checking MBR...\n"); + + if (data[510] != 0x55 || data[511] != 0xAA) + { + Drivers::uart_puts("[FAT32] Panic: Invalid Disk Signature!\n"); + while (1) + ; + } + + uint32 lba = *(uint32 *)(data + 446 + 8); + + if (lba > 0) + { + Drivers::uart_puts("[FAT32] MBR Partition 1 found @ Sector "); + Drivers::uart_put_int(lba); + Drivers::uart_puts("\n"); + partition_lba = lba; + } + else + { + Drivers::uart_puts("[FAT32] Panic: No partition found.\n"); + while (1) + ; + } + + BufferCache::brelse(b); + + b = BufferCache::bread(0, partition_lba / 2); + bpb = (struct FAT32_BPB *)b->data; + } + + if (bpb->bytesPerSector != 512) + { + Drivers::uart_puts("[FAT32] Panic: Invalid FAT32 BPB in partition.\n"); + while (1) + ; + } + + uint32 fat_sz = bpb->FATSz32; + this->fat_sz = fat_sz; + uint32 reserved_sec = bpb->reservedSecCnt; + uint8 num_fats = bpb->numFATs; + + sectors_per_cluster = bpb->secPerClus; + bytes_per_cluster = sectors_per_cluster * 512; + root_cluster = bpb->rootClus; + + fat_start_sector = partition_lba + reserved_sec; + data_start_sector = partition_lba + reserved_sec + (fat_sz * num_fats); + + Drivers::uart_puts(ANSI_GREEN); + Drivers::uart_puts("[FAT32] Init Success: RootClus="); + Drivers::uart_put_int(root_cluster); + Drivers::uart_puts(", DataStart="); + Drivers::uart_put_int(data_start_sector); + Drivers::uart_puts(ANSI_RESET); + Drivers::uart_puts("\n"); + + BufferCache::brelse(b); +} + +Inode *FAT32FileSystem::root() +{ + FAT32Inode *ip = new FAT32Inode(); + + ip->fs = this; + ip->dev = 0; + ip->inum = root_cluster; + ip->ref_cnt = 1; + ip->valid = 1; + ip->type = T_DIR; + ip->size = 0; + ip->nlink = 1; + + ip->first_clus = root_cluster; + ip->parent_clus = 0; + + return ip; +} + +uint32 FAT32FileSystem::cluster_to_sector(uint32 cluster) +{ + return data_start_sector + (cluster - 2) * sectors_per_cluster; +} + +uint32 FAT32FileSystem::next_cluster(uint32 cluster) +{ + uint32 fat_offset = cluster * 4; + uint32 fat_sector = fat_start_sector + (fat_offset / 512); + uint32 ent_offset = fat_offset % 512; + + // block = sector / 2 + struct Buf *b = BufferCache::bread(0, fat_sector / 2); + + uint32 block_offset = (fat_sector % 2) * 512 + ent_offset; + uint32 next = *(uint32 *)(b->data + block_offset); + + BufferCache::brelse(b); + + return next & 0x0FFFFFFF; +} + +// FAT32 Inode Implementation + +static bool match_name(const char *disk_name, const char *target) +{ + char temp[13]; + int i, j; + + for (i = 0; i < 8 && disk_name[i] != ' '; i++) + temp[i] = disk_name[i]; + if (disk_name[8] != ' ') + { + temp[i++] = '.'; + for (j = 0; j < 3 && disk_name[8 + j] != ' '; j++) + temp[i++] = disk_name[8 + j]; + } + temp[i] = 0; + return strcasecmp(temp, target) == 0; +} + +int FAT32Inode::read(char *dst, uint32 off, uint32 n, int isUser) +{ + if (first_clus == 0) + return 0; + + // [FIX] 只有非目录文件才检查 size 边界 + // 目录文件 size 可能为 0,应允许读取直到簇链结束 + if (type != T_DIR) + { + if (off >= size) + { + return 0; + } + if (off + n > size) + { + n = size - off; + } + } + + uint32 tot = 0; + uint32 clus = first_clus; + uint32 clus_sz = fs->bytes_per_cluster; + + uint32 skip = off / clus_sz; + uint32 current_off = off % clus_sz; + + while (skip > 0) + { + clus = fs->next_cluster(clus); + if (clus >= 0x0FFFFFF8) + return 0; + skip--; + } + + while (tot < n) + { + uint32 sec_in_clus = current_off / 512; + + if (sec_in_clus >= fs->sectors_per_cluster) + { + clus = fs->next_cluster(clus); + if (clus >= 0x0FFFFFF8) + break; + current_off = 0; + continue; + } + + uint32 sec = fs->cluster_to_sector(clus) + sec_in_clus; + uint32 off_in_sec = current_off % 512; + + uint32 m = 512 - off_in_sec; + if (n - tot < m) + m = n - tot; + + struct Buf *b = BufferCache::bread(dev, sec / 2); + uint32 block_offset = (sec % 2) * 512 + off_in_sec; + + if (isUser) + { + if (VM::copyout(myproc()->pagetable, (uint64)dst, (char *)(b->data + block_offset), m) < 0) + { + BufferCache::brelse(b); + return -1; + } + } + else + { + memmove(dst, b->data + block_offset, m); + } + BufferCache::brelse(b); + + tot += m; + dst += m; + current_off += m; + } + return tot; +} + +Inode *FAT32Inode::lookup(const char *name) +{ + if (type != T_DIR) + return nullptr; + + uint32 clus = first_clus; + struct FAT32_DirEnt de; + + while (clus < 0x0FFFFFF8) + { + uint32 sec = fs->cluster_to_sector(clus); + + for (uint32 i = 0; i < fs->sectors_per_cluster; i++) + { + uint32 current_sec = sec + i; + struct Buf *b = BufferCache::bread(dev, current_sec / 2); + uint32 block_base = (current_sec % 2) * 512; + + for (int off = 0; off < 512; off += 32) + { + memmove(&de, b->data + block_base + off, 32); + + if (de.name[0] == 0x00) + { + BufferCache::brelse(b); + return nullptr; + } + if (de.name[0] == 0xE5) + continue; + if (de.attr & ATTR_LONG_NAME) + continue; + + if (match_name(de.name, name)) + { + FAT32Inode *ip = new FAT32Inode(); + ip->fs = fs; + ip->dev = dev; + ip->type = (de.attr & ATTR_DIRECTORY) ? T_DIR : T_FILE; + ip->size = de.fileSize; + ip->first_clus = (de.fstClusHI << 16) | de.fstClusLO; + ip->inum = ip->first_clus; + ip->nlink = 1; + ip->ref_cnt = 1; + ip->valid = 1; + ip->ent_sec = current_sec; // Log Sector + ip->ent_off = off; // Log Setoff + + BufferCache::brelse(b); + return ip; + } + } + BufferCache::brelse(b); + } + clus = fs->next_cluster(clus); + } + return nullptr; +} + +// Placeholder +uint32 FAT32FileSystem::alloc_cluster() +{ + uint32 fat_entries_per_sector = 512 / 4; + for (uint32 i = 0; i < fat_sz; i++) + { + uint32 current_fat_sec = fat_start_sector + i; + struct Buf *b = BufferCache::bread(0, current_fat_sec / 2); + uint32 *table = (uint32 *)(b->data + (current_fat_sec % 2) * 512); + for (uint32 j = 0; j < fat_entries_per_sector; j++) + { + if (i == 0 && j < 2) + continue; + + if ((table[j] & 0x0FFFFFFF) == 0) + { + // Find the free cluster and mark it as EOF (0x0FFFFFFF) + table[j] = 0x0FFFFFFF; + BufferCache::bwrite(b); // Write back to disk + BufferCache::brelse(b); + + uint32 cluster_idx = i * fat_entries_per_sector + j; + + // Clear the data content of the new cluster + uint32 data_sec = cluster_to_sector(cluster_idx); + for (uint32 k = 0; k < sectors_per_cluster; k++) + { + struct Buf *db = BufferCache::bread(0, (data_sec + k) / 2); + memset(db->data + ((data_sec + k) % 2) * 512, 0, 512); + BufferCache::bwrite(db); + BufferCache::brelse(db); + } + + return cluster_idx; + } + } + BufferCache::brelse(b); + } + Drivers::uart_puts("[FAT32] Out of space!\n"); + return 0; +} + +void FAT32FileSystem::free_cluster_chain(uint32 cluster) +{ + while (cluster >= 2 && cluster < 0x0FFFFFF8) + { + uint32 next = next_cluster(cluster); + + // Calculate the current cluster's position in the FAT table + uint32 fat_offset = cluster * 4; + uint32 fat_sector = fat_start_sector + (fat_offset / 512); + uint32 ent_offset = fat_offset % 512; + + // Read FAT sector + struct Buf *b = BufferCache::bread(0, fat_sector / 2); + + // Marked as idle (0x00000000) + uint32 *entry = (uint32 *)(b->data + (fat_sector % 2) * 512 + ent_offset); + *entry = 0; + + BufferCache::bwrite(b); + BufferCache::brelse(b); + + cluster = next; + } +} + +void FAT32Inode::update() +{ + struct Buf *b = BufferCache::bread(dev, ent_sec / 2); + uint32 offset = (ent_sec % 2) * 512 + ent_off; + + struct FAT32_DirEnt *de = (struct FAT32_DirEnt *)(b->data + offset); + + // Update file size and starting cluster + de->fileSize = size; + de->fstClusHI = (uint16)((first_clus >> 16) & 0xFFFF); + de->fstClusLO = (uint16)(first_clus & 0xFFFF); + + BufferCache::bwrite(b); + BufferCache::brelse(b); +} + +int FAT32Inode::write(const char *src, uint32 off, uint32 n, int isUser) +{ + if (off > size) + { + return -1; + } + + uint32 tot = 0; + uint32 clus = first_clus; + uint32 clus_sz = fs->bytes_per_cluster; + uint32 prev_clus = 0; + + if (clus == 0) + { + clus = fs->alloc_cluster(); + if (clus == 0) + return -1; + first_clus = clus; + // Update the metadata immediately to prevent data loss in case of a power outage. + update(); + } + + uint32 skip = off / clus_sz; + uint32 current_off = off % clus_sz; + + while (skip > 0) + { + prev_clus = clus; + clus = fs->next_cluster(clus); + if (clus >= 0x0FFFFFF8) + { + return -1; + } + skip--; + } + while (tot < n) + { + if (current_off == clus_sz) + { + uint32 next = fs->next_cluster(clus); + if (next >= 0x0FFFFFF8) + { + uint32 new_clus = fs->alloc_cluster(); + if (new_clus == 0) + break; + + // Point the current cluster to the new cluster (update the FAT table) + uint32 fat_offset = clus * 4; + uint32 fat_sec = fs->fat_start_sector + (fat_offset / 512); + struct Buf *fat_b = BufferCache::bread(dev, fat_sec / 2); + uint32 *entry = (uint32 *)(fat_b->data + (fat_sec % 2) * 512 + (fat_offset % 512)); + *entry = new_clus; // Link + BufferCache::bwrite(fat_b); + BufferCache::brelse(fat_b); + + clus = new_clus; + } + else + { + clus = next; + } + current_off = 0; + } + + // Calculate the physical sector to write + uint32 sec_in_clus = current_off / 512; + uint32 sec = fs->cluster_to_sector(clus) + sec_in_clus; + uint32 off_in_sec = current_off % 512; + + // Calculate how many bytes can be written this time + uint32 m = 512 - off_in_sec; + if (n - tot < m) + m = n - tot; + + struct Buf *b = BufferCache::bread(dev, sec / 2); + uint32 block_offset = (sec % 2) * 512 + off_in_sec; + + if (isUser) + { + if (VM::copyin(myproc()->pagetable, (char *)(b->data + block_offset), (uint64)src, m) < 0) + { + BufferCache::brelse(b); + return -1; + } + } + else + { + memmove(b->data + block_offset, src, m); + } + + BufferCache::bwrite(b); + BufferCache::brelse(b); + + tot += m; + src += m; + current_off += m; + } + if (off + n > size) + { + size = off + n; + update(); + } + + return tot; +} + +Inode *FAT32Inode::create(const char *name, short type, short major, short minor) +{ + Inode *exist = lookup(name); + if (exist) + { + return exist; + } + + struct FAT32_DirEnt de; + memset(&de, 0, sizeof(de)); + generate_short_name(de.name, name); + de.attr = (type == T_DIR) ? ATTR_DIRECTORY : ATTR_ARCHIVE; + + uint32 new_dir_clus = 0; + if (type == T_DIR) + { + new_dir_clus = fs->alloc_cluster(); + if (new_dir_clus == 0) + return nullptr; + + de.fstClusHI = (uint16)((new_dir_clus >> 16) & 0xFFFF); + de.fstClusLO = (uint16)(new_dir_clus & 0xFFFF); + } + else + { + de.fileSize = 0; + } + + uint32 clus = first_clus; + bool written = false; + + while (true) + { + uint32 sec = fs->cluster_to_sector(clus); + + for (uint32 i = 0; i < fs->sectors_per_cluster; i++) + { + struct Buf *b = BufferCache::bread(dev, (sec + i) / 2); + uint8 *data = (uint8 *)b->data + ((sec + i) % 2) * 512; + + for (int off = 0; off < 512; off += 32) + { + if (data[off] == 0x00 || data[off] == 0xE5) + { + memmove(data + off, &de, 32); + BufferCache::bwrite(b); + BufferCache::brelse(b); + written = true; + break; + } + } + + if (written) + break; + BufferCache::brelse(b); + } + + if (written) + break; + + uint32 next = fs->next_cluster(clus); + if (next >= 0x0FFFFFF8) + { + uint32 new_parent_clus = fs->alloc_cluster(); + if (new_parent_clus == 0) + return nullptr; + + uint32 fat_off = clus * 4; + uint32 fat_sec = fs->fat_start_sector + (fat_off / 512); + struct Buf *fb = BufferCache::bread(dev, fat_sec / 2); + *(uint32 *)(fb->data + (fat_sec % 2) * 512 + (fat_off % 512)) = new_parent_clus; + BufferCache::bwrite(fb); + BufferCache::brelse(fb); + + uint32 s = fs->cluster_to_sector(new_parent_clus); + for (uint32 k = 0; k < fs->sectors_per_cluster; k++) + { + struct Buf *zb = BufferCache::bread(dev, (s + k) / 2); + uint8 *zdata = (uint8 *)zb->data + ((s + k) % 2) * 512; + memset(zdata, 0, 512); + + if (k == 0) + { + memmove(zdata, &de, 32); + } + + BufferCache::bwrite(zb); + BufferCache::brelse(zb); + } + + written = true; + break; + } + clus = next; + } + + if (written && type == T_DIR) + { + struct FAT32_DirEnt dot; + memset(&dot, 0, 32); + memset(dot.name, ' ', 11); + dot.name[0] = '.'; + dot.attr = ATTR_DIRECTORY; + dot.fstClusHI = (uint16)((new_dir_clus >> 16) & 0xFFFF); + dot.fstClusLO = (uint16)(new_dir_clus & 0xFFFF); + + struct FAT32_DirEnt dotdot; + memset(&dotdot, 0, 32); + memset(dotdot.name, ' ', 11); + dotdot.name[0] = '.'; + dotdot.name[1] = '.'; + dotdot.attr = ATTR_DIRECTORY; + + uint32 parent_clus_num = this->first_clus; + if (parent_clus_num == fs->root_cluster) + { + parent_clus_num = 0; + } + dotdot.fstClusHI = (uint16)((parent_clus_num >> 16) & 0xFFFF); + dotdot.fstClusLO = (uint16)(parent_clus_num & 0xFFFF); + + uint32 s = fs->cluster_to_sector(new_dir_clus); + + struct Buf *zb = BufferCache::bread(dev, s / 2); + uint8 *zdata = (uint8 *)zb->data + (s % 2) * 512; + + memmove(zdata, &dot, 32); // Offset 0 + memmove(zdata + 32, &dotdot, 32); // Offset 32 + + BufferCache::bwrite(zb); + BufferCache::brelse(zb); + } + + if (written) + { + return lookup(name); + } + + return nullptr; +} + +void FAT32Inode::truncate() +{ + if (first_clus == 0) + { + if (size != 0) + { + size = 0; + update(); + } + return; + } + + fs->free_cluster_chain(first_clus); + + size = 0; + first_clus = 0; + + update(); +} + +int FAT32Inode::unlink(const char *name) +{ + if (type != T_DIR) + return -1; + if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + return -1; + + uint32_t clus = first_clus; + bool found = false; + + // [FIX] Deadlock prevention: Do not hold buffer while looking up inode + uint32_t target_block = 0; + uint32_t target_offset = 0; + struct FAT32_DirEnt de; + + while (true) + { + uint32_t sec = fs->cluster_to_sector(clus); + for (uint32 i = 0; i < fs->sectors_per_cluster; ++i) + { + struct Buf *b = BufferCache::bread(dev, (sec + i) / 2); + uint8_t *data = (uint8_t *)b->data + ((sec + i) % 2) * 512; + + for (int off = 0; off < 512; off += 32) + { + memmove(&de, data + off, 32); + if (de.name[0] == 0x00) // end of dir + { + BufferCache::brelse(b); + return -1; // Not found + } + if (de.name[0] == 0xE5) + continue; // have been unlink + if (de.attr & ATTR_LONG_NAME) + continue; + + if (match_name(de.name, name)) + { + target_block = (sec + i) / 2; + target_offset = ((sec + i) % 2) * 512 + off; + found = true; + break; + } + } + + BufferCache::brelse(b); // Always release buffer + if (found) + break; + } + + if (found) + break; + + clus = fs->next_cluster(clus); + if (clus >= 0x0FFFFFF8) + return -1; + } + + if (found) + { + Inode *target_ip = lookup(name); + if (target_ip) + { + VFS::ilock(target_ip); + if (target_ip->type == T_DIR) + { + VFS::iunlockput(target_ip); + return -1; + } + + target_ip->nlink--; + target_ip->update(); + VFS::iunlockput(target_ip); + } + + // Re-read buffer to mark deletion + struct Buf *target_buf = BufferCache::bread(dev, target_block); + uint8_t *ptr = (uint8_t *)target_buf->data + target_offset; + *ptr = 0xE5; + + BufferCache::bwrite(target_buf); + BufferCache::brelse(target_buf); + return 0; + } + + return -1; +} + +// Test Function +void fat32_test() +{ + static bool inited = false; + if (!inited) + { + fat32_fs.init(); + inited = true; + } + + Drivers::uart_puts("\n[FAT32] Starting Test via Syscall...\n"); + + Inode *root = fat32_fs.root(); + if (root) + { + Drivers::uart_puts("[FAT32] Root Inode retrieved. Inum: "); + Drivers::uart_put_int(root->inum); + Drivers::uart_puts("\n"); + + Drivers::uart_puts("[FAT32] Looking for TEST.TXT...\n"); + Inode *node = root->lookup("test.txt"); + + if (node) + { + Drivers::uart_puts("[FAT32] Found TEST.TXT! Size: "); + Drivers::uart_put_int(node->size); + Drivers::uart_puts("\n"); + + char buf[128]; + int n = node->read(buf, 0, 127, 0); + if (n > 0) + { + buf[n] = 0; + Drivers::uart_puts("[FAT32] Content:\n>> "); + Drivers::uart_puts(buf); + Drivers::uart_puts("\n"); + } + else + { + Drivers::uart_puts("[FAT32] Read returned 0.\n"); + } + VFS::iput(node); + } + else + { + Drivers::uart_puts("[FAT32] TEST.TXT not found.\n"); + } + VFS::iput(root); + } + else + { + Drivers::uart_puts("[FAT32] Failed to get root inode.\n"); + } + + Drivers::uart_puts("[FAT32] Test Finished.\n"); +}
\ No newline at end of file |
