OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k/trunk/linux/linux-2.4/fs/isofs
    from Rev 1275 to Rev 1765
    Reverse comparison

Rev 1275 → Rev 1765

/dir.c
0,0 → 1,266
/*
* linux/fs/isofs/dir.c
*
* (C) 1992, 1993, 1994 Eric Youngdale Modified for ISO 9660 filesystem.
*
* (C) 1991 Linus Torvalds - minix filesystem
*
* Steve Beynon : Missing last directory entries fixed
* (stephen@askone.demon.co.uk) : 21st June 1996
*
* isofs directory handling functions
*/
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/iso_fs.h>
#include <linux/kernel.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/locks.h>
#include <linux/config.h>
 
#include <asm/uaccess.h>
 
static int isofs_readdir(struct file *, void *, filldir_t);
 
struct file_operations isofs_dir_operations =
{
read: generic_read_dir,
readdir: isofs_readdir,
};
 
/*
* directories can handle most operations...
*/
struct inode_operations isofs_dir_inode_operations =
{
lookup: isofs_lookup,
};
 
int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode)
{
char * old = de->name;
int len = de->name_len[0];
int i;
for (i = 0; i < len; i++) {
unsigned char c = old[i];
if (!c)
break;
 
if (c >= 'A' && c <= 'Z')
c |= 0x20; /* lower case */
 
/* Drop trailing '.;1' (ISO 9660:1988 7.5.1 requires period) */
if (c == '.' && i == len - 3 && old[i + 1] == ';' && old[i + 2] == '1')
break;
 
/* Drop trailing ';1' */
if (c == ';' && i == len - 2 && old[i + 1] == '1')
break;
 
/* Convert remaining ';' to '.' */
if (c == ';')
c = '.';
 
new[i] = c;
}
return i;
}
 
/* Acorn extensions written by Matthew Wilcox <willy@bofh.ai> 1998 */
int get_acorn_filename(struct iso_directory_record * de,
char * retname, struct inode * inode)
{
int std;
unsigned char * chr;
int retnamlen = isofs_name_translate(de, retname, inode);
if (retnamlen == 0) return 0;
std = sizeof(struct iso_directory_record) + de->name_len[0];
if (std & 1) std++;
if ((*((unsigned char *) de) - std) != 32) return retnamlen;
chr = ((unsigned char *) de) + std;
if (strncmp(chr, "ARCHIMEDES", 10)) return retnamlen;
if ((*retname == '_') && ((chr[19] & 1) == 1)) *retname = '!';
if (((de->flags[0] & 2) == 0) && (chr[13] == 0xff)
&& ((chr[12] & 0xf0) == 0xf0))
{
retname[retnamlen] = ',';
sprintf(retname+retnamlen+1, "%3.3x",
((chr[12] & 0xf) << 8) | chr[11]);
retnamlen += 4;
}
return retnamlen;
}
 
/*
* This should _really_ be cleaned up some day..
*/
static int do_isofs_readdir(struct inode *inode, struct file *filp,
void *dirent, filldir_t filldir,
char * tmpname, struct iso_directory_record * tmpde)
{
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
unsigned int block, offset;
int inode_number = 0; /* Quiet GCC */
struct buffer_head *bh = NULL;
int len;
int map;
int high_sierra;
int first_de = 1;
char *p = NULL; /* Quiet GCC */
struct iso_directory_record *de;
 
offset = filp->f_pos & (bufsize - 1);
block = filp->f_pos >> bufbits;
high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
 
while (filp->f_pos < inode->i_size) {
int de_len;
 
if (!bh) {
bh = isofs_bread(inode, block);
if (!bh)
return 0;
}
 
de = (struct iso_directory_record *) (bh->b_data + offset);
if (first_de)
inode_number = (bh->b_blocknr << bufbits) + offset;
 
de_len = *(unsigned char *) de;
 
/* If the length byte is zero, we should move on to the next
CDROM sector. If we are at the end of the directory, we
kick out of the while loop. */
 
if (de_len == 0) {
brelse(bh);
bh = NULL;
filp->f_pos = (filp->f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
block = filp->f_pos >> bufbits;
offset = 0;
continue;
}
 
offset += de_len;
 
/* Make sure we have a full directory entry */
if (offset >= bufsize) {
int slop = bufsize - offset + de_len;
memcpy(tmpde, de, slop);
offset &= bufsize - 1;
block++;
brelse(bh);
bh = NULL;
if (offset) {
bh = isofs_bread(inode, block);
if (!bh)
return 0;
memcpy((void *) tmpde + slop, bh->b_data, offset);
}
de = tmpde;
}
 
if (de->flags[-high_sierra] & 0x80) {
first_de = 0;
filp->f_pos += de_len;
continue;
}
first_de = 1;
 
/* Handle the case of the '.' directory */
if (de->name_len[0] == 1 && de->name[0] == 0) {
if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0)
break;
filp->f_pos += de_len;
continue;
}
 
len = 0;
 
/* Handle the case of the '..' directory */
if (de->name_len[0] == 1 && de->name[0] == 1) {
inode_number = filp->f_dentry->d_parent->d_inode->i_ino;
if (filldir(dirent, "..", 2, filp->f_pos, inode_number, DT_DIR) < 0)
break;
filp->f_pos += de_len;
continue;
}
 
/* Handle everything else. Do name translation if there
is no Rock Ridge NM field. */
if (inode->i_sb->u.isofs_sb.s_unhide == 'n') {
/* Do not report hidden or associated files */
if (de->flags[-high_sierra] & 5) {
filp->f_pos += de_len;
continue;
}
}
 
map = 1;
if (inode->i_sb->u.isofs_sb.s_rock) {
len = get_rock_ridge_filename(de, tmpname, inode);
if (len != 0) { /* may be -1 */
p = tmpname;
map = 0;
}
}
if (map) {
#ifdef CONFIG_JOLIET
if (inode->i_sb->u.isofs_sb.s_joliet_level) {
len = get_joliet_filename(de, tmpname, inode);
p = tmpname;
} else
#endif
if (inode->i_sb->u.isofs_sb.s_mapping == 'a') {
len = get_acorn_filename(de, tmpname, inode);
p = tmpname;
} else
if (inode->i_sb->u.isofs_sb.s_mapping == 'n') {
len = isofs_name_translate(de, tmpname, inode);
p = tmpname;
} else {
p = de->name;
len = de->name_len[0];
}
}
if (len > 0) {
if (filldir(dirent, p, len, filp->f_pos, inode_number, DT_UNKNOWN) < 0)
break;
}
filp->f_pos += de_len;
 
continue;
}
if (bh) brelse(bh);
return 0;
}
 
/*
* Handle allocation of temporary space for name translation and
* handling split directory entries.. The real work is done by
* "do_isofs_readdir()".
*/
static int isofs_readdir(struct file *filp,
void *dirent, filldir_t filldir)
{
int result;
char * tmpname;
struct iso_directory_record * tmpde;
struct inode *inode = filp->f_dentry->d_inode;
 
tmpname = (char *) __get_free_page(GFP_KERNEL);
if (!tmpname)
return -ENOMEM;
tmpde = (struct iso_directory_record *) (tmpname+1024);
 
result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde);
 
free_page((unsigned long) tmpname);
return result;
}
/rock.c
0,0 → 1,612
/*
* linux/fs/isofs/rock.c
*
* (C) 1992, 1993 Eric Youngdale
*
* Rock Ridge Extensions to iso9660
*/
 
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/iso_fs.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/smp_lock.h>
#include <asm/page.h>
 
#include "rock.h"
 
/* These functions are designed to read the system areas of a directory record
* and extract relevant information. There are different functions provided
* depending upon what information we need at the time. One function fills
* out an inode structure, a second one extracts a filename, a third one
* returns a symbolic link name, and a fourth one returns the extent number
* for the file. */
 
#define SIG(A,B) ((A) | ((B) << 8)) /* isonum_721() */
 
 
/* This is a way of ensuring that we have something in the system
use fields that is compatible with Rock Ridge */
#define CHECK_SP(FAIL) \
if(rr->u.SP.magic[0] != 0xbe) FAIL; \
if(rr->u.SP.magic[1] != 0xef) FAIL; \
inode->i_sb->u.isofs_sb.s_rock_offset=rr->u.SP.skip;
/* We define a series of macros because each function must do exactly the
same thing in certain places. We use the macros to ensure that everything
is done correctly */
 
#define CONTINUE_DECLS \
int cont_extent = 0, cont_offset = 0, cont_size = 0; \
void * buffer = 0
 
#define CHECK_CE \
{cont_extent = isonum_733(rr->u.CE.extent); \
cont_offset = isonum_733(rr->u.CE.offset); \
cont_size = isonum_733(rr->u.CE.size);}
 
#define SETUP_ROCK_RIDGE(DE,CHR,LEN) \
{LEN= sizeof(struct iso_directory_record) + DE->name_len[0]; \
if(LEN & 1) LEN++; \
CHR = ((unsigned char *) DE) + LEN; \
LEN = *((unsigned char *) DE) - LEN; \
if (inode->i_sb->u.isofs_sb.s_rock_offset!=-1) \
{ \
LEN-=inode->i_sb->u.isofs_sb.s_rock_offset; \
CHR+=inode->i_sb->u.isofs_sb.s_rock_offset; \
if (LEN<0) LEN=0; \
} \
}
 
#define MAYBE_CONTINUE(LABEL,DEV) \
{if (buffer) kfree(buffer); \
if (cont_extent){ \
int block, offset, offset1; \
struct buffer_head * pbh; \
buffer = kmalloc(cont_size,GFP_KERNEL); \
if (!buffer) goto out; \
block = cont_extent; \
offset = cont_offset; \
offset1 = 0; \
pbh = sb_bread(DEV->i_sb, block); \
if(pbh){ \
memcpy(buffer + offset1, pbh->b_data + offset, cont_size - offset1); \
brelse(pbh); \
chr = (unsigned char *) buffer; \
len = cont_size; \
cont_extent = 0; \
cont_size = 0; \
cont_offset = 0; \
goto LABEL; \
} \
printk("Unable to read rock-ridge attributes\n"); \
}}
 
/* This is the inner layer of the get filename routine, and is called
for each system area and continuation record related to the file */
 
int find_rock_ridge_relocation(struct iso_directory_record * de,
struct inode * inode) {
int flag;
int len;
int retval;
unsigned char * chr;
CONTINUE_DECLS;
flag = 0;
/* If this is a '..' then we are looking for the parent, otherwise we
are looking for the child */
if (de->name[0]==1 && de->name_len[0]==1) flag = 1;
/* Return value if we do not find appropriate record. */
retval = isonum_733 (de->extent);
if (!inode->i_sb->u.isofs_sb.s_rock) return retval;
 
SETUP_ROCK_RIDGE(de, chr, len);
repeat:
{
int rrflag, sig;
struct rock_ridge * rr;
while (len > 1){ /* There may be one byte for padding somewhere */
rr = (struct rock_ridge *) chr;
if (rr->len == 0) goto out; /* Something got screwed up here */
sig = isonum_721(chr);
chr += rr->len;
len -= rr->len;
 
switch(sig){
case SIG('R','R'):
rrflag = rr->u.RR.flags[0];
if (flag && !(rrflag & RR_PL)) goto out;
if (!flag && !(rrflag & RR_CL)) goto out;
break;
case SIG('S','P'):
CHECK_SP(goto out);
break;
case SIG('C','L'):
if (flag == 0) {
retval = isonum_733(rr->u.CL.location);
goto out;
}
break;
case SIG('P','L'):
if (flag != 0) {
retval = isonum_733(rr->u.PL.location);
goto out;
}
break;
case SIG('C','E'):
CHECK_CE; /* This tells is if there is a continuation record */
break;
default:
break;
}
}
}
MAYBE_CONTINUE(repeat, inode);
return retval;
out:
if(buffer) kfree(buffer);
return retval;
}
 
/* return length of name field; 0: not found, -1: to be ignored */
int get_rock_ridge_filename(struct iso_directory_record * de,
char * retname, struct inode * inode)
{
int len;
unsigned char * chr;
CONTINUE_DECLS;
int retnamlen = 0, truncate=0;
if (!inode->i_sb->u.isofs_sb.s_rock) return 0;
*retname = 0;
 
SETUP_ROCK_RIDGE(de, chr, len);
repeat:
{
struct rock_ridge * rr;
int sig;
while (len > 1){ /* There may be one byte for padding somewhere */
rr = (struct rock_ridge *) chr;
if (rr->len == 0) goto out; /* Something got screwed up here */
sig = isonum_721(chr);
chr += rr->len;
len -= rr->len;
 
switch(sig){
case SIG('R','R'):
if((rr->u.RR.flags[0] & RR_NM) == 0) goto out;
break;
case SIG('S','P'):
CHECK_SP(goto out);
break;
case SIG('C','E'):
CHECK_CE;
break;
case SIG('N','M'):
if (truncate) break;
/*
* If the flags are 2 or 4, this indicates '.' or '..'.
* We don't want to do anything with this, because it
* screws up the code that calls us. We don't really
* care anyways, since we can just use the non-RR
* name.
*/
if (rr->u.NM.flags & 6) {
break;
}
 
if (rr->u.NM.flags & ~1) {
printk("Unsupported NM flag settings (%d)\n",rr->u.NM.flags);
break;
}
if((strlen(retname) + rr->len - 5) >= 254) {
truncate = 1;
break;
}
strncat(retname, rr->u.NM.name, rr->len - 5);
retnamlen += rr->len - 5;
break;
case SIG('R','E'):
if (buffer) kfree(buffer);
return -1;
default:
break;
}
}
}
MAYBE_CONTINUE(repeat,inode);
return retnamlen; /* If 0, this file did not have a NM field */
out:
if(buffer) kfree(buffer);
return 0;
}
 
int parse_rock_ridge_inode_internal(struct iso_directory_record * de,
struct inode * inode,int regard_xa){
int len;
unsigned char * chr;
int symlink_len = 0;
CONTINUE_DECLS;
 
if (!inode->i_sb->u.isofs_sb.s_rock) return 0;
 
SETUP_ROCK_RIDGE(de, chr, len);
if (regard_xa)
{
chr+=14;
len-=14;
if (len<0) len=0;
};
repeat:
{
int cnt, sig;
struct inode * reloc;
struct rock_ridge * rr;
int rootflag;
while (len > 1){ /* There may be one byte for padding somewhere */
rr = (struct rock_ridge *) chr;
if (rr->len == 0) goto out; /* Something got screwed up here */
sig = isonum_721(chr);
chr += rr->len;
len -= rr->len;
switch(sig){
#ifndef CONFIG_ZISOFS /* No flag for SF or ZF */
case SIG('R','R'):
if((rr->u.RR.flags[0] &
(RR_PX | RR_TF | RR_SL | RR_CL)) == 0) goto out;
break;
#endif
case SIG('S','P'):
CHECK_SP(goto out);
break;
case SIG('C','E'):
CHECK_CE;
break;
case SIG('E','R'):
inode->i_sb->u.isofs_sb.s_rock = 1;
printk(KERN_DEBUG "ISO 9660 Extensions: ");
{ int p;
for(p=0;p<rr->u.ER.len_id;p++) printk("%c",rr->u.ER.data[p]);
}
printk("\n");
break;
case SIG('P','X'):
inode->i_mode = isonum_733(rr->u.PX.mode);
inode->i_nlink = isonum_733(rr->u.PX.n_links);
inode->i_uid = isonum_733(rr->u.PX.uid);
inode->i_gid = isonum_733(rr->u.PX.gid);
break;
case SIG('P','N'):
{ int high, low;
high = isonum_733(rr->u.PN.dev_high);
low = isonum_733(rr->u.PN.dev_low);
/*
* The Rock Ridge standard specifies that if sizeof(dev_t) <= 4,
* then the high field is unused, and the device number is completely
* stored in the low field. Some writers may ignore this subtlety,
* and as a result we test to see if the entire device number is
* stored in the low field, and use that.
*/
if((low & ~0xff) && high == 0) {
inode->i_rdev = MKDEV(low >> 8, low & 0xff);
} else {
inode->i_rdev = MKDEV(high, low);
}
}
break;
case SIG('T','F'):
/* Some RRIP writers incorrectly place ctime in the TF_CREATE field.
Try to handle this correctly for either case. */
cnt = 0; /* Rock ridge never appears on a High Sierra disk */
if(rr->u.TF.flags & TF_CREATE)
inode->i_ctime = iso_date(rr->u.TF.times[cnt++].time, 0);
if(rr->u.TF.flags & TF_MODIFY)
inode->i_mtime = iso_date(rr->u.TF.times[cnt++].time, 0);
if(rr->u.TF.flags & TF_ACCESS)
inode->i_atime = iso_date(rr->u.TF.times[cnt++].time, 0);
if(rr->u.TF.flags & TF_ATTRIBUTES)
inode->i_ctime = iso_date(rr->u.TF.times[cnt++].time, 0);
break;
case SIG('S','L'):
{int slen;
struct SL_component * slp;
struct SL_component * oldslp;
slen = rr->len - 5;
slp = &rr->u.SL.link;
inode->i_size = symlink_len;
while (slen > 1){
rootflag = 0;
switch(slp->flags &~1){
case 0:
inode->i_size += slp->len;
break;
case 2:
inode->i_size += 1;
break;
case 4:
inode->i_size += 2;
break;
case 8:
rootflag = 1;
inode->i_size += 1;
break;
default:
printk("Symlink component flag not implemented\n");
}
slen -= slp->len + 2;
oldslp = slp;
slp = (struct SL_component *) (((char *) slp) + slp->len + 2);
 
if(slen < 2) {
if( ((rr->u.SL.flags & 1) != 0)
&& ((oldslp->flags & 1) == 0) ) inode->i_size += 1;
break;
}
 
/*
* If this component record isn't continued, then append a '/'.
*/
if (!rootflag && (oldslp->flags & 1) == 0)
inode->i_size += 1;
}
}
symlink_len = inode->i_size;
break;
case SIG('R','E'):
printk(KERN_WARNING "Attempt to read inode for relocated directory\n");
goto out;
case SIG('C','L'):
inode->u.isofs_i.i_first_extent = isonum_733(rr->u.CL.location);
reloc = iget(inode->i_sb,
(inode->u.isofs_i.i_first_extent <<
inode -> i_sb -> u.isofs_sb.s_log_zone_size));
if (!reloc)
goto out;
inode->i_mode = reloc->i_mode;
inode->i_nlink = reloc->i_nlink;
inode->i_uid = reloc->i_uid;
inode->i_gid = reloc->i_gid;
inode->i_rdev = reloc->i_rdev;
inode->i_size = reloc->i_size;
inode->i_blocks = reloc->i_blocks;
inode->i_atime = reloc->i_atime;
inode->i_ctime = reloc->i_ctime;
inode->i_mtime = reloc->i_mtime;
iput(reloc);
break;
#ifdef CONFIG_ZISOFS
case SIG('Z','F'):
if ( !inode->i_sb->u.isofs_sb.s_nocompress ) {
int algo;
algo = isonum_721(rr->u.ZF.algorithm);
if ( algo == SIG('p','z') ) {
int block_shift = isonum_711(&rr->u.ZF.parms[1]);
if ( block_shift < PAGE_CACHE_SHIFT || block_shift > 17 ) {
printk(KERN_WARNING "isofs: Can't handle ZF block size of 2^%d\n", block_shift);
} else {
/* Note: we don't change i_blocks here */
inode->u.isofs_i.i_file_format = isofs_file_compressed;
/* Parameters to compression algorithm (header size, block size) */
inode->u.isofs_i.i_format_parm[0] = isonum_711(&rr->u.ZF.parms[0]);
inode->u.isofs_i.i_format_parm[1] = isonum_711(&rr->u.ZF.parms[1]);
inode->i_size = isonum_733(rr->u.ZF.real_size);
}
} else {
printk(KERN_WARNING "isofs: Unknown ZF compression algorithm: %c%c\n",
rr->u.ZF.algorithm[0], rr->u.ZF.algorithm[1]);
}
}
break;
#endif
default:
break;
}
}
}
MAYBE_CONTINUE(repeat,inode);
return 0;
out:
if(buffer) kfree(buffer);
return 0;
}
 
static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr, char *plimit)
{
int slen;
int rootflag;
struct SL_component *oldslp;
struct SL_component *slp;
slen = rr->len - 5;
slp = &rr->u.SL.link;
while (slen > 1) {
rootflag = 0;
switch (slp->flags & ~1) {
case 0:
if (slp->len > plimit - rpnt)
return NULL;
memcpy(rpnt, slp->text, slp->len);
rpnt+=slp->len;
break;
case 2:
if (rpnt >= plimit)
return NULL;
*rpnt++='.';
break;
case 4:
if (2 > plimit - rpnt)
return NULL;
*rpnt++='.';
*rpnt++='.';
break;
case 8:
if (rpnt >= plimit)
return NULL;
rootflag = 1;
*rpnt++='/';
break;
default:
printk("Symlink component flag not implemented (%d)\n",
slp->flags);
}
slen -= slp->len + 2;
oldslp = slp;
slp = (struct SL_component *) ((char *) slp + slp->len + 2);
 
if (slen < 2) {
/*
* If there is another SL record, and this component
* record isn't continued, then add a slash.
*/
if ((!rootflag) && (rr->u.SL.flags & 1) &&
!(oldslp->flags & 1)) {
if (rpnt >= plimit)
return NULL;
*rpnt++='/';
}
break;
}
 
/*
* If this component record isn't continued, then append a '/'.
*/
if (!rootflag && !(oldslp->flags & 1)) {
if (rpnt >= plimit)
return NULL;
*rpnt++='/';
}
}
return rpnt;
}
 
int parse_rock_ridge_inode(struct iso_directory_record * de,
struct inode * inode)
{
int result=parse_rock_ridge_inode_internal(de,inode,0);
/* if rockridge flag was reset and we didn't look for attributes
* behind eventual XA attributes, have a look there */
if ((inode->i_sb->u.isofs_sb.s_rock_offset==-1)
&&(inode->i_sb->u.isofs_sb.s_rock==2))
{
result=parse_rock_ridge_inode_internal(de,inode,14);
};
return result;
};
 
/* readpage() for symlinks: reads symlink contents into the page and either
makes it uptodate and returns 0 or returns error (-EIO) */
 
static int rock_ridge_symlink_readpage(struct file *file, struct page *page)
{
struct inode *inode = page->mapping->host;
char *link = kmap(page);
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
struct buffer_head *bh;
char *rpnt = link;
unsigned char *pnt;
struct iso_directory_record *raw_inode;
CONTINUE_DECLS;
int block;
int sig;
int len;
unsigned char *chr;
struct rock_ridge *rr;
 
if (!inode->i_sb->u.isofs_sb.s_rock)
panic ("Cannot have symlink with high sierra variant of iso filesystem\n");
 
block = inode->i_ino >> bufbits;
lock_kernel();
bh = sb_bread(inode->i_sb, block);
if (!bh)
goto out_noread;
 
pnt = (unsigned char *) bh->b_data + (inode->i_ino & (bufsize - 1));
 
raw_inode = (struct iso_directory_record *) pnt;
 
/*
* If we go past the end of the buffer, there is some sort of error.
*/
if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize)
goto out_bad_span;
 
/* Now test for possible Rock Ridge extensions which will override
some of these numbers in the inode structure. */
 
SETUP_ROCK_RIDGE(raw_inode, chr, len);
 
repeat:
while (len > 1) { /* There may be one byte for padding somewhere */
rr = (struct rock_ridge *) chr;
if (rr->len == 0)
goto out; /* Something got screwed up here */
sig = isonum_721(chr);
chr += rr->len;
len -= rr->len;
 
switch (sig) {
case SIG('R', 'R'):
if ((rr->u.RR.flags[0] & RR_SL) == 0)
goto out;
break;
case SIG('S', 'P'):
CHECK_SP(goto out);
break;
case SIG('S', 'L'):
rpnt = get_symlink_chunk(rpnt, rr,
link + (PAGE_SIZE - 1));
if (rpnt == NULL)
goto out;
break;
case SIG('C', 'E'):
/* This tells is if there is a continuation record */
CHECK_CE;
default:
break;
}
}
MAYBE_CONTINUE(repeat, inode);
 
if (rpnt == link)
goto fail;
brelse(bh);
*rpnt = '\0';
unlock_kernel();
SetPageUptodate(page);
kunmap(page);
UnlockPage(page);
return 0;
 
/* error exit from macro */
out:
if (buffer)
kfree(buffer);
goto fail;
out_noread:
printk("unable to read i-node block");
goto fail;
out_bad_span:
printk("symlink spans iso9660 blocks\n");
fail:
brelse(bh);
unlock_kernel();
SetPageError(page);
kunmap(page);
UnlockPage(page);
return -EIO;
}
 
struct address_space_operations isofs_symlink_aops = {
readpage: rock_ridge_symlink_readpage
};
/inode.c
0,0 → 1,1412
/*
* linux/fs/isofs/inode.c
*
* (C) 1991 Linus Torvalds - minix filesystem
* 1992, 1993, 1994 Eric Youngdale Modified for ISO 9660 filesystem.
* 1994 Eberhard Moenkeberg - multi session handling.
* 1995 Mark Dobie - allow mounting of some weird VideoCDs and PhotoCDs.
* 1997 Gordon Chaffee - Joliet CDs
* 1998 Eric Lammerts - ISO 9660 Level 3
*/
 
#include <linux/config.h>
#include <linux/module.h>
 
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/iso_fs.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/locks.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/cdrom.h>
#include <linux/init.h>
#include <linux/nls.h>
#include <linux/ctype.h>
#include <linux/smp_lock.h>
#include <linux/blkdev.h>
 
#include <asm/system.h>
#include <asm/uaccess.h>
 
#include "zisofs.h"
 
/*
* We have no support for "multi volume" CDs, but more and more disks carry
* wrong information within the volume descriptors.
*/
#define IGNORE_WRONG_MULTI_VOLUME_SPECS
#define BEQUIET
 
#ifdef LEAK_CHECK
static int check_malloc = 0;
static int check_bread = 0;
#endif
 
static int isofs_hashi(struct dentry *parent, struct qstr *qstr);
static int isofs_hash(struct dentry *parent, struct qstr *qstr);
static int isofs_dentry_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b);
static int isofs_dentry_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b);
 
#ifdef CONFIG_JOLIET
static int isofs_hashi_ms(struct dentry *parent, struct qstr *qstr);
static int isofs_hash_ms(struct dentry *parent, struct qstr *qstr);
static int isofs_dentry_cmpi_ms(struct dentry *dentry, struct qstr *a, struct qstr *b);
static int isofs_dentry_cmp_ms(struct dentry *dentry, struct qstr *a, struct qstr *b);
#endif
 
static void isofs_put_super(struct super_block *sb)
{
#ifdef CONFIG_JOLIET
if (sb->u.isofs_sb.s_nls_iocharset) {
unload_nls(sb->u.isofs_sb.s_nls_iocharset);
sb->u.isofs_sb.s_nls_iocharset = NULL;
}
#endif
 
#ifdef LEAK_CHECK
printk("Outstanding mallocs:%d, outstanding buffers: %d\n",
check_malloc, check_bread);
#endif
 
return;
}
 
static void isofs_read_inode(struct inode *);
static int isofs_statfs (struct super_block *, struct statfs *);
 
static struct super_operations isofs_sops = {
read_inode: isofs_read_inode,
put_super: isofs_put_super,
statfs: isofs_statfs,
};
 
static struct dentry_operations isofs_dentry_ops[] = {
{
d_hash: isofs_hash,
d_compare: isofs_dentry_cmp,
},
{
d_hash: isofs_hashi,
d_compare: isofs_dentry_cmpi,
},
#ifdef CONFIG_JOLIET
{
d_hash: isofs_hash_ms,
d_compare: isofs_dentry_cmp_ms,
},
{
d_hash: isofs_hashi_ms,
d_compare: isofs_dentry_cmpi_ms,
}
#endif
};
 
struct iso9660_options{
char map;
char rock;
char joliet;
char cruft;
char unhide;
char nocompress;
unsigned char check;
unsigned int blocksize;
mode_t mode;
gid_t gid;
uid_t uid;
char *iocharset;
unsigned char utf8;
/* LVE */
s32 session;
s32 sbsector;
};
 
/*
* Compute the hash for the isofs name corresponding to the dentry.
*/
static int
isofs_hash_common(struct dentry *dentry, struct qstr *qstr, int ms)
{
const char *name;
int len;
 
len = qstr->len;
name = qstr->name;
if (ms) {
while (len && name[len-1] == '.')
len--;
}
 
qstr->hash = full_name_hash(name, len);
 
return 0;
}
 
/*
* Compute the hash for the isofs name corresponding to the dentry.
*/
static int
isofs_hashi_common(struct dentry *dentry, struct qstr *qstr, int ms)
{
const char *name;
int len;
char c;
unsigned long hash;
 
len = qstr->len;
name = qstr->name;
if (ms) {
while (len && name[len-1] == '.')
len--;
}
 
hash = init_name_hash();
while (len--) {
c = tolower(*name++);
hash = partial_name_hash(tolower(c), hash);
}
qstr->hash = end_name_hash(hash);
 
return 0;
}
 
/*
* Case insensitive compare of two isofs names.
*/
static int
isofs_dentry_cmpi_common(struct dentry *dentry,struct qstr *a,struct qstr *b,int ms)
{
int alen, blen;
 
/* A filename cannot end in '.' or we treat it like it has none */
alen = a->len;
blen = b->len;
if (ms) {
while (alen && a->name[alen-1] == '.')
alen--;
while (blen && b->name[blen-1] == '.')
blen--;
}
if (alen == blen) {
if (strnicmp(a->name, b->name, alen) == 0)
return 0;
}
return 1;
}
 
/*
* Case sensitive compare of two isofs names.
*/
static int
isofs_dentry_cmp_common(struct dentry *dentry,struct qstr *a,struct qstr *b,int ms)
{
int alen, blen;
 
/* A filename cannot end in '.' or we treat it like it has none */
alen = a->len;
blen = b->len;
if (ms) {
while (alen && a->name[alen-1] == '.')
alen--;
while (blen && b->name[blen-1] == '.')
blen--;
}
if (alen == blen) {
if (strncmp(a->name, b->name, alen) == 0)
return 0;
}
return 1;
}
 
static int
isofs_hash(struct dentry *dentry, struct qstr *qstr)
{
return isofs_hash_common(dentry, qstr, 0);
}
 
static int
isofs_hashi(struct dentry *dentry, struct qstr *qstr)
{
return isofs_hashi_common(dentry, qstr, 0);
}
 
static int
isofs_dentry_cmp(struct dentry *dentry,struct qstr *a,struct qstr *b)
{
return isofs_dentry_cmp_common(dentry, a, b, 0);
}
 
static int
isofs_dentry_cmpi(struct dentry *dentry,struct qstr *a,struct qstr *b)
{
return isofs_dentry_cmpi_common(dentry, a, b, 0);
}
 
#ifdef CONFIG_JOLIET
static int
isofs_hash_ms(struct dentry *dentry, struct qstr *qstr)
{
return isofs_hash_common(dentry, qstr, 1);
}
 
static int
isofs_hashi_ms(struct dentry *dentry, struct qstr *qstr)
{
return isofs_hashi_common(dentry, qstr, 1);
}
 
static int
isofs_dentry_cmp_ms(struct dentry *dentry,struct qstr *a,struct qstr *b)
{
return isofs_dentry_cmp_common(dentry, a, b, 1);
}
 
static int
isofs_dentry_cmpi_ms(struct dentry *dentry,struct qstr *a,struct qstr *b)
{
return isofs_dentry_cmpi_common(dentry, a, b, 1);
}
#endif
 
static int parse_options(char *options, struct iso9660_options * popt)
{
char *this_char,*value;
 
popt->map = 'n';
popt->rock = 'y';
popt->joliet = 'y';
popt->cruft = 'n';
popt->unhide = 'n';
popt->check = 'u'; /* unset */
popt->nocompress = 0;
popt->blocksize = 1024;
popt->mode = S_IRUGO | S_IXUGO; /* r-x for all. The disc could
be shared with DOS machines so
virtually anything could be
a valid executable. */
popt->gid = 0;
popt->uid = 0;
popt->iocharset = NULL;
popt->utf8 = 0;
popt->session=-1;
popt->sbsector=-1;
if (!options) return 1;
for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
if (strncmp(this_char,"norock",6) == 0) {
popt->rock = 'n';
continue;
}
if (strncmp(this_char,"nojoliet",8) == 0) {
popt->joliet = 'n';
continue;
}
if (strncmp(this_char,"unhide",6) == 0) {
popt->unhide = 'y';
continue;
}
if (strncmp(this_char,"cruft",5) == 0) {
popt->cruft = 'y';
continue;
}
if (strncmp(this_char,"utf8",4) == 0) {
popt->utf8 = 1;
continue;
}
if (strncmp(this_char,"nocompress",10) == 0) {
popt->nocompress = 1;
continue;
}
if ((value = strchr(this_char,'=')) != NULL)
*value++ = 0;
 
#ifdef CONFIG_JOLIET
if (!strcmp(this_char,"iocharset") && value) {
popt->iocharset = value;
while (*value && *value != ',')
value++;
if (value == popt->iocharset)
return 0;
*value = 0;
} else
#endif
if (!strcmp(this_char,"map") && value) {
if (value[0] && !value[1] && strchr("ano",*value))
popt->map = *value;
else if (!strcmp(value,"off")) popt->map = 'o';
else if (!strcmp(value,"normal")) popt->map = 'n';
else if (!strcmp(value,"acorn")) popt->map = 'a';
else return 0;
}
if (!strcmp(this_char,"session") && value) {
char * vpnt = value;
unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
if(ivalue < 0 || ivalue >99) return 0;
popt->session=ivalue+1;
}
if (!strcmp(this_char,"sbsector") && value) {
char * vpnt = value;
unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
if(ivalue < 0 || ivalue >660*512) return 0;
popt->sbsector=ivalue;
}
else if (!strcmp(this_char,"check") && value) {
if (value[0] && !value[1] && strchr("rs",*value))
popt->check = *value;
else if (!strcmp(value,"relaxed")) popt->check = 'r';
else if (!strcmp(value,"strict")) popt->check = 's';
else return 0;
}
else if (!strcmp(this_char,"conv") && value) {
/* no conversion is done anymore;
we still accept the same mount options,
but ignore them */
if (value[0] && !value[1] && strchr("btma",*value)) ;
else if (!strcmp(value,"binary")) ;
else if (!strcmp(value,"text")) ;
else if (!strcmp(value,"mtext")) ;
else if (!strcmp(value,"auto")) ;
else return 0;
}
else if (value &&
(!strcmp(this_char,"block") ||
!strcmp(this_char,"mode") ||
!strcmp(this_char,"uid") ||
!strcmp(this_char,"gid"))) {
char * vpnt = value;
unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
if (*vpnt) return 0;
switch(*this_char) {
case 'b':
if ( ivalue != 512
&& ivalue != 1024
&& ivalue != 2048) return 0;
popt->blocksize = ivalue;
break;
case 'u':
popt->uid = ivalue;
break;
case 'g':
popt->gid = ivalue;
break;
case 'm':
popt->mode = ivalue;
break;
}
}
else return 1;
}
return 1;
}
 
/*
* look if the driver can tell the multi session redirection value
*
* don't change this if you don't know what you do, please!
* Multisession is legal only with XA disks.
* A non-XA disk with more than one volume descriptor may do it right, but
* usually is written in a nowhere standardized "multi-partition" manner.
* Multisession uses absolute addressing (solely the first frame of the whole
* track is #0), multi-partition uses relative addressing (each first frame of
* each track is #0), and a track is not a session.
*
* A broken CDwriter software or drive firmware does not set new standards,
* at least not if conflicting with the existing ones.
*
* emoenke@gwdg.de
*/
#define WE_OBEY_THE_WRITTEN_STANDARDS 1
 
static unsigned int isofs_get_last_session(struct super_block *sb,s32 session )
{
struct cdrom_multisession ms_info;
unsigned int vol_desc_start;
struct block_device *bdev = sb->s_bdev;
int i;
 
vol_desc_start=0;
ms_info.addr_format=CDROM_LBA;
if(session >= 0 && session <= 99) {
struct cdrom_tocentry Te;
Te.cdte_track=session;
Te.cdte_format=CDROM_LBA;
i = ioctl_by_bdev(bdev, CDROMREADTOCENTRY, (unsigned long) &Te);
if (!i) {
printk(KERN_DEBUG "Session %d start %d type %d\n",
session, Te.cdte_addr.lba,
Te.cdte_ctrl&CDROM_DATA_TRACK);
if ((Te.cdte_ctrl&CDROM_DATA_TRACK) == 4)
return Te.cdte_addr.lba;
}
printk(KERN_ERR "Invalid session number or type of track\n");
}
i = ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long) &ms_info);
if(session > 0) printk(KERN_ERR "Invalid session number\n");
#if 0
printk("isofs.inode: CDROMMULTISESSION: rc=%d\n",i);
if (i==0) {
printk("isofs.inode: XA disk: %s\n",ms_info.xa_flag?"yes":"no");
printk("isofs.inode: vol_desc_start = %d\n", ms_info.addr.lba);
}
#endif
if (i==0)
#if WE_OBEY_THE_WRITTEN_STANDARDS
if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */
#endif
vol_desc_start=ms_info.addr.lba;
return vol_desc_start;
}
 
/*
* Initialize the superblock and read the root inode.
*
* Note: a check_disk_change() has been done immediately prior
* to this call, so we don't need to check again.
*/
static struct super_block *isofs_read_super(struct super_block *s, void *data,
int silent)
{
kdev_t dev = s->s_dev;
struct buffer_head * bh = NULL, *pri_bh = NULL;
struct hs_primary_descriptor * h_pri = NULL;
struct iso_primary_descriptor * pri = NULL;
struct iso_supplementary_descriptor *sec = NULL;
struct iso_directory_record * rootp;
int joliet_level = 0;
int high_sierra;
int iso_blknum, block;
int orig_zonesize;
int table;
unsigned int blocksize, blocksize_bits;
unsigned int vol_desc_start;
unsigned long first_data_zone;
struct inode * inode;
struct iso9660_options opt;
 
if (!parse_options((char *) data, &opt))
goto out_unlock;
 
#if 0
printk("map = %c\n", opt.map);
printk("rock = %c\n", opt.rock);
printk("joliet = %c\n", opt.joliet);
printk("check = %c\n", opt.check);
printk("cruft = %c\n", opt.cruft);
printk("unhide = %c\n", opt.unhide);
printk("blocksize = %d\n", opt.blocksize);
printk("gid = %d\n", opt.gid);
printk("uid = %d\n", opt.uid);
printk("iocharset = %s\n", opt.iocharset);
#endif
 
/*
* First of all, get the hardware blocksize for this device.
* If we don't know what it is, or the hardware blocksize is
* larger than the blocksize the user specified, then use
* that value.
*/
blocksize = get_hardsect_size(dev);
if(blocksize > opt.blocksize) {
/*
* Force the blocksize we are going to use to be the
* hardware blocksize.
*/
opt.blocksize = blocksize;
}
 
blocksize_bits = 0;
{
int i = opt.blocksize;
while (i != 1){
blocksize_bits++;
i >>=1;
}
}
 
set_blocksize(dev, opt.blocksize);
s->s_blocksize = opt.blocksize;
 
s->u.isofs_sb.s_high_sierra = high_sierra = 0; /* default is iso9660 */
 
vol_desc_start = (opt.sbsector != -1) ?
opt.sbsector : isofs_get_last_session(s,opt.session);
 
for (iso_blknum = vol_desc_start+16;
iso_blknum < vol_desc_start+100; iso_blknum++)
{
struct hs_volume_descriptor * hdp;
struct iso_volume_descriptor * vdp;
 
block = iso_blknum << (ISOFS_BLOCK_BITS-blocksize_bits);
if (!(bh = sb_bread(s, block)))
goto out_no_read;
 
vdp = (struct iso_volume_descriptor *)bh->b_data;
hdp = (struct hs_volume_descriptor *)bh->b_data;
/* Due to the overlapping physical location of the descriptors,
* ISO CDs can match hdp->id==HS_STANDARD_ID as well. To ensure
* proper identification in this case, we first check for ISO.
*/
if (strncmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) {
if (isonum_711 (vdp->type) == ISO_VD_END)
break;
if (isonum_711 (vdp->type) == ISO_VD_PRIMARY) {
if (pri == NULL) {
pri = (struct iso_primary_descriptor *)vdp;
/* Save the buffer in case we need it ... */
pri_bh = bh;
bh = NULL;
}
}
#ifdef CONFIG_JOLIET
else if (isonum_711 (vdp->type) == ISO_VD_SUPPLEMENTARY) {
sec = (struct iso_supplementary_descriptor *)vdp;
if (sec->escape[0] == 0x25 && sec->escape[1] == 0x2f) {
if (opt.joliet == 'y') {
if (sec->escape[2] == 0x40) {
joliet_level = 1;
} else if (sec->escape[2] == 0x43) {
joliet_level = 2;
} else if (sec->escape[2] == 0x45) {
joliet_level = 3;
}
printk(KERN_DEBUG"ISO 9660 Extensions: Microsoft Joliet Level %d\n",
joliet_level);
}
goto root_found;
} else {
/* Unknown supplementary volume descriptor */
sec = NULL;
}
}
#endif
} else {
if (strncmp (hdp->id, HS_STANDARD_ID, sizeof hdp->id) == 0) {
if (isonum_711 (hdp->type) != ISO_VD_PRIMARY)
goto out_freebh;
s->u.isofs_sb.s_high_sierra = 1;
high_sierra = 1;
opt.rock = 'n';
h_pri = (struct hs_primary_descriptor *)vdp;
goto root_found;
}
}
 
/* Just skip any volume descriptors we don't recognize */
 
brelse(bh);
bh = NULL;
}
/*
* If we fall through, either no volume descriptor was found,
* or else we passed a primary descriptor looking for others.
*/
if (!pri)
goto out_unknown_format;
brelse(bh);
bh = pri_bh;
pri_bh = NULL;
 
root_found:
 
if (joliet_level && (pri == NULL || opt.rock == 'n')) {
/* This is the case of Joliet with the norock mount flag.
* A disc with both Joliet and Rock Ridge is handled later
*/
pri = (struct iso_primary_descriptor *) sec;
}
 
if(high_sierra){
rootp = (struct iso_directory_record *) h_pri->root_directory_record;
#ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS
if (isonum_723 (h_pri->volume_set_size) != 1)
goto out_no_support;
#endif /* IGNORE_WRONG_MULTI_VOLUME_SPECS */
s->u.isofs_sb.s_nzones = isonum_733 (h_pri->volume_space_size);
s->u.isofs_sb.s_log_zone_size = isonum_723 (h_pri->logical_block_size);
s->u.isofs_sb.s_max_size = isonum_733(h_pri->volume_space_size);
} else {
rootp = (struct iso_directory_record *) pri->root_directory_record;
#ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS
if (isonum_723 (pri->volume_set_size) != 1)
goto out_no_support;
#endif /* IGNORE_WRONG_MULTI_VOLUME_SPECS */
s->u.isofs_sb.s_nzones = isonum_733 (pri->volume_space_size);
s->u.isofs_sb.s_log_zone_size = isonum_723 (pri->logical_block_size);
s->u.isofs_sb.s_max_size = isonum_733(pri->volume_space_size);
}
 
s->u.isofs_sb.s_ninodes = 0; /* No way to figure this out easily */
 
orig_zonesize = s -> u.isofs_sb.s_log_zone_size;
/*
* If the zone size is smaller than the hardware sector size,
* this is a fatal error. This would occur if the disc drive
* had sectors that were 2048 bytes, but the filesystem had
* blocks that were 512 bytes (which should only very rarely
* happen.)
*/
if(blocksize != 0 && orig_zonesize < blocksize)
goto out_bad_size;
 
/* RDE: convert log zone size to bit shift */
switch (s -> u.isofs_sb.s_log_zone_size)
{ case 512: s -> u.isofs_sb.s_log_zone_size = 9; break;
case 1024: s -> u.isofs_sb.s_log_zone_size = 10; break;
case 2048: s -> u.isofs_sb.s_log_zone_size = 11; break;
 
default:
goto out_bad_zone_size;
}
 
s->s_magic = ISOFS_SUPER_MAGIC;
 
/* The CDROM is read-only, has no nodes (devices) on it, and since
all of the files appear to be owned by root, we really do not want
to allow suid. (suid or devices will not show up unless we have
Rock Ridge extensions) */
 
s->s_flags |= MS_RDONLY /* | MS_NODEV | MS_NOSUID */;
 
/* Set this for reference. Its not currently used except on write
which we don't have .. */
/* RDE: data zone now byte offset! */
 
first_data_zone = ((isonum_733 (rootp->extent) +
isonum_711 (rootp->ext_attr_length))
<< s -> u.isofs_sb.s_log_zone_size);
s->u.isofs_sb.s_firstdatazone = first_data_zone;
#ifndef BEQUIET
printk(KERN_DEBUG "Max size:%ld Log zone size:%ld\n",
s->u.isofs_sb.s_max_size,
1UL << s->u.isofs_sb.s_log_zone_size);
printk(KERN_DEBUG "First datazone:%ld Root inode number:%ld\n",
s->u.isofs_sb.s_firstdatazone >> s -> u.isofs_sb.s_log_zone_size,
s->u.isofs_sb.s_firstdatazone);
if(high_sierra)
printk(KERN_DEBUG "Disc in High Sierra format.\n");
#endif
 
/*
* If the Joliet level is set, we _may_ decide to use the
* secondary descriptor, but can't be sure until after we
* read the root inode. But before reading the root inode
* we may need to change the device blocksize, and would
* rather release the old buffer first. So, we cache the
* first_data_zone value from the secondary descriptor.
*/
if (joliet_level) {
pri = (struct iso_primary_descriptor *) sec;
rootp = (struct iso_directory_record *)
pri->root_directory_record;
first_data_zone = ((isonum_733 (rootp->extent) +
isonum_711 (rootp->ext_attr_length))
<< s -> u.isofs_sb.s_log_zone_size);
}
 
/*
* We're all done using the volume descriptor, and may need
* to change the device blocksize, so release the buffer now.
*/
brelse(pri_bh);
brelse(bh);
 
/*
* Force the blocksize to 512 for 512 byte sectors. The file
* read primitives really get it wrong in a bad way if we don't
* do this.
*
* Note - we should never be setting the blocksize to something
* less than the hardware sector size for the device. If we
* do, we would end up having to read larger buffers and split
* out portions to satisfy requests.
*
* Note2- the idea here is that we want to deal with the optimal
* zonesize in the filesystem. If we have it set to something less,
* then we have horrible problems with trying to piece together
* bits of adjacent blocks in order to properly read directory
* entries. By forcing the blocksize in this way, we ensure
* that we will never be required to do this.
*/
if ( orig_zonesize != opt.blocksize ) {
set_blocksize(dev, orig_zonesize);
#ifndef BEQUIET
printk(KERN_DEBUG
"ISOFS: Forcing new log zone size:%d\n", orig_zonesize);
#endif
}
s->s_blocksize = orig_zonesize;
s->s_blocksize_bits = s -> u.isofs_sb.s_log_zone_size;
 
s->u.isofs_sb.s_nls_iocharset = NULL;
 
#ifdef CONFIG_JOLIET
if (joliet_level && opt.utf8 == 0) {
char * p = opt.iocharset ? opt.iocharset : "iso8859-1";
s->u.isofs_sb.s_nls_iocharset = load_nls(p);
if (! s->u.isofs_sb.s_nls_iocharset) {
/* Fail only if explicit charset specified */
if (opt.iocharset)
goto out_unlock;
s->u.isofs_sb.s_nls_iocharset = load_nls_default();
}
}
#endif
s->s_op = &isofs_sops;
s->u.isofs_sb.s_mapping = opt.map;
s->u.isofs_sb.s_rock = (opt.rock == 'y' ? 2 : 0);
s->u.isofs_sb.s_rock_offset = -1; /* initial offset, will guess until SP is found*/
s->u.isofs_sb.s_cruft = opt.cruft;
s->u.isofs_sb.s_unhide = opt.unhide;
s->u.isofs_sb.s_uid = opt.uid;
s->u.isofs_sb.s_gid = opt.gid;
s->u.isofs_sb.s_utf8 = opt.utf8;
s->u.isofs_sb.s_nocompress = opt.nocompress;
/*
* It would be incredibly stupid to allow people to mark every file
* on the disk as suid, so we merely allow them to set the default
* permissions.
*/
s->u.isofs_sb.s_mode = opt.mode & 0777;
 
/*
* Read the root inode, which _may_ result in changing
* the s_rock flag. Once we have the final s_rock value,
* we then decide whether to use the Joliet descriptor.
*/
inode = iget(s, s->u.isofs_sb.s_firstdatazone);
 
/*
* If this disk has both Rock Ridge and Joliet on it, then we
* want to use Rock Ridge by default. This can be overridden
* by using the norock mount option. There is still one other
* possibility that is not taken into account: a Rock Ridge
* CD with Unicode names. Until someone sees such a beast, it
* will not be supported.
*/
if (s->u.isofs_sb.s_rock == 1) {
joliet_level = 0;
} else if (joliet_level) {
s->u.isofs_sb.s_rock = 0;
if (s->u.isofs_sb.s_firstdatazone != first_data_zone) {
s->u.isofs_sb.s_firstdatazone = first_data_zone;
printk(KERN_DEBUG
"ISOFS: changing to secondary root\n");
iput(inode);
inode = iget(s, s->u.isofs_sb.s_firstdatazone);
}
}
 
if (opt.check == 'u') {
/* Only Joliet is case insensitive by default */
if (joliet_level) opt.check = 'r';
else opt.check = 's';
}
s->u.isofs_sb.s_joliet_level = joliet_level;
 
/* check the root inode */
if (!inode)
goto out_no_root;
if (!inode->i_op)
goto out_bad_root;
/* get the root dentry */
s->s_root = d_alloc_root(inode);
if (!(s->s_root))
goto out_no_root;
 
table = 0;
if (joliet_level) table += 2;
if (opt.check == 'r') table++;
s->s_root->d_op = &isofs_dentry_ops[table];
 
return s;
 
/*
* Display error messages and free resources.
*/
out_bad_root:
printk(KERN_WARNING "isofs_read_super: root inode not initialized\n");
goto out_iput;
out_no_root:
printk(KERN_WARNING "isofs_read_super: get root inode failed\n");
out_iput:
iput(inode);
#ifdef CONFIG_JOLIET
if (s->u.isofs_sb.s_nls_iocharset)
unload_nls(s->u.isofs_sb.s_nls_iocharset);
#endif
goto out_unlock;
out_no_read:
printk(KERN_WARNING "isofs_read_super: "
"bread failed, dev=%s, iso_blknum=%d, block=%d\n",
kdevname(dev), iso_blknum, block);
goto out_unlock;
out_bad_zone_size:
printk(KERN_WARNING "Bad logical zone size %ld\n",
s->u.isofs_sb.s_log_zone_size);
goto out_freebh;
out_bad_size:
printk(KERN_WARNING "Logical zone size(%d) < hardware blocksize(%u)\n",
orig_zonesize, blocksize);
goto out_freebh;
#ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS
out_no_support:
printk(KERN_WARNING "Multi-volume disks not supported.\n");
goto out_freebh;
#endif
out_unknown_format:
if (!silent)
printk(KERN_WARNING "Unable to identify CD-ROM format.\n");
 
out_freebh:
brelse(bh);
out_unlock:
return NULL;
}
 
static int isofs_statfs (struct super_block *sb, struct statfs *buf)
{
buf->f_type = ISOFS_SUPER_MAGIC;
buf->f_bsize = sb->s_blocksize;
buf->f_blocks = (sb->u.isofs_sb.s_nzones
<< (sb->u.isofs_sb.s_log_zone_size - sb->s_blocksize_bits));
buf->f_bfree = 0;
buf->f_bavail = 0;
buf->f_files = sb->u.isofs_sb.s_ninodes;
buf->f_ffree = 0;
buf->f_namelen = NAME_MAX;
return 0;
}
 
/*
* Get a set of blocks; filling in buffer_heads if already allocated
* or getblk() if they are not. Returns the number of blocks inserted
* (0 == error.)
*/
int isofs_get_blocks(struct inode *inode, long iblock,
struct buffer_head **bh_result, unsigned long nblocks)
{
unsigned long b_off;
unsigned offset, sect_size;
unsigned int firstext;
unsigned long nextino;
int section, rv;
 
lock_kernel();
 
rv = 0;
if (iblock < 0) {
printk("isofs_get_blocks: block < 0\n");
goto abort;
}
 
b_off = iblock;
offset = 0;
firstext = inode->u.isofs_i.i_first_extent;
sect_size = inode->u.isofs_i.i_section_size >> ISOFS_BUFFER_BITS(inode);
nextino = inode->u.isofs_i.i_next_section_ino;
section = 0;
 
while ( nblocks ) {
/* If we are *way* beyond the end of the file, print a message.
* Access beyond the end of the file up to the next page boundary
* is normal, however because of the way the page cache works.
* In this case, we just return 0 so that we can properly fill
* the page with useless information without generating any
* I/O errors.
*/
if (b_off > ((inode->i_size + PAGE_CACHE_SIZE - 1) >> ISOFS_BUFFER_BITS(inode))) {
printk("isofs_get_blocks: block >= EOF (%ld, %ld)\n",
iblock, (unsigned long) inode->i_size);
goto abort;
}
if (nextino) {
while (b_off >= (offset + sect_size)) {
struct inode *ninode;
offset += sect_size;
if (nextino == 0)
goto abort;
ninode = iget(inode->i_sb, nextino);
if (!ninode)
goto abort;
firstext = ninode->u.isofs_i.i_first_extent;
sect_size = ninode->u.isofs_i.i_section_size;
nextino = ninode->u.isofs_i.i_next_section_ino;
iput(ninode);
if (++section > 100) {
printk("isofs_get_blocks: More than 100 file sections ?!?, aborting...\n");
printk("isofs_get_blocks: ino=%lu block=%ld firstext=%u sect_size=%u nextino=%lu\n",
inode->i_ino, iblock, firstext, (unsigned) sect_size, nextino);
goto abort;
}
}
}
if ( *bh_result ) {
(*bh_result)->b_dev = inode->i_dev;
(*bh_result)->b_blocknr = firstext + b_off - offset;
(*bh_result)->b_state |= (1UL << BH_Mapped);
} else {
*bh_result = sb_getblk(inode->i_sb, firstext+b_off-offset);
if ( !*bh_result )
goto abort;
}
bh_result++; /* Next buffer head */
b_off++; /* Next buffer offset */
nblocks--;
rv++;
}
 
 
abort:
unlock_kernel();
return rv;
}
 
/*
* Used by the standard interfaces.
*/
static int isofs_get_block(struct inode *inode, long iblock,
struct buffer_head *bh_result, int create)
{
if ( create ) {
printk("isofs_get_block: Kernel tries to allocate a block\n");
return -EROFS;
}
 
return isofs_get_blocks(inode, iblock, &bh_result, 1) ? 0 : -EIO;
}
 
static int isofs_bmap(struct inode *inode, int block)
{
struct buffer_head dummy;
int error;
 
dummy.b_state = 0;
dummy.b_blocknr = -1000;
error = isofs_get_block(inode, block, &dummy, 0);
if (!error)
return dummy.b_blocknr;
return 0;
}
 
struct buffer_head *isofs_bread(struct inode *inode, unsigned int block)
{
unsigned int blknr = isofs_bmap(inode, block);
if (!blknr)
return NULL;
return sb_bread(inode->i_sb, blknr);
}
 
static int isofs_readpage(struct file *file, struct page *page)
{
return block_read_full_page(page,isofs_get_block);
}
 
static int _isofs_bmap(struct address_space *mapping, long block)
{
return generic_block_bmap(mapping,block,isofs_get_block);
}
 
static struct address_space_operations isofs_aops = {
readpage: isofs_readpage,
sync_page: block_sync_page,
bmap: _isofs_bmap
};
 
static inline void test_and_set_uid(uid_t *p, uid_t value)
{
if(value) {
*p = value;
}
}
 
static inline void test_and_set_gid(gid_t *p, gid_t value)
{
if(value) {
*p = value;
}
}
 
static int isofs_read_level3_size(struct inode * inode)
{
unsigned long f_pos = inode->i_ino;
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
int high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
struct buffer_head * bh = NULL;
unsigned long block, offset;
int i = 0;
int more_entries = 0;
struct iso_directory_record * tmpde = NULL;
 
inode->i_size = 0;
inode->u.isofs_i.i_next_section_ino = 0;
 
block = f_pos >> ISOFS_BUFFER_BITS(inode);
offset = f_pos & (bufsize-1);
 
do {
struct iso_directory_record * de;
unsigned int de_len;
 
if (!bh) {
bh = sb_bread(inode->i_sb, block);
if (!bh)
goto out_noread;
}
de = (struct iso_directory_record *) (bh->b_data + offset);
de_len = *(unsigned char *) de;
 
if (de_len == 0) {
brelse(bh);
bh = NULL;
f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
block = f_pos >> ISOFS_BUFFER_BITS(inode);
offset = 0;
continue;
}
 
offset += de_len;
 
/* Make sure we have a full directory entry */
if (offset >= bufsize) {
int slop = bufsize - offset + de_len;
if (!tmpde) {
tmpde = kmalloc(256, GFP_KERNEL);
if (!tmpde)
goto out_nomem;
}
memcpy(tmpde, de, slop);
offset &= bufsize - 1;
block++;
brelse(bh);
bh = NULL;
if (offset) {
bh = sb_bread(inode->i_sb, block);
if (!bh)
goto out_noread;
memcpy((void *) tmpde + slop, bh->b_data, offset);
}
de = tmpde;
}
 
inode->i_size += isonum_733(de->size);
if (i == 1)
inode->u.isofs_i.i_next_section_ino = f_pos;
 
more_entries = de->flags[-high_sierra] & 0x80;
 
f_pos += de_len;
i++;
if(i > 100)
goto out_toomany;
} while(more_entries);
out:
if (tmpde)
kfree(tmpde);
if (bh)
brelse(bh);
return 0;
 
out_nomem:
if (bh)
brelse(bh);
return -ENOMEM;
 
out_noread:
printk(KERN_INFO "ISOFS: unable to read i-node block %lu\n", block);
if (tmpde)
kfree(tmpde);
return -EIO;
 
out_toomany:
printk(KERN_INFO "isofs_read_level3_size: "
"More than 100 file sections ?!?, aborting...\n"
"isofs_read_level3_size: inode=%lu ino=%lu\n",
inode->i_ino, f_pos);
goto out;
}
 
static void isofs_read_inode(struct inode * inode)
{
struct super_block *sb = inode->i_sb;
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
int block = inode->i_ino >> ISOFS_BUFFER_BITS(inode);
int high_sierra = sb->u.isofs_sb.s_high_sierra;
struct buffer_head * bh = NULL;
struct iso_directory_record * de;
struct iso_directory_record * tmpde = NULL;
unsigned int de_len;
unsigned long offset;
int volume_seq_no, i;
 
bh = sb_bread(inode->i_sb, block);
if (!bh)
goto out_badread;
 
offset = (inode->i_ino & (bufsize - 1));
de = (struct iso_directory_record *) (bh->b_data + offset);
de_len = *(unsigned char *) de;
 
if (offset + de_len > bufsize) {
int frag1 = bufsize - offset;
 
tmpde = kmalloc(de_len, GFP_KERNEL);
if (tmpde == NULL) {
printk(KERN_INFO "isofs_read_inode: out of memory\n");
goto fail;
}
memcpy(tmpde, bh->b_data + offset, frag1);
brelse(bh);
bh = sb_bread(inode->i_sb, ++block);
if (!bh)
goto out_badread;
memcpy((char *)tmpde+frag1, bh->b_data, de_len - frag1);
de = tmpde;
}
 
/* Assume it is a normal-format file unless told otherwise */
inode->u.isofs_i.i_file_format = isofs_file_normal;
 
if (de->flags[-high_sierra] & 2) {
inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
inode->i_nlink = 1; /* Set to 1. We know there are 2, but
the find utility tries to optimize
if it is 2, and it screws up. It is
easier to give 1 which tells find to
do it the hard way. */
} else {
/* Everybody gets to read the file. */
inode->i_mode = inode->i_sb->u.isofs_sb.s_mode;
inode->i_nlink = 1;
inode->i_mode |= S_IFREG;
/* If there are no periods in the name,
* then set the execute permission bit
*/
for(i=0; i< de->name_len[0]; i++)
if(de->name[i]=='.' || de->name[i]==';')
break;
if(i == de->name_len[0] || de->name[i] == ';')
inode->i_mode |= S_IXUGO; /* execute permission */
}
inode->i_uid = inode->i_sb->u.isofs_sb.s_uid;
inode->i_gid = inode->i_sb->u.isofs_sb.s_gid;
inode->i_blocks = inode->i_blksize = 0;
 
 
inode->u.isofs_i.i_section_size = isonum_733 (de->size);
if(de->flags[-high_sierra] & 0x80) {
if(isofs_read_level3_size(inode)) goto fail;
} else {
inode->i_size = isonum_733 (de->size);
}
 
/*
* The ISO-9660 filesystem only stores 32 bits for file size.
* mkisofs handles files up to 2GB-2 = 2147483646 = 0x7FFFFFFE bytes
* in size. This is according to the large file summit paper from 1996.
* WARNING: ISO-9660 filesystems > 1 GB and even > 2 GB are fully
* legal. Do not prevent to use DVD's schilling@fokus.gmd.de
*/
if ((inode->i_size < 0 || inode->i_size > 0x7FFFFFFE) &&
inode->i_sb->u.isofs_sb.s_cruft == 'n') {
printk(KERN_WARNING "Warning: defective CD-ROM. "
"Enabling \"cruft\" mount option.\n");
inode->i_sb->u.isofs_sb.s_cruft = 'y';
}
 
/*
* Some dipshit decided to store some other bit of information
* in the high byte of the file length. Catch this and holler.
* WARNING: this will make it impossible for a file to be > 16MB
* on the CDROM.
*/
 
if (inode->i_sb->u.isofs_sb.s_cruft == 'y' &&
inode->i_size & 0xff000000) {
inode->i_size &= 0x00ffffff;
}
 
if (de->interleave[0]) {
printk("Interleaved files not (yet) supported.\n");
inode->i_size = 0;
}
 
/* I have no idea what file_unit_size is used for, so
we will flag it for now */
if (de->file_unit_size[0] != 0) {
printk("File unit size != 0 for ISO file (%ld).\n",
inode->i_ino);
}
 
/* I have no idea what other flag bits are used for, so
we will flag it for now */
#ifdef DEBUG
if((de->flags[-high_sierra] & ~2)!= 0){
printk("Unusual flag settings for ISO file (%ld %x).\n",
inode->i_ino, de->flags[-high_sierra]);
}
#endif
 
inode->i_mtime = inode->i_atime = inode->i_ctime =
iso_date(de->date, high_sierra);
 
inode->u.isofs_i.i_first_extent = (isonum_733 (de->extent) +
isonum_711 (de->ext_attr_length));
 
/* Set the number of blocks for stat() - should be done before RR */
inode->i_blksize = PAGE_CACHE_SIZE; /* For stat() only */
inode->i_blocks = (inode->i_size + 511) >> 9;
 
/*
* Now test for possible Rock Ridge extensions which will override
* some of these numbers in the inode structure.
*/
 
if (!high_sierra) {
parse_rock_ridge_inode(de, inode);
/* if we want uid/gid set, override the rock ridge setting */
test_and_set_uid(&inode->i_uid, inode->i_sb->u.isofs_sb.s_uid);
test_and_set_gid(&inode->i_gid, inode->i_sb->u.isofs_sb.s_gid);
}
 
/* get the volume sequence number */
volume_seq_no = isonum_723 (de->volume_sequence_number) ;
 
/*
* Multi volume means tagging a group of CDs with info in their headers.
* All CDs of a group must share the same vol set name and vol set size
* and have different vol set seq num. Deciding that data is wrong based
* in that three fields is wrong. The fields are informative, for
* cataloging purposes in a big jukebox, ie. Read sections 4.17, 4.18, 6.6
* of ftp://ftp.ecma.ch/ecma-st/Ecma-119.pdf (ECMA 119 2nd Ed = ISO 9660)
*/
#ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS
/*
* Disable checking if we see any volume number other than 0 or 1.
* We could use the cruft option, but that has multiple purposes, one
* of which is limiting the file size to 16Mb. Thus we silently allow
* volume numbers of 0 to go through without complaining.
*/
if (inode->i_sb->u.isofs_sb.s_cruft == 'n' &&
(volume_seq_no != 0) && (volume_seq_no != 1)) {
printk(KERN_WARNING "Warning: defective CD-ROM "
"(volume sequence number %d). "
"Enabling \"cruft\" mount option.\n", volume_seq_no);
inode->i_sb->u.isofs_sb.s_cruft = 'y';
}
#endif /*IGNORE_WRONG_MULTI_VOLUME_SPECS */
 
/* Install the inode operations vector */
#ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS
if (inode->i_sb->u.isofs_sb.s_cruft != 'y' &&
(volume_seq_no != 0) && (volume_seq_no != 1)) {
printk(KERN_WARNING "Multi-volume CD somehow got mounted.\n");
} else
#endif /*IGNORE_WRONG_MULTI_VOLUME_SPECS */
{
if (S_ISREG(inode->i_mode)) {
inode->i_fop = &generic_ro_fops;
switch ( inode->u.isofs_i.i_file_format ) {
#ifdef CONFIG_ZISOFS
case isofs_file_compressed:
inode->i_data.a_ops = &zisofs_aops;
break;
#endif
default:
inode->i_data.a_ops = &isofs_aops;
break;
}
} else if (S_ISDIR(inode->i_mode)) {
inode->i_op = &isofs_dir_inode_operations;
inode->i_fop = &isofs_dir_operations;
} else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &page_symlink_inode_operations;
inode->i_data.a_ops = &isofs_symlink_aops;
} else
/* XXX - parse_rock_ridge_inode() had already set i_rdev. */
init_special_inode(inode, inode->i_mode,
kdev_t_to_nr(inode->i_rdev));
}
out:
if (tmpde)
kfree(tmpde);
if (bh)
brelse(bh);
return;
 
out_badread:
printk(KERN_WARNING "ISOFS: unable to read i-node block\n");
fail:
make_bad_inode(inode);
goto out;
}
 
#ifdef LEAK_CHECK
#undef malloc
#undef free_s
#undef sb_bread
#undef brelse
 
void * leak_check_malloc(unsigned int size){
void * tmp;
check_malloc++;
tmp = kmalloc(size, GFP_KERNEL);
return tmp;
}
 
void leak_check_free_s(void * obj, int size){
check_malloc--;
return kfree(obj);
}
 
struct buffer_head * leak_check_bread(struct super_block *sb, int block){
check_bread++;
return sb_bread(sb, block);
}
 
void leak_check_brelse(struct buffer_head * bh){
check_bread--;
return brelse(bh);
}
 
#endif
 
static DECLARE_FSTYPE_DEV(iso9660_fs_type, "iso9660", isofs_read_super);
 
static int __init init_iso9660_fs(void)
{
#ifdef CONFIG_ZISOFS
int err;
 
err = zisofs_init();
if ( err )
return err;
#endif
return register_filesystem(&iso9660_fs_type);
}
 
static void __exit exit_iso9660_fs(void)
{
unregister_filesystem(&iso9660_fs_type);
#ifdef CONFIG_ZISOFS
zisofs_cleanup();
#endif
}
 
EXPORT_NO_SYMBOLS;
 
module_init(init_iso9660_fs)
module_exit(exit_iso9660_fs)
MODULE_LICENSE("GPL");
 
/zisofs.h
0,0 → 1,21
/* ----------------------------------------------------------------------- *
*
* Copyright 2001 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
* USA; either version 2 of the License, or (at your option) any later
* version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
 
/*
* Prototypes for functions exported from the compressed isofs subsystem
*/
 
#ifdef CONFIG_ZISOFS
extern struct address_space_operations zisofs_aops;
extern int __init zisofs_init(void);
extern void __exit zisofs_cleanup(void);
#endif
/rock.h
0,0 → 1,119
/* These structs are used by the system-use-sharing protocol, in which the
Rock Ridge extensions are embedded. It is quite possible that other
extensions are present on the disk, and this is fine as long as they
all use SUSP */
 
struct SU_SP{
unsigned char magic[2];
unsigned char skip;
} __attribute__((packed));
 
struct SU_CE{
char extent[8];
char offset[8];
char size[8];
};
 
struct SU_ER{
unsigned char len_id;
unsigned char len_des;
unsigned char len_src;
unsigned char ext_ver;
char data[0];
} __attribute__((packed));
 
struct RR_RR{
char flags[1];
} __attribute__((packed));
 
struct RR_PX{
char mode[8];
char n_links[8];
char uid[8];
char gid[8];
};
 
struct RR_PN{
char dev_high[8];
char dev_low[8];
};
 
 
struct SL_component{
unsigned char flags;
unsigned char len;
char text[0];
} __attribute__((packed));
 
struct RR_SL{
unsigned char flags;
struct SL_component link;
} __attribute__((packed));
 
struct RR_NM{
unsigned char flags;
char name[0];
} __attribute__((packed));
 
struct RR_CL{
char location[8];
};
 
struct RR_PL{
char location[8];
};
 
struct stamp{
char time[7];
} __attribute__((packed));
 
struct RR_TF{
char flags;
struct stamp times[0]; /* Variable number of these beasts */
} __attribute__((packed));
 
/* Linux-specific extension for transparent decompression */
struct RR_ZF{
char algorithm[2];
char parms[2];
char real_size[8];
};
 
/* These are the bits and their meanings for flags in the TF structure. */
#define TF_CREATE 1
#define TF_MODIFY 2
#define TF_ACCESS 4
#define TF_ATTRIBUTES 8
#define TF_BACKUP 16
#define TF_EXPIRATION 32
#define TF_EFFECTIVE 64
#define TF_LONG_FORM 128
 
struct rock_ridge{
char signature[2];
unsigned char len;
unsigned char version;
union{
struct SU_SP SP;
struct SU_CE CE;
struct SU_ER ER;
struct RR_RR RR;
struct RR_PX PX;
struct RR_PN PN;
struct RR_SL SL;
struct RR_NM NM;
struct RR_CL CL;
struct RR_PL PL;
struct RR_TF TF;
struct RR_ZF ZF;
} u;
};
 
#define RR_PX 1 /* POSIX attributes */
#define RR_PN 2 /* POSIX devices */
#define RR_SL 4 /* Symbolic link */
#define RR_NM 8 /* Alternate Name */
#define RR_CL 16 /* Child link */
#define RR_PL 32 /* Parent link */
#define RR_RE 64 /* Relocation directory */
#define RR_TF 128 /* Timestamps */
/joliet.c
0,0 → 1,103
/*
* linux/fs/isofs/joliet.c
*
* (C) 1996 Gordon Chaffee
*
* Joliet: Microsoft's Unicode extensions to iso9660
*/
 
#include <linux/string.h>
#include <linux/nls.h>
#include <linux/slab.h>
#include <linux/iso_fs.h>
#include <asm/unaligned.h>
 
/*
* Convert Unicode 16 to UTF8 or ASCII.
*/
static int
uni16_to_x8(unsigned char *ascii, u16 *uni, int len, struct nls_table *nls)
{
wchar_t *ip, ch;
unsigned char *op;
 
ip = uni;
op = ascii;
 
while ((ch = get_unaligned(ip)) && len) {
int llen;
ch = be16_to_cpu(ch);
if ((llen = nls->uni2char(ch, op, NLS_MAX_CHARSET_SIZE)) > 0)
op += llen;
else
*op++ = '?';
ip++;
 
len--;
}
*op = 0;
return (op - ascii);
}
 
/* Convert big endian wide character string to utf8 */
static int
wcsntombs_be(__u8 *s, const __u8 *pwcs, int inlen, int maxlen)
{
const __u8 *ip;
__u8 *op;
int size;
__u16 c;
 
op = s;
ip = pwcs;
while ((*ip || ip[1]) && (maxlen > 0) && (inlen > 0)) {
c = (*ip << 8) | ip[1];
if (c > 0x7f) {
size = utf8_wctomb(op, c, maxlen);
if (size == -1) {
/* Ignore character and move on */
maxlen--;
} else {
op += size;
maxlen -= size;
}
} else {
*op++ = (__u8) c;
}
ip += 2;
inlen--;
}
return (op - s);
}
 
int
get_joliet_filename(struct iso_directory_record * de, unsigned char *outname, struct inode * inode)
{
unsigned char utf8;
struct nls_table *nls;
unsigned char len = 0;
 
utf8 = inode->i_sb->u.isofs_sb.s_utf8;
nls = inode->i_sb->u.isofs_sb.s_nls_iocharset;
 
if (utf8) {
len = wcsntombs_be(outname, de->name,
de->name_len[0] >> 1, PAGE_SIZE);
} else {
len = uni16_to_x8(outname, (u16 *) de->name,
de->name_len[0] >> 1, nls);
}
if ((len > 2) && (outname[len-2] == ';') && (outname[len-1] == '1')) {
len -= 2;
}
 
/*
* Windows doesn't like periods at the end of a name,
* so neither do we
*/
while (len >= 2 && (outname[len-1] == '.')) {
len--;
}
 
return len;
}
/namei.c
0,0 → 1,182
/*
* linux/fs/isofs/namei.c
*
* (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem.
*
* (C) 1991 Linus Torvalds - minix filesystem
*/
 
#include <linux/sched.h>
#include <linux/iso_fs.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/config.h> /* Joliet? */
 
#include <asm/uaccess.h>
 
/*
* ok, we cannot use strncmp, as the name is not in our data space.
* Thus we'll have to use isofs_match. No big problem. Match also makes
* some sanity tests.
*/
static int
isofs_cmp(struct dentry * dentry, const char * compare, int dlen)
{
struct qstr qstr;
 
if (!compare)
return 1;
 
/* check special "." and ".." files */
if (dlen == 1) {
/* "." */
if (compare[0] == 0) {
if (!dentry->d_name.len)
return 0;
compare = ".";
} else if (compare[0] == 1) {
compare = "..";
dlen = 2;
}
}
 
qstr.name = compare;
qstr.len = dlen;
return dentry->d_op->d_compare(dentry, &dentry->d_name, &qstr);
}
 
/*
* isofs_find_entry()
*
* finds an entry in the specified directory with the wanted name. It
* returns the inode number of the found entry, or 0 on error.
*/
static unsigned long
isofs_find_entry(struct inode *dir, struct dentry *dentry,
char * tmpname, struct iso_directory_record * tmpde)
{
unsigned long inode_number;
unsigned long bufsize = ISOFS_BUFFER_SIZE(dir);
unsigned char bufbits = ISOFS_BUFFER_BITS(dir);
unsigned int block, f_pos, offset;
struct buffer_head * bh = NULL;
 
if (!dir->u.isofs_i.i_first_extent)
return 0;
f_pos = 0;
offset = 0;
block = 0;
 
while (f_pos < dir->i_size) {
struct iso_directory_record * de;
int de_len, match, i, dlen;
char *dpnt;
 
if (!bh) {
bh = isofs_bread(dir, block);
if (!bh)
return 0;
}
 
de = (struct iso_directory_record *) (bh->b_data + offset);
inode_number = (bh->b_blocknr << bufbits) + offset;
 
de_len = *(unsigned char *) de;
if (!de_len) {
brelse(bh);
bh = NULL;
f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
block = f_pos >> bufbits;
offset = 0;
continue;
}
 
offset += de_len;
f_pos += de_len;
 
/* Make sure we have a full directory entry */
if (offset >= bufsize) {
int slop = bufsize - offset + de_len;
memcpy(tmpde, de, slop);
offset &= bufsize - 1;
block++;
brelse(bh);
bh = NULL;
if (offset) {
bh = isofs_bread(dir, block);
if (!bh)
return 0;
memcpy((void *) tmpde + slop, bh->b_data, offset);
}
de = tmpde;
}
 
dlen = de->name_len[0];
dpnt = de->name;
 
if (dir->i_sb->u.isofs_sb.s_rock &&
((i = get_rock_ridge_filename(de, tmpname, dir)))) {
dlen = i; /* possibly -1 */
dpnt = tmpname;
#ifdef CONFIG_JOLIET
} else if (dir->i_sb->u.isofs_sb.s_joliet_level) {
dlen = get_joliet_filename(de, tmpname, dir);
dpnt = tmpname;
#endif
} else if (dir->i_sb->u.isofs_sb.s_mapping == 'a') {
dlen = get_acorn_filename(de, tmpname, dir);
dpnt = tmpname;
} else if (dir->i_sb->u.isofs_sb.s_mapping == 'n') {
dlen = isofs_name_translate(de, tmpname, dir);
dpnt = tmpname;
}
 
/*
* Skip hidden or associated files unless unhide is set
*/
match = 0;
if (dlen > 0 &&
(!(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5)
|| dir->i_sb->u.isofs_sb.s_unhide == 'y'))
{
match = (isofs_cmp(dentry,dpnt,dlen) == 0);
}
if (match) {
if (bh) brelse(bh);
return inode_number;
}
}
if (bh) brelse(bh);
return 0;
}
 
struct dentry *isofs_lookup(struct inode * dir, struct dentry * dentry)
{
unsigned long ino;
struct inode *inode;
struct page *page;
 
dentry->d_op = dir->i_sb->s_root->d_op;
 
page = alloc_page(GFP_USER);
if (!page)
return ERR_PTR(-ENOMEM);
 
ino = isofs_find_entry(dir, dentry, page_address(page),
1024 + page_address(page));
__free_page(page);
 
inode = NULL;
if (ino) {
inode = iget(dir->i_sb, ino);
if (!inode)
return ERR_PTR(-EACCES);
}
d_add(dentry, inode);
return NULL;
}
/compress.c
0,0 → 1,360
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright 2001 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
* USA; either version 2 of the License, or (at your option) any later
* version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
 
/*
* linux/fs/isofs/compress.c
*
* Transparent decompression of files on an iso9660 filesystem
*/
 
#include <linux/config.h>
#include <linux/module.h>
 
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/iso_fs.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/locks.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/cdrom.h>
#include <linux/init.h>
#include <linux/nls.h>
#include <linux/ctype.h>
#include <linux/smp_lock.h>
#include <linux/blkdev.h>
#include <linux/vmalloc.h>
#include <linux/zlib.h>
 
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
 
#include "zisofs.h"
 
/* This should probably be global. */
static char zisofs_sink_page[PAGE_CACHE_SIZE];
 
/*
* This contains the zlib memory allocation and the mutex for the
* allocation; this avoids failures at block-decompression time.
*/
static void *zisofs_zlib_workspace;
static struct semaphore zisofs_zlib_semaphore;
 
/*
* When decompressing, we typically obtain more than one page
* per reference. We inject the additional pages into the page
* cache as a form of readahead.
*/
static int zisofs_readpage(struct file *file, struct page *page)
{
struct inode *inode = file->f_dentry->d_inode;
struct address_space *mapping = inode->i_mapping;
unsigned int maxpage, xpage, fpage, blockindex;
unsigned long offset;
unsigned long blockptr, blockendptr, cstart, cend, csize;
struct buffer_head *bh, *ptrbh[2];
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
unsigned int bufshift = ISOFS_BUFFER_BITS(inode);
unsigned long bufmask = bufsize - 1;
int err = -EIO;
int i;
unsigned int header_size = inode->u.isofs_i.i_format_parm[0];
unsigned int zisofs_block_shift = inode->u.isofs_i.i_format_parm[1];
/* unsigned long zisofs_block_size = 1UL << zisofs_block_shift; */
unsigned int zisofs_block_page_shift = zisofs_block_shift-PAGE_CACHE_SHIFT;
unsigned long zisofs_block_pages = 1UL << zisofs_block_page_shift;
unsigned long zisofs_block_page_mask = zisofs_block_pages-1;
struct page *pages[zisofs_block_pages];
unsigned long index = page->index;
int indexblocks;
 
/* We have already been given one page, this is the one
we must do. */
xpage = index & zisofs_block_page_mask;
pages[xpage] = page;
/* The remaining pages need to be allocated and inserted */
offset = index & ~zisofs_block_page_mask;
blockindex = offset >> zisofs_block_page_shift;
maxpage = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
maxpage = min(zisofs_block_pages, maxpage-offset);
 
for ( i = 0 ; i < maxpage ; i++, offset++ ) {
if ( i != xpage ) {
pages[i] = grab_cache_page_nowait(mapping, offset);
}
page = pages[i];
if ( page ) {
ClearPageError(page);
kmap(page);
}
}
 
/* This is the last page filled, plus one; used in case of abort. */
fpage = 0;
 
/* Find the pointer to this specific chunk */
/* Note: we're not using isonum_731() here because the data is known aligned */
/* Note: header_size is in 32-bit words (4 bytes) */
blockptr = (header_size + blockindex) << 2;
blockendptr = blockptr + 4;
 
indexblocks = ((blockptr^blockendptr) >> bufshift) ? 2 : 1;
ptrbh[0] = ptrbh[1] = 0;
 
if ( isofs_get_blocks(inode, blockptr >> bufshift, ptrbh, indexblocks) != indexblocks ) {
if ( ptrbh[0] ) brelse(ptrbh[0]);
printk(KERN_DEBUG "zisofs: Null buffer on reading block table, inode = %lu, block = %lu\n",
inode->i_ino, blockptr >> bufshift);
goto eio;
}
ll_rw_block(READ, indexblocks, ptrbh);
 
bh = ptrbh[0];
if ( !bh || (wait_on_buffer(bh), !buffer_uptodate(bh)) ) {
printk(KERN_DEBUG "zisofs: Failed to read block table, inode = %lu, block = %lu\n",
inode->i_ino, blockptr >> bufshift);
if ( ptrbh[1] )
brelse(ptrbh[1]);
goto eio;
}
cstart = le32_to_cpu(*(u32 *)(bh->b_data + (blockptr & bufmask)));
 
if ( indexblocks == 2 ) {
/* We just crossed a block boundary. Switch to the next block */
brelse(bh);
bh = ptrbh[1];
if ( !bh || (wait_on_buffer(bh), !buffer_uptodate(bh)) ) {
printk(KERN_DEBUG "zisofs: Failed to read block table, inode = %lu, block = %lu\n",
inode->i_ino, blockendptr >> bufshift);
goto eio;
}
}
cend = le32_to_cpu(*(u32 *)(bh->b_data + (blockendptr & bufmask)));
brelse(bh);
 
csize = cend-cstart;
 
/* Now page[] contains an array of pages, any of which can be NULL,
and the locks on which we hold. We should now read the data and
release the pages. If the pages are NULL the decompressed data
for that particular page should be discarded. */
if ( csize == 0 ) {
/* This data block is empty. */
 
for ( fpage = 0 ; fpage < maxpage ; fpage++ ) {
if ( (page = pages[fpage]) != NULL ) {
memset(page_address(page), 0, PAGE_CACHE_SIZE);
flush_dcache_page(page);
SetPageUptodate(page);
kunmap(page);
UnlockPage(page);
if ( fpage == xpage )
err = 0; /* The critical page */
else
page_cache_release(page);
}
}
} else {
/* This data block is compressed. */
z_stream stream;
int bail = 0, left_out = -1;
int zerr;
int needblocks = (csize + (cstart & bufmask) + bufmask) >> bufshift;
int haveblocks;
struct buffer_head *bhs[needblocks+1];
struct buffer_head **bhptr;
 
/* Because zlib is not thread-safe, do all the I/O at the top. */
 
blockptr = cstart >> bufshift;
memset(bhs, 0, (needblocks+1)*sizeof(struct buffer_head *));
haveblocks = isofs_get_blocks(inode, blockptr, bhs, needblocks);
ll_rw_block(READ, haveblocks, bhs);
 
bhptr = &bhs[0];
bh = *bhptr++;
 
/* First block is special since it may be fractional.
We also wait for it before grabbing the zlib
semaphore; odds are that the subsequent blocks are
going to come in in short order so we don't hold
the zlib semaphore longer than necessary. */
 
if ( !bh || (wait_on_buffer(bh), !buffer_uptodate(bh)) ) {
printk(KERN_DEBUG "zisofs: Hit null buffer, fpage = %d, xpage = %d, csize = %ld\n",
fpage, xpage, csize);
goto b_eio;
}
stream.next_in = bh->b_data + (cstart & bufmask);
stream.avail_in = min(bufsize-(cstart & bufmask), csize);
csize -= stream.avail_in;
 
stream.workspace = zisofs_zlib_workspace;
down(&zisofs_zlib_semaphore);
zerr = zlib_inflateInit(&stream);
if ( zerr != Z_OK ) {
if ( err && zerr == Z_MEM_ERROR )
err = -ENOMEM;
printk(KERN_DEBUG "zisofs: zisofs_inflateInit returned %d\n",
zerr);
goto z_eio;
}
 
while ( !bail && fpage < maxpage ) {
page = pages[fpage];
if ( page )
stream.next_out = page_address(page);
else
stream.next_out = (void *)&zisofs_sink_page;
stream.avail_out = PAGE_CACHE_SIZE;
 
while ( stream.avail_out ) {
int ao, ai;
if ( stream.avail_in == 0 && left_out ) {
if ( !csize ) {
printk(KERN_WARNING "zisofs: ZF read beyond end of input\n");
bail = 1;
break;
} else {
bh = *bhptr++;
if ( !bh ||
(wait_on_buffer(bh), !buffer_uptodate(bh)) ) {
/* Reached an EIO */
printk(KERN_DEBUG "zisofs: Hit null buffer, fpage = %d, xpage = %d, csize = %ld\n",
fpage, xpage, csize);
bail = 1;
break;
}
stream.next_in = bh->b_data;
stream.avail_in = min(csize,bufsize);
csize -= stream.avail_in;
}
}
ao = stream.avail_out; ai = stream.avail_in;
zerr = zlib_inflate(&stream, Z_SYNC_FLUSH);
left_out = stream.avail_out;
if ( zerr == Z_BUF_ERROR && stream.avail_in == 0 )
continue;
if ( zerr != Z_OK ) {
/* EOF, error, or trying to read beyond end of input */
if ( err && zerr == Z_MEM_ERROR )
err = -ENOMEM;
if ( zerr != Z_STREAM_END )
printk(KERN_DEBUG "zisofs: zisofs_inflate returned %d, inode = %lu, index = %lu, fpage = %d, xpage = %d, avail_in = %d, avail_out = %d, ai = %d, ao = %d\n",
zerr, inode->i_ino, index,
fpage, xpage,
stream.avail_in, stream.avail_out,
ai, ao);
bail = 1;
break;
}
}
 
if ( stream.avail_out && zerr == Z_STREAM_END ) {
/* Fractional page written before EOF. This may
be the last page in the file. */
memset(stream.next_out, 0, stream.avail_out);
stream.avail_out = 0;
}
 
if ( !stream.avail_out ) {
/* This page completed */
if ( page ) {
flush_dcache_page(page);
SetPageUptodate(page);
kunmap(page);
UnlockPage(page);
if ( fpage == xpage )
err = 0; /* The critical page */
else
page_cache_release(page);
}
fpage++;
}
}
zlib_inflateEnd(&stream);
 
z_eio:
up(&zisofs_zlib_semaphore);
 
b_eio:
for ( i = 0 ; i < haveblocks ; i++ ) {
if ( bhs[i] )
brelse(bhs[i]);
}
}
 
eio:
 
/* Release any residual pages, do not SetPageUptodate */
while ( fpage < maxpage ) {
page = pages[fpage];
if ( page ) {
flush_dcache_page(page);
if ( fpage == xpage )
SetPageError(page);
kunmap(page);
UnlockPage(page);
if ( fpage != xpage )
page_cache_release(page);
}
fpage++;
}
 
/* At this point, err contains 0 or -EIO depending on the "critical" page */
return err;
}
 
struct address_space_operations zisofs_aops = {
readpage: zisofs_readpage,
/* No sync_page operation supported? */
/* No bmap operation supported */
};
 
static int initialized = 0;
 
int __init zisofs_init(void)
{
if ( initialized ) {
printk("zisofs_init: called more than once\n");
return 0;
}
 
zisofs_zlib_workspace = vmalloc(zlib_inflate_workspacesize());
if ( !zisofs_zlib_workspace )
return -ENOMEM;
init_MUTEX(&zisofs_zlib_semaphore);
 
initialized = 1;
return 0;
}
 
void __exit zisofs_cleanup(void)
{
if ( !initialized ) {
printk("zisofs_cleanup: called without initialization\n");
return;
}
 
vfree(zisofs_zlib_workspace);
initialized = 0;
}
/Makefile
0,0 → 1,20
#
# Makefile for the Linux isofs filesystem routines.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (not a .c file).
#
# Note 2! The CFLAGS definitions are now in the main makefile.
 
O_TARGET := isofs.o
 
obj-y := namei.o inode.o dir.o util.o rock.o
obj-$(CONFIG_JOLIET) += joliet.o
obj-$(CONFIG_ZISOFS) += compress.o
 
obj-m := $(O_TARGET)
 
CFLAGS_compress.o := -I $(TOPDIR)/fs/inflate_fs
 
include $(TOPDIR)/Rules.make
/util.c
0,0 → 1,82
/*
* linux/fs/isofs/util.c
*/
 
#include <linux/time.h>
#include <linux/iso_fs.h>
 
/*
* We have to convert from a MM/DD/YY format to the Unix ctime format.
* We have to take into account leap years and all of that good stuff.
* Unfortunately, the kernel does not have the information on hand to
* take into account daylight savings time, but it shouldn't matter.
* The time stored should be localtime (with or without DST in effect),
* and the timezone offset should hold the offset required to get back
* to GMT. Thus we should always be correct.
*/
 
int iso_date(char * p, int flag)
{
int year, month, day, hour, minute, second, tz;
int crtime, days, i;
 
year = p[0] - 70;
month = p[1];
day = p[2];
hour = p[3];
minute = p[4];
second = p[5];
if (flag == 0) tz = p[6]; /* High sierra has no time zone */
else tz = 0;
if (year < 0) {
crtime = 0;
} else {
int monlen[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
 
days = year * 365;
if (year > 2)
days += (year+1) / 4;
for (i = 1; i < month; i++)
days += monlen[i-1];
if (((year+2) % 4) == 0 && month > 2)
days++;
days += day - 1;
crtime = ((((days * 24) + hour) * 60 + minute) * 60)
+ second;
 
/* sign extend */
if (tz & 0x80)
tz |= (-1 << 8);
/*
* The timezone offset is unreliable on some disks,
* so we make a sanity check. In no case is it ever
* more than 13 hours from GMT, which is 52*15min.
* The time is always stored in localtime with the
* timezone offset being what get added to GMT to
* get to localtime. Thus we need to subtract the offset
* to get to true GMT, which is what we store the time
* as internally. On the local system, the user may set
* their timezone any way they wish, of course, so GMT
* gets converted back to localtime on the receiving
* system.
*
* NOTE: mkisofs in versions prior to mkisofs-1.10 had
* the sign wrong on the timezone offset. This has now
* been corrected there too, but if you are getting screwy
* results this may be the explanation. If enough people
* complain, a user configuration option could be added
* to add the timezone offset in with the wrong sign
* for 'compatibility' with older discs, but I cannot see how
* it will matter that much.
*
* Thanks to kuhlmav@elec.canterbury.ac.nz (Volker Kuhlmann)
* for pointing out the sign error.
*/
if (-52 <= tz && tz <= 52)
crtime -= tz * 15 * 60;
}
return crtime;
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.