diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
commit | beb116954b9b7f3bb56412b2494b562f02b864b1 (patch) | |
tree | 120e997879884e1b9d93b265221b939d2ef1ade1 /fs/ext2/file.c | |
parent | 908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff) |
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'fs/ext2/file.c')
-rw-r--r-- | fs/ext2/file.c | 292 |
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. */ |