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; |
} |
|