diff options
Diffstat (limited to 'fs/sysv')
-rw-r--r-- | fs/sysv/file.c | 260 | ||||
-rw-r--r-- | fs/sysv/inode.c | 153 | ||||
-rw-r--r-- | fs/sysv/truncate.c | 13 |
3 files changed, 173 insertions, 253 deletions
diff --git a/fs/sysv/file.c b/fs/sysv/file.c index d60be8fa5..19443f289 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -33,7 +33,51 @@ #include <linux/fs.h> #include <linux/sysv_fs.h> -static ssize_t sysv_file_write(struct file *, const char *, size_t, loff_t *); +static int sysv_writepage (struct file * file, struct page * page) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + unsigned long block; + int *p, nr[PAGE_SIZE/512]; + int i, err, created; + struct buffer_head *bh; + + i = PAGE_SIZE >> inode->i_sb->sv_block_size_bits; + block = page->offset >> inode->i_sb->sv_block_size_bits; + p = nr; + bh = page->buffers; + do { + if (bh && bh->b_blocknr) + *p = bh->b_blocknr; + else + *p = sysv_getblk_block (inode, block, 1, &err, &created); + if (!*p) + return -EIO; + i--; + block++; + p++; + if (bh) + bh = bh->b_this_page; + } while (i > 0); + + /* IO start */ + brw_page(WRITE, page, inode->i_dev, nr, inode->i_sb->sv_block_size, 1); + return 0; +} + +static long sysv_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +{ + return block_write_one_page(file, page, offset, bytes, buf, sysv_getblk_block); +} + +/* + * Write to a file (through the page cache). + */ +static ssize_t +sysv_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + return generic_file_write(file, buf, count, ppos, sysv_write_one_page); +} /* * We have mostly NULLs here: the current defaults are OK for @@ -41,7 +85,7 @@ static ssize_t sysv_file_write(struct file *, const char *, size_t, loff_t *); */ static struct file_operations sysv_file_operations = { NULL, /* lseek - default */ - sysv_file_read, /* read */ + generic_file_read, /* read */ sysv_file_write, /* write */ NULL, /* readdir - bad */ NULL, /* poll - default */ @@ -50,7 +94,10 @@ static struct file_operations sysv_file_operations = { NULL, /* no special open is needed */ NULL, /* flush */ NULL, /* release */ - sysv_sync_file /* fsync */ + sysv_sync_file, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL /* revalidate */ }; struct inode_operations sysv_file_inode_operations = { @@ -67,208 +114,11 @@ struct inode_operations sysv_file_inode_operations = { NULL, /* readlink */ NULL, /* follow_link */ generic_readpage, /* readpage */ - NULL, /* writepage */ + sysv_writepage, /* writepage */ sysv_bmap, /* bmap */ sysv_truncate, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* revalidate */ + block_flushpage, /* flushpage */ }; - -ssize_t sysv_file_read(struct file * filp, char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = filp->f_dentry->d_inode; - struct super_block * sb = inode->i_sb; - ssize_t read,left,chars; - size_t block; - ssize_t blocks, offset; - int bhrequest, uptodate; - struct buffer_head ** bhb, ** bhe; - struct buffer_head * bhreq[NBUF]; - struct buffer_head * buflist[NBUF]; - size_t size; - - if (!inode) { - printk("sysv_file_read: inode = NULL\n"); - return -EINVAL; - } - if (!S_ISREG(inode->i_mode)) { - printk("sysv_file_read: mode = %07o\n",inode->i_mode); - return -EINVAL; - } - offset = *ppos; - 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 >> sb->sv_block_size_bits; - offset &= sb->sv_block_size_1; - size = (size + sb->sv_block_size_1) >> sb->sv_block_size_bits; - blocks = (left + offset + sb->sv_block_size_1) >> sb->sv_block_size_bits; - bhb = bhe = buflist; - if (filp->f_reada) { - blocks += read_ahead[MAJOR(inode->i_dev)] >> (sb->sv_block_size_bits - 9); - if (block + blocks > size) - blocks = size - block; - } - - /* We do this in a two stage process. We first try to request - as many blocks as we can, then we wait for the first one to - complete, and then we try to 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. - */ - - do { - bhrequest = 0; - uptodate = 1; - while (blocks) { - --blocks; - *bhb = sysv_getblk(inode, block++, 0); - if (*bhb && !buffer_uptodate(*bhb)) { - 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; - } - - /* Now request them all */ - if (bhrequest) - ll_rw_block(READ, bhrequest, bhreq); - - do { /* Finish off all I/O that has actually completed */ - if (*bhe) { - wait_on_buffer(*bhe); - if (!buffer_uptodate(*bhe)) { /* read error? */ - brelse(*bhe); - if (++bhe == &buflist[NBUF]) - bhe = buflist; - left = 0; - break; - } - } - if (left < sb->sv_block_size - offset) - chars = left; - else - chars = sb->sv_block_size - offset; - *ppos += chars; - left -= chars; - read += chars; - if (*bhe) { - copy_to_user(buf,offset+(*bhe)->b_data,chars); - brelse(*bhe); - buf += chars; - } else { - while (chars-- > 0) - put_user(0,buf++); - } - offset = 0; - if (++bhe == &buflist[NBUF]) - bhe = buflist; - } while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe))); - } while (left > 0); - -/* 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; - mark_inode_dirty(inode); - } - return read; -} - -static ssize_t sysv_file_write(struct file * filp, const char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = filp->f_dentry->d_inode; - struct super_block * sb = inode->i_sb; - off_t pos; - ssize_t written, c; - struct buffer_head * bh; - char * p; - - if (!inode) { - printk("sysv_file_write: inode = NULL\n"); - return -EINVAL; - } - if (!S_ISREG(inode->i_mode)) { - printk("sysv_file_write: mode = %07o\n",inode->i_mode); - return -EINVAL; - } -/* - * OK, append may not work when many processes are writing at the same time - * but so what. That way leads to madness anyway. - * But we need to protect against simultaneous truncate as we may end up - * writing our data into blocks that have meanwhile been incorporated into - * the freelist, thereby trashing the freelist. - */ - if (filp->f_flags & O_APPEND) - pos = inode->i_size; - else - pos = *ppos; - written = 0; - while (written<count) { - bh = sysv_getblk (inode, pos >> sb->sv_block_size_bits, 1); - if (!bh) { - if (!written) - written = -ENOSPC; - break; - } - c = sb->sv_block_size - (pos & sb->sv_block_size_1); - if (c > count-written) - c = count-written; - if (c != sb->sv_block_size && !buffer_uptodate(bh)) { - ll_rw_block(READ, 1, &bh); - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) { - brelse(bh); - if (!written) - written = -EIO; - break; - } - } - /* now either c==sb->sv_block_size or buffer_uptodate(bh) */ - p = (pos & sb->sv_block_size_1) + bh->b_data; - copy_from_user(p, buf, c); - update_vm_cache(inode, pos, p, c); - pos += c; - if (pos > inode->i_size) { - inode->i_size = pos; - mark_inode_dirty(inode); - } - written += c; - buf += c; - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 0); - brelse(bh); - } - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - *ppos = pos; - mark_inode_dirty(inode); - return written; -} diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index f8d508c3d..d335b5b50 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -657,7 +657,8 @@ int sysv_bmap(struct inode * inode,int block_nr) /* Access selected blocks of regular files (or directories) */ -static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create) +static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create, + int metadata, int *phys_block, int *created) { struct super_block *sb; u32 tmp; @@ -669,31 +670,48 @@ static struct buffer_head * inode_getblk(struct inode * inode, int nr, int creat repeat: tmp = *p; if (tmp) { - result = sv_getblk(sb, inode->i_dev, tmp); - if (tmp == *p) - return result; - brelse(result); - goto repeat; + if (metadata) { + result = sv_getblk(sb, inode->i_dev, tmp); + if (tmp == *p) + return result; + brelse(result); + goto repeat; + } else { + *phys_block = tmp; + return NULL; + } } if (!create) return NULL; tmp = sysv_new_block(sb); if (!tmp) return NULL; - result = sv_getblk(sb, inode->i_dev, tmp); - if (*p) { - sysv_free_block(sb,tmp); - brelse(result); - goto repeat; + if (metadata) { + result = sv_getblk(sb, inode->i_dev, tmp); + if (*p) { + sysv_free_block(sb, tmp); + brelse(result); + goto repeat; + } + } else { + if (*p) { + sysv_free_block(sb, tmp); + goto repeat; + } + *phys_block = tmp; + result = NULL; + *created = 1; } *p = tmp; + inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); return result; } static struct buffer_head * block_getblk(struct inode * inode, - struct buffer_head * bh, int nr, int create) + struct buffer_head * bh, int nr, int create, + int metadata, int *phys_block, int *created) { struct super_block *sb; u32 tmp, block; @@ -717,13 +735,19 @@ repeat: if (sb->sv_convert) block = from_coh_ulong(block); if (tmp) { - result = sv_getblk(sb, bh->b_dev, block); - if (tmp == *p) { + if (metadata) { + result = sv_getblk(sb, bh->b_dev, block); + if (tmp == *p) { + brelse(bh); + return result; + } + brelse(result); + goto repeat; + } else { + *phys_block = tmp; brelse(bh); - return result; + return NULL; } - brelse(result); - goto repeat; } if (!create) { brelse(bh); @@ -734,11 +758,17 @@ repeat: brelse(bh); return NULL; } - result = sv_getblk(sb, bh->b_dev, block); - if (*p) { - sysv_free_block(sb,block); - brelse(result); - goto repeat; + if (metadata) { + result = sv_getblk(sb, bh->b_dev, block); + if (*p) { + sysv_free_block(sb,block); + brelse(result); + goto repeat; + } + } else { + *phys_block = tmp; + result = NULL; + *created = 1; } *p = (sb->sv_convert ? to_coh_ulong(block) : block); mark_buffer_dirty(bh, 1); @@ -746,37 +776,74 @@ repeat: return result; } -struct buffer_head * sysv_getblk(struct inode * inode, unsigned int block, int create) +int sysv_getblk_block(struct inode *inode, long block, int create, + int *err, int *created) { - struct super_block * sb = inode->i_sb; - struct buffer_head * bh; + struct super_block *sb = inode->i_sb; + struct buffer_head *bh, *tmp; + int phys_block; - if (block < 10) - return inode_getblk(inode,block,create); + *err = -EIO; + if (block < 0) { + printk("sysv_getblk: block<0"); + return 0; + } + if (block > sb->sv_ind_per_block_3) { + printk("sysv_getblk: block>big"); + return 0; + } + if (block < 10) { + tmp = inode_getblk(inode, block, create, + 0, &phys_block, created); + goto out; + } block -= 10; if (block < sb->sv_ind_per_block) { - bh = inode_getblk(inode,10,create); - return block_getblk(inode, bh, block, create); + bh = inode_getblk(inode, 10, create, 1, NULL, NULL); + tmp = block_getblk(inode, bh, block, create, + 0, &phys_block, created); + goto out; } block -= sb->sv_ind_per_block; if (block < sb->sv_ind_per_block_2) { - bh = inode_getblk(inode,11,create); - bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_bits, create); - return block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create); + bh = inode_getblk(inode, 11, create, 1, NULL, NULL); + bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_bits, create, + 1, NULL, NULL); + tmp = block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create, + 0, &phys_block, created); + goto out; } block -= sb->sv_ind_per_block_2; - if (block < sb->sv_ind_per_block_3) { - bh = inode_getblk(inode,12,create); - bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_2_bits, create); - bh = block_getblk(inode, bh, (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1, create); - return block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create); - } - if ((int)block<0) { - printk("sysv_getblk: block<0"); - return NULL; + bh = inode_getblk(inode, 12, create, 1, NULL, NULL); + bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_2_bits, create, + 1, NULL, NULL); + bh = block_getblk(inode, bh, + (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1, + create, 1, NULL, NULL); + tmp = block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create, + 0, &phys_block, created); + +out: + *err = 0; + return phys_block; +} + +struct buffer_head *sysv_getblk (struct inode *inode, unsigned int block, int create) +{ + struct buffer_head *tmp = NULL; + int phys_block; + int err, created; + + phys_block = sysv_getblk_block(inode, block, create, &err, &created); + if (phys_block) { + tmp = getblk(inode->i_dev, phys_block, BLOCK_SIZE); + if (created) { + memset(tmp->b_data, 0, BLOCK_SIZE); + mark_buffer_uptodate(tmp, 1); + mark_buffer_dirty(tmp, 1); + } } - printk("sysv_getblk: block>big"); - return NULL; + return tmp; } struct buffer_head * sysv_file_bread(struct inode * inode, int block, int create) diff --git a/fs/sysv/truncate.c b/fs/sysv/truncate.c index c318648a9..a8c0e0745 100644 --- a/fs/sysv/truncate.c +++ b/fs/sysv/truncate.c @@ -35,6 +35,9 @@ * general case (size = XXX). I hope. */ +#define DATA_BUFFER_USED(bh) \ + ((bh->b_count > 1) || buffer_locked(bh)) + /* We throw away any data beyond inode->i_size. */ static int trunc_direct(struct inode * inode) @@ -58,7 +61,7 @@ repeat: brelse(bh); goto repeat; } - if ((bh && bh->b_count != 1) || (block != *p)) { + if ((bh && DATA_BUFFER_USED(bh)) || (block != *p)) { retry = 1; brelse(bh); continue; @@ -115,7 +118,7 @@ repeat: brelse(bh); goto repeat; } - if ((bh && bh->b_count != 1) || (tmp != *ind)) { + if ((bh && DATA_BUFFER_USED(bh)) || (tmp != *ind)) { retry = 1; brelse(bh); continue; @@ -128,7 +131,7 @@ repeat: for (i = 0; i < sb->sv_ind_per_block; i++) if (((sysv_zone_t *) indbh->b_data)[i]) goto done; - if ((indbh->b_count != 1) || (indtmp != *p)) { + if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) { brelse(indbh); return 1; } @@ -185,7 +188,7 @@ static int trunc_dindirect(struct inode * inode, unsigned long offset, sysv_zone for (i = 0; i < sb->sv_ind_per_block; i++) if (((sysv_zone_t *) indbh->b_data)[i]) goto done; - if ((indbh->b_count != 1) || (indtmp != *p)) { + if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) { brelse(indbh); return 1; } @@ -242,7 +245,7 @@ static int trunc_tindirect(struct inode * inode, unsigned long offset, sysv_zone for (i = 0; i < sb->sv_ind_per_block; i++) if (((sysv_zone_t *) indbh->b_data)[i]) goto done; - if ((indbh->b_count != 1) || (indtmp != *p)) { + if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) { brelse(indbh); return 1; } |