/* * Helper function which writes out a directory block. */ static int write_dir_block(ext2_filsys fs, blk64_t *block_nr, e2_blkcnt_t blockcnt, blk64_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct write_dir_struct *wd = (struct write_dir_struct *) priv_data; blk64_t blk; char *dir; if (*block_nr == 0) return 0; if (blockcnt >= wd->outdir->num) { e2fsck_read_bitmaps(wd->ctx); blk = *block_nr; ext2fs_unmark_block_bitmap2(wd->ctx->block_found_map, blk); ext2fs_block_alloc_stats2(fs, blk, -1); *block_nr = 0; wd->cleared++; return BLOCK_CHANGED; } if (blockcnt < 0) return 0; dir = wd->outdir->buf + (blockcnt * fs->blocksize); wd->err = ext2fs_write_dir_block3(fs, *block_nr, dir, 0); if (wd->err) return BLOCK_ABORT; return 0; }
/* * This fuction deallocates an inode */ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf) { ext2_filsys fs = ctx->fs; struct ext2_inode inode; struct problem_context pctx; __u32 count; struct del_block del_block; e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode"); e2fsck_clear_inode(ctx, ino, &inode, 0, "deallocate_inode"); clear_problem_context(&pctx); pctx.ino = ino; /* * Fix up the bitmaps... */ e2fsck_read_bitmaps(ctx); ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode)); if (ext2fs_file_acl_block(fs, &inode) && (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { pctx.errcode = ext2fs_adjust_ea_refcount3(fs, ext2fs_file_acl_block(fs, &inode), block_buf, -1, &count, ino); if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { pctx.errcode = 0; count = 1; } if (pctx.errcode) { pctx.blk = ext2fs_file_acl_block(fs, &inode); fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } if (count == 0) { ext2fs_unmark_block_bitmap2(ctx->block_found_map, ext2fs_file_acl_block(fs, &inode)); ext2fs_block_alloc_stats2(fs, ext2fs_file_acl_block(fs, &inode), -1); } ext2fs_file_acl_block_set(fs, &inode, 0); } if (!ext2fs_inode_has_valid_blocks2(fs, &inode)) return; if (LINUX_S_ISREG(inode.i_mode) && EXT2_I_SIZE(&inode) >= 0x80000000UL) ctx->large_files--; del_block.ctx = ctx; del_block.num = 0; pctx.errcode = ext2fs_block_iterate3(fs, ino, 0, block_buf, deallocate_inode_block, &del_block); if (pctx.errcode) { fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx); ctx->flags |= E2F_FLAG_ABORT; return; } }
static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct set_badblock_record *rec = (struct set_badblock_record *) priv_data; errcode_t retval; blk_t blk; if (blockcnt >= 0) { /* * Get the next bad block. */ if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk)) return BLOCK_ABORT; rec->bad_block_count++; } else { /* * An indirect block; fetch a block from the * previously used indirect block list. The block * most be not marked as used; if so, get another one. * If we run out of reserved indirect blocks, allocate * a new one. */ retry: if (rec->ind_blocks_ptr < rec->ind_blocks_size) { blk = rec->ind_blocks[rec->ind_blocks_ptr++]; if (ext2fs_test_block_bitmap2(fs->block_map, blk)) goto retry; } else { retval = ext2fs_new_block(fs, 0, 0, &blk); if (retval) { rec->err = retval; return BLOCK_ABORT; } } retval = io_channel_write_blk64(fs->io, blk, 1, rec->block_buf); if (retval) { rec->err = retval; return BLOCK_ABORT; } } /* * Update block counts */ ext2fs_block_alloc_stats2(fs, blk, +1); *block_nr = blk; return BLOCK_CHANGED; }
/* * Helper function which writes out a directory block. */ static int write_dir_block(ext2_filsys fs, blk64_t *block_nr, e2_blkcnt_t blockcnt, blk64_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct write_dir_struct *wd = (struct write_dir_struct *) priv_data; blk64_t blk; char *dir, *buf = 0; if (*block_nr == 0) return 0; if (blockcnt < 0) return 0; if (blockcnt < wd->outdir->num) dir = wd->outdir->buf + (blockcnt * fs->blocksize); else if (wd->ctx->lost_and_found == wd->dir) { /* Don't release any extra directory blocks for lost+found */ wd->err = ext2fs_new_dir_block(fs, 0, 0, &buf); if (wd->err) return BLOCK_ABORT; dir = buf; wd->outdir->num++; } else { /* We don't need this block, so release it */ e2fsck_read_bitmaps(wd->ctx); blk = *block_nr; /* * In theory, we only release blocks from the end of the * directory file, so it's fine to clobber a whole cluster at * once. */ if (blk % EXT2FS_CLUSTER_RATIO(fs) == 0) { ext2fs_block_alloc_stats2(fs, blk, -1); wd->cleared++; } *block_nr = 0; return BLOCK_CHANGED; } wd->err = ext2fs_write_dir_block4(fs, *block_nr, dir, 0, wd->dir); if (buf) ext2fs_free_mem(&buf); if (wd->err) return BLOCK_ABORT; return 0; }
static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct set_badblock_record *rec = (struct set_badblock_record *) priv_data; errcode_t retval; unsigned long old_size; if (!*block_nr) return 0; /* * If the block number is outrageous, clear it and ignore it. */ if (*block_nr >= ext2fs_blocks_count(fs->super) || *block_nr < fs->super->s_first_data_block) { *block_nr = 0; return BLOCK_CHANGED; } if (blockcnt < 0) { if (rec->ind_blocks_size >= rec->max_ind_blocks) { old_size = rec->max_ind_blocks * sizeof(blk_t); rec->max_ind_blocks += 10; retval = ext2fs_resize_mem(old_size, rec->max_ind_blocks * sizeof(blk_t), &rec->ind_blocks); if (retval) { rec->max_ind_blocks -= 10; rec->err = retval; return BLOCK_ABORT; } } rec->ind_blocks[rec->ind_blocks_size++] = *block_nr; } /* * Mark the block as unused, and update accounting information */ ext2fs_block_alloc_stats2(fs, *block_nr, -1); *block_nr = 0; return BLOCK_CHANGED; }
/* * This function zeros out the allocated block, and updates all of the * appropriate filesystem records. */ errcode_t ext2fs_alloc_block2(ext2_filsys fs, blk64_t goal, char *block_buf, blk64_t *ret) { errcode_t retval; blk64_t block; char *buf = 0; if (!block_buf) { retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; block_buf = buf; } memset(block_buf, 0, fs->blocksize); if (fs->get_alloc_block) { retval = (fs->get_alloc_block)(fs, goal, &block); if (retval) goto fail; } else { if (!fs->block_map) { retval = ext2fs_read_block_bitmap(fs); if (retval) goto fail; } retval = ext2fs_new_block2(fs, goal, 0, &block); if (retval) goto fail; } retval = io_channel_write_blk64(fs->io, block, 1, block_buf); if (retval) goto fail; ext2fs_block_alloc_stats2(fs, block, +1); *ret = block; fail: if (buf) ext2fs_free_mem(&buf); return retval; }
/* * This function is called to deallocate a block, and is an interator * functioned called by deallocate inode via ext2fs_iterate_block(). */ static int deallocate_inode_block(ext2_filsys fs, blk64_t *block_nr, e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), blk64_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct del_block *p = priv_data; if (HOLE_BLKADDR(*block_nr)) return 0; if ((*block_nr < fs->super->s_first_data_block) || (*block_nr >= ext2fs_blocks_count(fs->super))) return 0; ext2fs_unmark_block_bitmap2(p->ctx->block_found_map, *block_nr); ext2fs_block_alloc_stats2(fs, *block_nr, -1); p->num++; return 0; }
/* * Helper function which writes out a directory block. */ static int write_dir_block(ext2_filsys fs, blk64_t *block_nr, e2_blkcnt_t blockcnt, blk64_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct write_dir_struct *wd = (struct write_dir_struct *) priv_data; blk64_t blk; char *dir; if (*block_nr == 0) return 0; if (blockcnt >= wd->outdir->num) { e2fsck_read_bitmaps(wd->ctx); blk = *block_nr; /* * In theory, we only release blocks from the end of the * directory file, so it's fine to clobber a whole cluster at * once. */ if (blk % EXT2FS_CLUSTER_RATIO(fs) == 0) { ext2fs_unmark_block_bitmap2(wd->ctx->block_found_map, blk); ext2fs_block_alloc_stats2(fs, blk, -1); wd->cleared++; } *block_nr = 0; return BLOCK_CHANGED; } if (blockcnt < 0) return 0; dir = wd->outdir->buf + (blockcnt * fs->blocksize); wd->err = ext2fs_write_dir_block3(fs, *block_nr, dir, 0); if (wd->err) return BLOCK_ABORT; return 0; }
/* * This function zeros out the allocated block, and updates all of the * appropriate filesystem records. */ errcode_t ext2fs_alloc_block2(ext2_filsys fs, blk64_t goal, char *block_buf, blk64_t *ret) { errcode_t retval; blk64_t block; if (fs->get_alloc_block) { retval = (fs->get_alloc_block)(fs, goal, &block); if (retval) goto fail; } else { if (!fs->block_map) { retval = ext2fs_read_block_bitmap(fs); if (retval) goto fail; } retval = ext2fs_new_block2(fs, goal, 0, &block); if (retval) goto fail; } if (block_buf) { memset(block_buf, 0, fs->blocksize); retval = io_channel_write_blk64(fs->io, block, 1, block_buf); } else retval = ext2fs_zero_blocks2(fs, block, 1, NULL, NULL); if (retval) goto fail; ext2fs_block_alloc_stats2(fs, block, +1); *ret = block; fail: return retval; }
static int mkjournal_proc(ext2_filsys fs, blk64_t *blocknr, e2_blkcnt_t blockcnt, blk64_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct mkjournal_struct *es = (struct mkjournal_struct *) priv_data; blk64_t new_blk; errcode_t retval; if (*blocknr) { es->goal = *blocknr; return 0; } if (blockcnt && (EXT2FS_B2C(fs, es->goal) == EXT2FS_B2C(fs, es->goal+1))) new_blk = es->goal+1; else { es->goal &= ~EXT2FS_CLUSTER_MASK(fs); retval = ext2fs_new_block2(fs, es->goal, 0, &new_blk); if (retval) { es->err = retval; return BLOCK_ABORT; } ext2fs_block_alloc_stats2(fs, new_blk, +1); es->newblocks++; } if (blockcnt >= 0) es->num_blocks--; retval = 0; if (blockcnt <= 0) retval = io_channel_write_blk64(fs->io, new_blk, 1, es->buf); else if (!(es->flags & EXT2_MKJOURNAL_LAZYINIT)) { if (es->zero_count) { if ((es->blk_to_zero + es->zero_count == new_blk) && (es->zero_count < 1024)) es->zero_count++; else { retval = ext2fs_zero_blocks2(fs, es->blk_to_zero, es->zero_count, 0, 0); es->zero_count = 0; } } if (es->zero_count == 0) { es->blk_to_zero = new_blk; es->zero_count = 1; } } if (blockcnt == 0) memset(es->buf, 0, fs->blocksize); if (retval) { es->err = retval; return BLOCK_ABORT; } *blocknr = es->goal = new_blk; if (es->num_blocks == 0) return (BLOCK_CHANGED | BLOCK_ABORT); else return BLOCK_CHANGED; }
/* * This routine gets the lost_and_found inode, making it a directory * if necessary */ ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix) { ext2_filsys fs = ctx->fs; ext2_ino_t ino; blk64_t blk; errcode_t retval; struct ext2_inode inode; char * block; static const char name[] = "lost+found"; struct problem_context pctx; if (ctx->lost_and_found) return ctx->lost_and_found; clear_problem_context(&pctx); retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, sizeof(name)-1, 0, &ino); if (retval && !fix) return 0; if (!retval) { if (ext2fs_check_directory(fs, ino) == 0) { ctx->lost_and_found = ino; return ino; } /* Lost+found isn't a directory! */ if (!fix) return 0; pctx.ino = ino; if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx)) return 0; /* OK, unlink the old /lost+found file. */ pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0); if (pctx.errcode) { pctx.str = "ext2fs_unlink"; fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); return 0; } (void) e2fsck_dir_info_set_parent(ctx, ino, 0); e2fsck_adjust_inode_count(ctx, ino, -1); } else if (retval != EXT2_ET_FILE_NOT_FOUND) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx); } if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0)) return 0; /* * Read the inode and block bitmaps in; we'll be messing with * them. */ e2fsck_read_bitmaps(ctx); /* * First, find a free block */ retval = ext2fs_new_block2(fs, 0, ctx->block_found_map, &blk); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx); return 0; } ext2fs_mark_block_bitmap2(ctx->block_found_map, blk); ext2fs_block_alloc_stats2(fs, blk, +1); /* * Next find a free inode. */ retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700, ctx->inode_used_map, &ino); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx); return 0; } ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino); ext2fs_mark_inode_bitmap2(ctx->inode_dir_map, ino); ext2fs_inode_alloc_stats2(fs, ino, +1, 1); /* * Now let's create the actual data block for the inode */ retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx); return 0; } retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino); ext2fs_free_mem(&block); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx); return 0; } /* * Set up the inode structure */ memset(&inode, 0, sizeof(inode)); inode.i_mode = 040700; inode.i_size = fs->blocksize; inode.i_atime = inode.i_ctime = inode.i_mtime = ctx->now; inode.i_links_count = 2; ext2fs_iblk_set(fs, &inode, 1); inode.i_block[0] = blk; /* * Next, write out the inode. */ pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode); if (pctx.errcode) { pctx.str = "ext2fs_write_inode"; fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); return 0; } /* * Finally, create the directory link */ pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR); if (pctx.errcode) { pctx.str = "ext2fs_link"; fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); return 0; } /* * Miscellaneous bookkeeping that needs to be kept straight. */ e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO); e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1); ext2fs_icount_store(ctx->inode_count, ino, 2); ext2fs_icount_store(ctx->inode_link_info, ino, 2); ctx->lost_and_found = ino; quota_data_add(ctx->qctx, &inode, ino, fs->blocksize); quota_data_inodes(ctx->qctx, &inode, ino, +1); #if 0 printf("/lost+found created; inode #%lu\n", ino); #endif return ino; }
errcode_t ext2fs_symlink(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t ino, const char *name, char *target) { ext2_extent_handle_t handle; errcode_t retval; struct ext2_inode inode; ext2_ino_t scratch_ino; blk64_t blk; int fastlink; unsigned int target_len; char *block_buf = 0; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); /* The Linux kernel doesn't allow for links longer than a block */ target_len = strlen(target); if (target_len > fs->blocksize) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } /* * Allocate a data block for slow links */ fastlink = (target_len < sizeof(inode.i_block)); if (!fastlink) { retval = ext2fs_new_block2(fs, 0, 0, &blk); if (retval) goto cleanup; retval = ext2fs_get_mem(fs->blocksize, &block_buf); if (retval) goto cleanup; } /* * Allocate an inode, if necessary */ if (!ino) { retval = ext2fs_new_inode(fs, parent, LINUX_S_IFLNK | 0755, 0, &ino); if (retval) goto cleanup; } /* * Create the inode structure.... */ memset(&inode, 0, sizeof(struct ext2_inode)); inode.i_mode = LINUX_S_IFLNK | 0777; inode.i_uid = inode.i_gid = 0; ext2fs_iblk_set(fs, &inode, fastlink ? 0 : 1); inode.i_links_count = 1; inode.i_size = target_len; /* The time fields are set by ext2fs_write_new_inode() */ if (fastlink) { /* Fast symlinks, target stored in inode */ strcpy((char *)&inode.i_block, target); } else { /* Slow symlinks, target stored in the first block */ memset(block_buf, 0, fs->blocksize); strcpy(block_buf, target); if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) { /* * The extent bmap is setup after the inode and block * have been written out below. */ inode.i_flags |= EXT4_EXTENTS_FL; } } /* * Write out the inode and inode data block. The inode generation * number is assigned by write_new_inode, which means that the * operations using ino must come after it. */ retval = ext2fs_write_new_inode(fs, ino, &inode); if (retval) goto cleanup; if (!fastlink) { retval = ext2fs_bmap2(fs, ino, &inode, NULL, BMAP_SET, 0, NULL, &blk); if (retval) goto cleanup; retval = io_channel_write_blk64(fs->io, blk, 1, block_buf); if (retval) goto cleanup; } /* * Link the symlink into the filesystem hierarchy */ if (name) { retval = ext2fs_lookup(fs, parent, name, strlen(name), 0, &scratch_ino); if (!retval) { retval = EXT2_ET_FILE_EXISTS; goto cleanup; } if (retval != EXT2_ET_FILE_NOT_FOUND) goto cleanup; retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_SYMLINK); if (retval) goto cleanup; } /* * Update accounting.... */ if (!fastlink) ext2fs_block_alloc_stats2(fs, blk, +1); ext2fs_inode_alloc_stats2(fs, ino, +1, 0); cleanup: if (block_buf) ext2fs_free_mem(&block_buf); return retval; }
static int expand_dir_proc(ext2_filsys fs, blk64_t *blocknr, e2_blkcnt_t blockcnt, blk64_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data; blk64_t new_blk; char *block; errcode_t retval; if (*blocknr) { if (blockcnt >= 0) es->goal = *blocknr; return 0; } if (blockcnt && (EXT2FS_B2C(fs, es->goal) == EXT2FS_B2C(fs, es->goal+1))) new_blk = es->goal+1; else { es->goal &= ~EXT2FS_CLUSTER_MASK(fs); retval = ext2fs_new_block2(fs, es->goal, 0, &new_blk); if (retval) { es->err = retval; return BLOCK_ABORT; } es->newblocks++; } if (blockcnt > 0) { retval = ext2fs_new_dir_block(fs, 0, 0, &block); if (retval) { es->err = retval; return BLOCK_ABORT; } es->done = 1; retval = ext2fs_write_dir_block(fs, new_blk, block); } else { retval = ext2fs_get_mem(fs->blocksize, &block); if (retval) { es->err = retval; return BLOCK_ABORT; } memset(block, 0, fs->blocksize); retval = io_channel_write_blk64(fs->io, new_blk, 1, block); } if (blockcnt >= 0) es->goal = new_blk; if (retval) { es->err = retval; return BLOCK_ABORT; } ext2fs_free_mem(&block); *blocknr = new_blk; ext2fs_block_alloc_stats2(fs, new_blk, +1); if (es->done) return (BLOCK_CHANGED | BLOCK_ABORT); else return BLOCK_CHANGED; }
errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum, const char *name) { ext2_extent_handle_t handle; errcode_t retval; struct ext2_inode parent_inode, inode; ext2_ino_t ino = inum; ext2_ino_t scratch_ino; blk64_t blk; char *block = 0; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); /* * Allocate an inode, if necessary */ if (!ino) { retval = ext2fs_new_inode(fs, parent, LINUX_S_IFDIR | 0755, 0, &ino); if (retval) goto cleanup; } /* * Allocate a data block for the directory */ retval = ext2fs_new_block2(fs, 0, 0, &blk); if (retval) goto cleanup; /* * Create a scratch template for the directory */ retval = ext2fs_new_dir_block(fs, ino, parent, &block); if (retval) goto cleanup; /* * Get the parent's inode, if necessary */ if (parent != ino) { retval = ext2fs_read_inode(fs, parent, &parent_inode); if (retval) goto cleanup; } else memset(&parent_inode, 0, sizeof(parent_inode)); /* * Create the inode structure.... */ memset(&inode, 0, sizeof(struct ext2_inode)); inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask); inode.i_uid = inode.i_gid = 0; ext2fs_iblk_set(fs, &inode, 1); if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) inode.i_flags |= EXT4_EXTENTS_FL; else inode.i_block[0] = blk; inode.i_links_count = 2; inode.i_size = fs->blocksize; /* * Write out the inode and inode data block */ retval = ext2fs_write_dir_block(fs, blk, block); if (retval) goto cleanup; retval = ext2fs_write_new_inode(fs, ino, &inode); if (retval) goto cleanup; if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) { retval = ext2fs_extent_open2(fs, ino, &inode, &handle); if (retval) goto cleanup; retval = ext2fs_extent_set_bmap(handle, 0, blk, 0); ext2fs_extent_free(handle); if (retval) goto cleanup; } /* * Link the directory into the filesystem hierarchy */ if (name) { retval = ext2fs_lookup(fs, parent, name, strlen(name), 0, &scratch_ino); if (!retval) { retval = EXT2_ET_DIR_EXISTS; name = 0; goto cleanup; } if (retval != EXT2_ET_FILE_NOT_FOUND) goto cleanup; retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_DIR); if (retval) goto cleanup; } /* * Update parent inode's counts */ if (parent != ino) { parent_inode.i_links_count++; retval = ext2fs_write_inode(fs, parent, &parent_inode); if (retval) goto cleanup; } /* * Update accounting.... */ ext2fs_block_alloc_stats2(fs, blk, +1); ext2fs_inode_alloc_stats2(fs, ino, +1, 1); cleanup: if (block) ext2fs_free_mem(&block); return retval; }
/* * This routine gets the lost_and_found inode, making it a directory * if necessary */ ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix) { ext2_filsys fs = ctx->fs; ext2_ino_t ino; blk64_t blk; errcode_t retval; struct ext2_inode inode; char * block; static const char name[] = "lost+found"; struct problem_context pctx; int will_rehash, flags; if (ctx->lost_and_found) return ctx->lost_and_found; clear_problem_context(&pctx); will_rehash = e2fsck_dir_will_be_rehashed(ctx, EXT2_ROOT_INO); if (will_rehash) { flags = ctx->fs->flags; ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; } retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, sizeof(name)-1, 0, &ino); if (will_rehash) ctx->fs->flags = (flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) | (ctx->fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS); if (retval && !fix) return 0; if (!retval) { /* Lost+found shouldn't have inline data */ retval = ext2fs_read_inode(fs, ino, &inode); if (fix && retval) return 0; if (fix && (inode.i_flags & EXT4_INLINE_DATA_FL)) { if (!fix_problem(ctx, PR_3_LPF_INLINE_DATA, &pctx)) return 0; goto unlink; } if (fix && (inode.i_flags & EXT4_ENCRYPT_FL)) { if (!fix_problem(ctx, PR_3_LPF_ENCRYPTED, &pctx)) return 0; goto unlink; } if (ext2fs_check_directory(fs, ino) == 0) { ctx->lost_and_found = ino; return ino; } /* Lost+found isn't a directory! */ if (!fix) return 0; pctx.ino = ino; if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx)) return 0; unlink: /* OK, unlink the old /lost+found file. */ pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0); if (pctx.errcode) { pctx.str = "ext2fs_unlink"; fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); return 0; } (void) e2fsck_dir_info_set_parent(ctx, ino, 0); e2fsck_adjust_inode_count(ctx, ino, -1); /* * If the old lost+found was a directory, we've just * disconnected it from the directory tree, which * means we need to restart the directory tree scan. * The simplest way to do this is restart the whole * e2fsck operation. */ if (LINUX_S_ISDIR(inode.i_mode)) ctx->flags |= E2F_FLAG_RESTART; } else if (retval != EXT2_ET_FILE_NOT_FOUND) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx); } if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0)) return 0; /* * Read the inode and block bitmaps in; we'll be messing with * them. */ e2fsck_read_bitmaps(ctx); /* * First, find a free block */ if (ctx->lnf_repair_block) { blk = ctx->lnf_repair_block; ctx->lnf_repair_block = 0; goto skip_new_block; } retval = ext2fs_new_block2(fs, 0, ctx->block_found_map, &blk); if (retval == EXT2_ET_BLOCK_ALLOC_FAIL && fix_problem(ctx, PR_3_LPF_NO_SPACE, &pctx)) { fix_problem(ctx, PR_3_NO_SPACE_TO_RECOVER, &pctx); ctx->lost_and_found = EXT2_ROOT_INO; return 0; } if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx); return 0; } ext2fs_mark_block_bitmap2(ctx->block_found_map, blk); skip_new_block: ext2fs_block_alloc_stats2(fs, blk, +1); /* * Next find a free inode. */ retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700, ctx->inode_used_map, &ino); if (retval == EXT2_ET_INODE_ALLOC_FAIL && fix_problem(ctx, PR_3_LPF_NO_SPACE, &pctx)) { fix_problem(ctx, PR_3_NO_SPACE_TO_RECOVER, &pctx); ctx->lost_and_found = EXT2_ROOT_INO; return 0; } if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx); return 0; } ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino); ext2fs_mark_inode_bitmap2(ctx->inode_dir_map, ino); ext2fs_inode_alloc_stats2(fs, ino, +1, 1); /* * Set up the inode structure */ memset(&inode, 0, sizeof(inode)); inode.i_mode = 040700; inode.i_size = fs->blocksize; inode.i_atime = inode.i_ctime = inode.i_mtime = ctx->now; inode.i_links_count = 2; ext2fs_iblk_set(fs, &inode, 1); inode.i_block[0] = blk; /* * Next, write out the inode. */ pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode); if (pctx.errcode) { pctx.str = "ext2fs_write_inode"; fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); return 0; } /* * Now let's create the actual data block for the inode. * Due to metadata_csum, the directory block MUST be written * after the inode is written to disk! */ retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx); return 0; } retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino); ext2fs_free_mem(&block); if (retval) { pctx.errcode = retval; fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx); return 0; } /* * Finally, create the directory link */ pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR); if (pctx.errcode == EXT2_ET_DIR_NO_SPACE) { pctx.errcode = ext2fs_expand_dir(fs, EXT2_ROOT_INO); if (pctx.errcode) goto link_error; pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR); } if (pctx.errcode) { link_error: pctx.str = "ext2fs_link"; fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); return 0; } /* * Miscellaneous bookkeeping that needs to be kept straight. */ e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO); e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1); ext2fs_icount_store(ctx->inode_count, ino, 2); ext2fs_icount_store(ctx->inode_link_info, ino, 2); ctx->lost_and_found = ino; quota_data_add(ctx->qctx, &inode, ino, fs->blocksize); quota_data_inodes(ctx->qctx, &inode, ino, +1); #if 0 printf("/lost+found created; inode #%lu\n", ino); #endif return ino; }