X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;ds=sidebyside;f=fs%2Fisofs%2Frock.c;h=4326cb47f8fa267667311d2b65b361aa66508858;hb=3a69c7dc6f3d58aeb9ce5051fc7060d55e05c003;hp=e981c040a49b3e2c448c85c4c539edf544c13df7;hpb=7fa393a1d3d9485e428a3c74b5599190c14b13db;p=powerpc.git diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index e981c040a4..4326cb47f8 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -13,104 +13,211 @@ #include "isofs.h" #include "rock.h" -/* These functions are designed to read the system areas of a directory record +/* + * 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. */ + * 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; \ - ISOFS_SB(inode->i_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 = NULL - -#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 (LEN<0) LEN=0; \ - if (ISOFS_SB(inode->i_sb)->s_rock_offset!=-1) \ - { \ - LEN-=ISOFS_SB(inode->i_sb)->s_rock_offset; \ - CHR+=ISOFS_SB(inode->i_sb)->s_rock_offset; \ - if (LEN<0) LEN=0; \ - } \ +struct rock_state { + void *buffer; + unsigned char *chr; + int len; + int cont_size; + int cont_extent; + int cont_offset; + struct inode *inode; +}; + +/* + * This is a way of ensuring that we have something in the system + * use fields that is compatible with Rock Ridge. Return zero on success. + */ + +static int check_sp(struct rock_ridge *rr, struct inode *inode) +{ + if (rr->u.SP.magic[0] != 0xbe) + return -1; + if (rr->u.SP.magic[1] != 0xef) + return -1; + ISOFS_SB(inode->i_sb)->s_rock_offset = rr->u.SP.skip; + return 0; +} + +static void setup_rock_ridge(struct iso_directory_record *de, + struct inode *inode, struct rock_state *rs) +{ + rs->len = sizeof(struct iso_directory_record) + de->name_len[0]; + if (rs->len & 1) + (rs->len)++; + rs->chr = (unsigned char *)de + rs->len; + rs->len = *((unsigned char *)de) - rs->len; + if (rs->len < 0) + rs->len = 0; + + if (ISOFS_SB(inode->i_sb)->s_rock_offset != -1) { + rs->len -= ISOFS_SB(inode->i_sb)->s_rock_offset; + rs->chr += ISOFS_SB(inode->i_sb)->s_rock_offset; + if (rs->len < 0) + rs->len = 0; + } +} + +static void init_rock_state(struct rock_state *rs, struct inode *inode) +{ + memset(rs, 0, sizeof(*rs)); + rs->inode = inode; } -#define MAYBE_CONTINUE(LABEL,DEV) \ - {if (buffer) { kfree(buffer); buffer = NULL; } \ - 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){ \ - if (offset > pbh->b_size || offset + cont_size > pbh->b_size){ \ - brelse(pbh); \ - goto out; \ - } \ - 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"); \ - }} - -/* return length of name field; 0: not found, -1: to be ignored */ +/* + * Returns 0 if the caller should continue scanning, 1 if the scan must end + * and -ve on error. + */ +static int rock_continue(struct rock_state *rs) +{ + int ret = 1; + int blocksize = 1 << rs->inode->i_blkbits; + const int min_de_size = offsetof(struct rock_ridge, u); + + kfree(rs->buffer); + rs->buffer = NULL; + + if ((unsigned)rs->cont_offset > blocksize - min_de_size || + (unsigned)rs->cont_size > blocksize || + (unsigned)(rs->cont_offset + rs->cont_size) > blocksize) { + printk(KERN_NOTICE "rock: corrupted directory entry. " + "extent=%d, offset=%d, size=%d\n", + rs->cont_extent, rs->cont_offset, rs->cont_size); + ret = -EIO; + goto out; + } + + if (rs->cont_extent) { + struct buffer_head *bh; + + rs->buffer = kmalloc(rs->cont_size, GFP_KERNEL); + if (!rs->buffer) { + ret = -ENOMEM; + goto out; + } + ret = -EIO; + bh = sb_bread(rs->inode->i_sb, rs->cont_extent); + if (bh) { + memcpy(rs->buffer, bh->b_data + rs->cont_offset, + rs->cont_size); + put_bh(bh); + rs->chr = rs->buffer; + rs->len = rs->cont_size; + rs->cont_extent = 0; + rs->cont_size = 0; + rs->cont_offset = 0; + return 0; + } + printk("Unable to read rock-ridge attributes\n"); + } +out: + kfree(rs->buffer); + rs->buffer = NULL; + return ret; +} + +/* + * We think there's a record of type `sig' at rs->chr. Parse the signature + * and make sure that there's really room for a record of that type. + */ +static int rock_check_overflow(struct rock_state *rs, int sig) +{ + int len; + + switch (sig) { + case SIG('S', 'P'): + len = sizeof(struct SU_SP_s); + break; + case SIG('C', 'E'): + len = sizeof(struct SU_CE_s); + break; + case SIG('E', 'R'): + len = sizeof(struct SU_ER_s); + break; + case SIG('R', 'R'): + len = sizeof(struct RR_RR_s); + break; + case SIG('P', 'X'): + len = sizeof(struct RR_PX_s); + break; + case SIG('P', 'N'): + len = sizeof(struct RR_PN_s); + break; + case SIG('S', 'L'): + len = sizeof(struct RR_SL_s); + break; + case SIG('N', 'M'): + len = sizeof(struct RR_NM_s); + break; + case SIG('C', 'L'): + len = sizeof(struct RR_CL_s); + break; + case SIG('P', 'L'): + len = sizeof(struct RR_PL_s); + break; + case SIG('T', 'F'): + len = sizeof(struct RR_TF_s); + break; + case SIG('Z', 'F'): + len = sizeof(struct RR_ZF_s); + break; + default: + len = 0; + break; + } + len += offsetof(struct rock_ridge, u); + if (len > rs->len) { + printk(KERN_NOTICE "rock: directory entry would overflow " + "storage\n"); + printk(KERN_NOTICE "rock: sig=0x%02x, size=%d, remaining=%d\n", + sig, len, rs->len); + return -EIO; + } + return 0; +} + +/* + * 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; + struct rock_state rs; struct rock_ridge *rr; int sig; int retnamlen = 0; int truncate = 0; + int ret = 0; if (!ISOFS_SB(inode->i_sb)->s_rock) return 0; *retname = 0; - SETUP_ROCK_RIDGE(de, chr, len); + init_rock_state(&rs, inode); + setup_rock_ridge(de, inode, &rs); repeat: - while (len > 2) { /* There may be one byte for padding somewhere */ - rr = (struct rock_ridge *)chr; + while (rs.len > 2) { /* There may be one byte for padding somewhere */ + rr = (struct rock_ridge *)rs.chr; if (rr->len < 3) goto out; /* Something got screwed up here */ - sig = isonum_721(chr); - chr += rr->len; - len -= rr->len; - if (len < 0) - goto out; /* corrupted isofs */ + sig = isonum_721(rs.chr); + if (rock_check_overflow(&rs, sig)) + goto eio; + rs.chr += rr->len; + rs.len -= rr->len; + if (rs.len < 0) + goto eio; /* corrupted isofs */ switch (sig) { case SIG('R', 'R'): @@ -118,10 +225,13 @@ repeat: goto out; break; case SIG('S', 'P'): - CHECK_SP(goto out); + if (check_sp(rr, inode)) + goto out; break; case SIG('C', 'E'): - CHECK_CE; + rs.cont_extent = isonum_733(rr->u.CE.extent); + rs.cont_offset = isonum_733(rr->u.CE.offset); + rs.cont_size = isonum_733(rr->u.CE.size); break; case SIG('N', 'M'): if (truncate) @@ -151,57 +261,61 @@ repeat: retnamlen += rr->len - 5; break; case SIG('R', 'E'): - if (buffer) - kfree(buffer); + kfree(rs.buffer); return -1; default: break; } } - MAYBE_CONTINUE(repeat, inode); - kfree(buffer); - return retnamlen; /* If 0, this file did not have a NM field */ + ret = rock_continue(&rs); + if (ret == 0) + goto repeat; + if (ret == 1) + return retnamlen; /* If 0, this file did not have a NM field */ out: - if (buffer) - kfree(buffer); - return 0; + kfree(rs.buffer); + return ret; +eio: + ret = -EIO; + goto out; } static 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; int cnt, sig; struct inode *reloc; struct rock_ridge *rr; int rootflag; - - CONTINUE_DECLS; + struct rock_state rs; + int ret = 0; if (!ISOFS_SB(inode->i_sb)->s_rock) return 0; - SETUP_ROCK_RIDGE(de, chr, len); + init_rock_state(&rs, inode); + setup_rock_ridge(de, inode, &rs); if (regard_xa) { - chr += 14; - len -= 14; - if (len < 0) - len = 0; + rs.chr += 14; + rs.len -= 14; + if (rs.len < 0) + rs.len = 0; } repeat: - while (len > 2) { /* There may be one byte for padding somewhere */ - rr = (struct rock_ridge *)chr; + while (rs.len > 2) { /* There may be one byte for padding somewhere */ + rr = (struct rock_ridge *)rs.chr; if (rr->len < 3) goto out; /* Something got screwed up here */ - sig = isonum_721(chr); - chr += rr->len; - len -= rr->len; - if (len < 0) - goto out; /* corrupted isofs */ + sig = isonum_721(rs.chr); + if (rock_check_overflow(&rs, sig)) + goto eio; + rs.chr += rr->len; + rs.len -= rr->len; + if (rs.len < 0) + goto eio; /* corrupted isofs */ switch (sig) { #ifndef CONFIG_ZISOFS /* No flag for SF or ZF */ @@ -212,10 +326,13 @@ repeat: break; #endif case SIG('S', 'P'): - CHECK_SP(goto out); + if (check_sp(rr, inode)) + goto out; break; case SIG('C', 'E'): - CHECK_CE; + rs.cont_extent = isonum_733(rr->u.CE.extent); + rs.cont_offset = isonum_733(rr->u.CE.offset); + rs.cont_size = isonum_733(rr->u.CE.size); break; case SIG('E', 'R'): ISOFS_SB(inode->i_sb)->s_rock = 1; @@ -421,11 +538,17 @@ repeat: break; } } - MAYBE_CONTINUE(repeat, inode); + ret = rock_continue(&rs); + if (ret == 0) + goto repeat; + if (ret == 1) + ret = 0; out: - if (buffer) - kfree(buffer); - return 0; + kfree(rs.buffer); + return ret; +eio: + ret = -EIO; + goto out; } static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr, char *plimit) @@ -499,8 +622,11 @@ static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr, char *plimit) 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 rockridge flag was reset and we didn't look for attributes + * behind eventual XA attributes, have a look there + */ if ((ISOFS_SB(inode->i_sb)->s_rock_offset == -1) && (ISOFS_SB(inode->i_sb)->s_rock == 2)) { result = parse_rock_ridge_inode_internal(de, inode, 14); @@ -508,9 +634,10 @@ int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode) return result; } -/* readpage() for symlinks: reads symlink contents into the page and either - makes it uptodate and returns 0 or returns error (-EIO) */ - +/* + * 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; @@ -520,17 +647,17 @@ static int rock_ridge_symlink_readpage(struct file *file, struct page *page) struct buffer_head *bh; char *rpnt = link; unsigned char *pnt; - struct iso_directory_record *raw_inode; - CONTINUE_DECLS; + struct iso_directory_record *raw_de; unsigned long block, offset; int sig; - int len; - unsigned char *chr; struct rock_ridge *rr; + struct rock_state rs; + int ret; if (!ISOFS_SB(inode->i_sb)->s_rock) goto error; + init_rock_state(&rs, inode); block = ei->i_iget5_block; lock_kernel(); bh = sb_bread(inode->i_sb, block); @@ -540,7 +667,7 @@ static int rock_ridge_symlink_readpage(struct file *file, struct page *page) offset = ei->i_iget5_offset; pnt = (unsigned char *)bh->b_data + offset; - raw_inode = (struct iso_directory_record *)pnt; + raw_de = (struct iso_directory_record *)pnt; /* * If we go past the end of the buffer, there is some sort of error. @@ -548,20 +675,24 @@ static int rock_ridge_symlink_readpage(struct file *file, struct page *page) if (offset + *pnt > bufsize) goto out_bad_span; - /* Now test for possible Rock Ridge extensions which will override - some of these numbers in the inode structure. */ + /* + * 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); + setup_rock_ridge(raw_de, inode, &rs); repeat: - while (len > 2) { /* There may be one byte for padding somewhere */ - rr = (struct rock_ridge *)chr; + while (rs.len > 2) { /* There may be one byte for padding somewhere */ + rr = (struct rock_ridge *)rs.chr; if (rr->len < 3) goto out; /* Something got screwed up here */ - sig = isonum_721(chr); - chr += rr->len; - len -= rr->len; - if (len < 0) + sig = isonum_721(rs.chr); + if (rock_check_overflow(&rs, sig)) + goto out; + rs.chr += rr->len; + rs.len -= rr->len; + if (rs.len < 0) goto out; /* corrupted isofs */ switch (sig) { @@ -570,7 +701,8 @@ repeat: goto out; break; case SIG('S', 'P'): - CHECK_SP(goto out); + if (check_sp(rr, inode)) + goto out; break; case SIG('S', 'L'): rpnt = get_symlink_chunk(rpnt, rr, @@ -580,13 +712,18 @@ repeat: break; case SIG('C', 'E'): /* This tells is if there is a continuation record */ - CHECK_CE; + rs.cont_extent = isonum_733(rr->u.CE.extent); + rs.cont_offset = isonum_733(rr->u.CE.offset); + rs.cont_size = isonum_733(rr->u.CE.size); default: break; } } - MAYBE_CONTINUE(repeat, inode); - kfree(buffer); + ret = rock_continue(&rs); + if (ret == 0) + goto repeat; + if (ret < 0) + goto fail; if (rpnt == link) goto fail; @@ -600,8 +737,7 @@ repeat: /* error exit from macro */ out: - if (buffer) - kfree(buffer); + kfree(rs.buffer); goto fail; out_noread: printk("unable to read i-node block");