URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/tags/before_ORP/uclinux/uClinux-2.0.x/fs/isofs
- from Rev 901 to Rev 1765
- ↔ Reverse comparison
Rev 901 → Rev 1765
/rock.c
0,0 → 1,554
/* |
* linux/fs/isofs/rock.c |
* |
* (C) 1992, 1993 Eric Youngdale |
* |
* Rock Ridge Extensions to iso9660 |
* |
* Modifed by Russell King 11/02/96 |
* Allows ISO rock ridge extensions to work on ARM. |
*/ |
|
#include <linux/stat.h> |
#include <linux/sched.h> |
#include <linux/iso_fs.h> |
#include <linux/string.h> |
#include <linux/mm.h> |
#include <linux/malloc.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 << 8) | B) |
|
|
/* 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; |
|
/* 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= (int)(((struct iso_directory_record *)0)->name) + DE->name_len[0]; \ |
if(LEN & 1) LEN++; \ |
CHR = ((unsigned char *) DE) + LEN; \ |
LEN = *((unsigned char *) DE) - LEN;} |
|
#define MAYBE_CONTINUE(LABEL,DEV) \ |
{if (buffer) kfree(buffer); \ |
if (cont_extent){ \ |
int block, offset, offset1; \ |
struct buffer_head * bh; \ |
buffer = kmalloc(cont_size,GFP_KERNEL); \ |
if (!buffer) goto out; \ |
block = cont_extent; \ |
offset = cont_offset; \ |
offset1 = 0; \ |
if(ISOFS_BUFFER_SIZE(DEV) == 1024) { \ |
block <<= 1; \ |
if (offset >= 1024) block++; \ |
offset &= 1023; \ |
if(offset + cont_size >= 1024) { \ |
bh = bread(DEV->i_dev, block++, ISOFS_BUFFER_SIZE(DEV)); \ |
if(!bh) {printk("Unable to read continuation Rock Ridge record\n"); \ |
kfree(buffer); \ |
buffer = NULL; } else { \ |
memcpy(buffer, bh->b_data + offset, 1024 - offset); \ |
brelse(bh); \ |
offset1 = 1024 - offset; \ |
offset = 0;} \ |
} \ |
}; \ |
if(buffer) { \ |
bh = bread(DEV->i_dev, block, ISOFS_BUFFER_SIZE(DEV)); \ |
if(bh){ \ |
memcpy(buffer + offset1, bh->b_data + offset, cont_size - offset1); \ |
brelse(bh); \ |
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 = (chr[0] << 8) + chr[1]; |
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'): |
#ifdef DEBUG |
printk("RR: CL\n"); |
#endif |
if (flag == 0) { |
retval = isonum_733(rr->u.CL.location); |
goto out; |
}; |
break; |
case SIG('P','L'): |
#ifdef DEBUG |
printk("RR: PL\n"); |
#endif |
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; |
} |
|
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; |
retnamlen = 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 = (chr[0] << 8) + chr[1]; |
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 (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'): |
#ifdef DEBUG |
printk("RR: RE (%x)\n", inode->i_ino); |
#endif |
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(struct iso_directory_record * de, |
struct inode * inode){ |
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); |
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 = (chr[0] << 8) + chr[1]; |
chr += rr->len; |
len -= rr->len; |
|
switch(sig){ |
case SIG('R','R'): |
if((rr->u.RR.flags[0] & |
(RR_PX | RR_TF | RR_SL | RR_CL)) == 0) goto out; |
break; |
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"ISO9660 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'): |
{ |
struct stamp *times = (struct stamp *) &(rr->u.TF.__times); |
/* 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(times[cnt++].time, 0); |
if(rr->u.TF.flags & TF_MODIFY) |
inode->i_mtime = iso_date(times[cnt++].time, 0); |
if(rr->u.TF.flags & TF_ACCESS) |
inode->i_atime = iso_date(times[cnt++].time, 0); |
if(rr->u.TF.flags & TF_ATTRIBUTES) |
inode->i_ctime = iso_date(times[cnt++].time, 0); |
break; |
} |
case SIG('S','L'): |
{int slen; |
struct SL_component * slp; |
struct SL_component * oldslp; |
slen = rr->len - 5; |
slp = (struct SL_component *)&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("Attempt to read inode for relocated directory\n"); |
goto out; |
case SIG('C','L'): |
#ifdef DEBUG |
printk("RR CL (%x)\n",inode->i_ino); |
#endif |
inode->u.isofs_i.i_first_extent = isonum_733(rr->u.CL.location) << |
inode -> i_sb -> u.isofs_sb.s_log_zone_size; |
reloc = iget(inode->i_sb, inode->u.isofs_i.i_first_extent); |
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_atime = reloc->i_atime; |
inode->i_ctime = reloc->i_ctime; |
inode->i_mtime = reloc->i_mtime; |
iput(reloc); |
break; |
default: |
break; |
} |
}; |
} |
MAYBE_CONTINUE(repeat,inode); |
return 0; |
out: |
if(buffer) kfree(buffer); |
return 0; |
} |
|
|
/* Returns the name of the file that this inode is symlinked to. This is |
in malloc'd memory, so it needs to be freed, once we are through with it */ |
|
char * get_rock_ridge_symlink(struct inode * inode) |
{ |
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); |
unsigned char bufbits = ISOFS_BUFFER_BITS(inode); |
struct buffer_head * bh; |
unsigned char * pnt; |
void * cpnt = NULL; |
char * rpnt; |
struct iso_directory_record * raw_inode; |
CONTINUE_DECLS; |
int block; |
int sig; |
int rootflag; |
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"); |
|
rpnt = 0; |
|
block = inode->i_ino >> bufbits; |
if (!(bh=bread(inode->i_dev,block, bufsize))) { |
printk("unable to read i-node block"); |
return NULL; |
}; |
|
pnt = ((unsigned char *) bh->b_data) + (inode->i_ino & (bufsize - 1)); |
|
raw_inode = ((struct iso_directory_record *) pnt); |
|
if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize){ |
int frag1, offset; |
|
offset = (inode->i_ino & (bufsize - 1)); |
frag1 = bufsize - offset; |
cpnt = kmalloc(*pnt,GFP_KERNEL); |
if(!cpnt) return NULL; |
memcpy(cpnt, bh->b_data + offset, frag1); |
brelse(bh); |
if (!(bh = bread(inode->i_dev,++block, bufsize))) { |
kfree(cpnt); |
printk("unable to read i-node block"); |
return NULL; |
}; |
offset += *pnt - bufsize; |
memcpy((char *)cpnt+frag1, bh->b_data, offset); |
pnt = ((unsigned char *) cpnt); |
raw_inode = ((struct iso_directory_record *) pnt); |
}; |
|
/* 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 = (chr[0] << 8) + chr[1]; |
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'): |
{int slen; |
struct SL_component * oldslp; |
struct SL_component * slp; |
slen = rr->len - 5; |
slp = (struct SL_component *)&rr->u.SL.__link; |
while (slen > 1){ |
if (!rpnt){ |
rpnt = (char *) kmalloc (inode->i_size +1, GFP_KERNEL); |
if (!rpnt) goto out; |
*rpnt = 0; |
}; |
rootflag = 0; |
switch(slp->flags &~1){ |
case 0: |
strncat(rpnt,slp->text, slp->len); |
break; |
case 2: |
strcat(rpnt,"."); |
break; |
case 4: |
strcat(rpnt,".."); |
break; |
case 8: |
rootflag = 1; |
strcat(rpnt,"/"); |
break; |
default: |
printk("Symlink component flag not implemented (%d)\n",slen); |
}; |
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( ((rr->u.SL.flags & 1) != 0) |
&& ((oldslp->flags & 1) == 0) ) strcat(rpnt,"/"); |
break; |
} |
|
/* |
* If this component record isn't continued, then append a '/'. |
*/ |
if( (!rootflag) |
&& ((oldslp->flags & 1) == 0) ) strcat(rpnt,"/"); |
|
}; |
break; |
case SIG('C','E'): |
CHECK_CE; /* This tells is if there is a continuation record */ |
break; |
default: |
break; |
} |
}; |
}; |
MAYBE_CONTINUE(repeat,inode); |
brelse(bh); |
|
if (cpnt) { |
kfree(cpnt); |
cpnt = NULL; |
}; |
|
return rpnt; |
out: |
if(buffer) kfree(buffer); |
return 0; |
} |
|
|
|
|
|
|
/dir.c
0,0 → 1,320
/* |
* linux/fs/isofs/dir.c |
* |
* (C) 1992, 1993, 1994 Eric Youngdale Modified for ISO9660 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/malloc.h> |
#include <linux/sched.h> |
#include <linux/locks.h> |
|
#include <asm/segment.h> |
|
|
static int isofs_readdir(struct inode *, struct file *, void *, filldir_t); |
|
static struct file_operations isofs_dir_operations = |
{ |
NULL, /* lseek - default */ |
NULL, /* read */ |
NULL, /* write - bad */ |
isofs_readdir, /* readdir */ |
NULL, /* select - default */ |
NULL, /* ioctl - default */ |
NULL, /* no special open code */ |
NULL, /* no special release code */ |
NULL /* fsync */ |
}; |
|
/* |
* directories can handle most operations... |
*/ |
struct inode_operations isofs_dir_inode_operations = |
{ |
&isofs_dir_operations, /* default directory file-ops */ |
NULL, /* create */ |
isofs_lookup, /* lookup */ |
NULL, /* link */ |
NULL, /* unlink */ |
NULL, /* symlink */ |
NULL, /* mkdir */ |
NULL, /* rmdir */ |
NULL, /* mknod */ |
NULL, /* rename */ |
NULL, /* readlink */ |
NULL, /* follow_link */ |
NULL, /* readpage */ |
NULL, /* writepage */ |
isofs_bmap, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
|
static int parent_inode_number(struct inode * inode, struct iso_directory_record * de) |
{ |
int inode_number = inode->i_ino; |
|
if ((inode->i_sb->u.isofs_sb.s_firstdatazone) != inode->i_ino) |
inode_number = inode->u.isofs_i.i_backlink; |
|
if (inode_number != -1) |
return inode_number; |
|
/* This should never happen, but who knows. Try to be forgiving */ |
return isofs_lookup_grandparent(inode, find_rock_ridge_relocation(de, inode)); |
} |
|
static int isofs_name_translate(char * old, int len, char * new) |
{ |
int i, c; |
|
for (i = 0; i < len; i++) { |
c = old[i]; |
if (!c) |
break; |
if (c >= 'A' && c <= 'Z') |
c |= 0x20; /* lower case */ |
|
/* Drop trailing '.;1' (ISO9660: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; |
} |
|
/* |
* 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; |
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 = isofs_bmap(inode, filp->f_pos >> bufbits); |
high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra; |
|
if (!block) |
return 0; |
|
if (!(bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size))) |
return 0; |
|
while (filp->f_pos < inode->i_size) { |
int de_len, next_offset; |
#ifdef DEBUG |
printk("Block, offset, f_pos: %x %x %x\n", |
block, offset, filp->f_pos); |
printk("inode->i_size = %x\n",inode->i_size); |
#endif |
/* Next directory_record on next CDROM sector */ |
if (offset >= bufsize) { |
#ifdef DEBUG |
printk("offset >= bufsize\n"); |
#endif |
brelse(bh); |
offset = 0; |
block = isofs_bmap(inode, (filp->f_pos) >> bufbits); |
if (!block) |
return 0; |
bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size); |
if (!bh) |
return 0; |
continue; |
} |
|
de = (struct iso_directory_record *) (bh->b_data + offset); |
if(first_de) inode_number = (block << bufbits) + (offset & (bufsize - 1)); |
|
de_len = *(unsigned char *) de; |
#ifdef DEBUG |
printk("de_len = %ld\n", de_len); |
#endif |
|
|
/* 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); |
filp->f_pos = ((filp->f_pos & ~(ISOFS_BLOCK_SIZE - 1)) |
+ ISOFS_BLOCK_SIZE); |
offset = 0; |
block = isofs_bmap(inode, (filp->f_pos) >> bufbits); |
if (!block) |
return 0; |
bh = breada(inode->i_dev, block, bufsize, filp->f_pos, inode->i_size); |
if (!bh) |
return 0; |
continue; |
} |
|
/* Make sure that the entire directory record is in the |
current bh block. |
If not, put the two halves together in "tmpde" */ |
next_offset = offset + de_len; |
if (next_offset > bufsize) { |
#ifdef DEBUG |
printk("next_offset (%x) > bufsize (%x)\n",next_offset,bufsize); |
#endif |
next_offset &= (bufsize - 1); |
memcpy(tmpde, de, bufsize - offset); |
brelse(bh); |
block = isofs_bmap(inode, (filp->f_pos + de_len) >> bufbits); |
if (!block) |
{ |
return 0; |
} |
|
bh = breada(inode->i_dev, block, bufsize, |
filp->f_pos, |
inode->i_size); |
if (!bh) |
{ |
#ifdef DEBUG |
printk("!bh block=%ld, bufsize=%ld\n",block,bufsize); |
printk("filp->f_pos = %ld\n",filp->f_pos); |
printk("inode->i_size = %ld\n", inode->i_size); |
#endif |
return 0; |
} |
|
memcpy(bufsize - offset + (char *) tmpde, bh->b_data, next_offset); |
de = tmpde; |
} |
offset = next_offset; |
|
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) < 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 = parent_inode_number(inode, de); |
if (inode_number == -1) |
break; |
if (filldir(dirent, "..", 2, filp->f_pos, inode_number) < 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) { |
p = tmpname; |
map = 0; |
} |
} |
if (map) { |
if (inode->i_sb->u.isofs_sb.s_joliet_level) { |
len = get_joliet_filename(de, inode, tmpname); |
p = tmpname; |
} else { |
if (inode->i_sb->u.isofs_sb.s_mapping == 'n') { |
len = isofs_name_translate(de->name, de->name_len[0], |
tmpname); |
p = tmpname; |
} else { |
p = de->name; |
len = de->name_len[0]; |
} |
} |
} |
if (len > 0) { |
if (filldir(dirent, p, len, filp->f_pos, inode_number) < 0) |
break; |
|
dcache_add(inode, p, len, inode_number); |
} |
filp->f_pos += de_len; |
continue; |
} |
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 inode *inode, struct file *filp, |
void *dirent, filldir_t filldir) |
{ |
int result; |
char * tmpname; |
struct iso_directory_record * tmpde; |
|
if (!inode || !S_ISDIR(inode->i_mode)) |
return -EBADF; |
|
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; |
} |
/inode.c
0,0 → 1,1197
/* |
* linux/fs/isofs/inode.c |
* |
* (C) 1991 Linus Torvalds - minix filesystem |
* 1992, 1993, 1994 Eric Youngdale Modified for ISO9660 filesystem. |
* 1994 Eberhard Moenkeberg - multi session handling. |
* 1995 Mark Dobie - allow mounting of some weird VideoCDs and PhotoCDs. |
* |
*/ |
|
#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/malloc.h> |
#include <linux/errno.h> |
#include <linux/cdrom.h> |
#include <linux/nls.h> |
|
#include <asm/system.h> |
#include <asm/segment.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 |
|
/* |
* A home-burnt Joliet level 3 cd-rom with a 100 MB zip file had more than |
* 100 file sections, so the limit should be larger than that. What does the |
* ISO9660 standard say? (Ulrik Dickow <ukd@kampsax.dk>) |
*/ |
#define MAX_FILE_SECTIONS 1000 |
|
#ifdef LEAK_CHECK |
static int check_malloc = 0; |
static int check_bread = 0; |
#endif |
|
void isofs_put_super(struct super_block *sb) |
{ |
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; |
} |
lock_super(sb); |
|
#ifdef LEAK_CHECK |
printk("Outstanding mallocs:%d, outstanding buffers: %d\n", |
check_malloc, check_bread); |
#endif |
sb->s_dev = 0; |
unlock_super(sb); |
MOD_DEC_USE_COUNT; |
return; |
} |
|
static struct super_operations isofs_sops = { |
isofs_read_inode, |
NULL, /* notify_change */ |
NULL, /* write_inode */ |
NULL, /* put_inode */ |
isofs_put_super, |
NULL, /* write_super */ |
isofs_statfs, |
NULL |
}; |
|
struct iso9660_options{ |
char map; |
char rock; |
char joliet; |
char cruft; |
char unhide; |
unsigned char check; |
unsigned char conversion; |
unsigned int blocksize; |
mode_t mode; |
gid_t gid; |
uid_t uid; |
char *iocharset; |
unsigned char utf8; |
}; |
|
static int parse_options(char *options, struct iso9660_options * popt) |
{ |
char *this_char,*value,*p; |
int len; |
|
popt->map = 'n'; |
popt->rock = 'y'; |
popt->joliet = 'y'; |
popt->cruft = 'n'; |
popt->unhide = 'n'; |
popt->check = 's'; /* default: strict */ |
popt->conversion = 'b'; /* default: no conversion */ |
popt->blocksize = 1024; |
popt->mode = S_IRUGO; |
popt->gid = 0; |
popt->uid = 0; |
popt->iocharset = NULL; |
popt->utf8 = 0; |
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 ((value = strchr(this_char,'=')) != NULL) |
*value++ = 0; |
|
if (!strcmp(this_char,"iocharset")) { |
p = value; |
while (*value && *value != ',') value++; |
len = value - p; |
if (len) { |
popt->iocharset = kmalloc(len+1, GFP_KERNEL); |
memcpy(popt->iocharset, p, len); |
popt->iocharset[len] = 0; |
} else { |
popt->iocharset = NULL; |
return 0; |
} |
} |
else if (!strcmp(this_char,"map") && value) { |
if (value[0] && !value[1] && strchr("on",*value)) |
popt->map = *value; |
else if (!strcmp(value,"off")) popt->map = 'o'; |
else if (!strcmp(value,"normal")) popt->map = 'n'; |
else return 0; |
} |
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) { |
if (value[0] && !value[1] && strchr("btma",*value)) |
popt->conversion = *value; |
else if (!strcmp(value,"binary")) popt->conversion = 'b'; |
else if (!strcmp(value,"text")) popt->conversion = 't'; |
else if (!strcmp(value,"mtext")) popt->conversion = 'm'; |
else if (!strcmp(value,"auto")) popt->conversion = 'a'; |
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(kdev_t dev) |
{ |
struct cdrom_multisession ms_info; |
unsigned int vol_desc_start; |
struct inode inode_fake; |
extern struct file_operations * get_blkfops(unsigned int); |
int i; |
|
vol_desc_start=0; |
if (get_blkfops(MAJOR(dev))->ioctl!=NULL) |
{ |
/* Whoops. We must save the old FS, since otherwise |
* we would destroy the kernels idea about FS on root |
* mount in read_super... [chexum] |
*/ |
unsigned long old_fs=get_fs(); |
inode_fake.i_rdev=dev; |
ms_info.addr_format=CDROM_LBA; |
set_fs(KERNEL_DS); |
i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake, |
NULL, |
CDROMMULTISESSION, |
(unsigned long) &ms_info); |
set_fs(old_fs); |
#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 0 |
if (i==0) |
#if WE_OBEY_THE_WRITTEN_STANDARDS |
if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */ |
#endif WE_OBEY_THE_WRITTEN_STANDARDS |
vol_desc_start=ms_info.addr.lba; |
} |
return vol_desc_start; |
} |
|
struct super_block *isofs_read_super(struct super_block *s,void *data, |
int silent) |
{ |
struct buffer_head *bh=NULL; |
int iso_blknum; |
unsigned int blocksize_bits; |
int high_sierra; |
kdev_t dev = s->s_dev; |
unsigned int vol_desc_start; |
int orig_zonesize; |
char *p; |
int joliet_level = 0; |
|
struct iso_volume_descriptor *vdp; |
struct hs_volume_descriptor *hdp; |
|
struct iso_primary_descriptor *pri = NULL; |
struct iso_supplementary_descriptor *sec = NULL; |
struct hs_primary_descriptor *h_pri = NULL; |
|
struct iso_directory_record *rootp; |
|
struct iso9660_options opt; |
|
MOD_INC_USE_COUNT; |
|
if (!parse_options((char *) data,&opt)) { |
s->s_dev = 0; |
MOD_DEC_USE_COUNT; |
return NULL; |
} |
|
#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("conversion = %c\n", opt.conversion); |
printk("blocksize = %d\n", opt.blocksize); |
printk("gid = %d\n", opt.gid); |
printk("uid = %d\n", opt.uid); |
#endif |
|
blocksize_bits = 0; |
{ |
int i = opt.blocksize; |
while (i != 1){ |
blocksize_bits++; |
i >>=1; |
} |
} |
set_blocksize(dev, opt.blocksize); |
|
lock_super(s); |
|
s->u.isofs_sb.s_high_sierra = high_sierra = 0; /* default is iso9660 */ |
|
vol_desc_start = isofs_get_last_session(dev); |
|
for (iso_blknum = vol_desc_start+16; |
iso_blknum < vol_desc_start+100; iso_blknum++) |
{ |
int b = iso_blknum << (ISOFS_BLOCK_BITS-blocksize_bits); |
|
if (!(bh = bread(dev,b,opt.blocksize))) { |
s->s_dev = 0; |
printk("isofs_read_super: bread failed, dev " |
"%s iso_blknum %d block %d\n", |
kdevname(dev), iso_blknum, b); |
unlock_super(s); |
MOD_DEC_USE_COUNT; |
return NULL; |
} |
|
vdp = (struct iso_volume_descriptor *)bh->b_data; |
hdp = (struct hs_volume_descriptor *)bh->b_data; |
|
if (strncmp (hdp->id, HS_STANDARD_ID, sizeof hdp->id) == 0) { |
if (isonum_711 (hdp->type) != ISO_VD_PRIMARY) |
goto out; |
if (isonum_711 (hdp->type) == ISO_VD_END) |
goto out; |
|
s->u.isofs_sb.s_high_sierra = 1; |
high_sierra = 1; |
opt.rock = 'n'; |
h_pri = (struct hs_primary_descriptor *)vdp; |
break; |
} |
|
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; |
} |
} 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("ISO9660 Extensions: Microsoft Joliet Level %d\n", |
joliet_level); |
} |
break; |
} else { |
/* Unknown supplementary volume descriptor */ |
sec = NULL; |
} |
} |
/* Just skip any volume descriptors we don't recognize */ |
} |
|
brelse(bh); |
} |
if ((pri == NULL) && (sec == NULL) && (h_pri == NULL)) { |
if (!silent) |
printk("Unable to identify CD-ROM format.\n"); |
s->s_dev = 0; |
unlock_super(s); |
MOD_DEC_USE_COUNT; |
return NULL; |
} |
s->u.isofs_sb.s_joliet_level = joliet_level; |
if (joliet_level && 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) { |
printk("Multi-volume disks not supported.\n"); |
goto out; |
} |
#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) { |
printk("Multi-volume disks not supported.\n"); |
goto out; |
} |
#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 */ |
|
/* RDE: convert log zone size to bit shift */ |
|
orig_zonesize = s -> u.isofs_sb.s_log_zone_size; |
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: |
printk("Bad logical zone size %ld\n", s -> u.isofs_sb.s_log_zone_size); |
goto out; |
} |
|
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 */; |
|
brelse(bh); |
|
/* RDE: data zone now byte offset! */ |
|
s->u.isofs_sb.s_firstdatazone = ((isonum_733 (rootp->extent) + |
isonum_711 (rootp->ext_attr_length)) |
<< s -> u.isofs_sb.s_log_zone_size); |
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"); |
unlock_super(s); |
/* set up enough so that it can read an inode */ |
|
/* |
* 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. |
*/ |
if( orig_zonesize < opt.blocksize ) |
{ |
opt.blocksize = orig_zonesize; |
blocksize_bits = 0; |
{ |
int i = opt.blocksize; |
while (i != 1){ |
blocksize_bits++; |
i >>=1; |
} |
} |
set_blocksize(dev, opt.blocksize); |
printk(KERN_DEBUG "Forcing new log zone size:%d\n", opt.blocksize); |
} |
|
s->u.isofs_sb.s_nls_iocharset = NULL; |
if (joliet_level == 0) { |
if (opt.iocharset) { |
kfree(opt.iocharset); |
opt.iocharset = NULL; |
} |
} else if (opt.utf8 == 0) { |
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) { |
kfree(opt.iocharset); |
goto out; |
} else { |
s->u.isofs_sb.s_nls_iocharset = load_nls_default(); |
} |
} |
} |
s->s_dev = dev; |
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_name_check = opt.check; |
s->u.isofs_sb.s_conversion = opt.conversion; |
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; |
/* |
* 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; |
s->s_blocksize = opt.blocksize; |
s->s_blocksize_bits = blocksize_bits; |
s->s_mounted = 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 (joliet_level && opt.rock == 'y' && s->u.isofs_sb.s_rock != 1) { |
iput(s->s_mounted); |
pri = (struct iso_primary_descriptor *) sec; |
rootp = (struct iso_directory_record *) |
pri->root_directory_record; |
s->u.isofs_sb.s_firstdatazone = |
((isonum_733 (rootp->extent) + |
isonum_711 (rootp->ext_attr_length)) |
<< s -> u.isofs_sb.s_log_zone_size); |
s->s_mounted = iget(s, s->u.isofs_sb.s_firstdatazone); |
s->u.isofs_sb.s_rock = 0; |
} |
unlock_super(s); |
|
if (!(s->s_mounted)) { |
s->s_dev = 0; |
printk("get root inode failed\n"); |
if (s->u.isofs_sb.s_nls_iocharset) |
unload_nls(s->u.isofs_sb.s_nls_iocharset); |
if (opt.iocharset) kfree(opt.iocharset); |
MOD_DEC_USE_COUNT; |
return NULL; |
} |
|
#ifdef DO_FUNKY_BROKEN_MEDIA_CHANGE_CHECK |
if(!check_disk_change(s->s_dev)) { |
return s; |
} |
if (s->u.isofs_sb.s_nls_iocharset) |
unload_nls(s->u.isofs_sb.s_nls_iocharset); |
if (opt.iocharset) kfree(opt.iocharset); |
#else |
check_disk_change(s->s_dev); |
return s; |
#endif |
|
out: /* Kick out for various error conditions */ |
brelse(bh); |
s->s_dev = 0; |
unlock_super(s); |
MOD_DEC_USE_COUNT; |
return NULL; |
} |
|
void isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) |
{ |
struct statfs tmp; |
|
tmp.f_type = ISOFS_SUPER_MAGIC; |
tmp.f_bsize = sb->s_blocksize; |
tmp.f_blocks = (sb->u.isofs_sb.s_nzones |
<< (sb->u.isofs_sb.s_log_zone_size - sb->s_blocksize_bits)); |
tmp.f_bfree = 0; |
tmp.f_bavail = 0; |
tmp.f_files = sb->u.isofs_sb.s_ninodes; |
tmp.f_ffree = 0; |
tmp.f_namelen = NAME_MAX; |
memcpy_tofs(buf, &tmp, bufsiz); |
} |
|
int isofs_bmap(struct inode * inode,int block) |
{ |
off_t b_off, offset, size; |
struct inode *ino; |
unsigned int firstext; |
unsigned long nextino; |
int i; |
|
if (block<0) { |
printk("_isofs_bmap: block<0\n"); |
return 0; |
} |
|
b_off = block << ISOFS_BUFFER_BITS(inode); |
|
/* |
* If we are beyond the end of this file, don't give out any |
* blocks. |
*/ |
if( (b_off > inode->i_size) || |
((b_off == inode->i_size) && (b_off & (PAGE_SIZE - 1))) ) |
{ |
off_t max_legal_read_offset; |
|
/* |
* 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 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. |
*/ |
max_legal_read_offset = (inode->i_size + PAGE_SIZE - 1) |
& ~(PAGE_SIZE - 1); |
if( b_off >= max_legal_read_offset ) |
{ |
|
printk("_isofs_bmap: block>= EOF(%d, %ld)\n", block, |
inode->i_size); |
} |
return 0; |
} |
|
offset = 0; |
firstext = inode->u.isofs_i.i_first_extent; |
size = inode->u.isofs_i.i_section_size; |
nextino = inode->u.isofs_i.i_next_section_ino; |
#ifdef DEBUG |
printk("first inode: inode=%lu nextino=%lu firstext=%u size=%lu\n", |
inode->i_ino, nextino, firstext, size); |
#endif |
i = 0; |
if (nextino) { |
while(b_off >= offset + size) { |
offset += size; |
|
if(nextino == 0) return 0; |
ino = iget(inode->i_sb, nextino); |
if(!ino) return 0; |
firstext = ino->u.isofs_i.i_first_extent; |
size = ino->u.isofs_i.i_section_size; |
#ifdef DEBUG |
printk("read inode: inode=%lu ino=%lu nextino=%lu firstext=%u size=%lu\n", |
inode->i_ino, nextino, ino->u.isofs_i.i_next_section_ino, firstext, size); |
#endif |
nextino = ino->u.isofs_i.i_next_section_ino; |
iput(ino); |
|
if(++i > MAX_FILE_SECTIONS) { |
printk("isofs_bmap: More than %d file sections ?!?, aborting...\n", |
MAX_FILE_SECTIONS); |
printk("isofs_bmap: ino=%lu block=%d firstext=%u size=%u nextino=%lu\n", |
inode->i_ino, block, firstext, (unsigned)size, nextino); |
return 0; |
} |
} |
} |
#ifdef DEBUG |
printk("isofs_bmap: mapped inode:block %lu:%d to block %lu\n", |
inode->i_ino, block, (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode)); |
#endif |
return (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode); |
} |
|
|
static void test_and_set_uid(uid_t *p, uid_t value) |
{ |
if(value) { |
*p = value; |
#if 0 |
printk("Resetting to %d\n", value); |
#endif |
} |
} |
|
static int isofs_read_level3_size(struct inode * inode) |
{ |
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); |
struct buffer_head * bh = NULL; |
struct iso_directory_record * raw_inode = NULL; /* quiet gcc */ |
unsigned char *pnt = NULL; |
void *cpnt = NULL; |
int block = 0; /* Quiet GCC */ |
unsigned long ino; |
int i; |
|
inode->i_size = 0; |
inode->u.isofs_i.i_next_section_ino = 0; |
ino = inode->i_ino; |
i = 0; |
do { |
if(i > MAX_FILE_SECTIONS) { |
printk("isofs_read_level3_size: More than %d file sections ?!?, aborting...\n" |
"isofs_read_level3_size: inode=%lu ino=%lu\n", MAX_FILE_SECTIONS, |
inode->i_ino, ino); |
return 0; |
} |
|
if(bh == NULL || block != ino >> ISOFS_BUFFER_BITS(inode)) { |
if(bh) brelse(bh); |
block = ino >> ISOFS_BUFFER_BITS(inode); |
if (!(bh=bread(inode->i_dev,block, bufsize))) { |
printk("unable to read i-node block"); |
return 1; |
} |
} |
pnt = ((unsigned char *) bh->b_data |
+ (ino & (bufsize - 1))); |
|
if ((ino & (bufsize - 1)) + *pnt > bufsize){ |
int frag1, offset; |
|
offset = (ino & (bufsize - 1)); |
frag1 = bufsize - offset; |
cpnt = kmalloc(*pnt,GFP_KERNEL); |
if (cpnt == NULL) { |
printk(KERN_INFO "NoMem ISO inode %lu\n",inode->i_ino); |
brelse(bh); |
return 1; |
} |
memcpy(cpnt, bh->b_data + offset, frag1); |
brelse(bh); |
if (!(bh = bread(inode->i_dev,++block, bufsize))) { |
kfree(cpnt); |
printk("unable to read i-node block"); |
return 1; |
} |
offset += *pnt - bufsize; |
memcpy((char *)cpnt+frag1, bh->b_data, offset); |
pnt = ((unsigned char *) cpnt); |
} |
|
if(*pnt == 0) { |
ino = (ino & ~(ISOFS_BLOCK_SIZE - 1)) + ISOFS_BLOCK_SIZE; |
continue; |
} |
raw_inode = ((struct iso_directory_record *) pnt); |
|
inode->i_size += isonum_733 (raw_inode->size); |
if(i == 1) inode->u.isofs_i.i_next_section_ino = ino; |
|
ino += *pnt; |
if (cpnt) { |
kfree (cpnt); |
cpnt = NULL; |
} |
i++; |
} while(raw_inode->flags[-inode->i_sb->u.isofs_sb.s_high_sierra] & 0x80); |
brelse(bh); |
return 0; |
} |
|
void isofs_read_inode(struct inode * inode) |
{ |
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); |
struct buffer_head * bh; |
struct iso_directory_record * raw_inode; |
unsigned char *pnt = NULL; |
void *cpnt = NULL; |
int high_sierra; |
int block; |
int volume_seq_no ; |
int i; |
|
block = inode->i_ino >> ISOFS_BUFFER_BITS(inode); |
if (!(bh=bread(inode->i_dev,block, bufsize))) { |
printk("unable to read i-node block"); |
goto fail; |
} |
|
pnt = ((unsigned char *) bh->b_data |
+ (inode->i_ino & (bufsize - 1))); |
raw_inode = ((struct iso_directory_record *) pnt); |
high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra; |
|
if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize){ |
int frag1, offset; |
|
offset = (inode->i_ino & (bufsize - 1)); |
frag1 = bufsize - offset; |
cpnt = kmalloc(*pnt,GFP_KERNEL); |
if (cpnt == NULL) { |
printk(KERN_INFO "NoMem ISO inode %lu\n",inode->i_ino); |
brelse(bh); |
goto fail; |
} |
memcpy(cpnt, bh->b_data + offset, frag1); |
brelse(bh); |
if (!(bh = bread(inode->i_dev,++block, bufsize))) { |
kfree(cpnt); |
printk("unable to read i-node block"); |
goto fail; |
} |
offset += *pnt - bufsize; |
memcpy((char *)cpnt+frag1, bh->b_data, offset); |
pnt = ((unsigned char *) cpnt); |
raw_inode = ((struct iso_directory_record *) pnt); |
} |
|
if (raw_inode->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 { |
inode->i_mode = inode->i_sb->u.isofs_sb.s_mode; /* Everybody gets to read the file. */ |
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< raw_inode->name_len[0]; i++) |
if(raw_inode->name[i]=='.' || raw_inode->name[i]==';') |
break; |
if(i == raw_inode->name_len[0] || raw_inode->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->u.isofs_i.i_section_size = isonum_733 (raw_inode->size); |
if(raw_inode->flags[-high_sierra] & 0x80) { |
if(isofs_read_level3_size(inode)) goto fail; |
} else { |
inode->i_size = isonum_733 (raw_inode->size); |
} |
|
/* There are defective discs out there - we do this to protect |
ourselves. A cdrom will never contain more than 800Mb |
Allow 1Gig for DVD however - Ulrich Habel */ |
if((inode->i_size < 0 || inode->i_size > 1073741824) && |
inode->i_sb->u.isofs_sb.s_cruft == 'n') { |
printk("Warning: defective cdrom. 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){ |
/* printk("Illegal format on cdrom. Pester manufacturer.\n"); */ |
inode->i_size &= 0x00ffffff; |
} |
|
if (raw_inode->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(raw_inode->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((raw_inode->flags[-high_sierra] & ~2)!= 0){ |
printk("Unusual flag settings for ISO file (%ld %x).\n", |
inode->i_ino, raw_inode->flags[-high_sierra]); |
} |
#endif |
|
#ifdef DEBUG |
printk("Get inode %d: %d %d: %d\n",inode->i_ino, block, |
((int)pnt) & 0x3ff, inode->i_size); |
#endif |
|
inode->i_mtime = inode->i_atime = inode->i_ctime = |
iso_date(raw_inode->date, high_sierra); |
|
inode->u.isofs_i.i_first_extent = (isonum_733 (raw_inode->extent) + |
isonum_711 (raw_inode->ext_attr_length)) |
<< inode -> i_sb -> u.isofs_sb.s_log_zone_size; |
|
inode->u.isofs_i.i_backlink = 0xffffffff; /* Will be used for previous directory */ |
switch (inode->i_sb->u.isofs_sb.s_conversion){ |
case 'a': |
inode->u.isofs_i.i_file_format = ISOFS_FILE_UNKNOWN; /* File type */ |
break; |
case 'b': |
inode->u.isofs_i.i_file_format = ISOFS_FILE_BINARY; /* File type */ |
break; |
case 't': |
inode->u.isofs_i.i_file_format = ISOFS_FILE_TEXT; /* File type */ |
break; |
case 'm': |
inode->u.isofs_i.i_file_format = ISOFS_FILE_TEXT_M; /* File type */ |
break; |
} |
|
/* 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(raw_inode, inode); |
/* hmm..if we want uid or gid set, override the rock ridge setting */ |
test_and_set_uid(&inode->i_uid, inode->i_sb->u.isofs_sb.s_uid); |
} |
|
#ifdef DEBUG |
printk("Inode: %x extent: %x\n",inode->i_ino, inode->u.isofs_i.i_first_extent); |
#endif |
brelse(bh); |
|
inode->i_op = NULL; |
|
/* get the volume sequence number */ |
volume_seq_no = isonum_723 (raw_inode->volume_sequence_number) ; |
|
/* |
* 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("Warning: defective cdrom (volume sequence number). Enabling \"cruft\" mount option.\n"); |
inode->i_sb->u.isofs_sb.s_cruft = 'y'; |
} |
|
#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("Multi volume CD somehow got mounted.\n"); |
} else |
#endif IGNORE_WRONG_MULTI_VOLUME_SPECS |
{ |
if (S_ISREG(inode->i_mode)) |
inode->i_op = &isofs_file_inode_operations; |
else if (S_ISDIR(inode->i_mode)) |
inode->i_op = &isofs_dir_inode_operations; |
else if (S_ISLNK(inode->i_mode)) |
inode->i_op = &isofs_symlink_inode_operations; |
else if (S_ISCHR(inode->i_mode)) |
inode->i_op = &chrdev_inode_operations; |
else if (S_ISBLK(inode->i_mode)) |
inode->i_op = &blkdev_inode_operations; |
else if (S_ISFIFO(inode->i_mode)) |
init_fifo(inode); |
} |
if (cpnt) { |
kfree (cpnt); |
cpnt = NULL; |
} |
return; |
fail: |
/* With a data error we return this information */ |
inode->i_mtime = inode->i_atime = inode->i_ctime = 0; |
inode->u.isofs_i.i_first_extent = 0; |
inode->u.isofs_i.i_backlink = 0xffffffff; |
inode->i_size = 0; |
inode->i_nlink = 1; |
inode->i_uid = inode->i_gid = 0; |
inode->i_mode = S_IFREG; /*Regular file, no one gets to read*/ |
inode->i_op = NULL; |
return; |
} |
|
/* There are times when we need to know the inode number of a parent of |
a particular directory. When control passes through a routine that |
has access to the parent information, it fills it into the inode structure, |
but sometimes the inode gets flushed out of the queue, and someone |
remembers the number. When they try to open up again, we have lost |
the information. The '..' entry on the disc points to the data area |
for a particular inode, so we can follow these links back up, but since |
we do not know the inode number, we do not actually know how large the |
directory is. The disc is almost always correct, and there is |
enough error checking on the drive itself, but an open ended search |
makes me a little nervous. |
|
The bsd iso filesystem uses the extent number for an inode, and this |
would work really nicely for us except that the read_inode function |
would not have any clean way of finding the actual directory record |
that goes with the file. If we had such info, then it would pay |
to change the inode numbers and eliminate this function. |
*/ |
|
int isofs_lookup_grandparent(struct inode * parent, int extent) |
{ |
unsigned long bufsize = ISOFS_BUFFER_SIZE(parent); |
unsigned char bufbits = ISOFS_BUFFER_BITS(parent); |
unsigned int block,offset; |
int parent_dir, inode_number; |
int old_offset; |
void * cpnt = NULL; |
int result; |
int directory_size; |
struct buffer_head * bh; |
struct iso_directory_record * de; |
|
offset = 0; |
block = extent << (ISOFS_ZONE_BITS(parent) - bufbits); |
if (!(bh = bread(parent->i_dev, block, bufsize))) return -1; |
|
while (1 == 1) { |
de = (struct iso_directory_record *) (bh->b_data + offset); |
if (*((unsigned char *) de) == 0) |
{ |
brelse(bh); |
printk("Directory .. not found\n"); |
return -1; |
} |
|
offset += *((unsigned char *) de); |
|
if (offset >= bufsize) |
{ |
printk(".. Directory not in first block" |
" of directory.\n"); |
brelse(bh); |
return -1; |
} |
|
if (de->name_len[0] == 1 && de->name[0] == 1) |
{ |
parent_dir = find_rock_ridge_relocation(de, parent); |
directory_size = isonum_733 (de->size); |
brelse(bh); |
break; |
} |
} |
#ifdef DEBUG |
printk("Parent dir:%x\n",parent_dir); |
#endif |
/* Now we know the extent where the parent dir starts on. */ |
|
result = -1; |
|
offset = 0; |
block = parent_dir << (ISOFS_ZONE_BITS(parent) - bufbits); |
if (!block || !(bh = bread(parent->i_dev,block, bufsize))) |
{ |
return -1; |
} |
|
for(;;) |
{ |
de = (struct iso_directory_record *) (bh->b_data + offset); |
inode_number = (block << bufbits)+(offset & (bufsize - 1)); |
|
/* 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 (*((unsigned char *) de) == 0) |
{ |
brelse(bh); |
offset = 0; |
block++; |
directory_size -= bufsize; |
if(directory_size < 0) return -1; |
if((block & 1) && (ISOFS_ZONE_BITS(parent) - bufbits) == 1) |
{ |
return -1; |
} |
if((block & 3) && (ISOFS_ZONE_BITS(parent) - bufbits) == 2) |
{ |
return -1; |
} |
if (!block |
|| !(bh = bread(parent->i_dev,block, bufsize))) |
{ |
return -1; |
} |
continue; |
} |
|
/* Make sure that the entire directory record is in the current |
bh block. If not, we malloc a buffer, and put the two |
halves together, so that we can cleanly read the block. */ |
|
old_offset = offset; |
offset += *((unsigned char *) de); |
|
if (offset >= bufsize) |
{ |
unsigned int frag1; |
frag1 = bufsize - old_offset; |
cpnt = kmalloc(*((unsigned char *) de),GFP_KERNEL); |
if (!cpnt) return -1; |
memcpy(cpnt, bh->b_data + old_offset, frag1); |
de = (struct iso_directory_record *) ((char *)cpnt); |
brelse(bh); |
offset -= bufsize; |
directory_size -= bufsize; |
if(directory_size < 0) |
{ |
printk("Directory size < 0\n"); |
return -1; |
} |
block++; |
if(!(bh = bread(parent->i_dev,block,bufsize))) { |
kfree(cpnt); |
return -1; |
} |
memcpy((char *)cpnt+frag1, bh->b_data, offset); |
} |
|
if (find_rock_ridge_relocation(de, parent) == extent){ |
result = inode_number; |
goto out; |
} |
|
if (cpnt) { |
kfree(cpnt); |
cpnt = NULL; |
} |
} |
|
/* We go here for any condition we cannot handle. |
We also drop through to here at the end of the directory. */ |
|
out: |
if (cpnt) { |
kfree(cpnt); |
cpnt = NULL; |
} |
brelse(bh); |
#ifdef DEBUG |
printk("Resultant Inode %d\n",result); |
#endif |
return result; |
} |
|
#ifdef LEAK_CHECK |
#undef malloc |
#undef free_s |
#undef 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_s(obj, size); |
} |
|
struct buffer_head * leak_check_bread(int dev, int block, int size){ |
check_bread++; |
return bread(dev, block, size); |
} |
|
void leak_check_brelse(struct buffer_head * bh){ |
check_bread--; |
return brelse(bh); |
} |
|
#endif |
|
static struct file_system_type iso9660_fs_type = { |
isofs_read_super, "iso9660", 1, NULL |
}; |
|
int init_iso9660_fs(void) |
{ |
return register_filesystem(&iso9660_fs_type); |
} |
|
#ifdef MODULE |
int init_module(void) |
{ |
int status; |
|
if ((status = init_iso9660_fs()) == 0) |
register_symtab(0); |
return status; |
} |
|
void cleanup_module(void) |
{ |
unregister_filesystem(&iso9660_fs_type); |
} |
|
#endif |
|
/file.c
0,0 → 1,56
/* |
* 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 <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/fs.h> |
#include <linux/iso_fs.h> |
|
/* |
* 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 */ |
generic_file_read, /* read */ |
NULL, /* write */ |
NULL, /* readdir - bad */ |
NULL, /* select - default */ |
NULL, /* ioctl - default */ |
generic_file_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 */ |
generic_readpage, /* readpage */ |
NULL, /* writepage */ |
isofs_bmap, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
/rock.h
0,0 → 1,114
/* 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 */ |
/* |
* Altered for word-aligned structure problems on ARM by Russell King. |
*/ |
|
struct SU_SP{ |
unsigned char magic[2]; |
unsigned char skip; |
}; |
|
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]; |
}; |
|
struct RR_RR{ |
char flags[1]; |
}; |
|
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]; |
}; |
|
struct RR_SL{ |
unsigned char flags; |
unsigned char __link,__dummy; |
}; |
|
struct RR_NM{ |
unsigned char flags; |
char name[0]; |
}; |
|
struct RR_CL{ |
char location[8]; |
}; |
|
struct RR_PL{ |
char location[8]; |
}; |
|
struct stamp{ |
char time[7]; |
}; |
|
struct RR_TF{ |
char flags; |
unsigned char __times[0]; |
}; |
|
/* 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; |
} 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 */ |
/symlink.c
0,0 → 1,108
/* |
* linux/fs/isofs/symlink.c |
* |
* (C) 1992 Eric Youngdale Modified for ISO9660 filesystem. |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
* |
* isofs symlink handling code. This is only used with the Rock Ridge |
* extensions to iso9660 |
*/ |
|
#include <linux/errno.h> |
#include <linux/sched.h> |
#include <linux/fs.h> |
#include <linux/iso_fs.h> |
#include <linux/stat.h> |
#include <linux/malloc.h> |
|
#include <asm/segment.h> |
|
static int isofs_readlink(struct inode *, char *, int); |
static int isofs_follow_link(struct inode *, struct inode *, int, int, struct inode **); |
|
/* |
* symlinks can't do much... |
*/ |
struct inode_operations isofs_symlink_inode_operations = { |
NULL, /* no file-operations */ |
NULL, /* create */ |
NULL, /* lookup */ |
NULL, /* link */ |
NULL, /* unlink */ |
NULL, /* symlink */ |
NULL, /* mkdir */ |
NULL, /* rmdir */ |
NULL, /* mknod */ |
NULL, /* rename */ |
isofs_readlink, /* readlink */ |
isofs_follow_link, /* follow_link */ |
NULL, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
|
static int isofs_follow_link(struct inode * dir, struct inode * inode, |
int flag, int mode, struct inode ** res_inode) |
{ |
int error; |
char * pnt; |
|
if (!dir) { |
dir = current->fs->root; |
dir->i_count++; |
} |
if (!inode) { |
iput(dir); |
*res_inode = NULL; |
return -ENOENT; |
} |
if (!S_ISLNK(inode->i_mode)) { |
iput(dir); |
*res_inode = inode; |
return 0; |
} |
if ((current->link_count > 5) || |
!(pnt = get_rock_ridge_symlink(inode))) { |
iput(dir); |
iput(inode); |
*res_inode = NULL; |
return -ELOOP; |
} |
iput(inode); |
current->link_count++; |
error = open_namei(pnt,flag,mode,res_inode,dir); |
current->link_count--; |
kfree(pnt); |
return error; |
} |
|
static int isofs_readlink(struct inode * inode, char * buffer, int buflen) |
{ |
char * pnt; |
int i; |
char c; |
|
if (!S_ISLNK(inode->i_mode)) { |
iput(inode); |
return -EINVAL; |
} |
|
if (buflen > 1023) |
buflen = 1023; |
pnt = get_rock_ridge_symlink(inode); |
|
iput(inode); |
if (!pnt) |
return 0; |
i = 0; |
|
while (i<buflen && (c = pnt[i])) { |
i++; |
put_user(c,buffer++); |
} |
kfree(pnt); |
return i; |
} |
/joliet.c
0,0 → 1,117
/* |
* 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/malloc.h> |
#include <linux/iso_fs.h> |
|
/* |
* Convert Unicode 16 to UTF8 or ascii. |
*/ |
static int |
uni16_to_x8(unsigned char *ascii, unsigned char *uni, int len, |
struct nls_table *nls) |
{ |
unsigned char *ip, *op; |
unsigned char ch, cl; |
unsigned char *uni_page; |
|
ip = uni; |
op = ascii; |
|
while ((*ip || ip[1]) && len) { |
ch = *ip++; |
cl = *ip++; |
|
uni_page = nls->page_uni2charset[ch]; |
if (uni_page && uni_page[cl]) { |
*op++ = uni_page[cl]; |
} else { |
*op++ = '?'; |
} |
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, struct inode * inode, |
unsigned char *outname) |
{ |
unsigned char utf8; |
struct nls_table *nls; |
unsigned char len = 0; |
int i; |
char c; |
|
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, 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 |
*/ |
while (len >= 2 && (outname[len-1] == '.')) { |
len--; |
} |
|
if (inode->i_sb->u.isofs_sb.s_name_check == 'r') { |
for (i = 0; i < len; i++) { |
c = outname[i]; |
/* lower case */ |
if (c >= 'A' && c <= 'Z') c |= 0x20; |
if (c == ';') c = '.'; |
outname[i] = c; |
} |
} |
|
return len; |
} |
/namei.c
0,0 → 1,305
/* |
* linux/fs/isofs/namei.c |
* |
* (C) 1992 Eric Youngdale Modified for ISO9660 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 <asm/segment.h> |
#include <linux/malloc.h> |
|
#include <linux/errno.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. |
* |
* NOTE! unlike strncmp, isofs_match returns 1 for success, 0 for failure. |
*/ |
static int isofs_match(int len,const char * name, const char * compare, int dlen) |
{ |
if (!compare) |
return 0; |
|
/* check special "." and ".." files */ |
if (dlen == 1) { |
/* "." */ |
if (compare[0] == 0) { |
if (!len) |
return 1; |
compare = "."; |
} else if (compare[0] == 1) { |
compare = ".."; |
dlen = 2; |
} |
} |
#if 0 |
if (len <= 2) printk("Match: %d %d %s %d %d \n",len,dlen,compare,de->name[0], dlen); |
#endif |
|
if (dlen != len) |
return 0; |
return !memcmp(name, compare, len); |
} |
|
/* |
* isofs_find_entry() |
* |
* finds an entry in the specified directory with the wanted name. It |
* returns the cache buffer in which the entry was found, and the entry |
* itself (as an inode number). It does NOT read the inode of the |
* entry - you'll have to do that yourself if you want to. |
*/ |
static struct buffer_head * |
isofs_find_entry(struct inode * dir, const char * name, int namelen, |
unsigned long * ino, unsigned long * ino_back) |
{ |
unsigned long bufsize = ISOFS_BUFFER_SIZE(dir); |
unsigned char bufbits = ISOFS_BUFFER_BITS(dir); |
unsigned int block, i, f_pos, offset, inode_number; |
struct buffer_head * bh; |
void * cpnt = NULL; |
unsigned int old_offset; |
unsigned int backlink; |
int dlen, match; |
char * dpnt; |
unsigned char *page = NULL; |
struct iso_directory_record * de; |
char c; |
|
*ino = 0; |
if (!dir) return NULL; |
|
if (!(block = dir->u.isofs_i.i_first_extent)) return NULL; |
|
f_pos = 0; |
|
offset = f_pos & (bufsize - 1); |
block = isofs_bmap(dir,f_pos >> bufbits); |
|
if (!block || !(bh = bread(dir->i_dev,block,bufsize))) return NULL; |
|
while (f_pos < dir->i_size) { |
de = (struct iso_directory_record *) (bh->b_data + offset); |
backlink = dir->i_ino; |
inode_number = (block << bufbits) + (offset & (bufsize - 1)); |
|
/* If byte is zero, this is the end of file, or time to move to |
the next sector. Usually 2048 byte boundaries. */ |
|
if (*((unsigned char *) de) == 0) { |
brelse(bh); |
offset = 0; |
f_pos = ((f_pos & ~(ISOFS_BLOCK_SIZE - 1)) |
+ ISOFS_BLOCK_SIZE); |
block = isofs_bmap(dir,f_pos>>bufbits); |
if (!block || !(bh = bread(dir->i_dev,block,bufsize))) |
return NULL; |
continue; /* Will kick out if past end of directory */ |
} |
|
old_offset = offset; |
offset += *((unsigned char *) de); |
f_pos += *((unsigned char *) de); |
|
/* Handle case where the directory entry spans two blocks. |
Usually 1024 byte boundaries */ |
if (offset >= bufsize) { |
unsigned int frag1; |
frag1 = bufsize - old_offset; |
cpnt = kmalloc(*((unsigned char *) de),GFP_KERNEL); |
if (!cpnt) return NULL; |
memcpy(cpnt, bh->b_data + old_offset, frag1); |
|
de = (struct iso_directory_record *) cpnt; |
brelse(bh); |
offset = f_pos & (bufsize - 1); |
block = isofs_bmap(dir,f_pos>>bufbits); |
if (!block || !(bh = bread(dir->i_dev,block,bufsize))) { |
kfree(cpnt); |
return NULL; |
}; |
memcpy((char *)cpnt+frag1, bh->b_data, offset); |
} |
|
dlen = de->name_len[0]; |
dpnt = de->name; |
|
/* Handle the '.' case */ |
|
if (*dpnt==0 && dlen==1) { |
inode_number = dir->i_ino; |
backlink = 0; |
} |
|
/* Handle the '..' case */ |
|
else if (*dpnt==1 && dlen==1) { |
#if 0 |
printk("Doing .. (%d %d)", |
dir->i_sb->s_firstdatazone, |
dir->i_ino); |
#endif |
if((dir->i_sb->u.isofs_sb.s_firstdatazone) != dir->i_ino) |
inode_number = dir->u.isofs_i.i_backlink; |
else |
inode_number = dir->i_ino; |
backlink = 0; |
} else { |
if (dir->i_sb->u.isofs_sb.s_rock || |
dir->i_sb->u.isofs_sb.s_joliet_level) { |
page = (unsigned char *) |
__get_free_page(GFP_KERNEL); |
if (!page) return NULL; |
} |
if (dir->i_sb->u.isofs_sb.s_rock && |
((i = get_rock_ridge_filename(de, page, dir)))){ |
if (i == -1) |
goto out;/* Relocated deep directory */ |
dlen = i; |
dpnt = page; |
} else if (dir->i_sb->u.isofs_sb.s_joliet_level) { |
dlen = get_joliet_filename(de, dir, page); |
dpnt = page; |
} else if (dir->i_sb->u.isofs_sb.s_mapping == 'n') { |
for (i = 0; i < dlen; i++) { |
c = dpnt[i]; |
/* lower case */ |
if (c >= 'A' && c <= 'Z') c |= 0x20; |
if (c == ';' && i == dlen-2 && dpnt[i+1] == '1') { |
dlen -= 2; |
break; |
} |
if (c == ';') c = '.'; |
dpnt[i] = c; |
} |
/* This allows us to match with and without |
* a trailing period. */ |
if(dpnt[dlen-1] == '.' && namelen == dlen-1) |
dlen--; |
} |
} |
/* |
* Skip hidden or associated files unless unhide is set |
*/ |
match = 0; |
if( !(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5) |
|| dir->i_sb->u.isofs_sb.s_unhide == 'y' ) |
{ |
match = isofs_match(namelen,name,dpnt,dlen); |
} |
|
if (cpnt) |
{ |
kfree(cpnt); |
cpnt = NULL; |
} |
|
if (page) free_page((unsigned long) page); |
if (match) { |
if(inode_number == -1) { |
/* Should only happen for the '..' entry */ |
inode_number = |
isofs_lookup_grandparent(dir, |
find_rock_ridge_relocation(de,dir)); |
if(inode_number == -1){ |
/* Should never happen */ |
printk("Backlink not properly set %x %lx.\n", |
isonum_733(de->extent), |
dir->i_ino); |
goto out; |
} |
} |
*ino = inode_number; |
*ino_back = backlink; |
return bh; |
} |
} |
out: |
if (cpnt) |
kfree(cpnt); |
brelse(bh); |
return NULL; |
} |
|
int isofs_lookup(struct inode * dir,const char * name, int len, |
struct inode ** result) |
{ |
unsigned long ino, ino_back; |
struct buffer_head * bh; |
|
#ifdef DEBUG |
printk("lookup: %x %d\n",dir->i_ino, len); |
#endif |
*result = NULL; |
if (!dir) |
return -ENOENT; |
|
if (!S_ISDIR(dir->i_mode)) { |
iput(dir); |
return -ENOENT; |
} |
|
ino = 0; |
|
if (dcache_lookup(dir, name, len, &ino)) ino_back = dir->i_ino; |
|
|
if (!ino) { |
char *lcname; |
|
/* First try the original name. If that doesn't work and the fs |
* was mounted with check=relaxed, convert the name to lower |
* case and try again. |
*/ |
if (!(bh = isofs_find_entry(dir,name,len, &ino, &ino_back)) |
&& dir->i_sb->u.isofs_sb.s_name_check == 'r' |
&& (lcname = kmalloc(len, GFP_KERNEL)) != NULL) { |
int i; |
char c; |
|
for (i=0; i<len; i++) { |
c = name[i]; |
if (c >= 'A' && c <= 'Z') c |= 0x20; |
lcname[i] = c; |
} |
bh = isofs_find_entry(dir,lcname,len, &ino, &ino_back); |
kfree(lcname); |
} |
|
if (!bh) { |
iput(dir); |
return -ENOENT; |
} |
if (ino_back == dir->i_ino) { |
dcache_add(dir, name, len, ino); |
} |
brelse(bh); |
} |
|
if (!(*result = iget(dir->i_sb,ino))) { |
iput(dir); |
return -EACCES; |
} |
|
/* We need this backlink for the ".." entry unless the name that we |
are looking up traversed a mount point (in which case the inode |
may not even be on an iso9660 filesystem, and writing to |
u.isofs_i would only cause memory corruption). |
*/ |
|
if (ino_back && !(*result)->i_pipe && (*result)->i_sb == dir->i_sb) { |
(*result)->u.isofs_i.i_backlink = ino_back; |
} |
|
iput(dir); |
return 0; |
} |
/Makefile
0,0 → 1,14
# |
# 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 (ie not a .c file). |
# |
# Note 2! The CFLAGS definitions are now in the main makefile... |
|
O_TARGET := isofs.o |
O_OBJS := namei.o inode.o file.o dir.o util.o rock.o symlink.o joliet.o |
M_OBJS := $(O_TARGET) |
|
include $(TOPDIR)/Rules.make |
/util.c
0,0 → 1,135
/* |
* linux/fs/isofs/util.c |
* |
* The special functions in the file are numbered according to the section |
* of the iso 9660 standard in which they are described. isonum_733 will |
* convert numbers according to section 7.3.3, etc. |
* |
* isofs special functions. This file was lifted in its entirety from |
* the 386bsd iso9660 filesystem, by Pace Willisson <pace@blitz.com>. |
*/ |
|
#include <linux/time.h> |
|
int |
isonum_711 (char * p) |
{ |
return (*p & 0xff); |
} |
|
int |
isonum_712 (char * p) |
{ |
int val; |
|
val = *p; |
if (val & 0x80) |
val |= 0xffffff00; |
return (val); |
} |
|
int |
isonum_721 (char * p) |
{ |
return ((p[0] & 0xff) | ((p[1] & 0xff) << 8)); |
} |
|
int |
isonum_722 (char * p) |
{ |
return (((p[0] & 0xff) << 8) | (p[1] & 0xff)); |
} |
|
int |
isonum_723 (char * p) |
{ |
#if 0 |
if (p[0] != p[3] || p[1] != p[2]) { |
fprintf (stderr, "invalid format 7.2.3 number\n"); |
exit (1); |
} |
#endif |
return (isonum_721 (p)); |
} |
|
int |
isonum_731 (char * p) |
{ |
return ((p[0] & 0xff) |
| ((p[1] & 0xff) << 8) |
| ((p[2] & 0xff) << 16) |
| ((p[3] & 0xff) << 24)); |
} |
|
int |
isonum_732 (char * p) |
{ |
return (((p[0] & 0xff) << 24) |
| ((p[1] & 0xff) << 16) |
| ((p[2] & 0xff) << 8) |
| (p[3] & 0xff)); |
} |
|
int |
isonum_733 (char * p) |
{ |
#if 0 |
int i; |
|
for (i = 0; i < 4; i++) { |
if (p[i] != p[7-i]) { |
fprintf (stderr, "bad format 7.3.3 number\n"); |
exit (1); |
} |
} |
#endif |
return (isonum_731 (p)); |
} |
|
/* 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. |
*/ |
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}; |
extern struct timezone sys_tz; |
|
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; |
if (sys_tz.tz_dsttime) |
crtime -= 3600; |
|
/* sign extend */ |
if (tz & 0x80) |
tz |= (-1 << 8); |
|
/* timezone offset is unreliable on some disks */ |
if (-48 <= tz && tz <= 52) |
crtime += tz * 15 * 60; |
} |
return crtime; |
} |
|