Ext2 patches for Linux 2.1

tytso@mit.edu
Thu, 12 Mar 1998 23:12:53 -0500


Hi Linus,

Could you please install this patch set into the 2.1 tree? It
includes previous patches submitted by Stephen and Jakub (with some
fixes) and my own patches and improvements. None of these are really
major changes; most of them are changes to provide better a forward
migration path past Linux 2.2 for future ext2fs features.

These patches provide the following enhancements to ext2.

* Fixed a bug where we weren't byte-swapping the feature set
flags before checking EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER

* Added Stephen Tweedie's patches to allow the number of file
blocks which will be preallocated to be tuned (instead
of being fixed at 8 blocks).

* Added Stephen Tweedie's patches to allow directory blocks to
be preallocated. This change is only activated if the
EXT2_FEATURE_COMPAT_DIR_PREALLOC is enabled. (There
will soon be a new release of e2fsprogs that will
allow you to turn this on.) The change is compatible
with older kernels (that's why it's a COMPAT feature),
but we need to flag it in the feature set because the
e2fsck needs a few changes to support this.

* Added future support for B-trees in directories. I have a
design in mind which is fully read/only compatible
with the existing ext2 directories, which will make
its debut in the 2.3 kernel series. This patch will
allow 2.2 kernels to mount filesystems with B-tree
directories read-write; if a 2.2 kernel tries to
modify a B-tree directory, the B-tree valid bit will
be turned off, since the B-tree structures won't be
updated by 2.2 kernels. 2.0 kernels will be able to
mount filesystems with B-tree directories read-only.
This defines a new feature, EXT2_FEATURE_RO_COMPAT_BTREE_DIR.

* Added Jakub Jelinek's support for large files on 64-bit
platforms. On a 64-bit platform, the first time you
expand a file past the 32-bit boundary, the
EXT2_FEATURE_RO_COMPAT_LARGE_FILE is turned on.
2.0 machines will be able to mount such filesystems
read-only. 2.2 kernels on 32-bit platforms will be
able such filesystems read-write, but they will only
be able to see the first 2**32 bytes of the file, and
any attempt to open a large file for read/write access
will cause an EBIGF error.

* Added support for storing the file type in the directory
entry. This optimization was added to BSD 4.4 and
makes a very big difference for a number of
operations, since application programs can avoid doing
a stat in a number of situations. Support for this is
in the GNU user-land utilities, and is in glibc
already. Beyond this patch, we also need to implement
a new getdents system call that will return the
information all the way to libc.

The reason why it's important to get this change into
2.2 is that it requires "stealing" 8-bits from the
name_len field of the directory entry. Ext2fs limits
you to 255 characters in a file name, so the high-byte
of name_len is always zero. However, older kernels
look at both bytes of name_len, and will get confused
if we try to store something there. So we can only
update the file type field if the feature
EXT2_FEATURE_INCOMPAT_FILETYPE is enabled.

I want to get this support into the 2.2 kernel, since
even if it isn't used much (because people will want
their filesystems to be compatible with 2.0 kernels),
we will be able to migrate smoothly to using this
feature by default in the future.

Thanks!!

- Ted

Patch generated: on Thu Mar 12 23:10:20 EST 1998 by tytso@rsts-11
against Linux version 2.1.89

===================================================================
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 1998/03/12 01:33:55 1.1
+++ include/linux/ext2_fs.h 1998/03/12 15:22:39
@@ -31,6 +31,7 @@
* 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
@@ -191,6 +192,7 @@
#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */
#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */
#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
+#define EXT2_BTREE_FL 0x40000000 /* btree format dir */

/*
* ioctl commands
@@ -255,6 +257,8 @@
} 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
@@ -367,9 +371,25 @@
__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_reserved[206]; /* Padding to the end of the block */
+ /*
+ * 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[205]; /* 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
*/
@@ -394,11 +414,26 @@
* 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_FILETYPE 0x0001

#define EXT2_FEATURE_COMPAT_SUPP 0
-#define EXT2_FEATURE_INCOMPAT_SUPP 0
-#define EXT2_FEATURE_RO_COMPAT_SUPP EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
+#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
@@ -419,6 +454,36 @@
};

/*
+ * 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 4 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_WHT 8
+
+#define EXT2_FT_MAX 9
+
+/*
* EXT2_DIR_PAD defines the directory entries boundaries
*
* NOTE: It must be a multiple of 4
@@ -457,7 +522,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_i.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/ext2_fs_i.h
--- include/linux/ext2_fs_i.h 1998/03/12 06:11:17 1.1
+++ include/linux/ext2_fs_i.h 1998/03/12 07:41:17
@@ -35,7 +35,9 @@
__u32 i_next_alloc_goal;
__u32 i_prealloc_block;
__u32 i_prealloc_count;
+ __u32 i_size_high; /* save the original high bits of size */
int i_new_inode:1; /* Is a freshly allocated inode */
+ int i_large_file:1; /* On 32-bit hosts will prevent opening RW */
};

#endif /* _LINUX_EXT2_FS_I */
===================================================================
RCS file: fs/ext2/RCS/dir.c,v
retrieving revision 1.1
diff -u -r1.1 fs/ext2/dir.c
--- fs/ext2/dir.c 1998/03/12 01:35:11 1.1
+++ fs/ext2/dir.c 1998/03/12 01:57:16
@@ -75,7 +75,8 @@
};

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;
@@ -84,7 +85,7 @@
error_msg = "rec_len is smaller than minimal";
else if (le16_to_cpu(de->rec_len) % 4 != 0)
error_msg = "rec_len % 4 != 0";
- else if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len)))
+ else if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(de->name_len))
error_msg = "rec_len is too small for name_len";
else if (dir && ((char *) de - bh->b_data) + le16_to_cpu(de->rec_len) >
dir->i_sb->s_blocksize)
@@ -97,7 +98,7 @@
"offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
dir->i_ino, error_msg, offset,
(unsigned long) le32_to_cpu(de->inode),
- le16_to_cpu(de->rec_len), le16_to_cpu(de->name_len));
+ le16_to_cpu(de->rec_len), de->name_len);
return error_msg == NULL ? 1 : 0;
}

@@ -108,7 +109,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;
struct inode *inode = filp->f_dentry->d_inode;
@@ -158,7 +159,7 @@
* to make sure. */
if (filp->f_version != inode->i_version) {
for (i = 0; i < sb->s_blocksize && i < offset; ) {
- de = (struct ext2_dir_entry *)
+ de = (struct ext2_dir_entry_2 *)
(bh->b_data + i);
/* It's too expensive to do a full
* dirent test each time round this
@@ -178,7 +179,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
@@ -200,7 +201,7 @@
unsigned long version = inode->i_version;

error = filldir(dirent, de->name,
- le16_to_cpu(de->name_len),
+ de->name_len,
filp->f_pos, le32_to_cpu(de->inode));
if (error)
break;
===================================================================
RCS file: fs/ext2/RCS/namei.c,v
retrieving revision 1.1
diff -u -r1.1 fs/ext2/namei.c
--- fs/ext2/namei.c 1998/03/12 01:35:11 1.1
+++ fs/ext2/namei.c 1998/03/13 04:04:27
@@ -13,8 +13,10 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*
* Big-endian to little-endian byte-swapping/bitmaps by
- * David S. Miller (davem@caip.rutgers.edu), 1995
- */
+ * David S. Miller (davem@caip.rutgers.edu), 1995b
+ * Directory entry file type support and forward compatibility hooks
+ * for B-tree directories by Theodore Ts'o (tytso@mit.edu), 1998
+ * /

#include <asm/uaccess.h>

@@ -40,17 +42,17 @@
* 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 || !le32_to_cpu(de->inode) || len > EXT2_NAME_LEN)
return 0;
/*
* "" means "." ---> so paths like "/usr/lib//libc.a" work
*/
- if (!len && le16_to_cpu(de->name_len) == 1 && (de->name[0] == '.') &&
+ if (!len && de->name_len == 1 && (de->name[0] == '.') &&
(de->name[1] == '\0'))
return 1;
- if (len != le16_to_cpu(de->name_len))
+ if (len != de->name_len)
return 0;
return !memcmp(name, de->name, len);
}
@@ -65,7 +67,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];
@@ -96,7 +98,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) {
@@ -105,9 +107,11 @@
}
bh = bh_use[block % NAMEI_RA_SIZE];
if (!bh) {
+#if 0
ext2_error (sb, "ext2_find_entry",
"directory #%lu contains a hole at offset %lu",
dir->i_ino, offset);
+#endif
offset += sb->s_blocksize;
continue;
}
@@ -119,7 +123,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,
@@ -134,7 +138,7 @@
return bh;
}
offset += le16_to_cpu(de->rec_len);
- de = (struct ext2_dir_entry *)
+ de = (struct ext2_dir_entry_2 *)
((char *) de + le16_to_cpu(de->rec_len));
}

@@ -158,7 +162,7 @@
int ext2_lookup(struct inode * dir, struct dentry *dentry)
{
struct inode * inode;
- struct ext2_dir_entry * de;
+ struct ext2_dir_entry_2 * de;
struct buffer_head * bh;

if (dentry->d_name.len > EXT2_NAME_LEN)
@@ -190,13 +194,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;
@@ -226,7 +230,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) {
@@ -243,16 +247,17 @@

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 = le32_to_cpu(0);
de->rec_len = le16_to_cpu(sb->s_blocksize);
dir->i_size = offset + sb->s_blocksize;
+ dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(dir);
} else {

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,
@@ -267,18 +272,19 @@
return NULL;
}
if ((le32_to_cpu(de->inode) == 0 && le16_to_cpu(de->rec_len) >= rec_len) ||
- (le16_to_cpu(de->rec_len) >= EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len)) + rec_len)) {
+ (le16_to_cpu(de->rec_len) >= EXT2_DIR_REC_LEN(de->name_len) + rec_len)) {
offset += le16_to_cpu(de->rec_len);
if (le32_to_cpu(de->inode)) {
- de1 = (struct ext2_dir_entry *) ((char *) de +
- EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len)));
+ de1 = (struct ext2_dir_entry_2 *) ((char *) de +
+ EXT2_DIR_REC_LEN(de->name_len));
de1->rec_len = cpu_to_le16(le16_to_cpu(de->rec_len) -
- EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len)));
- de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len)));
+ EXT2_DIR_REC_LEN(de->name_len));
+ de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(de->name_len));
de = de1;
}
de->inode = cpu_to_le32(0);
- de->name_len = cpu_to_le16(namelen);
+ de->name_len = namelen;
+ de->file_type = 0;
memcpy (de->name, name, namelen);
/*
* XXX shouldn't update any times until successful
@@ -292,6 +298,7 @@
* and/or different from the directory change time.
*/
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(dir);
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
@@ -300,7 +307,7 @@
return bh;
}
offset += le16_to_cpu(de->rec_len);
- de = (struct ext2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len));
+ de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
}
brelse (bh);
return NULL;
@@ -310,15 +317,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))
@@ -333,7 +340,7 @@
}
i += le16_to_cpu(de->rec_len);
pde = de;
- de = (struct ext2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len));
+ de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
}
return -ENOENT;
}
@@ -350,7 +357,7 @@
{
struct inode * inode;
struct buffer_head * bh;
- struct ext2_dir_entry * de;
+ struct ext2_dir_entry_2 * de;
int err = -EIO;

/*
@@ -371,6 +378,9 @@
return err;
}
de->inode = cpu_to_le32(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;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
@@ -386,7 +396,7 @@
{
struct inode * inode;
struct buffer_head * bh;
- struct ext2_dir_entry * de;
+ struct ext2_dir_entry_2 * de;
int err = -EIO;

err = -ENAMETOOLONG;
@@ -400,29 +410,48 @@
inode->i_uid = current->fsuid;
inode->i_mode = mode;
inode->i_op = NULL;
- if (S_ISREG(inode->i_mode))
+ bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
+ if (!bh)
+ goto out_no_entry;
+ de->inode = cpu_to_le32(inode->i_ino);
+ dir->i_version = ++event;
+ if (S_ISREG(inode->i_mode)) {
inode->i_op = &ext2_file_inode_operations;
- else if (S_ISDIR(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_ISDIR(inode->i_mode)) {
inode->i_op = &ext2_dir_inode_operations;
if (dir->i_mode & S_ISGID)
inode->i_mode |= S_ISGID;
+ if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
+ EXT2_FEATURE_INCOMPAT_FILETYPE))
+ de->file_type = EXT2_FT_DIR;
}
- else if (S_ISLNK(inode->i_mode))
+ 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_SYMLINK;
+ } 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);
mark_inode_dirty(inode);
- bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
- if (!bh)
- goto out_no_entry;
- de->inode = cpu_to_le32(inode->i_ino);
- dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
@@ -445,7 +474,7 @@
{
struct inode * inode;
struct buffer_head * bh, * dir_block;
- struct ext2_dir_entry * de;
+ struct ext2_dir_entry_2 * de;
int err;

err = -ENAMETOOLONG;
@@ -463,6 +492,7 @@

inode->i_op = &ext2_dir_inode_operations;
inode->i_size = inode->i_sb->s_blocksize;
+ inode->i_blocks = 0;
dir_block = ext2_bread (inode, 0, 1, &err);
if (!dir_block) {
inode->i_nlink--; /* is this nlink == 0? */
@@ -470,17 +500,22 @@
iput (inode);
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 = cpu_to_le32(inode->i_ino);
- de->name_len = cpu_to_le16(1);
- de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(le16_to_cpu(de->name_len)));
+ de->name_len = 1;
+ de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(de->name_len));
strcpy (de->name, ".");
- de = (struct ext2_dir_entry *) ((char *) de + le16_to_cpu(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 + le16_to_cpu(de->rec_len));
de->inode = cpu_to_le32(dir->i_ino);
de->rec_len = cpu_to_le16(inode->i_sb->s_blocksize - EXT2_DIR_REC_LEN(1));
- de->name_len = cpu_to_le16(2);
+ 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);
@@ -492,6 +527,9 @@
if (!bh)
goto out_no_entry;
de->inode = cpu_to_le32(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;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
@@ -499,6 +537,7 @@
wait_on_buffer (bh);
}
dir->i_nlink++;
+ dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(dir);
d_instantiate(dentry, inode);
brelse (bh);
@@ -520,7 +559,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;

@@ -532,8 +571,8 @@
inode->i_ino);
return 1;
}
- de = (struct ext2_dir_entry *) bh->b_data;
- de1 = (struct ext2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len));
+ de = (struct ext2_dir_entry_2 *) bh->b_data;
+ de1 = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
if (le32_to_cpu(de->inode) != inode->i_ino || !le32_to_cpu(de1->inode) ||
strcmp (".", de->name) || strcmp ("..", de1->name)) {
ext2_warning (inode->i_sb, "empty_dir",
@@ -542,19 +581,21 @@
return 1;
}
offset = le16_to_cpu(de->rec_len) + le16_to_cpu(de1->rec_len);
- de = (struct ext2_dir_entry *) ((char *) de1 + le16_to_cpu(de1->rec_len));
+ de = (struct ext2_dir_entry_2 *) ((char *) de1 + le16_to_cpu(de1->rec_len));
while (offset < inode->i_size ) {
if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) {
brelse (bh);
bh = ext2_bread (inode, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1, &err);
if (!bh) {
+#if 0
ext2_error (sb, "empty_dir",
"directory #%lu contains a hole at offset %lu",
inode->i_ino, offset);
+#endif
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)) {
@@ -566,7 +607,7 @@
return 0;
}
offset += le16_to_cpu(de->rec_len);
- de = (struct ext2_dir_entry *) ((char *) de + le16_to_cpu(de->rec_len));
+ de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
}
brelse (bh);
return 1;
@@ -577,7 +618,7 @@
int retval;
struct inode * inode;
struct buffer_head * bh;
- struct ext2_dir_entry * de;
+ struct ext2_dir_entry_2 * de;

retval = -ENAMETOOLONG;
if (dentry->d_name.len > EXT2_NAME_LEN)
@@ -652,6 +693,7 @@
mark_inode_dirty(inode);
dir->i_nlink--;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(dir);
d_delete(dentry);

@@ -666,7 +708,7 @@
int retval;
struct inode * inode;
struct buffer_head * bh;
- struct ext2_dir_entry * de;
+ struct ext2_dir_entry_2 * de;

retval = -ENAMETOOLONG;
if (dentry->d_name.len > EXT2_NAME_LEN)
@@ -711,6 +753,7 @@
wait_on_buffer (bh);
}
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(dir);
inode->i_nlink--;
mark_inode_dirty(inode);
@@ -726,7 +769,7 @@

int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symname)
{
- struct ext2_dir_entry * de;
+ struct ext2_dir_entry_2 * de;
struct inode * inode;
struct buffer_head * bh = NULL, * name_block = NULL;
char * link;
@@ -774,6 +817,9 @@
if (!bh)
goto out_no_entry;
de->inode = cpu_to_le32(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;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
@@ -797,7 +843,7 @@
struct inode * dir, struct dentry *dentry)
{
struct inode *inode = old_dentry->d_inode;
- struct ext2_dir_entry * de;
+ struct ext2_dir_entry_2 * de;
struct buffer_head * bh;
int err;

@@ -815,6 +861,21 @@
return err;

de->inode = cpu_to_le32(inode->i_ino);
+ if (EXT2_HAS_INCOMPAT_FEATURE(inode->i_sb,
+ EXT2_FEATURE_INCOMPAT_FILETYPE)) {
+ if (S_ISREG(inode->i_mode))
+ de->file_type = EXT2_FT_REG_FILE;
+ else if (S_ISDIR(inode->i_mode))
+ de->file_type = EXT2_FT_DIR;
+ else if (S_ISLNK(inode->i_mode))
+ de->file_type = EXT2_FT_SYMLINK;
+ else if (S_ISCHR(inode->i_mode))
+ de->file_type = EXT2_FT_CHRDEV;
+ else if (S_ISBLK(inode->i_mode))
+ de->file_type = EXT2_FT_BLKDEV;
+ else if (S_ISFIFO(inode->i_mode))
+ de->file_type = EXT2_FT_FIFO;
+ }
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
@@ -831,8 +892,8 @@
}

#define PARENT_INO(buffer) \
- ((struct ext2_dir_entry *) ((char *) buffer + \
- le16_to_cpu(((struct ext2_dir_entry *) buffer)->rec_len)))->inode
+ ((struct ext2_dir_entry_2 *) ((char *) buffer + \
+ le16_to_cpu(((struct ext2_dir_entry_2 *) buffer)->rec_len)))->inode

/*
* rename uses retrying to avoid race-conditions: at least they should be
@@ -850,7 +911,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;

old_bh = new_bh = dir_bh = NULL;
@@ -942,6 +1003,10 @@
* ok, that's it
*/
new_de->inode = le32_to_cpu(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;
+
ext2_delete_entry (old_de, old_bh);

old_dir->i_version = ++event;
@@ -951,6 +1016,7 @@
mark_inode_dirty(new_inode);
}
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ old_dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(old_dir);
if (dir_bh) {
PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino);
@@ -962,6 +1028,7 @@
mark_inode_dirty(new_inode);
} else {
new_dir->i_nlink++;
+ new_dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(new_dir);
}
}
===================================================================
RCS file: fs/ext2/RCS/file.c,v
retrieving revision 1.1
diff -u -r1.1 fs/ext2/file.c
--- fs/ext2/file.c 1998/03/12 06:56:18 1.1
+++ fs/ext2/file.c 1998/03/13 04:02:53
@@ -13,6 +13,9 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*
* ext2 fs regular file handling primitives
+ *
+ * 64-bit file support on 64-bit platforms by Jakub Jelinek
+ * (jj@sunsite.ms.mff.cuni.cz)
*/

#include <asm/uaccess.h>
@@ -36,6 +39,20 @@
static long long ext2_file_lseek(struct file *, long long, int);
static ssize_t ext2_file_write (struct file *, const char *, size_t, loff_t *);
static int ext2_release_file (struct inode *, struct file *);
+#if BITS_PER_LONG < 64
+static int ext2_open_file (struct inode *, struct file *);
+#endif
+
+#define EXT2_MAX_SIZE(bits) \
+ (((EXT2_NDIR_BLOCKS + (1LL << (bits - 2)) + \
+ (1LL << (bits - 2)) * (1LL << (bits - 2)) + \
+ (1LL << (bits - 2)) * (1LL << (bits - 2)) * (1LL << (bits - 2))) * \
+ (1LL << bits)) - 1)
+
+static long long ext2_max_sizes[] = {
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+EXT2_MAX_SIZE(10), EXT2_MAX_SIZE(11), EXT2_MAX_SIZE(12), EXT2_MAX_SIZE(13)
+};

/*
* We have mostly NULL's here: the current defaults are ok for
@@ -49,7 +66,11 @@
NULL, /* poll - default */
ext2_ioctl, /* ioctl */
generic_file_mmap, /* mmap */
+#if BITS_PER_LONG == 64
NULL, /* no special open is needed */
+#else
+ ext2_open_file,
+#endif
ext2_release_file, /* release */
ext2_sync_file, /* fsync */
NULL, /* fasync */
@@ -86,7 +107,6 @@
long long offset,
int origin)
{
- long long retval;
struct inode *inode = file->f_dentry->d_inode;

switch (origin) {
@@ -96,17 +116,20 @@
case 1:
offset += file->f_pos;
}
- retval = -EINVAL;
- /* make sure the offset fits in 32 bits */
- if (((unsigned long long) offset >> 32) == 0) {
- if (offset != file->f_pos) {
- file->f_pos = offset;
- file->f_reada = 0;
- file->f_version = ++event;
- }
- retval = offset;
+ if (((unsigned long long) offset >> 32) != 0) {
+ if (sizeof(inode->i_size) > 4) {
+ if (offset > ext2_max_sizes
+ [EXT2_BLOCK_SIZE_BITS(inode->i_sb)])
+ return -EINVAL;
+ } else
+ return -EINVAL;
+ }
+ if (offset != file->f_pos) {
+ file->f_pos = offset;
+ file->f_reada = 0;
+ file->f_version = ++event;
}
- return retval;
+ return offset;
}

static inline void remove_suid(struct inode *inode)
@@ -128,7 +151,7 @@
size_t count, loff_t *ppos)
{
struct inode * inode = filp->f_dentry->d_inode;
- __u32 pos;
+ off_t pos;
long block;
int offset;
int written, c;
@@ -165,13 +188,35 @@
pos = *ppos;
if (pos != *ppos)
return -EINVAL;
+ if (sizeof(inode->i_size) > 4 && pos >
+ ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(sb)])
+ return -EINVAL;
}

/* Check for overflow.. */
- if (pos > (__u32) (pos + count)) {
- count = ~pos; /* == 0xFFFFFFFF - pos */
- if (!count)
- return -EFBIG;
+ if (sizeof(inode->i_size) > 4) {
+ off_t max = ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(sb)];
+
+ if (pos + count > max) {
+ count = max - pos;
+ if (!count)
+ return -EFBIG;
+ }
+ if (((pos + count) >> 32) &&
+ !(sb->u.ext2_sb.s_es->s_feature_ro_compat &
+ cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE))) {
+ /* If this is the first large file created, add a flag
+ to the superblock */
+ sb->u.ext2_sb.s_es->s_feature_ro_compat |=
+ cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE);
+ mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
+ }
+ } else {
+ if (pos > (__u32) (pos + count)) {
+ count = ~pos; /* == 0xFFFFFFFF - pos */
+ if (!count)
+ return -EFBIG;
+ }
}

/*
@@ -260,12 +305,25 @@

/*
* Called when an inode is released. Note that this is different
- * from ext2_open: open gets called at every open, but release
+ * from ext2_file_open: open gets called at every open, but release
* gets called only when /all/ the files are closed.
*/
static int ext2_release_file (struct inode * inode, struct file * filp)
{
- if (filp->f_mode & 2)
+ if (filp->f_mode & FMODE_WRITE)
ext2_discard_prealloc (inode);
return 0;
}
+
+#if BITS_PER_LONG < 64
+/*
+ * Called when an inode is about to be open.
+ * We use this to disallow opening RW large files on 32bit systems.
+ */
+static int ext2_open_file (struct inode * inode, struct file * filp)
+{
+ if (inode->u.ext2_i.i_large_file && (filp->f_mode & FMODE_WRITE))
+ return -EFBIG;
+ return 0;
+}
+#endif
===================================================================
RCS file: fs/ext2/RCS/inode.c,v
retrieving revision 1.1
diff -u -r1.1 fs/ext2/inode.c
--- fs/ext2/inode.c 1998/03/12 06:56:18 1.1
+++ fs/ext2/inode.c 1998/03/13 04:03:00
@@ -12,9 +12,12 @@
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
- * Goal-directed block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
+ * Goal-directed block allocation by Stephen Tweedie
+ * (sct@dcs.ed.ac.uk), 1993, 1998
* Big-endian to little-endian byte-swapping/bitmaps by
* David S. Miller (davem@caip.rutgers.edu), 1995
+ * 64-bit file support on 64-bit platforms by Jakub Jelinek
+ * (jj@sunsite.ms.mff.cuni.cz)
*/

#include <asm/uaccess.h>
@@ -417,9 +420,44 @@
int create, int *err)
{
struct buffer_head * bh;
-
+ int prev_blocks;
+
+ prev_blocks = inode->i_blocks;
+
bh = ext2_getblk (inode, block, create, err);
- if (!bh || buffer_uptodate(bh))
+ if (!bh)
+ return bh;
+
+ /*
+ * If the inode has grown, and this is a directory, then perform
+ * preallocation of a few more blocks to try to keep directory
+ * fragmentation down.
+ */
+ if (create &&
+ S_ISDIR(inode->i_mode) &&
+ inode->i_blocks > prev_blocks &&
+ EXT2_HAS_COMPAT_FEATURE(inode->i_sb,
+ EXT2_FEATURE_COMPAT_DIR_PREALLOC)) {
+ int i;
+ struct buffer_head *tmp_bh;
+
+ for (i = 1;
+ i < EXT2_SB(inode->i_sb)->s_es->s_prealloc_dir_blocks;
+ i++) {
+ /*
+ * ext2_getblk will zero out the contents of the
+ * directory for us
+ */
+ tmp_bh = ext2_getblk(inode, block+i, create, err);
+ if (!tmp_bh) {
+ brelse (bh);
+ return 0;
+ }
+ brelse (tmp_bh);
+ }
+ }
+
+ if (buffer_uptodate(bh))
return bh;
ll_rw_block (READ, 1, &bh);
wait_on_buffer (bh);
@@ -493,7 +531,21 @@
inode->u.ext2_i.i_frag_size = raw_inode->i_fsize;
inode->u.ext2_i.i_osync = 0;
inode->u.ext2_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
- inode->u.ext2_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
+ if (S_ISDIR(inode->i_mode))
+ inode->u.ext2_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
+ else {
+ inode->u.ext2_i.i_dir_acl = 0;
+ if (sizeof(inode->i_size) > 4)
+ inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
+ else {
+ inode->u.ext2_i.i_size_high = raw_inode->i_size_high;
+ if (raw_inode->i_size_high) {
+ inode->u.ext2_i.i_large_file = 1;
+ inode->i_size = (__u32)-1;
+ } else
+ inode->u.ext2_i.i_large_file = 0;
+ }
+ }
inode->u.ext2_i.i_version = le32_to_cpu(raw_inode->i_version);
inode->u.ext2_i.i_block_group = block_group;
inode->u.ext2_i.i_next_alloc_block = 0;
@@ -603,7 +655,12 @@
raw_inode->i_frag = inode->u.ext2_i.i_frag_no;
raw_inode->i_fsize = inode->u.ext2_i.i_frag_size;
raw_inode->i_file_acl = cpu_to_le32(inode->u.ext2_i.i_file_acl);
- raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext2_i.i_dir_acl);
+ if (S_ISDIR(inode->i_mode))
+ raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext2_i.i_dir_acl);
+ else if (sizeof(inode->i_size) > 4)
+ raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32);
+ else
+ raw_inode->i_size_high = inode->u.ext2_i.i_size_high;
raw_inode->i_version = cpu_to_le32(inode->u.ext2_i.i_version);
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev));
===================================================================
RCS file: fs/ext2/RCS/balloc.c,v
retrieving revision 1.1
diff -u -r1.1 fs/ext2/balloc.c
--- fs/ext2/balloc.c 1998/03/12 15:01:11 1.1
+++ fs/ext2/balloc.c 1998/03/12 15:15:00
@@ -455,10 +455,16 @@
*/
#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++) {
+ k < prealloc_goal && (j + k) < EXT2_BLOCKS_PER_GROUP(sb);
+ k++) {
if (sb->dq_op)
if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(1, sb->s_blocksize)))
break;
@@ -595,7 +601,7 @@
bitmap_nr = load_block_bitmap (sb, i);
bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];

- if (!(sb->u.ext2_sb.s_feature_ro_compat &
+ if (!(le32_to_cpu(sb->u.ext2_sb.s_feature_ro_compat) &
EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) ||
(test_root(i, 3) || test_root(i, 5) || test_root(i, 7))) {
if (!ext2_test_bit (0, bh->b_data))
===================================================================
RCS file: fs/ext2/RCS/super.c,v
retrieving revision 1.1
diff -u -r1.1 fs/ext2/super.c
--- fs/ext2/super.c 1998/03/12 15:03:30 1.1
+++ fs/ext2/super.c 1998/03/12 15:04:01
@@ -516,9 +516,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_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 <<
(__s32) le32_to_cpu(es->s_log_frag_size);
if (sb->u.ext2_sb.s_frag_size)

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu