diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
commit | 1513ff9b7899ab588401c89db0e99903dbf5f886 (patch) | |
tree | f69cc81a940a502ea23d664c3ffb2d215a479667 /fs/isofs/rock.c |
Import of Linus's Linux 1.1.68
Diffstat (limited to 'fs/isofs/rock.c')
-rw-r--r-- | fs/isofs/rock.c | 523 |
1 files changed, 523 insertions, 0 deletions
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c new file mode 100644 index 000000000..686c1d910 --- /dev/null +++ b/fs/isofs/rock.c @@ -0,0 +1,523 @@ +/* + * linux/fs/isofs/rock.c + * + * (C) 1992, 1993 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 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;} + +#define MAYBE_CONTINUE(LABEL,DEV) \ + {if (buffer) kfree(buffer); \ + if (cont_extent){ \ + int block, offset, offset1; \ + struct buffer_head * bh; \ + buffer = kmalloc(cont_size,GFP_KERNEL); \ + if (!buffer) goto out; \ + block = cont_extent; \ + offset = cont_offset; \ + offset1 = 0; \ + if(ISOFS_BUFFER_SIZE(DEV) == 1024) { \ + block <<= 1; \ + if (offset >= 1024) block++; \ + offset &= 1023; \ + if(offset + cont_size >= 1024) { \ + bh = bread(DEV->i_dev, block++, ISOFS_BUFFER_SIZE(DEV)); \ + if(!bh) {printk("Unable to read continuation Rock Ridge record\n"); \ + kfree(buffer); \ + buffer = NULL; } else { \ + memcpy(buffer, bh->b_data + offset, 1024 - offset); \ + brelse(bh); \ + offset1 = 1024 - offset; \ + offset = 0;} \ + } \ + }; \ + if(buffer) { \ + bh = bread(DEV->i_dev, block, ISOFS_BUFFER_SIZE(DEV)); \ + if(bh){ \ + memcpy(buffer + offset1, bh->b_data + offset, cont_size - offset1); \ + brelse(bh); \ + chr = (unsigned char *) buffer; \ + len = cont_size; \ + cont_extent = 0; \ + cont_size = 0; \ + cont_offset = 0; \ + goto LABEL; \ + }; \ + } \ + printk("Unable to read rock-ridge attributes\n"); \ + }} + +/* This is the inner layer of the get filename routine, and is called + for each system area and continuation record related to the file */ + +int find_rock_ridge_relocation(struct iso_directory_record * de, + struct inode * inode) { + int flag; + int len; + int retval; + unsigned char * chr; + CONTINUE_DECLS; + flag = 0; + + /* If this is a '..' then we are looking for the parent, otherwise we + are looking for the child */ + + if (de->name[0]==1 && de->name_len[0]==1) flag = 1; + /* Return value if we do not find appropriate record. */ + retval = isonum_733 (de->extent); + + if (!inode->i_sb->u.isofs_sb.s_rock) return retval; + + SETUP_ROCK_RIDGE(de, chr, len); + repeat: + { + int rrflag, sig; + struct rock_ridge * rr; + + while (len > 1){ /* There may be one byte for padding somewhere */ + rr = (struct rock_ridge *) chr; + if (rr->len == 0) goto out; /* Something got screwed up here */ + sig = (chr[0] << 8) + chr[1]; + chr += rr->len; + len -= rr->len; + + switch(sig){ + case SIG('R','R'): + rrflag = rr->u.RR.flags[0]; + if (flag && !(rrflag & RR_PL)) goto out; + if (!flag && !(rrflag & RR_CL)) goto out; + break; + case SIG('S','P'): + CHECK_SP(goto out); + break; + case SIG('C','L'): +#ifdef DEBUG + printk("RR: CL\n"); +#endif + if (flag == 0) { + retval = isonum_733(rr->u.CL.location); + goto out; + }; + break; + case SIG('P','L'): +#ifdef DEBUG + printk("RR: PL\n"); +#endif + if (flag != 0) { + retval = isonum_733(rr->u.PL.location); + goto out; + }; + break; + case SIG('C','E'): + CHECK_CE; /* This tells is if there is a continuation record */ + break; + default: + break; + } + }; + }; + MAYBE_CONTINUE(repeat, inode); + return retval; + out: + if(buffer) kfree(buffer); + return retval; +} + +int get_rock_ridge_filename(struct iso_directory_record * de, + char ** 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 */ + + if (!retname) goto out; + *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; + inode->i_size = 0; + 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) << + inode -> i_sb -> u.isofs_sb.s_log_zone_size; + reloc = iget(inode->i_sb, inode->u.isofs_i.i_first_extent); + inode->i_mode = reloc->i_mode; + inode->i_nlink = reloc->i_nlink; + inode->i_uid = reloc->i_uid; + inode->i_gid = reloc->i_gid; + inode->i_rdev = reloc->i_rdev; + inode->i_size = reloc->i_size; + inode->i_atime = reloc->i_atime; + inode->i_ctime = reloc->i_ctime; + inode->i_mtime = reloc->i_mtime; + iput(reloc); + break; + default: + break; + } + }; + } + MAYBE_CONTINUE(repeat,inode); + return 0; + out: + if(buffer) kfree(buffer); + return 0; +} + + +/* Returns the name of the file that this inode is symlinked to. This is + in malloc'd memory, so it needs to be freed, once we are through with it */ + +char * get_rock_ridge_symlink(struct inode * inode) +{ + unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); + unsigned char bufbits = ISOFS_BUFFER_BITS(inode); + struct buffer_head * bh; + unsigned char * pnt; + void * cpnt = NULL; + char * rpnt; + struct iso_directory_record * raw_inode; + CONTINUE_DECLS; + int block; + int sig; + int rootflag; + int len; + unsigned char * chr; + struct rock_ridge * rr; + + if (!inode->i_sb->u.isofs_sb.s_rock) + panic("Cannot have symlink with high sierra variant of iso filesystem\n"); + + rpnt = 0; + + block = inode->i_ino >> bufbits; + if (!(bh=bread(inode->i_dev,block, bufsize))) { + printk("unable to read i-node block"); + return NULL; + }; + + pnt = ((unsigned char *) bh->b_data) + (inode->i_ino & (bufsize - 1)); + + raw_inode = ((struct iso_directory_record *) pnt); + + if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize){ + int frag1, offset; + + offset = (inode->i_ino & (bufsize - 1)); + frag1 = bufsize - offset; + cpnt = kmalloc(*pnt,GFP_KERNEL); + if(!cpnt) return NULL; + memcpy(cpnt, bh->b_data + offset, frag1); + brelse(bh); + if (!(bh = bread(inode->i_dev,++block, bufsize))) { + kfree(cpnt); + printk("unable to read i-node block"); + return NULL; + }; + offset += *pnt - bufsize; + memcpy((char *)cpnt+frag1, bh->b_data, offset); + pnt = ((unsigned char *) cpnt); + raw_inode = ((struct iso_directory_record *) pnt); + }; + + /* Now test for possible Rock Ridge extensions which will override some of + these numbers in the inode structure. */ + + SETUP_ROCK_RIDGE(raw_inode, chr, len); + + repeat: + while (len > 1){ /* There may be one byte for padding somewhere */ + 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); + if (!rpnt) goto out; + *rpnt = 0; + }; + rootflag = 0; + switch(slp->flags &~1){ + case 0: + strncat(rpnt,slp->text, slp->len); + break; + case 2: + strcat(rpnt,"."); + break; + case 4: + strcat(rpnt,".."); + break; + case 8: + rootflag = 1; + strcat(rpnt,"/"); + break; + default: + printk("Symlink component flag not implemented (%d)\n",slen); + }; + slen -= slp->len + 2; + 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; +} + + + + + + |