/* * linux/fs/msdos/file.c * * Written 1992,1993 by Werner Almesberger * * MS-DOS regular file handling primitives */ #include #include #include #include #include #include #include #include #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #define MAX(a,b) (((a) > (b)) ? (a) : (b)) static int msdos_file_read(struct inode *inode,struct file *filp,char *buf, int count); static int msdos_file_write(struct inode *inode,struct file *filp,char *buf, int count); static struct file_operations msdos_file_operations = { NULL, /* lseek - default */ msdos_file_read, /* read */ msdos_file_write, /* write */ NULL, /* readdir - bad */ NULL, /* select - default */ NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open is needed */ NULL, /* release */ file_fsync /* fsync */ }; struct inode_operations msdos_file_inode_operations = { &msdos_file_operations, /* default file operations */ NULL, /* create */ NULL, /* lookup */ NULL, /* link */ NULL, /* unlink */ NULL, /* symlink */ NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ msdos_bmap, /* bmap */ msdos_truncate, /* truncate */ NULL /* permission */ }; /* No bmap for MS-DOS FS' that don't align data at kByte boundaries. */ struct inode_operations msdos_file_inode_operations_no_bmap = { &msdos_file_operations, /* default file operations */ NULL, /* create */ NULL, /* lookup */ NULL, /* link */ NULL, /* unlink */ NULL, /* symlink */ NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ NULL, /* bmap */ msdos_truncate, /* truncate */ NULL /* permission */ }; static int msdos_file_read(struct inode *inode,struct file *filp,char *buf, int count) { char *start; int left,offset,size,sector,cnt; char ch; struct buffer_head *bh; void *data; /* printk("msdos_file_read\n"); */ if (!inode) { printk("msdos_file_read: inode = NULL\n"); return -EINVAL; } if (!S_ISREG(inode->i_mode)) { printk("msdos_file_read: mode = %07o\n",inode->i_mode); return -EINVAL; } if (filp->f_pos >= inode->i_size || count <= 0) return 0; start = buf; while ((left = MIN(inode->i_size-filp->f_pos,count-(buf-start))) > 0){ if (!(sector = msdos_smap(inode,filp->f_pos >> SECTOR_BITS))) break; offset = filp->f_pos & (SECTOR_SIZE-1); if (!(bh = msdos_sread(inode->i_dev,sector,&data))) break; filp->f_pos += (size = MIN(SECTOR_SIZE-offset,left)); if (MSDOS_I(inode)->i_binary) { memcpy_tofs(buf,data+offset,size); buf += size; } else for (cnt = size; cnt; cnt--) { if ((ch = *((char *) data+offset++)) == '\r') size--; else { if (ch != 26) put_fs_byte(ch,buf++); else { filp->f_pos = inode->i_size; brelse(bh); if (start != buf && !IS_RDONLY(inode)) inode->i_atime = CURRENT_TIME; return buf-start; } } } brelse(bh); } if (start == buf) return -EIO; if (!IS_RDONLY(inode)) inode->i_atime = CURRENT_TIME; return buf-start; } static int msdos_file_write(struct inode *inode,struct file *filp,char *buf, int count) { int sector,offset,size,left,written; int error,carry; char *start,*to,ch; struct buffer_head *bh; void *data; if (!inode) { printk("msdos_file_write: inode = NULL\n"); return -EINVAL; } if (!S_ISREG(inode->i_mode)) { printk("msdos_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. */ if (filp->f_flags & O_APPEND) filp->f_pos = inode->i_size; if (count <= 0) return 0; error = carry = 0; for (start = buf; count || carry; count -= size) { while (!(sector = msdos_smap(inode,filp->f_pos >> SECTOR_BITS))) if ((error = msdos_add_cluster(inode)) < 0) break; if (error) { msdos_truncate(inode); break; } offset = filp->f_pos & (SECTOR_SIZE-1); size = MIN(SECTOR_SIZE-offset,MAX(carry,count)); if (!(bh = msdos_sread(inode->i_dev,sector,&data))) { error = -EIO; break; } if (MSDOS_I(inode)->i_binary) { memcpy_fromfs(data+(filp->f_pos & (SECTOR_SIZE-1)), buf,written = size); buf += size; } else { written = left = SECTOR_SIZE-offset; to = (char *) data+(filp->f_pos & (SECTOR_SIZE-1)); if (carry) { *to++ = '\n'; left--; carry = 0; } for (size = 0; size < count && left; size++) { if ((ch = get_fs_byte(buf++)) == '\n') { *to++ = '\r'; left--; } if (!left) carry = 1; else { *to++ = ch; left--; } } written -= left; } filp->f_pos += written; if (filp->f_pos > inode->i_size) { inode->i_size = filp->f_pos; inode->i_dirt = 1; } bh->b_dirt = 1; brelse(bh); } if (start == buf) return error; inode->i_mtime = inode->i_ctime = CURRENT_TIME; MSDOS_I(inode)->i_attrs |= ATTR_ARCH; inode->i_dirt = 1; return buf-start; } void msdos_truncate(struct inode *inode) { int cluster; cluster = SECTOR_SIZE*MSDOS_SB(inode->i_sb)->cluster_size; (void) fat_free(inode,(inode->i_size+(cluster-1))/cluster); MSDOS_I(inode)->i_attrs |= ATTR_ARCH; inode->i_dirt = 1; }