diff options
Diffstat (limited to 'gs/base/ramfs.c')
-rw-r--r-- | gs/base/ramfs.c | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/gs/base/ramfs.c b/gs/base/ramfs.c new file mode 100644 index 000000000..644eaf192 --- /dev/null +++ b/gs/base/ramfs.c @@ -0,0 +1,453 @@ +/* + memory-based simulated file system + + files only, no directories (well, one) + (C) 2006 Michael Slade <micksa@knobbits.org> + */ + +#include "unistd_.h" +#include "string_.h" +#include "malloc_.h" +#include "gx.h" +#include "gserrors.h" +#include "gp.h" +#include "gscdefs.h" +#include "gsparam.h" +#include "gsstruct.h" +#include "ramfs.h" + +#define MACROBLOCK_REALLOC_MAX 128 + +typedef struct _ramfs { + struct _ramdirent * files; + struct _ramfs_enum* active_enums; + int blocksfree; + int last_error; +}; + +gs_private_st_ptrs2(st_ramfs, struct _ramfs, "gsram_ramfs", + _ramfs_enum_ptrs, _ramfs_reloc_ptrs, files, active_enums); + +struct _ramdirent { + char* filename; + struct _ramfile* inode; + struct _ramdirent* next; +}; + +gs_private_st_ptrs3(st_ramdirent, struct _ramdirent, "gsram_ramdirent", + _ramdirent_enum_ptrs, _ramdirent_reloc_ptrs, filename, inode, next); + +typedef struct _ramfile { + ramfs* fs; + int refcount; + int size; + int blocks; + int blocklist_size; + char** data; +} ramfile; + +gs_private_st_ptrs2(st_ramfile, struct _ramfile, "gsram_ramfile", + _ramfile_enum_ptrs, _ramfile_reloc_ptrs, fs, data); + +struct _ramhandle { + ramfile * file; + int last_error; + int filepos; + int mode; +}; + +gs_private_st_ptrs1(st_ramhandle, struct _ramhandle, "gsram_ramhandle", + _ramhandle_enum_ptrs, _ramhandle_reloc_ptrs, file); + +struct _ramfs_enum { + ramfs* fs; + ramdirent * current; + struct _ramfs_enum* next; +}; + +gs_private_st_ptrs3(st_ramfs_enum, struct _ramfs_enum, "gsram_ramfs_enum", + _ramfs_enum_enum_ptrs, _ramfs_enum_reloc_ptrs, fs, current, next); + +static void unlink_node(ramfile * inode); +static int ramfile_truncate(ramhandle * handle,int size); + + +ramfs * ramfs_new(gs_memory_t *mem, int size) { + ramfs * fs = gs_alloc_struct(mem, ramfs, &st_ramfs, + "ramfs_new" + ); + + if (fs == NULL) { + fs->last_error = RAMFS_NOMEM; + return NULL; + } + size = size/(RAMFS_BLOCKSIZE/1024); + fs->files = NULL; + fs->active_enums = NULL; + fs->blocksfree = size; + fs->last_error = 0; + return fs; +} + +/* This function makes no attempt to check that there are no open files or + enums. If there are any when this function is called, memory leakage will + result and any attempt to access the open files or enums will probably + cause a segfault. Caveat emptor, or something. +*/ +void ramfs_destroy(gs_memory_t *mem, ramfs * fs) { + ramdirent * ent; + + if(fs == NULL) return; + + ent = fs->files; + while(ent) { + ramdirent* prev; + free(ent->filename); + unlink_node(ent->inode); + prev = ent; + ent = ent->next; + free(prev); + } + gs_free_object(mem, fs, "ramfs_destroy"); +} + +int ramfs_error(const ramfs* fs) { return fs->last_error; } + +static int resize(ramfile * file,int size) { + int newblocks = (size+RAMFS_BLOCKSIZE-1)/RAMFS_BLOCKSIZE; + if(newblocks > file->blocks) { + /* allocate blocks for file as necessary */ + + if(newblocks-file->blocks > file->fs->blocksfree) { + return -RAMFS_NOSPACE; + } + if(file->blocklist_size < newblocks) { + int newsize = file->blocklist_size; + if (newsize > MACROBLOCK_REALLOC_MAX) { + newsize = ((newblocks+MACROBLOCK_REALLOC_MAX-1)/ + MACROBLOCK_REALLOC_MAX) * MACROBLOCK_REALLOC_MAX; + } else { + if(!newsize) newsize = 1; + while(newsize < newblocks) newsize *= 2; + } + file->data = realloc(file->data,newsize * sizeof(char*)); + file->blocklist_size = newsize; + } + while(file->blocks<newblocks) { + char * block = file->data[file->blocks] = malloc(RAMFS_BLOCKSIZE); + if(!block) { + return -RAMFS_NOMEM; + } + file->blocks++; + file->fs->blocksfree--; + } + } else if (newblocks < file->blocks) { + /* don't bother shrinking the block array */ + file->fs->blocksfree += (file->blocks-newblocks); + while(file->blocks > newblocks) { + free(file->data[--file->blocks]); + } + } + file->size = size; + return 0; +} + +static ramdirent * ramfs_findfile(const ramfs* fs,const char *filename) { + ramdirent * this = fs->files; + while(this) { + if(strcmp(this->filename,filename) == 0) break; + this = this->next; + } + return this; +} + +ramhandle * ramfs_open(gs_memory_t *mem, ramfs* fs,const char * filename,int mode) { + ramdirent * this; + ramfile* file; + ramhandle* handle; + + if(mode & (RAMFS_CREATE|RAMFS_APPEND)) mode |= RAMFS_WRITE; + + this = ramfs_findfile(fs,filename); + + if(!this) { + /* create file? */ + char * dirent_filename; + + if(!(mode & RAMFS_CREATE)) { + fs->last_error = RAMFS_NOTFOUND; + return NULL; + } + this = malloc(sizeof(ramdirent)); + file = malloc(sizeof(ramfile)); + dirent_filename = malloc(strlen(filename)+1); + if(!(this && file && dirent_filename)) { + free(this); + free(file); + free(dirent_filename); + fs->last_error = RAMFS_NOMEM; + return NULL; + } + strcpy(dirent_filename,filename); + this->filename = dirent_filename; + file->refcount = 1; + file->size = 0; + file->blocks = 0; + file->blocklist_size = 0; + file->data = NULL; + file->fs = fs; + this->inode = file; + this->next = fs->files; + fs->files = this; + } + file = this->inode; + file->refcount++; + + handle = malloc(sizeof(ramhandle)); + if(!handle) { + fs->last_error = RAMFS_NOMEM; + return NULL; + } + handle->file = file; + handle->filepos = 0; + handle->mode = mode; + + if(mode & RAMFS_TRUNC) { + resize(file,0); + } + return handle; +} + +int ramfs_blocksize(ramfs * fs) { return RAMFS_BLOCKSIZE; } +int ramfs_blocksfree(ramfs * fs) { return fs->blocksfree; } +int ramfile_error(ramhandle * handle) { return handle->last_error; } + +static void unlink_node(ramfile * inode) { + int c; + + --inode->refcount; + if(inode->refcount) return; + + /* remove the file and its data */ + for(c=0;c<inode->blocks;c++) { + free(inode->data[c]); + } + inode->fs->blocksfree += c; + free(inode->data); + free(inode); +} + +int ramfs_unlink(ramfs * fs,const char *filename) { + ramdirent ** last; + ramdirent * this; + ramfs_enum* e; + + last = &fs->files; + while(1) { + if(!(this = *last)) { + fs->last_error = RAMFS_NOTFOUND; + return -1; + } + if(strcmp(this->filename,filename) == 0) break; + last = &(this->next); + } + + unlink_node(this->inode); + free(this->filename); + (*last) = this->next; + + e = fs->active_enums; + /* advance enums that are pointing to the just-deleted file */ + while(e) { + if(e->current == this) e->current = this->next; + e = e->next; + } + free(this); + return 0; +} + +int ramfs_rename(ramfs * fs,const char* oldname,const char* newname) { + ramdirent * this; + char * newnamebuf; + + this = ramfs_findfile(fs,oldname); + + if(!this) { + fs->last_error = RAMFS_NOTFOUND; + return -1; + } + + /* just in case */ + if(strcmp(oldname,newname) == 0) return 0; + + newnamebuf = realloc(this->filename,strlen(newname)+1); + if(!newnamebuf) { + fs->last_error = RAMFS_NOMEM; + return -1; + } + + /* this may return RAMFS_NOTFOUND, which can be ignored. */ + ramfs_unlink(fs,newname); + + strcpy(newnamebuf,newname); + this->filename = newnamebuf; + return 0; +} + +ramfs_enum * ramfs_enum_new(ramfs * fs) { + ramfs_enum * e; + + e = malloc(sizeof(ramfs_enum)); + if(!e) { + fs->last_error = RAMFS_NOMEM; + return NULL; + } + e->current = fs->files; + e->next = fs->active_enums; + e->fs = fs; + fs->active_enums = e; + return e; +} + +char* ramfs_enum_next(ramfs_enum * e) { + char * filename = NULL; + if(e->current) { + filename = e->current->filename; + e->current = e->current->next; + } + return filename; +} + +void ramfs_enum_end(ramfs_enum * e) { + ramfs_enum** last = &e->fs->active_enums; + while(*last) { + if(*last == e) { + *last = e->next; + break; + } + last = &(e->next); + } + free(e); +} + +int ramfile_read(ramhandle * handle,void * buf,int len) { + ramfile * file = handle->file; + int left; + char *t = (char *)buf; + + if(len>file->size - handle->filepos) len = file->size-handle->filepos; + if(len<0) return 0; + + left = len; + while(left) { + char * p = file->data[handle->filepos/RAMFS_BLOCKSIZE]+handle->filepos%RAMFS_BLOCKSIZE; + int x = RAMFS_BLOCKSIZE-handle->filepos%RAMFS_BLOCKSIZE; + if(x>left) x = left; + + memcpy(t,p,x); + handle->filepos += x; + left -= x; + t += x; + } + buf = (void *)t; + return len; +} + +int ramfile_write(ramhandle * handle,const void * buf,int len) { + ramfile * file = handle->file; + int left; + char *t = (char *)buf; + + if(!handle->mode & RAMFS_WRITE) { + handle->last_error = RAMFS_NOACCESS; + return -1; + } + + if(handle->mode & RAMFS_APPEND) { + handle->filepos = file->size; + } + + if(file->size < handle->filepos) { + /* if this fails then pass the error on */ + if(ramfile_truncate(handle,handle->filepos) == -1) return -1; + } + + if(file->size < handle->filepos+len) { + int x = resize(file,handle->filepos+len); + if(x) { + handle->last_error = -x; + return -1; + } + } + + /* This is exactly the same as for reading, cept the copy is in the + other direction. */ + left = len; + while(left) { + char * p = file->data[handle->filepos/RAMFS_BLOCKSIZE] + + handle->filepos%RAMFS_BLOCKSIZE; + int x = RAMFS_BLOCKSIZE-handle->filepos%RAMFS_BLOCKSIZE; + if(x>left) x = left; + + memcpy(p,buf,x); + handle->filepos += x; + left -= x; + t += x; + } + buf = (void *)t; + return len; +} + +int ramfile_seek(ramhandle * handle,int pos,int whence) { + /* Just set the handle's file position. The effects become noticeable + at the next read or write. + */ + if(whence == RAMFS_SEEK_CUR) { + handle->filepos += pos; + } else if(whence == RAMFS_SEEK_END) { + handle->filepos = handle->file->size+pos; + } else { + handle->filepos = pos; + } + return 0; +} + +int ramfile_size(ramhandle * handle) { + return handle->file->size; +} + +static int ramfile_truncate(ramhandle * handle,int size) { + ramfile * file = handle->file; + int oldsize = file->size; + int x = resize(file,size); + + if(x) { + handle->last_error = -x; + return -1; + } + if(oldsize >= size) return 0; + + /* file was expanded. fill the new space with zeros. */ + while(oldsize < file->size) { + char * p = file->data[oldsize/RAMFS_BLOCKSIZE]+oldsize%RAMFS_BLOCKSIZE; + int len = RAMFS_BLOCKSIZE - oldsize%RAMFS_BLOCKSIZE; + if(len>file->size-oldsize) len = file->size-oldsize; + oldsize += len; + memset(p,0,len); + } + return 0; +} + +void ramfile_close(ramhandle * handle) { + ramfile * file = handle->file; + unlink_node(file); + free(handle); +} + +int ramfile_tell(ramhandle* handle) { + return handle->filepos; +} + +int ramfile_eof(ramhandle* handle) { + return (handle->filepos >= handle->file->size); +} |