Write getdents for ext2. Fix and test all getdents functions.

This commit is contained in:
Enrico Fraccaroli
2021-12-16 09:08:25 +01:00
parent fe13f087c9
commit 33c02fe570
4 changed files with 184 additions and 89 deletions
+99 -19
View File
@@ -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)
+3 -2
View File
@@ -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.
+2 -3
View File
@@ -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;
}
+80 -65
View File
@@ -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);
}