253 lines
11 KiB
C
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
|