diff options
Diffstat (limited to 'fs/isofs/file.c')
-rw-r--r-- | fs/isofs/file.c | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/fs/isofs/file.c b/fs/isofs/file.c new file mode 100644 index 000000000..ee0877d7b --- /dev/null +++ b/fs/isofs/file.c @@ -0,0 +1,260 @@ +/* + * linux/fs/isofs/file.c + * + * (C) 1992, 1993, 1994 Eric Youngdale Modified for ISO9660 filesystem. + * + * (C) 1991 Linus Torvalds - minix filesystem + * + * isofs regular file handling primitives + */ + +#include <asm/segment.h> +#include <asm/system.h> + +#include <linux/sched.h> +#include <linux/iso_fs.h> +#include <linux/fcntl.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/stat.h> +#include <linux/locks.h> + +#include <linux/dirent.h> + +#define NBUF 32 + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +#include <linux/fs.h> +#include <linux/iso_fs.h> + +static int isofs_file_read(struct inode *, struct file *, char *, int); + +/* + * We have mostly NULL's here: the current defaults are ok for + * the isofs filesystem. + */ +static struct file_operations isofs_file_operations = { + NULL, /* lseek - default */ + isofs_file_read, /* read */ + NULL, /* write */ + NULL, /* readdir - bad */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + generic_mmap, /* mmap */ + NULL, /* no special open is needed */ + NULL, /* release */ + NULL /* fsync */ +}; + +struct inode_operations isofs_file_inode_operations = { + &isofs_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 */ + isofs_bmap, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + +/* This is a heuristic to determine if a file is text of binary. If it + * is text, then we translate all 0x0d characters to spaces. If the 0x0d + * character is not preceded or followed by a 0x0a, then we turn it into + * a 0x0a. A control-Z is also turned into a linefeed. + */ + +static inline void unixify_to_fs(char * outbuf, char * buffer, int chars, + int mode) +{ + char outchar; + + while(chars--){ + outchar = *buffer; + if(outchar == 0x1a) outchar = 0x0a; + if(outchar == 0x0d){ + if(mode == ISOFS_FILE_TEXT_M) outchar = 0x0a; + if(mode == ISOFS_FILE_TEXT) outchar = ' '; + } + put_fs_byte(outchar, outbuf++); + buffer++; + } +} + +/*This function determines if a given file has a DOS-like text format or not*/ + +static void isofs_determine_filetype(struct inode * inode) +{ + int block; + int result, i; + struct buffer_head * bh; + unsigned char * pnt; + + block = isofs_bmap(inode,0); + if (block && (bh = bread(inode->i_dev,block, ISOFS_BUFFER_SIZE(inode)))) { + pnt = (unsigned char *) bh->b_data; + result = ISOFS_FILE_TEXT_M; + for(i=0;i<(inode->i_size < ISOFS_BUFFER_SIZE(inode) ? inode->i_size : ISOFS_BUFFER_SIZE(inode)); + i++,pnt++){ + if(*pnt & 0x80) {result = ISOFS_FILE_BINARY; break;}; + if(*pnt >= 0x20 || *pnt == 0x1a) continue; + if(*pnt == 0x0a) {result = ISOFS_FILE_TEXT; continue;}; + if(*pnt >= 0x9 && *pnt <= 0x0d) continue; + result = ISOFS_FILE_BINARY; + break; + } + brelse(bh); + inode->u.isofs_i.i_file_format = result; + } +} + +static int isofs_file_read(struct inode * inode, struct file * filp, char * buf, int count) +{ + int read,left,chars; + int block, blocks, offset, total_blocks; + int bhrequest; + int ra_blocks, max_block, nextblock; + struct buffer_head ** bhb, ** bhe; + struct buffer_head * bhreq[NBUF]; + struct buffer_head * buflist[NBUF]; + + if (!inode) { + printk("isofs_file_read: inode = NULL\n"); + return -EINVAL; + } + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) { + printk("isofs_file_read: mode = %07o\n",inode->i_mode); + return -EINVAL; + } + if (inode->u.isofs_i.i_file_format == ISOFS_FILE_UNKNOWN) + isofs_determine_filetype(inode); + if (filp->f_pos > inode->i_size) + left = 0; + else + left = inode->i_size - filp->f_pos; + if (left > count) + left = count; + if (left <= 0) + return 0; + read = 0; + block = filp->f_pos >> ISOFS_BUFFER_BITS(inode); + offset = (inode->u.isofs_i.i_first_extent + filp->f_pos) + & (ISOFS_BUFFER_SIZE(inode)-1); + blocks = (left + offset + ISOFS_BUFFER_SIZE(inode) - 1) / ISOFS_BUFFER_SIZE(inode); + bhb = bhe = buflist; + + ra_blocks = read_ahead[MAJOR(inode->i_dev)] / (BLOCK_SIZE >> 9); + if(ra_blocks > blocks) blocks = ra_blocks; + + /* + * this is for stopping read ahead at EOF. It's important for + * reading PhotoCD's, because they have many small data tracks instead + * of one big. And between two data-tracks are some unreadable sectors. + * A read ahead after a EOF may try to read such an unreadable sector. + * kraxel@cs.tu-berlin.de (Gerd Knorr) + */ + total_blocks = (inode->i_size + (1 << ISOFS_BUFFER_BITS(inode)) - 1) + >> ISOFS_BUFFER_BITS(inode); + if (block + blocks > total_blocks) + blocks = total_blocks - block; + + max_block = (inode->i_size + BLOCK_SIZE - 1)/BLOCK_SIZE; + nextblock = -1; + + /* We do this in a two stage process. We first try and request + as many blocks as we can, then we wait for the first one to + complete, and then we try and 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; + while (blocks) { + int uptodate; + --blocks; + *bhb = getblk(inode->i_dev,isofs_bmap(inode, block++), ISOFS_BUFFER_SIZE(inode)); + uptodate = 1; + if (*bhb && !(*bhb)->b_uptodate) { + 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) {/* test for valid buffer */ + wait_on_buffer(*bhe); + if (!(*bhe)->b_uptodate) { + brelse(*bhe); + if (++bhe == &buflist[NBUF]) + bhe = buflist; + left = 0; + break; + } + } + + if (left < ISOFS_BUFFER_SIZE(inode) - offset) + chars = left; + else + chars = ISOFS_BUFFER_SIZE(inode) - offset; + filp->f_pos += chars; + left -= chars; + read += chars; + if (*bhe) { + if (inode->u.isofs_i.i_file_format == ISOFS_FILE_TEXT || + inode->u.isofs_i.i_file_format == ISOFS_FILE_TEXT_M) + unixify_to_fs(buf, offset+(*bhe)->b_data, chars, + inode->u.isofs_i.i_file_format); + else + memcpy_tofs(buf,offset+(*bhe)->b_data,chars); + brelse(*bhe); + buf += chars; + } else { + while (chars-->0) + put_fs_byte(0,buf++); + } + offset = 0; + if (++bhe == &buflist[NBUF]) + bhe = buflist; + } while( bhe != bhb && (*bhe == 0 || !(*bhe)->b_lock) && + (left > 0)); + } while (left > 0); + +/* Release the read-ahead blocks */ + while (bhe != bhb) { + if (*bhe) brelse(*bhe); + if (++bhe == &buflist[NBUF]) + bhe = buflist; + }; + + filp->f_reada = 1; + + if (!read) + return -EIO; + return read; +} |