/* * linux/fs/isofs/rock.c * * (C) 1992, 1993 Eric Youngdale * * Rock Ridge Extensions to iso9660 */ #ifdef MODULE #include #endif #include #include #include #include #include #include #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;pu.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(MINOR(low) != low && high == 0) { inode->i_rdev = low; } else { inode->i_rdev = MKDEV(high, low); } }; 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; }