[PATCH] isofs: change uses of f_{dentry, vfsmnt} to use f_path
[safe/jmp/linux-2.6] / fs / isofs / rock.c
index 9f2aaa2..f3a1db3 100644 (file)
 #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() */
 
+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.
+ * use fields that is compatible with Rock Ridge.  Return zero on success.
  */
 
 static int check_sp(struct rock_ridge *rr, struct inode *inode)
@@ -37,84 +49,175 @@ static int check_sp(struct rock_ridge *rr, struct inode *inode)
        return 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;                          \
-  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;                                          \
-  }                                                             \
+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;
+}
+
+/*
+ * 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;
 }
 
-#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 */
+/*
+ * 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;
-       int cont_extent = 0;
-       int cont_offset = 0;
-       int cont_size = 0;
-       void *buffer = NULL;
+       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'):
@@ -126,7 +229,9 @@ repeat:
                                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)
@@ -156,59 +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;
-       int cont_extent = 0;
-       int cont_offset = 0;
-       int cont_size = 0;
-       void *buffer = NULL;
+       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 */
@@ -223,7 +330,9 @@ repeat:
                                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;
@@ -429,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)
@@ -507,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);
@@ -516,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;
@@ -528,20 +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;
-       int cont_extent = 0;
-       int cont_offset = 0;
-       int cont_size = 0;
-       void *buffer = NULL;
+       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);
@@ -551,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.
@@ -559,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) {
@@ -592,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;
@@ -612,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");
@@ -630,6 +754,6 @@ error:
        return -EIO;
 }
 
-struct address_space_operations isofs_symlink_aops = {
+const struct address_space_operations isofs_symlink_aops = {
        .readpage = rock_ridge_symlink_readpage
 };