From 33c02fe570169bd9b65ba4f0cb52d3dc3593c129 Mon Sep 17 00:00:00 2001 From: Enrico Fraccaroli Date: Thu, 16 Dec 2021 09:08:25 +0100 Subject: [PATCH] Write getdents for ext2. Fix and test all getdents functions. --- mentos/src/fs/ext2.c | 118 ++++++++++++++++++++++++++------ mentos/src/fs/initrd.c | 5 +- mentos/src/fs/readdir.c | 5 +- programs/ls.c | 145 ++++++++++++++++++++++------------------ 4 files changed, 184 insertions(+), 89 deletions(-) diff --git a/mentos/src/fs/ext2.c b/mentos/src/fs/ext2.c index 57ab631..f20dd87 100644 --- a/mentos/src/fs/ext2.c +++ b/mentos/src/fs/ext2.c @@ -1064,24 +1064,105 @@ static ssize_t ext2_write_inode_data(ext2_filesystem_t *fs, ext2_inode_t *inode, return size_to_read; } -static int ext2_init_file(ext2_filesystem_t *fs, ext2_inode_t *inode, ext2_dirent_t *direntry, vfs_file_t *file); - -static vfs_file_t *ext2_find_entry(vfs_file_t *file, char *name) +/// @brief Reads contents of the directories to a dirent buffer, updating +/// the offset and returning the number of written bytes in the buffer, +/// it assumes that all paths are well-formed. +/// @param file The directory handler. +/// @param dirp The buffer where the data should be written. +/// @param doff The offset inside the buffer where the data should be written. +/// @param count The maximum length of the buffer. +/// @return The number of written bytes in the buffer. +static int ext2_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t count) { // Get the filesystem. ext2_filesystem_t *fs = (ext2_filesystem_t *)file->device; if (fs == NULL) { pr_err("The file does not belong to an EXT2 filesystem (%s).\n", file->name); - return NULL; + return -1; } // Get the inode associated with the file. ext2_inode_t inode; if (ext2_read_inode(fs, &inode, file->ino) == -1) { pr_err("Failed to read the inode (%d).\n", file->ino); + return -1; + } + + uint32_t block_index = 0, dir_offset = 0, total_offset = 0, written = 0; + off_t current = 0; + ext2_dirent_t *direntry = NULL; + + // Start by reading the first block of the inode. + if (!ext2_read_inode_block(fs, &inode, block_index, fs->block_buffer)) { + pr_err("Failed to read the inode block `%d`\n", block_index); + return -1; + } + + // Keep reading until we searched the whole inode. + while ((total_offset < inode.size) && (written < count)) { + // If we exceed the size of a block, move to the next block. + if (dir_offset >= fs->block_size) { + // Increase the block index. + ++block_index; + // Remove the exceeding size, so that we start correctly in the new block. + dir_offset -= fs->block_size; + // Read the new block. + if (!ext2_read_inode_block(fs, &inode, block_index, fs->block_buffer)) { + pr_err("Failed to read the inode block `%d`\n", block_index); + return -1; + } + } + // Get the directory entry. + direntry = (ext2_dirent_t *)((uintptr_t)fs->block_buffer + dir_offset); + if (direntry == NULL) { + pr_err("We found a NULL ext2_dirent_t\n"); + return -1; + } + + // Advance the offsets. + dir_offset += direntry->rec_len; + total_offset += direntry->rec_len; + + // Skip if already provided. + current += sizeof(dirent_t); + if (current <= doff) { + continue; + } + + // Write on current directory entry data. + dirp->d_ino = direntry->inode; + dirp->d_type = direntry->file_type; + memset(dirp->d_name, 0, NAME_MAX); + strncpy(dirp->d_name, direntry->name, direntry->name_len); + dirp->d_off = direntry->rec_len; + dirp->d_reclen = direntry->rec_len; + + // Increment the amount written. + written += sizeof(dirent_t); + + // Move to next writing position. + ++dirp; + } + return written; +} + +static int ext2_init_file(ext2_filesystem_t *fs, ext2_inode_t *inode, ext2_dirent_t *direntry, vfs_file_t *file); + +static vfs_file_t *ext2_find_entry(vfs_file_t *directory, char *name) +{ + // Get the filesystem. + ext2_filesystem_t *fs = (ext2_filesystem_t *)directory->device; + if (fs == NULL) { + pr_err("The file does not belong to an EXT2 filesystem (%s).\n", directory->name); + return NULL; + } + // Get the inode associated with the file. + ext2_inode_t inode; + if (ext2_read_inode(fs, &inode, directory->ino) == -1) { + pr_err("Failed to read the inode (%d).\n", directory->ino); return NULL; } uint32_t block_index = 0, dir_offset = 0, total_offset = 0; - ext2_dirent_t *direntry = NULL; + ext2_dirent_t direntry, *entry = NULL; // Start by reading the first block of the inode. if (!ext2_read_inode_block(fs, &inode, block_index, fs->block_buffer)) { pr_err("Failed to read the inode block `%d`\n", block_index); @@ -1102,33 +1183,32 @@ static vfs_file_t *ext2_find_entry(vfs_file_t *file, char *name) } } // Get the directory entry. - direntry = (ext2_dirent_t *)((uintptr_t)fs->block_buffer + dir_offset); - pr_debug("direntry : `%s` (%d vs %d)\n", direntry->name, direntry->name_len, strlen(name)); + entry = (ext2_dirent_t *)((uintptr_t)fs->block_buffer + dir_offset); // Check if the entry has the same name. - if ((direntry->inode != 0) && (strlen(name) == direntry->name_len)) - if (!strcmp(direntry->name, name)) + if ((entry->inode != 0) && (strlen(name) == entry->name_len)) + if (!strncmp(entry->name, name, entry->name_len)) break; // Advance the offsets. - dir_offset += direntry->rec_len; - total_offset += direntry->rec_len; + dir_offset += entry->rec_len; + total_offset += entry->rec_len; // Reset the direntry pointer. - direntry = NULL; + entry = NULL; } - - if (!direntry) { + if (!entry) { return NULL; } + memcpy(&direntry, entry, sizeof(ext2_dirent_t)); vfs_file_t *new_file = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL); memset(new_file, 0, sizeof(vfs_file_t)); - if (ext2_read_inode(fs, &inode, direntry->inode) == -1) { - pr_err("Failed to read the inode (%d).\n", direntry->inode); + if (ext2_read_inode(fs, &inode, direntry.inode) == -1) { + pr_err("Failed to read the inode (%d).\n", direntry.inode); kmem_cache_free(new_file); return NULL; } - if (ext2_init_file(fs, &inode, direntry, new_file) == -1) { - pr_err("Failed to initialize the new file (%d).\n", direntry->inode); + if (ext2_init_file(fs, &inode, &direntry, new_file) == -1) { + pr_err("Failed to initialize the new file (%d).\n", direntry.inode); kmem_cache_free(new_file); return NULL; } @@ -1233,7 +1313,7 @@ static vfs_file_operations_t ext2_fs_operations = { .lseek_f = NULL, .stat_f = NULL, .ioctl_f = NULL, - .getdents_f = NULL + .getdents_f = ext2_getdents }; static int ext2_init_file(ext2_filesystem_t *fs, ext2_inode_t *inode, ext2_dirent_t *direntry, vfs_file_t *file) diff --git a/mentos/src/fs/initrd.c b/mentos/src/fs/initrd.c index 2c6cb78..322531b 100644 --- a/mentos/src/fs/initrd.c +++ b/mentos/src/fs/initrd.c @@ -159,7 +159,6 @@ static int initrd_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t if (file->ino >= INITRD_MAX_FILES) { return -1; } - memset(dirp, 0, count); initrd_file_t *tdir = &fs_specs.headers[file->ino]; int len = strlen(tdir->fileName); @@ -183,9 +182,11 @@ static int initrd_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t continue; } // Skip if already provided. - if (current++ < doff) { + current += sizeof(dirent_t); + if (current <= doff) { continue; } + if (*(entry->fileName + len) == '/') ++len; // Write on current dirp. diff --git a/mentos/src/fs/readdir.c b/mentos/src/fs/readdir.c index 54f74f0..f200ff3 100644 --- a/mentos/src/fs/readdir.c +++ b/mentos/src/fs/readdir.c @@ -47,8 +47,7 @@ int sys_getdents(int fd, dirent_t *dirp, unsigned int count) int actual_read = vfs_getdents(file, dirp, vfd->file_struct->f_pos, count); // Update the offset. - if (actual_read > 0) { - vfd->file_struct->f_pos += (actual_read / sizeof(dirent_t)); - } + if (actual_read > 0) + vfd->file_struct->f_pos += actual_read; return actual_read; } diff --git a/programs/ls.c b/programs/ls.c index 0b233d3..92ac6a3 100644 --- a/programs/ls.c +++ b/programs/ls.c @@ -25,77 +25,92 @@ #define FG_BRIGHT_WHITE "\033[97m" #define FG_BRIGHT_YELLOW "\033[93m" +#define DENTS_NUM 12 + +static inline void print_dir_entry(dirent_t *dirent, const char *path, unsigned int flags, size_t *total_size) +{ + static char relative_path[PATH_MAX]; + tm_t *timeinfo; + stat_t dstat; + + // Check if the file starts with a dot (hidden), and we did not receive + // the `a` flag. + if ((dirent->d_name[0] == '.') && !bitmask_check(flags, FLAG_A)) { + return; + } + + // Prepare the relative path. + strcpy(relative_path, path); + if (strcmp(path, "/") != 0) + strcat(relative_path, "/"); + strcat(relative_path, dirent->d_name); + + // Stat the file. + if (stat(relative_path, &dstat) == -1) { + return; + } + + // Deal with the coloring. + if ((dirent->d_type == DT_REG) && bitmask_check(dstat.st_mode, S_IXUSR)) { + puts(FG_BRIGHT_YELLOW); + } else if (dirent->d_type == DT_DIR) { + puts(FG_BRIGHT_CYAN); + } else if (dirent->d_type == DT_BLK) { + puts(FG_BRIGHT_GREEN); + } + + // Deal with the -l. + if (bitmask_check(flags, FLAG_L)) { + // Get the broken down time from the creation time of the file. + timeinfo = localtime(&dstat.st_ctime); + // Print the file type. + putchar(dt_char_array[dirent->d_type]); + // Print the access permissions. + putchar(bitmask_check(dstat.st_mode, S_IRUSR) ? 'r' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IWUSR) ? 'w' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IXUSR) ? 'x' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IRGRP) ? 'r' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IWGRP) ? 'w' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IXGRP) ? 'x' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IROTH) ? 'r' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IWOTH) ? 'w' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IXOTH) ? 'x' : '-'); + // Add a space. + putchar(' '); + // Print the rest. + printf("%4d %4d %11s %2d/%2d %2d:%2d %s\n", + dstat.st_uid, + dstat.st_gid, + to_human_size(dstat.st_size), + timeinfo->tm_mon, + timeinfo->tm_mday, + timeinfo->tm_hour, + timeinfo->tm_min, + dirent->d_name); + (*total_size) += dstat.st_size; + } else { + printf("%s ", dirent->d_name); + } + + // Reset the color. + puts(FG_BRIGHT_WHITE); +} + static void print_ls(int fd, const char *path, unsigned int flags) { - char relative_path[PATH_MAX], hidden = 0; - dirent_t dent; - stat_t dstat; + dirent_t dents[DENTS_NUM]; + memset(&dents, 0, DENTS_NUM * sizeof(dirent_t)); + size_t total_size = 0; - tm_t *timeinfo; - while (getdents(fd, &dent, sizeof(dirent_t)) == sizeof(dirent_t)) { - // Check if the file starts with a dot (hidden), and we did not receive - // the `a` flag. - if ((dent.d_name[0] == '.') && !bitmask_check(flags, FLAG_A)) { - continue; + while (getdents(fd, dents, DENTS_NUM * sizeof(dirent_t))) { + for (size_t i = 0; i < DENTS_NUM; ++i) { + if (dents[i].d_ino == 0) + break; + print_dir_entry(&dents[i], path, flags, &total_size); } - - // Prepare the relative path. - strcpy(relative_path, path); - if (strcmp(path, "/") != 0) - strcat(relative_path, "/"); - strcat(relative_path, dent.d_name); - - // Stat the file. - if (stat(relative_path, &dstat) == -1) { - continue; - } - - // Deal with the coloring. - if ((dent.d_type == DT_REG) && bitmask_check(dstat.st_mode, S_IXUSR)) { - puts(FG_BRIGHT_YELLOW); - } else if (dent.d_type == DT_DIR) { - puts(FG_BRIGHT_CYAN); - } else if (dent.d_type == DT_BLK) { - puts(FG_BRIGHT_GREEN); - } - - // Deal with the -l. - if (bitmask_check(flags, FLAG_L)) { - // Get the broken down time from the creation time of the file. - timeinfo = localtime(&dstat.st_ctime); - // Print the file type. - putchar(dt_char_array[dent.d_type]); - // Print the access permissions. - putchar(bitmask_check(dstat.st_mode, S_IRUSR) ? 'r' : '-'); - putchar(bitmask_check(dstat.st_mode, S_IWUSR) ? 'w' : '-'); - putchar(bitmask_check(dstat.st_mode, S_IXUSR) ? 'x' : '-'); - putchar(bitmask_check(dstat.st_mode, S_IRGRP) ? 'r' : '-'); - putchar(bitmask_check(dstat.st_mode, S_IWGRP) ? 'w' : '-'); - putchar(bitmask_check(dstat.st_mode, S_IXGRP) ? 'x' : '-'); - putchar(bitmask_check(dstat.st_mode, S_IROTH) ? 'r' : '-'); - putchar(bitmask_check(dstat.st_mode, S_IWOTH) ? 'w' : '-'); - putchar(bitmask_check(dstat.st_mode, S_IXOTH) ? 'x' : '-'); - // Add a space. - putchar(' '); - // Print the rest. - printf("%4d %4d %11s %2d/%2d %2d:%2d %s\n", - dstat.st_uid, - dstat.st_gid, - to_human_size(dstat.st_size), - timeinfo->tm_mon, - timeinfo->tm_mday, - timeinfo->tm_hour, - timeinfo->tm_min, - dent.d_name); - total_size += dstat.st_size; - } else { - printf("%s ", dent.d_name); - } - - // Reset the color. - puts(FG_BRIGHT_WHITE); } printf("\n"); + if (bitmask_check(flags, FLAG_L)) { printf("Total: %d byte\n", total_size); }