Browse Source

read and seek

master
Mathieu Serandour 11 months ago
parent
commit
1e054c06b8
  1. 291
      kernel/fs/fat32/fat32.c
  2. 133
      kernel/fs/fat32/fat32.h
  3. 101
      kernel/fs/fs.h
  4. 31
      kernel/fs/vfs.h
  5. 514
      kernel/fs/vfs_dirs.c
  6. 336
      kernel/fs/vfs_files.c

291
kernel/fs/fat32/fat32.c

@ -19,9 +19,38 @@
#define FAT_CACHE_SIZE 2048
typedef uint32_t cluster_t;
//////////////////////////
// functions prototypes //
//////////////////////////
void fat32_unmount(fs_t* fs);
int fat32_read_file_sectors(
fs_t* restrict fs,
file_t* restrict fd,
void* restrict buf,
uint64_t begin,
size_t n
);
int fat32_write_file_sectors(
fs_t* restrict fs,
file_t* restrict file,
const void* restrict buf,
uint64_t begin,
size_t n
);
// juste take the lower
// lba bits
uint16_t lba_hash(uint64_t lba) {
static uint16_t lba_hash(uint64_t lba) {
return lba & (FAT_CACHE_SIZE-1);
}
@ -37,10 +66,10 @@ typedef struct block_cache {
} block_cache_t;
typedef struct {
uint32_t clusters_size;
uint32_t cluster_size;
uint32_t tables_count;
uint32_t fat_begin;
uint32_t data_begin;
cluster_t fat_begin;
cluster_t data_begin;
block_cache_t fat_cache;
@ -74,8 +103,10 @@ void write(disk_part_t* part, uint64_t lba, void* buf, size_t count) {
}
static uint64_t cluster_begin(uint32_t cluster, fat32_privates_t* fp) {
return ((cluster - 2) * fp->clusters_size) + fp->data_begin;
static
uint64_t __attribute__((const))
cluster_begin(cluster_t cluster, fat32_privates_t* restrict fp) {
return ((cluster - 2) * fp->cluster_size) + fp->data_begin;
}
@ -149,7 +180,7 @@ void* add_cache_entry(block_cache_t* cache, uint64_t lba) {
* the sector content.
*/
static
void* fetch_fat_sector(disk_part_t* part, block_cache_t* cache, uint32_t lba) {
void* fetch_fat_sector(disk_part_t* part, block_cache_t* cache, uint64_t lba) {
void* buf = cache_access(cache, lba);
// cache hit
@ -171,10 +202,10 @@ void* fetch_fat_sector(disk_part_t* part, block_cache_t* cache, uint32_t lba) {
*
* @param cluster the entry cluster
* @param last output: non null if this is the last entry of a file
* @return uint32_t a 28bit block offset for the entry
* @return cluster_t a 28bit block offset for the entry
*/
static
uint32_t readFAT(disk_part_t* part,
cluster_t readFAT(disk_part_t* part,
fat32_privates_t* pr,
uint64_t cluster,
int* last
@ -185,7 +216,7 @@ uint32_t readFAT(disk_part_t* part,
void* sector = fetch_fat_sector(part, &pr->fat_cache, lba);
uint32_t ent = *(uint32_t *)(sector + sector_offset);
cluster_t ent = *(cluster_t *)(sector + sector_offset);
assert(ent != 0);
assert(ent != 1);
@ -303,7 +334,7 @@ static int parse_dir_entry(
cur_entry->ino = dir->cluster_low |
((uint32_t)dir->cluster_high << 16);
cur_entry->reclen = dir->file_size;
cur_entry->file_size = dir->file_size;
// prepare for next entry
@ -322,7 +353,7 @@ dirent_t* fat32_read_dir(fs_t* fs, uint64_t cluster, size_t* n) {
fat32_privates_t* restrict pr = (void*)(fs+1);
unsigned bufsize = block_size(part) * pr->clusters_size;
unsigned bufsize = block_size(part) * pr->cluster_size;
uint8_t* buf = malloc(bufsize);
int end = 0;
@ -344,7 +375,7 @@ dirent_t* fat32_read_dir(fs_t* fs, uint64_t cluster, size_t* n) {
entries = realloc(entries,
sizeof(dirent_t) * (j + entries_per_cluster));
read(part, cluster_begin(cluster, pr), buf, pr->clusters_size);
read(part, cluster_begin(cluster, pr), buf, pr->cluster_size);
for(unsigned i = 0; i < entries_per_cluster; i++) {
@ -388,7 +419,7 @@ fs_t* fat32_mount(disk_part_t* part) {
// boot record
read(part, 0, buf, 1);
uint16_t total_clusters = *(uint16_t*)(buf + 0x13);
uint32_t total_clusters = *(uint16_t*)(buf + 0x13);
uint16_t fat32_sig = *(uint8_t *)(buf + 0x42);
uint16_t bootable_sig = *(uint16_t*)(buf + 510);
@ -422,7 +453,7 @@ fs_t* fat32_mount(disk_part_t* part) {
uint16_t fatsize = *(uint32_t*)(buf + 0x24);
// cluster id of root dir
uint32_t root_dir_cluster = *(uint32_t*)(buf + 0x2c);
cluster_t root_dir_cluster = *(uint32_t*)(buf + 0x2c);
fs_t* fs = malloc(sizeof(fs_t) + sizeof(fat32_privates_t));
@ -435,30 +466,26 @@ fs_t* fat32_mount(disk_part_t* part) {
fs->read_only = 0;
fs->seekable = 1;
fs->file_cursor_size = sizeof(fat32_file_cursor_t);
fs->root_addr = root_dir_cluster;
/**
* cast to void* because of polymophism:
* functions take fat32_file_cursor_t instead
* functions take file_t instead
* of void*
*/
fs->open_file = (void*)fat32_open_file;
fs->close_file = (void*)fat32_close_file;
fs->read_file_sector = (void*)fat32_read_file_sector;
fs->write_file_sector = (void*)fat32_write_file_sector;
fs->advance_file_cursor = (void*)fat32_advance_file_cursor;
fs->seek = (void*)fat32_seek;
fs->read_dir = fat32_read_dir;
fs->free_dirents = fat32_free_dirents;
fs->unmount = fat32_unmount;
//fs->open_file = (void*)fat32_open_file;
//fs->close_file = (void*)fat32_close_file;
fs->read_file_sectors = fat32_read_file_sectors;
fs->write_file_sectors = fat32_write_file_sectors;
fs->read_dir = fat32_read_dir;
fs->free_dirents = fat32_free_dirents;
fs->unmount = fat32_unmount;
fat32_privates_t* pr = (void*)fs + sizeof(fs_t);
pr->tables_count = tables_count;
pr->clusters_size = cluster_size;
pr->cluster_size = cluster_size;
pr->fat_begin = reserved;
pr->data_begin = reserved + tables_count * fatsize;
@ -474,168 +501,174 @@ fs_t* fat32_mount(disk_part_t* part) {
void fat32_unmount(fs_t* fs) {
fat32_privates_t* pr = (fat32_privates_t*)(fs+1);
assert(fs->n_open_files == 0);
cleanup_cache(&pr->fat_cache);
free(fs);
}
// open files checking should be done by the vfs
}
void fat32_open_file(file_t* restrict file, fat32_file_cursor_t* cur) {
/*
void fat32_open_file(file_t* restrict file, file_t* file) {
assert(file->fs);
assert(file->fs->type == FS_TYPE_FAT);
cur->cur_cluster_number = 0;
cur->cur_cluster = file->addr;
cur->cur_cluster_offset = 0;
cur->file_size = file->file_size;
cur->file_offset = 0;
cur->fs = file->fs;
file->cur_cluster_number = 0;
file->cur_cluster = file->addr;
file->cur_cluster_offset = 0;
file->file_size = file->file_size;
file->file_offset = 0;
file->fs = file->fs;
file->fs->n_open_files++;
cur->eof = file->file_size == 0;
file->eof = file->file_size == 0;
}
void fat32_close_file(fat32_file_cursor_t* cur) {
cur->fs->n_open_files--;
void fat32_close_file(file_t* file) {
file->fs->n_open_files--;
}
*/
/*
void fat32_advance_file_cursor(
fs_t* restrict fs,
fat32_file_cursor_t* restrict cur
file_t* restrict file
) {
fat32_privates_t* pr = (fat32_privates_t*)(fs+1);
disk_part_t* part = fs->part;
cur->file_offset += block_size(part);
file->file_offset += block_size(part);
// cursor reached the end
if(cur->file_offset > cur->file_size) {
if(file->file_offset > file->file_size) {
// this should be handled by the vfs
assert(0);
cur->eof = 1;
file->eof = 1;
return;
}
// advance cluster offset
cur->cur_cluster_offset++;
file->cur_cluster_offset++;
if(cur->cur_cluster_offset == pr->clusters_size) {
if(file->cur_cluster_offset == pr->cluster_size) {
// next sector!
cur->cur_cluster_offset = 0;
file->cur_cluster_offset = 0;
// unused
int last;
// advance cluster
cur->cur_cluster = readFAT(part, pr, cur->cur_cluster, &last);
cur->cur_cluster_number++;
file->cur_cluster = readFAT(part, pr, file->cur_cluster, &last);
file->cur_cluster_number++;
}
}
*/
int fat32_read_file_sector(
fs_t* restrict fs,
fat32_file_cursor_t* restrict cur,
void* restrict buf
/**
* @brief fetch file cluster disk offset
* with given file offset
*
*
* @param fs fs structure
* @param pr fs private (=fs+sizeof(fs_t))
* @param fd file descriptor
* @param off cluster offset in the file
* @return cluster_t cluster disk offset
* or 0 if the cluster offset is beyond
* the cluster chain end
*/
static
cluster_t fetch_cluster(
fs_t* restrict fs,
fat32_privates_t* restrict pr,
file_t* fd,
size_t off
) {
assert(fs->type == FS_TYPE_FAT);
cluster_t cluster = fd->addr;
fat32_privates_t* pr = (fat32_privates_t*)(fs+1);
assert(!cur->eof);
//if(cur->eof)
// return 0;
int last = 0;
// find first cluster to read
for(unsigned i = 0; i < off; i++) {
if(last)
return 0;
uint64_t lba = cluster_begin(cur->cur_cluster, pr)
+ cur->cur_cluster_offset;
cluster = readFAT(fs->part, pr, cluster, &last);
}
return cluster;
}
read(fs->part, lba, buf, 1);
// calculate the number of read bytes
int read_size = 1;
/*
if(cur->file_offset + block_size(fs->part)
> cur->file_size)
read_size = cur->file_size - cur->file_offset;
else
read_size = block_size(fs->part);
*/
int fat32_read_file_sectors(
fs_t* restrict fs,
file_t* restrict fd,
void* restrict buf,
uint64_t begin,
size_t n
) {
return read_size;
}
assert(fs->type == FS_TYPE_FAT);
int fat32_write_file_sector(
fs_t* restrict fs,
fat32_file_cursor_t* restrict cur,
const void* restrict buf,
int size) {
(void) fs;
(void) cur;
(void) buf;
(void) size;
panic("fat32_write_file_sector: unimplemented method");
__builtin_unreachable();
}
fat32_privates_t* restrict pr = (fat32_privates_t*)(fs+1);
int fat32_seek(
fs_t* restrict fs,
fat32_file_cursor_t* restrict cur,
uint64_t sector_offset,
int whence) {
fat32_privates_t* restrict pr =
(fat32_privates_t*)(fs+1);
disk_part_t* restrict part = fs->part;
assert((begin + n - 1) * pr->cluster_size < fd->file_size);
// compute the first cluster and offset
uint32_t cluster_offset = begin % pr->cluster_size;
uint32_t cluster = fetch_cluster(fs,pr,fd,begin / pr->cluster_size);
// if this fails, the fs is f***ed up
assert(cluster);
unsigned bsize = block_size(fs->part);
uint64_t absolute_so = sector_offset;
switch(whence) {
case SEEK_CUR:
absolute_so += cur->file_offset;
break;
case SEEK_END:
absolute_so += cur->file_size;
break;
case SEEK_SET:
break;
default:
// bad whence parameter value
return -1;
}
// cluster number in the file
// not the cluster id in the drive
unsigned new_cluster_number = absolute_so / pr->clusters_size;
while(n > 0) {
uint64_t lba = cluster_begin(cluster, pr)
+ cluster_offset;
// we can advance the cursor forward, but not backward.
if(new_cluster_number < cur->cur_cluster_number) {
// we can use the same algorithm
// with cur_cluster_number = 0
cur->cur_cluster_number = 0;
}
unsigned read_size = 1;
unsigned cluster_remaining =
pr->cluster_size - cluster_offset;
int last;
while(cur->cur_cluster != cur->cur_cluster_number) {
cur->cur_cluster = readFAT(part, pr, cur->cur_cluster, &last);
cur->cur_cluster_number++;
if(last) {
// we could allocate a cluster in the FAT
panic("fat32_seek: tried to seek too far");
if(n >= cluster_remaining) {
read_size = cluster_remaining;
cluster_offset = 0;
cluster++;
}
// else, this is the last iteration
read(fs->part, lba, buf, read_size);
buf += bsize * read_size;
n -= read_size;
}
return n;
}
return 0;
int fat32_write_file_sectors(
fs_t* restrict fs,
file_t* restrict file,
const void* restrict buf,
uint64_t begin,
size_t n) {
(void) fs;
(void) file;
(void) buf;
(void) n;
panic("fat32_write_file_sectors: unimplemented method");
__builtin_unreachable();
}

133
kernel/fs/fat32/fat32.h

@ -2,137 +2,6 @@
#include "../fs.h"
fs_t* fat32_mount(disk_part_t* part);
typedef struct fat32_file_cursor {
// file's filesystem
// must be a fat32 one
fs_t* fs;
// number of the cluster in the file:
// cursor position
uint32_t cur_cluster_number;
// physical cluster id
uint32_t cur_cluster;
// current sector in the
// current cluster
unsigned cur_cluster_offset;
// byte offset in the file
unsigned file_offset;
// file size in bytes
unsigned file_size;
// non zero if the cursor reached the
// end of the file
int eof;
} fat32_file_cursor_t;
/**
* @brief create a cursor over a file
*
* @param file the file to open
* @param cur (output) the cursor to that file
*/
void fat32_open_file(file_t* restrict file, fat32_file_cursor_t* cur);
void fat32_close_file(fat32_file_cursor_t *);
/**
* @brief advance by one sector the file
* cursor structure
*
* @param fs the filesystem structure
* @param cur the curstor structure to advance
*/
void fat32_advance_file_cursor(fs_t* fs, fat32_file_cursor_t* cur);
/**
* @brief read one sector from file
* the whole buffer will be overwritten even if
* only one byte is read. See fat32_advance_file_cursor
* for advancing file cursor between reads / writes
*
* @param fs fat32 partition structure
* @param cur the file cursor structure
* @param buf buffer that is big enough to hold one sector
* @return int the number of read bytes.
*/
int fat32_read_file_sector(
fs_t* restrict fs,
fat32_file_cursor_t* restrict cur,
void* restrict buf);
/**
* @brief read one sector from file
* the whole buffer will be overwritten even if
* only one byte is read. See fat32_advance_file_cursor
* for advancing file cursor between reads / writes
*
* @param fs fat32 partition structure
* @param cur the file cursor structure
* @param buf buffer to write from
* @param size must be 0 if this is't the last
* sector in the file. Else, it will be the size
* offset of the last sector.
* @return int the number of read bytes.
*/
int fat32_write_file_sector(
fs_t* restrict fs,
fat32_file_cursor_t* restrict cur,
const void* restrict buf,
int size);
// UNIX fseek like
// but offset is in fs blocks instead of
// bytes
int fat32_seek(
fs_t* restrict fs,
fat32_file_cursor_t* restrict cur,
uint64_t cluster_offset,
int whence
);
/**
* @brief read a directory and return its
* children in a cache structure array
*
* @param fs filesystem structure
* @param dir directory to read
* @param n_entries (output) the number of
* entries found in the directory
* @return dirent_t* allocated,
* must free later on with fat32_free_dirents
*/
dirent_t* fat32_read_dir(fs_t* fs, uint64_t dir_addr, size_t* n);
/**
* @brief free the output list from fat32_read_dir
*
*/
void fat32_free_dirents(dirent_t*);
/**
* @brief destruct the fs structure,
* cleanup every allocated memory
* before being called, the
* n_open_files must be cleared.
*
*/
void fat32_unmount(fs_t* fs);
fs_t* fat32_mount(disk_part_t* part);

101
kernel/fs/fs.h

@ -63,11 +63,11 @@ typedef uint64_t ino_t;
* structure used by vfs_read_dir
*/
typedef struct dirent {
ino_t ino; /* Inode number */
unsigned short reclen; /* Length of this record */
unsigned char type; /* Type of file; not supported
ino_t ino; /* Inode number */
size_t file_size; /* Length of this record */
unsigned char type; /* Type of file; not supported
by all filesystem types */
char name[MAX_FILENAME_LEN+1]; /* Null-terminated filename */
char name[MAX_FILENAME_LEN+1]; /* Null-terminated filename */
} dirent_t;
@ -78,9 +78,9 @@ typedef struct dirent {
*
*/
typedef struct fast_dirent {
ino_t ino; /* Inode number */
unsigned short reclen; /* Length of this record */
unsigned char type; /* Type of file; not supported
ino_t ino; /* Inode number */
size_t file_size; /* Length of this record */
unsigned char type; /* Type of file; not supported
by all filesystem types */
} fast_dirent_t;
@ -102,22 +102,25 @@ typedef struct fs {
// buffers to grant 1-granularity accesses
unsigned file_access_granularity;
// fs level file properties
union {
flags;
uint8_t flags;
struct {
unsigned read_only: 1;
unsigned cacheable: 1;
unsigned seekable: 1;
};
};
/**
* @brief size of the cursor handler
* @brief size of the file descriptor
* data structure used by open_file,
* close_file, read_file_sector,
* write_file_sector, seek
*
*/
unsigned file_cursor_size;
//unsigned fd_size;
/**
@ -141,69 +144,63 @@ typedef struct fs {
* @brief create a cursor over a file
*
* @param file the file to open
* @param cur (output) the cursor specific handler
* must be at least file_cursor_size big
* @param cur (output) the file descriptor
* must be at least fd_size big
*/
void (*open_file)(file_t* restrict file, void* cur);
//void (*open_file)(file_t* restrict file, void* cur);
/**
* @brief close a cursor
*
* @param cur file specific cursor handler
* must be at least file_cursor_size big
* @param cur file specific file descriptor
* must be at least fd_size big
*/
void (*close_file)(void *);
//void (*close_file)(void *);
/**
* @brief advance by one sector the file
* cursor structure by one grain
*
* @param fs the filesystem structure
* @param cur the curstor structure to advance
*/
void (*advance_file_cursor)(struct fs* fs, void* cur);
/**
* @brief read one sector from file
* the whole buffer will be overwritten even if
* only one byte is read. Advance the cursor
* @brief read sectors at position begin -> begin + n - 1
* from file
*
* no size checking is done: unfinded behavior if
* (begin + n - 1) * file_access_granularity > fd->file_size
*
* All the checking should be done by the VFS
*
* @param fs partition structure
* @param cur file cursor specific handler
* must be at least file_cursor_size big
* @param fd file descriptor
* must be at least fd_size big
* @param buf buffer that is big enough to hold one sector
* @param begin the position (in blocks) of the
* first sector to read
* @return int the number of read bytes.
*/
int (*read_file_sector)(struct fs* restrict fs,void* cur,void* restrict buf);
int (*read_file_sectors)(struct fs* restrict fs,file_t* fd,
void* restrict buf, uint64_t begin, size_t n);
/**
* @brief read one sector from file
* the whole buffer will be overwritten even if
* only one byte is read. Advance the cursor
* @brief write sectors at position begin -> begin + n - 1
* from file
*
* no size checking is done: unfinded behavior if
* (begin + n - 1) * file_access_granularity > fd->file_size
*
* All the checking should be done by the VFS
*
* @param fs partition structure
* @param cur file cursor specific handler
* must be at least file_cursor_size big
* @param fd file descriptor
* must be at least fd_size big
* @param buf buffer to write from
* @param size must be 0 if this is't the last
* sector in the file. Else, it will be the size
* offset of the last sector.
* @param begin the position (in blocks) of the
* first sector to write
* @param n amount of sectors to be written
* @return int the number of read bytes.
*/
int (*write_file_sector)(struct fs* restrict fs,void* cur,
const void* restrict buf,int size);
// UNIX fseek like
// but offset is in fs blocks instead of
// bytes
int (*seek)(struct fs* restrict fs,void* restrict cur,
uint64_t block_offset,int whence);
int (*write_file_sectors)(struct fs* restrict fs, file_t* fd,
const void* restrict buf, uint64_t begin, size_t n);
/**
@ -240,13 +237,19 @@ typedef struct fs {
* before being called, the
* n_open_files must be cleared.
*
* The checking n_open_files checking
* should be done by the VFS though
*
*/
void (*unmount)(struct fs* fs);
} fs_t;
// fs::type field values
#define FS_TYPE_FAT 1
// seek whence field values
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2

31
kernel/fs/vfs.h

@ -17,6 +17,24 @@
int vfs_mount(disk_part_t* part, const char* path);
/**
* @brief open a directory entry
*
* this function shouldn't be used outside the
* vfs routines: prefer vfs_open_file / vfs_opendir
*
*
* @param path the directory path
* @param dir (output) directory entry descriptor
* @return fs_t NULL if the directory does
* not exist. the file system associated with the dir
* otherwise
*/
fs_t *vfs_open(const char *path, fast_dirent_t *dir);
/**
* @brief unmount a partition
* from the vfs
@ -49,6 +67,8 @@ void vfs_cleanup(void);
* what we are doing with this structure
*/
typedef struct file_handler {
// file associated fs
fs_t* fs;
// if fs->cachable = 0: this field is
@ -65,6 +85,11 @@ typedef struct file_handler {
// buffer is empty
unsigned sector_offset;
// cursor's sector number
// should be equal to:
// (file_offset % fs->file_access_granularity)
size_t sector_count;
// current byte offset in the file
uint64_t file_offset;
@ -79,11 +104,11 @@ typedef struct file_handler {
// this fild's size will depend
// be fs->file_cursor_size
// be fs->fd_size
//
// uint64_t: make sure the structure
// will be aligned
uint64_t cursor[0];
file_t* file;
} file_handle_t;
@ -139,7 +164,7 @@ struct DIR* vfs_opendir(const char* path);
/**
* @brief close a dir opened
* by vfs_open_dir(...)
* by vfs_opendir(...)
*
*/
void vfs_closedir(struct DIR*);

514
kernel/fs/vfs.c → kernel/fs/vfs_dirs.c

@ -350,7 +350,7 @@ static dirent_t *read_dir(fs_t *fs, ino_t ino, const char *dir_path, size_t *n)
free_cache_entry(cache_ent);
cache_ent->cluster = dent->ino;
cache_ent->file_size = dent->reclen;
cache_ent->file_size = dent->file_size;
cache_ent->type = dent->type;
cache_ent->fs = fs;
cache_ent->path = path;
@ -581,7 +581,7 @@ static int find_fs_child(
{
child->ino = ents[i].ino;
child->reclen = ents[i].reclen;
child->file_size = ents[i].file_size;
child->type = ents[i].type;
found = 1;
@ -614,32 +614,26 @@ static dir_cache_ent_t *get_cache_entry(const char *path)
return NULL;
}
/**
* @brief open a directory entry
*
* @param path the directory path in cannonical
* form
* @param dir (output) directory entry descriptor
* @return fs_t NULL if the directory does
* not exist. the file system associated with the dir
* otherwise
*/
static fs_t *open_dir(const char *path, fast_dirent_t *dir)
fs_t *vfs_open(const char *path, fast_dirent_t *dir)
{
assert(is_absolute(path));
char* pathbuf = malloc(strlen(path)+1);
simplify_path(pathbuf, path);
assert(is_absolute(pathbuf));
// check cache
dir_cache_ent_t *ent = get_cache_entry(path);
dir_cache_ent_t *ent = get_cache_entry(pathbuf);
if (ent)
{ // present entry
dir->ino = ent->cluster;
dir->reclen = ent->file_size;
dir->file_size = ent->file_size;
dir->type = ent->type;
return ent->fs;
}
vdir_t *vdir = get_fs_vdir(path);
vdir_t *vdir = get_fs_vdir(pathbuf);
if (!vdir)
{
@ -650,17 +644,18 @@ static fs_t *open_dir(const char *path, fast_dirent_t *dir)
fs_t *fs = vdir->fs;
char *buf = malloc(strlen(path) + 1);
strcpy(buf, path + strlen(vdir->path));
// only keep the FS part
// of the path
char *buf = pathbuf + strlen(vdir->path);
char *sub = strtok(buf, "/");
fast_dirent_t cur = {
.ino = fs->root_addr,
.reclen = 0,
.file_size = 0,
.type = DT_DIR,
};
@ -671,22 +666,22 @@ static fs_t *open_dir(const char *path, fast_dirent_t *dir)
if (!find_fs_child(fs, &cur, &child, sub))
{
// the child does not exist
free(buf);
free(pathbuf);
return NULL;
}
cur.ino = child.ino;
cur.reclen = child.reclen;
cur.file_size = child.file_size;
cur.type = child.type;
sub = strtok(NULL, "/");
}
free(buf);
free(pathbuf);
dir->ino = cur.ino;
dir->reclen = cur.reclen;
dir->file_size = cur.file_size;
dir->type = cur.type;
return fs;
@ -706,11 +701,11 @@ static void log_tree(const char *path, int level)
for (int i = 0; i < level + 1; i++)
puts("-");
printf(" %s (%u)\n", dirent->name, dirent->type);
printf(" %d - %s (size %u) \n", dirent->type, dirent->name, dirent->file_size);
if (dirent->type == DT_DIR)
{
char *pathb = malloc(1024);
char *pathb = malloc(strlen(path) + strlen(dirent->name) + 2);
strcpy(pathb, path);
strcat(pathb, "/");
@ -725,6 +720,68 @@ static void log_tree(const char *path, int level)
vfs_closedir(dir);
}
static inline
void test_file(void) {
file_handle_t* f = vfs_open_file("/fs/file.dat");
assert(f);
//assert(!vfs_seek_file(f, 512, SEEK_SET));
//log_warn("FILE SIZE = %u", vfs_tell_file(f));
#define SIZE 512
vfs_seek_file(f, 0, SEEK_END);
size_t fsz = vfs_tell_file(f);
log_warn("vfs_tell_file(f) = %lu", vfs_tell_file(f));
vfs_seek_file(f, 0, SEEK_SET);
char* buf = malloc(SIZE+1);
int read = 0;
int x = 531;
for(int i = 0; i < 200; i++)
{
size_t seek = 0;
int rd = SIZE;
if(fsz > i * SIZE)
seek = fsz - i * SIZE;
else
rd = i * SIZE - fsz;
x = (x * 411 + 1431) % (1024 * 1024 / 4 - SIZE);
size_t y = x*4;
log_info("test %u", y);
vfs_seek_file(f, y, SEEK_SET);
read = vfs_read_file(buf, 1, SIZE, f);
assert(read);
buf[read] = 0;
uint32_t* chunk = buf;
for(unsigned j = 0; j < SIZE / 4; j++) {
if(chunk[j] != j + x) {
log_warn("chunk[j]=%x, j + x=%x", chunk[j], j+x);
panic("no :(");
}
assert(chunk[j] == j + x);
}
//dump(buf, SIZE, 8, DUMP_DEC32);
}
vfs_close_file(f);
}
int vfs_mount(disk_part_t *part, const char *path)
{
// register the new virtual directory
@ -763,29 +820,13 @@ int vfs_mount(disk_part_t *part, const char *path)
new->fs = fs;
file_handle_t* f = vfs_open_file("/fs/boot/limine.cfg");
assert(f);
assert(!vfs_seek_file(f, 512, SEEK_SET));
log_warn("FILE SIZE = %u", vfs_tell_file(f));
//vfs_seek_file(f, 0, SEEK_SET);
char buf[26];
int read = 0;
int i = 0;
while ((read = vfs_read_file(buf, 1, 25, f)))
{
buf[read] = 0;
puts(buf);
i++;
// log_debug("%u read %u", i++, read);
}
vfs_close_file(f);
log_debug("fg");
return 1;
}
int vfs_unmount(const char *path)
{
// do stuf!
@ -800,60 +841,7 @@ int vfs_unmount(const char *path)
return unmount(vdir);
}
file_handle_t *vfs_open_file(const char *path)
{
char *pathbuf = malloc(strlen(path) + 1);
simplify_path(pathbuf, path);
fast_dirent_t dirent;
fs_t *restrict fs = open_dir(pathbuf, &dirent);
free(pathbuf);
if (!fs) // dirent not found
return NULL;
// file does not exist or isn't a file
if (dirent.type != DT_REG)
return NULL;
// big allocation to allocate the
// sector buffer too
file_handle_t *handler = malloc(
sizeof(file_handle_t) + fs->file_cursor_size
// size of one sector
+ fs->file_access_granularity);
log_warn("file_access_granularity = %u", fs->file_access_granularity);
handler->file_size = dirent.reclen;
handler->fs = fs;
handler->file_offset = 0;
// empty buffer
handler->sector_offset = 0;
handler->buffer_valid = 0;
handler->sector_buff = (void *)handler +
sizeof(file_handle_t) + fs->file_cursor_size;
file_t file = {
.addr = dirent.ino,
.fs = fs,
.file_size = dirent.reclen,
};
fs->open_file(&file, handler->cursor);
return handler;
}
void vfs_close_file(file_handle_t *handle)
{
fs_t *fs = handle->fs;
fs->close_file(handle);
free(handle);
}
struct DIR *vfs_opendir(const char *path)
{
@ -869,7 +857,7 @@ struct DIR *vfs_opendir(const char *path)
size_t n = 0;
fast_dirent_t fdir;
fs_t *fs = open_dir(pathbuff, &fdir);
fs_t *fs = vfs_open(pathbuff, &fdir);
struct DIR *dir = NULL;
@ -903,7 +891,7 @@ struct DIR *vfs_opendir(const char *path)
{
list[i] = (dirent_t){
.ino = 0,
.reclen = 0,
.file_size = 0,
.type = DT_DIR,
};
@ -943,327 +931,3 @@ struct dirent *vfs_readdir(struct DIR *dir)
else
return &dir->children[dir->cur++];
}
size_t vfs_read_file(void *ptr, size_t size, size_t nmemb,
file_handle_t *restrict stream)
{
assert(stream);
assert(ptr);
void *const buf = stream->sector_buff;
fs_t *fs = stream->fs;
uint64_t file_size = stream->file_size;
unsigned granularity = fs->file_access_granularity;
unsigned cachable = fs->cacheable;
unsigned max_read = file_size - stream->file_offset;
unsigned bsize = size * nmemb;
// check nmemb bounds
if (max_read < bsize)
{
nmemb = max_read / size;
bsize = size * nmemb;
}
if(bsize == 0)
return 0;
// we are to read some bytes.
// if the fs is cachable, and the
// granularity cache is invalid,
// we have to fill it beforehand
if(cachable && !stream->buffer_valid) {
fs->read_file_sector(
stream->fs,
stream->cursor,
buf
);
stream->buffer_valid = 1;
}
while (bsize)
{
unsigned remaining = file_size - stream->file_offset;
unsigned n_ready = granularity - stream->sector_offset;
if(n_ready > remaining)
n_ready = remaining;
if(!cachable) {
// for sure, bsize != 0.
// we have to fetch again
// the sector
// without advancing the cursor
fs->read_file_sector(
stream->fs,
stream->cursor,
buf);
}
if (n_ready >= bsize) {
memcpy(
ptr,
buf + stream->sector_offset,
bsize
);
ptr += bsize;
stream->sector_offset += bsize;
stream->file_offset += bsize;
bsize = 0;
// here,
// n_ready = granularity - sector_offset >= bsize
// <=> sector_offset + bsize <= g
// when n_ready = bsize,
// we set stream->sector_offset to granularity.
//
// for consistancy with writes,
// sector_offset has to be strictly inferior to
// granularity, when at least one read has been
// done.
// We therefore advance the cursor
if (!cachable && stream->sector_offset == granularity) {
stream->sector_offset = 0;
fs->advance_file_cursor(fs, stream->cursor);
}
}
else {
if (n_ready) {
memcpy(
ptr,
buf + stream->sector_offset,
n_ready
);
bsize -= n_ready;
ptr += n_ready;
stream->file_offset += n_ready;
}
if (n_ready) // the cache was valid
fs->advance_file_cursor(fs, stream->cursor);
if (cachable) {
// iif the fs is cachable,
// we can fetch the next file sector
// for the next iteration
// for coherency with writes,
// the file cursor must always point
// on the current read sector: we therefore
// cannot advance the file cursor afterwards
// like read(cur++)
//
// Instead, we have to increment beforehand.
// we cannot advance the cursor like read(++cur)
// as the first sector would be forgotten
fs->read_file_sector(
stream->fs,
stream->cursor,
buf
);
}
stream->sector_offset = 0;
}
}
return nmemb;
}
size_t vfs_write_file(const void *ptr, size_t size, size_t nmemb,
file_handle_t *stream)
{
assert(stream);
assert(ptr);
assert(0);
/*
void * const buf = stream->sector_buff;
const fs_t *fs = stream->fs;
uint64_t file_size = stream->file_size;
unsigned granularity = fs->file_access_granularity;
unsigned cachable = fs->cacheable;
// no checking: we hope that the drive won't
// ever be overflowed
size_t bsize = size * nmemb;
while(bsize) {
// don't need no buffering
if((stream->sector_offset == 0
|| stream->sector_offset == granularity)
&& bsize >= granularity) {
int wr = fs->write_file_sector(
fs,
stream->cursor,
ptr,
granularity
);
assert(wr == (int)granularity);
ptr += granularity;
stream->file_offset += granularity;
bsize -= granularity;
}
else {
unsigned offset = stream->sector_offset;
unsigned write_size = MIN(bsize, granularity - offset);
memcpy(buf + offset, ptr, write_size);
if(stream->file_offset == stream->file_size
&& offset == 0) {
// no need to read before writing
}
else if(!fs->cacheable || offset == granularity) {
// need to
}
int wr = fs->write_file_sector(
fs,
stream->cursor,
ptr,
granularity
);
assert(wr == write_size);
ptr += bsize;
stream->file_offset += bsize;
bsize = 0;
}
stream->file_size = MAX(stream->file_offset,
stream->file_size);
unsigned remaining = file_size - stream->file_offset;
unsigned n_ready = granularity - stream->sector_offset;
if(n_ready > remaining)
n_ready = remaining;
if(!cachable)
n_ready = 0;
if(n_ready >= bsize)
{
memcpy(
ptr,
buf + stream->sector_offset,
bsize
);
ptr += bsize;
stream->sector_offset += bsize;
stream->file_offset += bsize;
bsize = 0;
}
else
{
if(n_ready) {
memcpy(
ptr,
buf + stream->sector_offset,
n_ready
);
bsize -= n_ready;
ptr += n_ready;
stream->file_offset += n_ready;
}
stream->sector_offset = 0;
fs->read_file_sector(
stream->fs,
stream->cursor,
buf);
}
}
*/
return nmemb;
}
int vfs_seek_file(file_handle_t *restrict stream, uint64_t offset, int whence)
{
// real file offset: to be
// calculated with the whence field
uint64_t absolute_offset = offset;
switch (whence)
{
case SEEK_CUR:
absolute_offset += stream->file_offset;
break;
case SEEK_END:
absolute_offset += stream->file_size;
break;
case SEEK_SET:
break;
default:
// bad whence parameter value
return -1;
}
size_t block_size = stream->fs->file_access_granularity;
uint64_t new_sector_id = absolute_offset / block_size;
uint64_t old_sector_id = stream->file_offset / block_size;
if (new_sector_id != old_sector_id)
{
log_warn("CHANGED SECTOR %u --> %u", old_sector_id, new_sector_id);
// the seeked value is in another block.
// We therefore need to locate the new one
fs_t *fs = stream->fs;
fs->seek(fs, stream->cursor, new_sector_id, SEEK_SET);
stream->buffer_valid = 0;
// need to flush the cache content
if(stream->fs->cacheable)
fs->read_file_sector(fs, stream->cursor, stream->sector_buff);
}
// else, only the sector offset changed.
// no need to access the fs
stream->sector_offset = absolute_offset % block_size;
stream->file_offset = absolute_offset;
log_warn("NEW OFFSET %u", stream->sector_offset);
// everything is fine: return 0
return 0;
}
long vfs_tell_file(file_handle_t *restrict stream)
{
return stream->file_offset;
}

336
kernel/fs/vfs_files.c

@ -0,0 +1,336 @@
#include "vfs.h"
#include "../lib/string.h"
#include "../lib/assert.h"
#include "../lib/logging.h"
#include "../lib/dump.h"
#include "../lib/math.h"
#include "../lib/panic.h"
#include "../acpi/power.h"
#include "../memory/heap.h"
#include "fat32/fat32.h"
/**
* for now, we use a sequencial
* search in the file table to
* invalid cahces of multiple used
*
*/
struct file_ent {
file_t file;
// number of distincs file handlers
// associated with this file
unsigned n_insts;
//
file_handle_t** fhs;
};
static unsigned n_open_files = 0;
static struct file_ent* open_files = NULL;
void vfs_files_init(void) {
}
void vfs_files_cleanup(void) {
// @TODO
for(unsigned i = 0; i < n_open_files; i++) {
if(open_files[i].fhs)
free(open_files[i].fhs);
}
if(open_files)
free(open_files);
}
static
struct file_ent* search_or_insert_file(fs_t* fs, uint64_t addr, uint64_t file_len) {
for(unsigned i = 0; i < n_open_files; i++) {
struct file_ent* e = &open_files[i];
if(e->file.addr == addr