/*****************************************************************************/
/* RAMDISK.C - Example user-defined device. */
/*****************************************************************************/
#include <stdlib.h>
#include <string.h>
#include <file.h>
/*****************************************************************************/
/* This file presents a sample implementation of a user-defined device. */
/* The user must define the seven basic functions (open, close, read, */
/* write, lseek, unlink, rename). These are expected to behave just like */
/* the Unix functions of these names, but this is not strictly necessary to */
/* work properly. Also consider something like a TTY device might not be */
/* lseek-able, but can still be opened and read from. A device is added */
/* using the add_device() RTS function call (q.v.). */
/*****************************************************************************/
/* The low-level interface is based on file names and file descriptors. */
/* The file names don't need to be valid Unix file names, but must mean */
/* something to the device. File descriptors are integers which represent */
/* distinct streams open to a file; there may be more than one descriptor */
/* open to each file. The device must keep track of its own file */
/* descriptors, which will be used by the calling functions to read and */
/* write to the device. If there can only be one instance of a "file" on a */
/* device, the file descriptor doesn't matter much, so just pass a */
/* non-negative integer such as zero. */
/*****************************************************************************/
/* The RAMDISK device attempts to follow the Unix devices as closely as */
/* possible, and even supports file descriptors which point to unlinked */
/* files; however, since the RTS doesn't make extensive use of errno, quite */
/* a bit of functionality can't be reproduced. */
/*****************************************************************************/
/*****************************************************************************/
/* RAM_FILES represents the actual ramdisk. It contains files which have */
/* been created. These files might not be open. Take note of the unlinked */
/* flag; if a file is marked unlinked, it is no longer accessible by name. */
/* Attempts to open the file actually create a new entry in RAM_FILES. */
/*****************************************************************************/
/* We could have dynamically reallocated these data structures, but that */
/* would just complicate the example. */
/*****************************************************************************/
#define _NRAMFILES 10
#define _NRAMFDS 10
static struct ram_file_info {
char *name;
unsigned char *data;
unsigned int size;
unsigned int unlinked:1;
} ram_files[_NRAMFILES] = { { 0 } };
/*****************************************************************************/
/* RAM_FDS contains information about open file descriptors. */
/*****************************************************************************/
static struct ram_fd_info {
struct ram_file_info *file;
unsigned int flags;
unsigned int pos;
} ram_fds[_NRAMFDS] = { { 0 } };
/*****************************************************************************/
/* */
/* RAM_SET_FILE_SIZE() - Truncate or extend current file size. */
/* */
/*****************************************************************************/
static int ram_set_file_size(struct ram_file_info *file, unsigned int newsize)
{
unsigned int oldsize = file->size;
unsigned char *new_data = realloc(file->data, newsize);
/*-----------------------------------------------------------------------*/
/* If realloc fails, there's not much we can do about it... */
/*-----------------------------------------------------------------------*/
if (newsize && !new_data) return -1;
file->data = (newsize == 0 ? NULL : new_data);
file->size = newsize;
/*-----------------------------------------------------------------------*/
/* If we have increased the size of the file (possibly by lseeking past */
/* the end), fill the gap with zeros. */
/*-----------------------------------------------------------------------*/
if (newsize > oldsize)
memset(file->data + oldsize, '\0', newsize - oldsize);
return 1;
}
/*****************************************************************************/
/* */
/* RAM_FIND_FILE() - Find (possibly creating) a file of a given name */
/* */
/*****************************************************************************/
static struct ram_file_info *ram_find_file(const char *path, int flags)
{
struct ram_file_info *avail = NULL;
int i;
/*-----------------------------------------------------------------------*/
/* Go through all the file slots, looking for a non-unlinked file of */
/* the given name. Also look for an empty slot for creating the file */
/* if it doesn't already exist. */
/*-----------------------------------------------------------------------*/
for (i=0; i < _NRAMFILES; i++)
if (ram_files[i].name == NULL)
avail = &ram_files[i];
else if (!ram_files[i].unlinked && !strcmp(path, ram_files[i].name))
return &ram_files[i];
/*-----------------------------------------------------------------------*/
/* If not found, and the O_CREAT flag was set, go ahead and create an */
/* empty file. */
/*-----------------------------------------------------------------------*/
if (flags & O_CREAT)
{
avail->name = malloc(strlen(path) + 1);
if (!avail->name) return NULL;
strcpy(avail->name, path);
avail->data = NULL;
avail->size = 0;
avail->unlinked = 0;
return avail;
}
else return NULL;
}
/*****************************************************************************/
/* */
/* RAM_MAYBE_UNLINK_FILE() - Delete a file with no open file descriptors. */
/* */
/*****************************************************************************/
static void ram_maybe_unlink_file(struct ram_file_info *file)
{
int i;
/*-----------------------------------------------------------------------*/
/* This function gets called at each attempt to unlink a file, and when */
/* an unlinked file is closed. The file is deleted from disk, but only */
/* if there are no open file descriptors to it. It is perfectly legal */
/* to unlink an open file; the actual file stays on disk until its last */
/* file descriptor is closed. */
/*-----------------------------------------------------------------------*/
for (i=0; i < _NRAMFDS; i++) if (ram_fds[i].file == file) return;
if (file->size) free(file->data);
free(file->name);
file->name = NULL;
}
/*****************************************************************************/
/*****************************************************************************/
int RAM_open_called = 0; // strictly for testing...
/*****************************************************************************/
/* */
/* RAM_OPEN() - Behaves just like open() */
/*