summaryrefslogtreecommitdiffstats
path: root/fs/ext2/file.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
committer <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
commitbeb116954b9b7f3bb56412b2494b562f02b864b1 (patch)
tree120e997879884e1b9d93b265221b939d2ef1ade1 /fs/ext2/file.c
parent908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff)
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'fs/ext2/file.c')
-rw-r--r--fs/ext2/file.c292
1 files changed, 88 insertions, 204 deletions
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index 9491942c5..c336a5ba6 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -15,7 +15,7 @@
* ext2 fs regular file handling primitives
*/
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/errno.h>
@@ -25,6 +25,8 @@
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
#define NBUF 32
@@ -34,8 +36,8 @@
#include <linux/fs.h>
#include <linux/ext2_fs.h>
-static int ext2_file_read (struct inode *, struct file *, char *, int);
-static int ext2_file_write (struct inode *, struct file *, char *, int);
+static long long ext2_file_lseek(struct inode *, struct file *, long long, int);
+static long ext2_file_write (struct inode *, struct file *, const char *, unsigned long);
static void ext2_release_file (struct inode *, struct file *);
/*
@@ -43,13 +45,13 @@ static void ext2_release_file (struct inode *, struct file *);
* the ext2 filesystem.
*/
static struct file_operations ext2_file_operations = {
- NULL, /* lseek - default */
- ext2_file_read, /* read */
+ ext2_file_lseek, /* lseek */
+ generic_file_read, /* read */
ext2_file_write, /* write */
NULL, /* readdir - bad */
NULL, /* select - default */
ext2_ioctl, /* ioctl */
- generic_mmap, /* mmap */
+ generic_file_mmap, /* mmap */
NULL, /* no special open is needed */
ext2_release_file, /* release */
ext2_sync_file, /* fsync */
@@ -71,204 +73,74 @@ struct inode_operations ext2_file_inode_operations = {
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
ext2_bmap, /* bmap */
ext2_truncate, /* truncate */
ext2_permission, /* permission */
NULL /* smap */
};
-static int ext2_file_read (struct inode * inode, struct file * filp,
- char * buf, int count)
+/*
+ * Make sure the offset never goes beyond the 32-bit mark..
+ */
+static long long ext2_file_lseek(struct inode *inode,
+ struct file *file,
+ long long offset,
+ int origin)
{
- int read, left, chars;
- int block, blocks, offset;
- int bhrequest, uptodate;
- int clusterblocks;
- struct buffer_head ** bhb, ** bhe;
- struct buffer_head * bhreq[NBUF];
- struct buffer_head * buflist[NBUF];
- struct super_block * sb;
- unsigned int size;
- int err;
+ long long retval;
- if (!inode) {
- printk ("ext2_file_read: inode = NULL\n");
- return -EINVAL;
- }
- sb = inode->i_sb;
- if (!S_ISREG(inode->i_mode)) {
- ext2_warning (sb, "ext2_file_read", "mode = %07o",
- inode->i_mode);
- return -EINVAL;
- }
- offset = filp->f_pos;
- size = inode->i_size;
- if (offset > size)
- left = 0;
- else
- left = size - offset;
- if (left > count)
- left = count;
- if (left <= 0)
- return 0;
- read = 0;
- block = offset >> EXT2_BLOCK_SIZE_BITS(sb);
- offset &= (sb->s_blocksize - 1);
- size = (size + sb->s_blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(sb);
- blocks = (left + offset + sb->s_blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(sb);
- bhb = bhe = buflist;
- if (filp->f_reada) {
- if (blocks < read_ahead[MAJOR(inode->i_dev)] >> (EXT2_BLOCK_SIZE_BITS(sb) - 9))
- blocks = read_ahead[MAJOR(inode->i_dev)] >> (EXT2_BLOCK_SIZE_BITS(sb) - 9);
- if (block + blocks > size)
- blocks = size - block;
+ switch (origin) {
+ case 2:
+ offset += inode->i_size;
+ break;
+ case 1:
+ offset += file->f_pos;
}
-
- /*
- * We do this in a two stage process. We first try and request
- * as many blocks as we can, then we wait for the first one to
- * complete, and then we try and wrap up as many as are actually
- * done. This routine is rather generic, in that it can be used
- * in a filesystem by substituting the appropriate function in
- * for getblk
- *
- * This routine is optimized to make maximum use of the various
- * buffers and caches.
- */
-
- clusterblocks = 0;
-
- do {
- bhrequest = 0;
- uptodate = 1;
- while (blocks) {
- --blocks;
-#if 1
- if(!clusterblocks) clusterblocks = ext2_getcluster(inode, block);
- if(clusterblocks) clusterblocks--;
-#endif
-
- *bhb = ext2_getblk (inode, block++, 0, &err);
- if (*bhb && !(*bhb)->b_uptodate) {
- uptodate = 0;
- bhreq[bhrequest++] = *bhb;
- }
-
- if (++bhb == &buflist[NBUF])
- bhb = buflist;
-
- /*
- * If the block we have on hand is uptodate, go ahead
- * and complete processing
- */
- if (uptodate)
- break;
-
- if (bhb == bhe)
- break;
+ 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;
+ }
+ return retval;
+}
- /*
- * Now request them all
- */
- if (bhrequest)
- ll_rw_block (READ, bhrequest, bhreq);
+static inline void remove_suid(struct inode *inode)
+{
+ unsigned int mode;
- do {
- /*
- * Finish off all I/O that has actually completed
- */
- if (*bhe) {
- wait_on_buffer (*bhe);
- if (!(*bhe)->b_uptodate) { /* read error? */
- brelse(*bhe);
- if (++bhe == &buflist[NBUF])
- bhe = buflist;
- left = 0;
- break;
- }
- }
- if (left < sb->s_blocksize - offset)
- chars = left;
- else
- chars = sb->s_blocksize - offset;
- filp->f_pos += chars;
- left -= chars;
- read += chars;
- if (*bhe) {
-#if 0
-printk("ext2_file_read() #1:\n");
-printk("ext2_file_read() #1.1: ");print_sp();
-#endif
- memcpy_tofs (buf, offset + (*bhe)->b_data,
- chars);
-#if 0
-printk("ext2_file_read() #2: buf == %08x\n", offset+(*bhe)->b_data);
-if(!buf)
- {
-#if 0
- printk("Flushing caches...\n");
- sys_cacheflush(0, ~0, 3);
-#endif
- printk("dumping #1 at %08lx...\n", (unsigned long) buf);
- dump16(buf);
- printk("dumping #2 at %08lx...\n", (unsigned long) (offset+(*bhe)->b_data));
- dump16(offset+(*bhe)->b_data);
-#if 0
- printk("Jumping to 0x0\n");
- __asm__ __volatile__ ("jr\t%0;nop"::"r" (0));
- printk("Freezing ...\n");
- while(1);
-#endif
- }
-/*dump_list(0);*/
-#endif
- brelse (*bhe);
-#if 0
-printk("ext2_file_read() #3:\n");
-#endif
- buf += chars;
- } else {
- while (chars-- > 0)
- put_fs_byte (0, buf++);
- }
- offset = 0;
- if (++bhe == &buflist[NBUF])
- bhe = buflist;
- } while (left > 0 && bhe != bhb && (!*bhe || !(*bhe)->b_lock));
- } while (left > 0);
+ /* set S_IGID if S_IXGRP is set, and always set S_ISUID */
+ mode = (inode->i_mode & S_IXGRP)*(S_ISGID/S_IXGRP) | S_ISUID;
- /*
- * Release the read-ahead blocks
- */
- while (bhe != bhb) {
- brelse (*bhe);
- if (++bhe == &buflist[NBUF])
- bhe = buflist;
- }
- if (!read)
- return -EIO;
- filp->f_reada = 1;
- if (!IS_RDONLY(inode)) {
- inode->i_atime = CURRENT_TIME;
+ /* was any of the uid bits set? */
+ mode &= inode->i_mode;
+ if (mode && suser()) {
+ inode->i_mode &= ~mode;
inode->i_dirt = 1;
}
- return read;
}
-static int ext2_file_write (struct inode * inode, struct file * filp,
- char * buf, int count)
+static long ext2_file_write (struct inode * inode, struct file * filp,
+ const char * buf, unsigned long count)
{
- const loff_t two_gb = 2147483647;
- loff_t pos;
- off_t pos2;
+ __u32 pos;
+ long block;
+ int offset;
int written, c;
struct buffer_head * bh, *bufferlist[NBUF];
- char * p;
struct super_block * sb;
int err;
int i,buffercount,write_error;
+ /* POSIX: mtime/ctime may not change for 0 count */
+ if (!count)
+ return 0;
write_error = buffercount = 0;
if (!inode) {
printk("ext2_file_write: inode = NULL\n");
@@ -283,15 +155,22 @@ static int ext2_file_write (struct inode * inode, struct file * filp,
if (!S_ISREG(inode->i_mode)) {
ext2_warning (sb, "ext2_file_write", "mode = %07o",
- inode->i_mode);
+ (unsigned int) inode->i_mode);
return -EINVAL;
}
- down(&inode->i_sem);
+ remove_suid(inode);
+
if (filp->f_flags & O_APPEND)
pos = inode->i_size;
else
pos = filp->f_pos;
- pos2 = (off_t) pos;
+ /* Check for overflow.. */
+ if (pos > (__u32) (pos + count)) {
+ count = ~pos; /* == 0xFFFFFFFF - pos */
+ if (!count)
+ return -EFBIG;
+ }
+
/*
* If a file has been opened in synchronous mode, we have to ensure
* that meta-data will also be written synchronously. Thus, we
@@ -300,39 +179,42 @@ static int ext2_file_write (struct inode * inode, struct file * filp,
*/
if (filp->f_flags & O_SYNC)
inode->u.ext2_i.i_osync++;
+ block = pos >> EXT2_BLOCK_SIZE_BITS(sb);
+ offset = pos & (sb->s_blocksize - 1);
+ c = sb->s_blocksize - offset;
written = 0;
- while (written < count) {
- if (pos > two_gb) {
- if (!written)
- written = -EFBIG;
- break;
- }
- bh = ext2_getblk (inode, pos2 / sb->s_blocksize, 1, &err);
+ do {
+ bh = ext2_getblk (inode, block, 1, &err);
if (!bh) {
if (!written)
written = err;
break;
}
- c = sb->s_blocksize - (pos2 % sb->s_blocksize);
- if (c > count-written)
- c = count - written;
- if (c != sb->s_blocksize && !bh->b_uptodate) {
+ if (c > count)
+ c = count;
+ if (c != sb->s_blocksize && !buffer_uptodate(bh)) {
ll_rw_block (READ, 1, &bh);
wait_on_buffer (bh);
- if (!bh->b_uptodate) {
+ if (!buffer_uptodate(bh)) {
brelse (bh);
if (!written)
written = -EIO;
break;
}
}
- p = (pos2 % sb->s_blocksize) + bh->b_data;
- pos2 += c;
+ c -= copy_from_user (bh->b_data + offset, buf, c);
+ if (!c) {
+ brelse(bh);
+ if (!written)
+ written = -EFAULT;
+ break;
+ }
+ update_vm_cache(inode, pos, bh->b_data + offset, c);
pos += c;
written += c;
- memcpy_fromfs (p, buf, c);
buf += c;
- bh->b_uptodate = 1;
+ count -= c;
+ mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 0);
if (filp->f_flags & O_SYNC)
bufferlist[buffercount++] = bh;
@@ -342,7 +224,7 @@ static int ext2_file_write (struct inode * inode, struct file * filp,
ll_rw_block(WRITE, buffercount, bufferlist);
for(i=0; i<buffercount; i++){
wait_on_buffer(bufferlist[i]);
- if (!bufferlist[i]->b_uptodate)
+ if (!buffer_uptodate(bufferlist[i]))
write_error=1;
brelse(bufferlist[i]);
}
@@ -350,12 +232,15 @@ static int ext2_file_write (struct inode * inode, struct file * filp,
}
if(write_error)
break;
- }
+ block++;
+ offset = 0;
+ c = sb->s_blocksize;
+ } while (count);
if ( buffercount ){
ll_rw_block(WRITE, buffercount, bufferlist);
for(i=0; i<buffercount; i++){
wait_on_buffer(bufferlist[i]);
- if (!bufferlist[i]->b_uptodate)
+ if (!buffer_uptodate(bufferlist[i]))
write_error=1;
brelse(bufferlist[i]);
}
@@ -364,7 +249,6 @@ static int ext2_file_write (struct inode * inode, struct file * filp,
inode->i_size = pos;
if (filp->f_flags & O_SYNC)
inode->u.ext2_i.i_osync--;
- up(&inode->i_sem);
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
filp->f_pos = pos;
inode->i_dirt = 1;
@@ -372,7 +256,7 @@ static int ext2_file_write (struct inode * inode, struct file * filp,
}
/*
- * Called when a inode is released. Note that this is different
+ * Called when an inode is released. Note that this is different
* from ext2_open: open gets called at every open, but release
* gets called only when /all/ the files are closed.
*/