Messages in this thread Patch in this message | | | Date | Fri, 17 Dec 1999 00:11:22 -0500 | Subject | Re: [security] Big problem on 2.0.x? (fwd) | From | tytso@mit ... |
| |
Speaking of 2.0 updates, there were some people who expressed interest at the Linux Standards Base meeting in New York City for my patches which allow a 2.0 kernel to be able to mount filesystems with sparse superblocks (which is the default in the latest e2fsprogs, and much more efficienct on larger disks.)
It's a pretty stable patch, so I'm confident about its stability; distributions that are still releasing 2.0 kernel updates might want to consider this for backwards compatibility reasons, if you need to mount filesystems created on newer systems.
- Ted
Patch generated: on Thu Oct 21 14:27:50 EDT 1999 by tytso@trampoline.thunk.org against Linux version 2.0.35
=================================================================== RCS file: fs/ext2/RCS/super.c,v retrieving revision 1.1 diff -u -r1.1 fs/ext2/super.c --- fs/ext2/super.c 1999/10/15 12:06:21 1.1 +++ fs/ext2/super.c 1999/10/15 15:25:40 @@ -463,6 +463,9 @@ goto failed_mount; } } + sb->u.ext2_sb.s_feature_compat = es->s_feature_compat; + sb->u.ext2_sb.s_feature_incompat = es->s_feature_incompat; + sb->u.ext2_sb.s_feature_ro_compat = es->s_feature_ro_compat; sb->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE << es->s_log_frag_size; if (sb->u.ext2_sb.s_frag_size) @@ -710,6 +713,7 @@ unsigned long overhead; unsigned long overhead_per_group; struct statfs tmp; + int ngroups, i; if (test_opt (sb, MINIX_DF)) overhead = 0; @@ -717,13 +721,35 @@ /* * 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 = 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)); } tmp.f_type = EXT2_SUPER_MAGIC; =================================================================== RCS file: fs/ext2/RCS/balloc.c,v retrieving revision 1.1 diff -u -r1.1 fs/ext2/balloc.c --- fs/ext2/balloc.c 1999/10/15 12:13:01 1.1 +++ fs/ext2/balloc.c 1999/10/15 12:15:57 @@ -645,6 +645,25 @@ 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; @@ -671,16 +690,22 @@ 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 (!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)) + 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)) ext2_error (sb, "ext2_check_blocks_bitmap", "Block bitmap for group %d is marked free", =================================================================== RCS file: fs/ext2/RCS/dir.c,v retrieving revision 1.1 diff -u -r1.1 fs/ext2/dir.c --- fs/ext2/dir.c 1999/10/20 18:51:43 1.1 +++ fs/ext2/dir.c 1999/10/20 18:54:45 @@ -72,7 +72,7 @@ }; int ext2_check_dir_entry (const char * function, struct inode * dir, - struct ext2_dir_entry * de, struct buffer_head * bh, + struct ext2_dir_entry_2 * de, struct buffer_head * bh, unsigned long offset) { const char * error_msg = NULL; @@ -104,7 +104,7 @@ unsigned long offset, blk; int i, num, stored; struct buffer_head * bh, * tmp, * bha[16]; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; struct super_block * sb; int err; @@ -173,7 +173,7 @@ while (!error && filp->f_pos < inode->i_size && offset < sb->s_blocksize) { - de = (struct ext2_dir_entry *) (bh->b_data + offset); + de = (struct ext2_dir_entry_2 *) (bh->b_data + offset); if (!ext2_check_dir_entry ("ext2_readdir", inode, de, bh, offset)) { /* On error, skip the f_pos to the =================================================================== RCS file: fs/ext2/RCS/namei.c,v retrieving revision 1.1 diff -u -r1.1 fs/ext2/namei.c --- fs/ext2/namei.c 1999/10/20 18:51:43 1.1 +++ fs/ext2/namei.c 1999/10/20 20:14:43 @@ -36,7 +36,7 @@ * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure. */ static int ext2_match (int len, const char * const name, - struct ext2_dir_entry * de) + struct ext2_dir_entry_2 * de) { if (!de || !de->inode || len > EXT2_NAME_LEN) return 0; @@ -61,7 +61,7 @@ */ static struct buffer_head * ext2_find_entry (struct inode * dir, const char * const name, int namelen, - struct ext2_dir_entry ** res_dir) + struct ext2_dir_entry_2 ** res_dir) { struct super_block * sb; struct buffer_head * bh_use[NAMEI_RA_SIZE]; @@ -92,7 +92,7 @@ for (block = 0, offset = 0; offset < dir->i_size; block++) { struct buffer_head * bh; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; char * dlimit; if ((block % NAMEI_RA_BLOCKS) == 0 && toread) { @@ -115,7 +115,7 @@ break; } - de = (struct ext2_dir_entry *) bh->b_data; + de = (struct ext2_dir_entry_2 *) bh->b_data; dlimit = bh->b_data + sb->s_blocksize; while ((char *) de < dlimit) { if (!ext2_check_dir_entry ("ext2_find_entry", dir, @@ -130,7 +130,7 @@ return bh; } offset += de->rec_len; - de = (struct ext2_dir_entry *) + de = (struct ext2_dir_entry_2 *) ((char *) de + de->rec_len); } @@ -155,7 +155,7 @@ struct inode ** result) { unsigned long ino; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; struct buffer_head * bh; *result = NULL; @@ -211,13 +211,13 @@ */ static struct buffer_head * ext2_add_entry (struct inode * dir, const char * name, int namelen, - struct ext2_dir_entry ** res_dir, + struct ext2_dir_entry_2 ** res_dir, int *err) { unsigned long offset; unsigned short rec_len; struct buffer_head * bh; - struct ext2_dir_entry * de, * de1; + struct ext2_dir_entry_2 * de, * de1; struct super_block * sb; *err = -EINVAL; @@ -247,7 +247,7 @@ return NULL; rec_len = EXT2_DIR_REC_LEN(namelen); offset = 0; - de = (struct ext2_dir_entry *) bh->b_data; + de = (struct ext2_dir_entry_2 *) bh->b_data; *err = -ENOSPC; while (1) { if ((char *)de >= sb->s_blocksize + bh->b_data) { @@ -264,7 +264,7 @@ ext2_debug ("creating next block\n"); - de = (struct ext2_dir_entry *) bh->b_data; + de = (struct ext2_dir_entry_2 *) bh->b_data; de->inode = 0; de->rec_len = sb->s_blocksize; dir->i_size = offset + sb->s_blocksize; @@ -273,7 +273,7 @@ ext2_debug ("skipping to next block\n"); - de = (struct ext2_dir_entry *) bh->b_data; + de = (struct ext2_dir_entry_2 *) bh->b_data; } } if (!ext2_check_dir_entry ("ext2_add_entry", dir, de, bh, @@ -291,7 +291,7 @@ (de->rec_len >= EXT2_DIR_REC_LEN(de->name_len) + rec_len)) { offset += de->rec_len; if (de->inode) { - de1 = (struct ext2_dir_entry *) ((char *) de + + de1 = (struct ext2_dir_entry_2 *) ((char *) de + EXT2_DIR_REC_LEN(de->name_len)); de1->rec_len = de->rec_len - EXT2_DIR_REC_LEN(de->name_len); @@ -300,6 +300,7 @@ } de->inode = 0; de->name_len = namelen; + de->file_type = 0; memcpy (de->name, name, namelen); /* * XXX shouldn't update any times until successful @@ -321,7 +322,7 @@ return bh; } offset += de->rec_len; - de = (struct ext2_dir_entry *) ((char *) de + de->rec_len); + de = (struct ext2_dir_entry_2 *) ((char *) de + de->rec_len); } brelse (bh); return NULL; @@ -331,15 +332,15 @@ * ext2_delete_entry deletes a directory entry by merging it with the * previous entry */ -static int ext2_delete_entry (struct ext2_dir_entry * dir, +static int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct buffer_head * bh) { - struct ext2_dir_entry * de, * pde; + struct ext2_dir_entry_2 * de, * pde; int i; i = 0; pde = NULL; - de = (struct ext2_dir_entry *) bh->b_data; + de = (struct ext2_dir_entry_2 *) bh->b_data; while (i < bh->b_size) { if (!ext2_check_dir_entry ("ext2_delete_entry", NULL, de, bh, i)) @@ -352,7 +353,7 @@ } i += de->rec_len; pde = de; - de = (struct ext2_dir_entry *) ((char *) de + de->rec_len); + de = (struct ext2_dir_entry_2 *) ((char *) de + de->rec_len); } return -ENOENT; } @@ -362,7 +363,7 @@ { struct inode * inode; struct buffer_head * bh; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; int err; *result = NULL; @@ -385,6 +386,9 @@ return err; } de->inode = inode->i_ino; + if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) + de->file_type = EXT2_FT_REG_FILE; dir->i_version = ++event; dcache_add(dir, de->name, de->name_len, de->inode); mark_buffer_dirty(bh, 1); @@ -403,7 +407,7 @@ { struct inode * inode; struct buffer_head * bh; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; int err; if (!dir) @@ -427,21 +431,27 @@ inode->i_uid = current->fsuid; inode->i_mode = mode; inode->i_op = NULL; - if (S_ISREG(inode->i_mode)) + if (S_ISREG(inode->i_mode)) { inode->i_op = &ext2_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) { - inode->i_op = &ext2_dir_inode_operations; - if (dir->i_mode & S_ISGID) - inode->i_mode |= S_ISGID; - } - else if (S_ISLNK(inode->i_mode)) - inode->i_op = &ext2_symlink_inode_operations; - else if (S_ISCHR(inode->i_mode)) + if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) + de->file_type = EXT2_FT_REG_FILE; + } else if (S_ISCHR(inode->i_mode)) { inode->i_op = &chrdev_inode_operations; - else if (S_ISBLK(inode->i_mode)) + if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) + de->file_type = EXT2_FT_CHRDEV; + } else if (S_ISBLK(inode->i_mode)) { inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) + if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) + de->file_type = EXT2_FT_BLKDEV; + } else if (S_ISFIFO(inode->i_mode)) { init_fifo(inode); + if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) + de->file_type = EXT2_FT_FIFO; + } if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = to_kdev_t(rdev); inode->i_dirt = 1; @@ -471,7 +481,7 @@ { struct inode * inode; struct buffer_head * bh, * dir_block; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; int err; if (!dir) @@ -506,16 +516,22 @@ return err; } inode->i_blocks = inode->i_sb->s_blocksize / 512; - de = (struct ext2_dir_entry *) dir_block->b_data; + de = (struct ext2_dir_entry_2 *) dir_block->b_data; de->inode = inode->i_ino; de->name_len = 1; de->rec_len = EXT2_DIR_REC_LEN(de->name_len); strcpy (de->name, "."); - de = (struct ext2_dir_entry *) ((char *) de + de->rec_len); + if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) + de->file_type = EXT2_FT_DIR; + de = (struct ext2_dir_entry_2 *) ((char *) de + de->rec_len); de->inode = dir->i_ino; de->rec_len = inode->i_sb->s_blocksize - EXT2_DIR_REC_LEN(1); de->name_len = 2; strcpy (de->name, ".."); + if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) + de->file_type = EXT2_FT_DIR; inode->i_nlink = 2; mark_buffer_dirty(dir_block, 1); brelse (dir_block); @@ -532,6 +548,9 @@ return err; } de->inode = inode->i_ino; + if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) + de->file_type = EXT2_FT_DIR; dir->i_version = ++event; dcache_add(dir, de->name, de->name_len, de->inode); mark_buffer_dirty(bh, 1); @@ -554,7 +573,7 @@ { unsigned long offset; struct buffer_head * bh; - struct ext2_dir_entry * de, * de1; + struct ext2_dir_entry_2 * de, * de1; struct super_block * sb; int err; @@ -566,8 +585,8 @@ inode->i_ino); return 1; } - de = (struct ext2_dir_entry *) bh->b_data; - de1 = (struct ext2_dir_entry *) ((char *) de + de->rec_len); + de = (struct ext2_dir_entry_2 *) bh->b_data; + de1 = (struct ext2_dir_entry_2 *) ((char *) de + de->rec_len); if (de->inode != inode->i_ino || !de1->inode || strcmp (".", de->name) || strcmp ("..", de1->name)) { ext2_warning (inode->i_sb, "empty_dir", @@ -577,7 +596,7 @@ return 1; } offset = de->rec_len + de1->rec_len; - de = (struct ext2_dir_entry *) ((char *) de1 + de1->rec_len); + de = (struct ext2_dir_entry_2 *) ((char *) de1 + de1->rec_len); while (offset < inode->i_size ) { if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) { brelse (bh); @@ -589,7 +608,7 @@ offset += sb->s_blocksize; continue; } - de = (struct ext2_dir_entry *) bh->b_data; + de = (struct ext2_dir_entry_2 *) bh->b_data; } if (!ext2_check_dir_entry ("empty_dir", inode, de, bh, offset)) { @@ -601,7 +620,7 @@ return 0; } offset += de->rec_len; - de = (struct ext2_dir_entry *) ((char *) de + de->rec_len); + de = (struct ext2_dir_entry_2 *) ((char *) de + de->rec_len); } brelse (bh); return 1; @@ -612,7 +631,7 @@ int retval; struct inode * inode; struct buffer_head * bh; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; repeat: if (!dir) @@ -701,7 +720,7 @@ int retval; struct inode * inode; struct buffer_head * bh; - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; repeat: if (!dir) @@ -766,7 +785,7 @@ int ext2_symlink (struct inode * dir, const char * name, int len, const char * symname) { - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; struct inode * inode = NULL; struct buffer_head * bh = NULL, * name_block = NULL; char * link; @@ -831,6 +850,9 @@ return err; } de->inode = inode->i_ino; + if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) + de->file_type = EXT2_FT_SYMLINK; dir->i_version = ++event; dcache_add(dir, de->name, de->name_len, de->inode); mark_buffer_dirty(bh, 1); @@ -847,7 +869,7 @@ int ext2_link (struct inode * oldinode, struct inode * dir, const char * name, int len) { - struct ext2_dir_entry * de; + struct ext2_dir_entry_2 * de; struct buffer_head * bh; int err; @@ -880,6 +902,21 @@ return err; } de->inode = oldinode->i_ino; + if (EXT2_HAS_INCOMPAT_FEATURE(oldinode->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) { + if (S_ISREG(oldinode->i_mode)) + de->file_type = EXT2_FT_REG_FILE; + else if (S_ISDIR(oldinode->i_mode)) + de->file_type = EXT2_FT_DIR; + else if (S_ISLNK(oldinode->i_mode)) + de->file_type = EXT2_FT_SYMLINK; + else if (S_ISCHR(oldinode->i_mode)) + de->file_type = EXT2_FT_CHRDEV; + else if (S_ISBLK(oldinode->i_mode)) + de->file_type = EXT2_FT_BLKDEV; + else if (S_ISFIFO(oldinode->i_mode)) + de->file_type = EXT2_FT_FIFO; + } dir->i_version = ++event; dcache_add(dir, de->name, de->name_len, de->inode); mark_buffer_dirty(bh, 1); @@ -921,12 +958,12 @@ } #define PARENT_INO(buffer) \ - ((struct ext2_dir_entry *) ((char *) buffer + \ - ((struct ext2_dir_entry *) buffer)->rec_len))->inode + ((struct ext2_dir_entry_2 *) ((char *) buffer + \ + ((struct ext2_dir_entry_2 *) buffer)->rec_len))->inode #define PARENT_NAME(buffer) \ - ((struct ext2_dir_entry *) ((char *) buffer + \ - ((struct ext2_dir_entry *) buffer)->rec_len))->name + ((struct ext2_dir_entry_2 *) ((char *) buffer + \ + ((struct ext2_dir_entry_2 *) buffer)->rec_len))->name /* * rename uses retrying to avoid race-conditions: at least they should be @@ -946,7 +983,7 @@ { struct inode * old_inode, * new_inode; struct buffer_head * old_bh, * new_bh, * dir_bh; - struct ext2_dir_entry * old_de, * new_de; + struct ext2_dir_entry_2 * old_de, * new_de; int retval; goto start_up; @@ -1060,6 +1097,9 @@ * ok, that's it */ new_de->inode = old_inode->i_ino; + if (EXT2_HAS_INCOMPAT_FEATURE(new_dir->i_sb, + EXT2_FEATURE_INCOMPAT_FILETYPE)) + new_de->file_type = old_de->file_type; dcache_add(new_dir, new_de->name, new_de->name_len, new_de->inode); retval = ext2_delete_entry (old_de, old_bh); if (retval == -ENOENT) =================================================================== RCS file: include/linux/RCS/ext2_fs.h,v retrieving revision 1.1 diff -u -r1.1 include/linux/ext2_fs.h --- include/linux/ext2_fs.h 1999/10/15 12:06:06 1.1 +++ include/linux/ext2_fs.h 1999/10/20 20:09:57 @@ -368,6 +368,15 @@ __u32 s_reserved[230]; /* 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 */ @@ -407,6 +416,35 @@ }; /* + * 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 @@ -417,11 +455,23 @@ ~EXT2_DIR_ROUND) /* - * Feature set definitions --- none are defined as of now + * 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_RO_COMPAT_SPARSE_SUPER 0x0001 + +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 + #define EXT2_FEATURE_COMPAT_SUPP 0 -#define EXT2_FEATURE_INCOMPAT_SUPP 0 -#define EXT2_FEATURE_RO_COMPAT_SUPP 0 +#define EXT2_FEATURE_INCOMPAT_SUPP EXT2_FEATURE_INCOMPAT_FILETYPE +#define EXT2_FEATURE_RO_COMPAT_SUPP EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER #ifdef __KERNEL__ /* @@ -440,6 +490,7 @@ extern int ext2_permission (struct inode *, int); /* balloc.c */ +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, @@ -452,7 +503,7 @@ /* dir.c */ extern int ext2_check_dir_entry (const char *, struct inode *, - struct ext2_dir_entry *, struct buffer_head *, + struct ext2_dir_entry_2 *, struct buffer_head *, unsigned long); /* file.c */ =================================================================== RCS file: include/linux/RCS/ext2_fs_sb.h,v retrieving revision 1.1 diff -u -r1.1 include/linux/ext2_fs_sb.h --- include/linux/ext2_fs_sb.h 1999/10/15 14:12:51 1.1 +++ include/linux/ext2_fs_sb.h 1999/10/20 20:14:47 @@ -60,6 +60,9 @@ 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 */ - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/
| |