summaryrefslogtreecommitdiffstats
path: root/fs/ext2/file.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-05-07 02:55:41 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-05-07 02:55:41 +0000
commitdcec8a13bf565e47942a1751a9cec21bec5648fe (patch)
tree548b69625b18cc2e88c3e68d0923be546c9ebb03 /fs/ext2/file.c
parent2e0f55e79c49509b7ff70ff1a10e1e9e90a3dfd4 (diff)
o Merge with Linux 2.1.99.
o Fix ancient bug in the ELF loader making ldd crash. o Fix ancient bug in the keyboard code for SGI, SNI and Jazz.
Diffstat (limited to 'fs/ext2/file.c')
-rw-r--r--fs/ext2/file.c97
1 files changed, 83 insertions, 14 deletions
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index 9c61f2f1c..143dc53d5 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -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,23 @@
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 *);
+
+#else
+
+#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)
+};
+
+#endif
/*
* We have mostly NULL's here: the current defaults are ok for
@@ -49,7 +69,11 @@ static struct file_operations ext2_file_operations = {
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 +110,6 @@ static long long ext2_file_lseek(
long long offset,
int origin)
{
- long long retval;
struct inode *inode = file->f_dentry->d_inode;
switch (origin) {
@@ -96,17 +119,20 @@ static long long ext2_file_lseek(
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 BITS_PER_LONG < 64
+ return -EINVAL;
+#else
+ if (offset > ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(inode->i_sb)])
+ return -EINVAL;
+#endif
+ }
+ 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 +154,7 @@ static ssize_t ext2_file_write (struct file * filp, const char * buf,
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,14 +191,39 @@ static ssize_t ext2_file_write (struct file * filp, const char * buf,
pos = *ppos;
if (pos != *ppos)
return -EINVAL;
+#if BITS_PER_LONG >= 64
+ if (pos > ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(sb)])
+ return -EINVAL;
+#endif
}
/* Check for overflow.. */
+#if BITS_PER_LONG < 64
if (pos > (__u32) (pos + count)) {
count = ~pos; /* == 0xFFFFFFFF - pos */
if (!count)
return -EFBIG;
}
+#else
+ {
+ 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);
+ }
+ }
+#endif
/*
* If a file has been opened in synchronous mode, we have to ensure
@@ -219,6 +270,11 @@ static ssize_t ext2_file_write (struct file * filp, const char * buf,
count -= c;
mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 0);
+
+ /* Mark the buffer untouched if we'll move on to the next one.. */
+ if (!(pos & (sb->s_blocksize-1)))
+ clear_bit(BH_Touched, &bh->b_state);
+
if (filp->f_flags & O_SYNC)
bufferlist[buffercount++] = bh;
else
@@ -260,12 +316,25 @@ static ssize_t ext2_file_write (struct file * filp, const char * buf,
/*
* 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_high_size && (filp->f_mode & FMODE_WRITE))
+ return -EFBIG;
+ return 0;
+}
+#endif