ext4: Correct block number handling, empty block vs. error code
[oweals/u-boot.git] / fs / ext4 / ext4_write.c
index fac3222ef8f2e093d7bd13bd358fb32af47c26fa..e4f0905876e7536c49bda8393f3099deb4d8dbe1 100644 (file)
@@ -447,92 +447,52 @@ static int ext4fs_delete_file(int inodeno)
                no_blocks++;
 
        if (le32_to_cpu(inode.flags) & EXT4_EXTENTS_FL) {
-               struct ext2fs_node *node_inode =
-                   zalloc(sizeof(struct ext2fs_node));
-               if (!node_inode)
-                       goto fail;
-               node_inode->data = ext4fs_root;
-               node_inode->ino = inodeno;
-               node_inode->inode_read = 0;
-               memcpy(&(node_inode->inode), &inode, sizeof(struct ext2_inode));
-
-               for (i = 0; i < no_blocks; i++) {
-                       blknr = read_allocated_block(&(node_inode->inode), i);
-                       bg_idx = blknr / blk_per_grp;
-                       if (fs->blksz == 1024) {
-                               remainder = blknr % blk_per_grp;
-                               if (!remainder)
-                                       bg_idx--;
-                       }
-                       ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx],
-                                               bg_idx);
-                       debug("EXT4_EXTENTS Block releasing %ld: %d\n",
-                             blknr, bg_idx);
-
-                       ext4fs_bg_free_blocks_inc(&bgd[bg_idx]);
-                       ext4fs_sb_free_blocks_inc(fs->sb);
-
-                       /* journal backup */
-                       if (prev_bg_bmap_idx != bg_idx) {
-                               status =
-                                   ext4fs_devread(
-                                                  (lbaint_t)le32_to_cpu(bgd[bg_idx].block_id) *
-                                                  fs->sect_perblk, 0,
-                                                  fs->blksz, journal_buffer);
-                               if (status == 0)
-                                       goto fail;
-                               if (ext4fs_log_journal(journal_buffer,
-                                                       le32_to_cpu(bgd[bg_idx].block_id)))
-                                       goto fail;
-                               prev_bg_bmap_idx = bg_idx;
-                       }
-               }
-               if (node_inode) {
-                       free(node_inode);
-                       node_inode = NULL;
-               }
+               /* FIXME delete extent index blocks, i.e. eh_depth >= 1 */
+               struct ext4_extent_header *eh =
+                       (struct ext4_extent_header *)
+                               inode.b.blocks.dir_blocks;
+               debug("del: dep=%d entries=%d\n", eh->eh_depth, eh->eh_entries);
        } else {
-
                delete_single_indirect_block(&inode);
                delete_double_indirect_block(&inode);
                delete_triple_indirect_block(&inode);
+       }
 
-               /* read the block no allocated to a file */
-               no_blocks = le32_to_cpu(inode.size) / fs->blksz;
-               if (le32_to_cpu(inode.size) % fs->blksz)
-                       no_blocks++;
-               for (i = 0; i < no_blocks; i++) {
-                       blknr = read_allocated_block(&inode, i);
-                       bg_idx = blknr / blk_per_grp;
-                       if (fs->blksz == 1024) {
-                               remainder = blknr % blk_per_grp;
-                               if (!remainder)
-                                       bg_idx--;
-                       }
-                       ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx],
-                                               bg_idx);
-                       debug("ActualB releasing %ld: %d\n", blknr, bg_idx);
+       /* release data blocks */
+       for (i = 0; i < no_blocks; i++) {
+               blknr = read_allocated_block(&inode, i);
+               if (blknr == 0)
+                       continue;
+               if (blknr < 0)
+                       goto fail;
+               bg_idx = blknr / blk_per_grp;
+               if (fs->blksz == 1024) {
+                       remainder = blknr % blk_per_grp;
+                       if (!remainder)
+                               bg_idx--;
+               }
+               ext4fs_reset_block_bmap(blknr, fs->blk_bmaps[bg_idx],
+                                       bg_idx);
+               debug("EXT4 Block releasing %ld: %d\n", blknr, bg_idx);
 
-                       ext4fs_bg_free_blocks_inc(&bgd[bg_idx]);
-                       ext4fs_sb_free_blocks_inc(fs->sb);
-                       /* journal backup */
-                       if (prev_bg_bmap_idx != bg_idx) {
-                               memset(journal_buffer, '\0', fs->blksz);
-                               status = ext4fs_devread(
-                                                       (lbaint_t)le32_to_cpu(bgd[bg_idx].block_id)
-                                                       * fs->sect_perblk,
-                                                       0, fs->blksz,
-                                                       journal_buffer);
-                               if (status == 0)
-                                       goto fail;
-                               if (ext4fs_log_journal(journal_buffer,
-                                               le32_to_cpu(bgd[bg_idx].block_id)))
-                                       goto fail;
-                               prev_bg_bmap_idx = bg_idx;
-                       }
+               ext4fs_bg_free_blocks_inc(&bgd[bg_idx]);
+               ext4fs_sb_free_blocks_inc(fs->sb);
+               /* journal backup */
+               if (prev_bg_bmap_idx != bg_idx) {
+                       uint32_t bgd_blknr = le32_to_cpu(bgd[bg_idx].block_id);
+                       status = ext4fs_devread((lbaint_t)bgd_blknr *
+                                               fs->sect_perblk,
+                                               0, fs->blksz,
+                                               journal_buffer);
+                       if (status == 0)
+                               goto fail;
+                       if (ext4fs_log_journal(journal_buffer, bgd_blknr))
+                               goto fail;
+                       prev_bg_bmap_idx = bg_idx;
                }
        }
 
+       /* release inode */
        /* from the inode no to blockno */
        inodes_per_block = fs->blksz / fs->inodesz;
        ibmap_idx = inodeno / inode_per_grp;
@@ -560,7 +520,7 @@ static int ext4fs_delete_file(int inodeno)
 
        read_buffer = read_buffer + blkoff;
        inode_buffer = (struct ext2_inode *)read_buffer;
-       memset(inode_buffer, '\0', sizeof(struct ext2_inode));
+       memset(inode_buffer, '\0', fs->inodesz);
 
        /* write the inode to original position in inode table */
        if (ext4fs_put_metadata(start_block_address, blkno))
@@ -609,7 +569,6 @@ int ext4fs_init(void)
 
        /* populate fs */
        fs->blksz = EXT2_BLOCK_SIZE(ext4fs_root);
-       fs->inodesz = INODE_SIZE_FILESYSTEM(ext4fs_root);
        fs->sect_perblk = fs->blksz >> fs->dev_desc->log2blksz;
 
        /* get the superblock */
@@ -763,6 +722,10 @@ void ext4fs_deinit(void)
        fs->curr_blkno = 0;
 }
 
+/*
+ * Write data to filesystem blocks. Uses same optimization for
+ * contigous sectors as ext4fs_read_file
+ */
 static int ext4fs_write_file(struct ext2_inode *file_inode,
                             int pos, unsigned int len, char *buf)
 {
@@ -789,7 +752,7 @@ static int ext4fs_write_file(struct ext2_inode *file_inode,
                int blockend = fs->blksz;
                int skipfirst = 0;
                blknr = read_allocated_block(file_inode, i);
-               if (blknr < 0)
+               if (blknr <= 0)
                        return -1;
 
                blknr = blknr << log2_fs_blocksize;
@@ -867,7 +830,7 @@ int ext4fs_write(const char *fname, unsigned char *buffer,
        ALLOC_CACHE_ALIGN_BUFFER(char, filename, 256);
        memset(filename, 0x00, 256);
 
-       g_parent_inode = zalloc(sizeof(struct ext2_inode));
+       g_parent_inode = zalloc(fs->inodesz);
        if (!g_parent_inode)
                goto fail;
 
@@ -881,8 +844,13 @@ int ext4fs_write(const char *fname, unsigned char *buffer,
                goto fail;
        if (ext4fs_iget(parent_inodeno, g_parent_inode))
                goto fail;
+       /* do not mess up a directory using hash trees */
+       if (le32_to_cpu(g_parent_inode->flags) & EXT4_INDEX_FL) {
+               printf("hash tree directory\n");
+               goto fail;
+       }
        /* check if the filename is already present in root */
-       existing_file_inodeno = ext4fs_filename_check(filename);
+       existing_file_inodeno = ext4fs_filename_unlink(filename);
        if (existing_file_inodeno != -1) {
                ret = ext4fs_delete_file(existing_file_inodeno);
                fs->first_pass_bbmap = 0;
@@ -907,7 +875,9 @@ int ext4fs_write(const char *fname, unsigned char *buffer,
                goto fail;
        }
 
-       ext4fs_update_parent_dentry(filename, &inodeno, FILETYPE_REG);
+       inodeno = ext4fs_update_parent_dentry(filename, FILETYPE_REG);
+       if (inodeno == -1)
+               goto fail;
        /* prepare file inode */
        inode_buffer = zalloc(fs->inodesz);
        if (!inode_buffer)
@@ -948,6 +918,7 @@ int ext4fs_write(const char *fname, unsigned char *buffer,
        /* copy the file content into data blocks */
        if (ext4fs_write_file(file_inode, 0, sizebytes, (char *)buffer) == -1) {
                printf("Error in copying content\n");
+               /* FIXME: Deallocate data blocks */
                goto fail;
        }
        ibmap_idx = parent_inodeno / le32_to_cpu(ext4fs_root->sblock.inodes_per_group);
@@ -963,22 +934,18 @@ int ext4fs_write(const char *fname, unsigned char *buffer,
                if (ext4fs_log_journal(temp_ptr, parent_itable_blkno))
                        goto fail;
 
-               memcpy(temp_ptr + blkoff, g_parent_inode,
-                       sizeof(struct ext2_inode));
+               memcpy(temp_ptr + blkoff, g_parent_inode, fs->inodesz);
                if (ext4fs_put_metadata(temp_ptr, parent_itable_blkno))
                        goto fail;
-               free(temp_ptr);
        } else {
                /*
                 * If parent and child fall in same inode table block
                 * both should be kept in 1 buffer
                 */
-               memcpy(temp_ptr + blkoff, g_parent_inode,
-                      sizeof(struct ext2_inode));
+               memcpy(temp_ptr + blkoff, g_parent_inode, fs->inodesz);
                gd_index--;
                if (ext4fs_put_metadata(temp_ptr, itable_blkno))
                        goto fail;
-               free(temp_ptr);
        }
        ext4fs_update();
        ext4fs_deinit();
@@ -989,6 +956,7 @@ int ext4fs_write(const char *fname, unsigned char *buffer,
        fs->curr_inode_no = 0;
        free(inode_buffer);
        free(g_parent_inode);
+       free(temp_ptr);
        g_parent_inode = NULL;
 
        return 0;
@@ -996,6 +964,7 @@ fail:
        ext4fs_deinit();
        free(inode_buffer);
        free(g_parent_inode);
+       free(temp_ptr);
        g_parent_inode = NULL;
 
        return -1;