Re: [security] Big problem on 2.0.x? (fwd)

tytso@mit.edu
Fri, 17 Dec 1999 00:11:22 -0500


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/