long block_write(struct inode * inode, struct file * filp,
const char * buf, unsigned long count)
{
int blocksize, blocksize_bits, i, j, buffercount,write_error;
int block, blocks;
loff_t offset;
int chars;
int written = 0;
int cluster_list[MAX_BUF_PER_PAGE];
struct buffer_head * bhlist[NBUF];
int blocks_per_cluster;
unsigned int size;
kdev_t dev;
struct buffer_head * bh, *bufferlist[NBUF];
register char * p;
int excess;
write_error = buffercount = 0;
dev = inode->i_rdev;
if ( is_read_only( inode->i_rdev ))
return -EPERM;
blocksize = BLOCK_SIZE;
if (blksize_size[MAJOR(dev)] && blksize_size[MAJOR(dev)][MINOR(dev)])
blocksize = blksize_size[MAJOR(dev)][MINOR(dev)];
i = blocksize;
blocksize_bits = 0;
while(i != 1) {
blocksize_bits++;
i »= 1;
}
blocks_per_cluster = PAGE_SIZE / blocksize;
block = filp->f_pos » blocksize_bits;
offset = filp->f_pos & (blocksize-1);
if (blk_size[MAJOR(dev)])
size = ((loff_t) blk_size[MAJOR(dev)][MINOR(dev)] « BLOCK_SIZE_BITS) » blocksize_bits;
else
size = INT_MAX;
while (count>0) {
if (block >= size)
return written ? written : -ENOSPC;
chars = blocksize - offset;
if (chars > count)
chars=count;
#if 0
if (chars == blocksize)
bh = getblk(dev, block, blocksize);
else
bh = breada(dev,block,block+1,block+2,-1);
#else
for(i=0; i<blocks_per_cluster; i++) cluster_list[i] = block+i;
if((block % blocks_per_cluster) == 0)
generate_cluster(dev, cluster_list, blocksize);
bh = getblk(dev, block, blocksize);
if (chars != blocksize && !buffer_uptodate(bh)) {
if(!filp->f_reada ||
!read_ahead[MAJOR(dev)]) {
/* We do this to force the read of a single buffer */
brelse(bh);
bh = bread(dev,block,blocksize);
} else {
/* Read-ahead before write */
blocks = read_ahead[MAJOR(dev)] / (blocksize » 9) / 2;
if (block + blocks > size) blocks = size - block;
if (blocks > NBUF) blocks=NBUF;
excess = (block + blocks)% blocks_per_cluster;
if ( blocks > excess )
blocks -= excess;
bhlist[0] = bh;
for(i=1; i<blocks; i++){ if(((i+block)% blocks_per_cluster) == 0) {
for(j=0; j<blocks_per_cluster; j++) cluster_list[j] = block+i+j;
generate_cluster(dev, cluster_list, blocksize);
};
bhlist[i] = getblk (dev, block+i, blocksize);
if(!bhlist[i]){
while(i >= 0) brelse(bhlist[i-]);
return written ? written : -EIO;
};
};
ll_rw_blOck(READ, blocks, bhlist);
for(i=1; i<blocks; i++) brelse(bhlist[i]);
wait_on_buffer(bh);
};
};
#endif
block++;
if (!bh)
return written ? written : -EIO;
p = offset + bh->b_data;
offset = 0;
filp->f_pos += chars;
written += chars;
count -= chars;
copy_from_user(p,buf,chars);
p += chars;
buf += chars;
mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 0);
if (filp->f_flags & O_SYNC)
bufferlist[buffercount++] = bh;
else
brelse(bh);
if (buffercount == NBUF){
ll_rw_bLock(WRITE, buffercount, bufferlist);
for(i=0; i<buffercount; i++){
wait_on_buffer(bufferlist[i]);
if (!buffer_uptodate(bufferlist[i]))
write_error=1;
brelse(bufferlist[i]);
}
buffercount=0;
}
if(write_error)
break;
}
if ( buffercount ){
ll_rw_block(WRITE, buffercount, bufferlist);
for(i=0; i<buffercount; i++){
wait_on_buffer(bufferlist[i]);
if (!buffer_uptodate(bufferlist[i]))
write_error=1;
brelse(bufferlist[i]);
}
}
filp->f_reada = 1;
if(write_error)
return -EIO;
return written;
}