blob: ebbb449626543bc32407a291090088d6d46d94cd [file] [log] [blame]
/*
* linux/fs/isofs/rock.c
*
* (C) 1992 Eric Youngdale
*
* Rock Ridge Extensions to iso9660
*/
#include <linux/config.h>
#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 everyting
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;}
#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); \
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)); \
memcpy(buffer, bh->b_data + offset, 1024 - offset); \
brelse(bh); \
offset1 = 1024 - offset; \
offset = 0; \
} \
}; \
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 ** name, int * namlen, struct inode * inode)
{
int len;
unsigned char * chr;
CONTINUE_DECLS;
char * retname = NULL;
int retnamlen = 0, truncate=0;
if (!inode->i_sb->u.isofs_sb.s_rock) return 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 (!retname){
retname = (char *) kmalloc (255,GFP_KERNEL);
/* This may be a waste, but we only
need this for a moment. The layers
that call this function should
deallocate the mem fairly soon
after control is returned */
*retname = 0; /* Zero length string */
retnamlen = 0;
};
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);
if (retname) kfree(retname);
return -1;
default:
break;
}
};
}
MAYBE_CONTINUE(repeat,inode);
if(retname){
*name = retname;
*namlen = retnamlen;
return 1;
};
return 0; /* This file did not have a NM field */
out:
if(buffer) kfree(buffer);
if (retname) kfree(retname);
return 0;
}
int parse_rock_ridge_inode(struct iso_directory_record * de,
struct inode * inode){
int len;
unsigned char * chr;
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'):
printk("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);
inode->i_rdev = ((high << 8) | (low & 0xff)) & 0xffff;
};
break;
case SIG('T','F'):
/* Some RRIP writers incorrectly place ctime in the TF_CREATE field.
Try and 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;
slen = rr->len - 5;
slp = &rr->u.SL.link;
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;
slp = (struct SL_component *) (((char *) slp) + slp->len + 2);
if(slen < 2) break;
if(!rootflag) inode->i_size += 1;
};
};
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) <<
(ISOFS_BLOCK_BITS - ISOFS_BUFFER_BITS(inode));
reloc = iget(inode->i_sb, inode->u.isofs_i.i_first_extent << ISOFS_BUFFER_BITS(inode));
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){
cpnt = kmalloc(1 << ISOFS_BLOCK_BITS, GFP_KERNEL);
memcpy(cpnt, bh->b_data, bufsize);
brelse(bh);
if (!(bh = bread(inode->i_dev,++block, bufsize))) {
kfree_s(cpnt, 1 << ISOFS_BLOCK_BITS);
printk("unable to read i-node block");
return NULL;
};
memcpy((char *)cpnt+bufsize, bh->b_data, bufsize);
pnt = ((unsigned char *) cpnt) + (inode->i_ino & (bufsize - 1));
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 */
if (rpnt) break;
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 * slp;
slen = rr->len - 5;
slp = &rr->u.SL.link;
while (slen > 1){
if (!rpnt){
rpnt = (char *) kmalloc (inode->i_size +1, GFP_KERNEL);
*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;
slp = (struct SL_component *) (((char *) slp) + slp->len + 2);
if(slen < 2) break;
if(!rootflag) strcat(rpnt,"/");
};
break;
default:
break;
}
};
};
MAYBE_CONTINUE(repeat,inode);
brelse(bh);
if (cpnt) {
kfree(cpnt);
cpnt = NULL;
};
return rpnt;
out:
if(buffer) kfree(buffer);
return 0;
}