From 2fd1dca06e55a258a8bd39822990e29a95e5ba1f Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Wed, 5 Mar 2014 21:52:03 +0100 Subject: [PATCH] Add support for file truncation --- kernel/include/fs/fat32.hpp | 4 + kernel/include/fs/sysfs.hpp | 1 + kernel/include/vfs/file_system.hpp | 1 + kernel/src/fs/fat32.cpp | 191 +++++++++++++++++++++++++++++ kernel/src/fs/sysfs.cpp | 4 + 5 files changed, 201 insertions(+) diff --git a/kernel/include/fs/fat32.hpp b/kernel/include/fs/fat32.hpp index 10560ae3..271ac602 100644 --- a/kernel/include/fs/fat32.hpp +++ b/kernel/include/fs/fat32.hpp @@ -36,6 +36,7 @@ public: size_t statfs(statfs_info& file); size_t read(const std::vector& file_path, char* buffer, size_t count, size_t offset, size_t& read); size_t write(const std::vector& file_path, char* buffer, size_t count, size_t offset, size_t& written); + size_t truncate(const std::vector& file_path, size_t size); size_t get_file(const std::vector& file_path, vfs::file& file); size_t ls(const std::vector& file_path, std::vector& contents); size_t touch(const std::vector& file_path); @@ -46,6 +47,9 @@ private: size_t rm_dir(uint32_t parent_cluster_number, size_t position, uint32_t cluster_number); size_t rm_file(uint32_t parent_cluster_number, size_t position, uint32_t cluster_number); + size_t set_file_size(uint32_t parent_cluster_number, size_t position, uint32_t file_size); + size_t set_cluster_number(uint32_t parent_cluster_number, size_t position, uint32_t cluster_number); + cluster_entry* find_free_entry(std::unique_heap_array& directory_cluster, size_t entries, uint32_t& cluster_number); cluster_entry* extend_directory(std::unique_heap_array& directory_cluster, size_t entries, uint32_t& cluster_number); diff --git a/kernel/include/fs/sysfs.hpp b/kernel/include/fs/sysfs.hpp index 79e56ad8..a3380f27 100644 --- a/kernel/include/fs/sysfs.hpp +++ b/kernel/include/fs/sysfs.hpp @@ -27,6 +27,7 @@ public: size_t statfs(statfs_info& file); size_t read(const std::vector& file_path, char* buffer, size_t count, size_t offset, size_t& read); size_t write(const std::vector& file_path, char* buffer, size_t count, size_t offset, size_t& written); + size_t truncate(const std::vector& file_path, size_t size); size_t get_file(const std::vector& file_path, vfs::file& file); size_t ls(const std::vector& file_path, std::vector& contents); size_t touch(const std::vector& file_path); diff --git a/kernel/include/vfs/file_system.hpp b/kernel/include/vfs/file_system.hpp index 52f292ac..16a7f288 100644 --- a/kernel/include/vfs/file_system.hpp +++ b/kernel/include/vfs/file_system.hpp @@ -22,6 +22,7 @@ struct file_system { virtual size_t statfs(statfs_info& file) = 0; virtual size_t read(const std::vector& file_path, char* buffer, size_t count, size_t offset, size_t& read) = 0; virtual size_t write(const std::vector& file_path, char* buffer, size_t count, size_t offset, size_t& written) = 0; + virtual size_t truncate(const std::vector& file_path, size_t size) = 0; virtual size_t get_file(const std::vector& file_path, vfs::file& file) = 0; virtual size_t ls(const std::vector& file_path, std::vector& contents) = 0; virtual size_t touch(const std::vector& file_path) = 0; diff --git a/kernel/src/fs/fat32.cpp b/kernel/src/fs/fat32.cpp index f6b822bd..7d7555c8 100644 --- a/kernel/src/fs/fat32.cpp +++ b/kernel/src/fs/fat32.cpp @@ -381,6 +381,93 @@ size_t fat32::fat32_file_system::write(const std::vector& file_path return 0; } +size_t fat32::fat32_file_system::truncate(const std::vector& file_path, size_t file_size){ + vfs::file file; + auto result = get_file(file_path, file); + if(result > 0){ + return result; + } + + uint32_t cluster_number = file.location; + size_t position = file.position; + + //Find the cluster number of the parent directory + auto cluster_number_search = find_cluster_number(file_path, 1); + if(!cluster_number_search.first){ + return std::ERROR_NOT_EXISTS; + } + + auto parent_cluster_number = cluster_number_search.second; + + auto cluster_size = 512 * fat_bs->sectors_per_cluster; + auto clusters = file_size % cluster_size == 0 ? file_size / cluster_size : (file_size / cluster_size) + 1; + + size_t capacity = 0; + auto last_cluster = cluster_number; + + if(last_cluster >= 2){ + ++capacity; + + while(true){ + auto next = next_cluster(last_cluster); + + if(!next || next == CLUSTER_CORRUPTED){ + break; + } + + last_cluster = next; + ++capacity; + } + } + + if(capacity == 0){ + auto cluster = find_free_cluster(); + if(!cluster){ + return std::ERROR_DISK_FULL; + } + + --fat_is->free_clusters; + + cluster_number = cluster; + last_cluster = cluster; + + ++capacity; + + set_cluster_number(parent_cluster_number, position, file_size); + } + + //Extend the clusters if necessary + for(auto i = capacity; i < clusters; ++i){ + auto cluster = find_free_cluster(); + if(!cluster){ + return std::ERROR_DISK_FULL; + } + + --fat_is->free_clusters; + + //Update the cluster chain + if(!write_fat_value(last_cluster, cluster)){ + return std::ERROR_FAILED; + } + + last_cluster = cluster; + } + + //Update the cluster chain + if(!write_fat_value(last_cluster, CLUSTER_END)){ + return std::ERROR_FAILED; + } + + if(!write_is()){ + return std::ERROR_FAILED; + } + + //Set the new file size in the directory entry + set_file_size(parent_cluster_number, position, file_size); + + return 0; +} + size_t fat32::fat32_file_system::ls(const std::vector& file_path, std::vector& contents){ //TODO Better handling of error inside files() contents = files(file_path); @@ -629,6 +716,110 @@ size_t fat32::fat32_file_system::rm_dir(uint32_t parent_cluster_number, size_t p return rm_file(parent_cluster_number, position, cluster_number); } +size_t fat32::fat32_file_system::set_file_size(uint32_t parent_cluster_number, size_t position, uint32_t file_size){ + std::unique_heap_array directory_cluster(16 * fat_bs->sectors_per_cluster); + if(!read_sectors(disk, cluster_lba(parent_cluster_number), fat_bs->sectors_per_cluster, directory_cluster.get())){ + return std::ERROR_FAILED; + } + + //1. Mark the entries in directory as unused + + bool found = false; + size_t cluster_position = 0; + while(!found){ + bool end_reached = false; + + //Verify if is the correct cluster + if(position >= cluster_position * directory_cluster.size() && position < (cluster_position + 1) * directory_cluster.size()){ + found = true; + + auto j = position % directory_cluster.size(); + directory_cluster[j].file_size = file_size; + + if(!write_sectors(disk, cluster_lba(parent_cluster_number), fat_bs->sectors_per_cluster, directory_cluster.get())){ + return std::ERROR_FAILED; + } + } + + //Jump to next cluser + if(!found && !end_reached){ + parent_cluster_number = next_cluster(parent_cluster_number); + + if(!parent_cluster_number){ + break; + } + + //The block is corrupted + if(parent_cluster_number == CLUSTER_CORRUPTED){ + break; + } + + if(!read_sectors(disk, cluster_lba(parent_cluster_number), fat_bs->sectors_per_cluster, directory_cluster.get())){ + break; + } + } + } + + if(!found){ + return std::ERROR_NOT_EXISTS; + } + + return 0; +} + +size_t fat32::fat32_file_system::set_cluster_number(uint32_t parent_cluster_number, size_t position, uint32_t cluster_number){ + std::unique_heap_array directory_cluster(16 * fat_bs->sectors_per_cluster); + if(!read_sectors(disk, cluster_lba(parent_cluster_number), fat_bs->sectors_per_cluster, directory_cluster.get())){ + return std::ERROR_FAILED; + } + + //1. Mark the entries in directory as unused + + bool found = false; + size_t cluster_position = 0; + while(!found){ + bool end_reached = false; + + //Verify if is the correct cluster + if(position >= cluster_position * directory_cluster.size() && position < (cluster_position + 1) * directory_cluster.size()){ + found = true; + + auto j = position % directory_cluster.size(); + + directory_cluster[j].cluster_low = cluster_number; + directory_cluster[j].cluster_high = cluster_number >> 16; + + if(!write_sectors(disk, cluster_lba(parent_cluster_number), fat_bs->sectors_per_cluster, directory_cluster.get())){ + return std::ERROR_FAILED; + } + } + + //Jump to next cluser + if(!found && !end_reached){ + parent_cluster_number = next_cluster(parent_cluster_number); + + if(!parent_cluster_number){ + break; + } + + //The block is corrupted + if(parent_cluster_number == CLUSTER_CORRUPTED){ + break; + } + + if(!read_sectors(disk, cluster_lba(parent_cluster_number), fat_bs->sectors_per_cluster, directory_cluster.get())){ + break; + } + } + } + + if(!found){ + return std::ERROR_NOT_EXISTS; + } + + return 0; +} + //Finds "entries" consecutive free entries in the given directory cluster fat32::cluster_entry* fat32::fat32_file_system::find_free_entry(std::unique_heap_array& directory_cluster, size_t entries, uint32_t& cluster_number){ while(true){ diff --git a/kernel/src/fs/sysfs.cpp b/kernel/src/fs/sysfs.cpp index c0e332b5..877b5bbc 100644 --- a/kernel/src/fs/sysfs.cpp +++ b/kernel/src/fs/sysfs.cpp @@ -281,6 +281,10 @@ size_t sysfs::sysfs_file_system::write(const std::vector&, char*, s return std::ERROR_PERMISSION_DENIED; } +size_t sysfs::sysfs_file_system::truncate(const std::vector& file_path, size_t size){ + return std::ERROR_PERMISSION_DENIED; +} + size_t sysfs::sysfs_file_system::ls(const std::vector& file_path, std::vector& contents){ auto& root_folder = find_root_folder(mount_point);