223 lines
5.3 KiB
C
223 lines
5.3 KiB
C
/*
|
|
* linux/fs/msdos/file.c
|
|
*
|
|
* Written 1992,1993 by Werner Almesberger
|
|
*
|
|
* MS-DOS regular file handling primitives
|
|
*/
|
|
|
|
#include <asm/segment.h>
|
|
#include <asm/system.h>
|
|
|
|
#include <linux/sched.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/msdos_fs.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/fcntl.h>
|
|
#include <linux/stat.h>
|
|
|
|
#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;
|
|
}
|