Whamcloud - gitweb
Many files:
[tools/e2fsprogs.git] / lib / ext2fs / inode.c
index ae69bc2..04c5e4d 100644 (file)
@@ -35,6 +35,8 @@ errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
 
        scan->magic = EXT2_ET_MAGIC_INODE_SCAN;
        scan->fs = fs;
+       scan->inode_size = EXT2_INODE_SIZE(fs->super);
+       scan->bytes_left = 0;
        scan->current_group = -1;
        scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8;
        scan->groups_left = fs->group_desc_count;
@@ -45,6 +47,12 @@ errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
                free(scan);
                return ENOMEM;
        }
+       scan->temp_buffer = malloc(scan->inode_size);
+       if (!scan->temp_buffer) {
+               free(scan->inode_buffer);
+               free(scan);
+               return ENOMEM;
+       }
        *ret_scan = scan;
        return 0;
 }
@@ -56,6 +64,8 @@ void ext2fs_close_inode_scan(ext2_inode_scan scan)
        
        free(scan->inode_buffer);
        scan->inode_buffer = NULL;
+       free(scan->temp_buffer);
+       scan->temp_buffer = NULL;
        free(scan);
        return;
 }
@@ -79,53 +89,82 @@ errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ino_t *ino,
 {
        errcode_t       retval;
        int             num_blocks;
+       int             extra_bytes = 0;
        
        EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
 
+       /*
+        * Do we need to start reading a new block group?
+        */
        if (scan->inodes_left <= 0) {
-               if (scan->blocks_left <= 0) {
-                       if (scan->done_group) {
-                               retval = (scan->done_group)
-                                       (scan->fs, scan,
-                                        scan->current_group,
-                                        scan->done_group_data);
-                               if (retval)
-                                       return retval;
-                       }
-                       do {
-                               if (scan->groups_left <= 0) {
-                                       *ino = 0;
-                                       return 0;
-                               }
-                               scan->current_group++;
-                               scan->groups_left--;
-                       
-                               scan->current_block =
-               scan->fs->group_desc[scan->current_group].bg_inode_table;
-                               scan->blocks_left = (EXT2_INODES_PER_GROUP(scan->fs->super) /
-                                                    EXT2_INODES_PER_BLOCK(scan->fs->super));
-                       } while (scan->current_block == 0);
-               } else {
-                       scan->current_block += scan->inode_buffer_blocks;
+               if (scan->done_group) {
+                       retval = (scan->done_group)
+                               (scan->fs, scan,
+                                scan->current_group,
+                                scan->done_group_data);
+                       if (retval)
+                               return retval;
+               }
+               if (scan->groups_left <= 0) {
+                       *ino = 0;
+                       return 0;
                }
+               scan->current_group++;
+               scan->groups_left--;
+                       
+               scan->current_block = scan->fs->
+                       group_desc[scan->current_group].bg_inode_table;
+
+               if (scan->current_block == 0)
+                       return EXT2_ET_MISSING_INODE_TABLE;
+               scan->bytes_left = 0;
+               scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
+               scan->blocks_left = scan->fs->inode_blocks_per_group;
+       }
+
+       /*
+        * Have we run out of space in the inode buffer?  If so, we
+        * need to read in more blocks.
+        */
+       if (scan->bytes_left < scan->inode_size) {
+               memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
+               extra_bytes = scan->bytes_left;
+               
                scan->blocks_left -= scan->inode_buffer_blocks;
                num_blocks = scan->inode_buffer_blocks;
                if (scan->blocks_left < 0)
                        num_blocks += scan->blocks_left;
                
-               scan->inodes_left = EXT2_INODES_PER_BLOCK(scan->fs->super) *
-                       num_blocks;
-
                retval = io_channel_read_blk(scan->fs->io, scan->current_block,
                                             num_blocks, scan->inode_buffer);
                if (retval)
                        return EXT2_ET_NEXT_INODE_READ;
-               scan->inode_scan_ptr = (struct ext2_inode *) scan->inode_buffer;
+               scan->ptr = scan->inode_buffer;
+               scan->bytes_left = num_blocks * scan->fs->blocksize;
+       
+               scan->current_block += scan->inode_buffer_blocks;
+       }
+
+       if (extra_bytes) {
+               memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
+                      scan->inode_size - extra_bytes);
+               scan->ptr += scan->inode_size - extra_bytes;
+               scan->bytes_left -= scan->inode_size - extra_bytes;
+
+               if (scan->fs->flags & EXT2_SWAP_BYTES)
+                       inocpy_with_swap(inode, (struct ext2_inode *)
+                                        scan->temp_buffer);
+               else
+                       *inode = *((struct ext2_inode *) scan->temp_buffer);
+       } else {
+               if (scan->fs->flags & EXT2_SWAP_BYTES)
+                       inocpy_with_swap(inode, (struct ext2_inode *)
+                                        scan->ptr);
+               else
+                       *inode = *((struct ext2_inode *) scan->ptr);
+               scan->ptr += scan->inode_size;
+               scan->bytes_left -= scan->inode_size;
        }
-       if (scan->fs->flags & EXT2_SWAP_BYTES)
-               inocpy_with_swap(inode, scan->inode_scan_ptr++);
-       else
-               *inode = *scan->inode_scan_ptr++;
 
        scan->inodes_left--;
        scan->current_inode++;
@@ -137,17 +176,16 @@ errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ino_t *ino,
  * Functions to read and write a single inode.
  */
 static char *inode_buffer = 0;
-static blk_t inode_buffer_block;
+static blk_t inode_buffer_block = 0;
 static int inode_buffer_size = 0;
 
 errcode_t ext2fs_read_inode (ext2_filsys fs, unsigned long ino,
                             struct ext2_inode * inode)
 {
-       unsigned long group;
-       unsigned long block;
-       unsigned long block_nr;
+       unsigned long   group, block, block_nr, offset;
+       char            *ptr;
        errcode_t       retval;
-       int i;
+       int             clen, length;
 
        EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
@@ -165,10 +203,9 @@ errcode_t ext2fs_read_inode (ext2_filsys fs, unsigned long ino,
        }
                
        group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
-       block = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) /
-               EXT2_INODES_PER_BLOCK(fs->super);
-       i = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) %
-               EXT2_INODES_PER_BLOCK(fs->super);
+       offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
+               EXT2_INODE_SIZE(fs->super);
+       block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
        block_nr = fs->group_desc[group].bg_inode_table + block;
        if (block_nr != inode_buffer_block) {
                retval = io_channel_read_blk(fs->io, block_nr, 1,
@@ -177,23 +214,44 @@ errcode_t ext2fs_read_inode (ext2_filsys fs, unsigned long ino,
                        return retval;
                inode_buffer_block = block_nr;
        }
+       offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
+       ptr = ((char *) inode_buffer) + offset;
+
+       memset(inode, 0, sizeof(struct ext2_inode));
+       length = EXT2_INODE_SIZE(fs->super);
+       if (length > sizeof(struct ext2_inode))
+               length = sizeof(struct ext2_inode);
+       
+       if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
+               clen = EXT2_BLOCK_SIZE(fs->super) - offset;
+               memcpy((char *) inode, ptr, clen);
+               length -= clen;
+               
+               retval = io_channel_read_blk(fs->io, block_nr+1, 1,
+                                            inode_buffer);
+               if (retval)
+                       return retval;
+               inode_buffer_block = block_nr+1;
+               
+               memcpy(((char *) inode) + clen,
+                      inode_buffer, length);
+       } else
+               memcpy((char *) inode, ptr, length);
+       
        if (fs->flags & EXT2_SWAP_BYTES)
-               inocpy_with_swap(inode,
-                                (struct ext2_inode *) inode_buffer + i);
-       else
-               memcpy (inode, (struct ext2_inode *) inode_buffer + i,
-                       sizeof (struct ext2_inode));
+               inocpy_with_swap(inode, inode);
+
        return 0;
 }
 
 errcode_t ext2fs_write_inode(ext2_filsys fs, unsigned long ino,
                     struct ext2_inode * inode)
 {
-       unsigned long group;
-       unsigned long block;
-       unsigned long block_nr;
+       unsigned long group, block, block_nr, offset;
        errcode_t       retval;
-       int i;
+       struct ext2_inode temp_inode;
+       char *ptr;
+       int i, clen, length;
 
        EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
@@ -213,13 +271,23 @@ errcode_t ext2fs_write_inode(ext2_filsys fs, unsigned long ino,
                inode_buffer_size = fs->blocksize;
                inode_buffer_block = 0;
        }
-               
+       if (fs->flags & EXT2_SWAP_BYTES)
+               inocpy_with_swap(&temp_inode, inode);
+       else
+               memcpy(&temp_inode, inode, sizeof(struct ext2_inode));
+       
        group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
-       block = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) /
-               EXT2_INODES_PER_BLOCK(fs->super);
-       i = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) %
-               EXT2_INODES_PER_BLOCK(fs->super);
+       offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
+               EXT2_INODE_SIZE(fs->super);
+       block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
        block_nr = fs->group_desc[group].bg_inode_table + block;
+       offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
+       ptr = (char *) inode_buffer + offset;
+
+       length = EXT2_INODE_SIZE(fs->super);
+       if (length > sizeof(struct ext2_inode))
+               length = sizeof(struct ext2_inode);
+       
        if (inode_buffer_block != block_nr) {
                retval = io_channel_read_blk(fs->io, block_nr, 1,
                                             inode_buffer);
@@ -227,15 +295,35 @@ errcode_t ext2fs_write_inode(ext2_filsys fs, unsigned long ino,
                        return retval;
                inode_buffer_block = block_nr;
        }
-       if (fs->flags & EXT2_SWAP_BYTES)
-               inocpy_with_swap((struct ext2_inode *) inode_buffer + i,
-                                inode);
-       else
-               memcpy ((struct ext2_inode *) inode_buffer + i, inode,
-                       sizeof (struct ext2_inode));
+       
+       if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
+               clen = EXT2_BLOCK_SIZE(fs->super) - offset;
+               memcpy(ptr, &temp_inode, clen);
+               length -= clen;
+       } else {
+               memcpy(ptr, &temp_inode, length);
+               length = 0;
+       }
        retval = io_channel_write_blk(fs->io, block_nr, 1, inode_buffer);
        if (retval)
                return retval;
+
+       if (length) {
+               retval = io_channel_read_blk(fs->io, ++block_nr, 1,
+                                            inode_buffer);
+               if (retval) {
+                       inode_buffer_block = 0;
+                       return retval;
+               }
+               inode_buffer_block = block_nr;
+               memcpy(inode_buffer, ((char *) &temp_inode) + clen, length);
+
+               retval = io_channel_write_blk(fs->io, block_nr, 1,
+                                             inode_buffer);
+               if (retval)
+                       return retval;
+       }
+       
        fs->flags |= EXT2_FLAG_CHANGED;
        return 0;
 }