From 9767c7befda3de757bb92286d61f15d842c9145b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Wed, 19 Mar 2003 17:50:50 +0000 Subject: [PATCH] Added my updated version of Dominic's fsh. Doesn't build as-is - the makefile is only provided as an example, if it is used by file systems in the repository, they should build it in their own test directories. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@2961 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kernel/file_systems/fs_shell/Debug.h | 100 + .../kernel/file_systems/fs_shell/argv.c | 96 + .../kernel/file_systems/fs_shell/argv.h | 2 + .../kernel/file_systems/fs_shell/cache.c | 2420 +++++++++++++ .../kernel/file_systems/fs_shell/cache.h | 99 + .../kernel/file_systems/fs_shell/compat.h | 204 ++ .../kernel/file_systems/fs_shell/fsh.c | 1460 ++++++++ .../kernel/file_systems/fs_shell/fsproto.h | 265 ++ .../kernel/file_systems/fs_shell/hexdump.c | 125 + .../kernel/file_systems/fs_shell/initfs.c | 51 + .../kernel/file_systems/fs_shell/initfs.h | 1 + .../kernel/file_systems/fs_shell/kernel.c | 3195 +++++++++++++++++ .../kernel/file_systems/fs_shell/kprotos.h | 52 + .../kernel/file_systems/fs_shell/lock.h | 28 + .../kernel/file_systems/fs_shell/makefile | 65 + .../kernel/file_systems/fs_shell/makefs.c | 82 + .../kernel/file_systems/fs_shell/mount.c | 416 +++ .../kernel/file_systems/fs_shell/mount.h | 5 + .../kernel/file_systems/fs_shell/myfs.h | 23 + .../kernel/file_systems/fs_shell/rootfs.c | 1109 ++++++ .../kernel/file_systems/fs_shell/skiplist.h | 83 + .../add-ons/kernel/file_systems/fs_shell/sl.c | 401 +++ .../kernel/file_systems/fs_shell/stub.c | 116 + .../kernel/file_systems/fs_shell/sysdep.c | 335 ++ .../kernel/file_systems/fs_shell/tstfs.c | 257 ++ 25 files changed, 10990 insertions(+) create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/Debug.h create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/argv.c create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/argv.h create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/cache.c create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/cache.h create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/compat.h create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/fsh.c create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/fsproto.h create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/hexdump.c create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/initfs.c create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/initfs.h create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/kernel.c create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/kprotos.h create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/lock.h create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/makefile create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/makefs.c create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/mount.c create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/mount.h create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/myfs.h create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/rootfs.c create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/skiplist.h create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/sl.c create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/stub.c create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/sysdep.c create mode 100644 src/tests/add-ons/kernel/file_systems/fs_shell/tstfs.c diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/Debug.h b/src/tests/add-ons/kernel/file_systems/fs_shell/Debug.h new file mode 100644 index 0000000000..ba3e8492e9 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/Debug.h @@ -0,0 +1,100 @@ +#ifndef DEBUG_H +#define DEBUG_H +/* Debug - debug stuff +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + +#ifdef DEBUG +# include +#endif + +#ifdef USER +# include +# define __out printf +#else +# define __out dprintf +#endif + +// Which debugger should be used when? +// The DEBUGGER() macro actually has no effect if DEBUG is not defined, +// use the DIE() macro if you really want to die. +#ifdef DEBUG +# ifdef USER +# define DEBUGGER(x) debugger x +# else +# define DEBUGGER(x) kernel_debugger x +# endif +#else +# define DEBUGGER(x) ; +#endif + +#ifdef USER +# define DIE(x) debugger x +#else +# define DIE(x) kernel_debugger x +#endif + +// Short overview over the debug output macros: +// PRINT() +// is for general messages that very unlikely should appear in a release build +// FATAL() +// this is for fatal messages, when something has really gone wrong +// INFORM() +// general information, as disk size, etc. +// REPORT_ERROR(status_t) +// prints out error information +// RETURN_ERROR(status_t) +// calls REPORT_ERROR() and return the value +// D() +// the statements in D() are only included if DEBUG is defined + + #include + #define kprintf printf + #define dprintf printf + +#ifdef DEBUG + #define PRINT(x) { __out("bfs: "); __out x; } + #define REPORT_ERROR(status) __out("bfs: %s:%s:%ld: %s\n", __FILE__, __FUNCTION__, __LINE__, strerror(status)); + #define RETURN_ERROR(err) { status_t _status = err; if (_status < B_OK) REPORT_ERROR(_status); return _status;} + #define FATAL(x) { __out("bfs: "); __out x; } + #define INFORM(x) { __out("bfs: "); __out x; } + #define FUNCTION() __out("bfs: %s()\n",__FUNCTION__); + #define FUNCTION_START(x) { __out("bfs: %s() ",__FUNCTION__); __out x; } +// #define FUNCTION() ; +// #define FUNCTION_START(x) ; + #define D(x) {x;}; + #define ASSERT(x) { if (!(x)) DEBUGGER(("bfs: assert failed: " #x "\n")); } +#else + #define PRINT(x) ; + #define REPORT_ERROR(status) ; + #define RETURN_ERROR(status) return status; + #define FATAL(x) { __out("bfs: "); __out x; } + #define INFORM(x) { __out("bfs: "); __out x; } + #define FUNCTION() ; + #define FUNCTION_START(x) ; + #define D(x) ; + #define ASSERT(x) ; +#endif + +#ifdef DEBUG + struct block_run; + struct bplustree_header; + struct bplustree_node; + struct data_stream; + struct bfs_inode; + struct disk_super_block; + class Volume; + + // some structure dump functions + extern void dump_block_run(const char *prefix, block_run &run); + extern void dump_super_block(disk_super_block *superBlock); + extern void dump_data_stream(data_stream *stream); + extern void dump_inode(bfs_inode *inode); + extern void dump_bplustree_header(bplustree_header *header); + extern void dump_bplustree_node(bplustree_node *node,bplustree_header *header = NULL,Volume *volume = NULL); + extern void dump_block(const char *buffer, int size); +#endif + +#endif /* DEBUG_H */ diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/argv.c b/src/tests/add-ons/kernel/file_systems/fs_shell/argv.c new file mode 100644 index 0000000000..286d443821 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/argv.c @@ -0,0 +1,96 @@ +/* + This file contains a function, build_argv(), which will take an input + string and chop it into individual words. The return value is an + argv style array (i.e. like what main() receives). + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ + +#include +#include +#include +#include + +#include "argv.h" + +#define DOUBLE_QUOTE '"' +#define SINGLE_QUOTE '\'' +#define BACK_SLASH '\\' + +char ** +build_argv(char *str, int *argc) +{ + int table_size = 16, _argc; + char *ptr=str; + char **argv; + + if (argc == NULL) + argc = &_argc; + + *argc = 0; + argv = (char **)calloc(table_size, sizeof(char *)); + + if (argv == NULL) + return NULL; + + while(*str) { + /* skip intervening white space */ + while(*str != '\0' && (*str == ' ' || *str == '\t' || *str == '\n')) + str++; + + if (*str == '\0') + break; + + if (*str == DOUBLE_QUOTE) { + argv[*argc] = ++str; + while(*str && *str != DOUBLE_QUOTE) { + if (*str == BACK_SLASH) + strcpy(str, str+1); /* copy everything down */ + str++; + } + } else if (*str == SINGLE_QUOTE) { + argv[*argc] = ++str; + while(*str && *str != SINGLE_QUOTE) { + if (*str == BACK_SLASH) + strcpy(str, str+1); /* copy everything down */ + str++; + } + } else { + argv[*argc] = str; + while(*str && *str != ' ' && *str != '\t' && *str != '\n') { + if (*str == BACK_SLASH) + strcpy(str, str+1); /* copy everything down */ + str++; + } + } + + if (*str != '\0') + *str++ = '\0'; /* chop the string */ + + *argc = *argc + 1; + if (*argc >= table_size-1) { + char **nargv; + + table_size = table_size * 2; + nargv = (char **)calloc(table_size, sizeof(char *)); + + if (nargv == NULL) { /* drats! failure. */ + free(argv); + return NULL; + } + + memcpy(nargv, argv, (*argc) * sizeof(char *)); + free(argv); + argv = nargv; + } + } + + return argv; +} diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/argv.h b/src/tests/add-ons/kernel/file_systems/fs_shell/argv.h new file mode 100644 index 0000000000..0402ab2ce9 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/argv.h @@ -0,0 +1,2 @@ +/* this function takes a string and chops it into individual "words" */ +char **build_argv(char *str, int *argc); diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/cache.c b/src/tests/add-ons/kernel/file_systems/fs_shell/cache.c new file mode 100644 index 0000000000..9d66a92e0f --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/cache.c @@ -0,0 +1,2420 @@ +/* + This file contains the global device cache for the BeOS. All + file system I/O comes through here. The cache can handle blocks + of different sizes for multiple different underlying physical + devices. + + The cache is organized as a hash table (for lookups by device + and block number) and two doubly-linked lists. The normal + list is for "normal" blocks which are either clean or dirty. + The locked list is for blocks that are locked in the cache by + BFS. The lists are LRU ordered. + + Most of the work happens in the function cache_block_io() which + is quite lengthy. The other functions of interest are get_ents() + which picks victims to be kicked out of the cache; flush_ents() + which does the work of flushing them; and set_blocks_info() which + handles cloning blocks and setting callbacks so that the BFS + journal will work properly. If you want to modify this code it + will take some study but it's not too bad. Do not think about + separating the list of clean and dirty blocks into two lists as + I did that already and it's slower. + + Originally this cache code was written while listening to the album + "Ride the Lightning" by Metallica. The current version was written + while listening to "Welcome to SkyValley" by Kyuss as well as an + ambient collection on the German label FAX. Music helps a lot when + writing code. + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifdef __BEOS__ +#include +#include +#endif + +#include "compat.h" +#include "lock.h" +#include "cache.h" + + + +#ifndef USER +#define printf dprintf +#endif +#ifdef USER +#define kprintf printf +#endif + + +/* forward prototypes */ +static int flush_ents(cache_ent **ents, int n_ents); + +static int do_dump(int argc, char **argv); +static int do_find_block(int argc, char **argv); +static int do_find_data(int argc, char **argv); +static void cache_flusher(void *arg, int phase); + + +int chatty_io = 0; + +#define CHUNK (512 * 1024) /* a hack to work around scsi driver bugs */ + +size_t +read_phys_blocks(int fd, fs_off_t bnum, void *data, uint num_blocks, int bsize) +{ + size_t ret = 0; + size_t sum; + + if (chatty_io) + printf("R: %8ld : %3d\n", bnum, num_blocks); + + if (num_blocks * bsize < CHUNK) + ret = read_pos(fd, bnum * bsize, data, num_blocks * bsize); + else { + for(sum=0; (sum + CHUNK) <= (num_blocks * bsize); sum += CHUNK) { + ret = read_pos(fd, (bnum * bsize) + sum, data, CHUNK); + if (ret != CHUNK) + break; + + data = (void *)((char *)data + CHUNK); + } + + if (ret == CHUNK && ((num_blocks * bsize) - sum) > 0) { + ret = read_pos(fd, (bnum * bsize) + sum, data, + (num_blocks * bsize) - sum); + + if (ret == (num_blocks * bsize) - sum) + ret = num_blocks * bsize; + } else if (ret == CHUNK) { + ret = num_blocks * bsize; + } + } + + if (ret == num_blocks * bsize) + return 0; + else + return EBADF; +} + +size_t +write_phys_blocks(int fd, fs_off_t bnum, void *data, uint num_blocks, int bsize) +{ + size_t ret = 0; + size_t sum; + + if (chatty_io) + printf("W: %8ld : %3d\n", bnum, num_blocks); + + if (num_blocks * bsize < CHUNK) + ret = write_pos(fd, bnum * bsize, data, num_blocks * bsize); + else { + for(sum=0; (sum + CHUNK) <= (num_blocks * bsize); sum += CHUNK) { + ret = write_pos(fd, (bnum * bsize) + sum, data, CHUNK); + if (ret != CHUNK) + break; + + data = (void *)((char *)data + CHUNK); + } + + if (ret == CHUNK && ((num_blocks * bsize) - sum) > 0) { + ret = write_pos(fd, (bnum * bsize) + sum, data, + (num_blocks * bsize) - sum); + + if (ret == (num_blocks * bsize) - sum) + ret = num_blocks * bsize; + } else if (ret == CHUNK) { + ret = num_blocks * bsize; + } + } + + + if (ret == num_blocks * bsize) + return 0; + else + return EBADF; +} + +// #pragma mark - + +static int +init_hash_table(hash_table *ht) +{ + ht->max = HT_DEFAULT_MAX; + ht->mask = ht->max - 1; + ht->num_elements = 0; + + ht->table = (hash_ent **)calloc(ht->max, sizeof(hash_ent *)); + if (ht->table == NULL) + return ENOMEM; + + return 0; +} + + +static void +shutdown_hash_table(hash_table *ht) +{ + int i, hash_len; + hash_ent *he, *next; + + for(i=0; i < ht->max; i++) { + he = ht->table[i]; + + for(hash_len=0; he; hash_len++, he=next) { + next = he->next; + free(he); + } + } + + if (ht->table) + free(ht->table); + ht->table = NULL; +} + +static void +print_hash_stats(hash_table *ht) +{ + int i, hash_len, max = -1, sum = 0; + hash_ent *he, *next; + + for(i=0; i < ht->max; i++) { + he = ht->table[i]; + + for(hash_len=0; he; hash_len++, he=next) { + next = he->next; + } + if (hash_len) + printf("bucket %3d : %3d\n", i, hash_len); + + sum += hash_len; + if (hash_len > max) + max = hash_len; + } + + printf("max # of chains: %d, average chain length %d\n", max,sum/ht->max); +} + + +#define HASH(d, b) ((((fs_off_t)d) << (sizeof(fs_off_t)*8 - 6)) | (b)) + +static hash_ent * +new_hash_ent(int dev, fs_off_t bnum, void *data) +{ + hash_ent *he; + + he = (hash_ent *)malloc(sizeof(*he)); + if (he == NULL) + return NULL; + + he->hash_val = HASH(dev, bnum); + he->dev = dev; + he->bnum = bnum; + he->data = data; + he->next = NULL; + + return he; +} + + +static int +grow_hash_table(hash_table *ht) +{ + int i, omax, newsize, newmask; + fs_off_t hash; + hash_ent **new_table, *he, *next; + + if (ht->max & ht->mask) { + printf("*** hashtable size %d or mask %d looks weird!\n", ht->max, + ht->mask); + } + + omax = ht->max; + newsize = omax * 2; /* have to grow in powers of two */ + newmask = newsize - 1; + + new_table = (hash_ent **)calloc(newsize, sizeof(hash_ent *)); + if (new_table == NULL) + return ENOMEM; + + for(i=0; i < omax; i++) { + for(he=ht->table[i]; he; he=next) { + hash = he->hash_val & newmask; + next = he->next; + + he->next = new_table[hash]; + new_table[hash] = he; + } + } + + free(ht->table); + ht->table = new_table; + ht->max = newsize; + ht->mask = newmask; + + return 0; +} + + + + +static int +hash_insert(hash_table *ht, int dev, fs_off_t bnum, void *data) +{ + fs_off_t hash; + hash_ent *he, *curr; + + hash = HASH(dev, bnum) & ht->mask; + + curr = ht->table[hash]; + for(; curr != NULL; curr=curr->next) + if (curr->dev == dev && curr->bnum == bnum) + break; + + if (curr && curr->dev == dev && curr->bnum == bnum) { + printf("entry %d:%ld already in the hash table!\n", dev, bnum); + return EEXIST; + } + + he = new_hash_ent(dev, bnum, data); + if (he == NULL) + return ENOMEM; + + he->next = ht->table[hash]; + ht->table[hash] = he; + + ht->num_elements++; + if (ht->num_elements >= ((ht->max * 3) / 4)) { + if (grow_hash_table(ht) != 0) + return ENOMEM; + } + + return 0; +} + +static void * +hash_lookup(hash_table *ht, int dev, fs_off_t bnum) +{ + hash_ent *he; + + he = ht->table[HASH(dev, bnum) & ht->mask]; + + for(; he != NULL; he=he->next) { + if (he->dev == dev && he->bnum == bnum) + break; + } + + if (he) + return he->data; + else + return NULL; +} + + +static void * +hash_delete(hash_table *ht, int dev, fs_off_t bnum) +{ + void *data; + fs_off_t hash; + hash_ent *he, *prev = NULL; + + hash = HASH(dev, bnum) & ht->mask; + he = ht->table[hash]; + + for(; he != NULL; prev=he,he=he->next) { + if (he->dev == dev && he->bnum == bnum) + break; + } + + if (he == NULL) { + printf("*** hash_delete: tried to delete non-existent block %d:%ld\n", + dev, bnum); + return NULL; + } + + data = he->data; + + if (ht->table[hash] == he) + ht->table[hash] = he->next; + else if (prev) + prev->next = he->next; + else + panic("hash table is inconsistent\n"); + + free(he); + ht->num_elements--; + + return data; +} + +// #pragma mark - + +/* + These are the global variables for the cache. +*/ +static block_cache bc; + +#define MAX_IOVECS 64 /* # of iovecs for use by cache code */ +static lock iovec_lock; +static struct iovec *iovec_pool[MAX_IOVECS]; /* each ptr is to an array of iovecs */ +static int iovec_used[MAX_IOVECS]; /* non-zero == iovec is in use */ + +#define NUM_FLUSH_BLOCKS 64 /* size of the iovec array pointed by each ptr */ + + +#define DEFAULT_READ_AHEAD_SIZE (32 * 1024) +static int read_ahead_size = DEFAULT_READ_AHEAD_SIZE; + +/* this array stores the size of each device so we can error check requests */ +#define MAX_DEVICES 256 +fs_off_t max_device_blocks[MAX_DEVICES]; + + +/* has the time of the last cache access so cache flushing doesn't interfere */ +static bigtime_t last_cache_access = 0; + + +int +init_block_cache(int max_blocks, int flags) +{ + memset(&bc, 0, sizeof(bc)); + memset(iovec_pool, 0, sizeof(iovec_pool)); + memset(iovec_used, 0, sizeof(iovec_used)); + memset(&max_device_blocks, 0, sizeof(max_device_blocks)); + + if (init_hash_table(&bc.ht) != 0) + return ENOMEM; + + bc.lock.s = iovec_lock.s = -1; + + bc.max_blocks = max_blocks; + bc.flags = flags; + if (new_lock(&bc.lock, "bollockcache") != 0) + goto err; + + if (new_lock(&iovec_lock, "iovec_lock") != 0) + goto err; + + /* allocate two of these up front so vm won't accidently re-enter itself */ + iovec_pool[0] = (struct iovec *)malloc(sizeof(struct iovec)*NUM_FLUSH_BLOCKS); + iovec_pool[1] = (struct iovec *)malloc(sizeof(struct iovec)*NUM_FLUSH_BLOCKS); + +#ifndef USER +#ifdef DEBUG + add_debugger_command("bcache", do_dump, "dump the block cache list"); + add_debugger_command("fblock", do_find_block, "find a block in the cache"); + add_debugger_command("fdata", do_find_data, "find a data block ptr in the cache"); +#endif + register_kernel_daemon(cache_flusher, NULL, 3); +#endif + + return 0; + + err: + if (bc.lock.s >= 0) + free_lock(&bc.lock); + + if (iovec_lock.s >= 0) + free_lock(&iovec_lock); + + shutdown_hash_table(&bc.ht); + memset((void *)&bc, 0, sizeof(bc)); + return ENOMEM; +} + + +static struct iovec * +get_iovec_array(void) +{ + int i; + struct iovec *iov; + + LOCK(iovec_lock); + + for(i=0; i < MAX_IOVECS; i++) { + if (iovec_used[i] == 0) + break; + } + + if (i >= MAX_IOVECS) /* uh-oh */ + panic("cache: ran out of iovecs (pool 0x%x, used 0x%x)!\n", + &iovec_pool[0], &iovec_used[0]); + + if (iovec_pool[i] == NULL) { + iovec_pool[i] = (struct iovec *)malloc(sizeof(struct iovec)*NUM_FLUSH_BLOCKS); + if (iovec_pool == NULL) + panic("can't allocate an iovec!\n"); + } + + iov = iovec_pool[i]; + iovec_used[i] = 1; + + UNLOCK(iovec_lock); + + return iov; +} + + +static void +release_iovec_array(struct iovec *iov) +{ + int i; + + LOCK(iovec_lock); + + for(i=0; i < MAX_IOVECS; i++) { + if (iov == iovec_pool[i]) + break; + } + + if (i < MAX_IOVECS) + iovec_used[i] = 0; + else /* uh-oh */ + printf("cache: released an iovec I don't own (iov 0x%x)\n", iov); + + + UNLOCK(iovec_lock); +} + + + + +static void +real_dump_cache_list(cache_ent_list *cel) +{ + cache_ent *ce; + + kprintf("starting from LRU end:\n"); + + for(ce=cel->lru; ce; ce=ce->next) { + kprintf("ce 0x%.8lx dev %2d bnum %6ld lock %d flag %d arg 0x%.8lx " + "clone 0x%.8lx\n", (ulong)ce, ce->dev, ce->block_num,ce->lock, + ce->flags, (ulong)ce->arg, (ulong)ce->clone); + } + kprintf("MRU end\n"); +} + +static void +dump_cache_list(void) +{ + kprintf("NORMAL BLOCKS\n"); + real_dump_cache_list(&bc.normal); + + kprintf("LOCKED BLOCKS\n"); + real_dump_cache_list(&bc.locked); + + kprintf("cur blocks %d, max blocks %d ht @ 0x%lx\n", bc.cur_blocks, + bc.max_blocks, (ulong)&bc.ht); +} + +static void +check_bcache(char *str) +{ + int count = 0; + cache_ent *ce, *prev = NULL; + + LOCK(bc.lock); + + for(ce=bc.normal.lru; ce; prev=ce, ce=ce->next) { + count++; + } + + for(ce=bc.locked.lru; ce; prev=ce, ce=ce->next) { + count++; + } + + if (count != bc.cur_blocks) { + if (count < bc.cur_blocks - 16) + panic("%s: count == %d, cur_blocks %d, prev 0x%x\n", + str, count, bc.cur_blocks, prev); + else + printf("%s: count == %d, cur_blocks %d, prev 0x%x\n", + str, count, bc.cur_blocks, prev); + } + + UNLOCK(bc.lock); +} + + +static void +dump_lists(void) +{ + cache_ent *nce; + + printf("LOCKED 0x%x (tail 0x%x, head 0x%x)\n", &bc.locked, + bc.locked.lru, bc.locked.mru); + for(nce=bc.locked.lru; nce; nce=nce->next) + printf("nce @ 0x%x dev %d bnum %ld flags %d lock %d clone 0x%x func 0x%x\n", + nce, nce->dev, nce->block_num, nce->flags, nce->lock, nce->clone, + nce->func); + + printf("NORMAL 0x%x (tail 0x%x, head 0x%x)\n", &bc.normal, + bc.normal.lru, bc.normal.mru); + for(nce=bc.normal.lru; nce; nce=nce->next) + printf("nce @ 0x%x dev %d bnum %ld flags %d lock %d clone 0x%x func 0x%x\n", + nce, nce->dev, nce->block_num, nce->flags, nce->lock, nce->clone, + nce->func); +} + + +static void +check_lists(void) +{ + cache_ent *ce, *prev, *oce; + cache_ent_list *cel; + + cel = &bc.normal; + for(ce=cel->lru,prev=NULL; ce; prev=ce, ce=ce->next) { + for(oce=bc.locked.lru; oce; oce=oce->next) { + if (oce == ce) { + dump_lists(); + panic("1:ce @ 0x%x is in two lists(cel 0x%x &LOCKED)\n",ce,cel); + } + } + } + if (prev && prev != cel->mru) { + dump_lists(); + panic("*** last element in list != cel mru (ce 0x%x, cel 0x%x)\n", + prev, cel); + } + + cel = &bc.locked; + for(ce=cel->lru,prev=NULL; ce; prev=ce, ce=ce->next) { + for(oce=bc.normal.lru; oce; oce=oce->next) { + if (oce == ce) { + dump_lists(); + panic("3:ce @ 0x%x is in two lists(cel 0x%x & DIRTY)\n",ce,cel); + } + } + } + if (prev && prev != cel->mru) { + dump_lists(); + panic("*** last element in list != cel mru (ce 0x%x, cel 0x%x)\n", + prev, cel); + } +} + + + +#ifdef DEBUG +static int +do_dump(int argc, char **argv) +{ + dump_cache_list(); + return 1; +} + + +static int +do_find_block(int argc, char **argv) +{ + int i; + fs_off_t bnum; + cache_ent *ce; + + if (argc < 2) { + kprintf("%s: needs a block # argument\n", argv[0]); + return 1; + } + + for(i=1; i < argc; i++) { + bnum = strtoul(argv[i], NULL, 0); + + for(ce=bc.normal.lru; ce; ce=ce->next) { + if (ce->block_num == bnum) { + kprintf("found clean bnum %ld @ 0x%lx (data @ 0x%lx)\n", + bnum, ce, ce->data); + } + } + + for(ce=bc.locked.lru; ce; ce=ce->next) { + if (ce->block_num == bnum) { + kprintf("found locked bnum %ld @ 0x%lx (data @ 0x%lx)\n", + bnum, ce, ce->data); + } + } + } + + return 0; +} + +static int +do_find_data(int argc, char **argv) +{ + int i; + void *data; + cache_ent *ce; + + if (argc < 2) { + kprintf("%s: needs a block # argument\n", argv[0]); + return 1; + } + + for(i=1; i < argc; i++) { + data = (void *)strtoul(argv[i], NULL, 0); + + for(ce=bc.normal.lru; ce; ce=ce->next) { + if (ce->data == data) { + kprintf("found normal data ptr for bnum %ld @ ce 0x%lx\n", + ce->block_num, ce); + } + } + + for(ce=bc.locked.lru; ce; ce=ce->next) { + if (ce->data == data) { + kprintf("found locked data ptr for bnum %ld @ ce 0x%lx\n", + ce->block_num, ce); + } + } + } + + return 0; +} +#endif /* DEBUG */ + + + +/* + this function detaches the cache_ent from the list. +*/ +static void +delete_from_list(cache_ent_list *cel, cache_ent *ce) +{ + if (ce->next) + ce->next->prev = ce->prev; + if (ce->prev) + ce->prev->next = ce->next; + + if (cel->lru == ce) + cel->lru = ce->next; + if (cel->mru == ce) + cel->mru = ce->prev; + + ce->next = NULL; + ce->prev = NULL; +} + + + +/* + this function adds the cache_ent ce to the head of the + list (i.e. the MRU end). the cache_ent should *not* + be in any lists. +*/ +static void +add_to_head(cache_ent_list *cel, cache_ent *ce) +{ +if (ce->next != NULL || ce->prev != NULL) { + panic("*** ath: ce has non-null next/prev ptr (ce 0x%x nxt 0x%x, prv 0x%x)\n", + ce, ce->next, ce->prev); +} + + ce->next = NULL; + ce->prev = cel->mru; + + if (cel->mru) + cel->mru->next = ce; + cel->mru = ce; + + if (cel->lru == NULL) + cel->lru = ce; +} + + +/* + this function adds the cache_ent ce to the tail of the + list (i.e. the MRU end). the cache_ent should *not* + be in any lists. +*/ +static void +add_to_tail(cache_ent_list *cel, cache_ent *ce) +{ +if (ce->next != NULL || ce->prev != NULL) { + panic("*** att: ce has non-null next/prev ptr (ce 0x%x nxt 0x%x, prv 0x%x)\n", + ce, ce->next, ce->prev); +} + + ce->next = cel->lru; + ce->prev = NULL; + + if (cel->lru) + cel->lru->prev = ce; + cel->lru = ce; + + if (cel->mru == NULL) + cel->mru = ce; +} + + +static int +cache_ent_cmp(const void *a, const void *b) +{ + fs_off_t diff; + cache_ent *p1 = *(cache_ent **)a, *p2 = *(cache_ent **)b; + + if (p1 == NULL || p2 == NULL) + panic("cache_ent pointers are null?!? (a 0x%lx, b 0x%lx\n)\n", a, b); + + if (p1->dev == p2->dev) { + diff = p1->block_num - p2->block_num; + return (int)diff; + } else { + return p1->dev - p2->dev; + } +} + +static void +cache_flusher(void *arg, int phase) +{ + int i, num_ents, err; + bigtime_t now = system_time(); + static cache_ent *ce = NULL; + static cache_ent *ents[NUM_FLUSH_BLOCKS]; + + /* + if someone else was in the cache recently then just bail out so + we don't lock them out unnecessarily + */ + if ((now - last_cache_access) < 1000000) + return; + + LOCK(bc.lock); + + ce = bc.normal.lru; + + for(num_ents=0; ce && num_ents < NUM_FLUSH_BLOCKS; ce=ce->next) { + if (ce->flags & CE_BUSY) + continue; + + if ((ce->flags & CE_DIRTY) == 0 && ce->clone == NULL) + continue; + + ents[num_ents] = ce; + ents[num_ents]->flags |= CE_BUSY; + num_ents++; + } + + /* if we've got some room left over, look for cloned locked blocks */ + if (num_ents < NUM_FLUSH_BLOCKS) { + ce = bc.locked.lru; + + for(; num_ents < NUM_FLUSH_BLOCKS;) { + for(; + ce && ((ce->flags & CE_BUSY) || ce->clone == NULL); + ce=ce->next) + /* skip ents that meet the above criteria */; + + if (ce == NULL) + break; + + ents[num_ents] = ce; + ents[num_ents]->flags |= CE_BUSY; + ce = ce->next; + num_ents++; + } + } + + UNLOCK(bc.lock); + + if (num_ents == 0) + return; + + qsort(ents, num_ents, sizeof(cache_ent **), cache_ent_cmp); + + if ((err = flush_ents(ents, num_ents)) != 0) { + printf("flush ents failed (ents @ 0x%lx, num_ents %d!\n", + (ulong)ents, num_ents); + } + + for(i=0; i < num_ents; i++) { /* clear the busy bit on each of ent */ + ents[i]->flags &= ~CE_BUSY; + } +} + + + +static int +flush_cache_ent(cache_ent *ce) +{ + int ret = 0; + void *data; + + /* if true, then there's nothing to flush */ + if ((ce->flags & CE_DIRTY) == 0 && ce->clone == NULL) + return 0; + + /* same thing here */ + if (ce->clone == NULL && ce->lock != 0) + return 0; + + restart: + if (ce->clone) + data = ce->clone; + else + data = ce->data; + + if (chatty_io > 2) printf("flush: %7Ld\n", ce->block_num); + ret = write_phys_blocks(ce->dev, ce->block_num, data, 1, ce->bsize); + + if (ce->func) { + ce->func(ce->logged_bnum, 1, ce->arg); + ce->func = NULL; + } + + if (ce->clone) { + free(ce->clone); + ce->clone = NULL; + + if (ce->lock == 0 && (ce->flags & CE_DIRTY)) + goto restart; /* also write the real data ptr */ + } else { + ce->flags &= ~CE_DIRTY; + } + + return ret; +} + + +static int +flush_ents(cache_ent **ents, int n_ents) +{ + int i, j, k, ret = 0, bsize, iocnt, do_again = 0; + fs_off_t start_bnum; + struct iovec *iov; + + iov = get_iovec_array(); + if (iov == NULL) + return ENOMEM; + +restart: + for(i=0; i < n_ents; i++) { + /* if true, then there's nothing to flush */ + if ((ents[i]->flags & CE_DIRTY) == 0 && ents[i]->clone == NULL) + continue; + + /* if true we can't touch the dirty data yet because it's locked */ + if (ents[i]->clone == NULL && ents[i]->lock != 0) + continue; + + + bsize = ents[i]->bsize; + start_bnum = ents[i]->block_num; + + for(j=i+1; j < n_ents && (j - i) < NUM_FLUSH_BLOCKS; j++) { + if (ents[j]->dev != ents[i]->dev || + ents[j]->block_num != start_bnum + (j - i)) + break; + + if (ents[j]->clone == NULL && ents[j]->lock != 0) + break; + } + + if (j == i+1) { /* only one block, just flush it directly */ + if ((ret = flush_cache_ent(ents[i])) != 0) + break; + continue; + } + + + for(k=i,iocnt=0; k < j; k++,iocnt++) { + if (ents[k]->clone) + iov[iocnt].iov_base = ents[k]->clone; + else + iov[iocnt].iov_base = ents[k]->data; + + iov[iocnt].iov_len = bsize; + } + + if (chatty_io) + printf("writev @ %Ld for %d blocks\n", start_bnum, iocnt); + + ret = writev_pos(ents[i]->dev, start_bnum * (fs_off_t)bsize, + &iov[0], iocnt); + if (ret != iocnt*bsize) { + int idx; + + printf("flush_ents: writev failed: iocnt %d start bnum %ld " + "bsize %d, ret %d\n", iocnt, start_bnum, bsize, ret); + + for(idx=0; idx < iocnt; idx++) + printf("iov[%2d] = 0x%8x :: %d\n", idx, iov[idx].iov_base, + iov[idx].iov_len); + + printf("error %s writing blocks %ld:%d (%d != %d)\n", + strerror(errno), start_bnum, iocnt, ret, iocnt*bsize); + ret = EINVAL; + break; + } + ret = 0; + + + for(k=i; k < j; k++) { + if (ents[k]->func) { + ents[k]->func(ents[k]->logged_bnum, 1, ents[k]->arg); + ents[k]->func = NULL; + } + + if (ents[k]->clone) { + free(ents[k]->clone); + ents[k]->clone = NULL; + } else { + ents[k]->flags &= ~CE_DIRTY; + } + } + + + i = j - 1; /* i gets incremented by the outer for loop */ + } + + /* + here we have to go back through and flush any blocks that are + still dirty. with an arched brow you astutely ask, "but how + could this happen given the above loop?" Ahhh young grasshopper + I say, the path through the cache is long and twisty and fraught + with peril. The reason it can happen is that a block can be both + cloned and dirty. The above loop would only flush the cloned half + of the data, not the main dirty block. So we have to go back + through and see if there are any blocks that are still dirty. If + there are we go back to the top of the function and do the whole + thing over. Kind of grody but it is necessary to insure the + correctness of the log for the Be file system. + */ + if (do_again == 0) { + for(i=0; i < n_ents; i++) { + if ((ents[i]->flags & CE_DIRTY) == 0 || ents[i]->lock) + continue; + + do_again = 1; + break; + } + + if (do_again) + goto restart; + } + + release_iovec_array(iov); + + + return ret; +} + +static void +delete_cache_list(cache_ent_list *cel) +{ + void *junk; + cache_ent *ce, *next; + + for(ce=cel->lru; ce; ce=next) { + next = ce->next; + if (ce->lock != 0) { + if (ce->func) + printf("*** shutdown_block_cache: block %ld, lock == %d " + "(arg 0x%lx)!\n", ce->block_num, ce->lock, + (ulong)ce->arg); + else + printf("*** shutdown_block_cache: block %ld, lock == %d!\n", + ce->block_num, ce->lock); + } + + if (ce->flags & CE_BUSY) { + printf("* shutdown block cache: bnum %ld is busy? ce 0x%lx\n", + ce->block_num, (ulong)ce); + } + + if ((ce->flags & CE_DIRTY) || ce->clone) { + flush_cache_ent(ce); + } + + if (ce->clone) + free(ce->clone); + ce->clone = NULL; + + if (ce->data) + free(ce->data); + ce->data = NULL; + + if ((junk = hash_delete(&bc.ht, ce->dev, ce->block_num)) != ce) { + printf("*** free_device_cache: bad hash table entry %ld " + "0x%lx != 0x%lx\n", ce->block_num, (ulong)junk, (ulong)ce); + } + + memset(ce, 0xfd, sizeof(*ce)); + free(ce); + + bc.cur_blocks--; + } +} + + +void +shutdown_block_cache(void) +{ + /* print_hash_stats(&bc.ht); */ + + if (bc.lock.s > 0) + LOCK(bc.lock); + +#ifndef USER + unregister_kernel_daemon(cache_flusher, NULL); +#endif + + delete_cache_list(&bc.normal); + delete_cache_list(&bc.locked); + + bc.normal.lru = bc.normal.mru = NULL; + bc.locked.lru = bc.locked.mru = NULL; + + shutdown_hash_table(&bc.ht); + + if (bc.lock.s > 0) + free_lock(&bc.lock); + bc.lock.s = -1; + + if (iovec_lock.s >= 0) + free_lock(&iovec_lock); +} + + + +int +init_cache_for_device(int fd, fs_off_t max_blocks) +{ + int ret = 0; + + if (fd >= MAX_DEVICES) + return -1; + + LOCK(bc.lock); + + if (max_device_blocks[fd] != 0) { + printf("device %d is already initialized!\n", fd); + ret = -1; + } else { + max_device_blocks[fd] = max_blocks; + } + + UNLOCK(bc.lock); + + return ret; +} + + + +/* + this routine assumes that bc.lock has been acquired +*/ +static cache_ent * +block_lookup(int dev, fs_off_t bnum) +{ + int count = 0; + cache_ent *ce; + + while (1) { + ce = hash_lookup(&bc.ht, dev, bnum); + if (ce == NULL) + return NULL; + + if ((ce->flags & CE_BUSY) == 0) /* it's ok, break out and return it */ + break; + + /* else, it's busy and we need to retry our lookup */ + UNLOCK(bc.lock); + + snooze(5000); + if (count++ == 5000) { /* then a lot of time has elapsed */ + printf("block %ld isn't coming un-busy (ce @ 0x%lx)\n", + ce->block_num, (ulong)ce); + } + + LOCK(bc.lock); + } + + if (ce->flags & CE_BUSY) + panic("block lookup: returning a busy block @ 0x%lx?!?\n",(ulong)ce); + + return ce; +} + + + +int +set_blocks_info(int dev, fs_off_t *blocks, int nblocks, + void (*func)(fs_off_t bnum, size_t nblocks, void *arg), void *arg) +{ + int i, j, cur; + cache_ent *ce; + cache_ent *ents[NUM_FLUSH_BLOCKS]; + + LOCK(bc.lock); + + + for(i=0, cur=0; i < nblocks; i++) { + + /* printf("sbi: %ld (arg 0x%x)\n", blocks[i], arg); */ + ce = block_lookup(dev, blocks[i]); + if (ce == NULL) { + panic("*** set_block_info can't find bnum %ld!\n", blocks[i]); + UNLOCK(bc.lock); + return ENOENT; /* hopefully this doesn't happen... */ + } + + + if (blocks[i] != ce->block_num || dev != ce->dev) { + UNLOCK(bc.lock); + panic("** error1: looked up dev %d block %ld but found dev %d " + "bnum %ld\n", dev, blocks[i], ce->dev, ce->block_num); + return EBADF; + } + + if (ce->lock == 0) { + panic("* set_block_info on bnum %ld (%d) but it's not locked!\n", + blocks[i], nblocks); + } + + + if ((ce->flags & CE_DIRTY) == 0) { + panic("*** set_block_info on non-dirty block bnum %ld (%d)!\n", + blocks[i], nblocks); + } + + ce->flags |= CE_BUSY; /* mark all blocks as busy till we're done */ + + /* if there is cloned data, it needs to be flushed now */ + if (ce->clone && ce->func) { + ents[cur++] = ce; + + if (cur >= NUM_FLUSH_BLOCKS) { + UNLOCK(bc.lock); + + qsort(ents, cur, sizeof(cache_ent **), cache_ent_cmp); + + flush_ents(ents, cur); + + LOCK(bc.lock); + for(j=0; j < cur; j++) + ents[j]->flags &= ~CE_BUSY; + cur = 0; + } + } + } + + + if (cur != 0) { + UNLOCK(bc.lock); + + qsort(ents, cur, sizeof(cache_ent **), cache_ent_cmp); + + flush_ents(ents, cur); + + LOCK(bc.lock); + for(j=0; j < cur; j++) + ents[j]->flags &= ~CE_BUSY; + cur = 0; + } + + + /* now go through and set the info that we were asked to */ + for(i=0; i < nblocks; i++) { + /* we can call hash_lookup() here because we know it's around */ + ce = hash_lookup(&bc.ht, dev, blocks[i]); + if (ce == NULL) { + panic("*** set_block_info can't find bnum %ld!\n", blocks[i]); + UNLOCK(bc.lock); + return ENOENT; /* hopefully this doesn't happen... */ + } + + ce->flags &= ~(CE_DIRTY | CE_BUSY); + + if (ce->func != NULL) { + panic("*** set_block_info non-null callback on bnum %ld\n", + ce->block_num); + } + + if (ce->clone != NULL) { + panic("*** ce->clone == 0x%lx, not NULL in set_block_info\n", + (ulong)ce->clone); + } + + ce->clone = (void *)malloc(ce->bsize); + if (ce->clone == NULL) + panic("*** can't clone bnum %ld (bsize %d)\n", + ce->block_num, ce->bsize); + + + memcpy(ce->clone, ce->data, ce->bsize); + + ce->func = func; + ce->arg = arg; + + ce->logged_bnum = blocks[i]; + + ce->lock--; + if (ce->lock < 0) { + printf("sbi: whoa nellie! ce @ 0x%lx (%ld) has lock == %d\n", + (ulong)ce, ce->block_num, ce->lock); + } + + if (ce->lock == 0) { + delete_from_list(&bc.locked, ce); + add_to_head(&bc.normal, ce); + } + } + + UNLOCK(bc.lock); + + return 0; +} + + +/* this function is only for use by flush_device() */ +static void +do_flush(cache_ent **ents, int max) +{ + int i; + + for(i=0; i < max; i++) { + ents[i]->flags |= CE_BUSY; + } + + UNLOCK(bc.lock); + + qsort(ents, max, sizeof(cache_ent **), cache_ent_cmp); + flush_ents(ents, max); + + LOCK(bc.lock); + for(i=0; i < max; i++) { + ents[i]->flags &= ~CE_BUSY; + } +} + +int +flush_device(int dev, int warn_locked) +{ + int cur; + cache_ent *ce; + cache_ent *ents[NUM_FLUSH_BLOCKS]; + + LOCK(bc.lock); + + cur = 0; + ce = bc.normal.lru; + while (ce) { + if (ce->dev != dev || (ce->flags & CE_BUSY)) { + ce = ce->next; + continue; + } + + if ((ce->flags & CE_DIRTY) || ce->clone) { + ents[cur++] = ce; + if (cur >= NUM_FLUSH_BLOCKS) { + do_flush(ents, cur); + + ce = bc.normal.lru; + cur = 0; + continue; + } + } + + ce = ce->next; + } + + if (cur != 0) + do_flush(ents, cur); + + cur = 0; + ce = bc.locked.lru; + while (ce) { + if (ce->dev != dev || (ce->flags & CE_BUSY)) { + ce = ce->next; + continue; + } + + if (ce->clone) { + ents[cur++] = ce; + if (cur >= NUM_FLUSH_BLOCKS) { + do_flush(ents, cur); + + ce = bc.locked.lru; + cur = 0; + continue; + } + } + + ce = ce->next; + } + + if (cur != 0) + do_flush(ents, cur); + + UNLOCK(bc.lock); + + return 0; +} + + +static void +real_remove_cached_blocks(int dev, int allow_writes, cache_ent_list *cel) +{ + void *junk; + cache_ent *ce, *next = NULL; + + for(ce=cel->lru; ce; ce=next) { + next = ce->next; + + if (ce->dev != dev) { + continue; + } + + if (ce->lock != 0 || (ce->flags & CE_BUSY)) { + printf("*** remove_cached_dev: block %ld has lock = %d, flags " + "0x%x! ce @ 0x%lx\n", ce->block_num, ce->lock, ce->flags, + (ulong)ce); + } + + if (allow_writes == ALLOW_WRITES && + ((ce->flags & CE_DIRTY) || ce->clone)) { + ce->flags |= CE_BUSY; + flush_cache_ent(ce); + ce->flags &= ~CE_BUSY; + } + + /* unlink this guy */ + if (cel->lru == ce) + cel->lru = ce->next; + + if (cel->mru == ce) + cel->mru = ce->prev; + + if (ce->prev) + ce->prev->next = ce->next; + if (ce->next) + ce->next->prev = ce->prev; + + if (ce->clone) + free(ce->clone); + ce->clone = NULL; + + if (ce->data) + free(ce->data); + ce->data = NULL; + + if ((junk = hash_delete(&bc.ht, ce->dev, ce->block_num)) != ce) { + panic("*** remove_cached_device: bad hash table entry %ld " + "0x%lx != 0x%lx\n", ce->block_num, (ulong)junk, (ulong)ce); + } + + free(ce); + + bc.cur_blocks--; + } + +} + +int +remove_cached_device_blocks(int dev, int allow_writes) +{ + LOCK(bc.lock); + + real_remove_cached_blocks(dev, allow_writes, &bc.normal); + real_remove_cached_blocks(dev, allow_writes, &bc.locked); + + max_device_blocks[dev] = 0; + + UNLOCK(bc.lock); + + return 0; +} + + + +int +flush_blocks(int dev, fs_off_t bnum, int nblocks) +{ + int cur, i; + cache_ent *ce; + cache_ent *ents[NUM_FLUSH_BLOCKS]; + + if (nblocks == 0) /* might as well check for this */ + return 0; + + LOCK(bc.lock); + + cur = 0; + for(; nblocks > 0; nblocks--, bnum++) { + ce = block_lookup(dev, bnum); + if (ce == NULL) + continue; + + if (bnum != ce->block_num || dev != ce->dev) { + UNLOCK(bc.lock); + panic("error2: looked up dev %d block %ld but found %d %ld\n", + dev, bnum, ce->dev, ce->block_num); + return EBADF; + } + + if ((ce->flags & CE_DIRTY) == 0 && ce->clone == NULL) + continue; + + ce->flags |= CE_BUSY; + ents[cur++] = ce; + + if (cur >= NUM_FLUSH_BLOCKS) { + UNLOCK(bc.lock); + + qsort(ents, cur, sizeof(cache_ent **), cache_ent_cmp); + flush_ents(ents, cur); + + LOCK(bc.lock); + for(i=0; i < cur; i++) { + ents[i]->flags &= ~CE_BUSY; + } + cur = 0; + } + } + + UNLOCK(bc.lock); + + if (cur == 0) /* nothing more to do */ + return 0; + + /* flush out the last few buggers */ + qsort(ents, cur, sizeof(cache_ent **), cache_ent_cmp); + flush_ents(ents, cur); + + for(i=0; i < cur; i++) { + ents[i]->flags &= ~CE_BUSY; + } + + return 0; +} + + +int +mark_blocks_dirty(int dev, fs_off_t bnum, int nblocks) +{ + int ret = 0; + cache_ent *ce; + + LOCK(bc.lock); + + while(nblocks > 0) { + ce = block_lookup(dev, bnum); + if (ce) { + ce->flags |= CE_DIRTY; + bnum += 1; + nblocks -= 1; + } else { /* hmmm, that's odd, didn't find it */ + printf("** mark_blocks_diry couldn't find block %ld (len %d)\n", + bnum, nblocks); + ret = ENOENT; + break; + } + } + + UNLOCK(bc.lock); + + return ret; +} + + + +int +release_block(int dev, fs_off_t bnum) +{ + cache_ent *ce; + + /* printf("rlsb: %ld\n", bnum); */ + LOCK(bc.lock); + + ce = block_lookup(dev, bnum); + if (ce) { + if (bnum != ce->block_num || dev != ce->dev) { + panic("*** error3: looked up dev %d block %ld but found %d %ld\n", + dev, bnum, ce->dev, ce->block_num); + UNLOCK(bc.lock); + return EBADF; + } + + ce->lock--; + if (ce->lock < 0) { + printf("rlsb: whoa nellie! ce %ld has lock == %d\n", + ce->block_num, ce->lock); + } + + if (ce->lock == 0) { + delete_from_list(&bc.locked, ce); + add_to_head(&bc.normal, ce); + } + + } else { /* hmmm, that's odd, didn't find it */ + panic("** release_block asked to find %ld but it's not here\n", + bnum); + } + + UNLOCK(bc.lock); + + return 0; +} + + +static cache_ent * +new_cache_ent(int bsize) +{ + cache_ent *ce; + + ce = (cache_ent *)calloc(1, sizeof(cache_ent)); + if (ce == NULL) { + panic("*** error: cache can't allocate memory!\n"); + return NULL; + } + + ce->data = malloc(bsize); + if (ce->data == NULL) { + free(ce); + panic("** error cache can't allocate data memory\n"); + UNLOCK(bc.lock); + return NULL; + } + + ce->dev = -1; + ce->block_num = -1; + + return ce; +} + + +static void +get_ents(cache_ent **ents, int num_needed, int max, int *num_gotten, int bsize) +{ + int cur, retry_counter = 0, max_retry = num_needed * 256; + cache_ent *ce; + + if (num_needed > max) + panic("get_ents: num_needed %d but max %d (doh!)\n", num_needed, max); + + /* if the cache isn't full yet, just allocate the blocks */ + for(cur=0; bc.cur_blocks < bc.max_blocks && cur < num_needed; cur++) { + ents[cur] = new_cache_ent(bsize); + if (ents[cur] == NULL) + break; + bc.cur_blocks++; + } + + /* pluck off blocks from the LRU end of the normal list, keep trying too */ + while(cur < num_needed && retry_counter < max_retry) { + for(ce=bc.normal.lru; ce && cur < num_needed; ce=ce->next) { + if (ce->lock) + panic("get_ents: normal list has locked blocks (ce 0x%x)\n",ce); + + if (ce->flags & CE_BUSY) /* don't touch busy blocks */ + continue; + + ce->flags |= CE_BUSY; + ents[cur++] = ce; + } + + if (cur < num_needed) { + UNLOCK(bc.lock); + snooze(10000); + LOCK(bc.lock); + retry_counter++; + } + } + + if (cur < num_needed && retry_counter >= max_retry) { /* oh shit! */ + dump_cache_list(); + UNLOCK(bc.lock); + panic("get_ents: waited too long; can't get enough ce's (c %d n %d)\n", + cur, num_needed); + } + + /* + If the last block is a dirty one, try to get more of 'em so + that we can flush a bunch of blocks at once. + */ + if (cur && cur < max && + ((ents[cur-1]->flags & CE_DIRTY) || ents[cur-1]->clone)) { + + for(ce=ents[cur-1]->next; ce && cur < max; ce=ce->next) { + if (ce->flags & CE_BUSY) /* don't touch busy blocks */ + continue; + + if (ce->lock) + panic("get_ents:2 dirty list has locked blocks (ce 0x%x)\n",ce); + + ce->flags |= CE_BUSY; + ents[cur++] = ce; + } + } + + *num_gotten = cur; +} + + +static int +read_into_ents(int dev, fs_off_t bnum, cache_ent **ents, int num, int bsize) +{ + int i, ret; + struct iovec *iov; + + iov = get_iovec_array(); + + for(i=0; i < num; i++) { + iov[i].iov_base = ents[i]->data; + iov[i].iov_len = bsize; + } + + if (chatty_io > 2) + printf("readv @ %Ld for %d blocks (at %Ld, block_size = %ld)\n", bnum, num, bnum*bsize,bsize); + ret = readv_pos(dev, bnum*bsize, iov, num); + + release_iovec_array(iov); + + if (ret != num*bsize) { + printf("read_into_ents: asked to read %d bytes but got %d\n", + num*bsize, ret); + printf("*** iov @ 0x%x (num %d)\n", iov, num); + return EINVAL; + } else + return 0; +} + + + +#define CACHE_READ 0x0001 +#define CACHE_WRITE 0x0002 +#define CACHE_NOOP 0x0004 /* for getting empty blocks */ +#define CACHE_LOCKED 0x0008 +#define CACHE_READ_AHEAD_OK 0x0010 /* it's ok to do read-ahead */ + + +static char * +op_to_str(int op) +{ + static char buff[128]; + + if (op & CACHE_READ) + strcpy(buff, "READ"); + else if (op & CACHE_WRITE) + strcpy(buff, "WRITE"); + else if (op & CACHE_NOOP) + strcpy(buff, "NOP"); + + if (op & CACHE_LOCKED) + strcat(buff, " LOCKED"); + + if (op & CACHE_READ_AHEAD_OK) + strcat(buff, " (AHEAD)"); + + return buff; +} + +static int +cache_block_io(int dev, fs_off_t bnum, void *data, fs_off_t num_blocks, int bsize, + int op, void **dataptr) +{ + size_t err = 0; + cache_ent *ce; + cache_ent_list *cel; + + if (chatty_io > 1) + printf("cbio: bnum = %Ld, num_blocks = %Ld, bsize = %d, op = %s\n", bnum, num_blocks, + bsize, op_to_str(op)); + + /* some sanity checks first */ + if (bsize == 0) + panic("cache_io: block size == 0 for bnum %ld?!?\n", bnum); + + if (num_blocks == 0) + panic("cache_io: bnum %ld has num_blocks == 0!\n", bnum); + + if (data == NULL && dataptr == NULL) { + printf("major butthead move: null data and dataptr! bnum %ld:%ld\n", + bnum, num_blocks); + return ENOMEM; + } + + if (data == NULL) { + if (num_blocks != 1) /* get_block() should never do that */ + panic("cache_io: num_blocks %ld but should be 1\n", + num_blocks); + + if (op & CACHE_WRITE) + panic("cache_io: get_block() asked to write?!?\n"); + } + + if (bnum + num_blocks > max_device_blocks[dev]) { + printf("dev %d: access to blocks %ld:%ld but max_dev_blocks is %ld\n", + dev, bnum, num_blocks, max_device_blocks[dev]); +*(int *)0x3100 = 0xc0debabe; + return EINVAL; + } + + last_cache_access = system_time(); + + /* if the i/o is greater than 64k, do it directly */ + if (num_blocks * bsize >= 64 * 1024) { + char *ptr; + fs_off_t tmp; + + if (data == NULL || (op & CACHE_LOCKED)) { + panic("*** asked to do a large locked io that's too hard!\n"); + } + + + if (op & CACHE_READ) { + if (read_phys_blocks(dev, bnum, data, num_blocks, bsize) != 0) { + printf("cache read:read_phys_blocks failed (%s on blocks %ld:%ld)!\n", + strerror(errno), bnum, num_blocks); + return EINVAL; + } + + LOCK(bc.lock); + + /* if any of the blocks are in the cache, grab them instead */ + ptr = data; + for(tmp=bnum; tmp < bnum+num_blocks; tmp++, ptr+=bsize) { + ce = block_lookup(dev, tmp); + /* + if we find a block in the cache we have to copy its + data just in case it is more recent than what we just + read from disk (which could happen if someone wrote + these blocks after we did the read but before we locked + the cache and entered this loop). + */ + if (ce) { + if (tmp != ce->block_num || dev != ce->dev) { + UNLOCK(bc.lock); + panic("*** error4: looked up dev %d block %ld but " + "found %d %ld\n", dev, tmp, ce->dev, + ce->block_num); + } + + memcpy(ptr, ce->data, bsize); + } + } + + UNLOCK(bc.lock); + } else if (op & CACHE_WRITE) { + LOCK(bc.lock); + + /* if any of the blocks are in the cache, update them too */ + ptr = data; + for(tmp=bnum; tmp < bnum+num_blocks; tmp++, ptr+=bsize) { + ce = block_lookup(dev, tmp); + if (ce) { + if (tmp != ce->block_num || dev != ce->dev) { + UNLOCK(bc.lock); + panic("*** error5: looked up dev %d block %ld but " + "found %d %ld\n", dev, tmp, ce->dev, + ce->block_num); + return EBADF; + } + + /* XXXdbg -- this isn't strictly necessary */ + if (ce->clone) { + printf("over-writing cloned data (ce 0x%x bnum %ld)...\n", ce,tmp); + flush_cache_ent(ce); + } + + /* copy the data into the cache */ + memcpy(ce->data, ptr, bsize); + } + } + + UNLOCK(bc.lock); + + if (write_phys_blocks(dev, bnum, data, num_blocks, bsize) != 0) { + printf("cache write: write_phys_blocks failed (%s on blocks " + "%ld:%ld)!\n", strerror(errno), bnum, num_blocks); + return EINVAL; + } + } else { + printf("bad cache op %d (bnum %ld nblocks %ld)\n", op, bnum, + num_blocks); + return EINVAL; + } + + return 0; + } + + + LOCK(bc.lock); + while(num_blocks) { + + ce = block_lookup(dev, bnum); + if (ce) { + if (bnum != ce->block_num || dev != ce->dev) { + UNLOCK(bc.lock); + panic("*** error6: looked up dev %d block %ld but found " + "%d %ld\n", dev, bnum, ce->dev, ce->block_num); + return EBADF; + } + + if (bsize != ce->bsize) { + panic("*** requested bsize %d but ce->bsize %d ce @ 0x%x\n", + bsize, ce->bsize, ce); + } + + /* delete this ent from the list it is in because it may change */ + if (ce->lock) + cel = &bc.locked; + else + cel = &bc.normal; + + delete_from_list(cel, ce); + + if (op & CACHE_READ) { + if (data && data != ce->data) { + memcpy(data, ce->data, bsize); + } else if (dataptr) { + *dataptr = ce->data; + } else { + printf("cbio:data 0x%x dptr 0x%x ce @ 0x%x ce->data 0x%x\n", + data, dataptr, ce, ce->data); + } + } else if (op & CACHE_WRITE) { + if (data && data != ce->data) + memcpy(ce->data, data, bsize); + + ce->flags |= CE_DIRTY; + } else if (op & CACHE_NOOP) { + memset(ce->data, 0, bsize); + if (data) + memset(data, 0, bsize); + + if (dataptr) + *dataptr = ce->data; + + ce->flags |= CE_DIRTY; + } else { + panic("cached_block_io: bogus op %d\n", op); + } + + if (op & CACHE_LOCKED) + ce->lock++; + + if (ce->lock) + cel = &bc.locked; + else + cel = &bc.normal; + + /* now put this ent at the head of the appropriate list */ + add_to_head(cel, ce); + + if (data != NULL) + data = (void *)((char *)data + bsize); + + bnum += 1; + num_blocks -= 1; + + continue; + } else { /* it's not in the cache */ + int cur, cur_nblocks, num_dirty, real_nblocks, num_needed; + cache_ent *ents[NUM_FLUSH_BLOCKS]; + + /* + here we find out how many additional blocks in this request + are not in the cache. the idea is that then we can do one + big i/o on that many blocks at once. + */ + for(cur_nblocks=1; + cur_nblocks < num_blocks && cur_nblocks < NUM_FLUSH_BLOCKS; + cur_nblocks++) { + + /* we can call hash_lookup() directly instead of + block_lookup() because we don't care about the + state of the busy bit of the block at this point + */ + if (hash_lookup(&bc.ht, dev, bnum + cur_nblocks)) + break; + } + + /* + here we try to figure out how many extra blocks we should read + for read-ahead. we want to read as many as possible that are + not already in the cache and that don't cause us to try and + read beyond the end of the disk. + */ + if ((op & CACHE_READ) && (op & CACHE_READ_AHEAD_OK) && + (cur_nblocks * bsize) < read_ahead_size) { + + for(num_needed=cur_nblocks; + num_needed < (read_ahead_size / bsize); + num_needed++) { + + if ((bnum + num_needed) >= max_device_blocks[dev]) + break; + + if (hash_lookup(&bc.ht, dev, bnum + num_needed)) + break; + } + } else { + num_needed = cur_nblocks; + } + + /* this will get us pointers to a bunch of cache_ents we can use */ + get_ents(ents, num_needed, NUM_FLUSH_BLOCKS, &real_nblocks, bsize); + + if (real_nblocks < num_needed) { + panic("don't have enough cache ents (need %d got %d %ld::%d)\n", + num_needed, real_nblocks, bnum, num_blocks); + } + + /* + There are now three variables used as limits within the ents + array. This is how they are related: + + cur_nblocks <= num_needed <= real_nblocks + + Ents from 0 to cur_nblocks-1 are going to be used to fulfill + this IO request. Ents from cur_nblocks to num_needed-1 are + for read-ahead. Ents from num_needed to real_nblocks are + extra blocks that get_ents() asked us to flush. Often (and + always on writes) cur_nblocks == num_needed. + + Below, we sort the list of ents so that when we flush them + they go out in order. + */ + + qsort(ents, real_nblocks, sizeof(cache_ent **), cache_ent_cmp); + + /* + delete each ent from its list because it will change. also + count up how many dirty blocks there are and insert into the + hash table any new blocks so that no one else will try to + read them in when we release the cache semaphore to do our I/O. + */ + for(cur=0,num_dirty=0; cur < real_nblocks; cur++) { + ce = ents[cur]; + ce->flags |= CE_BUSY; + + /* + insert the new block into the hash table with its new block + number. note that the block is still in the hash table for + its old block number -- and it has to be until we are done + flushing it from the cache (to prevent someone else from + sneaking in in front of us and trying to read the same + block that we're flushing). + */ + if (cur < num_needed) { + if (hash_insert(&bc.ht, dev, bnum + cur, ce) != 0) + panic("could not insert cache ent for %d %ld (0x%lx)\n", + dev, bnum + cur, (ulong)ents[cur]); + } + + if (ce->dev == -1) + continue; + + if ((ce->flags & CE_DIRTY) || ce->clone) + num_dirty++; + + if (ce->lock) + panic("cbio: can't use locked blocks here ce @ 0x%x\n",ce); + else + cel = &bc.normal; + + delete_from_list(cel, ce); + } + ce = NULL; + + + /* + we release the block cache semaphore here so that we can + go do all the i/o we need to do (flushing dirty blocks + that we're kicking out as well as reading any new data). + + because all the blocks we're touching are marked busy + no one else should mess with them while we're doing this. + */ + if (num_dirty || (op & CACHE_READ)) { + UNLOCK(bc.lock); + + /* this flushes any blocks we're kicking out that are dirty */ + if (num_dirty && (err = flush_ents(ents, real_nblocks)) != 0) { + printf("flush ents failed (ents @ 0x%lx, nblocks %d!\n", + (ulong)ents, cur_nblocks); + goto handle_err; + } + + } + + /* + now that everything is flushed to disk, go through and + make sure that the data blocks we're going to use are + the right block size for this current request (it's + possible we're kicking out some smaller blocks and need + to reallocate the data block pointer). We do this in two + steps, first free'ing everything and then going through + and doing the malloc's to try and be nice to the memory + system (i.e. allow it to coalesce stuff, etc). + */ + err = 0; + for(cur=0; cur < num_needed; cur++) { + if (ents[cur]->bsize != bsize) { + free(ents[cur]->data); + ents[cur]->data = NULL; + + if (ents[cur]->clone) { + free(ents[cur]->clone); + ents[cur]->clone = NULL; + } + } + } + + for(cur=0; cur < num_needed; cur++) { + if (ents[cur]->data == NULL) { + ents[cur]->data = (void *)malloc(bsize); + ents[cur]->bsize = bsize; + } + + if (ents[cur]->data == NULL) { + printf("cache: no memory for block (bsize %d)!\n", + bsize); + err = ENOMEM; + break; + } + } + + /* + if this condition is true it's a pretty serious error. + we'll try and back out gracefully but we're in pretty + deep at this point and it ain't going to be easy. + */ + handle_err: + if (err) { + for(cur=0; cur < num_needed; cur++) { + cache_ent *tmp_ce; + + tmp_ce = (cache_ent *)hash_delete(&bc.ht,dev,bnum+cur); + if (tmp_ce != ents[cur]) { + panic("hash_del0: %d %ld got 0x%lx, not 0x%lx\n", + dev, bnum+cur, (ulong)tmp_ce, + (ulong)ents[cur]); + } + + tmp_ce = (cache_ent *)hash_delete(&bc.ht,ents[cur]->dev, + ents[cur]->block_num); + if (tmp_ce != ents[cur]) { + panic("hash_del1: %d %ld got 0x%lx, not 0x%lx\n", + ents[cur]->dev, ents[cur]->block_num, (ulong)tmp_ce, + (ulong)ents[cur]); + } + + ents[cur]->flags &= ~CE_BUSY; + if (ents[cur]->data) + free(ents[cur]->data); + free(ents[cur]); + ents[cur] = NULL; + + bc.cur_blocks--; + } + + if (cur < real_nblocks) { + LOCK(bc.lock); + for(; cur < real_nblocks; cur++) { + ents[cur]->flags &= ~CE_BUSY; + + /* we have to put them back here */ + add_to_tail(&bc.normal, ents[cur]); + } + UNLOCK(bc.lock); + } + + return ENOMEM; + } + + + /* + If we go into this if statement, the block cache lock + has *already been released* up above when we flushed the + dirty entries. As always, since the blocks we're mucking + with are marked busy, they shouldn't get messed with. + */ + err = 0; + if (num_dirty || (op & CACHE_READ)) { + /* this section performs the i/o that we need to do */ + if (op & CACHE_READ) { + err = read_into_ents(dev, bnum, ents, num_needed, bsize); + } else { + err = 0; + } + + if (err != 0) { + printf("err %s on dev %d block %ld:%d (%d) " + "data 0x%x, ents[0] 0x%x\n", + strerror(errno), dev, bnum, cur_nblocks, + bsize, data, ents[0]); + } + + /* + acquire the semaphore here so that we can go on mucking + with the cache data structures. We need to delete old + block numbers from the hash table and set the new block + number's for the blocks we just read in. We also put the + read-ahead blocks at the head of mru list. + */ + + LOCK(bc.lock); + } + + for(cur=0; cur < num_needed; cur++) { + cache_ent *tmp_ce; + + ce = ents[cur]; + if (ce->dev != -1) { + tmp_ce = hash_delete(&bc.ht, ce->dev, ce->block_num); + if (tmp_ce == NULL || tmp_ce != ce) { + panic("*** hash_delete failure (ce 0x%x tce 0x%x)\n", + ce, tmp_ce); + } + } + + if (err == 0 && cur >= cur_nblocks) { + ce->dev = dev; + ce->block_num = bnum + cur; + ce->flags &= ~CE_BUSY; + add_to_head(&bc.normal, ce); + } + } + ce = NULL; + + /* + clear the busy bit on the blocks we force-flushed and + put them on the normal list since they're now clean. + */ + for(; cur < real_nblocks; cur++) { + ents[cur]->flags &= ~CE_BUSY; + + if (ents[cur]->lock) + panic("should not have locked blocks here (ce 0x%x)\n", + ents[cur]); + + add_to_tail(&bc.normal, ents[cur]); + } + + if (err) { /* then we have some cleanup to do */ + for(cur=0; cur < num_needed; cur++) { + cache_ent *tmp_ce; + + /* we delete all blocks from the cache so we don't + leave partially written blocks in the cache */ + + tmp_ce = (cache_ent *)hash_delete(&bc.ht,dev,bnum+cur); + if (tmp_ce != ents[cur]) { + panic("hash_del: %d %ld got 0x%lx, not 0x%lx\n", + dev, bnum+cur, (ulong)tmp_ce, + (ulong)ents[cur]); + } + + ce = ents[cur]; + ce->flags &= ~CE_BUSY; + + free(ce->data); + ce->data = NULL; + + free(ce); + ents[cur] = NULL; + + bc.cur_blocks--; + } + ce = NULL; + + UNLOCK(bc.lock); + return err; + } + + + /* + last step: go through and make sure all the cache_ent + structures have the right data in them, delete old guys, etc. + */ + for(cur=0; cur < cur_nblocks; cur++) { + ce = ents[cur]; + + if (ce->dev != -1) { /* then clean this guy up */ + if (ce->next || ce->prev) + panic("ce @ 0x%x should not be in a list yet!\n", ce); + + if (ce->clone) + free(ce->clone); + + if (ce->data == NULL) + panic("ce @ 0x%lx has a null data ptr\n", (ulong)ce); + } + + ce->dev = dev; + ce->block_num = bnum + cur; + ce->bsize = bsize; + ce->flags = CE_NORMAL; + ce->lock = 0; + ce->clone = NULL; + ce->func = ce->arg = NULL; + ce->next = ce->prev = NULL; + + if (op & CACHE_READ) { + if (data) + memcpy(data, ce->data, bsize); + } else if (op & CACHE_WRITE) { + ce->flags |= CE_DIRTY; + memcpy(ce->data, data, bsize); + } else if (op & CACHE_NOOP) { + memset(ce->data, 0, bsize); + if (data) + memset(data, 0, bsize); + + ce->flags |= CE_DIRTY; + } + + if (op & CACHE_LOCKED) { + ce->lock++; + cel = &bc.locked; + } else { + cel = &bc.normal; + } + + /* now stick this puppy at the head of the mru list */ + add_to_head(cel, ce); + + + if (dataptr) { + *dataptr = ce->data; + } + + if (data != NULL) + data = (void *)((char *)data + bsize); + else if (cur_nblocks != 1) + panic("cache can't handle setting data_ptr twice!\n"); + } /* end of for(cur=0; cur < cur_nblocks; cur++) */ + + bnum += cur_nblocks; + num_blocks -= cur_nblocks; + + } /* end of else it's not in the cache */ + + } /* end of while(num_blocks) */ + + UNLOCK(bc.lock); + + return 0; +} + + +void * +get_block(int dev, fs_off_t bnum, int bsize) +{ + void *data; + + if (cache_block_io(dev, bnum, NULL, 1, bsize, CACHE_READ|CACHE_LOCKED|CACHE_READ_AHEAD_OK, + &data) != 0) + return NULL; + + return data; +} + +void * +get_empty_block(int dev, fs_off_t bnum, int bsize) +{ + void *data; + + if (cache_block_io(dev, bnum, NULL, 1, bsize, CACHE_NOOP|CACHE_LOCKED, + &data) != 0) + return NULL; + + return data; +} + +int +cached_read(int dev, fs_off_t bnum, void *data, fs_off_t num_blocks, int bsize) +{ + return cache_block_io(dev, bnum, data, num_blocks, bsize, + CACHE_READ | CACHE_READ_AHEAD_OK, NULL); +} + + +int +cached_write(int dev, fs_off_t bnum, const void *data, fs_off_t num_blocks,int bsize) +{ + return cache_block_io(dev, bnum, (void *)data, num_blocks, bsize, + CACHE_WRITE, NULL); +} + +int +cached_write_locked(int dev, fs_off_t bnum, const void *data, + fs_off_t num_blocks, int bsize) +{ + return cache_block_io(dev, bnum, (void *)data, num_blocks, bsize, + CACHE_WRITE | CACHE_LOCKED, NULL); +} + + +void +force_cache_flush(int dev, int prefer_log_blocks) +{ + int i, count = 0; + cache_ent *ce; + cache_ent *ents[NUM_FLUSH_BLOCKS]; + + + LOCK(bc.lock); + + for(ce=bc.normal.lru; ce; ce=ce->next) { + if ((ce->dev == dev) && + (ce->flags & CE_BUSY) == 0 && + ((ce->flags & CE_DIRTY) || ce->clone) && + ((prefer_log_blocks && ce->func) || (prefer_log_blocks == 0))) { + + ce->flags |= CE_BUSY; + ents[count++] = ce; + + if (count >= NUM_FLUSH_BLOCKS) { + break; + } + } + } + + /* if we've got some room left, try and grab any cloned blocks */ + if (count < NUM_FLUSH_BLOCKS) { + for(ce=bc.locked.lru; ce; ce=ce->next) { + if ((ce->dev == dev) && + (ce->flags & CE_BUSY) == 0 && + (ce->clone)) { + + ce->flags |= CE_BUSY; + ents[count++] = ce; + + if (count >= NUM_FLUSH_BLOCKS) { + break; + } + } + } + } + + UNLOCK(bc.lock); + + if (count != 0) { + qsort(ents, count, sizeof(cache_ent **), cache_ent_cmp); + flush_ents(ents, count); + + for(i=0; i < count; i++) + ents[i]->flags &= ~CE_BUSY; + } +} diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/cache.h b/src/tests/add-ons/kernel/file_systems/fs_shell/cache.h new file mode 100644 index 0000000000..18c7b96469 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/cache.h @@ -0,0 +1,99 @@ +#ifndef _CACHE_H_ +#define _CACHE_H_ + +typedef struct hash_ent { + int dev; + fs_off_t bnum; + fs_off_t hash_val; + void *data; + struct hash_ent *next; +} hash_ent; + + +typedef struct hash_table { + hash_ent **table; + int max; + int mask; /* == max - 1 */ + int num_elements; +} hash_table; + + +#define HT_DEFAULT_MAX 128 + + +typedef struct cache_ent { + int dev; + fs_off_t block_num; + int bsize; + volatile int flags; + + void *data; + void *clone; /* copy of data by set_block_info() */ + int lock; + + void (*func)(fs_off_t bnum, size_t num_blocks, void *arg); + fs_off_t logged_bnum; + void *arg; + + struct cache_ent *next, /* points toward mru end of list */ + *prev; /* points toward lru end of list */ + +} cache_ent; + +#define CE_NORMAL 0x0000 /* a nice clean pristine page */ +#define CE_DIRTY 0x0002 /* needs to be written to disk */ +#define CE_BUSY 0x0004 /* this block has i/o happening, don't touch it */ + + +typedef struct cache_ent_list { + cache_ent *lru; /* tail of the list */ + cache_ent *mru; /* head of the list */ +} cache_ent_list; + + +typedef struct block_cache { + struct lock lock; + int flags; + int cur_blocks; + int max_blocks; + hash_table ht; + + cache_ent_list normal, /* list of "normal" blocks (clean & dirty) */ + locked; /* list of clean and locked blocks */ +} block_cache; + +#define ALLOW_WRITES 1 +#define NO_WRITES 0 + +extern int init_block_cache(int max_blocks, int flags); +extern void shutdown_block_cache(void); + +extern void force_cache_flush(int dev, int prefer_log_blocks); +extern int flush_blocks(int dev, fs_off_t bnum, int nblocks); +extern int flush_device(int dev, int warn_locked); + +extern int init_cache_for_device(int fd, fs_off_t max_blocks); +extern int remove_cached_device_blocks(int dev, int allow_write); + +extern void *get_block(int dev, fs_off_t bnum, int bsize); +extern void *get_empty_block(int dev, fs_off_t bnum, int bsize); +extern int release_block(int dev, fs_off_t bnum); +extern int mark_blocks_dirty(int dev, fs_off_t bnum, int nblocks); + + +extern int cached_read(int dev, fs_off_t bnum, void *data, + fs_off_t num_blocks, int bsize); +extern int cached_write(int dev, fs_off_t bnum, const void *data, + fs_off_t num_blocks, int bsize); +extern int cached_write_locked(int dev, fs_off_t bnum, const void *data, + fs_off_t num_blocks, int bsize); +extern int set_blocks_info(int dev, fs_off_t *blocks, int nblocks, + void (*func)(fs_off_t bnum, size_t nblocks, void *arg), + void *arg); + +extern size_t read_phys_blocks (int fd, fs_off_t bnum, void *data, + uint num_blocks, int bsize); +extern size_t write_phys_blocks(int fd, fs_off_t bnum, void *data, + uint num_blocks, int bsize); + +#endif /* _CACHE_H_ */ diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/compat.h b/src/tests/add-ons/kernel/file_systems/fs_shell/compat.h new file mode 100644 index 0000000000..6135a01702 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/compat.h @@ -0,0 +1,204 @@ +/* + This file contains some kit-wide typedefs and structs that basically + emulate most of a normal posix-y type system. The purpose of hiding + everything behind these typedefs is to avoid inconsistencies between + various systems (such as the difference in size between off_t on BeOS + and some versions of Unix). To further avoid complications I've also + hidden the stat and dirent structs since those vary even more widely. + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ + + +#ifndef _COMPAT_H +#define _COMPAT_H + + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __BEOS__ +#include /* for typedefs and prototypes */ +#include /* for a few typedefs */ +#include /* for various ioctl structs, etc */ +#include /* because we're boneheads sometimes */ +#else +#include +#endif + + +/* + By default (for portability reasons) the size of off_t's and ino_t's + is 32-bit. You can change the file system to be 64-bit if you want + by defining OFF_T_SIZE to be 8. + + NOTE: if you change the size of OFF_T_SIZE to be 8 you will have to + go through the code and change any calls to printf() to use the + appropriate format for 64-bit integers on your OS. I have seen + 4 different formats now: %Ld (BeOS and Linux), %qd (FreeBSD), + %lld (Irix) and %I64d (NT). +*/ +#define OFF_T_SIZE 8 + +#if OFF_T_SIZE == 4 +typedef long fs_off_t; +typedef long my_ino_t; +#elif OFF_T_SIZE == 8 +typedef long long fs_off_t; +typedef long long my_ino_t; +#else +#error OFF_T_SIZE must be either 4 or 8. +#endif + +typedef int my_dev_t; +typedef int my_mode_t; +typedef int my_uid_t; +typedef int my_gid_t; + +/* This is the maximum length of a file name. Adjust it as you see fit */ +#define FILE_NAME_LENGTH 256 + +/* This is maximum name size for naming a volume or semaphore/lock */ +#define IDENT_NAME_LENGTH 32 + + +typedef struct my_dirent { + my_dev_t d_dev; + my_ino_t d_ino; + unsigned short d_reclen; + char d_name[1]; +} my_dirent_t; + +typedef struct { + int fd; + struct my_dirent ent; +} MY_DIR; + + +/* + This is a pretty regular stat structure but it's our "internal" + version since if we depended on the host version we'd be exposed + to all sorts of nasty things (different sized ino_t's, etc). + We also can't use the normal naming style of "st_" for each field + name because on some systems fields like st_atime are really just + define's that expand to all sorts of weird stuff. +*/ +struct my_stat { + my_dev_t dev; /* "device" that this file resides on */ + my_ino_t ino; /* this file's inode #, unique per device */ + my_mode_t mode; /* mode bits (rwx for user, group, etc) */ + int nlink; /* number of hard links to this file */ + my_uid_t uid; /* user id of the owner of this file */ + my_gid_t gid; /* group id of the owner of this file */ + fs_off_t size; /* size in bytes of this file */ + size_t blksize; /* preferred block size for i/o */ + time_t atime; /* last access time */ + time_t mtime; /* last modification time */ + time_t ctime; /* last change time, not creation time */ + time_t crtime; /* creation time; not posix but useful */ +}; + + +#define MY_S_IFMT 00000170000 /* type of file */ +#define MY_S_IFLNK 00000120000 /* symbolic link */ +#define MY_S_IFREG 00000100000 /* regular */ +#define MY_S_IFBLK 00000060000 /* block special */ +#define MY_S_IFDIR 00000040000 /* directory */ +#define MY_S_IFCHR 00000020000 /* character special */ +#define MY_S_IFIFO 00000010000 /* fifo */ + +#define MY_S_ISREG(m) (((m) & MY_S_IFMT) == MY_S_IFREG) +#define MY_S_ISLNK(m) (((m) & MY_S_IFMT) == MY_S_IFLNK) +#define MY_S_ISBLK(m) (((m) & MY_S_IFMT) == MY_S_IFBLK) +#define MY_S_ISDIR(m) (((m) & MY_S_IFMT) == MY_S_IFDIR) +#define MY_S_ISCHR(m) (((m) & MY_S_IFMT) == MY_S_IFCHR) +#define MY_S_ISFIFO(m) (((m) & MY_S_IFMT) == MY_S_IFIFO) + +#define MY_S_IUMSK 07777 /* user settable bits */ + +#define MY_S_ISUID 04000 /* set user id on execution */ +#define MY_S_ISGID 02000 /* set group id on execution */ + +#define MY_S_ISVTX 01000 /* save swapped text even after use */ + +#define MY_S_IRWXU 00700 /* read, write, execute: owner */ +#define MY_S_IRUSR 00400 /* read permission: owner */ +#define MY_S_IWUSR 00200 /* write permission: owner */ +#define MY_S_IXUSR 00100 /* execute permission: owner */ +#define MY_S_IRWXG 00070 /* read, write, execute: group */ +#define MY_S_IRGRP 00040 /* read permission: group */ +#define MY_S_IWGRP 00020 /* write permission: group */ +#define MY_S_IXGRP 00010 /* execute permission: group */ +#define MY_S_IRWXO 00007 /* read, write, execute: other */ +#define MY_S_IROTH 00004 /* read permission: other */ +#define MY_S_IWOTH 00002 /* write permission: other */ +#define MY_S_IXOTH 00001 /* execute permission: other */ + + + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef __BEOS__ +typedef long sem_id; +typedef unsigned char uchar; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +#define ulong unsigned long /* make it a #define to avoid conflicts */ +typedef long long int64; +typedef unsigned long long uint64; +typedef unsigned int port_id; +typedef int bool; +typedef int image_id; +typedef long long bigtime_t; +typedef long thread_id; +typedef long status_t; + +sem_id create_sem(long count, const char *name); +long delete_sem(sem_id sem); +long acquire_sem(sem_id sem); +long acquire_sem_etc(sem_id sem, int count, int flags, + bigtime_t microsecond_timeout); +long release_sem(sem_id sem); +long release_sem_etc(sem_id sem, long count, long flags); + +long atomic_add(long *value, long addvalue); +int snooze(bigtime_t f); +bigtime_t system_time(void); +ssize_t read_pos(int fd, fs_off_t _pos, void *data, size_t nbytes); +ssize_t write_pos(int fd, fs_off_t _pos, const void *data, size_t nbytes); +ssize_t readv_pos(int fd, fs_off_t _pos, struct iovec *iov, int count); +ssize_t writev_pos(int fd, fs_off_t _pos, struct iovec *iov, int count); + + +#endif /* __BEOS__ */ + +void panic(const char *msg, ...); +int device_is_read_only(const char *device); +int get_device_block_size(int fd); +fs_off_t get_num_device_blocks(int fd); +int device_is_removeable(int fd); +int lock_removeable_device(int fd, bool on_or_off); +void hexdump(void *address, int size); + + +#endif /* _COMPAT_H */ diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/fsh.c b/src/tests/add-ons/kernel/file_systems/fs_shell/fsh.c new file mode 100644 index 0000000000..fdcf3483f9 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/fsh.c @@ -0,0 +1,1460 @@ +/* + This file contains a simple file system "shell" that lets you + manipulate a file system. There is a simple command table that + contains the available functions. It is very easy to extend. + + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ +#include +#include +#include +#include +#include + +#include "myfs.h" +#include "kprotos.h" +#include "argv.h" + +#include + +static void do_lat_fs(int argc, char **argv); +static void do_fsh(void); + +int +main(int argc, char **argv) +{ + int seed; + char *disk_name = "big_file"; + myfs_info *myfs; + + if (argv[1] != NULL && !isdigit(argv[1][0])) + disk_name = argv[1]; + else if (argv[1] && isdigit(argv[1][0])) + seed = strtoul(argv[1], NULL, 0); + else + seed = getpid() * time(NULL) | 1; + printf("random seed == 0x%x\n", seed); + + srand(seed); + + myfs = init_fs(disk_name); + + do_fsh(); + + + if (sys_unmount(1, -1, "/myfs") != 0) { + printf("could not un-mount /myfs\n"); + return 5; + } + + shutdown_block_cache(); + + return 0; +} + + +static void +make_random_name(char *buf, int len) +{ + int i, max = (rand() % (len - 5 - 3)) + 2; + + strcpy(buf, "/myfs/"); + for(i=0; i < max; i++) { + buf[i+6] = 'a' + (rand() % 26); + } + + buf[i] = '\0'; +} + + +static void +SubTime(struct timeval *a, struct timeval *b, struct timeval *c) +{ + if ((long)(a->tv_usec - b->tv_usec) < 0) + { + a->tv_sec--; + a->tv_usec += 1000000; + } + + c->tv_sec = a->tv_sec - b->tv_sec; + c->tv_usec = a->tv_usec - b->tv_usec; +} + + + +int cur_fd = -1; + + +static void +do_close(int argc, char **argv) +{ + int err; + + err = sys_close(1, cur_fd); +/* printf("close of fd %d returned: %d\n", cur_fd, err); */ + cur_fd = -1; +} + +static void +do_open(int argc, char **argv) +{ + int err; + char name[64], buff[64]; + + if (cur_fd >= 0) + do_close(0, NULL); + + if (argc < 2) + make_random_name(name, sizeof(name)); + else + sprintf(name, "/myfs/%s", &argv[1][0]); + + cur_fd = sys_open(1, -1, name, O_RDWR, MY_S_IFREG, 0); + if (cur_fd < 0) + printf("error opening %s : %s (%d)\n", name, strerror(cur_fd), cur_fd); + else + printf("opened: %s\n", name); +} + +static void +do_make(int argc, char **argv) +{ + int err; + char name[64], buff[64]; + + if (cur_fd >= 0) + do_close(0, NULL); + + if (argc < 2) + make_random_name(name, sizeof(name)); + else if (argv[1][0] != '/') + sprintf(name, "/myfs/%s", &argv[1][0]); + else + strcpy(name, &argv[1][0]); + + cur_fd = sys_open(1, -1, name, O_RDWR|O_CREAT, 0666, 0); + if (cur_fd < 0) { + printf("error creating: %s: %s\n", name, strerror(cur_fd)); + return; + } + +/* printf("created: %s (fd %d)\n", name, cur_fd); */ + +} + + + +static void +do_mkdir(int argc, char **argv) +{ + int err; + char name[64], buff[64]; + + if (argc < 2) + make_random_name(name, sizeof(name)); + else + sprintf(name, "/myfs/%s", &argv[1][0]); + + err = sys_mkdir(1, -1, name, MY_S_IRWXU); + if (err) + printf("mkdir of %s returned: %s (%d)\n", name, strerror(err), err); +} + + +static void +do_read_test(int argc, char **argv) +{ + int i, err; + char *buff; + size_t len = 256; + + + if (cur_fd < 0) { + printf("no file open! (open or create one with open or make)\n"); + return; + } + + if (argv[1] && isdigit(argv[1][0])) + len = strtoul(&argv[1][0], NULL, 0); + + buff = malloc(len); + if (buff == NULL) { + printf("no memory for write buffer of %d bytes\n", len); + return; + } + + for(i=0; i < len; i++) + buff[i] = (char)0xff; + + err = sys_read(1, cur_fd, buff, len); + + if (len < 512) + hexdump(buff, len); + else + hexdump(buff, 512); + + free(buff); + printf("read read %d bytes and returned %d\n", len, err); +} + + +static void +do_write_test(int argc, char **argv) +{ + int i, err; + char *buff; + size_t len = 256; + + if (cur_fd < 0) { + printf("no file open! (open or create one with open or make)\n"); + return; + } + + if (argv[1] && isdigit(argv[1][0])) + len = strtoul(&argv[1][0], NULL, 0); + + buff = malloc(len); + if (buff == NULL) { + printf("no memory for write buffer of %d bytes\n", len); + return; + } + + for(i=0; i < len; i++) + buff[i] = i; + + err = sys_write(1, cur_fd, buff, len); + free(buff); + + printf("write wrote %d bytes and returned %d\n", len, err); +} + + +static void +do_write_stream(int argc, char **argv) +{ + size_t amount = 100000; + char buffer[4096]; + int length = sizeof(buffer); + int i, err; + + if (cur_fd < 0) { + printf("no file open! (open or create one with open or make)\n"); + return; + } + + if (argv[1] && isdigit(argv[1][0])) + amount = strtoul(&argv[1][0], NULL, 0); + + for(i = 0;i < sizeof(buffer);i++) + buffer[i] = i; + + for (i = 0;i < amount;i++) { + err = sys_write(1, cur_fd, buffer, length); + if (err < B_OK) + break; + } + + printf("write wrote %d bytes and returned %d\n", length, err); +} + + +static void +do_read_attr(int argc, char **argv) +{ + char *attribute,*buffer; + size_t len = 256; + int i, err; + + if (cur_fd < 0) { + printf("no file open! (open or create one with open or make)\n"); + return; + } + + if (argc < 2) { + printf("usage: rdattr [bytes to write]\n"); + return; + } + + attribute = argv[1]; + if (argv[2] && isdigit(*argv[2])) + len = strtoul(argv[2], NULL, 0); + if (len < 0) { + printf("invalid length for write attribute!\n"); + return; + } + + buffer = malloc(len); + if (buffer == NULL) { + printf("no memory for write buffer of %d bytes\n", len); + return; + } + + for (i = 0;i < len;i++) + buffer[i] = (char)0xff; + + err = sys_read_attr(1, cur_fd, attribute, 'CSTR', buffer, len, 0); + + if (err >= 0) + hexdump(buffer, err < 512 ? err : 512); + + free(buffer); + printf("read read %d bytes and returned %d (%s)\n", len, err, strerror(err)); +} + + +static void +do_write_attr(int argc, char **argv) +{ + char *attribute,*buffer; + size_t len = 256; + int i, err; + + if (cur_fd < 0) { + printf("no file open! (open or create one with open or make)\n"); + return; + } + + if (argc < 2) { + printf("usage: wrattr [bytes to write]\n"); + return; + } + + attribute = argv[1]; + if (argv[2] && isdigit(*argv[2])) + len = strtoul(argv[2], NULL, 0); + if (len < 0) { + printf("invalid length for write attribute!\n"); + return; + } + + buffer = malloc(len); + if (buffer == NULL) { + printf("no memory for write buffer of %d bytes\n", len); + return; + } + + for (i = 0;i < len;i++) + buffer[i] = i; + + err = sys_write_attr(1, cur_fd, attribute, 'CSTR', buffer, len, 0); + free(buffer); + + printf("write wrote %d bytes and returned %d\n", len, err); +} + + +static void +do_remove_attr(int argc, char **argv) +{ + int err; + + if (cur_fd < 0) { + printf("no file open! (open or create one with open or make)\n"); + return; + } + + if (argc < 2) { + printf("usage: rmattr \n"); + return; + } + + err = sys_remove_attr(1, cur_fd, argv[1]); + + printf("remove_attr returned %d\n", err); +} + + +static void +mode_bits_to_str(int mode, char *str) +{ + int i; + + strcpy(str, "----------"); + + if (MY_S_ISDIR(mode)) + str[0] = 'd'; + else if (MY_S_ISLNK(mode)) + str[0] = 'l'; + else if (MY_S_ISBLK(mode)) + str[0] = 'b'; + else if (MY_S_ISCHR(mode)) + str[0] = 'c'; + + for(i=7; i > 0; i-=3, mode >>= 3) { + if (mode & MY_S_IROTH) + str[i] = 'r'; + if (mode & MY_S_IWOTH) + str[i+1] = 'w'; + if (mode & MY_S_IXOTH) + str[i+2] = 'x'; + } +} + + +static void +do_dir(int argc, char **argv) +{ + int dirfd, err, fd, max_err = 10; + char dirname[128], buff[512], time_buf[64] = { '\0', }; + size_t len,count = 0; + struct my_dirent *dent; + struct my_stat st; + struct tm *tm; + char mode_str[16]; + + dent = (struct my_dirent *)buff; + + strcpy(dirname, "/myfs/"); + if (argc > 1) + strcat(dirname, &argv[1][0]); + + if ((dirfd = sys_opendir(1, -1, dirname, 0)) < 0) { + printf("dir: error opening: %s\n", dirname); + return; + } + + printf("Directory listing for: %s\n", dirname); + printf(" inode# mode bits uid gid size " + "Date Name\n"); + + while (1) { + len = 1; + err = sys_readdir(1, dirfd, dent, sizeof(buff), len); + if (err < 0) { + printf("readdir failed for: %s\n", dent->d_name); + if (max_err-- <= 0) + break; + + continue; + } + + if (err == 0) + break; + + + err = sys_rstat(1, dirfd, dent->d_name, &st, 1); + if (err != 0) { + printf("stat failed for: %s (%ld)\n", dent->d_name, dent->d_ino); + if (max_err-- <= 0) + break; + + continue; + } + + tm = localtime(&st.st_mtime); + strftime(time_buf, sizeof(time_buf), "%b %d %I:%M", tm); + + mode_bits_to_str(st.st_mode, mode_str); + + printf("%12Ld %s %6d %6d %12Ld %s %s\n", st.st_ino, mode_str, + st.st_uid, st.st_gid, st.st_size, time_buf, dent->d_name); + count++; + } + + if (err != 0) { + printf("readdir failed on: %s\n", dent->d_name); + } + printf("%ld files in directory!\n",count); + + sys_closedir(1, dirfd); +} + + +#include "bfs_control.h" + +static void +do_chkbfs(int argc, char **argv) +{ + struct check_control result; + off_t files = 0, directories = 0, indices = 0, attributeDirectories = 0, attributes = 0; + int counter = 0; + + int fd = sys_open(1, -1, "/myfs/.", O_RDONLY, S_IFREG, 0); + if (fd < 0) { + printf("chkbfs: error opening '.'\n"); + return; + } + + memset(&result, 0, sizeof(result)); + result.flags = argc > 1 ? BFS_FIX_BITMAP_ERRORS : 0; + if (argc > 2) { + printf("will fix any severe errors!\n"); + result.flags |= BFS_REMOVE_WRONG_TYPES | BFS_REMOVE_INVALID; + } + + // start checking + if ((sys_ioctl(1, fd, BFS_IOCTL_START_CHECKING, &result, sizeof(result))) < 0) { + printf("chkbfs: error starting!\n"); + } + + // check all files and report errors + while (sys_ioctl(1, fd, BFS_IOCTL_CHECK_NEXT_NODE, &result, sizeof(result)) == B_OK) { + if (++counter % 50 == 0) + printf(" %7ld nodes processed\x1b[1A\n", counter); + + if (result.errors) { + printf("%s (inode = %Ld)", result.name, result.inode); + if (result.errors & BFS_MISSING_BLOCKS) + printf(", some blocks weren't allocated"); + if (result.errors & BFS_BLOCKS_ALREADY_SET) + printf(", has blocks already set"); + if (result.errors & BFS_INVALID_BLOCK_RUN) + printf(", has invalid block run(s)"); + if (result.errors & BFS_COULD_NOT_OPEN) + printf(", could not be opened"); + if (result.errors & BFS_WRONG_TYPE) + printf(", has wrong type"); + if (result.errors & BFS_NAMES_DONT_MATCH) + printf(", names don't match"); + putchar('\n'); + } + if ((result.mode & (S_INDEX_DIR | 0777)) == S_INDEX_DIR) + indices++; + else if (result.mode & S_ATTR_DIR) + attributeDirectories++; + else if (result.mode & S_ATTR) + attributes++; + else if (result.mode & S_IFDIR) + directories++; + else + files++; + } + if (result.status != B_ENTRY_NOT_FOUND) + printf("chkbfs: error occured during scan: %s\n", strerror(result.status)); + + // stop checking + if ((sys_ioctl(1, fd, BFS_IOCTL_STOP_CHECKING, &result, sizeof(result))) < 0) { + printf("chkbfs: error stopping!\n"); + } + + printf("checked %ld nodes, %Ld blocks not allocated, %Ld blocks already set, %Ld blocks could be freed\n", + counter, result.stats.missing, result.stats.already_set, result.stats.freed); + printf("\tfiles\t\t%Ld\n\tdirectories\t%Ld\n\tattributes\t%Ld\n\tattr. dirs\t%Ld\n\tindices\t\t%Ld\n", + files, directories, attributes, attributeDirectories, indices); + if (result.flags & BFS_FIX_BITMAP_ERRORS) + printf("errors have been fixed\n"); + + sys_close(1, fd); +} + +static void +do_ioctl(int argc, char **argv) +{ + int fd; + + if (argc < 2) { + printf("ioctl: too few arguments\n"); + return; + } + + if ((fd = sys_open(1, -1, "/myfs/.", O_RDONLY, S_IFREG, 0)) < 0) { + printf("ioctl: error opening '.'\n"); + return; + } + + if ((sys_ioctl(1, fd, atoi(argv[1]), 0, 0)) < 0) { + printf("ioctl: error!\n"); + } + + sys_close(1, fd); +} + + +static void +do_fcntl(int argc, char **argv) +{ + if (cur_fd < 0) { + printf("no file open! (open or create one with open or make)\n"); + return; + } + + if (argc < 2) { + printf("fcntl: too few arguments\n"); + return; + } + + if ((sys_ioctl(1, cur_fd, atoi(argv[1]), 0, 0)) < 0) { + printf("fcntl: error!\n"); + } +} + + +static void +do_rmall(int argc, char **argv) +{ + int dirfd, err, fd, max_err = 10; + char dirname[128], fname[512], buff[512]; + size_t len,count = 0; + struct my_dirent *dent; + struct my_stat st; + struct tm *tm; + + dent = (struct my_dirent *)buff; + + strcpy(dirname, "/myfs/"); + if (argc > 1) + strcat(dirname, &argv[1][0]); + + if ((dirfd = sys_opendir(1, -1, dirname, 0)) < 0) { + printf("dir: error opening: %s\n", dirname); + return; + } + + while(1) { + len = 1; + err = sys_readdir(1, dirfd, dent, sizeof(buff), len); + if (err < 0) { + printf("readdir failed for: %s\n", dent->d_name); + if (max_err-- <= 0) + break; + + continue; + } + + if (err == 0) + break; + + if (strcmp(dent->d_name, "..") == 0 || strcmp(dent->d_name, ".") == 0) + continue; + + sprintf(fname, "%s/%s", dirname, dent->d_name); + err = sys_unlink(1, -1, fname); + if (err != 0) { + printf("unlink failed for: %s (%ld)\n", fname, dent->d_ino); + } else + count++; + } + + if (err != 0) { + printf("readdir failed on: %s\n", dent->d_name); + } + printf("%ld files removed!n",count); + + sys_closedir(1, dirfd); +} + + + +static void +do_trunc(int argc, char **argv) +{ + int err, new_size; + char fname[256]; + struct my_stat st; + + strcpy(fname, "/myfs/"); + if (argc < 3) { + printf("usage: trunc fname newsize\n"); + return; + } + strcat(fname, argv[1]); + new_size = strtoul(argv[2], NULL, 0); + + st.st_size = new_size; + err = sys_wstat(1, -1, fname, &st, WSTAT_SIZE, 0); + if (err != 0) { + printf("truncate to %d bytes failed for %s\n", new_size, fname); + } +} + + + + +static void +do_seek(int argc, char **argv) +{ + fs_off_t err; + fs_off_t pos; + + if (cur_fd < 0) { + printf("no file open! (open or create one with open or make)\n"); + return; + } + + + if (argc < 2) { + printf("usage: seek pos\n"); + return; + } + pos = strtoul(&argv[1][0], NULL, 0); + + err = sys_lseek(1, cur_fd, pos, SEEK_SET); + if (err != pos) { + printf("seek to %ld failed (%ld)\n", pos, err); + } +} + + +static void +do_rm(int argc, char **argv) +{ + int err; + char name[256], buff[256]; + + if (cur_fd >= 0) + do_close(0, NULL); + + if (argc < 2) { + printf("rm: need a file name to remove\n"); + return; + } + + sprintf(name, "/myfs/%s", &argv[1][0]); + + err = sys_unlink(1, -1, name); + if (err != 0) { + printf("error removing: %s: %s\n", name, strerror(err)); + return; + } +} + + +static void +do_rmdir(int argc, char **argv) +{ + int err; + char name[256], buff[256]; + + if (cur_fd >= 0) + do_close(0, NULL); + + if (argc < 2) { + printf("rm: need a file name to remove\n"); + return; + } + + sprintf(name, "/myfs/%s", &argv[1][0]); + + err = sys_rmdir(1, -1, name); + if (err != 0) { + printf("rmdir: error removing: %s: %s\n", name, strerror(err)); + return; + } +} + + +static void +do_copy_to_myfs(char *host_file, char *bfile) +{ + int bfd, err = 0; + char myfs_name[128]; + FILE *fp; + size_t amt; + static char buff[4 * 1024]; + + sprintf(myfs_name, "/myfs/%s", bfile); + + fp = fopen(host_file, "rb"); + if (fp == NULL) { + printf("can't open host file: %s\n", host_file); + return; + } + + if ((bfd = sys_open(1, -1, myfs_name, O_RDWR|O_CREAT, + MY_S_IFREG|MY_S_IRWXU, 0)) < 0) { + fclose(fp); + printf("error opening: %s\n", myfs_name); + return; + } + + while((amt = fread(buff, 1, sizeof(buff), fp)) == sizeof(buff)) { + err = sys_write(1, bfd, buff, amt); + if (err < 0) + break; + } + + if (amt && err >= 0) { + err = sys_write(1, bfd, buff, amt); + } + + if (err < 0) { + printf("err == %d, amt == %d\n", err, amt); + perror("write error"); + } + + sys_close(1, bfd); + fclose(fp); +} + + +static void +do_copy_from_myfs(char *bfile, char *host_file) +{ + int bfd,err = 0; + char myfs_name[128]; + FILE *fp; + size_t amt; + static char buff[4 * 1024]; + + sprintf(myfs_name, "/myfs/%s", bfile); + + fp = fopen(host_file, "wb"); + if (fp == NULL) { + printf("can't open host file: %s\n", host_file); + return; + } + + if ((bfd = sys_open(1, -1, myfs_name, O_RDONLY, MY_S_IFREG, 0)) < 0) { + fclose(fp); + printf("error opening: %s\n", myfs_name); + return; + } + + while(1) { + amt = sizeof(buff); + err = sys_read(1, bfd, buff, amt); + if (err < 0) + break; + + if (fwrite(buff, 1, err, fp) != amt) + break; + } + + + if (err < 0) + perror("read error"); + + sys_close(1, bfd); + fclose(fp); +} + + +static void +do_copy(int argc, char **argv) +{ + char name1[128], name2[128]; + + if (cur_fd >= 0) + do_close(0, NULL); + + if (argc < 3) { + printf("copy needs two arguments!\n"); + return; + } + + if (argv[1][0] == ':' && argv[2][0] != ':') { + do_copy_to_myfs(&argv[1][1], &argv[2][0]); + return; + } + + if (argv[2][0] == ':' && argv[1][0] != ':') { + do_copy_from_myfs(&argv[1][0], &argv[2][1]); + return; + } + + printf("can't copy around inside of the file system (only in and out)\n"); +} + + +static int +copydir(char *fromPath,char *toPath) +{ + int bfd, err = 0; + char *myfs_name; //[1024]; + char *from_name; //[1024]; + int fd; + size_t amt; + char *buff; //[4 * 1024]; + size_t bufferSize = 4 * 1024; + DIR *from; + dirent_t *dirent; + + from = opendir(fromPath); + if (from == NULL) { + printf("could not open %s\n",fromPath); + return; + } + + myfs_name = malloc(1024); from_name = malloc(1024); buff = malloc(bufferSize); + if (myfs_name == NULL || from_name == NULL || buff == NULL) { + printf("out of memory\n"); + return; + } + + while ((dirent = readdir(from)) != NULL) { + DIR *attrDirectory; + dirent_t *attr; + struct stat st; + + if (!strcmp(dirent->d_name,".") || !strcmp(dirent->d_name,"..")) + continue; + + strcpy(from_name, fromPath); + if (from_name[strlen(from_name) - 1] != '/') + strcat(from_name,"/"); + strcat(from_name, dirent->d_name); + + if (stat(from_name,&st) != 0) + continue; + + if (st.st_mode & S_IFDIR) { + char path[1024]; + strcpy(path,toPath); + strcat(path,"/"); + strcat(path,dirent->d_name); + + if ((err = sys_mkdir(1, -1, path, MY_S_IRWXU)) == B_OK) + copydir(from_name,path); + else + printf("Could not create directory %s: (%s)\n",path,strerror(err)); + } else { + fd = open(from_name, O_RDONLY); + if (fd < 0) { + printf("can't open host file: %s\n", from_name); + return; + } + + sprintf(myfs_name, "%s/%s", toPath,dirent->d_name); + if ((bfd = sys_open(1, -1, myfs_name, O_RDWR|O_CREAT, + MY_S_IFREG|MY_S_IRWXU, 0)) < 0) { + close(fd); + printf("error opening: %s\n", myfs_name); + return; + } + + // copy attributes first! + if ((attrDirectory = fs_fopen_attr_dir(fd)) != NULL) { + while ((attr = fs_read_attr_dir(attrDirectory)) != NULL) { + struct attr_info attrInfo; + int32 size = bufferSize, bytesRead; + + if (fs_stat_attr(fd,attr->d_name,&attrInfo) != 0) + continue; + + if (attrInfo.size <= size) + size = attrInfo.size - 1; + else + printf("truncating attribute: %s\n", attr->d_name); + + bytesRead = fs_read_attr(fd, attr->d_name, attrInfo.type, 0, buff, size); + if (bytesRead < size) { + printf("could not read attribute %s: %s\n", attr->d_name, strerror(bytesRead)); + continue; + } + buff[size] = '\0'; + + err = sys_write_attr(1, bfd, attr->d_name, attrInfo.type, buff, size, 0); + if (err < B_OK) { + printf("write attr failed: %s\n",strerror(err)); + continue; + } + } + fs_close_attr_dir(attrDirectory); + } else + puts("could not open attr-dir"); + + while ((amt = read(fd, buff, bufferSize)) == bufferSize) { + err = sys_write(1, bfd, buff, amt); + if (err < 0) + break; + } + + if (amt && err >= 0) { + err = sys_write(1, bfd, buff, amt); + } + + if (err < 0) { + printf("write error: err == %d, amt == %d\n", err, amt); + } + + sys_close(1, bfd); + close(fd); + } + } + closedir(from); + + free(myfs_name); free(from_name); free(buff); +} + + +static void +do_copytest(int argc, char **argv) +{ + char *fromPath = "/boot/apps/internet/mozilla"; + + if (argc > 2) { + printf("usage: copytest "); + return; + } else if (argc == 2) + fromPath = argv[1]; + else + printf("copying from: %s\n",fromPath); + + copydir(fromPath,"/myfs"); +} + + +static int32 +copydirthread(void *data) +{ + char *args[] = {"cptest",(char *)data,NULL}; + char stack_filler[256*1024 - 0 * 1024]; + + do_copytest(2,args); + return 0; +} + + +static void +do_threadtest(int argc, char **argv) +{ + char *paths[] = {"/boot/home/mail/pinc - axeld/in","/boot/home/mail/pinc - axeld/out",NULL}; + int32 i; + + for (i = 0;paths[i] != NULL;i++) { + thread_id thread = spawn_thread(copydirthread,"copythread",B_NORMAL_PRIORITY,paths[i]); + resume_thread(thread); + } + + //do_lat_fs(0,NULL); +} + + +int32 gCopyNum; + + +static int32 +copyfilethread(void *data) +{ + char *args[] = {"cp",(char *)data,NULL,NULL}; + + char name[32]; + sprintf(name,"target%ld",gCopyNum++); + args[2] = name; + + do_copy(3,args); + return 0; +} + + +static void +do_threadfiletest(int argc, char **argv) +{ + char *paths[] = {":/video/Filme/Wallace & Gromit - A Close Shave.mpg",":/video/Filme/Wallace & Gromit - The Wrong Trousers.mpg",":/stuff/Simpsons/The Simpsons - 13.01 - Treehouse Of Horror XII.mpg",NULL}; + int32 i; + + for (i = 0;paths[i] != NULL;i++) { + thread_id thread = spawn_thread(copyfilethread,"copythread",B_NORMAL_PRIORITY,paths[i]); + resume_thread(thread); + } + + do_lat_fs(0,NULL); +} + + +static void +do_attrtest(int argc, char **argv) +{ + int iterations = 10240; + int maxSize = 1024; + char *buffer; + char name[2]; + int i; + + if (cur_fd < 0) { + printf("no file open! (open or create one with open or make)\n"); + return; + } + if (argc > 1 && isdigit(*argv[1])) + iterations = atol(argv[1]); + if (argc > 2 && isdigit(*argv[2])) + maxSize = atol(argv[2]); + if (argc > 1 && !isdigit(*argv[1]) || argc > 2 && !isdigit(*argv[2])) { + printf("usage: attrs [number of iterations] [max attr size]\n"); + return; + } + + buffer = malloc(1024); + for (i = 0; i < 1024; i++) + buffer[i] = i; + + name[1] = '\0'; + + printf("%d iterations...\n", iterations); + + for (i = 0; i < iterations; i++) { + int length = rand() % maxSize; + int err; + + name[0] = rand() % 26 + 'a'; + + err = sys_write_attr(1, cur_fd, name, 'CSTR', buffer, length, 0); + if (err < length) + printf("error writing attribute: %s\n", strerror(err)); + } + + free(buffer); +} + + +static void +do_rename(int argc, char **argv) +{ + int err; + char oldname[128], newname[128]; + + if (cur_fd >= 0) + do_close(0, NULL); + + if (argc < 3) { + printf("rename needs two arguments!\n"); + return; + } + + strcpy(oldname, "/myfs/"); + strcpy(newname, "/myfs/"); + + strcat(oldname, &argv[1][0]); + strcat(newname, &argv[2][0]); + + err = sys_rename(1, -1, oldname, -1, newname); + if (err) + printf("rename failed with err: %s\n", strerror(err)); +} + + +static void +do_sync(int argc, char **argv) +{ + int err; + + err = sys_sync(); + + if (err) + printf("sync failed with err %s\n", strerror(err)); +} + + +static void +do_query(int argc, char **argv) +{ + char buffer[2048]; + struct dirent *dent = (void *)buffer; + void *cookie; + char *query; + int max_err = 10; + int err; + + if (argc != 2) { + printf("query string expected"); + return; + } + query = argv[1]; + + err = sys_open_query(true,-1,"/myfs/.",query,&cookie); + if (err < 0) { + printf("could not open query: %s\n",strerror(err)); + return; + } + + while (true) { + err = sys_read_query(true,-1,"/myfs/.",cookie, dent, sizeof(buffer), 1); + if (err < 0) { + printf("readdir failed for: %s\n", dent->d_name); + if (max_err-- <= 0) + break; + + continue; + } + + if (err == 0) + break; + + printf("%s\n",dent->d_name); + } + + err = sys_close_query(true,-1,"/myfs/.",cookie); + if (err < 0) { + printf("could not close query: %s\n",strerror(err)); + return; + } +} + + +#define MAX_ITER 512 +#define NUM_READS 16 +#define READ_SIZE 4096 + +static void +do_cio(int argc, char **argv) +{ + int i, j, fd, err; + char fname[64]; + fs_off_t pos; + size_t len; + static char buff[READ_SIZE]; + struct timeval start, end, result; + + strcpy(fname, "/myfs/"); + if (argc == 1) + strcpy(fname, "/myfs/fsh"); + else + strcat(fname, &argv[1][0]); + + fd = sys_open(1, -1, fname, O_RDONLY, MY_S_IFREG, 0); + if (fd < 0) { + printf("can't open %s\n", fname); + return; + } + + gettimeofday(&start, NULL); + + for(i=0; i < MAX_ITER; i++) { + for(j=0; j < NUM_READS; j++) { + len = sizeof(buff); + if (sys_read(1, fd, buff, len) != len) { + perror("cio read"); + break; + } + } + + pos = 0; + if (sys_lseek(1, fd, pos, SEEK_SET) != pos) { + perror("cio lseek"); + break; + } + } + + gettimeofday(&end, NULL); + SubTime(&end, &start, &result); + + printf("read %d bytes in %2ld.%.6ld seconds\n", + (MAX_ITER * NUM_READS * sizeof(buff)) / 1024, + result.tv_sec, result.tv_usec); + + + sys_close(1, fd); +} + + + +static void +mkfile(char *s, int sz) +{ + int fd, len; + char *buffer; + + if ((fd = sys_open(1, -1, s, O_RDWR|O_CREAT, + MY_S_IFREG|MY_S_IRWXU, 0)) < 0) { + printf("error creating: %s\n", s); + return; + } + + buffer = malloc(16 * 1024); + if (buffer == NULL) + return; + + len = sz; + if (sz) { + if (sys_write(1, fd, buffer, len) != len) + printf("error writing %d bytes to %s\n", sz, s); + } + if (sys_close(1, fd) != 0) + printf("close failed?\n"); + + free(buffer); +} + +#define LAT_FS_ITER 1000 + + +static void +do_lat_fs(int argc, char **argv) +{ + int i, j, iter; +/* int sizes[] = { 0, 1024, 4096, 10*1024 }; */ + int sizes[] = { 0, 1024 }; + char name[64]; + + iter = LAT_FS_ITER; + + if (argc > 1) + iter = strtoul(&argv[1][0], NULL, 0); + + for (i = 0; i < sizeof(sizes)/sizeof(int); ++i) { + printf("CREATING: %d files of %5d bytes each\n", iter, sizes[i]); + for (j = 0; j < iter; ++j) { + sprintf(name, "/myfs/%.5d", j); + mkfile(name, sizes[i]); + } + + printf("DELETING: %d files of %5d bytes each\n", iter, sizes[i]); + for (j = 0; j < iter; ++j) { + sprintf(name, "/myfs/%.5d", j); + if (sys_unlink(1, -1, name) != 0) + printf("lat_fs: failed to remove: %s\n", name); + } + } +} + + + + +static void +do_create(int argc, char **argv) +{ + int i, j, iter = 100, err; + int size = 0; + char name[64]; + + sprintf(name, "/myfs/test"); + err = sys_mkdir(1, -1, name, MY_S_IRWXU); + if (err && err != EEXIST) + printf("mkdir of %s returned: %s (%d)\n", name, strerror(err), err); + + if (argc > 1) + iter = strtoul(&argv[1][0], NULL, 0); + if (argc > 2) + size = strtoul(&argv[2][0], NULL, 0); + + printf("creating %d files (each %d bytes long)...\n", iter, size); + + for (j = 0; j < iter; ++j) { + sprintf(name, "/myfs/test/%.5d", j); + /* printf("CREATING: %s (%5d)\n", name, size); */ + mkfile(name, size); + } +} + +static void +do_delete(int argc, char **argv) +{ + int i, j, iter = 100; + char name[64]; + + if (argc > 1) + iter =strtoul(&argv[1][0], NULL, 0); + + for (j = 0; j < iter; ++j) { + sprintf(name, "/myfs/test/%.5d", j); + printf("DELETING: %s\n", name); + if (sys_unlink(1, -1, name) != 0) + printf("lat_fs: failed to remove: %s\n", name); + } +} + + + +static void do_help(int argc, char **argv); + + +typedef struct cmd_entry { + char *name; + void (*func)(int argc, char **argv); + char *help; +} cmd_entry; + +cmd_entry fsh_cmds[] = +{ + { "ls", do_dir, "print a directory listing" }, + { "dir", do_dir, "print a directory listing (same as ls)" }, + { "open", do_open, "open an existing file for read/write access" }, + { "make", do_make, "create a file (optionally specifying a name)" }, + { "close", do_close, "close the currently open file" }, + { "mkdir", do_mkdir, "create a directory" }, + { "rdtest", do_read_test, "read N bytes from the current file. default is 256" }, + { "wrtest", do_write_test, "write N bytes to the current file. default is 256" }, + { "wrstream", do_write_stream, "write N blocks of 4096 bytes to the current file. default is 100000" }, + { "rm", do_rm, "remove the named file" }, + { "rmall", do_rmall, "remove all the files. if no dirname, use '.'" }, + { "rmdir", do_rmdir, "remove the named directory" }, + { "cp", do_copy, "copy a file to/from myfs. prefix a ':' for host filenames" }, + { "copy", do_copy, "same as cp" }, + { "trunc", do_trunc, "truncate a file to the size specified" }, + { "seek", do_seek, "seek to the position specified" }, + { "mv", do_rename, "rename a file or directory" }, + { "sync", do_sync, "call sync" }, + { "wrattr", do_write_attr, "write attribute \"name\" to the current file (N bytes [256])." }, + { "rdattr", do_read_attr, "read attribute \"name\" from the current file (N bytes [256])." }, + { "rmattr", do_remove_attr, "remove attribute \"name\" from the current file." }, + { "attrs", do_attrtest, "writes random attributes [a-z] up to 1023 bytes, N [10240] iterations." }, + { "lat_fs", do_lat_fs, "simulate what the lmbench test lat_fs does" }, + { "create", do_create, "create N files. default is 100" }, + { "delete", do_delete, "delete N files. default is 100" }, + { "query", do_query, "run a query on the file system" }, + { "ioctl", do_ioctl, "execute ioctl() without an inode (okay, with the root node)" }, + { "fcntl", do_fcntl, "execute ioctl() with the active inode" }, + { "cptest", do_copytest, "copies all files from the given path" }, + { "threads", do_threadtest, "copies several files, and does a lat_fs simulaneously" }, + { "mfile", do_threadfiletest, "copies several big files simulaneously" }, + { "chkbfs", do_chkbfs, "does a chkbfs on the volume" }, + { "cio", do_cio, "does a I/O speed test" }, + { "help", do_help, "print this help message" }, + { "?", do_help, "print this help message" }, + { NULL, NULL } +}; + + +static void +do_help(int argc, char **argv) +{ + cmd_entry *cmd; + + printf("commands fsh understands:\n"); + for(cmd=fsh_cmds; cmd->name != NULL; cmd++) { + printf("%8s - %s\n", cmd->name, cmd->help); + } +} + +static char * +getline(char *prompt, char *input, int len) +{ + printf("%s", prompt); fflush(stdout); + + return fgets(input, len, stdin); +} + + +static void +do_fsh(void) +{ + char stack_filler[1024 * 1024 * 16 - 32 * 1024]; + // actually emulating 12kB stack size (BeOS has 16 MB - ~20kB for the main thread) + int argc, len; + char *prompt = "fsh>> "; + char input[512], **argv; + cmd_entry *cmd; + + while(getline(prompt, input, sizeof(input)) != NULL) { + argc = 0; + argv = build_argv(input, &argc); + if (argv == NULL || argc == 0) { + continue; + } + + len = strlen(&argv[0][0]); + + for(cmd=fsh_cmds; cmd->name != NULL; cmd++) { + if (strncmp(cmd->name, &argv[0][0], len) == 0) { + cmd->func(argc, argv); + break; + } + } + + if (strncmp(&argv[0][0], "quit", 4) == 0) + break; + + if (cmd->name == NULL && argv[0][0] != '\0') + printf("command `%s' not understood\n", &argv[0][0]); + + free(argv); + } + + if (feof(stdin)) + printf("\n"); + + if (cur_fd != -1) + do_close(0, NULL); +} + + + + diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/fsproto.h b/src/tests/add-ons/kernel/file_systems/fs_shell/fsproto.h new file mode 100644 index 0000000000..c8bf28467c --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/fsproto.h @@ -0,0 +1,265 @@ +/* + Copyright 1999-2001, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef _FSPROTO_H +#define _FSPROTO_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "compat.h" + +typedef dev_t nspace_id; +typedef ino_t vnode_id; + +/* + * PUBLIC PART OF THE FILE SYSTEM PROTOCOL + */ + +#define WSTAT_MODE 0x0001 +#define WSTAT_UID 0x0002 +#define WSTAT_GID 0x0004 +#define WSTAT_SIZE 0x0008 +#define WSTAT_ATIME 0x0010 +#define WSTAT_MTIME 0x0020 +#define WSTAT_CRTIME 0x0040 + +#define WFSSTAT_NAME 0x0001 + +#define B_ENTRY_CREATED 1 +#define B_ENTRY_REMOVED 2 +#define B_ENTRY_MOVED 3 +#define B_STAT_CHANGED 4 +#define B_ATTR_CHANGED 5 +#define B_DEVICE_MOUNTED 6 +#define B_DEVICE_UNMOUNTED 7 + +#define B_STOP_WATCHING 0x0000 +#define B_WATCH_NAME 0x0001 +#define B_WATCH_STAT 0x0002 +#define B_WATCH_ATTR 0x0004 +#define B_WATCH_DIRECTORY 0x0008 + +#define SELECT_READ 1 +#define SELECT_WRITE 2 +#define SELECT_EXCEPTION 3 + +// missing ioctl() call added +#define IOCTL_FILE_UNCACHED_IO 10000 +#define IOCTL_CREATE_TIME 10002 +#define IOCTL_MODIFIED_TIME 10003 + + +// B_CUR_FS_API_VERSION is 2 for R5, but 3 on Dano, because of the +// added calls for power management - so it's set to 3 here because +// that's a requirement to let Dano boot from our fs... +#ifdef B_BEOS_VERSION_DANO +# define B_CUR_FS_API_VERSION 3 +#else +# define B_CUR_FS_API_VERSION 2 +#endif + +struct attr_info; +struct index_info; + +typedef int op_read_vnode(void *ns, vnode_id vnid, char r, void **node); +typedef int op_write_vnode(void *ns, void *node, char r); +typedef int op_remove_vnode(void *ns, void *node, char r); +typedef int op_secure_vnode(void *ns, void *node); +typedef int op_wake_vnode(void *ns, void *node); +typedef int op_suspend_vnode(void *ns, void *node); + +typedef int op_walk(void *ns, void *base, const char *file, char **newpath, + vnode_id *vnid); + +typedef int op_access(void *ns, void *node, int mode); + +typedef int op_create(void *ns, void *dir, const char *name, + int omode, int perms, vnode_id *vnid, void **cookie); +typedef int op_mkdir(void *ns, void *dir, const char *name, int perms); +typedef int op_symlink(void *ns, void *dir, const char *name, + const char *path); +typedef int op_link(void *ns, void *dir, const char *name, void *node); + +typedef int op_rename(void *ns, void *olddir, const char *oldname, + void *newdir, const char *newname); +typedef int op_unlink(void *ns, void *dir, const char *name); +typedef int op_rmdir(void *ns, void *dir, const char *name); + +typedef int op_readlink(void *ns, void *node, char *buf, size_t *bufsize); + +typedef int op_opendir(void *ns, void *node, void **cookie); +typedef int op_closedir(void *ns, void *node, void *cookie); +typedef int op_rewinddir(void *ns, void *node, void *cookie); +typedef int op_readdir(void *ns, void *node, void *cookie, long *num, + struct dirent *buf, size_t bufsize); + +typedef int op_open(void *ns, void *node, int omode, void **cookie); +typedef int op_close(void *ns, void *node, void *cookie); +typedef int op_free_cookie(void *ns, void *node, void *cookie); +typedef int op_read(void *ns, void *node, void *cookie, off_t pos, void *buf, + size_t *len); +typedef int op_write(void *ns, void *node, void *cookie, off_t pos, + const void *buf, size_t *len); +typedef int op_readv(void *ns, void *node, void *cookie, off_t pos, const iovec *vec, + size_t count, size_t *len); +typedef int op_writev(void *ns, void *node, void *cookie, off_t pos, const iovec *vec, + size_t count, size_t *len); +typedef int op_ioctl(void *ns, void *node, void *cookie, int cmd, void *buf, + size_t len); +typedef int op_setflags(void *ns, void *node, void *cookie, int flags); + +typedef int op_rstat(void *ns, void *node, struct stat *); +typedef int op_wstat(void *ns, void *node, struct stat *, long mask); +typedef int op_fsync(void *ns, void *node); + +typedef int op_select(void *ns, void *node, void *cookie, uint8 event, + uint32 ref, selectsync *sync); +typedef int op_deselect(void *ns, void *node, void *cookie, uint8 event, + selectsync *sync); + +typedef int op_initialize(const char *devname, void *parms, size_t len); +typedef int op_mount(nspace_id nsid, const char *devname, ulong flags, + void *parms, size_t len, void **data, vnode_id *vnid); +typedef int op_unmount(void *ns); +typedef int op_sync(void *ns); +typedef int op_rfsstat(void *ns, struct fs_info *); +typedef int op_wfsstat(void *ns, struct fs_info *, long mask); + + +typedef int op_open_attrdir(void *ns, void *node, void **cookie); +typedef int op_close_attrdir(void *ns, void *node, void *cookie); +typedef int op_rewind_attrdir(void *ns, void *node, void *cookie); +typedef int op_read_attrdir(void *ns, void *node, void *cookie, long *num, + struct dirent *buf, size_t bufsize); +typedef int op_remove_attr(void *ns, void *node, const char *name); +typedef int op_rename_attr(void *ns, void *node, const char *oldname, + const char *newname); +typedef int op_stat_attr(void *ns, void *node, const char *name, + struct attr_info *buf); + +typedef int op_write_attr(void *ns, void *node, const char *name, int type, + const void *buf, size_t *len, off_t pos); +typedef int op_read_attr(void *ns, void *node, const char *name, int type, + void *buf, size_t *len, off_t pos); + +typedef int op_open_indexdir(void *ns, void **cookie); +typedef int op_close_indexdir(void *ns, void *cookie); +typedef int op_rewind_indexdir(void *ns, void *cookie); +typedef int op_read_indexdir(void *ns, void *cookie, long *num, + struct dirent *buf, size_t bufsize); +typedef int op_create_index(void *ns, const char *name, int type, int flags); +typedef int op_remove_index(void *ns, const char *name); +typedef int op_rename_index(void *ns, const char *oldname, + const char *newname); +typedef int op_stat_index(void *ns, const char *name, struct index_info *buf); + +typedef int op_open_query(void *ns, const char *query, ulong flags, + port_id port, long token, void **cookie); +typedef int op_close_query(void *ns, void *cookie); +typedef int op_read_query(void *ns, void *cookie, long *num, + struct dirent *buf, size_t bufsize); + +typedef struct vnode_ops { + op_read_vnode (*read_vnode); + op_write_vnode (*write_vnode); + op_remove_vnode (*remove_vnode); + op_secure_vnode (*secure_vnode); + op_walk (*walk); + op_access (*access); + op_create (*create); + op_mkdir (*mkdir); + op_symlink (*symlink); + op_link (*link); + op_rename (*rename); + op_unlink (*unlink); + op_rmdir (*rmdir); + op_readlink (*readlink); + op_opendir (*opendir); + op_closedir (*closedir); + op_free_cookie (*free_dircookie); + op_rewinddir (*rewinddir); + op_readdir (*readdir); + op_open (*open); + op_close (*close); + op_free_cookie (*free_cookie); + op_read (*read); + op_write (*write); + op_readv (*readv); + op_writev (*writev); + op_ioctl (*ioctl); + op_setflags (*setflags); + op_rstat (*rstat); + op_wstat (*wstat); + op_fsync (*fsync); + op_initialize (*initialize); + op_mount (*mount); + op_unmount (*unmount); + op_sync (*sync); + op_rfsstat (*rfsstat); + op_wfsstat (*wfsstat); + op_select (*select); + op_deselect (*deselect); + op_open_indexdir (*open_indexdir); + op_close_indexdir (*close_indexdir); + op_free_cookie (*free_indexdircookie); + op_rewind_indexdir (*rewind_indexdir); + op_read_indexdir (*read_indexdir); + op_create_index (*create_index); + op_remove_index (*remove_index); + op_rename_index (*rename_index); + op_stat_index (*stat_index); + op_open_attrdir (*open_attrdir); + op_close_attrdir (*close_attrdir); + op_free_cookie (*free_attrdircookie); + op_rewind_attrdir (*rewind_attrdir); + op_read_attrdir (*read_attrdir); + op_write_attr (*write_attr); + op_read_attr (*read_attr); + op_remove_attr (*remove_attr); + op_rename_attr (*rename_attr); + op_stat_attr (*stat_attr); + op_open_query (*open_query); + op_close_query (*close_query); + op_free_cookie (*free_querycookie); + op_read_query (*read_query); + // for Dano compatibility only + op_wake_vnode (*wake_vnode); + op_suspend_vnode (*suspend_vnode); +} vnode_ops; + +extern _IMPEXP_KERNEL int new_path(const char *path, char **copy); +extern _IMPEXP_KERNEL void free_path(char *p); + +extern _IMPEXP_KERNEL int notify_listener(int op, nspace_id nsid, + vnode_id vnida, vnode_id vnidb, + vnode_id vnidc, const char *name); +extern _IMPEXP_KERNEL void notify_select_event(selectsync *sync, uint32 ref); +extern _IMPEXP_KERNEL int send_notification(port_id port, long token, + ulong what, long op, nspace_id nsida, + nspace_id nsidb, vnode_id vnida, + vnode_id vnidb, vnode_id vnidc, + const char *name); +extern _IMPEXP_KERNEL int get_vnode(nspace_id nsid, vnode_id vnid, void **data); +extern _IMPEXP_KERNEL int put_vnode(nspace_id nsid, vnode_id vnid); +extern _IMPEXP_KERNEL int new_vnode(nspace_id nsid, vnode_id vnid, void *data); +extern _IMPEXP_KERNEL int remove_vnode(nspace_id nsid, vnode_id vnid); +extern _IMPEXP_KERNEL int unremove_vnode(nspace_id nsid, vnode_id vnid); +extern _IMPEXP_KERNEL int is_vnode_removed(nspace_id nsid, vnode_id vnid); + + +extern _EXPORT vnode_ops fs_entry; +extern _EXPORT int32 api_version; + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/hexdump.c b/src/tests/add-ons/kernel/file_systems/fs_shell/hexdump.c new file mode 100644 index 0000000000..53dc48ef4d --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/hexdump.c @@ -0,0 +1,125 @@ +/* + This file contains a simple hex dump routine. + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ +#include +#include + +#include "compat.h" + +/* + * This routine is a simple memory dumper. It's nice and simple, and works + * well. + * + * The bad things about it are that it assumes roughly an 80 column output + * device and that output is fixed at BYTES_PER_LINE/2 columns (separated + * every two bytes). + * + * Obviously the bad things are fixable, but I don't need the extra + * flexibility at the moment, so I don't feel like doing it. + * + * Dominic Giampaolo + * (dbg@be.com) + */ + +#define BYTES_PER_LINE 16 /* a reasonable power of two */ + + +void hexdump(void *address, int size) +{ + int i; + int offset, num_spaces; + unsigned char *mem, *tmp; + + + offset = 0; + mem = (unsigned char *)address; + + /* + * Each line contains BYTES_PER_LINE bytes of data (presently 16). I + * chose 16 because it is a nice power of two and makes reading the + * hex offset column much easier. I used to use a value of 18 to fit + * more info on the screen, and 20 is also doable but much too crowded. + * Ideally it should be an argument or settable parameter... + * + * The data is formatted into BYTES_PER_LINE/2 (8) columns of 2 bytes + * each (printed in hex). + * + * The offset is formatted as a 4 byte hex number (i.e. 8 characters). + */ + while(offset < size) + { + printf("%.8x: ", offset); + + for(i=0,tmp=mem; i < BYTES_PER_LINE && (offset+i) < size; i++,tmp++) + { + printf("%.2x", *tmp); + if (((i+1) % 4) == 0) + printf(" "); + } + + /* + * This formula for the number of spaces to print is as follows: + * 10 is the number of characters printed at the beginning of + * the line (8 hex digits, the colon and a space). + * i*2 is the number of characters of data we dumped in hex. + * i/2 is the number of blanks we printed between columns. + * i is the number of bytes we will print in ascii. + * + * Then we subtract all that from 74 (the width of the output + * device) to decide how many spaces we need to push the ascii + * column as far to the right as possible. + * + * The number 58 is the column where we start we start printing + * the ascii dump. We subtract how many characters we've already + * printed and that gets us to where we need to be to start the + * ascii portion of the dump. + * + */ + num_spaces = 58 - (12 + i*2 + i/4); + for(i=0; i < num_spaces; i++) + printf(" "); + + for(i=0,tmp=mem; i < BYTES_PER_LINE && (offset+i) < size; i++, tmp++) + if (isprint(*tmp)) + printf("%c", *tmp); + else + printf("."); + + printf("\n"); + + offset += BYTES_PER_LINE; + mem = tmp; + } +} + + + +#ifdef TEST + + +char buff[] = "!blah, blah blah blah blah asldfj lkasjdf lka lkjasdflasdlj" + "asdj lasdfj lasdjf lasdjf lkasjdfl kjasdlf jasldfj lasdfj l" + "asdjflkasjdflk;ja sdfljasdfjk asjkfl;kasjfl;asjdfl;azzzzzzz"; + +main(int argc, char **argv) +{ + int i,j,k; + FILE *fp; + + hexdump(buff, 171); + + printf("---------\n"); + + hexdump(main, 57); +} + +#endif /* TEST */ diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/initfs.c b/src/tests/add-ons/kernel/file_systems/fs_shell/initfs.c new file mode 100644 index 0000000000..3254b0af9f --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/initfs.c @@ -0,0 +1,51 @@ +/* + This file contains some glue code that initializes the block cache, + the vnode layer, mounts the root file system (a simple container) + and then mounts our file system at the mount point /myfs. You could + modify this to mount other file systems or even multiple file systems + if you wanted. + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ +#include +#include + +#include "compat.h" +#include "fsproto.h" + +#include "kprotos.h" + +extern vnode_ops fs_entry; + +void * +init_fs(char *disk_name) +{ + int err; + void *data = NULL; + + init_block_cache(16348, 0); + init_vnode_layer(); + + err = sys_mkdir(1, -1, "/myfs", 0); + + if (install_file_system(&fs_entry, "myfs", 1, -1) == NULL) { + printf("can't install my file system\n"); + exit(0); + } + + + data = sys_mount(1, "myfs", -1, "/myfs", disk_name, 0, NULL, 0); + if (data == NULL) { + printf("could not mount %s on /myfs\n", disk_name); + exit(0); + } + + return data; +} diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/initfs.h b/src/tests/add-ons/kernel/file_systems/fs_shell/initfs.h new file mode 100644 index 0000000000..e579ced964 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/initfs.h @@ -0,0 +1 @@ +void *init_fs(char *disk_name); diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/kernel.c b/src/tests/add-ons/kernel/file_systems/fs_shell/kernel.c new file mode 100644 index 0000000000..2fd28e1424 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/kernel.c @@ -0,0 +1,3195 @@ +/* + This file contains the vnode layer used by the file system construction + kit. It is the file system independent layer and managed hooking up + requests from the test program (fsh and tstfs) to the actual file system + routines. It provides a rather posix-like interface generally with + the routines preceded by a "sys_" prefix. + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ +#include "compat.h" + +#include "skiplist.h" +#include "lock.h" +#include "fsproto.h" +#include "kprotos.h" + +#include + +#define OMODE_MASK (O_RDONLY | O_WRONLY | O_RDWR) +#define SLEEP_TIME (10000.0) +#define MAX_SYM_LINKS 16 + +#define FREE_LIST 0 +#define USED_LIST 1 +#define LOCKED_LIST 2 +#define LIST_NUM 3 + +#define FD_FILE 1 +#define FD_DIR 2 +#define FD_WD 4 + +#define FD_ALL (FD_FILE | FD_DIR | FD_WD) + +#define DEFAULT_FD_NUM (128) +#define VNNUM 256 + +typedef unsigned long fsystem_id; + +typedef struct vnode vnode; +typedef struct vnlist vnlist; +typedef struct vnlink vnlink; +typedef struct fsystem fsystem; +typedef struct nspace nspace; +typedef struct ofile ofile; +typedef struct ioctx ioctx; +typedef struct fdarray fdarray; + +struct vnlist { + vnode *head; + vnode *tail; + int num; +}; + +struct vnlink { + vnode *prev; + vnode *next; +}; + +struct vnode { + vnode_id vnid; + nspace * ns; + nspace * mounted; + char remove; + char busy; + char inlist; + char watched; + vnlink nspace; + vnlink list; + int rcnt; + void * data; +}; + +struct fsystem { + fsystem_id fsid; + bool fixed; + image_id aid; + char name[IDENT_NAME_LENGTH]; + int rcnt; + vnode_ops ops; +}; + +struct nspace { + nspace_id nsid; + my_dev_t dev; + my_ino_t ino; + fsystem *fs; + vnlist vnodes; + void *data; + vnode * root; + vnode * mount; + nspace * prev; + nspace * next; + char shutdown; +}; + +struct ofile { + short type; + ushort flags; + vnode * vn; + void * cookie; + long rcnt; + long ocnt; + + fs_off_t pos; + int omode; +}; + + +struct ioctx { + lock lock; + int kerrno; + vnode * cwd; + fdarray * fds; +}; + +struct fdarray { + long rcnt; + lock lock; + int num; + ulong *alloc; + ulong *coes; + ofile *fds[1]; +}; + +extern struct { + const char * name; + vnode_ops * ops; +} fixed_fs[]; + +static vnode_id invalid_vnid = 0; +static vnode * rootvn; +static int max_glb_file; +static fdarray * global_fds; +static vnlist lists[LIST_NUM]; +static nspace * nshead; +static lock vnlock; +static lock fstablock; +static nspace ** nstab; +static fsystem ** fstab; +static int nns; +static int nfs; +static fsystem_id nxfsid; +static nspace_id nxnsid; +static SkipList skiplist; +static int vnnum; +static int usdvnnum; + + + +int sys_rstat(bool kernel, int fd, const char *path, + struct my_stat *st, bool eatlink); +static fsystem * inc_file_system(const char *name); +static int dec_file_system(fsystem *fs); + +static int get_dir_fd(bool kernel, int fd, const char *path, char *filename, + vnode **dvn); +static int get_file_fd(bool kernel, int fd, const char *path, + int eatsymlink, vnode **vn); +static int get_file_vn(nspace_id nsid, vnode_id vnid, const char *path, + int eatsymlink, vnode **vn); +static int parse_path_fd(bool kerne, int fd, char **pstart, + int eatsymlink, vnode **vn); +static int parse_path_vn(nspace_id nsid, vnode_id vnid, char **start, + int eatsymlink, vnode **vn); +static int parse_path(vnode *bvn, char **pstart, char *path, + int eatsymlink, vnode **vn); + +static char * cat_paths(char *a, char *b); + +static int load_vnode(nspace_id nsid, vnode_id vnid, char r, vnode **vnp); +static vnode * lookup_vnode(nspace_id nsid, vnode_id vnid); +static void move_vnode(vnode *vn, int list); +static vnode * steal_vnode(int list); +static void flush_vnode(vnode *vn, char r); +static int sort_vnode(vnode *vn); +static void clear_vnode(vnode *vn); +static void inc_vnode(vnode *vn); +static void dec_vnode(vnode *vn, char r); +static int compare_vnode(vnode *vna, vnode *vnb); + +static nspace * nsidtons(nspace_id nsid); +static int alloc_wd_fd(bool kernel, vnode *vn, bool coe, int *fdp); + +static int is_root(vnode *root, vnode **mount); +static int is_mount_vnode(vnode *mount, vnode **root); +static int is_mount_vnid(nspace_id nsid, vnode_id vnid, vnode_id *mount); + +static ofile * get_fd(bool kernel, int fd, int type); +static int put_fd(ofile *f); +static int new_fd(bool kernel, int nfd, ofile *f, int fd, bool coe); +static int remove_fd(bool kernel, int fd, int type); +static int get_coe(bool kernel, int fd, int type, bool *coe); +static int set_coe(bool kernel, int fd, int type, bool coe); +static int get_omode(bool kernel, int fd, int type, int *omode); +static int invoke_close(ofile *f); +static int invoke_free(ofile *f); + +static fdarray * new_fds(int num); +static int free_fds(fdarray *fds); + + +#define BITSZ(n) (((n) + 31) & ~31) +#define SETBIT(a,i,v) *((a)+(i)/32) = (*((a)+(i)/32) & ~(1<<((i)%32))) | (v<<((i)%32)) +#define GETBIT(a,i) ((*((a)+(i)/32) & (1<<((i)%32))) >> ((i)%32)) + +/* ---------------------------------------------------------------- */ + +#include + +static void +PANIC(char *s) +{ + printf(s); +} + + +#ifndef USER +#ifdef DEBUG +int +dump_fsystem(int argc, char **argv) +{ + struct fsystem *fs; + + if (argv[1] == NULL) { + kprintf("%s needs an address argument\n", argv[0]); + return 1; + } + + fs = (struct fsystem *)strtoul(argv[1], NULL, 0); + + kprintf("fs @ 0x%x name %s rcnt %d ops @ 0x%x\n", fs, fs->name, + fs->rcnt, &fs->ops); +} + +int +dump_ioctx(int argc, char **argv) +{ + struct ioctx *ioctx; + + if (argv[1] == NULL) { + kprintf("%s needs an address argument\n", argv[0]); + return 1; + } + + ioctx = (struct ioctx *)strtoul(argv[1], NULL, 0); + + kprintf("ioctx @ 0x%x, kerrno %d, cwd 0x%x, fdarray 0x%x\n", ioctx, + ioctx->kerrno, ioctx->cwd, ioctx->fds); +} + + +int +dump_vnode(int argc, char **argv) +{ + struct vnode *vn; + + if (argv[1] == NULL) { + kprintf("%s needs an address argument\n", argv[0]); + return 1; + } + + vn = (vnode *)strtoul(argv[1], NULL, 0); + + kprintf("vnode @ 0x%x vnid 0x%x ns 0x%x mounted 0x%x\n", vn, vn->vnid, + vn->ns, vn->mounted); + kprintf("remove %d busy %d inlist %d\n", vn->remove, vn->busy, vn->inlist); + kprintf("nspace 0x%x list 0x%x rcnt 0x%x data 0x%x\n", &vn->nspace, + &vn->list, vn->rcnt, vn->data); +} + + +int +dump_fdarray(int argc, char **argv) +{ + int i; + struct fdarray *fds; + + if (argv[1] == NULL) { + kprintf("%s needs an address argument\n", argv[0]); + return 1; + } + + fds = (struct fdarray *)strtoul(argv[1], NULL, 0); + + kprintf("fdarray @ 0x%x rcnt %d lock %d num %d\n", fds, fds->rcnt, + fds->num); + kprintf("alloc 0x%x coes 0x%x\n", fds->alloc, fds->coes); + for(i=0; i < fds->num; i++) + if (fds->fds[i]) + kprintf("fd %3d @ 0x%x\n", i, fds->fds[i]); +} + + +int +dump_ofile(int argc, char **argv) +{ + struct ofile *ofile; + + if (argv[1] == NULL) { + kprintf("%s needs an address argument\n", argv[0]); + return 1; + } + + ofile = (struct ofile *)strtoul(argv[1], NULL, 0); + + kprintf("ofile @ 0x%x type %d flags %d vn 0x%x cookie 0x%x\n", + ofile, ofile->type, ofile->flags, ofile->vn, ofile->cookie); + kprintf("rcnt %d ocnt %d pos 0x%x omode 0x%x\n", ofile->rcnt, ofile->ocnt, + ofile->pos, ofile->omode); +} + +int +dump_nspace(int argc, char **argv) +{ + struct nspace *ns; + + if (argv[1] == NULL) { + kprintf("%s needs an address argument\n", argv[0]); + return 1; + } + + ns = (struct nspace *)strtoul(argv[1], NULL, 0); + + kprintf("ns @ 0x%x nsid %d vnlist @ 0x%x data 0x%x\n", ns, ns->nsid, + &ns->vnodes, ns->data); + kprintf("root 0x%x mount 0x%x prev 0x%x next 0x%x\n", ns->root, ns->mount, + ns->prev, ns->next); + kprintf("shutdown %d fs @ 0x%x\n", ns->shutdown, ns->fs); +} + +void +do_dump_io_info(thread_rec *thr) +{ + int i; + struct fdarray *fds; + + if (thr->ioctx == NULL || thr->ioctx->fds == NULL) { + kprintf("thread: %d (%s)\n No io info?!?\n", thr->thid, thr->name); + return; + } + + kprintf("thread: %d (%s)\n", thr->thid, thr->name); + fds = thr->ioctx->fds; + + for(i=0; i < fds->num; i++) + if (fds->fds[i]) + kprintf(" fd %3d vnode @ 0x%.8x (vnid 0x%.8x, data 0x%.8x)\n", i, + fds->fds[i]->vn, fds->fds[i]->vn->vnid, + fds->fds[i]->vn->data); +} + + +int +dump_io_info(int argc, char **argv) +{ + int i; + thread_rec *thr; + + if (argv[1] == NULL) { + kprintf("%s needs an thread name/address argument\n", argv[0]); + return 1; + } + + if (strcmp(argv[1], "-n") == 0) { + int len; + + /* hunt for the name in argv[2] */ + if (argv[2] == NULL) { + kprintf("thread: the `-name' option requires an argument\n"); + return 0; + } + + len = strlen(argv[2]); + for(i=0; i < nthreads; i++) { + if (thread_tab[i] == NULL) + continue; + + if (mystrstr(thread_tab[i]->name, argv[2]) != NULL) { + thr = thread_tab[i]; + do_dump_io_info(thr); + } + } + } else { + ulong num; + + num = strtoul(argv[2], NULL, 0); + + if (num < 0x2ffff && isbadthid(num) == 0) + thr = thread_tab[thidtoslot(num)]; + else + thr = (thread_rec *)num; + + if (thr == 0) + return 0; + + do_dump_io_info(thr); + } + + return 1; +} + + + +int +find_vn(int argc, char **argv) +{ + nspace *ns; + vnode *vn; + vnode fakevn; + vnode_id vnid; + + if (argv[1] == NULL) { + kprintf("%s needs a vnid argument\n", argv[0]); + return 1; + } + + vnid = (vnode_id)strtoul(argv[1], NULL, 0); + + for(ns=nshead; ns; ns=ns->next) { + fakevn.ns = ns; + fakevn.vnid = vnid; + vn = SearchSL(skiplist, &fakevn); + if (vn) + kprintf("vn = 0x%x (nsid = %d)\n", vn, vn->ns->nsid); + } + + return 0; +} + +#endif /* DEBUG */ +#endif /* USER*/ + + +/* ---------------------------------------------------------------- */ + +#ifdef USER +int memsize = 8 * 1024 * 1024; +#endif + +int +init_vnode_layer(void) +{ + int err; + vnode *vns; + vnode_id vnid; + int i; + fsystem *fs; + nspace *ns; + void *data; + size_t sz; + extern vnode_ops rootfs; /* XXXdbg */ + + /* + compute vnnum based on memsize. 256 vnodes with 8MB. + compute usdvnnum based on vnnum. only 1/4 of total vnodes should + remain unlocked. + */ + + vnnum = memsize >> 15; + usdvnnum = vnnum >> 2; + + vns = (vnode *) calloc(sizeof(vnode) * vnnum, 1); + for(i=0; i> 17; + nfs = memsize >> 18; + + nxfsid = 1; + nxnsid = 1; + nstab = (nspace **) malloc(nns * sizeof(void *)); + memset(nstab, 0, nns * sizeof(void *)); + fstab = (fsystem **) malloc(nfs * sizeof(void *)); + memset(fstab, 0, nfs * sizeof(void *)); + + new_lock(&vnlock, "vnlock"); + new_lock(&fstablock, "fstablock"); + + /* + determine the max number of files the kernel can open. + 8MB -> 256 + */ + + max_glb_file = memsize >> 15; + global_fds = new_fds(max_glb_file); + + + /* + install file systems + */ + install_file_system(&rootfs, "rootfs", TRUE, -1); + + /* + mount the root file system + */ + + fs = inc_file_system("rootfs"); + + ns = (nspace *) malloc(sizeof(nspace)); + ns->fs = fs; + ns->nsid = nxnsid++; + nstab[ns->nsid % nns] = ns; + ns->vnodes.head = ns->vnodes.tail = NULL; + ns->data = NULL; + ns->root = NULL; + ns->mount = NULL; + ns->shutdown = FALSE; + ns->prev = ns->next = NULL; + nshead = ns; + + err = (*fs->ops.mount)(ns->nsid, NULL, 0, NULL, 0, &data, &vnid); + ns->data = data; + ns->root = lookup_vnode(ns->nsid, vnid); + rootvn = ns->root; + +#ifndef USER +#ifdef DEBUG + + add_debugger_cmd("ioctx", dump_ioctx, "dump a thread ioctx struct"); + add_debugger_cmd("vnode", dump_vnode, "dump a vnode struct"); + add_debugger_cmd("fdarray", dump_fdarray, "dump an fd array"); + add_debugger_cmd("ofile", dump_ofile, "dump an ofile struct"); + add_debugger_cmd("nspace", dump_nspace, "dump a nspace struct"); + add_debugger_cmd("fsystem", dump_fsystem, "dump a fsystem struct"); + add_debugger_cmd("ioinfo", dump_io_info, "dump io info for a thread"); + add_debugger_cmd("findvn", find_vn, "find a vnid (in all threads)"); + +#endif /* DEBUG */ +#endif /* USER*/ + + return 0; +} + + +/* ---------------------------------------------------------------- */ + +int +sys_sync(void) +{ + nspace *ns; + op_sync *op; + + LOCK(vnlock); + for(ns = nshead; ns; ns = ns->next) { + ns->root->rcnt++; + UNLOCK(vnlock); + op = ns->fs->ops.sync; + if (op) + (*op)(ns->data); + LOCK(vnlock); + ns->root->rcnt--; + } + UNLOCK(vnlock); + return 0; +} + +static ioctx * +get_cur_ioctx(void) +{ + static int init = 0; + static ioctx io; + + if (init == 0) { + init = 1; + memset(&io, 0, sizeof(io)); + } + + return &io; +} + + +/* + * sys_chdir + */ + +int +sys_chdir(bool kernel, int fd, const char *path) +{ + int err; + ioctx *io; + vnode *vn; + op_rstat *op; + struct my_stat st; + + err = get_file_fd(kernel, fd, path, TRUE, &vn); + if (err) + goto error1; + op = vn->ns->fs->ops.rstat; + if (!op) { + err = EINVAL; + goto error2; + } + err = (*op)(vn->ns->data, vn->data, &st); + if (err) + goto error2; + if (!MY_S_ISDIR(st.st_mode)) { + err = ENOTDIR; + goto error2; + } + io = get_cur_ioctx(); + LOCK(io->lock); + dec_vnode(io->cwd, FALSE); + io->cwd = vn; + UNLOCK(io->lock); + + return 0; + +error2: + dec_vnode(vn, FALSE); +error1: + return err; +} + + +/* + * sys_access + */ + +int +sys_access(bool kernel, int fd, const char *path, int mode) +{ + int err; + vnode *vn; + op_access *op; + + err = get_file_fd(kernel, fd, path, TRUE, &vn); + if (err) + goto error1; + op = vn->ns->fs->ops.access; + if (!op) { + err = EINVAL; + goto error2; + } + err = (*op)(vn->ns->data, vn->data, mode); + if (err) + goto error2; + dec_vnode(vn, FALSE); + return 0; + +error2: + dec_vnode(vn, FALSE); +error1: + return err; +} + +/* + * sys_symlink + */ + +int +sys_symlink(bool kernel, const char *oldpath, int nfd, const char *newpath) +{ + int err; + char filename[FILE_NAME_LENGTH]; + char *buf; + vnode *dvn; + op_symlink *op; + + err = get_dir_fd(kernel, nfd, newpath, filename, &dvn); + if (err) + goto error1; + err = new_path(oldpath, &buf); + if (err) + goto error2; + op = dvn->ns->fs->ops.symlink; + if (!op) { + err = EINVAL; + goto error3; + } + err = (*op)(dvn->ns->data, dvn->data, filename, buf); + if (err) + goto error3; + + dec_vnode(dvn, FALSE); + free_path(buf); + + return 0; + +error3: + free_path(buf); +error2: + dec_vnode(dvn, FALSE); +error1: + return err; +} + +/* + * sys_readlink + */ + +ssize_t +sys_readlink(bool kernel, int fd, const char *path, char *buf, size_t bufsize) +{ + int err; + vnode *vn; + op_readlink *op; + size_t sz; + + err = get_file_fd(kernel, fd, path, FALSE, &vn); + if (err) + goto error1; + op = vn->ns->fs->ops.readlink; + if (!op) { + err = EINVAL; + goto error2; + } + sz = bufsize; + err = (*op)(vn->ns->data, vn->data, buf, &sz); + if (err) + goto error2; + dec_vnode(vn, FALSE); + + return sz; + +error2: + dec_vnode(vn, FALSE); +error1: + return err; +} + +/* + * sys_mkdir + */ + +int +sys_mkdir(bool kernel, int fd, const char *path, int perms) +{ + int err; + char filename[FILE_NAME_LENGTH]; + vnode *dvn; + op_mkdir *op; + + err = get_dir_fd(kernel, fd, path, filename, &dvn); + if (err) + goto error1; + op = dvn->ns->fs->ops.mkdir; + if (!op) { + err = EINVAL; + goto error2; + } + err = (*op)(dvn->ns->data, dvn->data, filename, perms); + if (err) + goto error2; + + dec_vnode(dvn, FALSE); + + return 0; + +error2: + dec_vnode(dvn, FALSE); +error1: + return err; +} + + +/* + * opendir. + */ + +int +sys_opendir(bool kernel, int fd, const char *path, bool coe) +{ + int err; + op_opendir *op; + op_free_cookie *opf; + ofile *f; + int nfd; + vnode *vn; + void *cookie; + + err = get_file_fd(kernel, fd, path, TRUE, &vn); + if (err) + goto error1; + op = vn->ns->fs->ops.opendir; + if (!op) { + err = EINVAL; + goto error2; + } + err = (*op)(vn->ns->data, vn->data, &cookie); + if (err) + goto error2; + + /* + find a file descriptor + */ + + f = (ofile *) calloc(sizeof(ofile), 1); + if (!f) { + err = ENOMEM; + goto error3; + } + + f->type = FD_DIR; + f->vn = vn; + f->cookie = cookie; + f->rcnt = 0; + f->ocnt = 0; + + nfd = new_fd(kernel, -1, f, -1, coe); + if (nfd < 0) { + err = EMFILE; + goto error4; + } + + return nfd; + +error4: + free(f); +error3: + (*vn->ns->fs->ops.closedir)(vn->ns->data, vn->data, cookie); + opf = vn->ns->fs->ops.free_dircookie; + if (opf) + (*opf)(vn->ns->data, vn->data, cookie); +error2: + dec_vnode(vn, FALSE); +error1: + if (err > 0) /* XXXdbg -- a hack for linux */ + err = -err; + + return err; +} + + +/* + * closedir + */ + +int +sys_closedir(bool kernel, int fd) +{ + return remove_fd(kernel, fd, FD_DIR); +} + +/* + * readdir. + */ + +int +sys_readdir(bool kernel, int fd, struct my_dirent *buf, size_t bufsize, + long count) +{ + ofile *f; + int err; + vnode *vn; + struct my_dirent *p; + struct my_stat st; + long i; + nspace_id nsid; + vnode_id vnid; + long nm; + + f = get_fd(kernel, fd, FD_DIR); + if (!f) { + err = EBADF; + goto error1; + } + vn = f->vn; + nm = count; + err = (*vn->ns->fs->ops.readdir)(vn->ns->data, vn->data, f->cookie, + &nm, buf, bufsize); + if (err) + goto error1; + + /* + patch the mount points and the root. + */ + + LOCK(vnlock); + nsid = vn->ns->nsid; + p = buf; + for(i=0; id_ino, &vnid)) + p->d_ino = vnid; + if (vn->ns->mount && !strcmp(p->d_name, "..")) { + UNLOCK(vnlock); + err = sys_rstat(kernel, fd, "..", &st, FALSE); + if (err) + goto error2; + LOCK(vnlock); + p->d_ino = st.st_ino; + } + p = (struct my_dirent *) ((char *) p + p->d_reclen); + } + UNLOCK(vnlock); + + put_fd(f); + return nm; + +error2: + put_fd(f); +error1: + return err; +} + + +/* + * rewinddir + */ + +int +sys_rewinddir(bool kernel, int fd) +{ + ofile *f; + int err; + vnode *vn; + + f = get_fd(kernel, fd, FD_DIR); + if (!f) + return EBADF; + vn = f->vn; + err = (*vn->ns->fs->ops.rewinddir)(vn->ns->data, vn->data, f->cookie); + put_fd(f); + return err; +} + + +/* + * open/create files. + */ + +int +sys_open(bool kernel, int fd, const char *path, int omode, int perms, + bool coe) +{ + int err; + char filename[FILE_NAME_LENGTH]; + vnode *vn, *dvn; + vnode_id vnid; + void *cookie; + ofile *f; + int nfd; + fdarray *fds; + op_create *opc; + op_open *opo; + op_free_cookie *opf; + + if (omode & O_CREAT) { + err = get_dir_fd(kernel, fd, path, filename, &dvn); + if (err) + goto errorA; + opc = dvn->ns->fs->ops.create; + if (!opc) { + err = EINVAL; + goto errorB; + } + err = (*opc)(dvn->ns->data, dvn->data, filename, omode, perms, &vnid, + &cookie); + if (err) + goto errorB; + LOCK(vnlock); + vn = lookup_vnode(dvn->ns->nsid, vnid); + UNLOCK(vnlock); + + dec_vnode(dvn, FALSE); + } else { + err = get_file_fd(kernel, fd, path, TRUE, &vn); + if (err) + goto error1; + opo = vn->ns->fs->ops.open; + if (!opo) { + err = EINVAL; + goto error2; + } + err = (*opo)(vn->ns->data, vn->data, omode, &cookie); + if (err) + goto error2; + } + + /* + find a file descriptor + */ + + f = (ofile *) calloc(sizeof(ofile), 1); + if (!f) { + err = ENOMEM; + goto error3; + } + + f->type = FD_FILE; + f->vn = vn; + f->cookie = cookie; + f->ocnt = 0; + f->rcnt = 0; + f->pos = 0; + f->omode = omode; + + nfd = new_fd(kernel, -1, f, -1, coe); + if (nfd < 0) { + err = EMFILE; + goto error4; + } + + return nfd; + +error4: + free(f); +error3: + (*vn->ns->fs->ops.close)(vn->ns->data, vn->data, cookie); + opf = vn->ns->fs->ops.free_cookie; + if (opf) + (*opf)(vn->ns->data, vn->data, cookie); + if (omode & O_CREAT) + goto errorC; +error2: + dec_vnode(vn, FALSE); +error1: + return err; + +errorC: + (*vn->ns->fs->ops.unlink)(dvn->ns->data, dvn->data, filename); + dec_vnode(vn, FALSE); +errorB: + dec_vnode(dvn, FALSE); +errorA: + if (err > 0) /* XXXdbg -- a hack for linux */ + err = -err; + + return err; +} + + +/* + * sys_close + */ + +int +sys_close(bool kernel, int fd) +{ + return remove_fd(kernel, fd, FD_FILE); +} + + +/* + * sys_lseek + */ + +fs_off_t +sys_lseek(bool kernel, int fd, fs_off_t pos, int whence) +{ + ofile *f; + int err; + struct my_stat st; + vnode *vn; + op_rstat *op; + + f = get_fd(kernel, fd, FD_FILE); + if (!f) { + err = EBADF; + goto error1; + } + switch(whence) { + case SEEK_SET: + f->pos = pos; + break; + case SEEK_CUR: + if ((f->omode & O_APPEND) == 0) + f->pos += pos; + else { /* we're in append mode so ask where the EOF is */ + vn = f->vn; + op = vn->ns->fs->ops.rstat; + if (!op) { + err = EINVAL; + goto error2; + } + err = (*op)(vn->ns->data, vn->data, &st); + if (err) + goto error2; + pos += st.st_size; + f->pos = pos; + break; + } + break; + case SEEK_END: + vn = f->vn; + op = vn->ns->fs->ops.rstat; + if (!op) { + err = EINVAL; + goto error2; + } + err = (*op)(vn->ns->data, vn->data, &st); + if (err) + goto error2; + pos += st.st_size; + f->pos = pos; + break; + default: + put_fd(f); + return EINVAL; + } + + if (f->pos < 0) { + f->pos = 0; + err = EINVAL; + goto error2; + } + + pos = f->pos; + put_fd(f); + return pos; + +error2: + put_fd(f); +error1: + return err; +} + + +/* + * sys_read + */ + + +ssize_t +sys_read(bool kernel, int fd, void *buf, size_t len) +{ + ofile *f; + int err; + vnode *vn; + size_t sz; + + + f = get_fd(kernel, fd, FD_FILE); + if (!f) { + err = EBADF; + goto error1; + } + if ((f->omode & OMODE_MASK) == O_WRONLY) { + err = EBADF; + goto error2; + } + vn = f->vn; + sz = len; + err = (*vn->ns->fs->ops.read)(vn->ns->data, vn->data, f->cookie, f->pos, + buf, &sz); + if (err) + goto error2; + + /* + the update of f->pos is not protected. does it matter? + simultaneous I/Os on the same file are unpredictable anyway. + */ + + f->pos += sz; + + put_fd(f); + + return sz; + +error2: + put_fd(f); +error1: + return err; +} + + +/* + * sys_write + */ + +ssize_t +sys_write(bool kernel, int fd, void *buf, size_t len) +{ + ofile *f; + int err; + vnode *vn; + size_t sz; + + f = get_fd(kernel, fd, FD_FILE); + if (!f) { + err = EBADF; + goto error1; + } + if ((f->omode & OMODE_MASK) == O_RDONLY) { + err = EBADF; + goto error2; + } + vn = f->vn; + sz = len; + err = (*vn->ns->fs->ops.write)(vn->ns->data, vn->data, f->cookie, f->pos, + buf, &sz); + if (err) + goto error2; + + /* + the update of f->pos is not protected. does it matter? + simultaneous I/Os on the same file are unpredictable anyway. + */ + + f->pos += sz; + + put_fd(f); + return sz; + +error2: + put_fd(f); +error1: + return err; +} + + +ssize_t +sys_read_attr(bool kernel, int fd, const char *name, int type, void *buffer, size_t len, off_t pos) +{ + ofile *f; + int err; + vnode *vn; + size_t sz; + + f = get_fd(kernel, fd, FD_FILE); + if (!f) { + err = EBADF; + goto error1; + } + + vn = f->vn; + sz = len; + err = (*vn->ns->fs->ops.read_attr)(vn->ns->data, vn->data, name, type, buffer, &sz, pos); + if (err) + goto error2; + + put_fd(f); + + return sz; + +error2: + put_fd(f); +error1: + return err; +} + + +ssize_t +sys_write_attr(bool kernel, int fd, const char *name,int type,void *buffer, size_t len, off_t pos) +{ + ofile *f; + int err; + vnode *vn; + size_t sz; + + f = get_fd(kernel, fd, FD_FILE); + if (!f) { + err = EBADF; + goto error1; + } + + vn = f->vn; + sz = len; + err = (*vn->ns->fs->ops.write_attr)(vn->ns->data, vn->data, name, type, buffer, &sz, pos); + if (err) + goto error2; + + put_fd(f); + return sz; + +error2: + put_fd(f); +error1: + return err; +} + + +ssize_t +sys_remove_attr(bool kernel, int fd, const char *name) +{ + ofile *f; + int err; + vnode *vn; + + f = get_fd(kernel, fd, FD_FILE); + if (!f) { + err = EBADF; + goto error1; + } + + vn = f->vn; + err = (*vn->ns->fs->ops.remove_attr)(vn->ns->data, vn->data, name); + if (err) + goto error2; + + put_fd(f); + + return 0; + +error2: + put_fd(f); +error1: + return err; +} + + +/* + * sys_ioctl + */ + +int +sys_ioctl(bool kernel, int fd, int cmd, void *arg, size_t sz) +{ + ofile *f; + int err; + vnode *vn; + op_ioctl *op; + + f = get_fd(kernel, fd, FD_FILE); + if (!f) { + err = EBADF; + goto error1; + } + vn = f->vn; + op = vn->ns->fs->ops.ioctl; + if (op) + err = (*op)(vn->ns->data, vn->data, f->cookie, cmd, arg, sz); + else + err = EINVAL; + if (err) + goto error2; + + put_fd(f); + return 0; + +error2: + put_fd(f); +error1: + return err; +} + + +/* + * sys_link + */ + +int +sys_link(bool kernel, int ofd, const char *oldpath, int nfd, + const char *newpath) +{ + int err; + vnode *vn, *dvn; + char filename[FILE_NAME_LENGTH]; + op_link *op; + + err = get_file_fd(kernel, ofd, oldpath, TRUE, &vn); + if (err) + goto error1; + err = get_dir_fd(kernel, nfd, newpath, filename, &dvn); + if (err) + goto error2; + + if (vn->ns != dvn->ns) { + err = EXDEV; + goto error3; + } + op = dvn->ns->fs->ops.link; + if (!op) { + err = EINVAL; + goto error3; + } + err = (*op)(dvn->ns->data, dvn->data, filename, vn->data); + if (err) + goto error3; + + dec_vnode(dvn, FALSE); + dec_vnode(vn, FALSE); + return 0; + +error3: + dec_vnode(dvn, FALSE); +error2: + dec_vnode(vn, FALSE); +error1: + return err; +} + +/* + * sys_unlink + */ + +int +sys_unlink(bool kernel, int fd, const char *path) +{ + int err; + vnode *dvn; + char filename[FILE_NAME_LENGTH]; + op_unlink *op; + + err = get_dir_fd(kernel, fd, path, filename, &dvn); + if (err) + goto error1; + + op = dvn->ns->fs->ops.unlink; + if (!op) { + err = EINVAL; + goto error2; + } + err = (*op)(dvn->ns->data, dvn->data, filename); + if (err) + goto error2; + + dec_vnode(dvn, FALSE); + return 0; + +error2: + dec_vnode(dvn, FALSE); +error1: + return err; +} + + +/* + * sys_rmdir + */ + +int +sys_rmdir(bool kernel, int fd, const char *path) +{ + int err; + vnode *dvn; + char filename[FILE_NAME_LENGTH]; + op_unlink *op; + + err = get_dir_fd(kernel, fd, path, filename, &dvn); + if (err) + goto error1; + + op = dvn->ns->fs->ops.rmdir; + if (!op) { + err = EINVAL; + goto error2; + } + err = (*op)(dvn->ns->data, dvn->data, filename); + if (err) + goto error2; + + dec_vnode(dvn, FALSE); + return 0; + +error2: + dec_vnode(dvn, FALSE); +error1: + return err; +} + +/* + * sys_rename + */ + +int +sys_rename(bool kernel, int ofd, const char *oldpath, + int nfd, const char *newpath) +{ + int err; + char newname[FILE_NAME_LENGTH], oldname[FILE_NAME_LENGTH]; + vnode *odvn, *ndvn; + op_rename *op; + + err = get_dir_fd(kernel, ofd, oldpath, oldname, &odvn); + if (err) + goto error1; + err = get_dir_fd(kernel, nfd, newpath, newname, &ndvn); + if (err) + goto error2; + + if (odvn->ns != ndvn->ns) { + err = EXDEV; + goto error2; + } + + op = odvn->ns->fs->ops.rename; + if (!op) { + err = EINVAL; + goto error3; + } + err = (*op)(odvn->ns->data, odvn->data, oldname, ndvn->data, newname); + if (err) + goto error3; + + dec_vnode(odvn, FALSE); + dec_vnode(ndvn, FALSE); + + return 0; + +error3: + dec_vnode(ndvn, FALSE); +error2: + dec_vnode(odvn, FALSE); +error1: + return err; +} + +/* + * sys_rstat + */ + +int +sys_rstat(bool kernel, int fd, const char *path, struct my_stat *st, + bool eatlink) +{ + int err; + vnode *vn; + op_rstat *op; + + err = get_file_fd(kernel, fd, path, eatlink, &vn); + if (err) + goto error1; + op = vn->ns->fs->ops.rstat; + if (!op) { + err = EINVAL; + goto error2; + } + err = (*op)(vn->ns->data, vn->data, st); + if (err) + goto error2; + dec_vnode(vn, FALSE); + + return 0; + +error2: + dec_vnode(vn, FALSE); +error1: + return err; +} + +/* + * sys_wstat + */ + +int +sys_wstat(bool kernel, int fd, const char *path, struct my_stat *st, long mask, + bool eatlink) +{ + int err; + vnode *vn; + op_wstat *op; + + err = get_file_fd(kernel, fd, path, eatlink, &vn); + if (err) + goto error1; + op = vn->ns->fs->ops.wstat; + if (!op) { + err = EINVAL; + goto error2; + } + err = (*op)(vn->ns->data, vn->data, st, mask); + if (err) + goto error2; + dec_vnode(vn, FALSE); + + return 0; + +error2: + dec_vnode(vn, FALSE); +error1: + return err; +} + + +/* + * sys_mount + */ + +void * +sys_mount(bool kernel, const char *filesystem, int fd, const char *where, + const char *device, ulong flags, void *parms, size_t len) +{ + int err; + int i; + vnode_id vnid; + vnode *mount; + fsystem *fs; + nspace *ns, *ans; + void *data; + struct stat st; + struct my_stat mst; + my_dev_t dev; + my_ino_t ino; + + dev = -1; + ino = -1; + if (device) { + err = stat(device, &st); + if (err) + return NULL; + dev = st.st_dev; + ino = st.st_ino; + } + + err = get_file_fd(TRUE, fd, where, TRUE, &mount); + if (err) + goto error1; + + err = (*mount->ns->fs->ops.rstat)(mount->ns->data, mount->data, &mst); + if (err) + goto error2; + if (!MY_S_ISDIR(mst.st_mode)) { + err = ENOTDIR; + goto error2; + } + + ns = (nspace *) malloc(sizeof(nspace)); + if (!ns) { + err = ENOMEM; + goto error2; + } + + fs = inc_file_system(filesystem); + if (!fs) { + err = ENODEV; + goto error3; + } + + LOCK(vnlock); + + if (device) { + for(ans=nshead; ans; ans=ans->next) + if ((ans->dev == dev) && (ans->ino == ino)) { + UNLOCK(vnlock); +printf("KERNEL: trying to mount %s twice (already mounted as %s)\n", device, ans->fs->name); + err = ENODEV; + goto error4; + } + } + + for(i=0; insid = nxnsid; + nxnsid++; + break; + } + if (i == nns) { + UNLOCK(vnlock); + err = EMFILE; + goto error4; + } + nstab[ns->nsid % nns] = ns; + ns->fs = fs; + ns->vnodes.head = ns->vnodes.tail = NULL; + ns->data = NULL; + ns->root = NULL; + ns->mount = NULL; + ns->prev = NULL; + ns->next = nshead; + ns->shutdown = FALSE; + ns->dev = dev; + ns->ino = ino; + if (nshead) + nshead->prev = ns; + nshead = ns; + + UNLOCK(vnlock); + + err = (*fs->ops.mount)(ns->nsid, device, flags, parms, len, &data, &vnid); + if (err) + goto error5; + + LOCK(vnlock); + ns->root = lookup_vnode(ns->nsid, vnid); + ns->data = data; + + if ((mount == rootvn) || (mount->mounted)) { + err = EBUSY; + goto error6; + } + mount->mounted = ns; + ns->mount = mount; + + + UNLOCK(vnlock); + + return data; + +error6: + dec_vnode(ns->root, FALSE); + (*fs->ops.unmount)(data); +error5: + LOCK(vnlock); + nstab[ns->nsid % nns] = NULL; + if (ns->prev) + ns->prev->next = ns->next; + else + nshead = ns->next; + if (ns->next) + ns->next->prev = ns->prev; + UNLOCK(vnlock); +error4: + dec_file_system(fs); +error3: + free(ns); +error2: + dec_vnode(mount, FALSE); +error1: + errno = err; + return NULL; +} + + +/* + * sys_unmount + */ + +int +sys_unmount(bool kernel, int fd, const char *where) +{ + int err; + nspace *ns; + fsystem *fs; + vnode *root, *vn, *mount; + + err = get_file_fd(TRUE, fd, where, TRUE, &root); + if (err) + goto error1; + + LOCK(vnlock); + + ns = root->ns; + fs = ns->fs; + if (ns->root != root) { + err = EINVAL; + goto error2; + } + + + /* + don't allow to unmount the root file system + */ + + if (root == rootvn) { + err = EBUSY; + goto error2; + } + + /* + decrement twice root: one for the mount, one for the get_file. + */ + + root->rcnt -= 2; + + for(vn = ns->vnodes.head; vn; vn = vn->nspace.next) + if (vn->busy || (vn->rcnt != 0)) { + err = EBUSY; + goto error3; + } + + mount = ns->mount; + mount->mounted = NULL; + + ns->shutdown = TRUE; + for(vn = ns->vnodes.head; vn; vn = vn->nspace.next) + vn->busy = TRUE; + + while (ns->vnodes.head) { + vn = ns->vnodes.head; + UNLOCK(vnlock); + err = (*fs->ops.write_vnode)(vn->ns->data, vn->data, FALSE); + LOCK(vnlock); + if (err) + PANIC("ERROR WRITING VNODE!!!\n"); + vn->busy = FALSE; + clear_vnode(vn); + move_vnode(vn, FREE_LIST); + } + + if (ns->prev) + ns->prev->next = ns->next; + else + nshead = ns->next; + if (ns->next) + ns->next->prev = ns->prev; + + nstab[ns->nsid % nns] = NULL; + UNLOCK(vnlock); + + (*fs->ops.unmount)(ns->data); + + free(ns); + + dec_file_system(fs); + dec_vnode(mount, FALSE); + + return 0; + +error3: + root->rcnt++; +error2: + UNLOCK(vnlock); +error1: + return err; +} + + +int +sys_open_query(bool kernel, int fd, const char *path, const char *query, void **cookie) +{ + int err; + nspace *ns; + fsystem *fs; + vnode *root, *vn, *mount; + + printf("sys_open_query() -- start\n"); + err = get_file_fd(TRUE, fd, path, TRUE, &root); + if (err) + return err; + + ns = root->ns; + fs = ns->fs; + if (ns->root != root) + return EINVAL; + + if (fs->ops.open_query == NULL) { + dec_vnode(root, FALSE); + return EPERM; + } + err = (*fs->ops.open_query)(ns->data, query, 0, -1, 0, cookie); + printf("sys_open_query() -- end: %d\n",err); + dec_vnode(root, FALSE); + + return err; +} + + +int +sys_close_query(bool kernel, int fd, const char *path, void *cookie) +{ + int err; + nspace *ns; + fsystem *fs; + vnode *root, *vn, *mount; + + err = get_file_fd(TRUE, fd, path, TRUE, &root); + if (err) + return err; + + ns = root->ns; + fs = ns->fs; + if (ns->root != root) + return EINVAL; + + if (fs->ops.close_query == NULL) { + dec_vnode(root, FALSE); + return EPERM; + } + + err = (*fs->ops.close_query)(ns->data, cookie); + (*fs->ops.free_querycookie)(ns->data, NULL, cookie); + + dec_vnode(root, FALSE); + + return err; +} + + +int +sys_read_query(bool kernel, int fd, const char *path, void *cookie,struct dirent *dent,size_t bufferSize,long count) +{ + int err; + nspace *ns; + fsystem *fs; + vnode *root, *vn, *mount; + long num; + + err = get_file_fd(TRUE, fd, path, TRUE, &root); + if (err) + return err; + + ns = root->ns; + fs = ns->fs; + if (ns->root != root) + return EINVAL; + + if (fs->ops.close_query == NULL) { + dec_vnode(root, FALSE); + return EPERM; + } + + num = count; + err = (*fs->ops.read_query)(ns->data, cookie, &num, dent, bufferSize); + + dec_vnode(root, FALSE); + + if (err == 0) + return num; + + return err; +} + +/* + * get_dir and get_file: basic functions to parse a path and get the vnode + * for either the parent directory or the file itself. + */ + +static int +get_dir_fd(bool kernel, int fd, const char *path, char *filename, vnode **dvn) +{ + int err; + char *p, *np; + + err = new_path(path, &p); + if (err) + goto error1; + np = strrchr(p, '/'); + if (!np) { + strcpy(filename, p); + strcpy(p, "."); + } else { + strcpy(filename, np+1); + np[1] = '.'; + np[2] = '\0'; + } + err = parse_path_fd(kernel, fd, &p, TRUE, dvn); + if (err) + goto error2; + free_path(p); + return 0; + +error2: + free_path(p); +error1: + return err; +} + +static int +get_file_fd(bool kernel, int fd, const char *path, int eatsymlink, vnode **vn) +{ + int err; + char *p; + + err = new_path(path, &p); + if (err) + goto error1; + err = parse_path_fd(kernel, fd, &p, eatsymlink, vn); + if (err) + goto error2; + free_path(p); + return 0; + +error2: + free_path(p); +error1: + return err; +} + +static int +get_file_vn(nspace_id nsid, vnode_id vnid, const char *path, int eatsymlink, + vnode **vn) +{ + int err; + char *p; + + err = new_path(path, &p); + if (err) + goto error1; + err = parse_path_vn(nsid, vnid, &p, eatsymlink, vn); + if (err) + goto error2; + free_path(p); + return 0; + +error2: + free_path(p); +error1: + return err; +} + +static int +parse_path_fd(bool kernel, int fd, char **pstart, int eatsymlink, vnode **vnp) +{ + vnode *bvn; + ofile *f; + ioctx *io; + char *path; + + path = *pstart; + if (path && (*path == '/')) { + do + path++; + while (*path == '/'); + bvn = rootvn; + inc_vnode(bvn); + } else + if (fd >= 0) { + f = get_fd(kernel, fd, FD_ALL); + if (!f) + return EBADF; + bvn = f->vn; + inc_vnode(bvn); + put_fd(f); + } else { + io = get_cur_ioctx(); + LOCK(io->lock); + bvn = io->cwd; + inc_vnode(bvn); + UNLOCK(io->lock); + } + return parse_path(bvn, pstart, path, eatsymlink, vnp); +} + +static int +parse_path_vn(nspace_id nsid, vnode_id vnid, char **pstart, int eatsymlink, + vnode **vnp) +{ + int err; + vnode *bvn; + char *path; + + path = *pstart; + if (path && (*path == '/')) { + do + path++; + while (*path == '/'); + bvn = rootvn; + inc_vnode(bvn); + } else { + err = load_vnode(nsid, vnid, FALSE, &bvn); + if (err) + return err; + } + return parse_path(bvn, pstart, path, eatsymlink, vnp); + +error1: + dec_vnode(bvn, FALSE); + return err; +} + +static int +parse_path(vnode *bvn, char **pstart, char *path, int eatsymlink, vnode **vnp) +{ + int err; + int iter; + char *p, *np, *newpath, **fred; + vnode_id vnid; + vnode *vn; + + if (!path) { + *vnp = bvn; + return 0; + } + + iter = 0; + p = path; + vn = NULL; + + while(TRUE) { + + /* + exit if we're done + */ + + if (*p == '\0') { + err = 0; + break; + } + + /* + isolate the next component + */ + + np = strchr(p+1, '/'); + if (np) { + *np = '\0'; + do + np++; + while (*np == '/'); + } else + np = strchr(p+1, '\0'); + + /* + filter '..' if at the root of a namespace + */ + + if (!strcmp(p, "..") && is_root(bvn, &vn)) { + dec_vnode(bvn, FALSE); + bvn = vn; + } + + /* + ask the file system to eat this component + */ + + newpath = NULL; + fred = &newpath; + if (!eatsymlink && (*np == '\0')) + fred = NULL; + + err = (*bvn->ns->fs->ops.walk)(bvn->ns->data, bvn->data, p, fred, + &vnid); + p = np; + if (!err) { + if (newpath) + vn = bvn; + else { + LOCK(vnlock); + vn = lookup_vnode(bvn->ns->nsid, vnid); + UNLOCK(vnlock); + dec_vnode(bvn, FALSE); + } + } else { + dec_vnode(bvn, FALSE); + break; + } + + /* + deal with symbolic links + */ + + if (newpath) { + + /* + protection against cyclic graphs (with bad symbolic links). + */ + + iter++; + if (iter > MAX_SYM_LINKS) { + dec_vnode(vn, FALSE); + err = ELOOP; + break; + } + + p = cat_paths(newpath, np); + if (!p) { + dec_vnode(vn, FALSE); + err = ENOMEM; + break; + } + free_path(*pstart); + *pstart = p; + if (*p == '/') { + do + p++; + while (*p == '/'); + dec_vnode(vn, FALSE); + bvn = rootvn; + inc_vnode(bvn); + } else + bvn = vn; + continue; + } + + /* + reached a mounting point + */ + + if (is_mount_vnode(vn, &bvn)) { + dec_vnode(vn, FALSE); + continue; + } + + bvn = vn; + } + + if (!err) + *vnp = bvn; + + return err; +} + + + +/* + * get_vnode + */ + +int +get_vnode(nspace_id nsid, vnode_id vnid, void **data) +{ + int err; + vnode *vn; + + err = load_vnode(nsid, vnid, TRUE, &vn); + if (err) + return err; + *data = vn->data; + return 0; +} + + +/* + * put_vnode + */ + +int +put_vnode(nspace_id nsid, vnode_id vnid) +{ + vnode *vn; + + LOCK(vnlock); + vn = lookup_vnode(nsid, vnid); + if (!vn) { + UNLOCK(vnlock); + return ENOENT; + } + UNLOCK(vnlock); + dec_vnode(vn, TRUE); + return 0; +} + +/* + * new_vnode + */ + +int +new_vnode(nspace_id nsid, vnode_id vnid, void *data) +{ + int err; + vnode *vn; + + LOCK(vnlock); + vn = steal_vnode(FREE_LIST); + if (!vn) { + vn = steal_vnode(USED_LIST); + if (!vn) { + PANIC("OUT OF VNODE!!!\n"); + UNLOCK(vnlock); + return ENOMEM; + } + flush_vnode(vn, TRUE); + } + + vn->ns = nsidtons(nsid); + if (!vn->ns) { + UNLOCK(vnlock); + return ENOENT; + } + vn->vnid = vnid; + vn->data = data; + vn->rcnt = 1; + err = sort_vnode(vn); + UNLOCK(vnlock); + return err; +} + +/* + * remove_vnode + */ + +int +remove_vnode(nspace_id nsid, vnode_id vnid) +{ + vnode *vn; + + LOCK(vnlock); + vn = lookup_vnode(nsid, vnid); + if (!vn || (vn->rcnt == 0)) { + UNLOCK(vnlock); + return ENOENT; + } + vn->remove = TRUE; + UNLOCK(vnlock); + return 0; +} + +/* + * unremove_vnode + */ + +int +unremove_vnode(nspace_id nsid, vnode_id vnid) +{ + vnode *vn; + + LOCK(vnlock); + vn = lookup_vnode(nsid, vnid); + if (!vn || (vn->rcnt == 0)) { + UNLOCK(vnlock); + return ENOENT; + } + vn->remove = FALSE; + UNLOCK(vnlock); + return 0; +} + +/* + * is_vnode_removed + */ + +int +is_vnode_removed(nspace_id nsid, vnode_id vnid) +{ + vnode *vn; + int res; + + LOCK(vnlock); + vn = lookup_vnode(nsid, vnid); + if (!vn) { + UNLOCK(vnlock); + return ENOENT; + } + res = vn->remove; + UNLOCK(vnlock); + return res; +} + +/* + * miscelleanous vnode functions + */ + + +static void +inc_vnode(vnode *vn) +{ + LOCK(vnlock); + vn->rcnt++; + UNLOCK(vnlock); +} + +static void +dec_vnode(vnode *vn, char r) +{ + vnode *ovn; + + LOCK(vnlock); + vn->rcnt--; + if (vn->rcnt == 0) + if (vn->remove) { + vn->busy = TRUE; + move_vnode(vn, LOCKED_LIST); + UNLOCK(vnlock); + (*vn->ns->fs->ops.remove_vnode)(vn->ns->data, vn->data, r); + LOCK(vnlock); + clear_vnode(vn); + move_vnode(vn, FREE_LIST); + } else { + move_vnode(vn, USED_LIST); + if (lists[USED_LIST].num > usdvnnum) { + ovn = steal_vnode(USED_LIST); + flush_vnode(ovn, r); + move_vnode(ovn, FREE_LIST); + } + } + UNLOCK(vnlock); + + return; +} + +static void +clear_vnode(vnode *vn) +{ + DeleteSL(skiplist, vn); + + if (vn->nspace.prev) + vn->nspace.prev->nspace.next = vn->nspace.next; + else + vn->ns->vnodes.head = vn->nspace.next; + if (vn->nspace.next) + vn->nspace.next->nspace.prev = vn->nspace.prev; + else + vn->ns->vnodes.tail = vn->nspace.prev; + vn->nspace.next = vn->nspace.prev = NULL; + + vn->vnid = invalid_vnid; + vn->ns = NULL; + vn->remove = FALSE; + vn->data = NULL; + vn->rcnt = 0; + vn->busy = FALSE; + vn->mounted = NULL; +} + +static int +sort_vnode(vnode *vn) +{ + if (!InsertSL(skiplist, vn)) + return ENOMEM; + + vn->nspace.next = vn->ns->vnodes.head; + vn->nspace.prev = NULL; + if (vn->ns->vnodes.head) + vn->ns->vnodes.head->nspace.prev = vn; + else + vn->ns->vnodes.tail = vn; + vn->ns->vnodes.head = vn; + return 0; +} + +static void +move_vnode(vnode *vn, int list) +{ + if (vn->list.prev) + vn->list.prev->list.next = vn->list.next; + else + lists[vn->inlist].head = vn->list.next; + if (vn->list.next) + vn->list.next->list.prev = vn->list.prev; + else + lists[vn->inlist].tail = vn->list.prev; + lists[vn->inlist].num--; + vn->inlist = list; + vn->list.next = NULL; + vn->list.prev = lists[list].tail; + if (vn->list.prev) + vn->list.prev->list.next = vn; + else + lists[list].head = vn; + lists[list].tail = vn; + lists[list].num++; +} + +static vnode * +steal_vnode(int list) +{ + vnode *vn; + + vn = lists[list].head; + if (!vn) + return NULL; + move_vnode(vn, LOCKED_LIST); + return vn; +} + +static void +flush_vnode(vnode *vn, char r) +{ + int err; + + vn->busy = TRUE; + UNLOCK(vnlock); + err = (*vn->ns->fs->ops.write_vnode)(vn->ns->data, vn->data, r); + if (err) + PANIC("ERROR WRITING VNODE!!!\n"); + LOCK(vnlock); + vn->busy = FALSE; + clear_vnode(vn); +} + +static vnode * +lookup_vnode(nspace_id nsid, vnode_id vnid) +{ + vnode fakevn; + nspace fakens; + + fakens.nsid = nsid; + fakevn.ns = &fakens; + fakevn.vnid = vnid; + return SearchSL(skiplist, &fakevn); +} + +static int +load_vnode(nspace_id nsid, vnode_id vnid, char r, vnode **vnp) +{ + int err; + vnode *vn; + + LOCK(vnlock); + while (TRUE) { + vn = lookup_vnode(nsid, vnid); + if (vn) + if (vn->busy) { + UNLOCK(vnlock); + snooze(SLEEP_TIME); + LOCK(vnlock); + continue; + } else + break; + + vn = steal_vnode(FREE_LIST); + if (!vn) { + vn = steal_vnode(USED_LIST); + if (!vn) + PANIC("OUT OF VNODE!!!\n"); + } else + break; + + flush_vnode(vn, r); + move_vnode(vn, FREE_LIST); + } + + if (vn->ns == NULL) { + vn->ns = nsidtons(nsid); + if (!vn->ns) { + err = ENOENT; + goto error1; + } + vn->vnid = vnid; + vn->busy = TRUE; + err = sort_vnode(vn); + if (err) + goto error2; + move_vnode(vn, LOCKED_LIST); + UNLOCK(vnlock); + err = (*vn->ns->fs->ops.read_vnode)(vn->ns->data, vnid, r, &vn->data); + LOCK(vnlock); + vn->busy = FALSE; + if (err) + goto error2; + vn->rcnt = 1; + } else { + vn->rcnt++; + if (vn->rcnt == 1) + move_vnode(vn, LOCKED_LIST); + } + *vnp = vn; + UNLOCK(vnlock); + return 0; + +error2: + clear_vnode(vn); +error1: + move_vnode(vn, FREE_LIST); + UNLOCK(vnlock); + return err; +} + +static int +compare_vnode(vnode *vna, vnode *vnb) +{ + if (vna->vnid > vnb->vnid) + return 1; + else + if (vna->vnid < vnb->vnid) + return -1; + else + if (vna->ns->nsid > vnb->ns->nsid) + return 1; + else + if (vna->ns->nsid < vnb->ns->nsid) + return -1; + else + return 0; +} + +/* + * path management functions + */ + +int +new_path(const char *path, char **copy) +{ + const char *q, *r; + char *p; + int l, s; + + if (!path) { + *copy = NULL; + return 0; + } + l = strlen(path); + if (l == 0) + return ENOENT; + s = l; + if (path[l-1] == '/') + s++; + + if (l >= MAXPATHLEN) + return ENAMETOOLONG; + + q = path; + while (*q != '\0') { + while (*q == '/') + q++; + r = q; + while ((*q != '/') && (*q != '\0')) + q++; + if (q - r >= FILE_NAME_LENGTH) + return ENAMETOOLONG; + } + + p = (char *) malloc(s+1); + if (!p) + return ENOMEM; + + /* ### do real checking: MAXPATHLEN, max file name len, buffer address... */ + + strcpy(p, path); + if (p[l-1] == '/') { + p[l] = '.'; + p[l+1] = '\0'; + } + *copy = p; + return 0; +} + +void +free_path(char *p) +{ + if (p) { + free(p); + } +} + +static char * +cat_paths(char *a, char *b) +{ + char *p; + + p = (char *) realloc(a, strlen(a) + strlen(b) + 2); + if (!p) + return NULL; + strcat(p, "/"); + strcat(p, b); + return p; +} + +/* + * mount point management functions + */ + +static int +is_mount_vnode(vnode *mount, vnode **root) +{ + nspace *ns; + + LOCK(vnlock); + ns = mount->mounted; + if (ns) { + *root = ns->root; + ns->root->rcnt++; + } + UNLOCK(vnlock); + return (ns != NULL); +} + +static int +is_mount_vnid(nspace_id nsid, vnode_id vnid, vnode_id *mount) +{ + nspace *ns; + + for(ns = nshead; ns; ns = ns->next) { + if (!ns->mount) + continue; + if (ns->mount->ns->nsid != nsid) + continue; + if (ns->mount->vnid != vnid) + continue; + *mount = ns->root->vnid; + return TRUE; + } + return FALSE; +} + +static int +is_root(vnode *root, vnode **mount) +{ + if ((root->ns->root == root) && root->ns->mount) { + *mount = root->ns->mount; + inc_vnode(*mount); + return TRUE; + } else + return FALSE; +} + +/* + * file descriptor management functions + */ + +static ofile * +get_fd(bool kernel, int fd, int type) +{ + ofile *f; + fdarray *fds; + + f = NULL; + if (kernel) + fds = global_fds; + else + fds = get_cur_ioctx()->fds; + LOCK(fds->lock); + if ((fd >= 0) && (fd < fds->num) && fds->fds[fd]) { + f = fds->fds[fd]; + if (f->type & type) + atomic_add(&f->rcnt, 1); + else + f = NULL; + } + UNLOCK(fds->lock); + return f; +} + + +static int +put_fd(ofile *f) +{ + long cnt; + + cnt = atomic_add(&f->rcnt, -1); + if (cnt == 1) + invoke_free(f); + return 0; +} + +static int +new_fd(bool kernel, int nfd, ofile *f, int fd, bool coe) +{ + int i, j, num, end; + fdarray *fds; + ofile *of; + int err; + long cnt; + + if (kernel) + fds = global_fds; + else + fds = get_cur_ioctx()->fds; + + LOCK(fds->lock); + + num = fds->num; + + if (!f) { + if ((fd < 0) || (fd >= num)) { + err = EBADF; + goto error1; + } + f = fds->fds[fd]; + if (!f) { + err = EBADF; + goto error1; + } + } + + atomic_add(&f->rcnt, 1); + atomic_add(&f->ocnt, 1); + + if (nfd >= 0) { + if (nfd >= num) { + err = EBADF; + goto error2; + } + of = fds->fds[nfd]; + fds->fds[nfd] = f; + SETBIT(fds->alloc, nfd, TRUE); + SETBIT(fds->coes, nfd, coe); + + UNLOCK(fds->lock); + if (of) { + cnt = atomic_add(&of->ocnt, -1); + if (cnt == 1) + invoke_close(of); + cnt = atomic_add(&of->rcnt, -1); + if (cnt == 1) + invoke_free(of); + } + return nfd; + } + + end = num & ~31; + for(j=0; jalloc[j/32] != 0xffffffff) + for(i=j; ialloc, i)) + goto found; + for(i=end; ialloc, i)) + goto found; + + err = EMFILE; + goto error2; + +found: + + SETBIT(fds->alloc, i, 1); + fds->fds[i] = f; + SETBIT(fds->coes, i, coe); + UNLOCK(fds->lock); + return i; + +error2: + atomic_add(&f->rcnt, -1); + atomic_add(&f->ocnt, -1); +error1: + UNLOCK(fds->lock); + return err; +} + +static int +remove_fd(bool kernel, int fd, int type) +{ + ofile *f; + fdarray *fds; + long cnt; + int err; + + f = NULL; + if (kernel) + fds = global_fds; + else + fds = get_cur_ioctx()->fds; + LOCK(fds->lock); + if ((fd >= 0) && (fd < fds->num) && fds->fds[fd]) { + f = fds->fds[fd]; + if (f->type == type) { + SETBIT(fds->alloc, fd, 0); + fds->fds[fd] = NULL; + } else + f = NULL; + } + UNLOCK(fds->lock); + if (f == NULL) + return EBADF; + + err = 0; + cnt = atomic_add(&f->ocnt, -1); + if (cnt == 1) + err = invoke_close(f); + cnt = atomic_add(&f->rcnt, -1); + if (cnt == 1) + invoke_free(f); + return err; +} + +static int +get_coe(bool kernel, int fd, int type, bool *coe) +{ + ofile *f; + fdarray *fds; + + f = NULL; + if (kernel) + fds = global_fds; + else + fds = get_cur_ioctx()->fds; + LOCK(fds->lock); + if ((fd >= 0) && (fd < fds->num) && fds->fds[fd]) { + f = fds->fds[fd]; + if (f->type == type) { + *coe = GETBIT(fds->coes, fd); + UNLOCK(fds->lock); + return 0; + } + } + UNLOCK(fds->lock); + return EBADF; +} + +static int +set_coe(bool kernel, int fd, int type, bool coe) +{ + ofile *f; + fdarray *fds; + + f = NULL; + if (kernel) + fds = global_fds; + else + fds = get_cur_ioctx()->fds; + LOCK(fds->lock); + if ((fd >= 0) && (fd < fds->num) && fds->fds[fd]) { + f = fds->fds[fd]; + if (f->type == type) { + SETBIT(fds->coes, fd, coe); + UNLOCK(fds->lock); + return 0; + } + } + UNLOCK(fds->lock); + return EBADF; +} + +static int +get_omode(bool kernel, int fd, int type, int *omode) +{ + ofile *f; + fdarray *fds; + + f = NULL; + if (kernel) + fds = global_fds; + else + fds = get_cur_ioctx()->fds; + LOCK(fds->lock); + if ((fd >= 0) && (fd < fds->num) && fds->fds[fd]) { + f = fds->fds[fd]; + if (f->type == type) { + *omode = f->omode; + UNLOCK(fds->lock); + return 0; + } + } + UNLOCK(fds->lock); + return EBADF; +} + +static int +invoke_close(ofile *f) +{ + int err; + vnode *vn; + + vn = f->vn; + switch (f->type) { + case FD_FILE: + err = (*vn->ns->fs->ops.close)(vn->ns->data, vn->data, f->cookie); + break; + case FD_DIR: + err = (*vn->ns->fs->ops.closedir)(vn->ns->data, vn->data, f->cookie); + break; + case FD_WD: + default: + err = 0; + break; + } + return err; +} + +static int +invoke_free(ofile *f) +{ + vnode *vn; + op_free_cookie *op = NULL; + + vn = f->vn; + switch(f->type) { + case FD_FILE: + op = vn->ns->fs->ops.free_cookie; + break; + case FD_DIR: + op = vn->ns->fs->ops.free_dircookie; + break; + case FD_WD: + op = NULL; + break; + } + if (op) + (*op)(vn->ns->data, vn->data, f->cookie); + dec_vnode(vn, FALSE); + + free(f); + return 0; +} + +/* + * other routines + */ + +static nspace * +nsidtons(nspace_id nsid) +{ + nspace *ns; + + ns = nstab[nsid % nns]; + if (!ns || (ns->nsid != nsid) || ns->shutdown) + return NULL; + return ns; +} + +static int +alloc_wd_fd(bool kernel, vnode *vn, bool coe, int *fdp) +{ + int err; + ofile *f; + int nfd; + + /* + find a file descriptor + */ + + f = (ofile *) calloc(sizeof(ofile), 1); + if (!f) { + err = ENOMEM; + goto error1; + } + + f->type = FD_WD; + f->vn = vn; + f->rcnt = 0; + f->ocnt = 0; + + nfd = new_fd(kernel, -1, f, -1, coe); + if (nfd < 0) { + err = EMFILE; + goto error2; + } + + *fdp = nfd; + return 0; + +error2: + free(f); +error1: + return err; +} + + + +/* + * file system operations + */ + +void * +install_file_system(vnode_ops *ops, const char *name, bool fixed, image_id aid) +{ + fsystem *fs; + int i; + + fs = (fsystem *) malloc(sizeof(fsystem)); + if (!fs) + return NULL; + + memcpy(&fs->ops, ops, sizeof(vnode_ops)); + strcpy(fs->name, name); + fs->rcnt = 1; + fs->fixed = fixed; + fs->aid = aid; + + for(i=0; ifsid = nxfsid; + nxfsid++; + break; + } + + if (i == nfs) { + free(fs); + return NULL; + } + return (void *)fs; +} + +static fsystem * +load_file_system(const char *name) +{ + return NULL; +} + +static int +unload_file_system(fsystem *fs) +{ + fstab[fs->fsid % nfs] = NULL; + free(fs); + return 0; +} + + +static fsystem * +inc_file_system(const char *name) +{ + fsystem *fs; + int i; + + fs = NULL; + LOCK(fstablock); + + for(i=0; iname, name)) { + fs = fstab[i]; + fs->rcnt++; + break; + } + + if (!fs) + fs = load_file_system(name); + + UNLOCK(fstablock); + + return fs; +} + +static int +dec_file_system(fsystem *fs) +{ + LOCK(fstablock); + + fs->rcnt--; + if (!fs->fixed && (fs->rcnt == 0)) + unload_file_system(fs); + + UNLOCK(fstablock); + return 0; +} + + +static fdarray * +new_fds(int num) +{ + fdarray *fds; + size_t sz; + + sz = sizeof(fdarray) + (num-1) * sizeof(void *) + 2*BITSZ(num); + fds = (fdarray *) malloc(sz); + if (!fds) + return NULL; + memset(fds, 0, sz); + fds->rcnt = 1; + if (new_lock(&fds->lock, "fdlock") < 0) { + free(fds); + return NULL; + } + fds->num = num; + fds->alloc = (ulong *) &fds->fds[num]; + fds->coes = &fds->alloc[BITSZ(num) / sizeof(ulong)]; + return fds; +} + +static int +free_fds(fdarray *fds) +{ + long cnt; + int i; + ofile *f; + + for(i=0; inum; i++) + if (fds->fds[i]) { + f = fds->fds[i]; + cnt = atomic_add(&f->ocnt, -1); + if (cnt == 1) + invoke_close(f); + cnt = atomic_add(&f->rcnt, -1); + if (cnt == 1) + invoke_free(f); + } + delete_sem(fds->lock.s); + free(fds); + return 0; +} + + +// dummies + + +int +notify_listener(int op, nspace_id nsid, vnode_id vnida, vnode_id vnidb, vnode_id vnidc, const char *name) +{ +#ifdef DEBUG + char *text; + + switch (op) { + case B_ENTRY_CREATED: + text = "B_ENTRY_CREATED"; + break; + case B_ENTRY_REMOVED: + text = "B_ENTRY_REMOVED"; + break; + case B_ENTRY_MOVED: + text = "B_ENTRY_MOVED"; + break; + case B_STAT_CHANGED: + text = "B_STAT_CHANGED"; + break; + case B_ATTR_CHANGED: + text = "B_ATTR_CHANGED"; + break; + case B_DEVICE_MOUNTED: + text = "B_DEVICE_MOUNTED"; + break; + case B_DEVICE_UNMOUNTED: + text = "B_DEVICE_UNMOUNTED"; + break; + default: + text = "unknown operation..."; + break; + } + printf("notify_listener: %s\n",text); +#endif + return 0; +} + + +int +send_notification(port_id port, long token, ulong what, long op, nspace_id nsida, + nspace_id nsidb, vnode_id vnida,vnode_id vnidb, vnode_id vnidc, + const char *name) +{ +#ifdef DEBUG + printf("send_notification... op = %s, name = %s\n",op == B_ENTRY_CREATED ? "B_ENTRY_CREATED" : "B_ENTRY_REMOVED",name); +#endif + return 0; +} + + +void +notify_select_event(selectsync *sync, uint32 ref) +{ +} + diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/kprotos.h b/src/tests/add-ons/kernel/file_systems/fs_shell/kprotos.h new file mode 100644 index 0000000000..9b5229d539 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/kprotos.h @@ -0,0 +1,52 @@ +#define my_stat stat +#define my_dirent dirent + +int sys_symlink(bool kernel, const char *oldpath, int nfd, + const char *newpath); +ssize_t sys_readlink(bool kernel, int fd, const char *path, char *buf, + size_t bufsize); +int sys_mkdir(bool kernel, int fd, const char *path, int perms); +int sys_open(bool kernel, int fd, const char *path, int omode, + int perms, bool coe); +int sys_close(bool kernel, int fd); +fs_off_t sys_lseek(bool kernel, int fd, fs_off_t pos, int whence); +ssize_t sys_read(bool kernel, int fd, void *buf, size_t len); +ssize_t sys_write(bool kernel, int fd, void *buf, size_t len); +int sys_ioctl(bool kernel, int fd, int cmd, void *arg, size_t sz); +int sys_unlink(bool kernel, int fd, const char *path); +int sys_link(bool kernel, int ofd, const char *oldpath, int nfd, + const char *newpath); +int sys_rmdir(bool kernel, int fd, const char *path); +int sys_rename(bool glb, int fd, const char *oldpath, + int nfd, const char *newpath); +void *sys_mount(bool kernel, const char *filesystem, int fd, + const char *where, const char *device, ulong flags, + void *parms, size_t len); +int sys_unmount(bool kernel, int fd, const char *where); +int sys_rstat(bool kernel, int fd, const char *path, struct my_stat *st, + bool eatlink); +int sys_wstat(bool kernel, int fd, const char *path, struct my_stat *st, + long mask, bool eatlink); +int sys_ioctl(bool kernel, int fd, int cmd, void *arg, size_t sz); + +int sys_opendir(bool kernel, int fd, const char *path, bool coe); +int sys_readdir(bool kernel, int fd, struct my_dirent *buf, size_t bufsize, + long count); +int sys_rewinddir(bool kernel, int fd); +int sys_closedir(bool kernel, int fd); +int sys_chdir(bool kernel, int fd, const char *path); +int sys_access(bool kernel, int fd, const char *path, int mode); + +int sys_sync(void); + +ssize_t sys_read_attr(bool kernel, int fd, const char *name, int type, void *buffer, size_t len, off_t pos); +ssize_t sys_write_attr(bool kernel, int fd, const char *name, int type, void *buffer, size_t len, off_t pos); +ssize_t sys_remove_attr(bool kernel, int fd, const char *name); + +int sys_open_query(bool kernel, int fd, const char *path, const char *query, void **cookie); +int sys_close_query(bool kernel, int fd, const char *path, void *cookie); +int sys_read_query(bool kernel, int fd, const char *path, void *cookie,struct dirent *dent,size_t bufferSize,long num); + +int init_vnode_layer(void); +void *install_file_system(vnode_ops *ops, const char *name, + bool fixed, image_id aid); diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/lock.h b/src/tests/add-ons/kernel/file_systems/fs_shell/lock.h new file mode 100644 index 0000000000..c8fe856a57 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/lock.h @@ -0,0 +1,28 @@ +#ifndef _LOCK_H +#define _LOCK_H + +typedef struct lock lock; +typedef struct mlock mlock; + +struct lock { + sem_id s; + long c; +}; + +struct mlock { + sem_id s; +}; + +extern int new_lock(lock *l, const char *name); +extern int free_lock(lock *l); + +#define LOCK(l) if (atomic_add(&l.c, -1) <= 0) acquire_sem(l.s); +#define UNLOCK(l) if (atomic_add(&l.c, 1) < 0) release_sem(l.s); + +extern int new_mlock(mlock *l, long c, const char *name); +extern int free_mlock(mlock *l); + +#define LOCKM(l,cnt) acquire_sem_etc(l.s, cnt, 0, 0.0) +#define UNLOCKM(l,cnt) release_sem_etc(l.s, cnt, 0) + +#endif diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/makefile b/src/tests/add-ons/kernel/file_systems/fs_shell/makefile new file mode 100644 index 0000000000..9ff522c957 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/makefile @@ -0,0 +1,65 @@ +TARGETS = fsh tstfs + +all : $(TARGETS) + +zip: + zip -y obfs-fskit-`date +%Y-%m-%d`.zip *.[ch]* makefile + +# +# change the -O7 to -O3 if your compiler doesn't grok -O7 +# +DEFINES = -DUSER=1 -DDEBUG=1 +CFLAGS = -D_NO_INLINE_ASM -O0 -g -fno-exceptions -fno-rtti -I. -fcheck-memory-usage +LDFLAGS = #-p + +SUPPORT_OBJS = rootfs.o initfs.o kernel.o cache.o sl.o stub.o +MISC_OBJS = sysdep.o hexdump.o argv.o + +FS_OBJS = Volume.o BPlusTree.o Inode.o Index.o Query.o Journal.o \ + BlockAllocator.o kernel_interface.o Utility.o Debug.o BufferPool.o cpp.o + + +fsh : fsh.o $(FS_OBJS) $(SUPPORT_OBJS) $(MISC_OBJS) + cc $(LDFLAGS) -o $@ fsh.o $(FS_OBJS) $(SUPPORT_OBJS) $(MISC_OBJS) + +tstfs : tstfs.o $(FS_OBJS) $(SUPPORT_OBJS) $(MISC_OBJS) + cc $(LDFLAGS) -o $@ tstfs.o $(FS_OBJS) $(SUPPORT_OBJS) $(MISC_OBJS) + +makefs : makefs.o $(FS_OBJS) $(SUPPORT_OBJS) $(MISC_OBJS) + cc $(LDFLAGS) -o $@ makefs.o $(FS_OBJS) $(SUPPORT_OBJS) $(MISC_OBJS) + + +.c.o: + $(CC) -c $(DEFINES) $(CFLAGS) -o $@ $< + +.cpp.o: + $(CC) -c $(DEFINES) $(CFLAGS) -o $@ $< + + +#makefs.o : makefs.c bfs.h +fsh.o : fsh.c bfs.h +tstfs.o : tstfs.c bfs.h + + +# mount.o : mount.c myfs.h +# journal.o : journal.c myfs.h +# bitmap.o : bitmap.c myfs.h +# inode.o : inode.c myfs.h +# dstream.o : dstream.c myfs.h +# dir.o : dir.c myfs.h +# file.o : file.c myfs.h +# bitvector.o : bitvector.c bitvector.h +# util.o : util.c myfs.h +# +bfs.h : compat.h cache.h lock.h fsproto.h + +sysdep.o : sysdep.c compat.h +kernel.o : kernel.c compat.h fsproto.h kprotos.h +rootfs.o : compat.h fsproto.h +initfs.o : initfs.c compat.h fsproto.h +sl.o : sl.c skiplist.h +cache.o : cache.c cache.h compat.h +stub.o : stub.c compat.h + +clean: + rm -f *.o $(TARGETS) diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/makefs.c b/src/tests/add-ons/kernel/file_systems/fs_shell/makefs.c new file mode 100644 index 0000000000..99da2f6f95 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/makefs.c @@ -0,0 +1,82 @@ +/* + This file contains the code that will call the initialization routine + for a file system (which in turn will initialize the file system). It + also has to do a few other housekeeping chores to make sure that the + file system is unmounted properly and all that. + + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "myfs.h" +#include "kprotos.h" + + +static int +get_value(char *str) +{ + char buff[128]; + + printf("%s: ", str); fflush(stdout); + fgets(buff, sizeof(buff), stdin); + + return strtol(buff, NULL, 0); +} + + +int +main(int argc, char **argv) +{ + int block_size = 1024, i; + char *disk_name = "big_file"; + char *volume_name = "untitled"; + myfs_info *myfs; + + for (i=1; i < argc; i++) { + if (isdigit(argv[i][0])) { + block_size = strtoul(argv[i], NULL, 0); + } else if (disk_name == NULL) { + disk_name = argv[i]; + } else { + volume_name = argv[i]; + } + } + + if (disk_name == NULL) { + fprintf(stderr, "makefs error: you must specify a file name that\n"); + fprintf(stderr, " will contain the file systemn"); + exit(5); + } + + init_block_cache(256, 0); + + myfs = myfs_create_fs(disk_name, volume_name, block_size, NULL); + if (myfs != NULL) + printf("MYFS w/%d byte blocks successfully created on %s as %s\n", + block_size, disk_name, volume_name); + else { + printf("!HOLA! FAILED to create a MYFS file system on %s\n", disk_name); + exit(5); + } + + myfs_unmount(myfs); + + shutdown_block_cache(); + + return 0; +} diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/mount.c b/src/tests/add-ons/kernel/file_systems/fs_shell/mount.c new file mode 100644 index 0000000000..6f8e4b7486 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/mount.c @@ -0,0 +1,416 @@ +/* + This file contains the code that will create a file system, mount + a file system and unmount a file system. + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ +#include "myfs.h" + + +#ifndef min_c +#define min_c(a, b) (((a) < (b)) ? (a) : (b)) +#endif /* min_c */ + + + +myfs_info * +myfs_create_fs(char *device, char *name, int block_size, char *opts) +{ + int dev_block_size, bshift, warned = 0; + char *ptr; + fs_off_t num_dev_blocks; + myfs_info *myfs; + + if ((block_size % sizeof(myfs_inode)) != 0) { + printf("ERROR: inode size %d is not an even divisor of the block " + "size %d\n", sizeof(myfs_inode), block_size); + printf(" check myfs.h for more details and info.\n"); + return NULL; + } + + if (name == NULL) + name = "untitled"; + + for(ptr=name; *ptr; ptr++) { + if (*ptr == '/') { + if (warned == 0) { + fprintf(stderr, "Volume name: %s contains the '/' character. " + "They are being converted to '-' for safety.\n", name); + warned = 1; + } + *ptr = '-'; + } + } + + if (block_size < 512) { + printf("minimum block size is 512 bytes\n"); + block_size = 512; + } + + for(bshift=0; bshift < sizeof(int)*8; bshift++) + if ((1 << bshift) == block_size) + break; + + if (bshift >= sizeof(int)*8) { + printf("block_size %d is not a power of two!\n", block_size); + return NULL; + } + + + myfs = (myfs_info *)calloc(1, sizeof(myfs_info)); + if (myfs == NULL) { + printf("can't allocate mem for myfs_info struct\n"); + return NULL; + } + + myfs->fd = -1; + + myfs->nsid = (nspace_id)myfs; /* we can only do this when creating */ + + myfs->dsb.magic1 = SUPER_BLOCK_MAGIC1; + myfs->dsb.magic2 = SUPER_BLOCK_MAGIC2; + myfs->dsb.magic3 = SUPER_BLOCK_MAGIC3; + myfs->dsb.fs_byte_order = MYFS_BIG_ENDIAN; /* checked when mounting */ + + myfs->sem = create_sem(MAX_READERS, "myfs_sem"); + if (myfs->sem < 0) { + printf("can't create semaphore!\n"); + goto cleanup; + } + + + myfs->fd = open(device, O_RDWR); + if (myfs->fd < 0) { + printf("can't open device %s\n", device); + goto cleanup; + } + + dev_block_size = get_device_block_size(myfs->fd); + num_dev_blocks = get_num_device_blocks(myfs->fd); + if (block_size < dev_block_size) { + printf("warning: fs block size too small, set to device block size %d\n", + dev_block_size); + block_size = dev_block_size; + } + + if ((block_size % dev_block_size) != 0) { + printf("error: block size %d is not an even multiple of ", + block_size); + printf("device block size %d\n", dev_block_size); + + goto cleanup; + } + + myfs->dsb.block_size = block_size; + myfs->dsb.block_shift = bshift; + myfs->dev_block_conversion = block_size / dev_block_size; + myfs->dev_block_size = dev_block_size; + myfs->dsb.num_blocks = num_dev_blocks / myfs->dev_block_conversion; + + init_cache_for_device(myfs->fd, num_dev_blocks / myfs->dev_block_conversion); + + + if (init_tmp_blocks(myfs) != 0) { + printf("init_tmp_blocks failed\n"); + goto cleanup; + } + + if (myfs_create_storage_map(myfs) != 0) { + printf("create storage map failed\n"); + goto cleanup; + } + + if (myfs_create_inodes(myfs) != 0) { + printf("create inodes failed\n"); + goto cleanup; + } + + if (myfs_create_journal(myfs) != 0) { + printf("create journal failed\n"); + goto cleanup; + } + + if (myfs_create_root_dir(myfs) != 0) { + printf("create root dir failed\n"); + goto cleanup; + } + + strncpy(myfs->dsb.name, name, + min_c(sizeof(myfs->dsb.name) - 1, strlen(name))); + + /* now it's finally safe to write this */ + if (write_super_block(myfs) != 0) { + printf("creating superblock failed\n"); + goto cleanup; + } + + return myfs; + + +cleanup: + if (myfs) { + /* making the file system failed so make sure block zero is bogus */ + if (myfs->fd >= 0) { + static char block[4096]; + + memset(block, 0xff, sizeof(block)); + write_blocks(myfs, 0, block, 1); + } + + myfs_shutdown_storage_map(myfs); + myfs_shutdown_inodes(myfs); + myfs_shutdown_journal(myfs); + shutdown_tmp_blocks(myfs); + + close(myfs->fd); + + delete_sem(myfs->sem); + + free(myfs); + } + + return NULL; + +} + + +static int +super_block_is_sane(myfs_info *myfs) +{ + fs_off_t num_dev_blocks; + int block_size; + + if (myfs->dsb.magic1 != SUPER_BLOCK_MAGIC1 || + myfs->dsb.magic2 != SUPER_BLOCK_MAGIC2 || + myfs->dsb.magic3 != SUPER_BLOCK_MAGIC3) { + + printf("warning: super block magic numbers are wrong:\n"); + printf("0x%x (0x%x) 0x%x (0x%x) 0x%x (0x%x)\n", + myfs->dsb.magic1, SUPER_BLOCK_MAGIC1, + myfs->dsb.magic2, SUPER_BLOCK_MAGIC2, + myfs->dsb.magic3, SUPER_BLOCK_MAGIC3); + return 0; + + } + + if ((myfs->dsb.block_size % myfs->dev_block_size) != 0) { + printf("warning: fs block size %d not a multiple of ", + myfs->dsb.block_size); + printf(" device block size %d\n", myfs->dev_block_size); + + return 0; + } + + block_size = get_device_block_size(myfs->fd); + if (block_size == 0) { + printf("warning: could not fetch block size\n"); + return 0; + } + + /* make sure that the partition is as big as the super block + says it is */ + num_dev_blocks = get_num_device_blocks(myfs->fd); + if (myfs->dsb.num_blocks * myfs->dsb.block_size > + num_dev_blocks * block_size) { + printf("warning: fs blocks %lx larger than device blocks %lx\n", + myfs->dsb.num_blocks * (myfs->dsb.block_size/block_size), + num_dev_blocks); + return 0; + } + + if (myfs->dsb.block_size != (1 << myfs->dsb.block_shift)) { + int i; + + printf("warning: block_shift %d does not match block size %d\n", + myfs->dsb.block_shift, myfs->dsb.block_size); + + if (myfs->dsb.block_shift > 8 && myfs->dsb.block_shift < 16) { + printf("setting block_size to %d\n", (1 << myfs->dsb.block_shift)); + myfs->dsb.block_size = (1 << myfs->dsb.block_shift); + } else { + for(i=0; i < sizeof(int) * 8; i++) + if ((1 << i) == myfs->dsb.block_size) + break; + + if (i >= sizeof(int) * 8 || i > 16) { + printf("neither block_size nor block_shift make sense!\n"); + return 0; + } + + myfs->dsb.block_shift = i; + printf("setting block_shift to %d\n", i); + } + } + + return 1; +} + + + +int +myfs_mount(nspace_id nsid, const char *device, ulong flags, + void *parms, size_t len, void **data, vnode_id *vnid) +{ + int ret = 0, oflags = O_RDWR; + char buff[128]; + myfs_info *myfs; + + myfs = (myfs_info *)calloc(1, sizeof(myfs_info)); + if (myfs == NULL) { + printf("no memory for myfs structure!\n"); + return ENOMEM; + } + + myfs->nsid = nsid; + *data = (void *)myfs; + + + sprintf(buff, "myfs:%s", device); + myfs->sem = create_sem(MAX_READERS, buff); + if (myfs->sem < 0) { + printf("could not create myfs sem!\n"); + ret = ENOMEM; + goto error0; + } + + myfs->fd = open(device, oflags); + if (myfs->fd < 0) { + printf("could not open %s to try and mount a myfs\n", device); + ret = ENODEV; + goto error1; + } + + if (read_super_block(myfs) != 0) { + printf("could not read super block on device %s\n", device); + ret = EBADF; + goto error2; + } + + if (super_block_is_sane(myfs) == 0) { + printf("bad super block\n"); + ret = EBADF; + goto error2; + } + + if ((myfs->dsb.block_size % sizeof(myfs_inode)) != 0) { + printf("ERROR: inode size %d is not an even divisor of the block " + "size %d\n", sizeof(myfs_inode), myfs->dsb.block_size); + printf(" check myfs.h for more details and info.\n"); + ret = EINVAL; + goto error2; + } + + if (init_cache_for_device(myfs->fd, myfs->dsb.num_blocks) != 0) { + printf("could not initialize cache access for fd %d\n", myfs->fd); + ret = EBADF; + goto error2; + } + + if (init_tmp_blocks(myfs) != 0) { + printf("could not init tmp blocks\n"); + ret = ENOMEM; + goto error2; + } + + if (myfs_init_journal(myfs) != 0) { + printf("could not initialize the journal\n"); + ret = EBADF; + goto error3; + } + + if (myfs_init_inodes(myfs) != 0) { + printf("could not initialize inodes\n"); + ret = ENOMEM; + goto error5; + } + + if (myfs_init_storage_map(myfs) != 0) { + printf("could not initialize the storage map\n"); + ret = EBADF; + goto error6; + } + + *vnid = myfs->dsb.root_inum; + if (myfs_read_vnode(myfs, *vnid, 0, (void **)&myfs->root_dir) != 0) { + printf("could not read root dir inode\n"); + ret = EBADF; + goto error7; + } + + if (new_vnode(myfs->nsid, *vnid, (void *)myfs->root_dir) != 0) { + printf("could not initialize a vnode for the root directory!\n"); + ret = ENOMEM; + goto error7; + } + + return 0; + + + error7: + myfs_shutdown_storage_map(myfs); + error6: + myfs_shutdown_inodes(myfs); + error5: + myfs_shutdown_journal(myfs); + error3: + shutdown_tmp_blocks(myfs); + error2: + remove_cached_device_blocks(myfs->fd, NO_WRITES); + close(myfs->fd); + error1: + delete_sem(myfs->sem); + error0: + memset(myfs, 0xff, sizeof(*myfs)); /* yeah, I'm paranoid */ + free(myfs); + + return ret; +} + + +/* + note that the order in which things are done here is *very* + important. don't mess with it unless you know what you're doing +*/ +int +myfs_unmount(void *ns) +{ + myfs_info *myfs = (myfs_info *)ns; + + if (myfs == NULL) + return EINVAL; + + sync_journal(myfs); + + myfs_shutdown_storage_map(myfs); + myfs_shutdown_inodes(myfs); + + /* + have to do this after the above steps because the above steps + might actually have to do transactions + */ + sync_journal(myfs); + + remove_cached_device_blocks(myfs->fd, ALLOW_WRITES); + myfs_shutdown_journal(myfs); + + write_super_block(myfs); + + shutdown_tmp_blocks(myfs); + + close(myfs->fd); + + if (myfs->sem > 0) + delete_sem(myfs->sem); + + memset(myfs, 0xff, sizeof(*myfs)); /* trash it just to be sure */ + free(myfs); + + return 0; +} diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/mount.h b/src/tests/add-ons/kernel/file_systems/fs_shell/mount.h new file mode 100644 index 0000000000..6bb8474a7b --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/mount.h @@ -0,0 +1,5 @@ +myfs_info *myfs_create_fs(char *device, char *volname, + int block_size, char *opts); +int myfs_mount(nspace_id nsid, const char *device, ulong flags, + void *parms, size_t len, void **data, vnode_id *vnid); +int myfs_unmount(void *ns); diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/myfs.h b/src/tests/add-ons/kernel/file_systems/fs_shell/myfs.h new file mode 100644 index 0000000000..dd7916bf74 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/myfs.h @@ -0,0 +1,23 @@ +#ifndef MYFS_H +#define MYFS_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#include "compat.h" +#include "fsproto.h" + +#include "lock.h" +#include "cache.h" + +#include "initfs.h" + +typedef void myfs_info; + +#ifdef __cplusplus +} +#endif + +#endif /* MYFS_H */ diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/rootfs.c b/src/tests/add-ons/kernel/file_systems/fs_shell/rootfs.c new file mode 100644 index 0000000000..5b954eaac0 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/rootfs.c @@ -0,0 +1,1109 @@ +/* + This file contains a simple memory based file system that is used + as the top-level name space by the vnode layer. It is a complete + file system in and of itself but it only supports creating directories + and symlinks. It is also entirely memory based so it is re-created + each time the vnode layer is initialized. It is only used to mount + other file systems (i.e. to create a directory that can be used as + a mount point for another file system). + + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ +#include +#include +#include +#include + +#include "compat.h" + +#include "skiplist.h" +#include "lock.h" + +#include "fsproto.h" + +#define my_dirent dirent +#define my_stat stat + +typedef struct vnode vnode; +typedef struct nspace nspace; +typedef struct dirpos dirpos; + + +struct nspace { + nspace_id nsid; + long vnnum; + vnode * root; + vnode_id nxvnid; + lock lock; + SkipList skiplist; +}; + +struct vnode { + char * name; + char removed; + nspace *ns; + vnode_id vnid; + vnode *parent; + time_t crtime; + time_t mtime; + uid_t uid; + gid_t gid; + mode_t mode; + vnode * next; + vnode * prev; + vnode * head; /* for directories */ + char * symlink; /* for symbolic links */ +}; + +struct dirpos { + lock lock; + int pos; + char name[FILE_NAME_LENGTH]; +}; + + +static int rootfs_read_vnode(void *ns, vnode_id vnid, char r, + void **node); +static int rootfs_write_vnode(void *ns, void *node, char r); +static int rootfs_remove_vnode(void *ns, void *node, char r); +static int rootfs_walk(void *ns, void *base, const char *file, + char **newpath, vnode_id *vnid); +static int rootfs_access(void *ns, void *node, int mode); +static int rootfs_symlink(void *ns, void *dir, const char *name, + const char *path); +static int rootfs_mkdir(void *ns, void *dir, const char *name, + int perms); +static int rootfs_rename(void *ns, void *olddir, const char *oldname, + void *newdir, const char *newname); +static int rootfs_unlink(void *ns, void *dir, const char *name); +static int rootfs_rmdir(void *ns, void *dir, const char *name); +static int rootfs_readlink(void *ns, void *node, char *buf, + size_t *bufsize); +static int rootfs_opendir(void *ns, void *node, void **cookie); +static int rootfs_closedir(void *ns, void *node, void *cookie); +static int rootfs_free_dircookie(void *ns, void *node, void *cookie); +static int rootfs_rewinddir(void *ns, void *node, void *cookie); +static int rootfs_readdir(void *ns, void *node, void *cookie, + long *num, struct my_dirent *buf, size_t bufsize); +static int rootfs_rstat(void *ns, void *node, struct my_stat *st); +static int rootfs_wstat(void *ns, void *node, struct my_stat *st, long mask); +static int rootfs_mount(nspace_id nsid, const char *device, ulong flags, + void *parms, size_t len, void **data, vnode_id *vnid); +static int rootfs_unmount(void *ns); + +static int compare_vnode(vnode *vna, vnode *vnb); +static int do_create(nspace *ns, vnode *dir, const char *name, + mode_t mode, vnode **vnp); +static int do_unlink(nspace *ns, vnode *dir, const char *name, bool isdir); + +vnode_ops rootfs = { + &rootfs_read_vnode, + &rootfs_write_vnode, + &rootfs_remove_vnode, + NULL, + &rootfs_walk, + &rootfs_access, + NULL, + &rootfs_mkdir, + &rootfs_symlink, + NULL, + &rootfs_rename, + &rootfs_unlink, + &rootfs_rmdir, + &rootfs_readlink, + &rootfs_opendir, + &rootfs_closedir, + &rootfs_free_dircookie, + &rootfs_rewinddir, + &rootfs_readdir, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &rootfs_rstat, + &rootfs_wstat, + NULL, + NULL, + &rootfs_mount, + &rootfs_unmount, + NULL +}; + + +#define LCK_MULTIPLE 1 +#define LCK_EXCLUSIVE 1000 + +#define OMODE_MASK (O_RDONLY | O_WRONLY | O_RDWR) + + +/* ----------------------------------------------------------------- */ + + +static int +rootfs_walk(void *_ns, void *_base, const char *file, char **newpath, + vnode_id *vnid) +{ + nspace *ns; + vnode *base; + int err; + vnode *vn; + char *np; + + ns = (nspace *) _ns; + base = (vnode *) _base; + + LOCK(ns->lock); + + /* + make sure base is a directory and that it has not been removed. + */ + + if (!MY_S_ISDIR(base->mode)) { + err = ENOTDIR; + goto exit; + } + if (base->removed) { + err = ENOENT; + goto exit; + } + + /* + lookup the special directory '.' + */ + + if (!strcmp(file, ".")) { + err = get_vnode(ns->nsid, base->vnid, (void *)&vn); + if (!err) + *vnid = base->vnid; + goto exit; + } + + /* + lookup the special directory '..' + */ + + if (!strcmp(file, "..")) { + err = get_vnode(ns->nsid, base->parent->vnid, (void *)&vn); + if (!err) + *vnid = vn->vnid; + goto exit; + } + + /* + lookup the name in the directory + */ + + vn = base->head; + while (vn) { + if (!strcmp(vn->name, file)) + break; + vn = vn->next; + } + + /* + the name has not been found. advance the path pointer, update the vnid + and report an error. + */ + + if (!vn) { + err = ENOENT; + goto exit; + } + + /* + we have found the item. + */ + + /* + it is a symbolic link that the kernel wants us to eat. + */ + + if (MY_S_ISLNK(vn->mode) && newpath) { + err = new_path(vn->symlink, &np); + if (err) + goto exit; + + *newpath = np; + + } else { + + /* + it is a directory or it is a symbolic link that the kernel does + not want to 'eat'. + */ + + err = get_vnode(ns->nsid, vn->vnid, (void *)&vn); + if (!err) + *vnid = vn->vnid; + } + +exit: + UNLOCK(ns->lock); + return err; +} + + +static int +rootfs_mkdir(void *_ns, void *_dir, const char *name, int perms) +{ + nspace *ns; + vnode *dir; + int err; + vnode *vn; + + ns = (nspace *) _ns; + dir = (vnode *) _dir; + + LOCK(ns->lock); + err = do_create(ns, dir, name, (perms & ~MY_S_IFMT) | MY_S_IFDIR, &vn); + UNLOCK(ns->lock); + return err; +} + +static int +rootfs_symlink(void *_ns, void *_dir, const char *name, const char *path) +{ + nspace *ns; + vnode *dir; + int err; + char *buf; + vnode *vn; + + ns = (nspace *) _ns; + dir = (vnode *) _dir; + + buf = (char *) malloc(strlen(path)+1); + if (!buf) { + err = ENOMEM; + goto error1; + } + strcpy(buf, path); + LOCK(ns->lock); + err = do_create(ns, dir, name, MY_S_IFLNK, &vn); + if (err) + goto error2; + vn->symlink = buf; + UNLOCK(ns->lock); + return 0; + +error2: + UNLOCK(ns->lock); +error1: + return err; +} + + +static int +rootfs_rename(void *_ns, void *_olddir, const char *oldname, void *_newdir, + const char *newname) +{ + nspace *ns; + vnode *olddir, *newdir; + int err; + vnode *vn, *nvn, *pvn, *avn; + char *p; + + ns = (nspace *) _ns; + olddir = (vnode *) _olddir; + newdir = (vnode *) _newdir; + + LOCK(ns->lock); + if (!MY_S_ISDIR(olddir->mode) || !MY_S_ISDIR(newdir->mode)) { + err = ENOTDIR; + goto error1; + } + + /* + find (olddir, oldname) + */ + + if (!strcmp(oldname, ".") || !strcmp(oldname, "..")) { + err = EPERM; + goto error1; + } + + vn = olddir->head; + while (vn) { + if (!strcmp(vn->name, oldname)) + break; + vn = vn->next; + } + if (!vn) { + err = ENOENT; + goto error1; + } + + /* + look for (newdir, newname) + */ + + if (!strcmp(newname, ".") || !strcmp(newname, "..")) { + err = EPERM; + goto error1; + } + + nvn = newdir->head; + while (nvn) { + if (!strcmp(nvn->name, newname)) + break; + nvn = nvn->next; + } + + /* + don't do anything if old and new are the same + */ + + if (vn == nvn) + goto exit; + + /* + make sure new is not a subdirectory of old + */ + + avn = newdir; + while (avn != ns->root) { + avn = avn->parent; + if (avn == olddir) { + err = EINVAL; + goto error1; + } + } + + if (strlen(newname) > strlen(vn->name)) { + p = (char *) realloc(vn->name, strlen(newname)+1); + if (!p) { + err = ENOMEM; + goto error1; + } + } else + p = vn->name; + + /* + if (newdir, newname) exists, remove it from the name space + */ + + if (nvn) { + + /* + make sure it is not the root and it is not empty + */ + + if (nvn == nvn->ns->root) { + err = EBUSY; + goto error1; + } + if (MY_S_ISDIR(nvn->mode) && nvn->head) { + err = ENOTEMPTY; + goto error1; + } + + err = get_vnode(ns->nsid, nvn->vnid, (void *)&nvn); + if (err) + goto error1; + + err = remove_vnode(ns->nsid, nvn->vnid); + if (err) + goto error1; + + if (nvn->prev) + nvn->prev->next = nvn->next; + else + nvn->parent->head = nvn->next; + if (nvn->next) + nvn->next->prev = nvn->prev; + nvn->prev = nvn->next = NULL; + + put_vnode(ns->nsid, nvn->vnid); + } + + if (vn->prev) + vn->prev->next = vn->next; + else + vn->parent->head = vn->next; + if (vn->next) + vn->next->prev = vn->prev; + + pvn = NULL; + nvn = newdir->head; + while (nvn && (strcmp(newname, nvn->name) > 0)) { + pvn = nvn; + nvn = nvn->next; + } + vn->next = nvn; + if (nvn) + nvn->prev = vn; + vn->prev = pvn; + if (pvn) + pvn->next = vn; + else + newdir->head = vn; + + vn->parent = newdir; + newdir->mtime = olddir->mtime = time(NULL); + strcpy(p, newname); + vn->name = p; + +exit: + UNLOCK(ns->lock); + return 0; + +error1: + UNLOCK(ns->lock); + return err; +} + +static int +rootfs_unlink(void *_ns, void *_dir, const char *name) +{ + nspace *ns; + vnode *dir; + + ns = (nspace *) _ns; + dir = (vnode *) _dir; + + return do_unlink(ns, dir, name, FALSE); +} + +static int +rootfs_rmdir(void *_ns, void *_dir, const char *name) +{ + nspace *ns; + vnode *dir; + + ns = (nspace *) _ns; + dir = (vnode *) _dir; + + return do_unlink(ns, dir, name, TRUE); +} + + + +static int +rootfs_read_vnode(void *_ns, vnode_id vnid, char r, void **node) +{ + nspace *ns; + vnode *vn; + vnode fakevn; + + ns = (nspace *) _ns; + + if (!r) + LOCK(ns->lock); + fakevn.vnid = vnid; + fakevn.ns = ns; + vn = SearchSL(ns->skiplist, &fakevn); + if (vn) + *node = vn; + if (!r) + UNLOCK(ns->lock); + return (vn ? 0 : ENOENT); +} + +static int +rootfs_write_vnode(void *_ns, void *_node, char r) +{ + return 0; +} + +static int +rootfs_remove_vnode(void *_ns, void *_node, char r) +{ + nspace *ns; + vnode *node; + + ns = (nspace *) _ns; + node = (vnode *) _node; + + if (!r) + LOCK(ns->lock); + DeleteSL(ns->skiplist, node); + if (!r) + UNLOCK(ns->lock); + + atomic_add(&ns->vnnum, -1); + if (node->symlink) + free(node->symlink); + free(node->name); + free(node); + return 0; +} + + +static int +rootfs_readlink(void *_ns, void *_node, char *buf, size_t *bufsize) +{ + nspace *ns; + vnode *node; + int err; + size_t l; + + ns = (nspace *) _ns; + node = (vnode *) _node; + + if (!MY_S_ISLNK(node->mode)) { + err = EINVAL; + goto error1; + } + l = strlen(node->symlink); + if (l > *bufsize) + memcpy(buf, node->symlink, *bufsize); + else + memcpy(buf, node->symlink, l); + *bufsize = l; + return 0; + +error1: + return err; +} + +static int +rootfs_opendir(void *_ns, void *_node, void **cookie) +{ + nspace *ns; + vnode *node; + int err; + dirpos *pos; + + ns = (nspace *) _ns; + node = (vnode *) _node; + + if (!MY_S_ISDIR(node->mode)) { + err = ENOTDIR; + goto error1; + } + pos = (dirpos *) malloc(sizeof(dirpos)); + if (!pos) { + err = ENOMEM; + goto error1; + } + if (new_lock(&pos->lock, "rootdirlock") < 0) { + err = EINVAL; + goto error2; + } + pos->pos = 0; + pos->name[0] = '\0'; + *cookie = pos; + return 0; + +error2: + free(pos); +error1: + return err; +} + +static int +rootfs_closedir(void *_ns, void *_node, void *_cookie) +{ + return 0; +} + +static int +rootfs_free_dircookie(void *_ns, void *_node, void *_cookie) +{ + nspace *ns; + vnode *node; + dirpos *cookie; + + ns = (nspace *) _ns; + node = (vnode *) _node; + cookie = (dirpos *) _cookie; + + free_lock(&cookie->lock); + free(cookie); + return 0; +} + +static int +rootfs_rewinddir(void *_ns, void *_node, void *_cookie) +{ + nspace *ns; + vnode *node; + dirpos *cookie; + + ns = (nspace *) _ns; + node = (vnode *) _node; + cookie = (dirpos *) _cookie; + + LOCK(cookie->lock); + cookie->pos = 0; + cookie->name[0] = '\0'; + UNLOCK(cookie->lock); + return 0; +} + +static int +rootfs_readdir(void *_ns, void *_node, void *_cookie, long *num, + struct my_dirent *buf, size_t bufsize) +{ + nspace *ns; + vnode *node; + dirpos *cookie; + char *e, *q; + struct my_dirent *p; + long i; + vnode *vn; + vnode_id vnid; + char *name, *last; + int sl, rl; + + ns = (nspace *) _ns; + node = (vnode *) _node; + cookie = (dirpos *) _cookie; + + LOCK(ns->lock); + LOCK(cookie->lock); + vn = node->head; + p = (struct my_dirent *) buf; + e = (char *) buf + bufsize; + if (cookie->pos > 2) + while (vn && (strcmp(cookie->name, vn->name) >= 0)) + vn = vn->next; + for(i=0; (i < *num) && ((cookie->pos < 2) || vn); i++, cookie->pos++) { + switch(cookie->pos) { + case 0: + name = "."; + vnid = node->vnid; + break; + case 1: + name = ".."; + vnid = node->parent->vnid; + break; + default: + name = vn->name; + vnid = vn->vnid; + vn = vn->next; + break; + } + sl = strlen(name) + 1; + rl = sizeof(struct my_dirent) + sl - 1; + if ((char *)p + rl > e) + break; + last = name; + p->d_reclen = (rl + 7) & ~7; + p->d_ino = vnid; + memcpy(p->d_name, name, sl); + p = (struct my_dirent *)((char *)p + p->d_reclen); + } + if ((cookie->pos > 2) && (i > 0)) + strcpy(cookie->name, last); + *num = i; +exit: + UNLOCK(cookie->lock); + UNLOCK(ns->lock); + return 0; +} + +static int +rootfs_rstat(void *_ns, void *_node, struct my_stat *st) +{ + nspace *ns; + vnode *node; + + ns = (nspace *) _ns; + node = (vnode *) _node; + + LOCK(ns->lock); + st->st_dev = ns->nsid; + st->st_ino = node->vnid; + st->st_mode = node->mode; + st->st_nlink = 1; + st->st_uid = node->uid; + st->st_gid = node->gid; + st->st_size = 0; + st->st_blksize = 0; + st->st_atime = st->st_ctime = st->st_mtime = node->mtime; + UNLOCK(ns->lock); + return 0; +} + +static int +rootfs_wstat(void *_ns, void *_node, struct my_stat *st, long mask) +{ + nspace *ns; + vnode *node; + + ns = (nspace *) _ns; + node = (vnode *) _node; + + if (mask & WSTAT_SIZE) + return EINVAL; + + LOCK(ns->lock); + + if (mask & WSTAT_MODE) + node->mode = (node->mode & MY_S_IFMT) | (st->st_mode & ~MY_S_IFMT); + if (mask & WSTAT_UID) + node->uid = st->st_uid; + if (mask & WSTAT_GID) + node->gid = st->st_gid; + if (mask & WSTAT_MTIME) + node->mtime = st->st_mtime; + if (mask & WSTAT_ATIME) + node->mtime = st->st_atime; + + UNLOCK(ns->lock); + return 0; +} + +static int +rootfs_mount(nspace_id nsid, const char *device, ulong flags, void *parms, + size_t len, void **data, vnode_id *vnid) +{ + int err; + nspace *ns; + vnode *root; + vnode_id rvnid; + + if (device || parms || (len != 0)) { + err = EINVAL; + goto error1; + } + + ns = (nspace *) malloc(sizeof(nspace)); + if (!ns) { + err = ENOMEM; + goto error1; + } + + root = (vnode *) malloc(sizeof(vnode)); + if (!root) { + err = ENOMEM; + goto error2; + } + + rvnid = 1; + + ns->nsid = nsid; + ns->vnnum = 0; + ns->nxvnid = rvnid; + ns->root = root; + if (new_lock(&ns->lock, "rootfs") < 0) { + err = -1; + goto error3; + } + ns->skiplist = NewSL(&compare_vnode, NULL, NO_DUPLICATES); + if (!ns->skiplist) { + err = -1; + goto error4; + } + + root->vnid = rvnid; + root->parent = root; + root->ns = ns; + root->removed = FALSE; + root->name = NULL; + root->next = root->prev = NULL; + root->head = NULL; + root->symlink = NULL; + + /* ### do it for real */ + root->uid = 0; + root->gid = 0; + root->mode = MY_S_IFDIR | 0777; + root->mtime = time(NULL); + + err = new_vnode(nsid, rvnid, root); + if (err) + goto error5; + + *data = ns; + *vnid = rvnid; + + return 0; + +error5: + FreeSL(ns->skiplist); +error4: + free_lock(&ns->lock); +error3: + free(root); +error2: + free(ns); +error1: + return err; +} + +static int +rootfs_unmount(void *_ns) +{ + nspace *ns; + vnode *vn, *avn; + + ns = (nspace *) _ns; + + vn = ns->root; + while (TRUE) { + while(vn->head) + vn = vn->head; + vn = vn->parent; + if (vn == ns->root) + break; + avn = vn->head; + if (avn->prev) + avn->prev->next = avn->next; + else + vn->head = avn->next; + if (avn->next) + avn->next->prev = avn->prev; + rootfs_remove_vnode(ns, avn, TRUE); + } + free(ns->root); + free_lock(&ns->lock); + FreeSL(ns->skiplist); + free(ns); + return 0; +} + +/* ### should do real permission check */ +static int +rootfs_access(void *_ns, void *_node, int mode) +{ + return 0; +} + +static int +compare_vnode(vnode *vna, vnode *vnb) +{ + if (vna->vnid > vnb->vnid) + return 1; + else + if (vna->vnid < vnb->vnid) + return -1; + else + return 0; +} + +static int +do_create(nspace *ns, vnode *dir, const char *name, mode_t mode, vnode **vnp) +{ + int err; + int c; + vnode *vn, *pvn, *nvn; + char *buf; + vnode_id vnid; + + if (!MY_S_ISDIR(dir->mode)) { + err = ENOTDIR; + goto error1; + } + + /* + make sure we are not trying to create something in a directory + that has been removed. + */ + + if (dir->removed) { + err = ENOENT; + goto error1; + } + + /* + filter the name: + can't be '.', or '..' + */ + + if (!strcmp(name, ".") || !strcmp(name, "..")) { + err = EEXIST; + goto error1; + } + + /* + lookup the name in the directory + */ + + vn = dir->head; + while (vn) { + c = strcmp(name, vn->name); + if (c < 0) + vn = NULL; + if (c <= 0) + break; + vn = vn->next; + } + + + /* + if it was found, report an error. + */ + + if (vn) { + err = EEXIST; + goto error1; + } + + /* + allocate a vnode and fill it + */ + + vn = NULL; + buf = NULL; + vn = (vnode *) malloc(sizeof(vnode)); + buf = (char *) malloc(strlen(name)+1); + if (!vn || !buf) { + err = ENOMEM; + goto error2; + } + strcpy(buf, name); + + vnid = ++ns->nxvnid; + + vn->vnid = vnid; + vn->parent = dir; + vn->ns = ns; + vn->removed = FALSE; + vn->name = buf; + + vn->mode = mode; + vn->uid = 0; + vn->gid = 0; + dir->mtime = vn->mtime = time(NULL); + + pvn = NULL; + nvn = dir->head; + while (nvn && (strcmp(name, nvn->name) > 0)) { + pvn = nvn; + nvn = nvn->next; + } + vn->next = nvn; + if (nvn) + nvn->prev = vn; + vn->prev = pvn; + if (pvn) + pvn->next = vn; + else + dir->head = vn; + + vn->head = NULL; + vn->symlink = NULL; + + atomic_add(&ns->vnnum, 1); + + InsertSL(ns->skiplist, vn); + + *vnp = vn; + + return 0; + +error2: + if (vn) + free(vn); + if (buf) + free(buf); +error1: + return err; +} + +static int +do_unlink(nspace *ns, vnode *dir, const char *name, bool isdir) +{ + int err; + vnode *vn; + + LOCK(ns->lock); + if (!MY_S_ISDIR(dir->mode)) { + err = ENOTDIR; + goto error1; + } + + /* + can't delete '..' and '.' + */ + + if (!strcmp(name, "..") || !strcmp(name, ".")) { + err = EINVAL; + goto error1; + } + + /* + lookup the name in the directory + */ + + vn = dir->head; + while (vn) { + if (!strcmp(vn->name, name)) + break; + vn = vn->next; + } + + /* + if it was not found, report an error. + */ + + if (!vn) { + err = ENOENT; + goto error1; + } + + /* + ensure it is of the appropriate type. + */ + + if (isdir && !(vn->mode & MY_S_IFDIR)) { + err = ENOTDIR; + goto error1; + } + + if (!isdir && (vn->mode & MY_S_IFDIR)) { + err = EISDIR; + goto error1; + } + + /* + make sure it is not the root + */ + + if (vn == vn->ns->root) { + err = EBUSY; + goto error1; + } + + /* + if it is a directory, make sure it is empty + */ + + if (MY_S_ISDIR(vn->mode) && vn->head) { + err = ENOTEMPTY; + goto error1; + } + + err = get_vnode(ns->nsid, vn->vnid, (void *)&vn); + if (err) + goto error1; + + err = remove_vnode(ns->nsid, vn->vnid); + if (err) + goto error1; + + if (vn->prev) + vn->prev->next = vn->next; + else + vn->parent->head = vn->next; + if (vn->next) + vn->next->prev = vn->prev; + vn->prev = vn->next = NULL; + vn->removed = TRUE; + dir->mtime = time(NULL); + + put_vnode(ns->nsid, vn->vnid); + + UNLOCK(ns->lock); + + return 0; + +error1: + UNLOCK(ns->lock); + return err; +} diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/skiplist.h b/src/tests/add-ons/kernel/file_systems/fs_shell/skiplist.h new file mode 100644 index 0000000000..87ee6afb07 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/skiplist.h @@ -0,0 +1,83 @@ +/* + This header file contains the definitions for use with the generic + SkipList package. + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com + */ + +#ifndef SKIPLIST_H +#define SKIPLIST_H + + +/* RAND_MAX should be defined if you are using an ANSI compiler system, + * but alas it isn't always. You should define it to be the correct + * value for whatever your library rand() function returns. + * + * Under unix (mach, bsd, etc), that's 2^31 - 1. On my Amiga at home + * it's 2^15 - 1. It would be wise to verify what your compiler uses + * for RAND_MAX (the maximum value returned from rand()) because otherwise + * the code will _not_ work. + */ +#ifndef RAND_MAX +#define RAND_MAX (0x7fffffff) +#endif + + +#define ALLOW_DUPLICATES 1 /* allow or disallow duplicates in a list */ +#define NO_DUPLICATES 0 +#define DUPLICATE_ITEM -1 /* ret val from InsertSL if dups not allowed */ + + +/* typedef's */ +typedef struct SLNodeStruct *SLNode; + +struct SLNodeStruct +{ + void *key; + SLNode forward[1]; /* variable sized array of forward pointers */ +}; + +typedef struct _SkipList +{ + struct SLNodeStruct *header; /* pointer to header */ + + int (*compare)(); + void (*freeitem)(); + + int flags; + int level; /* max index+1 of the forward array */ + + int count; /* number of elements in the list */ +} *SkipList; + + + +/* protos */ +SkipList NewSL(int (*compare)(), void (*freeitem)(), int flags); +void FreeSL(SkipList l); +int InsertSL(SkipList l, void *key); +int DeleteSL(SkipList l, void *key); +void *SearchSL(SkipList l, void *key); +void DoForSL(SkipList l, int (*function)(), void *arg); +void DoForRangeSL(SkipList l, void *key, int (*compare)(), + int (*func)(), void *arg); + +int NumInSL(SkipList l); + + +/* These defines are to be used as return values from the function + * you pass to DoForSL(). They can be or'ed together to do multiple + * things (like delete a node and then quit going through the list). + */ +#define SL_CONTINUE 0x00 +#define SL_DELETE 0x01 +#define SL_QUIT 0x02 + +#endif /* SKIPLIST_H */ diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/sl.c b/src/tests/add-ons/kernel/file_systems/fs_shell/sl.c new file mode 100644 index 0000000000..100ab17857 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/sl.c @@ -0,0 +1,401 @@ +/* This file contains a heavily hacked and generalized version of the + Example skiplist code distributed on mimsy.cs.umd.edu. + + Here is a short excerpt from the original comment : + + Example of Skip List source code for C : + + Skip Lists are a probabilistic alternative to balanced trees, + as described in the June 1990 issue of CACM and were invented by + William Pugh in 1987. + + These are my additions : + + This file contains my (Dominic Giampaolo's) heavily hacked version + of skip lists. These work on any arbitrary data by using callback + functions which you supply (at list creation time) to do the data + comparisons. You could instantly use this package to implement a + symbol table for a compiler which would be blazingly fast and + require zippo effort on your part. + + I've changed the function names (not to protect the innocent, but + to make them easier to read :) and changed the data structures a bit. + I've ansi'fied the code, added prototypes, and changed all those ugly + do/while's to for loops. I also removed the dependance on those silly + NIL items at the end of the list (it just checks for regular NULL + pointers instead). Additionally, the code is more easily reentrant now, + and doesn't depend on any global variables. The code is quite a bit + different looking than it originally was, but the underlying algorithims + (of course) remain unchanged. + + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ +#include +#include +#include "skiplist.h" + +/* define's */ +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define MaxNumberOfLevels 16 +#define MaxLevel (MaxNumberOfLevels-1) +#define NewNodeOfLevel(x) (SLNode)malloc(sizeof(struct SLNodeStruct)+(x)*sizeof(SLNode *)) + + +/* private proto */ +static int RandomLevelSL(SkipList l); + + +/* functions */ +SkipList NewSL(int (*compare)(), void (*freeitem)(), int flags) +{ + SkipList l; + int i; + + if (compare == NULL) /* need at least a compare function... */ + return NULL; + + l = (SkipList)malloc(sizeof(struct _SkipList)); + if (l == NULL) + return NULL; + + l->level = 1; + l->header = NewNodeOfLevel(MaxNumberOfLevels); + if (l->header == NULL) + { free(l); return NULL; } + + for(i=0; i < MaxNumberOfLevels; i++) + l->header->forward[i] = NULL; + l->header->key = NULL; /* just to be sure */ + + /* XXXdbg -- don't want this! srand(time(NULL) | 0x01); */ /* seed with an odd number */ + + l->compare = compare; + l->freeitem = freeitem; + l->flags = flags; + + l->count = 0; + + return(l); +} + + +void FreeSL(SkipList l) +{ + register SLNode p,q; + void (*freeitem)() = l->freeitem; + + if (l == NULL) + return; + + if (l->header == NULL) + { + free(l); + return; + } + + + p = l->header; /* free header node first, because it doesn't */ + q = p->forward[0]; /* have a real key to it */ + free(p); + p = q; + + while (p != NULL) + { + q = p->forward[0]; + if (freeitem) + (*freeitem)(p->key); + free(p); + p = q; + } + + free(l); +} + + + + +/* + * This RandomLevelSL function generates a very good representation of + * p=.25 (or p=.5, etc). The number of nodes of each level works out + * to be very very close to what they should be. I didn't check it + * statistically, but on large data sets, I imagine it's +/- 5% of what + * it should be. This P value is good for lists of up to 64K elements. + * + * For more info about the P value, see the papers by Mr. Pugh (available + * in postscript from mimsy.umd.edu). + */ +#define P_50 (RAND_MAX / 2) /* p value of .50 */ +#define P_25 (RAND_MAX / 4) /* p value of .25 */ +#define P_125 (RAND_MAX / 8) /* p value of .125 */ + +static int RandomLevelSL(SkipList l) +{ + register int level = 0; + + while(rand() < P_25) + { + level++; + } + + return (level > MaxLevel ? MaxLevel : level); +} + + +int InsertSL(SkipList l, void *key) +{ + register int i,k; + SLNode update[MaxNumberOfLevels]; + register SLNode p,q; + int (*compare)() = l->compare; + + p = l->header; + + for(k = l->level-1; k >= 0; k--) + { + while((q = p->forward[k]) && (*compare)(q->key, key) < 0) + p = q; + + update[k] = p; + } + + if ((l->flags & ALLOW_DUPLICATES) == FALSE) /* if no duplicates allowed */ + if (q && (*compare)(q->key, key) == 0) /* item is a duplicate */ + { + return DUPLICATE_ITEM; + } + + + k = RandomLevelSL(l); + if (k >= l->level) + { + k = l->level; + l->level++; + update[k] = l->header; + } + + q = NewNodeOfLevel(k); /* was k+1 */ + + if (q == NULL) + return FALSE; + + l->count++; /* update the number of nodes in the list */ + + q->key = key; + for(i=0; i < k; i++) + q->forward[i] = NULL; + + for(; k >= 0; k--) + { + p = update[k]; + q->forward[k] = p->forward[k]; + p->forward[k] = q; + } + + return TRUE; +} + + + +int DeleteSL(SkipList l, void *key) +{ + register int k,m; + SLNode update[MaxNumberOfLevels]; + register SLNode p,q; + int (*compare)() = l->compare; + void (*freeitem)() = l->freeitem; + + p = l->header; + + for(k=l->level-1; k >= 0; k--) + { + while((q = p->forward[k]) && (*compare)(q->key, key) < 0) + p = q; + + update[k] = p; + } + q = p->forward[0]; + + if (q && (*compare)(q->key, key) == 0) + { + m = l->level; + for(k=0; k < m; k++) + { + p = update[k]; + if (p == NULL || p->forward[k] != q) + break; + p->forward[k] = q->forward[k]; + } + + l->count--; + + if (freeitem) + (*freeitem)(q->key); + + free(q); + + m = l->level - 1; + while(l->header->forward[m] == NULL && m > 0) + m--; + + l->level = m + 1; + return TRUE; + } + else + return FALSE; +} + + +void *SearchSL(SkipList l, void *key) +{ + register int k; + register SLNode p,q; + int (*compare)() = l->compare; + + p = l->header; + + for(k=l->level-1; k >= 0; k--) + { + while((q = p->forward[k]) && (*compare)(q->key, key) < 0) + p = q; + } + + if (q == NULL || (*compare)(q->key, key) != 0) + return NULL; + + return q->key; +} + + +void DoForSL(SkipList l, int (*function)(), void *arg) +{ + register SLNode p,q, fix; + register int k,m, ret; + SLNode save[MaxNumberOfLevels], who[MaxNumberOfLevels]; + void (*freeitem)() = l->freeitem; + + + if (l == NULL || l->header == NULL || function == NULL) + return; + + p = l->header; /* skip header node because it isn't a real node */ + + /* Save the initial header info + */ + for(k=0; k < l->level; k++) + { + save[k] = p->forward[k]; + who[k] = p; + } + + p = p->forward[0]; /* skip to the first data node */ + + while (p != NULL) + { + q = p->forward[0]; + ret = (*function)(p->key, arg); + + if (ret & SL_DELETE) + { + k = 0; + while(save[k] == p) + { + fix = who[k]; + fix->forward[k] = p->forward[k]; + save[k] = p->forward[k]; + k++; + } + + l->count--; /* decrement the count of items */ + + if (freeitem) + (*freeitem)(p->key, arg); + free(p); + } + else + { + k = 0; + while(save[k] == p) + { + save[k] = p->forward[k]; + who[k] = p; + k++; + } + } + + if (ret & SL_QUIT) + break; + + p = q; /* advance to the next one */ + } +} + + +void DoForRangeSL(SkipList l, void *key, int (*compare)(), int (*func)(), + void *arg) +{ + register int k,m; + SLNode update[MaxNumberOfLevels]; + register SLNode p,q; + void (*freeitem)() = l->freeitem; + int ret; + + p = l->header; + + for(k=l->level-1; k >= 0; k--) + { + while((q = p->forward[k]) && (*compare)(q->key, key) < 0) + p = q; + + update[k] = p; + } + p = p->forward[0]; + + if (p == NULL || (*compare)(p->key, key) != 0) /* then nothing matched */ + return; + + do + { + q = p->forward[0]; /* save next pointer */ + ret = (*func)(p->key, arg); + + if (ret & SL_DELETE) + { + for(k=0; k < l->level && update[k] && update[k]->forward[k] == p; k++) + update[k]->forward[k] = p->forward[k]; + + l->count--; /* decrement the count of items */ + + if (freeitem) + (*freeitem)(p->key, arg); + free(p); + } + + if (ret & SL_QUIT) + break; + + p = q; /* advance to the next one */ + } + while(p != NULL && (*compare)(p->key, key) == 0); + +} + + +int NumInSL(SkipList l) +{ + return l->count; +} + diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/stub.c b/src/tests/add-ons/kernel/file_systems/fs_shell/stub.c new file mode 100644 index 0000000000..335dd039be --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/stub.c @@ -0,0 +1,116 @@ +/* + This file contains some stub routines to cover up the differences + between the BeOS and the rest of the world. + + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ +#include +#include + +#include "myfs.h" + +#ifndef __BEOS__ + +void +unload_kernel_addon(aid) +{ +} + +sem_id +create_sem(long count, const char *name) +{ + int *ptr; + ptr = (int *)malloc(sizeof(int) + strlen(name) + 1); /* a hack */ + *ptr = count; + memcpy(ptr+1, name, strlen(name)); + + return (sem_id)ptr; +} + + +long +delete_sem(sem_id semid) +{ + int *ptr = (int *)semid; + + free(ptr); + return 0; +} + +long +acquire_sem(sem_id sem) +{ + int *ptr = (int *)sem; + + if (*ptr <= 0) { + myfs_die("You lose sucka! acquire of sem with count == %d\n", *ptr); + } + + *ptr -= 1; + + return 0; +} + + +long +acquire_sem_etc(sem_id sem, int count, int j1, bigtime_t j2) +{ + int *ptr = (int *)sem; + + if (*ptr <= 0) { + myfs_die("You lose sucka! acquire_sem_etc of sem with count == %d\n", + *ptr); + } + + *ptr -= count; + + return 0; +} + +long +release_sem(sem_id sem) +{ + int *ptr = (int *)sem; + + *ptr += 1; + + return 0; +} + +long +release_sem_etc(sem_id sem, long count, long j1) +{ + int *ptr = (int *)sem; + + *ptr += count; + + return 0; +} + + +long +atomic_add(long *ptr, long val) +{ + int old = *ptr; + + *ptr += val; + + return old; +} + +int +snooze(bigtime_t f) +{ + sleep(1); + return 1; +} + +#endif /* __BEOS__ */ diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/sysdep.c b/src/tests/add-ons/kernel/file_systems/fs_shell/sysdep.c new file mode 100644 index 0000000000..7c3f01f6b2 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/sysdep.c @@ -0,0 +1,335 @@ +/* + This file contains some routines that are #ifdef'ed based on what + system you're on. Currently it supports the BeOS and Unix. It + could be extended to support Windows NT but their posix support + is such a joke that it would probably be a real pain in the arse. + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ +#include +#include +#include +#include + +#include "compat.h" + + +int +device_is_read_only(const char *device) +{ +#ifdef unix + return 0; /* XXXdbg should do an ioctl or something */ +#else + int fd; + device_geometry dg; + + fd = open(device, O_RDONLY); + if (ioctl(fd, B_GET_GEOMETRY, &dg) < 0) + return 0; + + close(fd); + + return dg.read_only; +#endif +} + +int +get_device_block_size(int fd) +{ +#ifdef unix + return 512; /* XXXdbg should do an ioctl or something */ +#else + struct stat st; + device_geometry dg; + + if (ioctl(fd, B_GET_GEOMETRY, &dg) < 0) { + if (fstat(fd, &st) < 0 || S_ISDIR(st.st_mode)) + return 0; + + return 512; /* just assume it's a plain old file or something */ + } + + return dg.bytes_per_sector; +#endif +} + +fs_off_t +get_num_device_blocks(int fd) +{ +#ifdef unix + struct stat st; + + fstat(fd, &st); /* XXXdbg should be an ioctl or something */ + + return st.st_size / get_device_block_size(fd); +#else + struct stat st; + device_geometry dg; + + if (ioctl(fd, B_GET_GEOMETRY, &dg) >= 0) { + return (fs_off_t)dg.cylinder_count * + (fs_off_t)dg.sectors_per_track * + (fs_off_t)dg.head_count; + } + + /* if the ioctl fails, try just stat'ing in case it's a regular file */ + if (fstat(fd, &st) < 0) + return 0; + + return st.st_size / get_device_block_size(fd); +#endif +} + +int +device_is_removeable(int fd) +{ +#ifdef unix + return 0; /* XXXdbg should do an ioctl or something */ +#else + struct stat st; + device_geometry dg; + + if (ioctl(fd, B_GET_GEOMETRY, &dg) < 0) { + return 0; + } + + return dg.removable; +#endif +} + +#if defined(__BEOS__) && !defined(USER) +#include "scsi.h" +#endif + +int +lock_removeable_device(int fd, bool on_or_off) +{ +#if defined(unix) || defined(USER) + return 0; /* XXXdbg should do an ioctl or something */ +#else + return ioctl(fd, B_SCSI_PREVENT_ALLOW, &on_or_off); +#endif +} + + + + +#ifndef __BEOS__ +ssize_t +read_pos(int fd, fs_off_t _pos, void *data, size_t nbytes) +{ + off_t pos = (off_t)_pos; + size_t ret; + + if (lseek(fd, pos, SEEK_SET) < 0) { + perror("read lseek"); + return EINVAL; + } + + ret = read(fd, data, nbytes); + + if (ret != nbytes) { + printf("read_pos: wanted %d, got %d\n", nbytes, ret); + return -1; + } + + return ret; +} + +ssize_t +write_pos(int fd, fs_off_t _pos, const void *data, size_t nbytes) +{ + off_t pos = (off_t)_pos; + size_t ret; + + if (lseek(fd, pos, SEEK_SET) < 0) { + perror("read lseek"); + return EINVAL; + } + + ret = write(fd, data, nbytes); + + if (ret != nbytes) { + printf("write_pos: wanted %d, got %d\n", nbytes, ret); + return -1; + } + + return ret; +} + + +#ifdef sun /* bloody wankers */ +#include +#ifdef DEF_IOV_MAX +#define MAX_IOV DEF_IOV_MAX +#else +#define MAX_IOV 16 +#endif +#else /* the rest of the world... */ +#define MAX_IOV 8192 /* something way bigger than we'll ever use */ +#endif + +ssize_t +readv_pos(int fd, fs_off_t _pos, struct iovec *iov, int count) +{ + off_t pos = (off_t)_pos; + size_t amt = 0; + ssize_t ret; + struct iovec *tmpiov; + int i, n; + + if (lseek(fd, pos, SEEK_SET) < 0) { + perror("read lseek"); + return EINVAL; + } + + i = 0; + tmpiov = iov; + while (i < count) { + if (i + MAX_IOV < count) + n = MAX_IOV; + else + n = (count - i); + + ret = readv(fd, tmpiov, n); + amt += ret; + + if (ret < 0) + break; + + i += n; + tmpiov += n; + } + + return amt; +} + +ssize_t +writev_pos(int fd, fs_off_t _pos, struct iovec *iov, int count) +{ + off_t pos = (off_t)_pos; + size_t amt = 0; + ssize_t ret; + struct iovec *tmpiov; + int i, n; + + if (lseek(fd, pos, SEEK_SET) < 0) { + perror("read lseek"); + return EINVAL; + } + + i = 0; + tmpiov = iov; + while (i < count) { + if (i + MAX_IOV < count) + n = MAX_IOV; + else + n = (count - i); + + ret = writev(fd, tmpiov, n); + amt += ret; + + if (ret < 0) + break; + + i += n; + tmpiov += n; + } + + return amt; +} + + + +#endif /* __BEOS__ */ + + +#include + + +void +panic(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + + while (TRUE) + ; +} + + + +#include "lock.h" + +int +new_lock(lock *l, const char *name) +{ + l->c = 1; + l->s = create_sem(0, (char *)name); + if (l->s <= 0) + return l->s; + return 0; +} + +int +free_lock(lock *l) +{ + delete_sem(l->s); + + return 0; +} + +int +new_mlock(mlock *l, long c, const char *name) +{ + l->s = create_sem(c, (char *)name); + if (l->s <= 0) + return l->s; + return 0; +} + +int +free_mlock(mlock *l) +{ + delete_sem(l->s); + + return 0; +} + + +#ifdef unix +#include + +bigtime_t +system_time(void) +{ + bigtime_t t; + struct timeval tv; + + gettimeofday(&tv, NULL); + + t = ((bigtime_t)tv.tv_sec * 1000000) + (bigtime_t)tv.tv_usec; + return t; +} + +/* + If you're compiler/system can't deal with the version of system_time() + as defined above, use this one instead +bigtime_t +system_time(void) +{ + return (bigtime_t)time(NULL); +} +*/ + +#endif /* unix */ diff --git a/src/tests/add-ons/kernel/file_systems/fs_shell/tstfs.c b/src/tests/add-ons/kernel/file_systems/fs_shell/tstfs.c new file mode 100644 index 0000000000..c1b9eda5f3 --- /dev/null +++ b/src/tests/add-ons/kernel/file_systems/fs_shell/tstfs.c @@ -0,0 +1,257 @@ +/* + This file contains a simple test program that can be used as a sort + of stress test for the file system. It's not exhaustive but it provides + a decent first level sanity check on whether a file system will work. + + Basically it just randomly creates and deletes files. The defines + just after the includes control how many files and how many iterations. + Be careful if you just bump up the numbers really high -- it will take + a long time to run and if you only have a 16 megabyte file system + it will probably run out of space. + + + THIS CODE COPYRIGHT DOMINIC GIAMPAOLO. NO WARRANTY IS EXPRESSED + OR IMPLIED. YOU MAY USE THIS CODE AND FREELY DISTRIBUTE IT FOR + NON-COMMERCIAL USE AS LONG AS THIS NOTICE REMAINS ATTACHED. + + FOR COMMERCIAL USE, CONTACT DOMINIC GIAMPAOLO (dbg@be.com). + + Dominic Giampaolo + dbg@be.com +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "myfs.h" +#include "kprotos.h" + +#define MAX_LOOPS 1024 +#define MAX_FILES 512 +#define MAX_NAME 24 + +char buf[MAX_FILES][MAX_NAME]; +fs_off_t sizes[MAX_FILES]; + + +static void +make_random_name(char *buf, int len) +{ + int i, max = (rand() % (len - 7)) + 6; + + for(i=0; i < max; i++) { + buf[i] = 'a' + (rand() % 26); + } + + buf[i] = '\0'; +} + +static void +SubTime(struct timeval *a, struct timeval *b, struct timeval *c) +{ + if ((long)(a->tv_usec - b->tv_usec) < 0) + { + a->tv_sec--; + a->tv_usec += 1000000; + } + + c->tv_sec = a->tv_sec - b->tv_sec; + c->tv_usec = a->tv_usec - b->tv_usec; +} + + +static void +write_rand_data(int fd, int max_data) +{ + int i, k, err; + size_t j; + static char buf[4096]; + ulong sum = 0; + + for(i=0; max_data > 0; i++) { + j = rand() % sizeof(buf); + if ((int)(max_data - j) < 0) + j = max_data; + + memset(buf, rand() >> 8, j); + + for(k=0; k < j; k++) + sum += buf[k]; + + /* printf("write: %d\n", j); */ + err = sys_write(1, fd, buf, j); + if (err != j) { + errno = err; + perror("write_rand_data"); + printf("err %d j %d\n", err, j); + if (errno != ENOSPC) + while(1) + sleep(1); + break; + } + + max_data -= j; + } + +#if INSANELY_SLOW_CHECKSUM + pos = 0; + err = sys_lseek(1, fd, SEEK_SET, &pos); + for(i=0; i < max; i++) { + j = sizeof(buf); + sys_read(1, fd, buf, j); + for(k=0; k < j; k++) + nsum += buf[k]; + } + + if (sum != nsum) + printf("sum = 0x%x, nsum 0x%x\n", sum, nsum); +#endif +} + + +int +main(int argc, char **argv) +{ + int i, j, fd, seed, err, size, sum, name_size = 0; + struct my_stat st; + struct timeval start, end, result; + char *disk_name = "big_file"; + myfs_info *myfs; + + + if (argv[1] != NULL && !isdigit(argv[1][0])) + disk_name = argv[1]; + else if (argv[1] && isdigit(argv[1][0])) + seed = strtoul(argv[1], NULL, 0); + else + seed = getpid() * time(NULL) | 1; + printf("random seed == 0x%x\n", seed); + + srand(seed); + + myfs = init_fs(disk_name); + + + for(i=0; i < MAX_FILES; i++) + buf[i][0] = '\0'; + + + printf("creating & deleting files...\n"); fflush(stdout); + gettimeofday(&start, NULL); + for(i=0,sum=0; i < MAX_LOOPS; i++) { + j = rand() % MAX_FILES; + + size = (rand() % 65536) + 1; + +#if 1 + if ((i % 10) == 0) { + printf("\r \r"); + printf("iteration: %7d", i); + fflush(stdout); + } +#endif + + if (buf[j][0] == '\0') { /* then create a file */ + strcpy(&buf[j][0], "/myfs/"); + make_random_name(&buf[j][6], MAX_NAME-6); + name_size += strlen(&buf[j][6]); + + sum += sizes[j] = size; + + /* printf("\rcreating: %s %d bytes", &buf[j][0], size); */ + + fd = sys_open(1, -1, &buf[j][0], O_CREAT|O_RDWR, + MY_S_IFREG|MY_S_IRWXU, 0); + + if (fd < 0) { + printf("error creating: %s\n", &buf[j][0]); + break; + } + + write_rand_data(fd, size); + + sys_close(1, fd); + } else { /* then delete the file */ + /* printf("\runlinking %s", &buf[j][0]); */ + name_size -= strlen(&buf[j][6]); + + err = sys_unlink(1, -1, &buf[j][0]); + if (err != 0) { + printf("error removing: %s: %s\n", &buf[j][0], strerror(err)); + break; + } + + buf[j][0] = '\0'; + sum -= sizes[j]; + } + +#if 0 /* doing this is really anal */ + for(k=0; k < MAX_FILES; k++) { + if (buf[k][0] == '\0') + continue; + + fd = sys_open(1, -1, &buf[k][0], O_RDWR, 0, 0); + if (fd < 0) { + printf("file: %s is not present and should be!\n", &buf[k][0]); + sys_unmount(1, -1, "/myfs"); + exit(0); + } + + sys_close(1, fd); + } +#endif + } + gettimeofday(&end, NULL); + SubTime(&end, &start, &result); + + printf("\rcreated %d files in %2ld.%.6ld seconds (%d k data)\n", i, + result.tv_sec, result.tv_usec, sum/1024); + + printf("now verifying files....\n"); + for(i=0; i < MAX_FILES; i++) { + if (buf[i][0] == '\0') + continue; + + printf(" \r"); + printf("opening: %s\r", &buf[i][0]); + fflush(stdout); + + fd = sys_open(1, -1, &buf[i][0], O_RDWR, 0, 0); + if (fd != 0) { + printf("file: %s is not present and should be!\n", &buf[i][0]); + sys_unmount(1, -1, "/myfs"); + exit(0); + } + + err = sys_rstat(1, -1, &buf[i][0], &st, 1); + if (err != 0) { + printf("stat failed for: %s\n", &buf[i][0]); + continue; + } + + if (st.st_size != sizes[i]) { + printf("size mismatch on %s: %ld != %ld\n", &buf[i][0], + st.st_size, sizes[i]); + } + + sys_close(1, fd); + } + printf("done verifying files \n"); + + if (sys_unmount(1, -1, "/myfs") != 0) { + printf("could not UNmount /myfs\n"); + return 5; + } + + shutdown_block_cache(); + + /* check_mem(); */ + + return 0; +}