diff --git a/boredos.iso b/boredos.iso index 0ac7c54..090b150 100644 Binary files a/boredos.iso and b/boredos.iso differ diff --git a/build/fat32.o b/build/fat32.o index ad7c4cc..174b8fe 100644 Binary files a/build/fat32.o and b/build/fat32.o differ diff --git a/build/main.o b/build/main.o index a37f552..5b3817e 100644 Binary files a/build/main.o and b/build/main.o differ diff --git a/disk.img b/disk.img index 1f924f8..04be088 100644 Binary files a/disk.img and b/disk.img differ diff --git a/src/kernel/fat32.c b/src/kernel/fat32.c index 43db23c..b27969d 100644 --- a/src/kernel/fat32.c +++ b/src/kernel/fat32.c @@ -309,7 +309,7 @@ static int ramfs_read(FAT32_FileHandle *handle, void *buffer, int size) { int to_read = size - bytes_read; int available = handle->size - handle->position; if (to_read > available) to_read = available; - if (to_read > FAT32_CLUSTER_SIZE - offset_in_cluster) to_read = FAT32_CLUSTER_SIZE - offset_in_cluster; + if (to_read > (int)(FAT32_CLUSTER_SIZE - offset_in_cluster)) to_read = FAT32_CLUSTER_SIZE - offset_in_cluster; if (handle->cluster >= MAX_CLUSTERS) break; @@ -342,7 +342,7 @@ static int ramfs_write(FAT32_FileHandle *handle, const void *buffer, int size) { while (bytes_written < size) { uint32_t offset_in_cluster = handle->position % FAT32_CLUSTER_SIZE; int to_write = size - bytes_written; - if (to_write > FAT32_CLUSTER_SIZE - offset_in_cluster) to_write = FAT32_CLUSTER_SIZE - offset_in_cluster; + if (to_write > (int)(FAT32_CLUSTER_SIZE - offset_in_cluster)) to_write = FAT32_CLUSTER_SIZE - offset_in_cluster; if (handle->cluster >= MAX_CLUSTERS) break; @@ -514,13 +514,7 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m const char *p = path; if (*p == '/') p++; - fs_serial_str("[FAT32] realfs_open drive="); - fs_serial_char(drive); - fs_serial_str(" path='"); - fs_serial_str(path); - fs_serial_str("' mode="); - fs_serial_char(mode[0]); - fs_serial_str("\n"); + if (*p == 0) { // Root dir @@ -559,6 +553,7 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m found = false; uint32_t search_cluster = current_cluster; uint8_t *cluster_buf = (uint8_t*)kmalloc(vol->sectors_per_cluster * 512); + if (!cluster_buf) return NULL; while (search_cluster < 0x0FFFFFF8) { if (realfs_read_cluster(vol, search_cluster, cluster_buf) != 0) break; @@ -594,13 +589,6 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m if (match) { uint32_t cluster = (entry[e].start_cluster_high << 16) | entry[e].start_cluster_low; - fs_serial_str("[FAT32] MATCH '"); - fs_serial_str(name); - fs_serial_str("' cluster="); - fs_serial_num(cluster); - fs_serial_str(" size="); - fs_serial_num(entry[e].file_size); - fs_serial_str("\n"); uint32_t lba = vol->cluster_begin_lba + (search_cluster - 2) * vol->sectors_per_cluster; int sect_in_cluster = (e * 32) / 512; @@ -644,52 +632,53 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m int count = (vol->sectors_per_cluster * 512) / 32; for (int e = 0; e < count; e++) { - if (entries[e].filename[0] == 0 || entries[e].filename[0] == 0xE5) { - uint32_t lba = vol->cluster_begin_lba + (search_cluster - 2) * vol->sectors_per_cluster; - int sect_in_cluster = (e * 32) / 512; - free_sector = lba + sect_in_cluster; - free_offset = (e * 32) % 512; - found_free = true; - break; - } + if (entries[e].filename[0] == 0 || entries[e].filename[0] == 0xE5) { + uint32_t lba = vol->cluster_begin_lba + (search_cluster - 2) * vol->sectors_per_cluster; + int sect_in_cluster = (e * 32) / 512; + free_sector = lba + sect_in_cluster; + free_offset = (e * 32) % 512; + found_free = true; + break; + } } if (!found_free) search_cluster = realfs_next_cluster(vol, search_cluster); } if (found_free) { uint8_t *sect_buf = (uint8_t*)kmalloc(512); - vol->disk->read_sector(vol->disk, free_sector, sect_buf); - FAT32_DirEntry *d = (FAT32_DirEntry*)(sect_buf + free_offset); - - for(int k=0; k<8; k++) d->filename[k] = dos_name[k]; - for(int k=0; k<3; k++) d->extension[k] = dos_name[8+k]; - d->attributes = ATTR_ARCHIVE; - d->start_cluster_high = 0; - d->start_cluster_low = 0; - d->file_size = 0; - - // Write to disk - if (vol->disk->write_sector(vol->disk, free_sector, sect_buf) != 0) { - // Write failed, free the buffer and return NULL + if (sect_buf) { + vol->disk->read_sector(vol->disk, free_sector, sect_buf); + FAT32_DirEntry *d = (FAT32_DirEntry*)(sect_buf + free_offset); + + for(int k=0; k<8; k++) d->filename[k] = dos_name[k]; + for(int k=0; k<3; k++) d->extension[k] = dos_name[8+k]; + d->attributes = ATTR_ARCHIVE; + d->start_cluster_high = 0; + d->start_cluster_low = 0; + d->file_size = 0; + + // Write to disk + if (vol->disk->write_sector(vol->disk, free_sector, sect_buf) != 0) { + kfree(sect_buf); + kfree(cluster_buf); + return NULL; + } kfree(sect_buf); - kfree(cluster_buf); - return NULL; - } - kfree(sect_buf); - - FAT32_FileHandle *fh = ramfs_find_free_handle(); - if (fh) { - fh->valid = true; - fh->drive = drive; - fh->start_cluster = 0; - fh->cluster = 0; - fh->position = 0; - fh->size = 0; - fh->mode = 1; // Write - fh->dir_sector = free_sector; - fh->dir_offset = free_offset; - kfree(cluster_buf); - return fh; + + FAT32_FileHandle *fh = ramfs_find_free_handle(); + if (fh) { + fh->valid = true; + fh->drive = drive; + fh->start_cluster = 0; + fh->cluster = 0; + fh->position = 0; + fh->size = 0; + fh->mode = (mode[0] == 'a' ? 2 : 1); + fh->dir_sector = free_sector; + fh->dir_offset = free_offset; + kfree(cluster_buf); + return fh; + } } } kfree(cluster_buf); @@ -710,9 +699,22 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m fh->cluster = current_cluster; fh->position = 0; fh->size = file_size; - fh->mode = (mode[0] == 'w' ? 1 : 0); // Only R/W supported + fh->mode = (mode[0] == 'w' ? 1 : (mode[0] == 'a' ? 2 : 0)); fh->dir_sector = entry_sector; fh->dir_offset = entry_offset; + + if (mode[0] == 'a') { + fh->position = fh->size; + // Seek to EOF cluster + uint32_t cluster_size = vol->sectors_per_cluster * 512; + uint32_t pos = 0; + while (pos + cluster_size <= fh->position) { + uint32_t next = realfs_next_cluster(vol, fh->cluster); + if (next >= 0x0FFFFFF8) break; + fh->cluster = next; + pos += cluster_size; + } + } return fh; } @@ -736,7 +738,7 @@ static int realfs_read(FAT32_FileHandle *handle, void *buffer, int size) { uint32_t offset = handle->position % cluster_size; int to_copy = size - bytes_read; int available = cluster_size - offset; - if (handle->size - handle->position < available) available = handle->size - handle->position; + if ((int)(handle->size - handle->position) < available) available = handle->size - handle->position; if (to_copy > available) to_copy = available; for (int i = 0; i < to_copy; i++) { @@ -808,22 +810,25 @@ static int realfs_write(FAT32_FileHandle *handle, const void *buffer, int size) handle->start_cluster = new_cluster; handle->cluster = new_cluster; - // Mark new cluster as EOF in FAT + // Mark new cluster as EOF in FAT, preserve high bits uint32_t fat_sector = vol->fat_begin_lba + (new_cluster * 4) / 512; uint32_t fat_offset = (new_cluster * 4) % 512; uint8_t *fat_buf = (uint8_t*)kmalloc(512); - if (vol->disk->read_sector(vol->disk, fat_sector, fat_buf) == 0) { - *(uint32_t*)&fat_buf[fat_offset] = 0x0FFFFFF8; // EOF marker + if (fat_buf && vol->disk->read_sector(vol->disk, fat_sector, fat_buf) == 0) { + uint32_t old_val = *(uint32_t*)&fat_buf[fat_offset]; + *(uint32_t*)&fat_buf[fat_offset] = (old_val & 0xF0000000) | 0x0FFFFFF8; // EOF vol->disk->write_sector(vol->disk, fat_sector, fat_buf); } - kfree(fat_buf); + if (fat_buf) kfree(fat_buf); // Initialize the new cluster with zeros to avoid garbage uint32_t cluster_size_bytes = vol->sectors_per_cluster * 512; uint8_t *cbuf = (uint8_t*)kmalloc(cluster_size_bytes); - for(uint32_t i=0; idir_sector != 0 && handle->dir_offset != 0xFFFFFFFF && handle->dir_offset < 512) { @@ -875,36 +880,41 @@ static int realfs_write(FAT32_FileHandle *handle, const void *buffer, int size) if (handle->position % cluster_size == 0 && bytes_written < size) { uint32_t next = realfs_next_cluster(vol, handle->cluster); - if (next >= 0x0FFFFFF8) { + // If next is 0 (unallocated) or EOC, we must allocate a new cluster + if (next < 2 || next >= 0x0FFFFFF8) { uint32_t new_cluster = realfs_allocate_cluster(vol); if (new_cluster == 0) break; - // Link current cluster to new cluster in FAT + // Link current cluster to new cluster in FAT, preserve high bits uint32_t fat_sector = vol->fat_begin_lba + (handle->cluster * 4) / 512; uint32_t fat_offset = (handle->cluster * 4) % 512; uint8_t *fat_buf = (uint8_t*)kmalloc(512); - if (vol->disk->read_sector(vol->disk, fat_sector, fat_buf) == 0) { - *(uint32_t*)&fat_buf[fat_offset] = new_cluster; + if (fat_buf && vol->disk->read_sector(vol->disk, fat_sector, fat_buf) == 0) { + uint32_t old_val = *(uint32_t*)&fat_buf[fat_offset]; + *(uint32_t*)&fat_buf[fat_offset] = (old_val & 0xF0000000) | (new_cluster & 0x0FFFFFFF); vol->disk->write_sector(vol->disk, fat_sector, fat_buf); } - kfree(fat_buf); + if (fat_buf) kfree(fat_buf); - // Mark new cluster as EOF in FAT + // Mark new cluster as EOF in FAT, preserve high bits fat_sector = vol->fat_begin_lba + (new_cluster * 4) / 512; fat_offset = (new_cluster * 4) % 512; fat_buf = (uint8_t*)kmalloc(512); - if (vol->disk->read_sector(vol->disk, fat_sector, fat_buf) == 0) { - *(uint32_t*)&fat_buf[fat_offset] = 0x0FFFFFF8; // EOF marker + if (fat_buf && vol->disk->read_sector(vol->disk, fat_sector, fat_buf) == 0) { + uint32_t old_val = *(uint32_t*)&fat_buf[fat_offset]; + *(uint32_t*)&fat_buf[fat_offset] = (old_val & 0xF0000000) | 0x0FFFFFF8; // EOF vol->disk->write_sector(vol->disk, fat_sector, fat_buf); } - kfree(fat_buf); + if (fat_buf) kfree(fat_buf); // Init new cluster uint8_t *cbuf = (uint8_t*)kmalloc(cluster_size); - for(uint32_t i=0; istart_cluster; + fat32_close(parent_fh); + + // Check if it already exists + FAT32_FileHandle *check_fh = realfs_open(drive, path, "r"); + if (check_fh) { + fat32_close(check_fh); + return false; + } + + // Allocate cluster for new directory + uint32_t new_cluster = realfs_allocate_cluster(vol); + if (new_cluster == 0) return false; + + // Initialize new directory cluster with . and .. entries + uint32_t cluster_size = vol->sectors_per_cluster * 512; + uint8_t *cluster_buf = (uint8_t*)kmalloc(cluster_size); + if (!cluster_buf) return false; + for (uint32_t i = 0; i < cluster_size; i++) cluster_buf[i] = 0; + + FAT32_DirEntry *dot = (FAT32_DirEntry*)cluster_buf; + FAT32_DirEntry *dotdot = (FAT32_DirEntry*)(cluster_buf + 32); + + // . entry + for (int i = 0; i < 8; i++) dot->filename[i] = ' '; + for (int i = 0; i < 3; i++) dot->extension[i] = ' '; + dot->filename[0] = '.'; + dot->attributes = ATTR_DIRECTORY; + dot->start_cluster_high = (new_cluster >> 16); + dot->start_cluster_low = (new_cluster & 0xFFFF); + + // .. entry + for (int i = 0; i < 8; i++) dotdot->filename[i] = ' '; + for (int i = 0; i < 3; i++) dotdot->extension[i] = ' '; + dotdot->filename[0] = '.'; dotdot->filename[1] = '.'; + dotdot->attributes = ATTR_DIRECTORY; + dotdot->start_cluster_high = (parent_cluster >> 16); + dotdot->start_cluster_low = (parent_cluster & 0xFFFF); + + if (realfs_write_cluster(vol, new_cluster, cluster_buf) != 0) { + kfree(cluster_buf); + return false; + } + kfree(cluster_buf); + + // Find free entry in parent directory + uint32_t search_cluster = parent_cluster; + uint8_t *sect_buf = (uint8_t*)kmalloc(512); + bool found_free = false; + uint32_t free_sector = 0; + uint32_t free_offset = 0; + + uint8_t *dir_cluster_buf = (uint8_t*)kmalloc(cluster_size); + if (!dir_cluster_buf) { + kfree(sect_buf); + return false; + } + + while (search_cluster < 0x0FFFFFF8 && !found_free) { + if (realfs_read_cluster(vol, search_cluster, dir_cluster_buf) != 0) break; + FAT32_DirEntry *entries = (FAT32_DirEntry*)dir_cluster_buf; + int count = cluster_size / 32; + for (int e = 0; e < count; e++) { + if (entries[e].filename[0] == 0 || entries[e].filename[0] == 0xE5) { + uint32_t lba = vol->cluster_begin_lba + (search_cluster - 2) * vol->sectors_per_cluster; + free_sector = lba + (e * 32) / 512; + free_offset = (e * 32) % 512; + found_free = true; + break; + } + } + if (!found_free) { + uint32_t next = realfs_next_cluster(vol, search_cluster); + if (next >= 0x0FFFFFF8) { + // Need to expand directory? + uint32_t expanded = realfs_allocate_cluster(vol); + if (expanded == 0) break; + // Link, preserve high bits + uint32_t fat_sector = vol->fat_begin_lba + (search_cluster * 4) / 512; + uint32_t fat_offset = (search_cluster * 4) % 512; + if (vol->disk->read_sector(vol->disk, fat_sector, sect_buf) == 0) { + uint32_t old_val = *(uint32_t*)§_buf[fat_offset]; + *(uint32_t*)§_buf[fat_offset] = (old_val & 0xF0000000) | (expanded & 0x0FFFFFFF); + vol->disk->write_sector(vol->disk, fat_sector, sect_buf); + } + // Zero out new cluster + for(uint32_t k=0; kdisk->read_sector(vol->disk, free_sector, sect_buf); + FAT32_DirEntry *d = (FAT32_DirEntry*)(sect_buf + free_offset); + char dos_name[11]; + to_dos_filename(dirname, dos_name); + for (int k = 0; k < 8; k++) d->filename[k] = dos_name[k]; + for (int k = 0; k < 3; k++) d->extension[k] = dos_name[8+k]; + d->attributes = ATTR_DIRECTORY; + d->start_cluster_high = (new_cluster >> 16); + d->start_cluster_low = (new_cluster & 0xFFFF); + d->file_size = 0; + vol->disk->write_sector(vol->disk, free_sector, sect_buf); + } + + kfree(sect_buf); + return found_free; +} + bool fat32_mkdir(const char *path) { - if (parse_drive_from_path(&path) != 'A') return false; // RAMFS only for now + const char *p = path; + char drive = parse_drive_from_path(&p); uint64_t rflags; asm volatile("pushfq; pop %0; cli" : "=r"(rflags)); + + if (drive != 'A') { + bool res = realfs_mkdir(drive, p); + wm_notify_fs_change(); + asm volatile("push %0; popfq" : : "r"(rflags)); + return res; + } + char normalized[FAT32_MAX_PATH]; - fat32_normalize_path(path, normalized); + fat32_normalize_path(p, normalized); if (ramfs_find_file(normalized)) { asm volatile("push %0; popfq" : : "r"(rflags)); @@ -1428,6 +1576,48 @@ bool fat32_delete(const char *path) { return result; } +int fat32_get_info(const char *path, FAT32_FileInfo *info) { + uint64_t rflags; + asm volatile("pushfq; pop %0; cli" : "=r"(rflags)); + + const char *p = path; + char drive = parse_drive_from_path(&p); + + int result = -1; + if (drive == 'A') { + char normalized[FAT32_MAX_PATH]; + fat32_normalize_path(p, normalized); + FileEntry *entry = ramfs_find_file(normalized); + if (entry) { + fs_strcpy(info->name, entry->filename); + info->size = entry->size; + info->is_directory = (entry->attributes & ATTR_DIRECTORY) != 0; + info->start_cluster = entry->start_cluster; + result = 0; + } + } else { + FAT32_FileHandle *fh = realfs_open(drive, p, "r"); + if (fh) { + + extract_filename(p, info->name); + info->size = fh->size; + info->start_cluster = fh->start_cluster; + + if (fs_strcmp(p, "/") == 0 || fs_strcmp(p, "") == 0) { + info->is_directory = 1; + } else { + + info->is_directory = fat32_is_directory(path); + } + fat32_close(fh); + result = 0; + } + } + + asm volatile("push %0; popfq" : : "r"(rflags)); + return result; +} + bool fat32_exists(const char *path) { uint64_t rflags; asm volatile("pushfq; pop %0; cli" : "=r"(rflags)); @@ -1539,10 +1729,7 @@ int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entr char normalized[FAT32_MAX_PATH]; fat32_normalize_path(p, normalized); - extern void serial_write(const char *str); - serial_write("[DEBUG] fat32_list_directory (RAMFS) path: "); - serial_write(normalized); - serial_write("\n"); + for (int i = 0; i < MAX_FILES && count < max_entries; i++) { if (files[i].used && fs_strcmp(files[i].parent_path, normalized) == 0) { @@ -1553,10 +1740,6 @@ int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entr count++; } } - serial_write("[DEBUG] fat32_list_directory (RAMFS) found count: "); - extern void serial_write_num(uint32_t n); - serial_write_num(count); - serial_write("\n"); } else { count = realfs_list_directory(drive, p, entries, max_entries); } diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index bf54d4b..6c65bfa 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -468,6 +468,12 @@ static uint64_t syscall_handler_inner(uint64_t syscall_num, uint64_t arg1, uint6 const char *path = (const char *)arg2; if (!path) return -1; return fat32_delete(path) ? 0 : -1; + } else if (cmd == FS_CMD_GET_INFO) { + const char *path = (const char *)arg2; + FAT32_FileInfo *info = (FAT32_FileInfo *)arg3; + if (!path || !info) return -1; + extern int fat32_get_info(const char *path, FAT32_FileInfo *info); + return (uint64_t)fat32_get_info(path, info); } else if (cmd == FS_CMD_MKDIR) { const char *path = (const char *)arg2; if (!path) return -1; diff --git a/src/kernel/syscall.h b/src/kernel/syscall.h index b4401a4..78fd788 100644 --- a/src/kernel/syscall.h +++ b/src/kernel/syscall.h @@ -37,6 +37,7 @@ typedef struct registers_t registers_t; #define FS_CMD_EXISTS 11 #define FS_CMD_GETCWD 12 #define FS_CMD_CHDIR 13 +#define FS_CMD_GET_INFO 14 void syscall_init(void); uint64_t syscall_handler_c(registers_t *regs); diff --git a/src/kernel/userland/cp.c b/src/kernel/userland/cp.c index 21b7c13..6007e70 100644 --- a/src/kernel/userland/cp.c +++ b/src/kernel/userland/cp.c @@ -4,33 +4,163 @@ #include #include -int main(int argc, char **argv) { - if (argc < 3) { - printf("Usage: cp \n"); - return 1; +typedef int bool; +#define true 1 +#define false 0 + +void combine_path(char *dest, const char *path1, const char *path2) { + int i = 0; + while (path1[i]) { + dest[i] = path1[i]; + i++; + } + if (i > 0 && dest[i-1] != '/') { + dest[i++] = '/'; + } + int j = 0; + while (path2[j]) { + dest[i++] = path2[j++]; + } + dest[i] = 0; +} + +const char* get_basename(const char *path) { + const char *last_slash = NULL; + int len = 0; + while (path[len]) { + if (path[len] == '/') last_slash = path + len; + len++; } - int fd_in = sys_open(argv[1], "r"); - if (fd_in < 0) { - printf("Error: Cannot open source %s\n", argv[1]); - return 1; + if (!last_slash) return path; + + // If it ends with a slash, skip it and find the previous one + if (last_slash[1] == '\0') { + if (len <= 1) return path; // root "/" + int i = len - 2; + while (i >= 0 && path[i] != '/') i--; + if (i < 0) return path; + return path + i + 1; } - int fd_out = sys_open(argv[2], "w"); - if (fd_out < 0) { - printf("Error: Cannot create destination %s\n", argv[2]); + return last_slash + 1; +} + +void copy_recursive(const char *src, const char *dst) { + FAT32_FileInfo info; + if (sys_get_file_info(src, &info) < 0) { + printf("Error: Cannot get info for %s\n", src); + return; + } + + if (info.is_directory) { + if (sys_mkdir(dst) < 0) { + // Might already exist + } + + FAT32_FileInfo entries[64]; + int count = sys_list(src, entries, 64); + for (int i = 0; i < count; i++) { + if (strcmp(entries[i].name, ".") == 0 || strcmp(entries[i].name, "..") == 0) continue; + + char sub_src[512], sub_dst[512]; + combine_path(sub_src, src, entries[i].name); + combine_path(sub_dst, dst, entries[i].name); + copy_recursive(sub_src, sub_dst); + } + } else { + int fd_in = sys_open(src, "r"); + if (fd_in < 0) { + printf("Error: Cannot open source %s\n", src); + return; + } + int fd_out = sys_open(dst, "w"); + if (fd_out < 0) { + printf("Error: Cannot create destination %s\n", dst); + sys_close(fd_in); + return; + } + char buffer[4096]; + int bytes; + while ((bytes = sys_read(fd_in, buffer, sizeof(buffer))) > 0) { + sys_write_fs(fd_out, buffer, bytes); + } sys_close(fd_in); + sys_close(fd_out); + } +} + +int main(int argc, char **argv) { + bool recursive = false; + char *src_path = NULL; + char *dst_path = NULL; + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-r") == 0) { + recursive = true; + } else if (!src_path) { + src_path = argv[i]; + } else if (!dst_path) { + dst_path = argv[i]; + } + } + + if (!src_path || !dst_path) { + printf("Usage: cp [-r] \n"); return 1; } - char buffer[4096]; - int bytes; - while ((bytes = sys_read(fd_in, buffer, sizeof(buffer))) > 0) { - sys_write_fs(fd_out, buffer, bytes); + FAT32_FileInfo info_src; + if (sys_get_file_info(src_path, &info_src) < 0) { + printf("Error: Source %s does not exist\n", src_path); + return 1; + } + + if (info_src.is_directory && !recursive) { + printf("Error: %s is a directory (use -r to copy recursively)\n", src_path); + return 1; + } + + char actual_dst[512]; + FAT32_FileInfo info_dst; + if (sys_get_file_info(dst_path, &info_dst) == 0 && info_dst.is_directory) { + // If destination is a directory, copy INTO it + const char *base = get_basename(src_path); + // Clean up trailing slash from basename if any (get_basename handles it mostly) + char clean_base[256]; + int k = 0; + while (base[k] && base[k] != '/') { + clean_base[k] = base[k]; + k++; + } + clean_base[k] = 0; + combine_path(actual_dst, dst_path, clean_base); + } else { + strcpy(actual_dst, dst_path); + } + + if (recursive) { + copy_recursive(src_path, actual_dst); + } else { + int fd_in = sys_open(src_path, "r"); + if (fd_in < 0) { + printf("Error: Cannot open source %s\n", src_path); + return 1; + } + int fd_out = sys_open(actual_dst, "w"); + if (fd_out < 0) { + printf("Error: Cannot create destination %s\n", actual_dst); + sys_close(fd_in); + return 1; + } + char buffer[4096]; + int bytes; + while ((bytes = sys_read(fd_in, buffer, sizeof(buffer))) > 0) { + sys_write_fs(fd_out, buffer, bytes); + } + sys_close(fd_in); + sys_close(fd_out); } - - sys_close(fd_in); - sys_close(fd_out); return 0; } diff --git a/src/kernel/userland/libc/syscall.c b/src/kernel/userland/libc/syscall.c index 6f7fc6d..d7c0fab 100644 --- a/src/kernel/userland/libc/syscall.c +++ b/src/kernel/userland/libc/syscall.c @@ -109,6 +109,10 @@ int sys_list(const char *path, FAT32_FileInfo *entries, int max_entries) { return (int)syscall4(SYS_FS, FS_CMD_LIST, (uint64_t)path, (uint64_t)entries, (uint64_t)max_entries); } +int sys_get_file_info(const char *path, FAT32_FileInfo *info) { + return (int)syscall3(SYS_FS, FS_CMD_GET_INFO, (uint64_t)path, (uint64_t)info); +} + int sys_delete(const char *path) { return (int)syscall2(SYS_FS, FS_CMD_DELETE, (uint64_t)path); } diff --git a/src/kernel/userland/libc/syscall.h b/src/kernel/userland/libc/syscall.h index 19d6437..9dd04d4 100644 --- a/src/kernel/userland/libc/syscall.h +++ b/src/kernel/userland/libc/syscall.h @@ -27,6 +27,7 @@ #define FS_CMD_EXISTS 11 #define FS_CMD_GETCWD 12 #define FS_CMD_CHDIR 13 +#define FS_CMD_GET_INFO 14 // System Commands (via SYS_SYSTEM) #define SYSTEM_CMD_SET_BG_COLOR 1 @@ -100,6 +101,7 @@ typedef struct { } FAT32_FileInfo; int sys_list(const char *path, FAT32_FileInfo *entries, int max_entries); +int sys_get_file_info(const char *path, FAT32_FileInfo *info); // Network API typedef struct { uint8_t bytes[6]; } net_mac_address_t; diff --git a/src/kernel/userland/ls.c b/src/kernel/userland/ls.c index c31e37f..56c611d 100644 --- a/src/kernel/userland/ls.c +++ b/src/kernel/userland/ls.c @@ -20,6 +20,24 @@ int main(int argc, char **argv) { } } + FAT32_FileInfo info; + if (sys_get_file_info(path, &info) < 0) { + sys_set_text_color(error_color); + printf("Error: Path '%s' does not exist\n", path); + sys_set_text_color(default_color); + return 1; + } + + if (!info.is_directory) { + sys_set_text_color(file_color); + printf("[FILE] %s", info.name); + sys_set_text_color(size_color); + printf(" (%d bytes)\n", info.size); + sys_set_text_color(default_color); + printf("\nTotal: 1 items\n"); + return 0; + } + FAT32_FileInfo entries[128]; int count = sys_list(path, entries, 128); diff --git a/src/kernel/userland/mv.c b/src/kernel/userland/mv.c index 9a35cc2..a6d3c41 100644 --- a/src/kernel/userland/mv.c +++ b/src/kernel/userland/mv.c @@ -4,37 +4,125 @@ #include #include +void combine_path(char *dest, const char *path1, const char *path2) { + int i = 0; + while (path1[i]) { + dest[i] = path1[i]; + i++; + } + if (i > 0 && dest[i-1] != '/') { + dest[i++] = '/'; + } + int j = 0; + while (path2[j]) { + dest[i++] = path2[j++]; + } + dest[i] = 0; +} + +const char* get_basename(const char *path) { + const char *last_slash = NULL; + int len = 0; + while (path[len]) { + if (path[len] == '/') last_slash = path + len; + len++; + } + + if (!last_slash) return path; + + if (last_slash[1] == '\0') { + if (len <= 1) return path; + int i = len - 2; + while (i >= 0 && path[i] != '/') i--; + if (i < 0) return path; + return path + i + 1; + } + + return last_slash + 1; +} + +void copy_recursive(const char *src, const char *dst) { + FAT32_FileInfo info; + if (sys_get_file_info(src, &info) < 0) return; + + if (info.is_directory) { + sys_mkdir(dst); + FAT32_FileInfo entries[64]; + int count = sys_list(src, entries, 64); + for (int i = 0; i < count; i++) { + if (strcmp(entries[i].name, ".") == 0 || strcmp(entries[i].name, "..") == 0) continue; + char sub_src[512], sub_dst[512]; + combine_path(sub_src, src, entries[i].name); + combine_path(sub_dst, dst, entries[i].name); + copy_recursive(sub_src, sub_dst); + } + } else { + int fd_in = sys_open(src, "r"); + if (fd_in < 0) return; + int fd_out = sys_open(dst, "w"); + if (fd_out < 0) { sys_close(fd_in); return; } + char buffer[4096]; + int bytes; + while ((bytes = sys_read(fd_in, buffer, sizeof(buffer))) > 0) { + sys_write_fs(fd_out, buffer, bytes); + } + sys_close(fd_in); + sys_close(fd_out); + } +} + +void delete_recursive(const char *path) { + FAT32_FileInfo info; + if (sys_get_file_info(path, &info) < 0) return; + + if (info.is_directory) { + FAT32_FileInfo entries[64]; + int count = sys_list(path, entries, 64); + for (int i = 0; i < count; i++) { + if (strcmp(entries[i].name, ".") == 0 || strcmp(entries[i].name, "..") == 0) continue; + char sub_path[512]; + combine_path(sub_path, path, entries[i].name); + delete_recursive(sub_path); + } + sys_delete(path); + } else { + sys_delete(path); + } +} + int main(int argc, char **argv) { if (argc < 3) { printf("Usage: mv \n"); return 1; } - int fd_in = sys_open(argv[1], "r"); - if (fd_in < 0) { - printf("Error: Cannot open source %s\n", argv[1]); + char *src_path = argv[1]; + char *dst_path = argv[2]; + + FAT32_FileInfo info_src; + if (sys_get_file_info(src_path, &info_src) < 0) { + printf("Error: Cannot open source %s\n", src_path); return 1; } - - int fd_out = sys_open(argv[2], "w"); - if (fd_out < 0) { - printf("Error: Cannot create destination %s\n", argv[2]); - sys_close(fd_in); - return 1; - } - - char buffer[4096]; - int bytes; - while ((bytes = sys_read(fd_in, buffer, sizeof(buffer))) > 0) { - sys_write_fs(fd_out, buffer, bytes); - } - - sys_close(fd_in); - sys_close(fd_out); - - if (sys_delete(argv[1]) != 0) { - printf("Warning: Failed to delete source %s after copy\n", argv[1]); + + char actual_dst[512]; + FAT32_FileInfo info_dst; + if (sys_get_file_info(dst_path, &info_dst) == 0 && info_dst.is_directory) { + const char *base = get_basename(src_path); + char clean_base[256]; + int k = 0; + while (base[k] && base[k] != '/') { + clean_base[k] = base[k]; + k++; + } + clean_base[k] = 0; + combine_path(actual_dst, dst_path, clean_base); + } else { + strcpy(actual_dst, dst_path); } + + copy_recursive(src_path, actual_dst); + delete_recursive(src_path); return 0; }