/newcnix/cnix-experimental/fs/write.c
C | 342 lines | 268 code | 66 blank | 8 comment | 54 complexity | d00d87d052472ed58b80b3d1f273b1e3 MD5 | raw file
- #include <cnix/string.h>
- #include <cnix/errno.h>
- #include <cnix/devices.h>
- #include <cnix/fs.h>
- #include <cnix/kernel.h>
- #include <asm/system.h>
- #include "ext2/ext2.h"
- static ssize_t dev_write(
- struct inode * inoptr,
- const char * buffer,
- size_t count,
- off_t pos,
- int * error,
- int flags
- )
- {
- dev_t dev;
- ssize_t writed;
-
- writed = 0;
- dev = inoptr->i_ops->realdev(inoptr);
- if(MAJOR(dev) < get_dev_nr() && dev_table[MAJOR(dev)].write)
- writed = dev_table[MAJOR(dev)].write(
- MINOR(dev),
- buffer,
- count,
- pos,
- error,
- flags
- );
- return writed;
- }
- extern ssize_t socket_write(
- struct inode * inoptr,
- const char * buffer,
- size_t count,
- int * error,
- int falgs
- );
- ssize_t do_write(int fd, const char * buffer, size_t count)
- {
- int ret, error = 0;
- off_t pos, inblock;
- unsigned long block;
- ssize_t writed, writing;
- struct filp * fp;
- struct inode * inoptr;
- struct buf_head * bh;
- unsigned long flags;
- mode_t mode;
- int blocksize;
- struct super_block * sb;
- fp = fget(fd);
- if(!fp)
- DIE("BUG: cannot happen\n");
- if(count < 0)
- DIE("BUG: cannot happen\n");
- if(!count)
- return 0;
-
- if((fp->f_mode & O_ACCMODE) == O_RDONLY)
- return -EINVAL;
- writed = 0;
- inoptr = fp->f_ino;
- if(is_socket(inoptr)){
- writed = socket_write(
- inoptr,
- buffer,
- count,
- &error,
- fp->f_mode | current->fflags[fd]
- );
- if(error < 0)
- return error;
- return writed;
- }
- if(is_pipe(inoptr)){
- int writep, count1;
- unsigned char * ibuffer;
- count1 = count;
- check_again:
- if(!inoptr->i_buddy){
- sendsig(current, SIGPIPE);
- return -EPIPE;
- }
- if(inoptr->i_pipesize >= 4096){
- lockb_all(flags);
- current->sleep_spot = &inoptr->i_wait;
- sleepon(&inoptr->i_wait);
- if(anysig(current)){
- unlockb_all(flags);
- return -EINTR;
- }
- unlockb_all(flags);
- goto check_again;
- }
- if(count > 4096 - inoptr->i_pipesize)
- count = 4096 - inoptr->i_pipesize;
- writep = inoptr->i_writep;
- ibuffer = inoptr->i_buffer;
- while(count > 0){
- writing = 4096 - writep;
- if(writing > count)
- writing = count;
- ret = memcpy_from_user(
- &ibuffer[writep], &buffer[writed], writing
- );
- if(ret < 0){
- wakeup(&inoptr->i_wait);
- select_wakeup(&inoptr->i_select, OPTYPE_READ);
- return -EFAULT;
- }
- writep += writing;
- if(writep >= 4096)
- writep = 0;
- inoptr->i_writep = writep;
- inoptr->i_pipesize += writing;
- writed += writing;
- count -= writing;
- }
- wakeup(&inoptr->i_wait);
- select_wakeup(&inoptr->i_select, OPTYPE_READ);
- if(writed < count1){
- count = count1 - writed;
- goto check_again;
- }
- return writed;
- }
- mode = inoptr->i_ops->mode(inoptr);
- if(S_ISDIR(mode))
- return -EISDIR;
- if(S_ISBLK(mode) || S_ISCHR(mode)){
- writed = dev_write(
- inoptr,
- buffer,
- count,
- fp->f_pos,
- &error,
- fp->f_mode | current->fflags[fd]
- );
- fp->f_pos += writed;
- if(error < 0)
- return error;
- return writed;
- }else if(S_ISFIFO(mode)){
- inoptr->i_count++;
- iput(inoptr);
- return -EINVAL;
- }
- sb = inoptr->i_sb;
- pos = fp->f_pos;
- blocksize = inoptr->i_ops->blocksize(inoptr);
-
- inblock = pos % blocksize;
- if(inblock){
- if(pos >= inoptr->i_ops->max_filesize(inoptr))
- return -EFBIG;
- writing = blocksize - inblock;
- if(writing > count)
- writing = count;
- block = inoptr->i_ops->bmap(inoptr, pos);
- if(block == NOBLK){
- // xxx: error if block size is not equal to zone
- block = sb->s_filesystem->balloc(sb);
- if(block == NOBLK){
- printk("out of disk space\n");
- goto errout;
- }
- bh = getblk(inoptr->i_dev, block, blocksize);
- if(!bh){
- sb->s_filesystem->bfree(sb, block);
- goto errout;
- }
-
- memset(bh->b_data, 0, blocksize);
- if(!inoptr->i_ops->minode(inoptr, pos, block)){
- sb->s_filesystem->bfree(sb, block);
- brelse(bh);
- goto errout;
- }
- }else{
- bh = sd_bread(inoptr->i_dev, block*EXT2_BLOCK_GRAN);
- if(!bh)
- goto errout;
- }
- ret = memcpy_from_user(&bh->b_data[inblock], buffer, writing);
- if(ret < 0){
- brelse(bh);
- return -EFAULT;
- }
- if(fp->f_mode & O_SYNC){ // xxx: need test
- bwrite(bh);
- brelse(bh);
- }else{
- bh->b_flags |= B_DIRTY | B_DONE; // delay write
- brelse(bh);
- }
- writed += writing;
- pos += writing;
- if(pos > inoptr->i_ops->size(inoptr))
- inoptr->i_ops->setsize(inoptr, pos);
- }
- //if(pos == 49152)
- // printk("here is the fish: writed: %d count: %d\n",writed,count);
- while(writed < count){
- if(pos >= inoptr->i_ops->max_filesize(inoptr))
- return -EFBIG;
- if((count - writed) > blocksize)
- writing = blocksize;
- else
- writing = count - writed;
- block = inoptr->i_ops->bmap(inoptr, pos);
- //if(pos==49152)
- // printk("where is the fish? ino: %x block: %u\n",fp->f_ino,block);
- if(block == NOBLK){
- // xxx: error if block size is not equal to zone
- block = sb->s_filesystem->balloc(sb);
- if(block == NOBLK){
- printk("out of disk space\n");
- goto errout;
- }
- bh = getblk(inoptr->i_dev, block, blocksize);
- if(!bh){
- sb->s_filesystem->bfree(sb, block);
- goto errout;
- }
- memset(bh->b_data, 0, blocksize);
- if(!inoptr->i_ops->minode(inoptr, pos, block)){
- sb->s_filesystem->bfree(sb, block);
- brelse(bh);
- goto errout;
- }
- }else{
- bh = sd_bread(inoptr->i_dev, block*EXT2_BLOCK_GRAN);
- if(!bh)
- goto errout;
- }
- ret = memcpy_from_user(bh->b_data, &buffer[writed], writing);
- if(ret < 0){
- brelse(bh);
- return -EFAULT;
- }
- //if(pos == 49152)
- // printk("buf: %x first bytes: %x\n",bh->b_data,*(unsigned*)(bh->b_data));
- if(fp->f_mode & O_SYNC){ // xxx: need test
- bwrite(bh);
- brelse(bh);
- }else{
- bh->b_flags |= B_DIRTY | B_DONE; // delay write
- brelse(bh);
- }
- writed += writing;
- pos += writing;
- if(pos > inoptr->i_ops->size(inoptr))
- inoptr->i_ops->setsize(inoptr, pos);
- }
- fp->f_pos = pos;
- if(pos > inoptr->i_ops->size(inoptr))
- inoptr->i_ops->setsize(inoptr, pos);
- if (S_ISREG(mode) || S_ISDIR(mode))
- {
- inoptr->i_update |= MTIME;
- inoptr->i_dirty = 1;
- }
-
- inoptr->i_count++;
- iput(inoptr);
- return writed;
- errout:
- fp->f_pos = pos;
- if(pos > inoptr->i_ops->size(inoptr))
- inoptr->i_ops->setsize(inoptr, pos);
- if (S_ISREG(mode) || S_ISDIR(mode))
- {
- inoptr->i_update |= MTIME;
- inoptr->i_dirty = 1;
- }
- inoptr->i_count++;
- iput(inoptr);
- return -EIO;
- }