slkern/fsinstance.h

253 lines
11 KiB
C

// This is NEW CODE
#ifndef _FSINSTANCE_H
#define _FSINSTANCE_H
#include "param.h"
#include "sched.h"
#ifndef NULL
#define NULL ((void*)0ULL)
#endif
#include "mkfs/fsformat.h"
#define FSINSTANCE_MAXBLOCKSPEROP 10
#define FSINSTANCE_MAXOPSPERLOG 3
// This shouly definitely be updated to a higher number
#define FSINSTANCE_MAXLOGBLOCKS (FSINSTANCE_MAXBLOCKSPEROP * FSINSTANCE_MAXOPSPERLOG)
// The fsinstance struct contains the information used by a single instance
// of the filesystem (that is, by one mounted partition).
typedef struct fsinstance fsinstance_t;
// A log header is used by the filesystem logging system to track the blocks
// in the log.
typedef struct fsinstance_logheader fsinstance_logheader_t;
struct fsinstance_logheader {
int number;
int blocks[FSINSTANCE_MAXLOGBLOCKS];
};
// This is an inode structure as it exists in kernel memory, only the second
// part of the structure is written to disk.
typedef struct fsinstance_inode fsinstance_inode_t;
// TODO: This should replace NDIRECT in old code
#define FSINSTANCE_DIRECTADDRS 12
// NOTE: The internal structure mostly resembles the old code for now but might chenge
struct fsinstance_inode {
fsinstance_t* instance;
int device;
int referencecount;
unsigned int inodenumber; // Inode number, specific to this filesystem instance
int valid; // Set to true if it has been loaded properly
sched_sleeplock_t lock;
short type;
short major;
short minor;
short nlink;
unsigned int size; // TODO: Make bigger.
unsigned int addrs[FSINSTANCE_DIRECTADDRS+1];
};
struct fsinstance {
struct diskio_cache* cache;
// The superblock structure is now allocated by fsinstance_alloc and kept
// with the fsinstance structure instead of being a global variable.
fsformat_superblock_t* superblock;
sched_spinlock_t itbl_spin;
fsinstance_inode_t** itbl;
sched_spinlock_t fslog_lock;
fsinstance_logheader_t fslog_header;
int fsversion; // 0 for old xv6-compatible, 1+ for new versions
int fslog_sleepvariable; // Only used as a pointer to sleep on, to avoid multiple parties sleeping on the instance itself
int fslog_device;
int fslog_start;
int fslog_size;
int fslog_outstanding;
int fslog_committing;
};
// Allocates an empty filesystem instance.
fsinstance_t* fsinstance_alloc();
void* fsinstance_init(fsinstance_t* instance, unsigned int device);
void fsinstance_resetblocktozero(fsinstance_t* instance, unsigned int device, unsigned int blocknumber);
// Counts the free blocks (NOTE: This should be run inside a transaction
// for consistency, although it doesn't modify any blocks). Returns the
// number of blocks marked free for data or directory allocation.
unsigned int fsinstance_countfreeblocks(fsinstance_t* instance, unsigned int device);
// Attemptes to allocate a block, clearing the block to zeroes and
// returning it's block number on success or printing a warning and
// returning 0 if out of space.
unsigned int fsinstance_allocdatablock(fsinstance_t* instance, unsigned int device);
// Marks a data block as free in the used/free bitmap (this assumes that
// any reference to it will have been reset separately).
void fsinstance_freedatablock(fsinstance_t* instance, unsigned int device, unsigned int blocknumber);
// Looks up the inode number on the filesystem and returns the in-memory
// inode structure with reference count adjusted for the caller, without
// locking or loading the inode.
fsinstance_inode_t* fsinstance_getinode(fsinstance_t* instance, unsigned int device, unsigned int inodenumber);
// Allocates an inode, marking it with the given type. Returns the
// allocated inode (without locking it) on success or prints a warning
// and returns NULL on failure.
fsinstance_inode_t* fsinstance_allocinode(fsinstance_t* instance, unsigned int device, short inodetype);
// Increases the reference count of an inode, locking on the inode table
// and returning the inode pointer to simulate a copy operation.
fsinstance_inode_t* fsinstance_inode_copyref(fsinstance_inode_t* inode);
// Saves an inode to disk. This should be called after modifying any
// inode field that's saved to disk, and must be called while the caller
// holds the inode's lock.
void fsinstance_inode_save(fsinstance_inode_t* inode);
// Locks the inode, and reads it into memory for the first time if it
// hasn't been loaded yet.
void fsinstance_inode_lockandload(fsinstance_inode_t* inode);
// Unlocks an inode but does not perform any other actions like saving.
void fsinstance_inode_unlock(fsinstance_inode_t* inode);
// Deletes the entire contents of an inode but does not perform any
// other action such as deleting the inode (it's size will become 0).
// The inode must be locked by the caller.
void fsinstance_inode_deletecontents(fsinstance_inode_t* inode);
// The inverse of a get operation, and is also responsible for
// deleting inodes which have been "unlinked". This must be called from
// within a transaction.
void fsinstance_inode_unget(fsinstance_inode_t* inode);
// Performs an "unlock" then an "unget" call in succession.
void fsinstance_inode_unlockandunget(fsinstance_inode_t* inode);
// Defined elsehwere:
struct stat;
// Copy information for a "stat" system call to a (kernel-mode or
// filesystem-mode) buffer. The inode must be locked by the caller.
void fsinstance_inode_getstatinfo(fsinstance_inode_t* inode, struct stat* output);
// Returns the actual block number of the given logical block of a file,
// allocating an associated block if it doesn't exist.
unsigned int fsinstance_inode_getactualblock(fsinstance_inode_t* inode, unsigned int logicalblocknumber);
// Reads from an inode's data into memory. The memory can be either in
// user-land or kernel-land, with isuserland set to 1 in the case of
// filling a user-land buffer. The caller is expected to have locked
// the inode.
int fsinstance_inode_read(fsinstance_inode_t* inode, int isuserland, unsigned long long address, unsigned int offset, unsigned int size);
// Writes to an inode's data from memory. The address can be either
// in user-land or kernel-land, with isuserland set to 1 in the case of
// reading from a user-land buffer. The caller is expected to have
// locked the inode.
int fsinstance_inode_write(fsinstance_inode_t* inode, int isuserland, unsigned long long address, unsigned int offset, unsigned int size);
int fsinstance_nameseq(fsinstance_t* instance, const char* namea, const char* nameb);
fsinstance_inode_t* fsinstance_inode_lookup(fsinstance_inode_t* directory, char* filename, unsigned int* entryoffsetvar);
// Checks if a directory has a given filename in it, returning 1 if
// there is a match and 0 otherwise.
int fsinstance_inode_hasentry(fsinstance_inode_t* directory, char* filename);
// Attempts to insert a new directory entry into the given directory,
// returning 0 if successful or -1 otherwise. This function
int fsinstance_inode_insert(fsinstance_inode_t* directory, char* filename, unsigned int inodenumber);
// Scan to the next part of the path, reading the first part into
// filenamevar (which must have FSFORMAT_NAMESIZE reserved bytes).
// Returns the path iterator at the start of the next filename/dirname
// or NULL to indicate end of string.
char* fsinstance_scanfilename(fsinstance_t* instance, char* pathiterator, char* filenamevar);
// The internal path lookup function (used by the simple lookups). The
// filenamevar must point to an array of FSINSTANCE_NAMESIZE bytes which
// will be used as a scratch variable as well as to look up the filename
// within the parent directory (if getparent is non-zero). Any file
// lookup needs to happen within a transaction.
fsinstance_inode_t* fsinstance_lookuppath(fsinstance_t* instance, char* path, int getparent, char* filenamevar);
// Simple path lookup of a path's filename within a parent directory.
// The filename variable needs to point to at least FSFORMAT_NAMESIZE
// bytes of reserved memory. Any file lookup needs to happen within a
// transaction.
fsinstance_inode_t* fsinstance_lookupparent(fsinstance_t* instance, char* path, char* filenamevar);
// Simple path lookup, returning the inode matching the path or NULL if
// not found. Any file lookup needs to happen within a transaction.
fsinstance_inode_t* fsinstance_lookup(fsinstance_t* instance, char* path);
// Saves the log header to disk, this is used internally to set the disk
// into a state where the transaction is finishable (or cleared).
void fsinstance_savetransaction(fsinstance_t* instance);
// Loads the log header from disk, this is used internally to reload the
// structure at startup.
void fsinstance_loadtransaction(fsinstance_t* instance);
// Called internally to save the cached blocks included in this
// transaction.
void fsinstance_savecache(fsinstance_t* instance);
// Called internally to copy the logged blocks to their final locations.
// This is mostly an inverse of fsinstance_savecache.
void fsinstance_applytransactionblocks(fsinstance_t* instance, int rebootmode);
// Called internally to reset the on-disk transaction to a "zero blocks"
// state.
void fsinstance_resettransaction(fsinstance_t* instance);
// Called at startup time or whenever else the disk is mounted to check
// and apply any partially-applied writes, i.e. an "after-reboot check".
void fsinstance_rebootcheck(fsinstance_t* instance);
// Called internally to perform a "commit" of the current transaction
// using the other internal functions. This will have no effect in cases
// where no actual data has been modified in this transaction.
void fsinstance_innercommit(fsinstance_t* instance);
// This is like the diskio_buffer_write function except that it just
// adds the block to the log to be committed later.
struct diskio_buffer; // Defined elsewhere
void fsinstance_writelogged(fsinstance_t* instance, struct diskio_buffer* buffer);
// Called at the beginning of a filesystem transaction, waits until the
// filesystem is ready to accept more transactions and then returns with
// a new transaction marked as being in progress.
void fsinstance_begin(fsinstance_t* instance);
// Called at the end of a filesystem transaction. Commits the whole set
// of transactions if this is the last one in progress.
void fsinstance_end(fsinstance_t* instance);
// This is called internally to initialise/"reboot" the filesystem
// transaction logging system. This will initialise the logging-related
// variables and call fsinstance_rebootcheck to perform any unfinished
// transactions to leave the filesystem structures in a consistent state
void fsinstance_inittransactions(fsinstance_t* instance, unsigned int device, int blocksize);
// Panics if a filesystem instance is not valid.
#define FSINSTANCE_CHECK(i) \
do { \
if ((i) == NULL || (i->superblock == NULL)) { \
printf("bad fsinstance!\n"); \
panic((char*)__func__); \
} \
} while(0)
// From ifndef at top of file:
#endif