summaryrefslogtreecommitdiffstats
path: root/fs/isofs/rock.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/isofs/rock.c')
-rw-r--r--fs/isofs/rock.c523
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;
+}
+
+
+
+
+
+