diff options
Diffstat (limited to 'ext2fs/ext2fs.h')
-rw-r--r-- | ext2fs/ext2fs.h | 574 |
1 files changed, 574 insertions, 0 deletions
diff --git a/ext2fs/ext2fs.h b/ext2fs/ext2fs.h new file mode 100644 index 00000000..3422af2f --- /dev/null +++ b/ext2fs/ext2fs.h @@ -0,0 +1,574 @@ +/* Common definitions for the ext2 filesystem translator + + Copyright (C) 1995, 1996, 1999, 2002, 2004 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <mach.h> +#include <hurd.h> +#include <hurd/ports.h> +#include <hurd/pager.h> +#include <hurd/fshelp.h> +#include <hurd/iohelp.h> +#include <hurd/store.h> +#include <hurd/diskfs.h> +#include <hurd/ihash.h> +#include <assert.h> +#include <pthread.h> +#include <sys/mman.h> + +#define __hurd__ /* Enable some hurd-specific fields. */ + +/* Types used by the ext2 header files. */ +typedef u_int32_t __u32; +typedef int32_t __s32; +typedef u_int16_t __u16; +typedef int16_t __s16; +typedef u_int8_t __u8; +typedef int8_t __s8; + +#include "ext2_fs.h" +#include "ext2_fs_i.h" + +#define i_mode_high osd2.hurd2.h_i_mode_high /* missing from ext2_fs.h */ + + +/* If ext2_fs.h defined a debug routine, undef it and use our own. */ +#undef ext2_debug + +#ifdef EXT2FS_DEBUG +extern int ext2_debug_flag; +#define ext2_debug(f, a...) \ + do { if (ext2_debug_flag) printf ("ext2fs: (debug) %s: " f "\n", __FUNCTION__ , ## a); } while (0) +#else +#define ext2_debug(f, a...) (void)0 +#endif + +#undef __hurd__ + +/* Define this if memory objects should not be cached by the kernel. + Normally, don't define it, but defining it causes a much greater rate + of paging requests, which may be helpful in catching bugs. */ + +#undef DONT_CACHE_MEMORY_OBJECTS + +int printf (const char *fmt, ...); + +/* A block number. */ +typedef __u32 block_t; + +/* ---------------------------------------------------------------- */ + +struct poke +{ + vm_offset_t offset; + vm_size_t length; + struct poke *next; +}; + +struct pokel +{ + struct poke *pokes, *free_pokes; + pthread_spinlock_t lock; + struct pager *pager; + void *image; +}; + +void pokel_init (struct pokel *pokel, struct pager *pager, void *image); +/* Clean up any state associated with POKEL (but don't free POKEL). */ +void pokel_finalize (struct pokel *pokel); + +/* Remember that data here on the disk has been modified. */ +void pokel_add (struct pokel *pokel, void *loc, vm_size_t length); + +/* Sync all the modified pieces of disk */ +void pokel_sync (struct pokel *pokel, int wait); + +/* Flush (that is, drop on the ground) all pending pokes in POKEL. */ +void pokel_flush (struct pokel *pokel); + +/* Transfer all regions from FROM to POKEL, which must have the same pager. */ +void pokel_inherit (struct pokel *pokel, struct pokel *from); + +#include <features.h> +#ifdef EXT2FS_DEFINE_EI +#define EXT2FS_EI +#else +#define EXT2FS_EI __extern_inline +#endif + +/* ---------------------------------------------------------------- */ +/* Bitmap routines. */ + +#include <stdint.h> + +extern int test_bit (unsigned num, char *bitmap); + +extern int set_bit (unsigned num, char *bitmap); + +#if defined(__USE_EXTERN_INLINES) || defined(EXT2FS_DEFINE_EI) +/* Returns TRUE if bit NUM is set in BITMAP. */ +EXT2FS_EI int +test_bit (unsigned num, char *bitmap) +{ + const uint32_t *const bw = (uint32_t *) bitmap + (num >> 5); + const uint_fast32_t mask = 1 << (num & 31); + return *bw & mask; +} + +/* Sets bit NUM in BITMAP, and returns the previous state of the bit. Unlike + the linux version, this function is NOT atomic! */ +EXT2FS_EI int +set_bit (unsigned num, char *bitmap) +{ + uint32_t *const bw = (uint32_t *) bitmap + (num >> 5); + const uint_fast32_t mask = 1 << (num & 31); + return (*bw & mask) ?: (*bw |= mask, 0); +} + +/* Clears bit NUM in BITMAP, and returns the previous state of the bit. + Unlike the linux version, this function is NOT atomic! */ +EXT2FS_EI int +clear_bit (unsigned num, char *bitmap) +{ + uint32_t *const bw = (uint32_t *) bitmap + (num >> 5); + const uint_fast32_t mask = 1 << (num & 31); + return (*bw & mask) ? (*bw &= ~mask, mask) : 0; +} +#endif /* Use extern inlines. */ + +/* ---------------------------------------------------------------- */ + +/* ext2fs specific per-file data. */ +struct disknode +{ + /* For a directory, this array holds the number of directory entries in + each DIRBLKSIZE piece of the directory. */ + int *dirents; + + /* Links on hash list. */ + struct node *hnext, **hprevp; + + /* Lock to lock while fiddling with this inode's block allocation info. */ + pthread_rwlock_t alloc_lock; + + /* Where changes to our indirect blocks are added. */ + struct pokel indir_pokel; + + /* Random extra info used by the ext2 routines. */ + struct ext2_inode_info info; + uint32_t info_i_translator; /* That struct from Linux source lacks this. */ + + /* This file's pager. */ + struct pager *pager; + + /* True if the last page of the file has been made writable, but is only + partially allocated. */ + int last_page_partially_writable; + + /* Index to start a directory lookup at. */ + int dir_idx; +}; + +struct user_pager_info +{ + enum pager_type + { + DISK, + FILE_DATA, + } type; + struct node *node; + vm_prot_t max_prot; +}; + +/* ---------------------------------------------------------------- */ +/* pager.c */ + +#define DISK_CACHE_BLOCKS 65536 + +#include <hurd/diskfs-pager.h> + +/* Set up the disk pager. */ +void create_disk_pager (void); + +/* Call this when we should turn off caching so that unused memory object + ports get freed. */ +void drop_pager_softrefs (struct node *node); + +/* Call this when we should turn on caching because it's no longer + important for unused memory object ports to get freed. */ +void allow_pager_softrefs (struct node *node); + +/* Invalidate any pager data associated with NODE. */ +void flush_node_pager (struct node *node); + +/* ---------------------------------------------------------------- */ + +/* The physical media. */ +extern struct store *store; +/* What the user specified. */ +extern struct store_parsed *store_parsed; + +/* Mapped image of cached blocks of the disk. */ +extern void *disk_cache; +extern store_offset_t disk_cache_size; +extern int disk_cache_blocks; + +#define DC_INCORE 0x01 /* Not in core. */ +#define DC_UNTOUCHED 0x02 /* Not touched by disk_pager_read_paged + or disk_cache_block_ref. */ +#define DC_FIXED 0x04 /* Must not be re-associated. */ + +/* Flags that forbid re-association of page. DC_UNTOUCHED is included + because this flag is used only when page is already to be + re-associated, so it's not good candidate for another + remapping. */ +#define DC_DONT_REUSE (DC_INCORE | DC_UNTOUCHED | DC_FIXED) + +#define DC_NO_BLOCK ((block_t) -1L) + +#ifndef NDEBUG +#define DISK_CACHE_LAST_READ_XOR 0xDEADBEEF +#endif + +/* Disk cache blocks' meta info. */ +struct disk_cache_info +{ + block_t block; + uint16_t flags; + uint16_t ref_count; +#ifndef NDEBUG + block_t last_read, last_read_xor; +#endif +}; + +/* block num --> pointer to in-memory block */ +extern hurd_ihash_t disk_cache_bptr; +/* Metadata about cached block. */ +extern struct disk_cache_info *disk_cache_info; +/* Lock for these mappings */ +extern pthread_mutex_t disk_cache_lock; +/* Fired when a re-association is done. */ +extern pthread_cond_t disk_cache_reassociation; + +void *disk_cache_block_ref (block_t block); +void disk_cache_block_ref_ptr (void *ptr); +void disk_cache_block_deref (void *ptr); +int disk_cache_block_is_ref (block_t block); + +/* Our in-core copy of the super-block (pointer into the disk_cache). */ +struct ext2_super_block *sblock; +/* True if sblock has been modified. */ +int sblock_dirty; + +/* Where the super-block is located on disk (at min-block 1). */ +#define SBLOCK_BLOCK 1 /* Default location, second 1k block. */ +#define SBLOCK_SIZE (sizeof (struct ext2_super_block)) +extern unsigned int sblock_block; /* Specified location (in 1k blocks). */ +#define SBLOCK_OFFS (sblock_block << 10) /* Byte offset of superblock. */ + +/* The filesystem block-size. */ +unsigned int block_size; +/* The log base 2 of BLOCK_SIZE. */ +unsigned int log2_block_size; + +/* The number of bits to scale min-blocks to get filesystem blocks. */ +#define BLOCKSIZE_SCALE (sblock->s_log_block_size) + +/* log2 of the number of device blocks in a filesystem block. */ +unsigned log2_dev_blocks_per_fs_block; + +/* log2 of the number of stat blocks (512 bytes) in a filesystem block. */ +unsigned log2_stat_blocks_per_fs_block; + +/* A handy page of page-aligned zeros. */ +vm_address_t zeroblock; + +/* Get the superblock from the disk, point `sblock' to it, and setup + various global info from it. */ +void get_hypermetadata (); + +/* Map `group_desc_image' pointers to disk cache. Also, establish a + non-exported mapping to the superblock that will be used by + diskfs_set_hypermetadata to update the superblock from the cache + `sblock' points to. */ +void map_hypermetadata (); + +/* ---------------------------------------------------------------- */ +/* Random stuff calculated from the super block. */ + +unsigned long frag_size; /* Size of a fragment in bytes */ +unsigned long frags_per_block; /* Number of fragments per block */ +unsigned long inodes_per_block; /* Number of inodes per block */ + +unsigned long itb_per_group; /* Number of inode table blocks per group */ +unsigned long db_per_group; /* Number of descriptor blocks per group */ +unsigned long desc_per_block; /* Number of group descriptors per block */ +unsigned long addr_per_block; /* Number of disk addresses per block */ + +unsigned long groups_count; /* Number of groups in the fs */ + +/* ---------------------------------------------------------------- */ + +pthread_spinlock_t node_to_page_lock; + +pthread_spinlock_t generation_lock; +unsigned long next_generation; + +/* ---------------------------------------------------------------- */ +/* Functions for looking inside disk_cache */ + +#define trunc_block(offs) \ + ((off_t) ((offs) >> log2_block_size) << log2_block_size) +#define round_block(offs) \ + ((off_t) (((offs) + block_size - 1) >> log2_block_size) << log2_block_size) + +/* block num --> byte offset on disk */ +#define boffs(block) ((off_t) (block) << log2_block_size) +/* byte offset on disk --> block num */ +#define boffs_block(offs) ((offs) >> log2_block_size) + +/* pointer to in-memory block -> index in disk_cache_info */ +#define bptr_index(ptr) (((char *)ptr - (char *)disk_cache) >> log2_block_size) + +/* byte offset on disk --> pointer to in-memory block */ +EXT2FS_EI char * +boffs_ptr (off_t offset) +{ + block_t block = boffs_block (offset); + pthread_mutex_lock (&disk_cache_lock); + char *ptr = hurd_ihash_find (disk_cache_bptr, block); + pthread_mutex_unlock (&disk_cache_lock); + assert (ptr); + ptr += offset % block_size; + ext2_debug ("(%lld) = %p", offset, ptr); + return ptr; +} + +/* pointer to in-memory block --> byte offset on disk */ +EXT2FS_EI off_t +bptr_offs (void *ptr) +{ + vm_offset_t mem_offset = (char *)ptr - (char *)disk_cache; + off_t offset; + assert (mem_offset < disk_cache_size); + pthread_mutex_lock (&disk_cache_lock); + offset = (off_t) disk_cache_info[boffs_block (mem_offset)].block + << log2_block_size; + assert (offset || mem_offset < block_size); + offset += mem_offset % block_size; + pthread_mutex_unlock (&disk_cache_lock); + ext2_debug ("(%p) = %lld", ptr, offset); + return offset; +} + +/* block num --> pointer to in-memory block */ +#define bptr(block) boffs_ptr(boffs(block)) +/* pointer to in-memory block --> block num */ +#define bptr_block(ptr) boffs_block(bptr_offs(ptr)) + +/* Get the descriptor for block group NUM. The block group descriptors are + stored starting in the filesystem block following the super block. + We cache a pointer into the disk image for easy lookup. */ +#define group_desc(num) (&group_desc_image[num]) +struct ext2_group_desc *group_desc_image; + +#define inode_group_num(inum) (((inum) - 1) / sblock->s_inodes_per_group) + +extern struct ext2_inode *dino (ino_t inum); + +#if defined(__USE_EXTERN_INLINES) || defined(EXT2FS_DEFINE_EI) +/* Convert an inode number to the dinode on disk. */ +EXT2FS_EI struct ext2_inode * +dino_ref (ino_t inum) +{ + unsigned long inodes_per_group = sblock->s_inodes_per_group; + unsigned long bg_num = (inum - 1) / inodes_per_group; + unsigned long group_inum = (inum - 1) % inodes_per_group; + struct ext2_group_desc *bg = group_desc (bg_num); + block_t block = bg->bg_inode_table + (group_inum / inodes_per_block); + struct ext2_inode *inode = disk_cache_block_ref (block); + inode += group_inum % inodes_per_block; + ext2_debug ("(%llu) = %p", inum, inode); + return inode; +} + +EXT2FS_EI void +dino_deref (struct ext2_inode *inode) +{ + ext2_debug ("(%p)", inode); + disk_cache_block_deref (inode); +} +#endif /* Use extern inlines. */ + +/* ---------------------------------------------------------------- */ +/* inode.c */ + +/* Write all active disknodes into the inode pager. */ +void write_all_disknodes (); + +/* Lookup node INUM (which must have a reference already) and return it + without allocating any new references. */ +struct node *ifind (ino_t inum); + +void inode_init (void); + +/* ---------------------------------------------------------------- */ + +/* What to lock if changing global data data (e.g., the superblock or block + group descriptors or bitmaps). */ +pthread_spinlock_t global_lock; + +/* Where to record such changes. */ +struct pokel global_pokel; + +/* If the block size is less than the page size, then this bitmap is used to + record which disk blocks are actually modified, so we don't stomp on parts + of the disk which are backed by file pagers. */ +char *modified_global_blocks; +pthread_spinlock_t modified_global_blocks_lock; + +extern int global_block_modified (block_t block); +extern void record_global_poke (void *ptr); +extern void sync_global_ptr (void *bptr, int wait); +extern void record_indir_poke (struct node *node, void *ptr); +extern void sync_global (int wait); +extern void alloc_sync (struct node *np); + +#if defined(__USE_EXTERN_INLINES) || defined(EXT2FS_DEFINE_EI) +/* Marks the global block BLOCK as being modified, and returns true if we + think it may have been clean before (but we may not be sure). Note that + this isn't enough to cause the block to be synced; you must call + record_global_poke to do that. */ +EXT2FS_EI int +global_block_modified (block_t block) +{ + if (modified_global_blocks) + { + int was_clean; + pthread_spin_lock (&modified_global_blocks_lock); + was_clean = !set_bit(block, modified_global_blocks); + pthread_spin_unlock (&modified_global_blocks_lock); + return was_clean; + } + else + return 1; +} + +/* This records a modification to a non-file block. */ +EXT2FS_EI void +record_global_poke (void *ptr) +{ + block_t block = boffs_block (bptr_offs (ptr)); + void *block_ptr = bptr (block); + ext2_debug ("(%p = %p)", ptr, block_ptr); + assert (disk_cache_block_is_ref (block)); + global_block_modified (block); + pokel_add (&global_pokel, block_ptr, block_size); +} + +/* This syncs a modification to a non-file block. */ +EXT2FS_EI void +sync_global_ptr (void *bptr, int wait) +{ + block_t block = boffs_block (bptr_offs (bptr)); + void *block_ptr = bptr (block); + ext2_debug ("(%p -> %u)", bptr, block); + global_block_modified (block); + disk_cache_block_deref (block_ptr); + pager_sync_some (diskfs_disk_pager, + block_ptr - disk_cache, block_size, wait); + +} + +/* This records a modification to one of a file's indirect blocks. */ +EXT2FS_EI void +record_indir_poke (struct node *node, void *ptr) +{ + block_t block = boffs_block (bptr_offs (ptr)); + void *block_ptr = bptr (block); + ext2_debug ("(%llu, %p)", node->cache_id, ptr); + assert (disk_cache_block_is_ref (block)); + global_block_modified (block); + pokel_add (&node->dn->indir_pokel, block_ptr, block_size); +} + +/* ---------------------------------------------------------------- */ + +EXT2FS_EI void +sync_global (int wait) +{ + ext2_debug ("%d", wait); + pokel_sync (&global_pokel, wait); +} + +/* Sync all allocation information and node NP if diskfs_synchronous. */ +EXT2FS_EI void +alloc_sync (struct node *np) +{ + if (diskfs_synchronous) + { + if (np) + { + diskfs_node_update (np, 1); + pokel_sync (&np->dn->indir_pokel, 1); + } + diskfs_set_hypermetadata (1, 0); + } +} +#endif /* Use extern inlines. */ + +/* ---------------------------------------------------------------- */ +/* getblk.c */ + +void ext2_discard_prealloc (struct node *node); + +/* Returns in DISK_BLOCK the disk block corresponding to BLOCK in NODE. + If there is no such block yet, but CREATE is true, then it is created, + otherwise EINVAL is returned. */ +error_t ext2_getblk (struct node *node, block_t block, int create, block_t *disk_block); + +block_t ext2_new_block (block_t goal, + block_t prealloc_goal, + block_t *prealloc_count, block_t *prealloc_block); + +void ext2_free_blocks (block_t block, unsigned long count); + +/* ---------------------------------------------------------------- */ + +/* Write disk block ADDR with DATA of LEN bytes, waiting for completion. */ +error_t dev_write_sync (block_t addr, vm_address_t data, long len); + +/* Write diskblock ADDR with DATA of LEN bytes; don't bother waiting + for completion. */ +error_t dev_write (block_t addr, vm_address_t data, long len); + +/* Read disk block ADDR; put the address of the data in DATA; read LEN + bytes. Always *DATA should be a full page no matter what. */ +error_t dev_read_sync (block_t addr, vm_address_t *data, long len); + +/* ---------------------------------------------------------------- */ + +#define ext2_error(fmt, args...) _ext2_error (__FUNCTION__, fmt , ##args) +extern void _ext2_error (const char *, const char *, ...) + __attribute__ ((format (printf, 2, 3))); + +#define ext2_panic(fmt, args...) _ext2_panic (__FUNCTION__, fmt , ##args) +extern void _ext2_panic (const char *, const char *, ...) + __attribute__ ((format (printf, 2, 3))); + +extern void ext2_warning (const char *, ...) + __attribute__ ((format (printf, 1, 2))); |