// 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