diff options
author | Bruce Evans <bde@FreeBSD.org> | 2000-01-01 13:09:17 +0000 |
---|---|---|
committer | Bruce Evans <bde@FreeBSD.org> | 2000-01-01 13:09:17 +0000 |
commit | 18e776a6c1a90b9ba252756fd704f82b078c1a9a (patch) | |
tree | c9c69c2d074d4553f723b0aa0167844e148f54b3 | |
parent | 0631f31872b79dd0e240e8bb7261978f3af04dc7 (diff) |
Notes
-rw-r--r-- | sys/gnu/ext2fs/ext2_bitmap.c | 3 | ||||
-rw-r--r-- | sys/gnu/ext2fs/ext2_fs.h | 261 | ||||
-rw-r--r-- | sys/gnu/ext2fs/ext2_fs_i.h | 4 | ||||
-rw-r--r-- | sys/gnu/ext2fs/ext2_fs_sb.h | 12 | ||||
-rw-r--r-- | sys/gnu/ext2fs/ext2_linux_balloc.c | 451 | ||||
-rw-r--r-- | sys/gnu/ext2fs/ext2_linux_ialloc.c | 374 | ||||
-rw-r--r-- | sys/gnu/ext2fs/ext2_super.c | 524 | ||||
-rw-r--r-- | sys/gnu/ext2fs/i386-bitops.h | 180 |
8 files changed, 1123 insertions, 686 deletions
diff --git a/sys/gnu/ext2fs/ext2_bitmap.c b/sys/gnu/ext2fs/ext2_bitmap.c index 8b9b5d233064..c0ffb68cdac9 100644 --- a/sys/gnu/ext2fs/ext2_bitmap.c +++ b/sys/gnu/ext2fs/ext2_bitmap.c @@ -8,7 +8,8 @@ */ #include <linux/fs.h> -#include <linux/ext2_fs.h> + + static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0}; diff --git a/sys/gnu/ext2fs/ext2_fs.h b/sys/gnu/ext2fs/ext2_fs.h index 1129d1cf2a51..836038e59d6e 100644 --- a/sys/gnu/ext2fs/ext2_fs.h +++ b/sys/gnu/ext2fs/ext2_fs.h @@ -28,30 +28,16 @@ #undef EXT2FS_DEBUG /* - * Define EXT2FS_DEBUG_CACHE to produce cache debug messages - */ -#undef EXT2FS_DEBUG_CACHE - -/* - * Define EXT2FS_CHECK_CACHE to add some checks to the name cache code - */ -#undef EXT2FS_CHECK_CACHE - -/* - * Define EXT2FS_PRE_02B_COMPAT to convert ext 2 fs prior to 0.2b - */ -#undef EXT2FS_PRE_02B_COMPAT - -/* * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files */ #define EXT2_PREALLOCATE +#define EXT2_DEFAULT_PREALLOC_BLOCKS 8 /* * The second extended file system version */ -#define EXT2FS_DATE "95/03/19" -#define EXT2FS_VERSION "0.5a" +#define EXT2FS_DATE "95/08/09" +#define EXT2FS_VERSION "0.5b" /* * Debug code @@ -75,12 +61,13 @@ #define EXT2_ACL_DATA_INO 4 /* ACL inode */ #define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ #define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ -#define EXT2_FIRST_INO 11 /* First non reserved inode */ + +/* First non-reserved inode for old ext2 filesystems */ +#define EXT2_GOOD_OLD_FIRST_INO 11 /* * The second extended file system magic number */ -#define EXT2_PRE_02B_MAGIC 0xEF51 #define EXT2_SUPER_MAGIC 0xEF53 /* @@ -102,11 +89,22 @@ #define EXT2_ACLE_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry)) #define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32)) #ifdef __KERNEL__ -# define EXT2_BLOCK_SIZE_BITS(s) ((s)->u.ext2_sb.s_es->s_log_block_size + 10) +# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) #else # define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) #endif -#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_inode)) +#ifdef __KERNEL__ +#define EXT2_ADDR_PER_BLOCK_BITS(s) ((s)->u.ext2_sb.s_addr_per_block_bits) +#define EXT2_INODE_SIZE(s) ((s)->u.ext2_sb.s_inode_size) +#define EXT2_FIRST_INO(s) ((s)->u.ext2_sb.s_first_ino) +#else +#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_INODE_SIZE : \ + (s)->s_inode_size) +#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_FIRST_INO : \ + (s)->s_first_ino) +#endif /* * Macro-instructions used to manage fragments @@ -147,15 +145,6 @@ struct ext2_acl_entry /* Access Control List Entry */ /* * Structure of a blocks group descriptor */ -struct ext2_old_group_desc -{ - __u32 bg_block_bitmap; /* Blocks bitmap block */ - __u32 bg_inode_bitmap; /* Inodes bitmap block */ - __u32 bg_inode_table; /* Inodes table block */ - __u16 bg_free_blocks_count; /* Free blocks count */ - __u16 bg_free_inodes_count; /* Free inodes count */ -}; - struct ext2_group_desc { __u32 bg_block_bitmap; /* Blocks bitmap block */ @@ -175,6 +164,7 @@ struct ext2_group_desc # define EXT2_BLOCKS_PER_GROUP(s) ((s)->u.ext2_sb.s_blocks_per_group) # define EXT2_DESC_PER_BLOCK(s) ((s)->u.ext2_sb.s_desc_per_block) # define EXT2_INODES_PER_GROUP(s) ((s)->u.ext2_sb.s_inodes_per_group) +# define EXT2_DESC_PER_BLOCK_BITS(s) ((s)->u.ext2_sb.s_desc_per_block_bits) #else # define EXT2_BLOCKS_PER_GROUP(s) ((s)->s_blocks_per_group) # define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) @@ -200,6 +190,18 @@ struct ext2_group_desc #define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ #define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ #define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ +#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ +/* Reserved for compression usage... */ +#define EXT2_DIRTY_FL 0x00000100 +#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ +#define EXT2_NOCOMP_FL 0x00000400 /* Don't compress */ +#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ +/* End compression flags --- maybe not all used */ +#define EXT2_BTREE_FL 0x00001000 /* btree format dir */ +#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ + +#define EXT2_FL_USER_VISIBLE 0x00001FFF /* User visible flags */ +#define EXT2_FL_USER_MODIFIABLE 0x000000FF /* User modifiable flags */ /* * ioctl commands @@ -213,17 +215,17 @@ struct ext2_group_desc * Structure of an inode on the disk */ struct ext2_inode { - __u16 i_mode; /* File mode */ - __u16 i_uid; /* Owner Uid */ - __u32 i_size; /* Size in bytes */ - __u32 i_atime; /* Access time */ - __u32 i_ctime; /* Creation time */ - __u32 i_mtime; /* Modification time */ - __u32 i_dtime; /* Deletion Time */ - __u16 i_gid; /* Group Id */ - __u16 i_links_count; /* Links count */ - __u32 i_blocks; /* Blocks count */ - __u32 i_flags; /* File flags */ + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Owner Uid */ + __u32 i_size; /* Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* Creation time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* Deletion Time */ + __u16 i_gid; /* Group Id */ + __u16 i_links_count; /* Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* File flags */ union { struct { __u32 l_i_reserved1; @@ -236,10 +238,10 @@ struct ext2_inode { } masix1; } osd1; /* OS dependent 1 */ __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ - __u32 i_version; /* File version (for NFS) */ + __u32 i_generation; /* File version (for NFS) */ __u32 i_file_acl; /* File ACL */ __u32 i_dir_acl; /* Directory ACL */ - __u32 i_faddr; /* Fragment address */ + __u32 i_faddr; /* Fragment address */ union { struct { __u8 l_i_frag; /* Fragment number */ @@ -264,6 +266,8 @@ struct ext2_inode { } osd2; /* OS dependent 2 */ }; +#define i_size_high i_dir_acl + #if defined(__KERNEL__) || defined(__linux__) #define i_reserved1 osd1.linux1.l_i_reserved1 #define i_frag osd2.linux2.l_i_frag @@ -347,22 +351,104 @@ struct ext2_super_block { __u16 s_magic; /* Magic signature */ __u16 s_state; /* File system state */ __u16 s_errors; /* Behaviour when detecting errors */ - __u16 s_pad; + __u16 s_minor_rev_level; /* minor revision level */ __u32 s_lastcheck; /* time of last check */ __u32 s_checkinterval; /* max. time between checks */ __u32 s_creator_os; /* OS */ __u32 s_rev_level; /* Revision level */ __u16 s_def_resuid; /* Default uid for reserved blocks */ __u16 s_def_resgid; /* Default gid for reserved blocks */ - __u32 s_reserved[235]; /* Padding to the end of the block */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __u32 s_first_ino; /* First non-reserved inode */ + __u16 s_inode_size; /* size of inode structure */ + __u16 s_block_group_nr; /* block group # of this superblock */ + __u32 s_feature_compat; /* compatible feature set */ + __u32 s_feature_incompat; /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ + __u8 s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + __u32 s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_COMPAT_PREALLOC flag is on. + */ + __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + __u16 s_padding1; + __u32 s_reserved[204]; /* Padding to the end of the block */ }; +#ifdef __KERNEL__ +#define EXT2_SB(sb) (&((sb)->u.ext2_sb)) +#else +/* Assume that user mode programs are passing in an ext2fs superblock, not + * a kernel struct super_block. This will allow us to call the feature-test + * macros from user land. */ +#define EXT2_SB(sb) (sb) +#endif + +/* + * Codes for operating systems + */ #define EXT2_OS_LINUX 0 #define EXT2_OS_HURD 1 #define EXT2_OS_MASIX 2 +#define EXT2_OS_FREEBSD 3 +#define EXT2_OS_LITES 4 -#define EXT2_CURRENT_REV 0 +/* + * Revision levels + */ +#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ +#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ + +#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV +#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV + +#define EXT2_GOOD_OLD_INODE_SIZE 128 + +/* + * Feature set definitions + */ +#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_compat & (mask) ) +#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_ro_compat & (mask) ) +#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_feature_incompat & (mask) ) + +#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 + +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 + +#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 + +#define EXT2_FEATURE_COMPAT_SUPP 0 +#define EXT2_FEATURE_INCOMPAT_SUPP EXT2_FEATURE_INCOMPAT_FILETYPE +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) + +/* + * Default values for user and/or group using reserved blocks + */ #define EXT2_DEF_RESUID 0 #define EXT2_DEF_RESGID 0 @@ -379,6 +465,35 @@ struct ext2_dir_entry { }; /* + * The new version of the directory entry. Since EXT2 structures are + * stored in intel byte order, and the name_len field could never be + * bigger than 255 chars, it's safe to reclaim the extra byte for the + * file_type field. + */ +struct ext2_dir_entry_2 { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u8 name_len; /* Name length */ + __u8 file_type; + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * Ext2 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +#define EXT2_FT_UNKNOWN 0 +#define EXT2_FT_REG_FILE 1 +#define EXT2_FT_DIR 2 +#define EXT2_FT_CHRDEV 3 +#define EXT2_FT_BLKDEV 4 +#define EXT2_FT_FIFO 5 +#define EXT2_FT_SOCK 6 +#define EXT2_FT_SYMLINK 7 + +#define EXT2_FT_MAX 8 + +/* * EXT2_DIR_PAD defines the directory entries boundaries * * NOTE: It must be a multiple of 4 @@ -397,33 +512,31 @@ struct ext2_dir_entry { * Ok, these declarations are also in <linux/kernel.h> but none of the * ext2 source programs needs to include it so they are duplicated here. */ -#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) -# define NORET_TYPE __volatile__ -# define ATTRIB_NORET /**/ -# define NORET_AND /**/ -#else # define NORET_TYPE /**/ # define ATTRIB_NORET __attribute__((noreturn)) # define NORET_AND noreturn, -#endif /* acl.c */ extern int ext2_permission (struct inode *, int); /* balloc.c */ -extern int ext2_new_block (struct super_block *, unsigned long, - __u32 *, __u32 *); -extern void ext2_free_blocks (struct super_block *, unsigned long, +extern int ext2_group_sparse(int group); +extern int ext2_new_block (const struct inode *, unsigned long, + __u32 *, __u32 *, int *); +extern void ext2_free_blocks (const struct inode *, unsigned long, unsigned long); extern unsigned long ext2_count_free_blocks (struct super_block *); extern void ext2_check_blocks_bitmap (struct super_block *); +extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb, + unsigned int block_group, + struct buffer_head ** bh); /* bitmap.c */ extern unsigned long ext2_count_free (struct buffer_head *, unsigned); /* dir.c */ -extern int ext2_check_dir_entry (char *, struct inode *, - struct ext2_dir_entry *, struct buffer_head *, +extern int ext2_check_dir_entry (const char *, struct inode *, + struct ext2_dir_entry_2 *, struct buffer_head *, unsigned long); /* file.c */ @@ -431,24 +544,27 @@ extern int ext2_read (struct inode *, struct file *, char *, int); extern int ext2_write (struct inode *, struct file *, char *, int); /* fsync.c */ -extern int ext2_sync_file (struct inode *, struct file *); +extern int ext2_sync_file (struct file *, struct dentry *); /* ialloc.c */ -extern struct inode * ext2_new_inode (const struct inode *, int); +extern struct inode * ext2_new_inode (const struct inode *, int, int *); extern void ext2_free_inode (struct inode *); extern unsigned long ext2_count_free_inodes (struct super_block *); extern void ext2_check_inodes_bitmap (struct super_block *); /* inode.c */ -extern int ext2_bmap (struct inode *, int); +extern long ext2_bmap (struct inode *, long); +extern int ext2_get_block (struct inode *, long, struct buffer_head *, int); extern struct buffer_head * ext2_getblk (struct inode *, long, int, int *); +extern int ext2_getblk_block (struct inode *, long, int, int *, int *); extern struct buffer_head * ext2_bread (struct inode *, int, int, int *); extern int ext2_getcluster (struct inode * inode, long block); extern void ext2_read_inode (struct inode *); extern void ext2_write_inode (struct inode *); extern void ext2_put_inode (struct inode *); +extern void ext2_delete_inode (struct inode *); extern int ext2_sync_inode (struct inode *); extern void ext2_discard_prealloc (struct inode *); @@ -458,17 +574,16 @@ extern int ext2_ioctl (struct inode *, struct file *, unsigned int, /* namei.c */ extern void ext2_release (struct inode *, struct file *); -extern int ext2_lookup (struct inode *,const char *, int, struct inode **); -extern int ext2_create (struct inode *,const char *, int, int, - struct inode **); -extern int ext2_mkdir (struct inode *, const char *, int, int); -extern int ext2_rmdir (struct inode *, const char *, int); -extern int ext2_unlink (struct inode *, const char *, int); -extern int ext2_symlink (struct inode *, const char *, int, const char *); -extern int ext2_link (struct inode *, struct inode *, const char *, int); -extern int ext2_mknod (struct inode *, const char *, int, int, int); -extern int ext2_rename (struct inode *, const char *, int, - struct inode *, const char *, int); +extern struct dentry *ext2_lookup (struct inode *, struct dentry *); +extern int ext2_create (struct inode *,struct dentry *,int); +extern int ext2_mkdir (struct inode *,struct dentry *,int); +extern int ext2_rmdir (struct inode *,struct dentry *); +extern int ext2_unlink (struct inode *,struct dentry *); +extern int ext2_symlink (struct inode *,struct dentry *,const char *); +extern int ext2_link (struct dentry *, struct inode *, struct dentry *); +extern int ext2_mknod (struct inode *, struct dentry *, int, int); +extern int ext2_rename (struct inode *, struct dentry *, + struct inode *, struct dentry *); /* super.c */ extern void ext2_error (struct super_block *, const char *, const char *, ...) @@ -482,7 +597,8 @@ extern void ext2_put_super (struct super_block *); extern void ext2_write_super (struct super_block *); extern int ext2_remount (struct super_block *, int *, char *); extern struct super_block * ext2_read_super (struct super_block *,void *,int); -extern void ext2_statfs (struct super_block *, struct statfs *); +extern int init_ext2_fs(void); +extern int ext2_statfs (struct super_block *, struct statfs *, int); /* truncate.c */ extern void ext2_truncate (struct inode *); @@ -499,6 +615,7 @@ extern struct inode_operations ext2_file_inode_operations; /* symlink.c */ extern struct inode_operations ext2_symlink_inode_operations; +extern struct inode_operations ext2_fast_symlink_inode_operations; #endif /* __KERNEL__ */ diff --git a/sys/gnu/ext2fs/ext2_fs_i.h b/sys/gnu/ext2fs/ext2_fs_i.h index f3eca4480a1b..72bcd5c06da8 100644 --- a/sys/gnu/ext2fs/ext2_fs_i.h +++ b/sys/gnu/ext2fs/ext2_fs_i.h @@ -29,12 +29,14 @@ struct ext2_inode_info { __u32 i_file_acl; __u32 i_dir_acl; __u32 i_dtime; - __u32 i_version; + __u32 not_used_1; /* FIX: not used/ 2.2 placeholder */ __u32 i_block_group; __u32 i_next_alloc_block; __u32 i_next_alloc_goal; __u32 i_prealloc_block; __u32 i_prealloc_count; + __u32 i_high_size; + int i_new_inode:1; /* Is a freshly allocated inode */ }; #endif /* _LINUX_EXT2_FS_I */ diff --git a/sys/gnu/ext2fs/ext2_fs_sb.h b/sys/gnu/ext2fs/ext2_fs_sb.h index 685efeb348f4..207216323d18 100644 --- a/sys/gnu/ext2fs/ext2_fs_sb.h +++ b/sys/gnu/ext2fs/ext2_fs_sb.h @@ -16,6 +16,8 @@ #ifndef _LINUX_EXT2_FS_SB #define _LINUX_EXT2_FS_SB +#include <linux/ext2_fs.h> + /* * The following is not needed anymore since the descriptors buffer * heads are now dynamically allocated @@ -47,12 +49,18 @@ struct ext2_sb_info { struct buffer_head * s_inode_bitmap[EXT2_MAX_GROUP_LOADED]; unsigned long s_block_bitmap_number[EXT2_MAX_GROUP_LOADED]; struct buffer_head * s_block_bitmap[EXT2_MAX_GROUP_LOADED]; - int s_rename_lock; - struct wait_queue * s_rename_wait; unsigned long s_mount_opt; unsigned short s_resuid; unsigned short s_resgid; unsigned short s_mount_state; + unsigned short s_pad; + int s_addr_per_block_bits; + int s_desc_per_block_bits; + int s_inode_size; + int s_first_ino; + int s_feature_compat; + int s_feature_incompat; + int s_feature_ro_compat; }; #endif /* _LINUX_EXT2_FS_SB */ diff --git a/sys/gnu/ext2fs/ext2_linux_balloc.c b/sys/gnu/ext2fs/ext2_linux_balloc.c index c476fa2b256c..43a425bbb148 100644 --- a/sys/gnu/ext2fs/ext2_linux_balloc.c +++ b/sys/gnu/ext2fs/ext2_linux_balloc.c @@ -7,8 +7,15 @@ * Universite Pierre et Marie Curie (Paris VI) * * Enhanced block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 + * Big-endian to little-endian byte-swapping/bitmaps by + * David S. Miller (davem@caip.rutgers.edu), 1995 */ +#include <linux/fs.h> +#include <linux/locks.h> +#include <linux/quotaops.h> + + /* * balloc.c contains the blocks allocation and deallocation routines */ @@ -24,38 +31,36 @@ * when a file system is mounted (see ext2_read_super). */ -#include <linux/fs.h> -#include <linux/ext2_fs.h> -#include <linux/stat.h> -#include <linux/sched.h> -#include <linux/string.h> -#include <linux/locks.h> - -#include <asm/bitops.h> #define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1) -static struct ext2_group_desc * get_group_desc (struct super_block * sb, - unsigned int block_group, - struct buffer_head ** bh) +struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb, + unsigned int block_group, + struct buffer_head ** bh) { unsigned long group_desc; unsigned long desc; struct ext2_group_desc * gdp; - if (block_group >= sb->u.ext2_sb.s_groups_count) - ext2_panic (sb, "get_group_desc", + if (block_group >= sb->u.ext2_sb.s_groups_count) { + ext2_error (sb, "ext2_get_group_desc", "block_group >= groups_count - " "block_group = %d, groups_count = %lu", block_group, sb->u.ext2_sb.s_groups_count); + return NULL; + } + group_desc = block_group / EXT2_DESC_PER_BLOCK(sb); desc = block_group % EXT2_DESC_PER_BLOCK(sb); - if (!sb->u.ext2_sb.s_group_desc[group_desc]) - ext2_panic (sb, "get_group_desc", + if (!sb->u.ext2_sb.s_group_desc[group_desc]) { + ext2_error (sb, "ext2_get_group_desc", "Group descriptor not loaded - " "block_group = %d, group_desc = %lu, desc = %lu", block_group, group_desc, desc); + return NULL; + } + gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[group_desc]->b_data; if (bh) @@ -63,22 +68,41 @@ static struct ext2_group_desc * get_group_desc (struct super_block * sb, return gdp + desc; } -static void read_block_bitmap (struct super_block * sb, +/* + * Read the bitmap for a given block_group, reading into the specified + * slot in the superblock's bitmap cache. + * + * Return >=0 on success or a -ve error code. + */ + +static int read_block_bitmap (struct super_block * sb, unsigned int block_group, unsigned long bitmap_nr) { struct ext2_group_desc * gdp; - struct buffer_head * bh; + struct buffer_head * bh = NULL; + int retval = -EIO; - gdp = get_group_desc (sb, block_group, NULL); - bh = bread (sb->s_dev, gdp->bg_block_bitmap, sb->s_blocksize); - if (!bh) - ext2_panic (sb, "read_block_bitmap", + gdp = ext2_get_group_desc (sb, block_group, NULL); + if (!gdp) + goto error_out; + retval = 0; + bh = bread (sb->s_dev, le32_to_cpu(gdp->bg_block_bitmap), sb->s_blocksize); + if (!bh) { + ext2_error (sb, "read_block_bitmap", "Cannot read block bitmap - " "block_group = %d, block_bitmap = %lu", block_group, (unsigned long) gdp->bg_block_bitmap); + retval = -EIO; + } + /* + * On IO error, just leave a zero in the superblock's block pointer for + * this group. The IO will be retried next time. + */ +error_out: sb->u.ext2_sb.s_block_bitmap_number[bitmap_nr] = block_group; sb->u.ext2_sb.s_block_bitmap[bitmap_nr] = bh; + return retval; } /* @@ -91,11 +115,13 @@ static void read_block_bitmap (struct super_block * sb, * 1/ There is one cache per mounted file system. * 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups, * this function reads the bitmap without maintaining a LRU cache. + * + * Return the slot used to store the bitmap, or a -ve error code. */ -static int load__block_bitmap (struct super_block * sb, - unsigned int block_group) +static int __load_block_bitmap (struct super_block * sb, + unsigned int block_group) { - int i, j; + int i, j, retval = 0; unsigned long block_bitmap_number; struct buffer_head * block_bitmap; @@ -107,16 +133,16 @@ static int load__block_bitmap (struct super_block * sb, if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) { if (sb->u.ext2_sb.s_block_bitmap[block_group]) { - if (sb->u.ext2_sb.s_block_bitmap_number[block_group] != + if (sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group) - ext2_panic (sb, "load_block_bitmap", - "block_group != block_bitmap_number"); - else return block_group; - } else { - read_block_bitmap (sb, block_group, block_group); - return block_group; + ext2_error (sb, "__load_block_bitmap", + "block_group != block_bitmap_number"); } + retval = read_block_bitmap (sb, block_group, block_group); + if (retval < 0) + return retval; + return block_group; } for (i = 0; i < sb->u.ext2_sb.s_loaded_block_bitmaps && @@ -134,6 +160,14 @@ static int load__block_bitmap (struct super_block * sb, } sb->u.ext2_sb.s_block_bitmap_number[0] = block_bitmap_number; sb->u.ext2_sb.s_block_bitmap[0] = block_bitmap; + + /* + * There's still one special case here --- if block_bitmap == 0 + * then our last attempt to read the bitmap failed and we have + * just ended up caching that failure. Try again to read it. + */ + if (!block_bitmap) + retval = read_block_bitmap (sb, block_group, 0); } else { if (sb->u.ext2_sb.s_loaded_block_bitmaps < EXT2_MAX_GROUP_LOADED) sb->u.ext2_sb.s_loaded_block_bitmaps++; @@ -145,27 +179,74 @@ static int load__block_bitmap (struct super_block * sb, sb->u.ext2_sb.s_block_bitmap[j] = sb->u.ext2_sb.s_block_bitmap[j - 1]; } - read_block_bitmap (sb, block_group, 0); + retval = read_block_bitmap (sb, block_group, 0); } - return 0; + return retval; } +/* + * Load the block bitmap for a given block group. First of all do a couple + * of fast lookups for common cases and then pass the request onto the guts + * of the bitmap loader. + * + * Return the slot number of the group in the superblock bitmap cache's on + * success, or a -ve error code. + * + * There is still one inconsistency here --- if the number of groups in this + * filesystems is <= EXT2_MAX_GROUP_LOADED, then we have no way of + * differentiating between a group for which we have never performed a bitmap + * IO request, and a group for which the last bitmap read request failed. + */ static inline int load_block_bitmap (struct super_block * sb, unsigned int block_group) { + int slot; + + /* + * Do the lookup for the slot. First of all, check if we're asking + * for the same slot as last time, and did we succeed that last time? + */ if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 && - sb->u.ext2_sb.s_block_bitmap_number[0] == block_group) + sb->u.ext2_sb.s_block_bitmap_number[0] == block_group && + sb->u.ext2_sb.s_block_bitmap[block_group]) { return 0; - - if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED && - sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group && - sb->u.ext2_sb.s_block_bitmap[block_group]) - return block_group; + } + /* + * Or can we do a fast lookup based on a loaded group on a filesystem + * small enough to be mapped directly into the superblock? + */ + else if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED && + sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group && + sb->u.ext2_sb.s_block_bitmap[block_group]) { + slot = block_group; + } + /* + * If not, then do a full lookup for this block group. + */ + else { + slot = __load_block_bitmap (sb, block_group); + } - return load__block_bitmap (sb, block_group); + /* + * <0 means we just got an error + */ + if (slot < 0) + return slot; + + /* + * If it's a valid slot, we may still have cached a previous IO error, + * in which case the bh in the superblock cache will be zero. + */ + if (!sb->u.ext2_sb.s_block_bitmap[slot]) + return -EIO; + + /* + * Must have been read in OK to get this far. + */ + return slot; } -void ext2_free_blocks (struct super_block * sb, unsigned long block, +void ext2_free_blocks (const struct inode * inode, unsigned long block, unsigned long count) { struct buffer_head * bh; @@ -174,44 +255,57 @@ void ext2_free_blocks (struct super_block * sb, unsigned long block, unsigned long bit; unsigned long i; int bitmap_nr; + unsigned long overflow; + struct super_block * sb; struct ext2_group_desc * gdp; struct ext2_super_block * es; + sb = inode->i_sb; if (!sb) { printk ("ext2_free_blocks: nonexistent device"); return; } lock_super (sb); es = sb->u.ext2_sb.s_es; - if (block < es->s_first_data_block || - (block + count) > es->s_blocks_count) { + if (block < le32_to_cpu(es->s_first_data_block) || + (block + count) > le32_to_cpu(es->s_blocks_count)) { ext2_error (sb, "ext2_free_blocks", "Freeing blocks not in datazone - " "block = %lu, count = %lu", block, count); - unlock_super (sb); - return; + goto error_return; } ext2_debug ("freeing block %lu\n", block); - block_group = (block - es->s_first_data_block) / +do_more: + overflow = 0; + block_group = (block - le32_to_cpu(es->s_first_data_block)) / EXT2_BLOCKS_PER_GROUP(sb); - bit = (block - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb); - if (bit + count > EXT2_BLOCKS_PER_GROUP(sb)) - ext2_panic (sb, "ext2_free_blocks", - "Freeing blocks across group boundary - " - "Block = %lu, count = %lu", - block, count); + bit = (block - le32_to_cpu(es->s_first_data_block)) % + EXT2_BLOCKS_PER_GROUP(sb); + /* + * Check to see if we are freeing blocks across a group + * boundary. + */ + if (bit + count > EXT2_BLOCKS_PER_GROUP(sb)) { + overflow = bit + count - EXT2_BLOCKS_PER_GROUP(sb); + count -= overflow; + } bitmap_nr = load_block_bitmap (sb, block_group); + if (bitmap_nr < 0) + goto error_return; + bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr]; - gdp = get_group_desc (sb, block_group, &bh2); + gdp = ext2_get_group_desc (sb, block_group, &bh2); + if (!gdp) + goto error_return; if (test_opt (sb, CHECK_STRICT) && - (in_range (gdp->bg_block_bitmap, block, count) || - in_range (gdp->bg_inode_bitmap, block, count) || - in_range (block, gdp->bg_inode_table, + (in_range (le32_to_cpu(gdp->bg_block_bitmap), block, count) || + in_range (le32_to_cpu(gdp->bg_inode_bitmap), block, count) || + in_range (block, le32_to_cpu(gdp->bg_inode_table), sb->u.ext2_sb.s_itb_per_group) || - in_range (block + count - 1, gdp->bg_inode_table, + in_range (block + count - 1, le32_to_cpu(gdp->bg_inode_table), sb->u.ext2_sb.s_itb_per_group))) ext2_panic (sb, "ext2_free_blocks", "Freeing blocks in system zones - " @@ -219,13 +313,16 @@ void ext2_free_blocks (struct super_block * sb, unsigned long block, block, count); for (i = 0; i < count; i++) { - if (!clear_bit (bit + i, bh->b_data)) + if (!ext2_clear_bit (bit + i, bh->b_data)) ext2_warning (sb, "ext2_free_blocks", "bit already cleared for block %lu", block); else { - gdp->bg_free_blocks_count++; - es->s_free_blocks_count++; + DQUOT_FREE_BLOCK(sb, inode, 1); + gdp->bg_free_blocks_count = + cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count)+1); + es->s_free_blocks_count = + cpu_to_le32(le32_to_cpu(es->s_free_blocks_count)+1); } } @@ -237,7 +334,13 @@ void ext2_free_blocks (struct super_block * sb, unsigned long block, ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } + if (overflow) { + block += count; + count = overflow; + goto do_more; + } sb->s_dirt = 1; +error_return: unlock_super (sb); return; } @@ -249,32 +352,34 @@ void ext2_free_blocks (struct super_block * sb, unsigned long block, * each block group the search first looks for an entire free byte in the block * bitmap, and then for any free bit if that fails. */ -int ext2_new_block (struct super_block * sb, unsigned long goal, - u32 * prealloc_count, - u32 * prealloc_block) +int ext2_new_block (const struct inode * inode, unsigned long goal, + u32 * prealloc_count, u32 * prealloc_block, int * err) { struct buffer_head * bh; struct buffer_head * bh2; char * p, * r; int i, j, k, tmp; - unsigned long lmap; int bitmap_nr; + struct super_block * sb; struct ext2_group_desc * gdp; struct ext2_super_block * es; - #ifdef EXT2FS_DEBUG static int goal_hits = 0, goal_attempts = 0; #endif + *err = -ENOSPC; + sb = inode->i_sb; if (!sb) { printk ("ext2_new_block: nonexistent device"); return 0; } + lock_super (sb); es = sb->u.ext2_sb.s_es; - if (es->s_free_blocks_count <= es->s_r_blocks_count && - (!fsuser() && (sb->u.ext2_sb.s_resuid != current->fsuid) && + if (le32_to_cpu(es->s_free_blocks_count) <= le32_to_cpu(es->s_r_blocks_count) && + ((sb->u.ext2_sb.s_resuid != current->fsuid) && (sb->u.ext2_sb.s_resgid == 0 || - !in_group_p (sb->u.ext2_sb.s_resgid)))) { + !in_group_p (sb->u.ext2_sb.s_resgid)) && + !capable(CAP_SYS_RESOURCE))) { unlock_super (sb); return 0; } @@ -285,22 +390,29 @@ repeat: /* * First, test whether the goal block is free. */ - if (goal < es->s_first_data_block || goal >= es->s_blocks_count) - goal = es->s_first_data_block; - i = (goal - es->s_first_data_block) / EXT2_BLOCKS_PER_GROUP(sb); - gdp = get_group_desc (sb, i, &bh2); - if (gdp->bg_free_blocks_count > 0) { - j = ((goal - es->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb)); + if (goal < le32_to_cpu(es->s_first_data_block) || + goal >= le32_to_cpu(es->s_blocks_count)) + goal = le32_to_cpu(es->s_first_data_block); + i = (goal - le32_to_cpu(es->s_first_data_block)) / EXT2_BLOCKS_PER_GROUP(sb); + gdp = ext2_get_group_desc (sb, i, &bh2); + if (!gdp) + goto io_error; + + if (le16_to_cpu(gdp->bg_free_blocks_count) > 0) { + j = ((goal - le32_to_cpu(es->s_first_data_block)) % EXT2_BLOCKS_PER_GROUP(sb)); #ifdef EXT2FS_DEBUG if (j) goal_attempts++; #endif bitmap_nr = load_block_bitmap (sb, i); + if (bitmap_nr < 0) + goto io_error; + bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr]; ext2_debug ("goal is at %d:%d.\n", i, j); - if (!test_bit(j, bh->b_data)) { + if (!ext2_test_bit(j, bh->b_data)) { #ifdef EXT2FS_DEBUG goal_hits++; ext2_debug ("goal bit allocated.\n"); @@ -310,22 +422,16 @@ repeat: if (j) { /* * The goal was occupied; search forward for a free - * block within the next 32 blocks + * block within the next XX blocks. + * + * end_goal is more or less random, but it has to be + * less than EXT2_BLOCKS_PER_GROUP. Aligning up to the + * next 64-bit boundary is simple.. */ - lmap = ((((unsigned long *) bh->b_data)[j >> 5]) >> - ((j & 31) + 1)); - if (j < EXT2_BLOCKS_PER_GROUP(sb) - 32) - lmap |= (((unsigned long *) bh->b_data)[(j >> 5) + 1]) << - (31 - (j & 31)); - else - lmap |= 0xffffffff << (31 - (j & 31)); - if (lmap != 0xffffffffl) { - k = ffz(lmap) + 1; - if ((j + k) < EXT2_BLOCKS_PER_GROUP(sb)) { - j += k; - goto got_block; - } - } + int end_goal = (j + 63) & ~63; + j = ext2_find_next_zero_bit(bh->b_data, end_goal, j); + if (j < end_goal) + goto got_block; } ext2_debug ("Bit not found near goal\n"); @@ -346,7 +452,8 @@ repeat: j = k; goto search_back; } - k = find_next_zero_bit ((unsigned long *) bh->b_data, + + k = ext2_find_next_zero_bit ((unsigned long *) bh->b_data, EXT2_BLOCKS_PER_GROUP(sb), j); if (k < EXT2_BLOCKS_PER_GROUP(sb)) { @@ -365,8 +472,13 @@ repeat: i++; if (i >= sb->u.ext2_sb.s_groups_count) i = 0; - gdp = get_group_desc (sb, i, &bh2); - if (gdp->bg_free_blocks_count > 0) + gdp = ext2_get_group_desc (sb, i, &bh2); + if (!gdp) { + *err = -EIO; + unlock_super (sb); + return 0; + } + if (le16_to_cpu(gdp->bg_free_blocks_count) > 0) break; } if (k >= sb->u.ext2_sb.s_groups_count) { @@ -374,13 +486,16 @@ repeat: return 0; } bitmap_nr = load_block_bitmap (sb, i); + if (bitmap_nr < 0) + goto io_error; + bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr]; r = memscan(bh->b_data, 0, EXT2_BLOCKS_PER_GROUP(sb) >> 3); j = (r - bh->b_data) << 3; if (j < EXT2_BLOCKS_PER_GROUP(sb)) goto search_back; else - j = find_first_zero_bit ((unsigned long *) bh->b_data, + j = ext2_find_first_zero_bit ((unsigned long *) bh->b_data, EXT2_BLOCKS_PER_GROUP(sb)); if (j >= EXT2_BLOCKS_PER_GROUP(sb)) { ext2_error (sb, "ext2_new_block", @@ -395,25 +510,35 @@ search_back: * bitmap. Now search backwards up to 7 bits to find the * start of this group of free blocks. */ - for (k = 0; k < 7 && j > 0 && !test_bit (j - 1, bh->b_data); k++, j--); + for (k = 0; k < 7 && j > 0 && !ext2_test_bit (j - 1, bh->b_data); k++, j--); got_block: ext2_debug ("using block group %d(%d)\n", i, gdp->bg_free_blocks_count); - tmp = j + i * EXT2_BLOCKS_PER_GROUP(sb) + es->s_first_data_block; + /* + * Check quota for allocation of this block. + */ + if(DQUOT_ALLOC_BLOCK(sb, inode, 1)) { + unlock_super(sb); + *err = -EDQUOT; + return 0; + } + + tmp = j + i * EXT2_BLOCKS_PER_GROUP(sb) + le32_to_cpu(es->s_first_data_block); if (test_opt (sb, CHECK_STRICT) && - (tmp == gdp->bg_block_bitmap || - tmp == gdp->bg_inode_bitmap || - in_range (tmp, gdp->bg_inode_table, sb->u.ext2_sb.s_itb_per_group))) + (tmp == le32_to_cpu(gdp->bg_block_bitmap) || + tmp == le32_to_cpu(gdp->bg_inode_bitmap) || + in_range (tmp, le32_to_cpu(gdp->bg_inode_table), sb->u.ext2_sb.s_itb_per_group))) ext2_panic (sb, "ext2_new_block", "Allocating block in system zone - " "block = %u", tmp); - if (set_bit (j, bh->b_data)) { + if (ext2_set_bit (j, bh->b_data)) { ext2_warning (sb, "ext2_new_block", "bit already set for block %d", j); + DQUOT_FREE_BLOCK(sb, inode, 1); goto repeat; } @@ -424,16 +549,30 @@ got_block: */ #ifdef EXT2_PREALLOCATE if (prealloc_block) { + int prealloc_goal; + + prealloc_goal = es->s_prealloc_blocks ? + es->s_prealloc_blocks : EXT2_DEFAULT_PREALLOC_BLOCKS; + *prealloc_count = 0; *prealloc_block = tmp + 1; for (k = 1; - k < 8 && (j + k) < EXT2_BLOCKS_PER_GROUP(sb); k++) { - if (set_bit (j + k, bh->b_data)) + k < prealloc_goal && (j + k) < EXT2_BLOCKS_PER_GROUP(sb); + k++) { + if (DQUOT_PREALLOC_BLOCK(sb, inode, 1)) break; + if (ext2_set_bit (j + k, bh->b_data)) { + DQUOT_FREE_BLOCK(sb, inode, 1); + break; + } (*prealloc_count)++; } - gdp->bg_free_blocks_count -= *prealloc_count; - es->s_free_blocks_count -= *prealloc_count; + gdp->bg_free_blocks_count = + cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) - + *prealloc_count); + es->s_free_blocks_count = + cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) - + *prealloc_count); ext2_debug ("Preallocated a further %lu bits.\n", *prealloc_count); } @@ -447,33 +586,32 @@ got_block: wait_on_buffer (bh); } - if (j >= es->s_blocks_count) { + if (j >= le32_to_cpu(es->s_blocks_count)) { ext2_error (sb, "ext2_new_block", - "block >= blocks count - " - "block_group = %d, block=%d", i, j); - unlock_super (sb); - return 0; - } - if (!(bh = getblk (sb->s_dev, j, sb->s_blocksize))) { - ext2_error (sb, "ext2_new_block", "cannot get block %d", j); + "block(%d) >= blocks count(%d) - " + "block_group = %d, es == %p ",j, + le32_to_cpu(es->s_blocks_count), i, es); unlock_super (sb); return 0; } - memset(bh->b_data, 0, sb->s_blocksize); - bh->b_uptodate = 1; - mark_buffer_dirty(bh, 1); - brelse (bh); ext2_debug ("allocating block %d. " "Goal hits %d of %d.\n", j, goal_hits, goal_attempts); - gdp->bg_free_blocks_count--; + gdp->bg_free_blocks_count = cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) - 1); mark_buffer_dirty(bh2, 1); - es->s_free_blocks_count--; + es->s_free_blocks_count = cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) - 1); mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); sb->s_dirt = 1; unlock_super (sb); + *err = 0; return j; + +io_error: + *err = -EIO; + unlock_super (sb); + return 0; + } unsigned long ext2_count_free_blocks (struct super_block * sb) @@ -491,21 +629,26 @@ unsigned long ext2_count_free_blocks (struct super_block * sb) bitmap_count = 0; gdp = NULL; for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) { - gdp = get_group_desc (sb, i, NULL); - desc_count += gdp->bg_free_blocks_count; + gdp = ext2_get_group_desc (sb, i, NULL); + if (!gdp) + continue; + desc_count += le16_to_cpu(gdp->bg_free_blocks_count); bitmap_nr = load_block_bitmap (sb, i); + if (bitmap_nr < 0) + continue; + x = ext2_count_free (sb->u.ext2_sb.s_block_bitmap[bitmap_nr], sb->s_blocksize); printk ("group %d: stored = %d, counted = %lu\n", - i, gdp->bg_free_blocks_count, x); + i, le16_to_cpu(gdp->bg_free_blocks_count), x); bitmap_count += x; } printk("ext2_count_free_blocks: stored = %lu, computed = %lu, %lu\n", - es->s_free_blocks_count, desc_count, bitmap_count); + le32_to_cpu(es->s_free_blocks_count), desc_count, bitmap_count); unlock_super (sb); return bitmap_count; #else - return sb->u.ext2_sb.s_es->s_free_blocks_count; + return le32_to_cpu(sb->u.ext2_sb.s_es->s_free_blocks_count); #endif } @@ -513,10 +656,29 @@ static inline int block_in_use (unsigned long block, struct super_block * sb, unsigned char * map) { - return test_bit ((block - sb->u.ext2_sb.s_es->s_first_data_block) % + return ext2_test_bit ((block - le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block)) % EXT2_BLOCKS_PER_GROUP(sb), map); } +static int test_root(int a, int b) +{ + if (a == 0) + return 1; + while (1) { + if (a == 1) + return 1; + if (a % b) + return 0; + a = a / b; + } +} + +int ext2_group_sparse(int group) +{ + return (test_root(group, 3) || test_root(group, 5) || + test_root(group, 7)); +} + void ext2_check_blocks_bitmap (struct super_block * sb) { struct buffer_head * bh; @@ -535,49 +697,60 @@ void ext2_check_blocks_bitmap (struct super_block * sb) desc_blocks = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) / EXT2_DESC_PER_BLOCK(sb); for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) { - gdp = get_group_desc (sb, i, NULL); - desc_count += gdp->bg_free_blocks_count; + gdp = ext2_get_group_desc (sb, i, NULL); + if (!gdp) + continue; + desc_count += le16_to_cpu(gdp->bg_free_blocks_count); bitmap_nr = load_block_bitmap (sb, i); + if (bitmap_nr < 0) + continue; + bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr]; - if (!test_bit (0, bh->b_data)) - ext2_error (sb, "ext2_check_blocks_bitmap", - "Superblock in group %d is marked free", i); - - for (j = 0; j < desc_blocks; j++) - if (!test_bit (j + 1, bh->b_data)) + if (!(sb->u.ext2_sb.s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) || + ext2_group_sparse(i)) { + if (!ext2_test_bit (0, bh->b_data)) ext2_error (sb, "ext2_check_blocks_bitmap", + "Superblock in group %d " + "is marked free", i); + + for (j = 0; j < desc_blocks; j++) + if (!ext2_test_bit (j + 1, bh->b_data)) + ext2_error (sb, + "ext2_check_blocks_bitmap", "Descriptor block #%d in group " "%d is marked free", j, i); + } - if (!block_in_use (gdp->bg_block_bitmap, sb, bh->b_data)) + if (!block_in_use (le32_to_cpu(gdp->bg_block_bitmap), sb, bh->b_data)) ext2_error (sb, "ext2_check_blocks_bitmap", "Block bitmap for group %d is marked free", i); - if (!block_in_use (gdp->bg_inode_bitmap, sb, bh->b_data)) + if (!block_in_use (le32_to_cpu(gdp->bg_inode_bitmap), sb, bh->b_data)) ext2_error (sb, "ext2_check_blocks_bitmap", "Inode bitmap for group %d is marked free", i); for (j = 0; j < sb->u.ext2_sb.s_itb_per_group; j++) - if (!block_in_use (gdp->bg_inode_table + j, sb, bh->b_data)) + if (!block_in_use (le32_to_cpu(gdp->bg_inode_table) + j, sb, bh->b_data)) ext2_error (sb, "ext2_check_blocks_bitmap", "Block #%d of the inode table in " "group %d is marked free", j, i); x = ext2_count_free (bh, sb->s_blocksize); - if (gdp->bg_free_blocks_count != x) + if (le16_to_cpu(gdp->bg_free_blocks_count) != x) ext2_error (sb, "ext2_check_blocks_bitmap", "Wrong free blocks count for group %d, " "stored = %d, counted = %lu", i, - gdp->bg_free_blocks_count, x); + le16_to_cpu(gdp->bg_free_blocks_count), x); bitmap_count += x; } - if (es->s_free_blocks_count != bitmap_count) + if (le32_to_cpu(es->s_free_blocks_count) != bitmap_count) ext2_error (sb, "ext2_check_blocks_bitmap", "Wrong free blocks count in super block, " "stored = %lu, counted = %lu", - (unsigned long) es->s_free_blocks_count, bitmap_count); + (unsigned long) le32_to_cpu(es->s_free_blocks_count), bitmap_count); unlock_super (sb); } diff --git a/sys/gnu/ext2fs/ext2_linux_ialloc.c b/sys/gnu/ext2fs/ext2_linux_ialloc.c index 9102d02b2d96..d519f6001ae7 100644 --- a/sys/gnu/ext2fs/ext2_linux_ialloc.c +++ b/sys/gnu/ext2fs/ext2_linux_ialloc.c @@ -8,8 +8,15 @@ * * BSD ufs-inspired inode and directory allocation by * Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 + * Big-endian to little-endian byte-swapping/bitmaps by + * David S. Miller (davem@caip.rutgers.edu), 1995 */ +#include <linux/fs.h> +#include <linux/locks.h> +#include <linux/quotaops.h> + + /* * ialloc.c contains the inodes allocation and deallocation routines */ @@ -25,59 +32,42 @@ * when a file system is mounted (see ext2_read_super). */ -#include <linux/fs.h> -#include <linux/ext2_fs.h> -#include <linux/sched.h> -#include <linux/stat.h> -#include <linux/string.h> -#include <linux/locks.h> - -#include <asm/bitops.h> - -static struct ext2_group_desc * get_group_desc (struct super_block * sb, - unsigned int block_group, - struct buffer_head ** bh) -{ - unsigned long group_desc; - unsigned long desc; - struct ext2_group_desc * gdp; - - if (block_group >= sb->u.ext2_sb.s_groups_count) - ext2_panic (sb, "get_group_desc", - "block_group >= groups_count - " - "block_group = %d, groups_count = %lu", - block_group, sb->u.ext2_sb.s_groups_count); - - group_desc = block_group / EXT2_DESC_PER_BLOCK(sb); - desc = block_group % EXT2_DESC_PER_BLOCK(sb); - if (!sb->u.ext2_sb.s_group_desc[group_desc]) - ext2_panic (sb, "get_group_desc", - "Group descriptor not loaded - " - "block_group = %d, group_desc = %lu, desc = %lu", - block_group, group_desc, desc); - gdp = (struct ext2_group_desc *) - sb->u.ext2_sb.s_group_desc[group_desc]->b_data; - if (bh) - *bh = sb->u.ext2_sb.s_group_desc[group_desc]; - return gdp + desc; -} -static void read_inode_bitmap (struct super_block * sb, +/* + * Read the inode allocation bitmap for a given block_group, reading + * into the specified slot in the superblock's bitmap cache. + * + * Return >=0 on success or a -ve error code. + */ +static int read_inode_bitmap (struct super_block * sb, unsigned long block_group, unsigned int bitmap_nr) { struct ext2_group_desc * gdp; - struct buffer_head * bh; + struct buffer_head * bh = NULL; + int retval = 0; - gdp = get_group_desc (sb, block_group, NULL); - bh = bread (sb->s_dev, gdp->bg_inode_bitmap, sb->s_blocksize); - if (!bh) - ext2_panic (sb, "read_inode_bitmap", + gdp = ext2_get_group_desc (sb, block_group, NULL); + if (!gdp) { + retval = -EIO; + goto error_out; + } + bh = bread (sb->s_dev, le32_to_cpu(gdp->bg_inode_bitmap), sb->s_blocksize); + if (!bh) { + ext2_error (sb, "read_inode_bitmap", "Cannot read inode bitmap - " "block_group = %lu, inode_bitmap = %lu", block_group, (unsigned long) gdp->bg_inode_bitmap); + retval = -EIO; + } + /* + * On IO error, just leave a zero in the superblock's block pointer for + * this group. The IO will be retried next time. + */ +error_out: sb->u.ext2_sb.s_inode_bitmap_number[bitmap_nr] = block_group; sb->u.ext2_sb.s_inode_bitmap[bitmap_nr] = bh; + return retval; } /* @@ -90,11 +80,13 @@ static void read_inode_bitmap (struct super_block * sb, * 1/ There is one cache per mounted file system. * 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups, * this function reads the bitmap without maintaining a LRU cache. + * + * Return the slot used to store the bitmap, or a -ve error code. */ static int load_inode_bitmap (struct super_block * sb, unsigned int block_group) { - int i, j; + int i, j, retval = 0; unsigned long inode_bitmap_number; struct buffer_head * inode_bitmap; @@ -104,7 +96,8 @@ static int load_inode_bitmap (struct super_block * sb, "block_group = %d, groups_count = %lu", block_group, sb->u.ext2_sb.s_groups_count); if (sb->u.ext2_sb.s_loaded_inode_bitmaps > 0 && - sb->u.ext2_sb.s_inode_bitmap_number[0] == block_group) + sb->u.ext2_sb.s_inode_bitmap_number[0] == block_group && + sb->u.ext2_sb.s_inode_bitmap[0] != NULL) return 0; if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) { if (sb->u.ext2_sb.s_inode_bitmap[block_group]) { @@ -114,7 +107,10 @@ static int load_inode_bitmap (struct super_block * sb, else return block_group; } else { - read_inode_bitmap (sb, block_group, block_group); + retval = read_inode_bitmap (sb, block_group, + block_group); + if (retval < 0) + return retval; return block_group; } } @@ -135,6 +131,15 @@ static int load_inode_bitmap (struct super_block * sb, } sb->u.ext2_sb.s_inode_bitmap_number[0] = inode_bitmap_number; sb->u.ext2_sb.s_inode_bitmap[0] = inode_bitmap; + + /* + * There's still one special case here --- if inode_bitmap == 0 + * then our last attempt to read the bitmap failed and we have + * just ended up caching that failure. Try again to read it. + */ + if (!inode_bitmap) + retval = read_inode_bitmap (sb, block_group, 0); + } else { if (sb->u.ext2_sb.s_loaded_inode_bitmaps < EXT2_MAX_GROUP_LOADED) sb->u.ext2_sb.s_loaded_inode_bitmaps++; @@ -146,49 +151,32 @@ static int load_inode_bitmap (struct super_block * sb, sb->u.ext2_sb.s_inode_bitmap[j] = sb->u.ext2_sb.s_inode_bitmap[j - 1]; } - read_inode_bitmap (sb, block_group, 0); + retval = read_inode_bitmap (sb, block_group, 0); } - return 0; + return retval; } /* - * This function sets the deletion time for the inode + * NOTE! When we get the inode, we're the only people + * that have access to it, and as such there are no + * race conditions we have to worry about. The inode + * is not on the hash-lists, and it cannot be reached + * through the filesystem because the directory entry + * has been deleted earlier. * - * This may be used one day by an 'undelete' program + * HOWEVER: we must make sure that we get no aliases, + * which means that we have to call "clear_inode()" + * _before_ we mark the inode not in use in the inode + * bitmaps. Otherwise a newly created file might use + * the same inode number (not actually the same pointer + * though), and then we'd have two inodes sharing the + * same inode number and space on the harddisk. */ -static void set_inode_dtime (struct inode * inode, - struct ext2_group_desc * gdp) -{ - unsigned long inode_block; - struct buffer_head * bh; - struct ext2_inode * raw_inode; - - inode_block = gdp->bg_inode_table + (((inode->i_ino - 1) % - EXT2_INODES_PER_GROUP(inode->i_sb)) / - EXT2_INODES_PER_BLOCK(inode->i_sb)); - bh = bread (inode->i_sb->s_dev, inode_block, inode->i_sb->s_blocksize); - if (!bh) - ext2_panic (inode->i_sb, "set_inode_dtime", - "Cannot load inode table block - " - "inode=%lu, inode_block=%lu", - inode->i_ino, inode_block); - raw_inode = ((struct ext2_inode *) bh->b_data) + - (((inode->i_ino - 1) % - EXT2_INODES_PER_GROUP(inode->i_sb)) % - EXT2_INODES_PER_BLOCK(inode->i_sb)); - raw_inode->i_links_count = 0; - raw_inode->i_dtime = CURRENT_TIME; - mark_buffer_dirty(bh, 1); - if (IS_SYNC(inode)) { - ll_rw_block (WRITE, 1, &bh); - wait_on_buffer (bh); - } - brelse (bh); -} - void ext2_free_inode (struct inode * inode) { - struct super_block * sb; + struct super_block * sb = inode->i_sb; + int is_directory; + unsigned long ino; struct buffer_head * bh; struct buffer_head * bh2; unsigned long block_group; @@ -197,15 +185,12 @@ void ext2_free_inode (struct inode * inode) struct ext2_group_desc * gdp; struct ext2_super_block * es; - if (!inode) - return; if (!inode->i_dev) { printk ("ext2_free_inode: inode has no device\n"); return; } if (inode->i_count > 1) { - printk ("ext2_free_inode: inode has count=%d\n", - inode->i_count); + printk ("ext2_free_inode: inode has count=%d\n", inode->i_count); return; } if (inode->i_nlink) { @@ -213,87 +198,71 @@ void ext2_free_inode (struct inode * inode) inode->i_nlink); return; } - if (!inode->i_sb) { + if (!sb) { printk("ext2_free_inode: inode on nonexistent device\n"); return; } - ext2_debug ("freeing inode %lu\n", inode->i_ino); + ino = inode->i_ino; + ext2_debug ("freeing inode %lu\n", ino); + + /* + * Note: we must free any quota before locking the superblock, + * as writing the quota to disk may need the lock as well. + */ + DQUOT_FREE_INODE(sb, inode); + DQUOT_DROP(inode); - sb = inode->i_sb; lock_super (sb); - if (inode->i_ino < EXT2_FIRST_INO || - inode->i_ino > sb->u.ext2_sb.s_es->s_inodes_count) { + es = sb->u.ext2_sb.s_es; + if (ino < EXT2_FIRST_INO(sb) || + ino > le32_to_cpu(es->s_inodes_count)) { ext2_error (sb, "free_inode", "reserved inode or nonexistent inode"); - unlock_super (sb); - return; + goto error_return; } - es = sb->u.ext2_sb.s_es; - block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(sb); - bit = (inode->i_ino - 1) % EXT2_INODES_PER_GROUP(sb); + block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb); + bit = (ino - 1) % EXT2_INODES_PER_GROUP(sb); bitmap_nr = load_inode_bitmap (sb, block_group); + if (bitmap_nr < 0) + goto error_return; + bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr]; - if (!clear_bit (bit, bh->b_data)) + + is_directory = S_ISDIR(inode->i_mode); + + /* Do this BEFORE marking the inode not in use */ + clear_inode (inode); + + /* Ok, now we can actually update the inode bitmaps.. */ + if (!ext2_clear_bit (bit, bh->b_data)) ext2_warning (sb, "ext2_free_inode", - "bit already cleared for inode %lu", inode->i_ino); + "bit already cleared for inode %lu", ino); else { - gdp = get_group_desc (sb, block_group, &bh2); - gdp->bg_free_inodes_count++; - if (S_ISDIR(inode->i_mode)) - gdp->bg_used_dirs_count--; + gdp = ext2_get_group_desc (sb, block_group, &bh2); + if (gdp) { + gdp->bg_free_inodes_count = + cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) + 1); + if (is_directory) + gdp->bg_used_dirs_count = + cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) - 1); + } mark_buffer_dirty(bh2, 1); - es->s_free_inodes_count++; + es->s_free_inodes_count = + cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) + 1); mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); - set_inode_dtime (inode, gdp); } mark_buffer_dirty(bh, 1); if (sb->s_flags & MS_SYNCHRONOUS) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } - sb->s_dirt = 1; - clear_inode (inode); +error_return: unlock_super (sb); } /* - * This function increments the inode version number - * - * This may be used one day by the NFS server - */ -static void inc_inode_version (struct inode * inode, - struct ext2_group_desc *gdp, - int mode) -{ - unsigned long inode_block; - struct buffer_head * bh; - struct ext2_inode * raw_inode; - - inode_block = gdp->bg_inode_table + (((inode->i_ino - 1) % - EXT2_INODES_PER_GROUP(inode->i_sb)) / - EXT2_INODES_PER_BLOCK(inode->i_sb)); - bh = bread (inode->i_sb->s_dev, inode_block, inode->i_sb->s_blocksize); - if (!bh) { - ext2_error (inode->i_sb, "inc_inode_version", - "Cannot load inode table block - " - "inode=%lu, inode_block=%lu\n", - inode->i_ino, inode_block); - inode->u.ext2_i.i_version = 1; - return; - } - raw_inode = ((struct ext2_inode *) bh->b_data) + - (((inode->i_ino - 1) % - EXT2_INODES_PER_GROUP(inode->i_sb)) % - EXT2_INODES_PER_BLOCK(inode->i_sb)); - raw_inode->i_version++; - inode->u.ext2_i.i_version = raw_inode->i_version; - mark_buffer_dirty(bh, 1); - brelse (bh); -} - -/* * There are two policies for allocating an inode. If the new inode is * a directory, then a forward search is made for a block group with both * free space and a low directory-to-inode ratio; if that fails, then of @@ -303,7 +272,7 @@ static void inc_inode_version (struct inode * inode, * For other inodes, search forward from the parent directory\'s block * group to find a free inode. */ -struct inode * ext2_new_inode (const struct inode * dir, int mode) +struct inode * ext2_new_inode (const struct inode * dir, int mode, int * err) { struct super_block * sb; struct buffer_head * bh; @@ -315,25 +284,37 @@ struct inode * ext2_new_inode (const struct inode * dir, int mode) struct ext2_group_desc * tmp; struct ext2_super_block * es; - if (!dir || !(inode = get_empty_inode ())) + /* Cannot create files in a deleted directory */ + if (!dir || !dir->i_nlink) { + *err = -EPERM; + return NULL; + } + + inode = get_empty_inode (); + if (!inode) { + *err = -ENOMEM; return NULL; + } + sb = dir->i_sb; inode->i_sb = sb; - inode->i_flags = sb->s_flags; + inode->i_flags = 0; lock_super (sb); es = sb->u.ext2_sb.s_es; repeat: gdp = NULL; i=0; + *err = -ENOSPC; if (S_ISDIR(mode)) { - avefreei = es->s_free_inodes_count / + avefreei = le32_to_cpu(es->s_free_inodes_count) / sb->u.ext2_sb.s_groups_count; /* I am not yet convinced that this next bit is necessary. i = dir->u.ext2_i.i_block_group; for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) { - tmp = get_group_desc (sb, i, &bh2); - if ((tmp->bg_used_dirs_count << 8) < - tmp->bg_free_inodes_count) { + tmp = ext2_get_group_desc (sb, i, &bh2); + if (tmp && + (le16_to_cpu(tmp->bg_used_dirs_count) << 8) < + le16_to_cpu(tmp->bg_free_inodes_count)) { gdp = tmp; break; } @@ -343,12 +324,13 @@ repeat: */ if (!gdp) { for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) { - tmp = get_group_desc (sb, j, &bh2); - if (tmp->bg_free_inodes_count && - tmp->bg_free_inodes_count >= avefreei) { + tmp = ext2_get_group_desc (sb, j, &bh2); + if (tmp && + le16_to_cpu(tmp->bg_free_inodes_count) && + le16_to_cpu(tmp->bg_free_inodes_count) >= avefreei) { if (!gdp || - (tmp->bg_free_blocks_count > - gdp->bg_free_blocks_count)) { + (le16_to_cpu(tmp->bg_free_blocks_count) > + le16_to_cpu(gdp->bg_free_blocks_count))) { i = j; gdp = tmp; } @@ -362,8 +344,8 @@ repeat: * Try to place the inode in its parent directory */ i = dir->u.ext2_i.i_block_group; - tmp = get_group_desc (sb, i, &bh2); - if (tmp->bg_free_inodes_count) + tmp = ext2_get_group_desc (sb, i, &bh2); + if (tmp && le16_to_cpu(tmp->bg_free_inodes_count)) gdp = tmp; else { @@ -375,8 +357,9 @@ repeat: i += j; if (i >= sb->u.ext2_sb.s_groups_count) i -= sb->u.ext2_sb.s_groups_count; - tmp = get_group_desc (sb, i, &bh2); - if (tmp->bg_free_inodes_count) { + tmp = ext2_get_group_desc (sb, i, &bh2); + if (tmp && + le16_to_cpu(tmp->bg_free_inodes_count)) { gdp = tmp; break; } @@ -390,8 +373,9 @@ repeat: for (j = 2; j < sb->u.ext2_sb.s_groups_count; j++) { if (++i >= sb->u.ext2_sb.s_groups_count) i = 0; - tmp = get_group_desc (sb, i, &bh2); - if (tmp->bg_free_inodes_count) { + tmp = ext2_get_group_desc (sb, i, &bh2); + if (tmp && + le16_to_cpu(tmp->bg_free_inodes_count)) { gdp = tmp; break; } @@ -405,11 +389,18 @@ repeat: return NULL; } bitmap_nr = load_inode_bitmap (sb, i); + if (bitmap_nr < 0) { + unlock_super (sb); + iput(inode); + *err = -EIO; + return NULL; + } + bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr]; - if ((j = find_first_zero_bit ((unsigned long *) bh->b_data, + if ((j = ext2_find_first_zero_bit ((unsigned long *) bh->b_data, EXT2_INODES_PER_GROUP(sb))) < EXT2_INODES_PER_GROUP(sb)) { - if (set_bit (j, bh->b_data)) { + if (ext2_set_bit (j, bh->b_data)) { ext2_warning (sb, "ext2_new_inode", "bit already set for inode %d", j); goto repeat; @@ -420,7 +411,7 @@ repeat: wait_on_buffer (bh); } } else { - if (gdp->bg_free_inodes_count != 0) { + if (le16_to_cpu(gdp->bg_free_inodes_count) != 0) { ext2_error (sb, "ext2_new_inode", "Free inodes count corrupted in group %d", i); @@ -431,7 +422,7 @@ repeat: goto repeat; } j += i * EXT2_INODES_PER_GROUP(sb) + 1; - if (j < EXT2_FIRST_INO || j > es->s_inodes_count) { + if (j < EXT2_FIRST_INO(sb) || j > le32_to_cpu(es->s_inodes_count)) { ext2_error (sb, "ext2_new_inode", "reserved inode or inode > inodes count - " "block_group = %d,inode=%d", i, j); @@ -439,16 +430,18 @@ repeat: iput (inode); return NULL; } - gdp->bg_free_inodes_count--; + gdp->bg_free_inodes_count = + cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) - 1); if (S_ISDIR(mode)) - gdp->bg_used_dirs_count++; + gdp->bg_used_dirs_count = + cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) + 1); mark_buffer_dirty(bh2, 1); - es->s_free_inodes_count--; + es->s_free_inodes_count = + cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) - 1); mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); sb->s_dirt = 1; inode->i_mode = mode; inode->i_sb = sb; - inode->i_count = 1; inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; @@ -460,11 +453,12 @@ repeat: mode |= S_ISGID; } else inode->i_gid = current->fsgid; - inode->i_dirt = 1; + inode->i_ino = j; - inode->i_blksize = sb->s_blocksize; + inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */ inode->i_blocks = 0; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->u.ext2_i.i_new_inode = 1; inode->u.ext2_i.i_flags = dir->u.ext2_i.i_flags; if (S_ISLNK(mode)) inode->u.ext2_i.i_flags &= ~(EXT2_IMMUTABLE_FL | EXT2_APPEND_FL); @@ -479,11 +473,20 @@ repeat: if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) inode->i_flags |= MS_SYNCHRONOUS; insert_inode_hash(inode); - inc_inode_version (inode, gdp, mode); + inode->i_generation = event++; + mark_inode_dirty(inode); + unlock_super (sb); + if(DQUOT_ALLOC_INODE(sb, inode)) { + sb->dq_op->drop(inode); + inode->i_nlink = 0; + iput(inode); + *err = -EDQUOT; + return NULL; + } ext2_debug ("allocating inode %lu\n", inode->i_ino); - unlock_super (sb); + *err = 0; return inode; } @@ -502,21 +505,26 @@ unsigned long ext2_count_free_inodes (struct super_block * sb) bitmap_count = 0; gdp = NULL; for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) { - gdp = get_group_desc (sb, i, NULL); - desc_count += gdp->bg_free_inodes_count; + gdp = ext2_get_group_desc (sb, i, NULL); + if (!gdp) + continue; + desc_count += le16_to_cpu(gdp->bg_free_inodes_count); bitmap_nr = load_inode_bitmap (sb, i); + if (bitmap_nr < 0) + continue; + x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr], EXT2_INODES_PER_GROUP(sb) / 8); printk ("group %d: stored = %d, counted = %lu\n", - i, gdp->bg_free_inodes_count, x); + i, le16_to_cpu(gdp->bg_free_inodes_count), x); bitmap_count += x; } printk("ext2_count_free_inodes: stored = %lu, computed = %lu, %lu\n", - es->s_free_inodes_count, desc_count, bitmap_count); + le32_to_cpu(es->s_free_inodes_count), desc_count, bitmap_count); unlock_super (sb); return desc_count; #else - return sb->u.ext2_sb.s_es->s_free_inodes_count; + return le32_to_cpu(sb->u.ext2_sb.s_es->s_free_inodes_count); #endif } @@ -534,22 +542,28 @@ void ext2_check_inodes_bitmap (struct super_block * sb) bitmap_count = 0; gdp = NULL; for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) { - gdp = get_group_desc (sb, i, NULL); - desc_count += gdp->bg_free_inodes_count; + gdp = ext2_get_group_desc (sb, i, NULL); + if (!gdp) + continue; + desc_count += le16_to_cpu(gdp->bg_free_inodes_count); bitmap_nr = load_inode_bitmap (sb, i); + if (bitmap_nr < 0) + continue; + x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr], EXT2_INODES_PER_GROUP(sb) / 8); - if (gdp->bg_free_inodes_count != x) + if (le16_to_cpu(gdp->bg_free_inodes_count) != x) ext2_error (sb, "ext2_check_inodes_bitmap", "Wrong free inodes count in group %d, " "stored = %d, counted = %lu", i, - gdp->bg_free_inodes_count, x); + le16_to_cpu(gdp->bg_free_inodes_count), x); bitmap_count += x; } - if (es->s_free_inodes_count != bitmap_count) + if (le32_to_cpu(es->s_free_inodes_count) != bitmap_count) ext2_error (sb, "ext2_check_inodes_bitmap", "Wrong free inodes count in super block, " "stored = %lu, counted = %lu", - (unsigned long) es->s_free_inodes_count, bitmap_count); + (unsigned long) le32_to_cpu(es->s_free_inodes_count), + bitmap_count); unlock_super (sb); } diff --git a/sys/gnu/ext2fs/ext2_super.c b/sys/gnu/ext2fs/ext2_super.c index b73cc521cb37..11f99b4dc291 100644 --- a/sys/gnu/ext2fs/ext2_super.c +++ b/sys/gnu/ext2fs/ext2_super.c @@ -11,21 +11,20 @@ * linux/fs/minix/inode.c * * Copyright (C) 1991, 1992 Linus Torvalds + * + * Big-endian to little-endian byte-swapping/bitmaps by + * David S. Miller (davem@caip.rutgers.edu), 1995 */ -#include <stdarg.h> - -#include <asm/segment.h> -#include <asm/system.h> - -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/ext2_fs.h> -#include <linux/malloc.h> -#include <linux/sched.h> -#include <linux/stat.h> +#include <linux/module.h> #include <linux/string.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/init.h> #include <linux/locks.h> +#include <asm/uaccess.h> + + static char error_buf[1024]; @@ -36,7 +35,8 @@ void ext2_error (struct super_block * sb, const char * function, if (!(sb->s_flags & MS_RDONLY)) { sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS; - sb->u.ext2_sb.s_es->s_state |= EXT2_ERROR_FS; + sb->u.ext2_sb.s_es->s_state = + cpu_to_le16(le16_to_cpu(sb->u.ext2_sb.s_es->s_state) | EXT2_ERROR_FS); mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); sb->s_dirt = 1; } @@ -44,14 +44,14 @@ void ext2_error (struct super_block * sb, const char * function, vsprintf (error_buf, fmt, args); va_end (args); if (test_opt (sb, ERRORS_PANIC) || - (sb->u.ext2_sb.s_es->s_errors == EXT2_ERRORS_PANIC && + (le16_to_cpu(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_PANIC && !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_RO))) - panic ("EXT2-fs panic (device %d/%d): %s: %s\n", - MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf); - printk (KERN_CRIT "EXT2-fs error (device %d/%d): %s: %s\n", - MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf); + panic ("EXT2-fs panic (device %s): %s: %s\n", + bdevname(sb->s_dev), function, error_buf); + printk (KERN_CRIT "EXT2-fs error (device %s): %s: %s\n", + bdevname(sb->s_dev), function, error_buf); if (test_opt (sb, ERRORS_RO) || - (sb->u.ext2_sb.s_es->s_errors == EXT2_ERRORS_RO && + (le16_to_cpu(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_RO && !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_PANIC))) { printk ("Remounting filesystem read-only\n"); sb->s_flags |= MS_RDONLY; @@ -65,15 +65,20 @@ NORET_TYPE void ext2_panic (struct super_block * sb, const char * function, if (!(sb->s_flags & MS_RDONLY)) { sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS; - sb->u.ext2_sb.s_es->s_state |= EXT2_ERROR_FS; + sb->u.ext2_sb.s_es->s_state = + cpu_to_le16(le16_to_cpu(sb->u.ext2_sb.s_es->s_state) | EXT2_ERROR_FS); mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); sb->s_dirt = 1; } va_start (args, fmt); vsprintf (error_buf, fmt, args); va_end (args); - panic ("EXT2-fs panic (device %d/%d): %s: %s\n", - MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf); + /* this is to prevent panic from syncing this filesystem */ + if (sb->s_lock) + sb->s_lock=0; + sb->s_flags |= MS_RDONLY; + panic ("EXT2-fs panic (device %s): %s: %s\n", + bdevname(sb->s_dev), function, error_buf); } void ext2_warning (struct super_block * sb, const char * function, @@ -84,8 +89,8 @@ void ext2_warning (struct super_block * sb, const char * function, va_start (args, fmt); vsprintf (error_buf, fmt, args); va_end (args); - printk (KERN_WARNING "EXT2-fs warning (device %d/%d): %s: %s\n", - MAJOR(sb->s_dev), MINOR(sb->s_dev), function, error_buf); + printk (KERN_WARNING "EXT2-fs warning (device %s): %s: %s\n", + bdevname(sb->s_dev), function, error_buf); } void ext2_put_super (struct super_block * sb) @@ -93,12 +98,10 @@ void ext2_put_super (struct super_block * sb) int db_count; int i; - lock_super (sb); if (!(sb->s_flags & MS_RDONLY)) { - sb->u.ext2_sb.s_es->s_state = sb->u.ext2_sb.s_mount_state; + sb->u.ext2_sb.s_es->s_state = le16_to_cpu(sb->u.ext2_sb.s_mount_state); mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); } - sb->s_dev = 0; db_count = sb->u.ext2_sb.s_db_per_group; for (i = 0; i < db_count; i++) if (sb->u.ext2_sb.s_group_desc[i]) @@ -112,63 +115,23 @@ void ext2_put_super (struct super_block * sb) if (sb->u.ext2_sb.s_block_bitmap[i]) brelse (sb->u.ext2_sb.s_block_bitmap[i]); brelse (sb->u.ext2_sb.s_sbh); - unlock_super (sb); + + MOD_DEC_USE_COUNT; return; } -static struct super_operations ext2_sops = { +static struct super_operations ext2_sops = { ext2_read_inode, - NULL, ext2_write_inode, ext2_put_inode, + ext2_delete_inode, + NULL, ext2_put_super, ext2_write_super, ext2_statfs, ext2_remount }; -#ifdef EXT2FS_PRE_02B_COMPAT - -static int convert_pre_02b_fs (struct super_block * sb, - struct buffer_head * bh) -{ - struct ext2_super_block * es; - struct ext2_old_group_desc old_group_desc [BLOCK_SIZE / sizeof (struct ext2_old_group_desc)]; - struct ext2_group_desc * gdp; - struct buffer_head * bh2; - int groups_count; - int i; - - es = (struct ext2_super_block *) bh->b_data; - bh2 = bread (sb->s_dev, 2, BLOCK_SIZE); - if (!bh2) { - printk ("Cannot read descriptor blocks while converting !\n"); - return 0; - } - memcpy (old_group_desc, bh2->b_data, BLOCK_SIZE); - groups_count = (sb->u.ext2_sb.s_blocks_count - - sb->u.ext2_sb.s_first_data_block + - (EXT2_BLOCK_SIZE(sb) * 8) - 1) / - (EXT2_BLOCK_SIZE(sb) * 8); - memset (bh2->b_data, 0, BLOCK_SIZE); - gdp = (struct ext2_group_desc *) bh2->b_data; - for (i = 0; i < groups_count; i++) { - gdp[i].bg_block_bitmap = old_group_desc[i].bg_block_bitmap; - gdp[i].bg_inode_bitmap = old_group_desc[i].bg_inode_bitmap; - gdp[i].bg_inode_table = old_group_desc[i].bg_inode_table; - gdp[i].bg_free_blocks_count = old_group_desc[i].bg_free_blocks_count; - gdp[i].bg_free_inodes_count = old_group_desc[i].bg_free_inodes_count; - } - mark_buffer_dirty(bh2, 1); - brelse (bh2); - es->s_magic = EXT2_SUPER_MAGIC; - mark_buffer_dirty(bh, 1); - sb->s_magic = EXT2_SUPER_MAGIC; - return 1; -} - -#endif - /* * This function has been shamelessly adapted from the msdos fs */ @@ -195,9 +158,9 @@ static int parse_options (char * options, unsigned long * sb_block, clear_opt (*mount_options, CHECK_NORMAL); clear_opt (*mount_options, CHECK_STRICT); } - else if (strcmp (value, "normal")) + else if (!strcmp (value, "normal")) set_opt (*mount_options, CHECK_NORMAL); - else if (strcmp (value, "strict")) { + else if (!strcmp (value, "strict")) { set_opt (*mount_options, CHECK_NORMAL); set_opt (*mount_options, CHECK_STRICT); } @@ -287,6 +250,12 @@ static int parse_options (char * options, unsigned long * sb_block, return 0; } } + /* Silently ignore the quota options */ + else if (!strcmp (this_char, "grpquota") + || !strcmp (this_char, "noquota") + || !strcmp (this_char, "quota") + || !strcmp (this_char, "usrquota")) + /* Don't do anything ;-) */ ; else { printk ("EXT2-fs: Unrecognized mount option %s\n", this_char); return 0; @@ -298,7 +267,7 @@ static int parse_options (char * options, unsigned long * sb_block, static void ext2_setup_super (struct super_block * sb, struct ext2_super_block * es) { - if (es->s_rev_level > EXT2_CURRENT_REV) { + if (le32_to_cpu(es->s_rev_level) > EXT2_MAX_SUPP_REV) { printk ("EXT2-fs warning: revision level too high, " "forcing read/only mode\n"); sb->s_flags |= MS_RDONLY; @@ -310,19 +279,20 @@ static void ext2_setup_super (struct super_block * sb, else if ((sb->u.ext2_sb.s_mount_state & EXT2_ERROR_FS)) printk ("EXT2-fs warning: mounting fs with errors, " "running e2fsck is recommended\n"); - else if (es->s_max_mnt_count >= 0 && - es->s_mnt_count >= (unsigned short) es->s_max_mnt_count) + else if ((__s16) le16_to_cpu(es->s_max_mnt_count) >= 0 && + le16_to_cpu(es->s_mnt_count) >= + (unsigned short) (__s16) le16_to_cpu(es->s_max_mnt_count)) printk ("EXT2-fs warning: maximal mount count reached, " "running e2fsck is recommended\n"); - else if (es->s_checkinterval && - (es->s_lastcheck + es->s_checkinterval <= CURRENT_TIME)) + else if (le32_to_cpu(es->s_checkinterval) && + (le32_to_cpu(es->s_lastcheck) + le32_to_cpu(es->s_checkinterval) <= CURRENT_TIME)) printk ("EXT2-fs warning: checktime reached, " "running e2fsck is recommended\n"); - es->s_state &= ~EXT2_VALID_FS; - if (!es->s_max_mnt_count) - es->s_max_mnt_count = EXT2_DFL_MAX_MNT_COUNT; - es->s_mnt_count++; - es->s_mtime = CURRENT_TIME; + es->s_state = cpu_to_le16(le16_to_cpu(es->s_state) & ~EXT2_VALID_FS); + if (!(__s16) le16_to_cpu(es->s_max_mnt_count)) + es->s_max_mnt_count = (__s16) cpu_to_le16(EXT2_DFL_MAX_MNT_COUNT); + es->s_mnt_count=cpu_to_le16(le16_to_cpu(es->s_mnt_count) + 1); + es->s_mtime = cpu_to_le32(CURRENT_TIME); mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); sb->s_dirt = 1; if (test_opt (sb, DEBUG)) @@ -339,13 +309,20 @@ static void ext2_setup_super (struct super_block * sb, ext2_check_inodes_bitmap (sb); } } +#if 0 /* ibasket's still have unresolved bugs... -DaveM */ + + /* [T. Schoebel-Theuer] This limit should be maintained on disk. + * This is just provisionary. + */ + sb->s_ibasket_max = 100; +#endif } static int ext2_check_descriptors (struct super_block * sb) { int i; int desc_block = 0; - unsigned long block = sb->u.ext2_sb.s_es->s_first_data_block; + unsigned long block = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block); struct ext2_group_desc * gdp = NULL; ext2_debug ("Checking group descriptors"); @@ -354,32 +331,32 @@ static int ext2_check_descriptors (struct super_block * sb) { if ((i % EXT2_DESC_PER_BLOCK(sb)) == 0) gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[desc_block++]->b_data; - if (gdp->bg_block_bitmap < block || - gdp->bg_block_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb)) + if (le32_to_cpu(gdp->bg_block_bitmap) < block || + le32_to_cpu(gdp->bg_block_bitmap) >= block + EXT2_BLOCKS_PER_GROUP(sb)) { ext2_error (sb, "ext2_check_descriptors", "Block bitmap for group %d" " not in group (block %lu)!", - i, (unsigned long) gdp->bg_block_bitmap); + i, (unsigned long) le32_to_cpu(gdp->bg_block_bitmap)); return 0; } - if (gdp->bg_inode_bitmap < block || - gdp->bg_inode_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb)) + if (le32_to_cpu(gdp->bg_inode_bitmap) < block || + le32_to_cpu(gdp->bg_inode_bitmap) >= block + EXT2_BLOCKS_PER_GROUP(sb)) { ext2_error (sb, "ext2_check_descriptors", "Inode bitmap for group %d" " not in group (block %lu)!", - i, (unsigned long) gdp->bg_inode_bitmap); + i, (unsigned long) le32_to_cpu(gdp->bg_inode_bitmap)); return 0; } - if (gdp->bg_inode_table < block || - gdp->bg_inode_table + sb->u.ext2_sb.s_itb_per_group >= + if (le32_to_cpu(gdp->bg_inode_table) < block || + le32_to_cpu(gdp->bg_inode_table) + sb->u.ext2_sb.s_itb_per_group >= block + EXT2_BLOCKS_PER_GROUP(sb)) { ext2_error (sb, "ext2_check_descriptors", "Inode table for group %d" " not in group (block %lu)!", - i, (unsigned long) gdp->bg_inode_table); + i, (unsigned long) le32_to_cpu(gdp->bg_inode_table)); return 0; } block += EXT2_BLOCKS_PER_GROUP(sb); @@ -388,6 +365,8 @@ static int ext2_check_descriptors (struct super_block * sb) return 1; } +#define log2(n) ffz(~(n)) + struct super_block * ext2_read_super (struct super_block * sb, void * data, int silent) { @@ -397,13 +376,27 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, unsigned short resuid = EXT2_DEF_RESUID; unsigned short resgid = EXT2_DEF_RESGID; unsigned long logic_sb_block = 1; - int dev = sb->s_dev; + unsigned long offset = 0; + kdev_t dev = sb->s_dev; + int blocksize = BLOCK_SIZE; + int hblock; int db_count; int i, j; -#ifdef EXT2FS_PRE_02B_COMPAT - int fs_converted = 0; -#endif + /* + * See what the current blocksize for the device is, and + * use that as the blocksize. Otherwise (or if the blocksize + * is smaller than the default) use the default. + * This is important for devices that have a hardware + * sectorsize that is larger than the default. + */ + blocksize = get_hardblocksize(dev); + if( blocksize == 0 || blocksize < BLOCK_SIZE ) + { + blocksize = BLOCK_SIZE; + } + + sb->u.ext2_sb.s_mount_opt = 0; set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL); if (!parse_options ((char *) data, &sb_block, &resuid, &resgid, &sb->u.ext2_sb.s_mount_opt)) { @@ -411,212 +404,206 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, return NULL; } + MOD_INC_USE_COUNT; lock_super (sb); - set_blocksize (dev, BLOCK_SIZE); - if (!(bh = bread (dev, sb_block, BLOCK_SIZE))) { + set_blocksize (dev, blocksize); + + /* + * If the superblock doesn't start on a sector boundary, + * calculate the offset. FIXME(eric) this doesn't make sense + * that we would have to do this. + */ + if (blocksize != BLOCK_SIZE) { + logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize; + offset = (sb_block*BLOCK_SIZE) % blocksize; + } + + if (!(bh = bread (dev, logic_sb_block, blocksize))) { sb->s_dev = 0; unlock_super (sb); printk ("EXT2-fs: unable to read superblock\n"); + MOD_DEC_USE_COUNT; return NULL; } /* * Note: s_es must be initialized s_es as soon as possible because * some ext2 macro-instructions depend on its value */ - es = (struct ext2_super_block *) bh->b_data; + es = (struct ext2_super_block *) (((char *)bh->b_data) + offset); sb->u.ext2_sb.s_es = es; - sb->s_magic = es->s_magic; - if (sb->s_magic != EXT2_SUPER_MAGIC -#ifdef EXT2FS_PRE_02B_COMPAT - && sb->s_magic != EXT2_PRE_02B_MAGIC -#endif - ) { + sb->s_magic = le16_to_cpu(es->s_magic); + if (sb->s_magic != EXT2_SUPER_MAGIC) { + if (!silent) + printk ("VFS: Can't find an ext2 filesystem on dev " + "%s.\n", bdevname(dev)); + failed_mount: sb->s_dev = 0; unlock_super (sb); - brelse (bh); - if (!silent) - printk ("VFS: Can't find an ext2 filesystem on dev %d/%d.\n", - MAJOR(dev), MINOR(dev)); + if (bh) + brelse(bh); + MOD_DEC_USE_COUNT; return NULL; } - sb->s_blocksize = EXT2_MIN_BLOCK_SIZE << es->s_log_block_size; - sb->s_blocksize_bits = EXT2_BLOCK_SIZE_BITS(sb); - if (sb->s_blocksize != BLOCK_SIZE && - (sb->s_blocksize == 1024 || sb->s_blocksize == 2048 || + if (le32_to_cpu(es->s_rev_level) > EXT2_GOOD_OLD_REV) { + if (le32_to_cpu(es->s_feature_incompat) & ~EXT2_FEATURE_INCOMPAT_SUPP) { + printk("EXT2-fs: %s: couldn't mount because of " + "unsupported optional features.\n", + bdevname(dev)); + goto failed_mount; + } + if (!(sb->s_flags & MS_RDONLY) && + (le32_to_cpu(es->s_feature_ro_compat) & ~EXT2_FEATURE_RO_COMPAT_SUPP)) { + printk("EXT2-fs: %s: couldn't mount RDWR because of " + "unsupported optional features.\n", + bdevname(dev)); + goto failed_mount; + } + } + sb->s_blocksize_bits = le32_to_cpu(sb->u.ext2_sb.s_es->s_log_block_size) + 10; + sb->s_blocksize = 1 << sb->s_blocksize_bits; + if (sb->s_blocksize != BLOCK_SIZE && + (sb->s_blocksize == 1024 || sb->s_blocksize == 2048 || sb->s_blocksize == 4096)) { - unsigned long offset; + /* + * Make sure the blocksize for the filesystem is larger + * than the hardware sectorsize for the machine. + */ + hblock = get_hardblocksize(dev); + if( (hblock != 0) + && (sb->s_blocksize < hblock) ) + { + printk("EXT2-fs: blocksize too small for device.\n"); + goto failed_mount; + } brelse (bh); set_blocksize (dev, sb->s_blocksize); logic_sb_block = (sb_block*BLOCK_SIZE) / sb->s_blocksize; offset = (sb_block*BLOCK_SIZE) % sb->s_blocksize; bh = bread (dev, logic_sb_block, sb->s_blocksize); - if(!bh) - return NULL; + if(!bh) { + printk("EXT2-fs: Couldn't read superblock on " + "2nd try.\n"); + goto failed_mount; + } es = (struct ext2_super_block *) (((char *)bh->b_data) + offset); sb->u.ext2_sb.s_es = es; - if (es->s_magic != EXT2_SUPER_MAGIC) { - sb->s_dev = 0; - unlock_super (sb); - brelse (bh); + if (es->s_magic != le16_to_cpu(EXT2_SUPER_MAGIC)) { printk ("EXT2-fs: Magic mismatch, very weird !\n"); - return NULL; + goto failed_mount; } } + if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) { + sb->u.ext2_sb.s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; + sb->u.ext2_sb.s_first_ino = EXT2_GOOD_OLD_FIRST_INO; + } else { + sb->u.ext2_sb.s_inode_size = le16_to_cpu(es->s_inode_size); + sb->u.ext2_sb.s_first_ino = le32_to_cpu(es->s_first_ino); + if (sb->u.ext2_sb.s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) { + printk ("EXT2-fs: unsupported inode size: %d\n", + sb->u.ext2_sb.s_inode_size); + goto failed_mount; + } + } + sb->u.ext2_sb.s_feature_compat = le32_to_cpu(es->s_feature_compat); + sb->u.ext2_sb.s_feature_incompat = le32_to_cpu(es->s_feature_incompat); + sb->u.ext2_sb.s_feature_ro_compat = le32_to_cpu(es->s_feature_ro_compat); sb->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE << - es->s_log_frag_size; + le32_to_cpu(es->s_log_frag_size); if (sb->u.ext2_sb.s_frag_size) sb->u.ext2_sb.s_frags_per_block = sb->s_blocksize / sb->u.ext2_sb.s_frag_size; else sb->s_magic = 0; - sb->u.ext2_sb.s_blocks_per_group = es->s_blocks_per_group; - sb->u.ext2_sb.s_frags_per_group = es->s_frags_per_group; - sb->u.ext2_sb.s_inodes_per_group = es->s_inodes_per_group; + sb->u.ext2_sb.s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group); + sb->u.ext2_sb.s_frags_per_group = le32_to_cpu(es->s_frags_per_group); + sb->u.ext2_sb.s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group); sb->u.ext2_sb.s_inodes_per_block = sb->s_blocksize / - sizeof (struct ext2_inode); + EXT2_INODE_SIZE(sb); sb->u.ext2_sb.s_itb_per_group = sb->u.ext2_sb.s_inodes_per_group / sb->u.ext2_sb.s_inodes_per_block; sb->u.ext2_sb.s_desc_per_block = sb->s_blocksize / sizeof (struct ext2_group_desc); sb->u.ext2_sb.s_sbh = bh; - sb->u.ext2_sb.s_es = es; if (resuid != EXT2_DEF_RESUID) sb->u.ext2_sb.s_resuid = resuid; else - sb->u.ext2_sb.s_resuid = es->s_def_resuid; + sb->u.ext2_sb.s_resuid = le16_to_cpu(es->s_def_resuid); if (resgid != EXT2_DEF_RESGID) sb->u.ext2_sb.s_resgid = resgid; else - sb->u.ext2_sb.s_resgid = es->s_def_resgid; - sb->u.ext2_sb.s_mount_state = es->s_state; - sb->u.ext2_sb.s_rename_lock = 0; - sb->u.ext2_sb.s_rename_wait = NULL; -#ifdef EXT2FS_PRE_02B_COMPAT - if (sb->s_magic == EXT2_PRE_02B_MAGIC) { - if (es->s_blocks_count > 262144) { - /* - * fs > 256 MB can't be converted - */ - sb->s_dev = 0; - unlock_super (sb); - brelse (bh); - printk ("EXT2-fs: trying to mount a pre-0.2b file" - "system which cannot be converted\n"); - return NULL; - } - printk ("EXT2-fs: mounting a pre 0.2b file system, " - "will try to convert the structure\n"); - if (!(sb->s_flags & MS_RDONLY)) { - sb->s_dev = 0; - unlock_super (sb); - brelse (bh); - printk ("EXT2-fs: cannot convert a read-only fs\n"); - return NULL; - } - if (!convert_pre_02b_fs (sb, bh)) { - sb->s_dev = 0; - unlock_super (sb); - brelse (bh); - printk ("EXT2-fs: conversion failed !!!\n"); - return NULL; - } - printk ("EXT2-fs: conversion succeeded !!!\n"); - fs_converted = 1; - } -#endif + sb->u.ext2_sb.s_resgid = le16_to_cpu(es->s_def_resgid); + sb->u.ext2_sb.s_mount_state = le16_to_cpu(es->s_state); + sb->u.ext2_sb.s_addr_per_block_bits = + log2 (EXT2_ADDR_PER_BLOCK(sb)); + sb->u.ext2_sb.s_desc_per_block_bits = + log2 (EXT2_DESC_PER_BLOCK(sb)); if (sb->s_magic != EXT2_SUPER_MAGIC) { - sb->s_dev = 0; - unlock_super (sb); - brelse (bh); if (!silent) - printk ("VFS: Can't find an ext2 filesystem on dev %d/%d.\n", - MAJOR(dev), MINOR(dev)); - return NULL; + printk ("VFS: Can't find an ext2 filesystem on dev " + "%s.\n", + bdevname(dev)); + goto failed_mount; } if (sb->s_blocksize != bh->b_size) { - sb->s_dev = 0; - unlock_super (sb); - brelse (bh); if (!silent) - printk ("VFS: Unsupported blocksize on dev 0x%04x.\n", - dev); - return NULL; + printk ("VFS: Unsupported blocksize on dev " + "%s.\n", bdevname(dev)); + goto failed_mount; } if (sb->s_blocksize != sb->u.ext2_sb.s_frag_size) { - sb->s_dev = 0; - unlock_super (sb); - brelse (bh); printk ("EXT2-fs: fragsize %lu != blocksize %lu (not supported yet)\n", sb->u.ext2_sb.s_frag_size, sb->s_blocksize); - return NULL; + goto failed_mount; } if (sb->u.ext2_sb.s_blocks_per_group > sb->s_blocksize * 8) { - sb->s_dev = 0; - unlock_super (sb); - brelse (bh); printk ("EXT2-fs: #blocks per group too big: %lu\n", sb->u.ext2_sb.s_blocks_per_group); - return NULL; + goto failed_mount; } if (sb->u.ext2_sb.s_frags_per_group > sb->s_blocksize * 8) { - sb->s_dev = 0; - unlock_super (sb); - brelse (bh); printk ("EXT2-fs: #fragments per group too big: %lu\n", sb->u.ext2_sb.s_frags_per_group); - return NULL; + goto failed_mount; } if (sb->u.ext2_sb.s_inodes_per_group > sb->s_blocksize * 8) { - sb->s_dev = 0; - unlock_super (sb); - brelse (bh); printk ("EXT2-fs: #inodes per group too big: %lu\n", sb->u.ext2_sb.s_inodes_per_group); - return NULL; + goto failed_mount; } - sb->u.ext2_sb.s_groups_count = (es->s_blocks_count - - es->s_first_data_block + + sb->u.ext2_sb.s_groups_count = (le32_to_cpu(es->s_blocks_count) - + le32_to_cpu(es->s_first_data_block) + EXT2_BLOCKS_PER_GROUP(sb) - 1) / EXT2_BLOCKS_PER_GROUP(sb); db_count = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) / EXT2_DESC_PER_BLOCK(sb); sb->u.ext2_sb.s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL); if (sb->u.ext2_sb.s_group_desc == NULL) { - sb->s_dev = 0; - unlock_super (sb); - brelse (bh); printk ("EXT2-fs: not enough memory\n"); - return NULL; + goto failed_mount; } for (i = 0; i < db_count; i++) { sb->u.ext2_sb.s_group_desc[i] = bread (dev, logic_sb_block + i + 1, sb->s_blocksize); if (!sb->u.ext2_sb.s_group_desc[i]) { - sb->s_dev = 0; - unlock_super (sb); for (j = 0; j < i; j++) brelse (sb->u.ext2_sb.s_group_desc[j]); kfree_s (sb->u.ext2_sb.s_group_desc, db_count * sizeof (struct buffer_head *)); - brelse (bh); printk ("EXT2-fs: unable to read group descriptors\n"); - return NULL; + goto failed_mount; } } if (!ext2_check_descriptors (sb)) { - sb->s_dev = 0; - unlock_super (sb); for (j = 0; j < db_count; j++) brelse (sb->u.ext2_sb.s_group_desc[j]); kfree_s (sb->u.ext2_sb.s_group_desc, db_count * sizeof (struct buffer_head *)); - brelse (bh); printk ("EXT2-fs: group descriptors corrupted !\n"); - return NULL; + goto failed_mount; } for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) { sb->u.ext2_sb.s_inode_bitmap_number[i] = 0; @@ -633,7 +620,8 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, */ sb->s_dev = dev; sb->s_op = &ext2_sops; - if (!(sb->s_mounted = iget (sb, EXT2_ROOT_INO))) { + sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO)); + if (!sb->s_root) { sb->s_dev = 0; for (i = 0; i < db_count; i++) if (sb->u.ext2_sb.s_group_desc[i]) @@ -642,15 +630,9 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, db_count * sizeof (struct buffer_head *)); brelse (bh); printk ("EXT2-fs: get root inode failed\n"); + MOD_DEC_USE_COUNT; return NULL; } -#ifdef EXT2FS_PRE_02B_COMPAT - if (fs_converted) { - for (i = 0; i < db_count; i++) - mark_buffer_dirty(sb->u.ext2_sb.s_group_desc[i], 1); - sb->s_dirt = 1; - } -#endif ext2_setup_super (sb, es); return sb; } @@ -658,7 +640,7 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, static void ext2_commit_super (struct super_block * sb, struct ext2_super_block * es) { - es->s_wtime = CURRENT_TIME; + es->s_wtime = cpu_to_le32(CURRENT_TIME); mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); sb->s_dirt = 0; } @@ -683,9 +665,9 @@ void ext2_write_super (struct super_block * sb) ext2_debug ("setting valid to 0\n"); - if (es->s_state & EXT2_VALID_FS) { - es->s_state &= ~EXT2_VALID_FS; - es->s_mtime = CURRENT_TIME; + if (le16_to_cpu(es->s_state) & EXT2_VALID_FS) { + es->s_state = cpu_to_le16(le16_to_cpu(es->s_state) & ~EXT2_VALID_FS); + es->s_mtime = cpu_to_le32(CURRENT_TIME); } ext2_commit_super (sb, es); } @@ -703,7 +685,7 @@ int ext2_remount (struct super_block * sb, int * flags, char * data) /* * Allow the "check" option to be passed as a remount option. */ - set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL); + new_mount_opt = EXT2_MOUNT_CHECK_NORMAL; if (!parse_options (data, &tmp, &resuid, &resgid, &new_mount_opt)) return -EINVAL; @@ -715,15 +697,15 @@ int ext2_remount (struct super_block * sb, int * flags, char * data) if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) return 0; if (*flags & MS_RDONLY) { - if (es->s_state & EXT2_VALID_FS || + if (le16_to_cpu(es->s_state) & EXT2_VALID_FS || !(sb->u.ext2_sb.s_mount_state & EXT2_VALID_FS)) return 0; /* * OK, we are remounting a valid rw partition rdonly, so set * the rdonly flag and then mark the partition as valid again. */ - es->s_state = sb->u.ext2_sb.s_mount_state; - es->s_mtime = CURRENT_TIME; + es->s_state = cpu_to_le16(sb->u.ext2_sb.s_mount_state); + es->s_mtime = cpu_to_le32(CURRENT_TIME); mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); sb->s_dirt = 1; ext2_commit_super (sb, es); @@ -731,21 +713,21 @@ int ext2_remount (struct super_block * sb, int * flags, char * data) else { /* * Mounting a RDONLY partition read-write, so reread and - * store the current valid flag. (It may have been changed + * store the current valid flag. (It may have been changed * by e2fsck since we originally mounted the partition.) */ - sb->u.ext2_sb.s_mount_state = es->s_state; + sb->u.ext2_sb.s_mount_state = le16_to_cpu(es->s_state); sb->s_flags &= ~MS_RDONLY; ext2_setup_super (sb, es); } return 0; } -void ext2_statfs (struct super_block * sb, struct statfs * buf) +int ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz) { - long tmp; unsigned long overhead; - unsigned long overhead_per_group; + struct statfs tmp; + int ngroups, i; if (test_opt (sb, MINIX_DF)) overhead = 0; @@ -753,28 +735,68 @@ void ext2_statfs (struct super_block * sb, struct statfs * buf) /* * Compute the overhead (FS structures) */ - overhead_per_group = 1 /* super block */ + - sb->u.ext2_sb.s_db_per_group /* descriptors */ + - 1 /* block bitmap */ + - 1 /* inode bitmap */ + - sb->u.ext2_sb.s_itb_per_group /* inode table */; - overhead = sb->u.ext2_sb.s_es->s_first_data_block + - sb->u.ext2_sb.s_groups_count * overhead_per_group; + + /* + * All of the blocks before first_data_block are + * overhead + */ + overhead = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block); + + /* + * Add the overhead attributed to the superblock and + * block group descriptors. If this is sparse + * superblocks is turned on, then not all groups have + * this. + */ + if (sb->u.ext2_sb.s_feature_ro_compat & + EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) { + ngroups = 0; + for (i=0 ; i < sb->u.ext2_sb.s_groups_count; i++) + if (ext2_group_sparse(i)) + ngroups++; + } else + ngroups = sb->u.ext2_sb.s_groups_count; + overhead += ngroups * (1 + sb->u.ext2_sb.s_db_per_group); + + /* + * Every block group has an inode bitmap, a block + * bitmap, and an inode table. + */ + overhead += (sb->u.ext2_sb.s_groups_count * + (2 + sb->u.ext2_sb.s_itb_per_group)); } - put_fs_long (EXT2_SUPER_MAGIC, &buf->f_type); - put_fs_long (sb->s_blocksize, &buf->f_bsize); - put_fs_long (sb->u.ext2_sb.s_es->s_blocks_count - overhead, - &buf->f_blocks); - tmp = ext2_count_free_blocks (sb); - put_fs_long (tmp, &buf->f_bfree); - if (tmp >= sb->u.ext2_sb.s_es->s_r_blocks_count) - put_fs_long (tmp - sb->u.ext2_sb.s_es->s_r_blocks_count, - &buf->f_bavail); - else - put_fs_long (0, &buf->f_bavail); - put_fs_long (sb->u.ext2_sb.s_es->s_inodes_count, &buf->f_files); - put_fs_long (ext2_count_free_inodes (sb), &buf->f_ffree); - put_fs_long (EXT2_NAME_LEN, &buf->f_namelen); - /* Don't know what value to put in buf->f_fsid */ + tmp.f_type = EXT2_SUPER_MAGIC; + tmp.f_bsize = sb->s_blocksize; + tmp.f_blocks = le32_to_cpu(sb->u.ext2_sb.s_es->s_blocks_count) - overhead; + tmp.f_bfree = ext2_count_free_blocks (sb); + tmp.f_bavail = tmp.f_bfree - le32_to_cpu(sb->u.ext2_sb.s_es->s_r_blocks_count); + if (tmp.f_bfree < le32_to_cpu(sb->u.ext2_sb.s_es->s_r_blocks_count)) + tmp.f_bavail = 0; + tmp.f_files = le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count); + tmp.f_ffree = ext2_count_free_inodes (sb); + tmp.f_namelen = EXT2_NAME_LEN; + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; +} + +static struct file_system_type ext2_fs_type = { + "ext2", + FS_REQUIRES_DEV /* | FS_IBASKET */, /* ibaskets have unresolved bugs */ + ext2_read_super, + NULL +}; + +static int __init init_ext2_fs(void) +{ + return register_filesystem(&ext2_fs_type); } + +static int __exit exit_ext2_fs(void) +{ + unregister_filesystem(&ext2_fs_type); +} + +EXPORT_NO_SYMBOLS; + +module_init(init_ext2_fs) +module_exit(exit_ext2_fs) diff --git a/sys/gnu/ext2fs/i386-bitops.h b/sys/gnu/ext2fs/i386-bitops.h index ee339bd646f2..86068d069773 100644 --- a/sys/gnu/ext2fs/i386-bitops.h +++ b/sys/gnu/ext2fs/i386-bitops.h @@ -13,86 +13,141 @@ * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1). */ +#ifdef __SMP__ +#define LOCK_PREFIX "lock ; " +#else +#define LOCK_PREFIX "" +#endif + +/* + * Function prototypes to keep gcc -Wall happy + */ +extern void set_bit(int nr, volatile void * addr); +extern void clear_bit(int nr, volatile void * addr); +extern void change_bit(int nr, volatile void * addr); +extern int test_and_set_bit(int nr, volatile void * addr); +extern int test_and_clear_bit(int nr, volatile void * addr); +extern int test_and_change_bit(int nr, volatile void * addr); +extern int __constant_test_bit(int nr, const volatile void * addr); +extern int __test_bit(int nr, volatile void * addr); +extern int find_first_zero_bit(void * addr, unsigned size); +extern int find_next_zero_bit (void * addr, int size, int offset); +extern unsigned long ffz(unsigned long word); + /* * Some hacks to defeat gcc over-optimizations.. */ struct __dummy { unsigned long a[100]; }; -#define ADDR (*(struct __dummy *) addr) +#define ADDR (*(volatile struct __dummy *) addr) +#define CONST_ADDR (*(volatile const struct __dummy *) addr) -extern __inline__ int set_bit(int nr, void * addr) +extern __inline__ void set_bit(int nr, volatile void * addr) +{ + __asm__ __volatile__( LOCK_PREFIX + "btsl %1,%0" + :"=m" (ADDR) + :"Ir" (nr)); +} + +extern __inline__ void clear_bit(int nr, volatile void * addr) +{ + __asm__ __volatile__( LOCK_PREFIX + "btrl %1,%0" + :"=m" (ADDR) + :"Ir" (nr)); +} + +extern __inline__ void change_bit(int nr, volatile void * addr) +{ + __asm__ __volatile__( LOCK_PREFIX + "btcl %1,%0" + :"=m" (ADDR) + :"Ir" (nr)); +} + +extern __inline__ int test_and_set_bit(int nr, volatile void * addr) { int oldbit; - __asm__ __volatile__("btsl %2,%1\n\tsbbl %0,%0" + __asm__ __volatile__( LOCK_PREFIX + "btsl %2,%1\n\tsbbl %0,%0" :"=r" (oldbit),"=m" (ADDR) - :"r" (nr)); + :"Ir" (nr)); return oldbit; } -extern __inline__ int clear_bit(int nr, void * addr) +extern __inline__ int test_and_clear_bit(int nr, volatile void * addr) { int oldbit; - __asm__ __volatile__("btrl %2,%1\n\tsbbl %0,%0" + __asm__ __volatile__( LOCK_PREFIX + "btrl %2,%1\n\tsbbl %0,%0" :"=r" (oldbit),"=m" (ADDR) - :"r" (nr)); + :"Ir" (nr)); return oldbit; } -extern __inline__ int change_bit(int nr, void * addr) +extern __inline__ int test_and_change_bit(int nr, volatile void * addr) { int oldbit; - __asm__ __volatile__("btcl %2,%1\n\tsbbl %0,%0" + __asm__ __volatile__( LOCK_PREFIX + "btcl %2,%1\n\tsbbl %0,%0" :"=r" (oldbit),"=m" (ADDR) - :"r" (nr)); + :"Ir" (nr)); return oldbit; } /* - * This routine doesn't need to be atomic, but it's faster to code it - * this way. + * This routine doesn't need to be atomic. */ -extern __inline__ int test_bit(int nr, void * addr) +extern __inline__ int __constant_test_bit(int nr, const volatile void * addr) +{ + return ((1UL << (nr & 31)) & (((const volatile unsigned int *) addr)[nr >> 5])) != 0; +} + +extern __inline__ int __test_bit(int nr, volatile void * addr) { int oldbit; - __asm__ __volatile__("btl %2,%1\n\tsbbl %0,%0" + __asm__ __volatile__( + "btl %2,%1\n\tsbbl %0,%0" :"=r" (oldbit) - :"m" (ADDR),"r" (nr)); + :"m" (ADDR),"Ir" (nr)); return oldbit; } +#define test_bit(nr,addr) \ +(__builtin_constant_p(nr) ? \ + __constant_test_bit((nr),(addr)) : \ + __test_bit((nr),(addr))) + /* * Find-bit routines.. */ -extern inline int find_first_zero_bit(void * addr, unsigned size) +extern __inline__ int find_first_zero_bit(void * addr, unsigned size) { + int d0, d1, d2; int res; if (!size) return 0; - __asm__(" - cld - movl $-1,%%eax - repe; scasl - je 1f - subl $4,%%edi - movl (%%edi),%%eax - notl %%eax - bsfl %%eax,%%edx - jmp 2f -1: xorl %%edx,%%edx -2: subl %%ebx,%%edi - shll $3,%%edi - addl %%edi,%%edx" - :"=d" (res) - :"c" ((size + 31) >> 5), "D" (addr), "b" (addr) - :"ax", "bx", "cx", "di"); + __asm__("movl $-1,%%eax\n\t" + "xorl %%edx,%%edx\n\t" + "repe; scasl\n\t" + "je 1f\n\t" + "xorl -4(%%edi),%%eax\n\t" + "subl $4,%%edi\n\t" + "bsfl %%eax,%%edx\n" + "1:\tsubl %%ebx,%%edi\n\t" + "shll $3,%%edi\n\t" + "addl %%edi,%%edx" + :"=d" (res), "=&c" (d0), "=&D" (d1), "=&a" (d2) + :"1" ((size + 31) >> 5), "2" (addr), "b" (addr)); return res; } -extern inline int find_next_zero_bit (void * addr, int size, int offset) +extern __inline__ int find_next_zero_bit (void * addr, int size, int offset) { unsigned long * p = ((unsigned long *) addr) + (offset >> 5); int set = 0, bit = offset & 31, res; @@ -101,11 +156,10 @@ extern inline int find_next_zero_bit (void * addr, int size, int offset) /* * Look for zero in first byte */ - __asm__(" - bsfl %1,%0 - jne 1f - movl $32, %0 -1: " + __asm__("bsfl %1,%0\n\t" + "jne 1f\n\t" + "movl $32, %0\n" + "1:" : "=r" (set) : "r" (~(*p >> bit))); if (set < (32 - bit)) @@ -124,7 +178,7 @@ extern inline int find_next_zero_bit (void * addr, int size, int offset) * ffz = Find First Zero in word. Undefined if no zero exists, * so code should check against ~0UL first.. */ -extern inline unsigned long ffz(unsigned long word) +extern __inline__ unsigned long ffz(unsigned long word) { __asm__("bsfl %1,%0" :"=r" (word) @@ -132,4 +186,50 @@ extern inline unsigned long ffz(unsigned long word) return word; } +#ifdef __KERNEL__ + +/* + * ffs: find first bit set. This is defined the same way as + * the libc and compiler builtin ffs routines, therefore + * differs in spirit from the above ffz (man ffs). + */ + +extern __inline__ int ffs(int x) +{ + int r; + + __asm__("bsfl %1,%0\n\t" + "jnz 1f\n\t" + "movl $-1,%0\n" + "1:" : "=r" (r) : "g" (x)); + return r+1; +} + +/* + * hweightN: returns the hamming weight (i.e. the number + * of bits set) of a N-bit word + */ + +#define hweight32(x) generic_hweight32(x) +#define hweight16(x) generic_hweight16(x) +#define hweight8(x) generic_hweight8(x) + +#endif /* __KERNEL__ */ + +#ifdef __KERNEL__ + +#define ext2_set_bit test_and_set_bit +#define ext2_clear_bit test_and_clear_bit +#define ext2_test_bit test_bit +#define ext2_find_first_zero_bit find_first_zero_bit +#define ext2_find_next_zero_bit find_next_zero_bit + +/* Bitmap functions for the minix filesystem. */ +#define minix_set_bit(nr,addr) test_and_set_bit(nr,addr) +#define minix_clear_bit(nr,addr) test_and_clear_bit(nr,addr) +#define minix_test_bit(nr,addr) test_bit(nr,addr) +#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) + +#endif /* __KERNEL__ */ + #endif /* _I386_BITOPS_H */ |