diff --git a/build.sh b/build.sh index 2548d40..c5a3da7 100755 --- a/build.sh +++ b/build.sh @@ -21,3 +21,4 @@ mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/shell ::bin/s mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/echo ::bin/echo mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/ls ::bin/ls mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/cat ::bin/cat +mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/cp ::bin/cp diff --git a/meson.build b/meson.build index 104ea0b..ba9a79c 100644 --- a/meson.build +++ b/meson.build @@ -310,3 +310,42 @@ custom_target( command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'], build_by_default: true, ) + +cp_start = custom_target( + 'cp_start', + input: 'src/user/start.asm', + output: 'cp_start.o', + command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'], +) + +cp_syscall = custom_target( + 'cp_syscall', + input: 'src/user/syscall.asm', + output: 'cp_syscall.o', + command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'], +) + +cp_elf = executable( + 'cp.elf', + sources: [cp_start, cp_syscall, lib_sources, 'src/user/app/cp/cp.c'], + c_args: [ + '-ffreestanding', + '-nostdlib', + '-fno-stack-protector', + '-mcmodel=large', + '-DVERSION="' + meson.project_version() + '"', + ], + link_args: [ + '-T', meson.project_source_root() / 'src/user/linker.ld', + '-nostdlib', + ], + link_depends: 'src/user/linker.ld', +) + +custom_target( + 'cp', + input: cp_elf, + output: 'cp', + command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'], + build_by_default: true, +) diff --git a/src/kernel/fat16.c b/src/kernel/fat16.c index 23d816e..8dbae2b 100644 --- a/src/kernel/fat16.c +++ b/src/kernel/fat16.c @@ -33,15 +33,96 @@ typedef struct __attribute__((packed)) { uint32_t size; } fat16_dir_entry_t; +#define MAX_INODES 256 + +typedef struct { + uint32_t refs; + uint16_t first_cluster; + uint32_t size; + uint16_t dir_cluster; + uint16_t dir_index; + uint8_t valid; +} fat16_inode_t; + +static fat16_inode_t inodes[MAX_INODES]; + +static fat16_inode_t *inode_create(uint16_t first_cluster, uint32_t size, uint16_t dir_cluster, uint16_t dir_index) { + for (uint64_t i = 0; i < MAX_INODES; i++) { + if (!inodes[i].valid) { + inodes[i] = (fat16_inode_t){ + .refs = 1, + .first_cluster = first_cluster, + .size = size, + .dir_cluster = dir_cluster, + .dir_index = dir_index, + .valid = 1, + }; + return &inodes[i]; + } + } + return NUL; +} + +static fat16_inode_t *inode_get(uint32_t dir_cluster, uint16_t dir_index) { + for (uint64_t i = 0; i < MAX_INODES; i++) { + if (inodes[i].valid && inodes[i].dir_cluster == dir_cluster && inodes[i].dir_index == dir_index) { + return &inodes[i]; + } + } + return NUL; +} + +static fat16_inode_t *inode_use(uint16_t first_cluster, uint32_t size, uint16_t dir_cluster, uint16_t dir_index) { + fat16_inode_t *result = inode_get(dir_cluster, dir_index); + if (!result) { + result = inode_create(first_cluster, size, dir_cluster, dir_index); + } else { + result->refs++; + } + return result; +} + +static void inode_free(fat16_inode_t *inode) { + inode->refs--; + if (!inode->refs) { + for (uint64_t i = 0; i < MAX_INODES; i++) { + if (&inodes[i] == inode) { + inodes[i].valid = 0; + break; + } + } + } +} + typedef struct { fs_node_t base; - fat16_dir_entry_t entry; + fat16_inode_t *inode; } fat16_node_t; static fat16_bpb_t bpb; // Assuming one partition. +static uint32_t data_start; static fs_node_t *fs = NUL; static uint16_t *fat = NUL; +fs_node_t *fat16_mount() { // Assuming one partition. + uint8_t sector[512]; + ata_read_sectors(FIRST_PARTITION_SECTOR, 1, §or); + memory_copy(sector + 11, sizeof(fat16_bpb_t), &bpb); + data_start = FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count + + (bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE; + fat16_node_t *node = memory_allocate(sizeof(fat16_node_t)); + node->base.type = FAT16; + node->base.name[0] = '/'; + node->base.size = sizeof(fat16_dir_entry_t) * bpb.root_entry_count; + node->base.is_dir = 1; + node->inode = inode_use(0, node->base.size, 0, UINT16_MAX); + if (!node->inode) { + memory_free(node); + return NUL; + } + return fs = (fs_node_t *)node; +} + static void to_8_3(const char *name, char *output) { memory_set(' ', 11, output); output[11] = '\0'; @@ -75,18 +156,6 @@ static void from_8_3(const char *name, const char *extension, char *output) { } } -fs_node_t *fat16_mount() { // Assuming one partition. - uint8_t sector[512]; - ata_read_sectors(FIRST_PARTITION_SECTOR, 1, §or); - memory_copy(sector + 11, sizeof(fat16_bpb_t), &bpb); - fat16_node_t *node = memory_allocate(sizeof(fat16_node_t)); - node->base.type = FAT16; - node->base.name[0] = node->entry.name[0] = '/'; - node->base.size = node->entry.size = sizeof(fat16_dir_entry_t) * bpb.root_entry_count; - node->base.is_dir = 1; - return fs = (fs_node_t *)node; -} - static void ensure_fat() { ASSERT(bpb.sectors_per_fat < 256, "ensure_fat: big FAT not implemented") if (!fat) { @@ -95,17 +164,32 @@ static void ensure_fat() { } } -static fat16_dir_entry_t *load_directory(fat16_node_t *directory) { +static uint16_t allocate_cluster() { + ensure_fat(); + for (uint16_t i = 2; i < bpb.sectors_per_fat * SECTOR_SIZE / 2; i++) { + if (fat[i] == 0x0000) { + fat[i] = 0xFFFF; + return i; + } + } + return 0; +} + +static void flush_fat() { + ata_write_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors, (uint8_t)bpb.sectors_per_fat, fat); +} + +static fat16_dir_entry_t *read_directory(uint16_t dir_cluster) { fat16_dir_entry_t *entries; - if (!directory->entry.first_cluster) { - uint8_t sectors = (uint8_t)((directory->base.size + SECTOR_SIZE - 1) / SECTOR_SIZE); + if (!dir_cluster) { + uint8_t sectors = (uint8_t)((sizeof(fat16_dir_entry_t) * bpb.root_entry_count + SECTOR_SIZE - 1) / SECTOR_SIZE); entries = memory_allocate(sectors * SECTOR_SIZE + sizeof(fat16_dir_entry_t)); // One extra as null terminator. ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.fats_count * bpb.sectors_per_fat, sectors, entries); } else { ensure_fat(); - uint16_t next_cluster = directory->entry.first_cluster; + uint16_t next_cluster = dir_cluster; uint32_t cluster_count = 0; while (next_cluster < 0xFFF8) { cluster_count++; @@ -115,12 +199,9 @@ static fat16_dir_entry_t *load_directory(fat16_node_t *directory) { memory_allocate(cluster_count * bpb.sectors_per_cluster * SECTOR_SIZE + sizeof(fat16_dir_entry_t)); // One extra as null terminator. uint8_t *chunk = (uint8_t *)entries; - next_cluster = directory->entry.first_cluster; + next_cluster = dir_cluster; while (next_cluster < 0xFFF8) { - ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count + - (bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE + - bpb.sectors_per_cluster * (next_cluster - 2), - bpb.sectors_per_cluster, chunk); + ata_read_sectors(data_start + bpb.sectors_per_cluster * (next_cluster - 2), bpb.sectors_per_cluster, chunk); chunk += bpb.sectors_per_cluster * SECTOR_SIZE; next_cluster = fat[next_cluster]; } @@ -129,50 +210,80 @@ static fat16_dir_entry_t *load_directory(fat16_node_t *directory) { return entries; } -static fs_node_t *open_entry(const char *name, const fat16_dir_entry_t *entry) { +static fs_node_t *open_entry(uint16_t dir_cluster, const fat16_dir_entry_t *entries, uint16_t index) { + const fat16_dir_entry_t *entry = &entries[index]; + if (!entry->name[0]) { return NUL; } fat16_node_t *result = memory_allocate(sizeof(fat16_node_t)); - memory_copy((char *)name, string_length(name) + 1, &(result->base.name)); result->base.type = FAT16; + from_8_3(entry->name, entry->ext, result->base.name); + result->base.size = entry->size; result->base.is_dir = entry->attributes & ATTRIBUTE_SUBDIRECTORY; - memory_copy((fat16_dir_entry_t *)entry, sizeof(fat16_dir_entry_t), (uint8_t *)result + sizeof(fs_node_t)); + result->inode = inode_use(entry->first_cluster, entry->size, dir_cluster, index); + + if (!result->inode) { + memory_free(result); + return NUL; + } return (fs_node_t *)result; } +static void write_directory(uint16_t dir_cluster, const fat16_dir_entry_t *entries) { + if (!dir_cluster) { + ata_write_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.fats_count * bpb.sectors_per_fat, + (uint8_t)((sizeof(fat16_dir_entry_t) * bpb.root_entry_count + SECTOR_SIZE - 1) / SECTOR_SIZE), entries); + } else { + ensure_fat(); + + // Assuming no entries were added or removed. + uint8_t *chunk = (uint8_t *)entries; + uint16_t next_cluster = dir_cluster; + while (next_cluster < 0xFFF8) { + ata_write_sectors(data_start + bpb.sectors_per_cluster * (next_cluster - 2), bpb.sectors_per_cluster, chunk); + chunk += bpb.sectors_per_cluster * SECTOR_SIZE; + next_cluster = fat[next_cluster]; + } + } +} + fs_node_t *fat16_open_by(const fs_node_t *directory, const char *name) { - ASSERT(directory->type == FAT16, "fat16_open_by: directory is not FAT16"); - ASSERT(directory->is_dir, "fat16_open_by: directory is not a directory"); + if (directory->type != FAT16 || !directory->is_dir) { + return NUL; + } char name_8_3[12]; to_8_3(name, name_8_3); - fat16_dir_entry_t *entries = load_directory((fat16_node_t *)directory); + fat16_dir_entry_t *entries = read_directory(((fat16_node_t *)directory)->inode->first_cluster); fat16_dir_entry_t *entry = entries; + uint16_t index = 0; while (entry->name[0]) { if ((uint8_t)entry->name[0] != 0xE5 && (uint8_t)entry->attributes != 0x0F && bytes_equal(name_8_3, (char *)entry, 11)) { break; } entry++; + index++; } - fs_node_t *result = open_entry(name, entry); + fs_node_t *result = open_entry(((fat16_node_t *)directory)->inode->first_cluster, entries, index); memory_free(entries); return result; } fs_node_t *fat16_open_at(const fs_node_t *directory, uint64_t index) { - ASSERT(directory->type == FAT16, "fat16_open_at: directory is not FAT16"); - ASSERT(directory->is_dir, "fat16_open_at: directory is not a directory"); + if (directory->type != FAT16 || !directory->is_dir) { + return NUL; + } - fat16_dir_entry_t *entries = load_directory((fat16_node_t *)directory); + fat16_dir_entry_t *entries = read_directory(((fat16_node_t *)directory)->inode->first_cluster); - uint64_t ei = 0, vi = 0; + uint16_t ei = 0, vi = 0; while (entries[ei].name[0]) { if ((uint8_t)entries[ei].name[0] != 0xE5 && (uint8_t)entries[ei].attributes != 0x0F) { if (vi == index) { @@ -183,14 +294,7 @@ fs_node_t *fat16_open_at(const fs_node_t *directory, uint64_t index) { ei++; } - if (!entries[ei].name[0]) { - return NUL; - } - - char name[13]; - from_8_3(entries[ei].name, entries[ei].ext, name); - - fs_node_t *result = open_entry(name, entries + ei); + fs_node_t *result = open_entry(((fat16_node_t *)directory)->inode->first_cluster, entries, ei); memory_free(entries); return result; } @@ -198,30 +302,228 @@ fs_node_t *fat16_open_at(const fs_node_t *directory, uint64_t index) { fs_node_t *fat16_open_again(const fs_node_t *source) { fs_node_t *result = memory_allocate(sizeof(fat16_node_t)); memory_copy(source, sizeof(fat16_node_t), result); + ((fat16_node_t *)result)->inode->refs++; return result; } +uint64_t fat16_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void *to) { + fat16_node_t *fat_file = (fat16_node_t *)file; + + if (fat_file->base.type != FAT16 || fat_file->base.is_dir) { + return (uint64_t)-1; + } + + if (offset >= fat_file->inode->size) { + return 0; + } + + if (offset + bytes >= fat_file->inode->size) { + bytes = fat_file->inode->size - offset; + } + + ensure_fat(); + + uint32_t cluster_size = bpb.sectors_per_cluster * SECTOR_SIZE; + uint32_t next_cluster = fat_file->inode->first_cluster; + + while (offset >= cluster_size) { + next_cluster = fat[next_cluster]; + offset -= cluster_size; + } + + uint32_t readden = 0; + + uint8_t *cursor = to; + + uint8_t *tmp = memory_allocate(cluster_size); + ata_read_sectors(data_start + bpb.sectors_per_cluster * (next_cluster - 2), bpb.sectors_per_cluster, tmp); + uint32_t prefix_size = bytes <= cluster_size - offset ? bytes : cluster_size - offset; + memory_copy(tmp + offset, prefix_size, cursor); + bytes -= prefix_size; + readden += prefix_size; + cursor += prefix_size; + next_cluster = fat[next_cluster]; + + while (bytes >= cluster_size) { + ata_read_sectors(data_start + bpb.sectors_per_cluster * (next_cluster - 2), bpb.sectors_per_cluster, cursor); + bytes -= cluster_size; + readden += cluster_size; + cursor += cluster_size; + next_cluster = fat[next_cluster]; + } + + if (bytes) { + ata_read_sectors(data_start + bpb.sectors_per_cluster * (next_cluster - 2), bpb.sectors_per_cluster, tmp); + memory_copy(tmp, bytes, cursor); + readden += bytes; + } + + memory_free(tmp); + + return readden; +} + +uint64_t fat16_write(fs_node_t *file, uint32_t offset, const void *from, uint32_t bytes) { + fat16_node_t *fat_file = (fat16_node_t *)file; + + if (fat_file->base.type != FAT16 || fat_file->base.is_dir) { + return (uint64_t)-1; + } + + ensure_fat(); + + if (!fat_file->inode->first_cluster) { + fat_file->inode->first_cluster = allocate_cluster(); + } + + uint32_t cluster_size = bpb.sectors_per_cluster * SECTOR_SIZE; + uint32_t next_cluster = fat_file->inode->first_cluster; + + uint32_t allocated = cluster_size; + while (allocated < offset + bytes) { + if (fat[next_cluster] >= 0xFFF8) { + fat[next_cluster] = allocate_cluster(); + } + next_cluster = fat[next_cluster]; + allocated += cluster_size; + } + + flush_fat(); + + cluster_size = bpb.sectors_per_cluster * SECTOR_SIZE; + next_cluster = fat_file->inode->first_cluster; + + while (offset >= cluster_size) { + next_cluster = fat[next_cluster]; + offset -= cluster_size; + } + + uint32_t written = 0; + + const uint8_t *cursor = from; + + uint8_t *tmp = memory_allocate(cluster_size); + ata_read_sectors(data_start + bpb.sectors_per_cluster * (next_cluster - 2), bpb.sectors_per_cluster, tmp); + uint32_t prefix_size = bytes <= cluster_size - offset ? bytes : cluster_size - offset; + memory_copy(cursor, prefix_size, tmp + offset); + ata_write_sectors(data_start + bpb.sectors_per_cluster * (next_cluster - 2), bpb.sectors_per_cluster, tmp); + bytes -= prefix_size; + written += prefix_size; + cursor += prefix_size; + next_cluster = fat[next_cluster]; + + while (bytes >= cluster_size) { + ata_write_sectors(data_start + bpb.sectors_per_cluster * (next_cluster - 2), bpb.sectors_per_cluster, cursor); + bytes -= cluster_size; + written += cluster_size; + cursor += cluster_size; + next_cluster = fat[next_cluster]; + } + + if (bytes) { + ata_read_sectors(data_start + bpb.sectors_per_cluster * (next_cluster - 2), bpb.sectors_per_cluster, tmp); + memory_copy(cursor, bytes, tmp); + ata_write_sectors(data_start + bpb.sectors_per_cluster * (next_cluster - 2), bpb.sectors_per_cluster, tmp); + written += bytes; + } + + memory_free(tmp); + + if (offset + written > fat_file->inode->size) { + fat_file->inode->size = offset + written; + + fat16_dir_entry_t *entries = read_directory(fat_file->inode->dir_cluster); + entries[fat_file->inode->dir_index].first_cluster = fat_file->inode->first_cluster; + entries[fat_file->inode->dir_index].size = fat_file->inode->size; + write_directory(fat_file->inode->dir_cluster, entries); + memory_free(entries); + } + + return written; +} + +uint64_t fat16_truncate(fs_node_t *file, uint32_t size) { + fat16_node_t *fat_file = (fat16_node_t *)file; + + if (fat_file->base.type != FAT16 || fat_file->base.is_dir) { + return (uint64_t)-1; + } + + if (file->size == size) { + return size; + } else if (file->size < size) { + uint32_t diff = size - file->size; + uint8_t *tmp = memory_allocate(diff); + uint64_t written = fat16_write(file, file->size, tmp, diff); + memory_free(tmp); + return written == (uint64_t)-1 ? (uint64_t)-1 : file->size + written; + } else if (file->size > size) { + uint32_t cluster_size = bpb.sectors_per_cluster * SECTOR_SIZE; + uint32_t prev_cluster = 0; + uint32_t next_cluster = fat_file->inode->first_cluster; + + ensure_fat(); + uint32_t allocated = 0; + while (allocated < size) { + prev_cluster = next_cluster; + next_cluster = fat[next_cluster]; + allocated += cluster_size; + } + if (prev_cluster) { + fat[prev_cluster] = 0xFFFF; + } else { + fat_file->inode->first_cluster = 0; + } + do { + uint32_t swap = fat[next_cluster]; + fat[next_cluster] = 0; + next_cluster = swap; + } while (next_cluster < 0xFFF8); + flush_fat(); + + fat_file->inode->size = size; + fat16_dir_entry_t *entries = read_directory(fat_file->inode->dir_cluster); + entries[fat_file->inode->dir_index].first_cluster = fat_file->inode->first_cluster; + entries[fat_file->inode->dir_index].size = fat_file->inode->size; + write_directory(fat_file->inode->dir_cluster, entries); + memory_free(entries); + + return size; + } + return (uint64_t)-1; +} + typedef struct { stream_t stream; fs_node_t *node; - uint64_t offset; + uint32_t offset; } fat16_file_stream_t; -static uint64_t file_stream_write(__attribute__((unused)) const stream_t *self, __attribute__((unused)) const char *from, - __attribute__((unused)) uint64_t bytes) { - return (uint64_t)-1; +static uint64_t file_stream_write(stream_t *self, const char *from, uint64_t bytes) { + fat16_file_stream_t *ffs = (fat16_file_stream_t *)self; + uint64_t written = fat16_write(ffs->node, ffs->offset, from, bytes > UINT32_MAX ? UINT32_MAX : (uint32_t)bytes); + if (written != (uint64_t)-1) { + ffs->offset += written; + } + return written; } -static uint64_t file_stream_read(const stream_t *self, uint64_t max, char *to) { +static uint64_t file_stream_read(stream_t *self, uint64_t max, char *to) { fat16_file_stream_t *ffs = (fat16_file_stream_t *)self; - if (ffs->offset >= ffs->node->size) { - return 0; + uint64_t readden = fat16_read(ffs->node, ffs->offset, max > UINT32_MAX ? UINT32_MAX : (uint32_t)max, to); + if (readden != (uint64_t)-1) { + ffs->offset += readden; } - uint64_t remaining = ffs->node->size - ffs->offset; - uint64_t to_read = remaining > max ? max : remaining; - fat16_read(ffs->node, ffs->offset, to_read, to); - ffs->offset += to_read; - return to_read; + return readden; +} + +static uint64_t file_stream_truncate(stream_t *self, uint64_t size) { + fat16_file_stream_t *ffs = (fat16_file_stream_t *)self; + uint64_t resized = fat16_truncate(ffs->node, size > UINT32_MAX ? UINT32_MAX : (uint32_t)size); + if (resized != (uint64_t)-1 && resized < ffs->offset) { + ffs->offset = (uint32_t)resized; + } + return resized; } static void file_stream_close(stream_t *self) { @@ -233,15 +535,15 @@ static void file_stream_close(stream_t *self) { typedef struct { stream_t stream; fs_node_t *node; - uint64_t index; + uint32_t index; } fat16_directory_stream_t; -static uint64_t directory_stream_write(__attribute__((unused)) const stream_t *self, __attribute__((unused)) const char *from, +static uint64_t directory_stream_write(__attribute__((unused)) stream_t *self, __attribute__((unused)) const char *from, __attribute__((unused)) uint64_t bytes) { return (uint64_t)-1; } -static uint64_t directory_stream_read(const stream_t *self, uint64_t max, char *to) { +static uint64_t directory_stream_read(stream_t *self, uint64_t max, char *to) { fat16_directory_stream_t *fds = (fat16_directory_stream_t *)self; fs_node_t *node = fat16_open_at(fds->node, fds->index++); if (!node) { @@ -250,11 +552,14 @@ static uint64_t directory_stream_read(const stream_t *self, uint64_t max, char * uint64_t length = string_length(node->name); uint64_t to_read = length + 1 > max ? max : length + 1; memory_copy(node->name, to_read, to); - to[max - 1] = '\0'; - + to[to_read - 1] = '\0'; return to_read; } +static uint64_t directory_stream_truncate(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t size) { + return (uint64_t)-1; +} + static void directory_stream_close(stream_t *self) { fat16_directory_stream_t *fds = (fat16_directory_stream_t *)self; fat16_close(fds->node); @@ -266,6 +571,7 @@ stream_t *fat16_open_stream(const fs_node_t *source) { fat16_file_stream_t *result = memory_allocate(sizeof(fat16_file_stream_t)); result->stream.read = file_stream_read; result->stream.write = file_stream_write; + result->stream.truncate = file_stream_truncate; result->stream.close = file_stream_close; result->node = fat16_open_again(source); result->offset = 0; @@ -274,6 +580,7 @@ stream_t *fat16_open_stream(const fs_node_t *source) { fat16_directory_stream_t *result = memory_allocate(sizeof(fat16_directory_stream_t)); result->stream.read = directory_stream_read; result->stream.write = directory_stream_write; + result->stream.truncate = directory_stream_truncate; result->stream.close = directory_stream_close; result->node = fat16_open_again(source); result->index = 0; @@ -281,68 +588,25 @@ stream_t *fat16_open_stream(const fs_node_t *source) { } } -void fat16_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to) { - ASSERT(file->type == FAT16, "fat16_read: file is not FAT16"); - ASSERT(!file->is_dir, "fat16_read: can not read directory"); - ASSERT(file->size >= offset + size, "fat16_read: offset/size are out of bounds"); - - ensure_fat(); - - fat16_node_t *fat_file = (fat16_node_t *)file; - - uint32_t cluster_size = bpb.sectors_per_cluster * SECTOR_SIZE; - uint32_t next_cluster = fat_file->entry.first_cluster; - - // Assuming filesystem is correct. TODO Check for real. - - while (offset >= cluster_size) { - next_cluster = fat[next_cluster]; - offset -= cluster_size; - } - - uint8_t *cursor = to; - - uint8_t *tmp = memory_allocate(cluster_size); - ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count + - (bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE + - bpb.sectors_per_cluster * (next_cluster - 2), - bpb.sectors_per_cluster, tmp); - uint64_t prefix_size = size <= cluster_size - offset ? size : cluster_size - offset; - memory_copy(tmp + offset, prefix_size, cursor); - size -= prefix_size; - cursor += prefix_size; - next_cluster = fat[next_cluster]; - - while (size >= cluster_size) { - ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count + - (bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE + - bpb.sectors_per_cluster * (next_cluster - 2), - bpb.sectors_per_cluster, cursor); - size -= cluster_size; - cursor += cluster_size; - next_cluster = fat[next_cluster]; - } - - if (size) { - ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count + - (bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE + - bpb.sectors_per_cluster * (next_cluster - 2), - bpb.sectors_per_cluster, tmp); - memory_copy(tmp, size, cursor); - } - - memory_free(tmp); -} - void fat16_close(fs_node_t *node) { - ASSERT(node->type == FAT16, "fat16_close: node is not FAT16"); - ASSERT(node != fs, "fat16_close: can not unmount FS"); + if (node->type != FAT16 || node == fs) { + return; + } + inode_free(((fat16_node_t *)node)->inode); memory_free(node); } void fat16_unmount(fs_node_t *node) { - ASSERT(node->type == FAT16, "fat16_unmount: node is not FAT16"); - ASSERT(node == fs, "fat16_unmount: node is not filesystem"); + if (node->type != FAT16 || node != fs) { + return; + } + inode_free(((fat16_node_t *)node)->inode); memory_free(node); + + if (fat) { + memory_free(fat); + fat = NUL; + } + fs = NUL; } diff --git a/src/kernel/fat16.h b/src/kernel/fat16.h index 45616fc..f1c4e1f 100644 --- a/src/kernel/fat16.h +++ b/src/kernel/fat16.h @@ -15,7 +15,11 @@ fs_node_t *fat16_open_again(const fs_node_t *source); stream_t *fat16_open_stream(const fs_node_t *source); -void fat16_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to); +uint64_t fat16_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void *to); + +uint64_t fat16_write(fs_node_t *file, uint32_t offset, const void *from, uint32_t bytes); + +uint64_t fat16_truncate(fs_node_t *file, uint32_t size); void fat16_close(fs_node_t *node); diff --git a/src/kernel/fs.c b/src/kernel/fs.c index 43f5390..e551fe2 100644 --- a/src/kernel/fs.c +++ b/src/kernel/fs.c @@ -21,8 +21,16 @@ stream_t *fs_open_stream(const fs_node_t *source) { return fat16_open_stream(source); } -void fs_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to) { - fat16_read(file, offset, size, to); +void fs_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void *to) { + fat16_read(file, offset, bytes, to); +} + +void fs_write(fs_node_t *file, uint32_t offset, const void *from, uint32_t bytes) { + fat16_write(file, offset, from, bytes); +} + +void fs_truncate(fs_node_t *file, uint32_t size) { + fat16_truncate(file, size); } void fs_close(fs_node_t *node) { diff --git a/src/kernel/fs.h b/src/kernel/fs.h index e9b02cd..a6b0b6d 100644 --- a/src/kernel/fs.h +++ b/src/kernel/fs.h @@ -22,7 +22,11 @@ fs_node_t *fs_open_again(const fs_node_t *source); stream_t *fs_open_stream(const fs_node_t *source); -void fs_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to); +void fs_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void *to); + +void fs_write(fs_node_t *file, uint32_t offset, const void *from, uint32_t bytes); + +void fs_truncate(fs_node_t *file, uint32_t size); void fs_close(fs_node_t *node); diff --git a/src/kernel/keyboard.c b/src/kernel/keyboard.c index 5a20671..6d6f1ac 100644 --- a/src/kernel/keyboard.c +++ b/src/kernel/keyboard.c @@ -51,12 +51,12 @@ static void append_sequence(const char *s) { } } -static uint64_t stream_write(__attribute__((unused)) const stream_t *self, __attribute__((unused)) const char *from, +static uint64_t stream_write(__attribute__((unused)) stream_t *self, __attribute__((unused)) const char *from, __attribute__((unused)) uint64_t bytes) { return 0; } -static uint64_t stream_read(__attribute__((unused)) const stream_t *self, uint64_t max, char *to) { +static uint64_t stream_read(__attribute__((unused)) stream_t *self, uint64_t max, char *to) { while (!buffer_length) { __asm__ volatile("sti"); process_next(); @@ -79,10 +79,14 @@ static uint64_t stream_read(__attribute__((unused)) const stream_t *self, uint64 return size; } +static uint64_t stream_truncate(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t size) { + return size; +} + static void stream_close(__attribute__((unused)) stream_t *self) { } -static stream_t stream = {stream_write, stream_read, stream_close}; +static stream_t stream = {stream_write, stream_read, stream_truncate, stream_close}; static void on_key(uint8_t scancode) { const uint8_t pressed = !(scancode & 0x80); diff --git a/src/kernel/pipe.c b/src/kernel/pipe.c index 2dd7a7e..84c5198 100644 --- a/src/kernel/pipe.c +++ b/src/kernel/pipe.c @@ -23,17 +23,17 @@ typedef struct { pipe_t *pipe; } pipe_write_stream_t; -static uint64_t null_read(__attribute__((unused)) const stream_t *self, __attribute__((unused)) uint64_t max, +static uint64_t null_read(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t max, __attribute__((unused)) char *to) { return 0; } -static uint64_t null_write(__attribute__((unused)) const stream_t *self, __attribute__((unused)) const char *from, +static uint64_t null_write(__attribute__((unused)) stream_t *self, __attribute__((unused)) const char *from, __attribute__((unused)) uint64_t bytes) { return 0; } -static uint64_t pipe_read(const stream_t *self, uint64_t max, char *to) { +static uint64_t pipe_read(stream_t *self, uint64_t max, char *to) { pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe; while (pipe->begin == pipe->end) { @@ -60,7 +60,7 @@ static uint64_t pipe_read(const stream_t *self, uint64_t max, char *to) { return to_read; } -static uint64_t pipe_write(const stream_t *self, const char *from, uint64_t bytes) { +static uint64_t pipe_write(stream_t *self, const char *from, uint64_t bytes) { pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe; uint64_t written = 0; diff --git a/src/kernel/stream.h b/src/kernel/stream.h index a4d73f4..8e6c8e5 100644 --- a/src/kernel/stream.h +++ b/src/kernel/stream.h @@ -27,12 +27,14 @@ typedef struct stream stream_t; -typedef uint64_t (*stream_write_t)(const stream_t *self, const char *from, uint64_t bytes); -typedef uint64_t (*stream_read_t)(const stream_t *self, uint64_t max, char *to); +typedef uint64_t (*stream_write_t)(stream_t *self, const char *from, uint64_t bytes); +typedef uint64_t (*stream_read_t)(stream_t *self, uint64_t max, char *to); +typedef uint64_t (*stream_truncate_t)(stream_t *self, uint64_t size); typedef void (*stream_close_t)(stream_t *self); typedef struct stream { stream_write_t write; stream_read_t read; + stream_truncate_t truncate; stream_close_t close; } stream_t; diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index c1d35e8..386ffae 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -182,6 +182,13 @@ static uint64_t close(uint64_t fd) { return 0; } +static uint64_t truncate(uint64_t fd, uint64_t size) { + if (fd >= MAX_FDS || !current_process->fds[fd]) { + return (uint64_t)-1; + } + return current_process->fds[fd]->truncate(current_process->fds[fd], size); +} + static void exit(exit_code_t code) { current_process->state = PROCESS_ZOMBIE; current_process->code = code; @@ -214,6 +221,8 @@ uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t return open((const char *)arg1); case SYSCALL_CLOSE: return close(arg1); + case SYSCALL_TRUNCATE: + return truncate(arg1, arg2); case SYSCALL_EXIT: exit(arg1); return 0; diff --git a/src/kernel/syscall.h b/src/kernel/syscall.h index c7553ec..b5d3497 100644 --- a/src/kernel/syscall.h +++ b/src/kernel/syscall.h @@ -10,6 +10,7 @@ #define SYSCALL_WAIT 5 #define SYSCALL_OPEN 6 #define SYSCALL_CLOSE 7 +#define SYSCALL_TRUNCATE 8 #define SYSCALL_EXIT 60 void syscall_init(); diff --git a/src/kernel/vga.c b/src/kernel/vga.c index cb04e5e..96db46c 100644 --- a/src/kernel/vga.c +++ b/src/kernel/vga.c @@ -142,7 +142,7 @@ static void on_char_received(char c) { } } -static uint64_t stream_write(__attribute__((unused)) const stream_t *self, const char *from, uint64_t bytes) { +static uint64_t stream_write(__attribute__((unused)) stream_t *self, const char *from, uint64_t bytes) { uint64_t left = bytes; while (left--) { on_char_received(*from++); @@ -150,15 +150,19 @@ static uint64_t stream_write(__attribute__((unused)) const stream_t *self, const return bytes; } -static uint64_t stream_read(__attribute__((unused)) const stream_t *self, __attribute__((unused)) uint64_t max, +static uint64_t stream_read(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t max, __attribute__((unused)) char *to) { return 0; } +static uint64_t stream_truncate(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t size) { + return size; +} + static void stream_close(__attribute__((unused)) stream_t *self) { } -static stream_t stream = {stream_write, stream_read, stream_close}; +static stream_t stream = {stream_write, stream_read, stream_truncate, stream_close}; stream_t *vga_init() { clear(); diff --git a/src/user/app/cp/cp.c b/src/user/app/cp/cp.c new file mode 100644 index 0000000..23b3167 --- /dev/null +++ b/src/user/app/cp/cp.c @@ -0,0 +1,55 @@ +#include "src/lib/util.h" +#include "src/user/syscall.h" + +#define BLOCK_SIZE 65536 + +#define PRINT_S(s) write(1, s, sizeof(s) - 1); +#define PRINT_D(s) write(1, s, string_length(s)); + +exit_code_t main(uint64_t argc, const char **argv) { + if (argc != 3) { + PRINT_S("cp: requires source and target"); + return EXIT_CODE_GENERAL_FAILURE; + } + + uint64_t source = open(argv[1]); + if (source == (uint64_t)-1) { + PRINT_S("cp: source does not exist\n"); + return EXIT_CODE_GENERAL_FAILURE; + } + + uint64_t target = open(argv[2]); + if (target == (uint64_t)-1) { + PRINT_S("cp: target does not exist\n"); + close(source); + return EXIT_CODE_GENERAL_FAILURE; + } + + if (truncate(target, 0) == (uint64_t)-1) { + close(source); + close(target); + return EXIT_CODE_GENERAL_FAILURE; + } + + static char buffer[BLOCK_SIZE]; + uint64_t bytes; + while ((bytes = read(source, BLOCK_SIZE, buffer))) { + if (bytes == (uint64_t)-1) { + PRINT_S("cp: could not read file\n"); + close(source); + close(target); + return EXIT_CODE_GENERAL_FAILURE; + } + if (write(target, buffer, bytes) == (uint64_t)-1) { + PRINT_S("cp: could not write file\n"); + close(source); + close(target); + return EXIT_CODE_GENERAL_FAILURE; + } + } + + close(source); + close(target); + + return EXIT_CODE_OK; +} diff --git a/src/user/syscall.asm b/src/user/syscall.asm index f4a195f..564bd37 100644 --- a/src/user/syscall.asm +++ b/src/user/syscall.asm @@ -48,6 +48,12 @@ close: syscall ret +global truncate +truncate: + mov rax, 8 + syscall + ret + global exit exit: mov rax, 60 diff --git a/src/user/syscall.h b/src/user/syscall.h index 11af13a..ef60f3b 100644 --- a/src/user/syscall.h +++ b/src/user/syscall.h @@ -19,4 +19,6 @@ uint64_t open(const char *path); uint64_t close(uint64_t fd); +uint64_t truncate(uint64_t fd, uint64_t size); + void exit(exit_code_t code);