/* * Make sure the first entry in the directory is '.', and that the * directory entry is sane. */ static int check_dot(e2fsck_t ctx, struct ext2_dir_entry *dirent, ext2_ino_t ino, struct problem_context *pctx) { struct ext2_dir_entry *nextdir; unsigned int rec_len, new_len; int status = 0; int created = 0; problem_t problem = 0; if (!dirent->inode) problem = PR_2_MISSING_DOT; else if ((ext2fs_dirent_name_len(dirent) != 1) || (dirent->name[0] != '.')) problem = PR_2_1ST_NOT_DOT; else if (dirent->name[1] != '\0') problem = PR_2_DOT_NULL_TERM; (void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len); if (problem) { if (fix_problem(ctx, problem, pctx)) { if (rec_len < 12) rec_len = dirent->rec_len = 12; dirent->inode = ino; ext2fs_dirent_set_name_len(dirent, 1); ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN); dirent->name[0] = '.'; dirent->name[1] = '\0'; status = 1; created = 1; } } if (dirent->inode != ino) { if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) { dirent->inode = ino; status = 1; } } if (rec_len > 12) { new_len = rec_len - 12; if (new_len > 12) { if (created || fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) { nextdir = (struct ext2_dir_entry *) ((char *) dirent + 12); dirent->rec_len = 12; (void) ext2fs_set_rec_len(ctx->fs, new_len, nextdir); nextdir->inode = 0; ext2fs_dirent_set_name_len(nextdir, 0); ext2fs_dirent_set_file_type(nextdir, EXT2_FT_UNKNOWN); status = 1; } } } return status; }
static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf) { struct ext2_dir_entry *dir; struct ext2_dx_countlimit *limits; memset(buf, 0, fs->blocksize); dir = (struct ext2_dir_entry *) buf; dir->inode = 0; (void) ext2fs_set_rec_len(fs, fs->blocksize, dir); limits = (struct ext2_dx_countlimit *) (buf+8); limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry); limits->count = 0; return (struct ext2_dx_entry *) limits; }
static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf) { struct ext2_dir_entry *dir; struct ext2_dx_countlimit *limits; int csum_size = 0; memset(buf, 0, fs->blocksize); dir = (struct ext2_dir_entry *) buf; dir->inode = 0; (void) ext2fs_set_rec_len(fs, fs->blocksize, dir); if (ext2fs_has_feature_metadata_csum(fs->super)) csum_size = sizeof(struct ext2_dx_tail); limits = (struct ext2_dx_countlimit *) (buf+8); limits->limit = (fs->blocksize - (8 + csum_size)) / sizeof(struct ext2_dx_entry); limits->count = 0; return (struct ext2_dx_entry *) limits; }
static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf) { struct ext2_dir_entry *dir; struct ext2_dx_countlimit *limits; int csum_size = 0; memset(buf, 0, fs->blocksize); dir = (struct ext2_dir_entry *) buf; dir->inode = 0; (void) ext2fs_set_rec_len(fs, fs->blocksize, dir); if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) csum_size = sizeof(struct ext2_dx_tail); limits = (struct ext2_dx_countlimit *) (buf+8); limits->limit = (fs->blocksize - (8 + csum_size)) / sizeof(struct ext2_dx_entry); limits->count = 0; return (struct ext2_dx_entry *) limits; }
static errcode_t copy_dir_entries(e2fsck_t ctx, struct fill_dir_struct *fd, struct out_dir *outdir) { ext2_filsys fs = ctx->fs; errcode_t retval; char *block_start; struct hash_entry *ent; struct ext2_dir_entry *dirent; unsigned int rec_len, prev_rec_len, left, slack, offset; int i; ext2_dirhash_t prev_hash; int csum_size = 0; struct ext2_dir_entry_tail *t; if (ctx->htree_slack_percentage == 255) { profile_get_uint(ctx->profile, "options", "indexed_dir_slack_percentage", 0, 20, &ctx->htree_slack_percentage); if (ctx->htree_slack_percentage > 100) ctx->htree_slack_percentage = 20; } if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) csum_size = sizeof(struct ext2_dir_entry_tail); outdir->max = 0; retval = alloc_size_dir(fs, outdir, (fd->dir_size / fs->blocksize) + 2); if (retval) return retval; outdir->num = fd->compress ? 0 : 1; offset = 0; outdir->hashes[0] = 0; prev_hash = 1; if ((retval = get_next_block(fs, outdir, &block_start))) return retval; dirent = (struct ext2_dir_entry *) block_start; prev_rec_len = 0; rec_len = 0; left = fs->blocksize - csum_size; slack = fd->compress ? 12 : ((fs->blocksize - csum_size) * ctx->htree_slack_percentage)/100; if (slack < 12) slack = 12; for (i = 0; i < fd->num_array; i++) { ent = fd->harray + i; if (ent->dir->inode == 0) continue; rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(ent->dir)); if (rec_len > left) { if (left) { left += prev_rec_len; retval = ext2fs_set_rec_len(fs, left, dirent); if (retval) return retval; } if (csum_size) { t = EXT2_DIRENT_TAIL(block_start, fs->blocksize); ext2fs_initialize_dirent_tail(fs, t); } if ((retval = get_next_block(fs, outdir, &block_start))) return retval; offset = 0; } left = (fs->blocksize - csum_size) - offset; dirent = (struct ext2_dir_entry *) (block_start + offset); if (offset == 0) { if (ent->hash == prev_hash) outdir->hashes[outdir->num-1] = ent->hash | 1; else outdir->hashes[outdir->num-1] = ent->hash; } dirent->inode = ent->dir->inode; ext2fs_dirent_set_name_len(dirent, ext2fs_dirent_name_len(ent->dir)); ext2fs_dirent_set_file_type(dirent, ext2fs_dirent_file_type(ent->dir)); retval = ext2fs_set_rec_len(fs, rec_len, dirent); if (retval) return retval; prev_rec_len = rec_len; memcpy(dirent->name, ent->dir->name, ext2fs_dirent_name_len(dirent)); offset += rec_len; left -= rec_len; if (left < slack) { prev_rec_len += left; retval = ext2fs_set_rec_len(fs, prev_rec_len, dirent); if (retval) return retval; offset += left; left = 0; } prev_hash = ent->hash; } if (left) retval = ext2fs_set_rec_len(fs, rec_len + left, dirent); if (csum_size) { t = EXT2_DIRENT_TAIL(block_start, fs->blocksize); ext2fs_initialize_dirent_tail(fs, t); } return retval; }
static int fill_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 fill_dir_struct *fd = (struct fill_dir_struct *) priv_data; struct hash_entry *new_array, *ent; struct ext2_dir_entry *dirent; char *dir; unsigned int offset, dir_offset, rec_len, name_len; int hash_alg; if (blockcnt < 0) return 0; offset = blockcnt * fs->blocksize; if (offset + fs->blocksize > fd->inode->i_size) { fd->err = EXT2_ET_DIR_CORRUPTED; return BLOCK_ABORT; } dir = (fd->buf+offset); if (*block_nr == 0) { memset(dir, 0, fs->blocksize); dirent = (struct ext2_dir_entry *) dir; (void) ext2fs_set_rec_len(fs, fs->blocksize, dirent); } else { int flags = fs->flags; fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; fd->err = ext2fs_read_dir_block4(fs, *block_nr, dir, 0, fd->dir); fs->flags = (flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) | (fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS); if (fd->err) return BLOCK_ABORT; } hash_alg = fs->super->s_def_hash_version; if ((hash_alg <= EXT2_HASH_TEA) && (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH)) hash_alg += 3; /* While the directory block is "hot", index it. */ dir_offset = 0; while (dir_offset < fs->blocksize) { dirent = (struct ext2_dir_entry *) (dir + dir_offset); (void) ext2fs_get_rec_len(fs, dirent, &rec_len); name_len = ext2fs_dirent_name_len(dirent); if (((dir_offset + rec_len) > fs->blocksize) || (rec_len < 8) || ((rec_len % 4) != 0) || (name_len + 8 > rec_len)) { fd->err = EXT2_ET_DIR_CORRUPTED; return BLOCK_ABORT; } dir_offset += rec_len; if (dirent->inode == 0) continue; if (!fd->compress && (name_len == 1) && (dirent->name[0] == '.')) continue; if (!fd->compress && (name_len == 2) && (dirent->name[0] == '.') && (dirent->name[1] == '.')) { fd->parent = dirent->inode; continue; } if (fd->num_array >= fd->max_array) { new_array = realloc(fd->harray, sizeof(struct hash_entry) * (fd->max_array+500)); if (!new_array) { fd->err = ENOMEM; return BLOCK_ABORT; } fd->harray = new_array; fd->max_array += 500; } ent = fd->harray + fd->num_array++; ent->dir = dirent; fd->dir_size += EXT2_DIR_REC_LEN(name_len); ent->ino = dirent->inode; if (fd->compress) ent->hash = ent->minor_hash = 0; else { fd->err = ext2fs_dirhash(hash_alg, dirent->name, name_len, fs->super->s_hash_seed, &ent->hash, &ent->minor_hash); if (fd->err) return BLOCK_ABORT; } } return 0; }
static errcode_t ext2fs_inline_data_convert_dir(ext2_filsys fs, ext2_ino_t ino, char *bbuf, char *ibuf, int size) { struct ext2_dir_entry *dir, *dir2; struct ext2_dir_entry_tail *t; errcode_t retval; unsigned int offset, rec_len; int csum_size = 0; int filetype = 0; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) csum_size = sizeof(struct ext2_dir_entry_tail); /* Create '.' and '..' */ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT2_FEATURE_INCOMPAT_FILETYPE)) filetype = EXT2_FT_DIR; /* * Set up entry for '.' */ dir = (struct ext2_dir_entry *) bbuf; dir->inode = ino; ext2fs_dirent_set_name_len(dir, 1); ext2fs_dirent_set_file_type(dir, filetype); dir->name[0] = '.'; rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1); dir->rec_len = EXT2_DIR_REC_LEN(1); /* * Set up entry for '..' */ dir = (struct ext2_dir_entry *) (bbuf + dir->rec_len); dir->rec_len = EXT2_DIR_REC_LEN(2); dir->inode = ext2fs_le32_to_cpu(((__u32 *)ibuf)[0]); ext2fs_dirent_set_name_len(dir, 2); ext2fs_dirent_set_file_type(dir, filetype); dir->name[0] = '.'; dir->name[1] = '.'; /* * Ajust the last rec_len */ offset = EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2); dir = (struct ext2_dir_entry *) (bbuf + offset); memcpy(bbuf + offset, ibuf + EXT4_INLINE_DATA_DOTDOT_SIZE, size - EXT4_INLINE_DATA_DOTDOT_SIZE); size += EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) - EXT4_INLINE_DATA_DOTDOT_SIZE; do { dir2 = dir; retval = ext2fs_get_rec_len(fs, dir, &rec_len); if (retval) goto err; offset += rec_len; dir = (struct ext2_dir_entry *) (bbuf + offset); } while (offset < size); rec_len += fs->blocksize - csum_size - offset; retval = ext2fs_set_rec_len(fs, rec_len, dir2); if (retval) goto err; if (csum_size) { t = EXT2_DIRENT_TAIL(bbuf, fs->blocksize); ext2fs_initialize_dirent_tail(fs, t); } err: return retval; }
int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino, void *priv_data) { struct dir_context *ctx; struct ext2_inode inode; struct ext2_dir_entry dirent; struct ext2_inline_data data; int ret = BLOCK_ABORT; e2_blkcnt_t blockcnt = 0; char *old_buf; unsigned int old_buflen; int old_flags; ctx = (struct dir_context *)priv_data; old_buf = ctx->buf; old_buflen = ctx->buflen; old_flags = ctx->flags; ctx->flags |= DIRENT_FLAG_INCLUDE_INLINE_DATA; ctx->errcode = ext2fs_read_inode(fs, ino, &inode); if (ctx->errcode) goto out; if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) { ctx->errcode = EXT2_ET_NO_INLINE_DATA; goto out; } if (!LINUX_S_ISDIR(inode.i_mode)) { ctx->errcode = EXT2_ET_NO_DIRECTORY; goto out; } ret = 0; /* we first check '.' and '..' dir */ dirent.inode = ino; dirent.name_len = 1; ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(2), &dirent); dirent.name[0] = '.'; dirent.name[1] = '\0'; ctx->buf = (char *)&dirent; ext2fs_get_rec_len(fs, &dirent, &ctx->buflen); ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); if (ret & BLOCK_ABORT) goto out; dirent.inode = ext2fs_le32_to_cpu(inode.i_block[0]); dirent.name_len = 2; ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(3), &dirent); dirent.name[0] = '.'; dirent.name[1] = '.'; dirent.name[2] = '\0'; ctx->buf = (char *)&dirent; ext2fs_get_rec_len(fs, &dirent, &ctx->buflen); ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); if (ret & BLOCK_INLINE_DATA_CHANGED) { errcode_t err; inode.i_block[0] = ext2fs_cpu_to_le32(dirent.inode); err = ext2fs_write_inode(fs, ino, &inode); if (err) goto out; ret &= ~BLOCK_INLINE_DATA_CHANGED; } if (ret & BLOCK_ABORT) goto out; ctx->buf = (char *)inode.i_block + EXT4_INLINE_DATA_DOTDOT_SIZE; ctx->buflen = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE; #ifdef WORDS_BIGENDIAN ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0); if (ctx->errcode) { ret |= BLOCK_ABORT; goto out; } #endif ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); if (ret & BLOCK_INLINE_DATA_CHANGED) { #ifdef WORDS_BIGENDIAN ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf, ctx->buflen, 0); if (ctx->errcode) { ret |= BLOCK_ABORT; goto out; } #endif ctx->errcode = ext2fs_write_inode(fs, ino, &inode); if (ctx->errcode) ret |= BLOCK_ABORT; ret &= ~BLOCK_INLINE_DATA_CHANGED; } if (ret & BLOCK_ABORT) goto out; data.fs = fs; data.ino = ino; ctx->errcode = ext2fs_inline_data_ea_get(&data); if (ctx->errcode) { ret |= BLOCK_ABORT; goto out; } if (data.ea_size <= 0) goto out1; ctx->buf = data.ea_data; ctx->buflen = data.ea_size; #ifdef WORDS_BIGENDIAN ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0); if (ctx->errcode) { ret |= BLOCK_ABORT; goto out1; } #endif ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data); if (ret & BLOCK_INLINE_DATA_CHANGED) { #ifdef WORDS_BIGENDIAN ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf, ctx->buflen, 0); if (ctx->errcode) { ret |= BLOCK_ABORT; goto out1; } #endif ctx->errcode = ext2fs_inline_data_ea_set(&data); if (ctx->errcode) ret |= BLOCK_ABORT; } out1: ext2fs_free_mem(&data.ea_data); out: ctx->buf = old_buf; ctx->buflen = old_buflen; ctx->flags = old_flags; ret &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED); return ret; }
/* * Given a busted directory, try to salvage it somehow. * */ static void salvage_directory(ext2_filsys fs, struct ext2_dir_entry *dirent, struct ext2_dir_entry *prev, unsigned int *offset) { char *cp = (char *) dirent; int left; unsigned int rec_len, prev_rec_len; unsigned int name_len = dirent->name_len & 0xFF; (void) ext2fs_get_rec_len(fs, dirent, &rec_len); left = fs->blocksize - *offset - rec_len; /* * Special case of directory entry of size 8: copy what's left * of the directory block up to cover up the invalid hole. */ if ((left >= 12) && (rec_len == 8)) { memmove(cp, cp+8, left); memset(cp + left, 0, 8); return; } /* * If the directory entry overruns the end of the directory * block, and the name is small enough to fit, then adjust the * record length. */ if ((left < 0) && ((int) rec_len + left > 8) && ((int) name_len + 8 <= (int) rec_len + left) && dirent->inode <= fs->super->s_inodes_count && strnlen(dirent->name, name_len) == name_len) { (void) ext2fs_set_rec_len(fs, (int) rec_len + left, dirent); return; } /* * If the record length of the directory entry is a multiple * of four, and not too big, such that it is valid, let the * previous directory entry absorb the invalid one. */ if (prev && rec_len && (rec_len % 4) == 0 && (*offset + rec_len <= fs->blocksize)) { (void) ext2fs_get_rec_len(fs, prev, &prev_rec_len); prev_rec_len += rec_len; (void) ext2fs_set_rec_len(fs, prev_rec_len, prev); *offset += rec_len; return; } /* * Default salvage method --- kill all of the directory * entries for the rest of the block. We will either try to * absorb it into the previous directory entry, or create a * new empty directory entry the rest of the directory block. */ if (prev) { (void) ext2fs_get_rec_len(fs, prev, &prev_rec_len); prev_rec_len += fs->blocksize - *offset; (void) ext2fs_set_rec_len(fs, prev_rec_len, prev); *offset = fs->blocksize; } else { rec_len = fs->blocksize - *offset; (void) ext2fs_set_rec_len(fs, rec_len, dirent); dirent->name_len = 0; dirent->inode = 0; } }
static errcode_t copy_dir_entries(e2fsck_t ctx, struct fill_dir_struct *fd, struct out_dir *outdir) { ext2_filsys fs = ctx->fs; errcode_t retval; char *block_start; struct hash_entry *ent; struct ext2_dir_entry *dirent; unsigned int rec_len, prev_rec_len; int i, left; ext2_dirhash_t prev_hash; int offset, slack; if (ctx->htree_slack_percentage == 255) { profile_get_uint(ctx->profile, "options", "indexed_dir_slack_percentage", 0, 20, &ctx->htree_slack_percentage); if (ctx->htree_slack_percentage > 100) ctx->htree_slack_percentage = 20; } outdir->max = 0; retval = alloc_size_dir(fs, outdir, (fd->dir_size / fs->blocksize) + 2); if (retval) return retval; outdir->num = fd->compress ? 0 : 1; offset = 0; outdir->hashes[0] = 0; prev_hash = 1; if ((retval = get_next_block(fs, outdir, &block_start))) return retval; dirent = (struct ext2_dir_entry *) block_start; prev_rec_len = 0; left = fs->blocksize; slack = fd->compress ? 12 : (fs->blocksize * ctx->htree_slack_percentage)/100; if (slack < 12) slack = 12; for (i=0; i < fd->num_array; i++) { ent = fd->harray + i; if (ent->dir->inode == 0) continue; rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF); if (rec_len > left) { if (left) { left += prev_rec_len; retval = ext2fs_set_rec_len(fs, left, dirent); if (retval) return retval; } if ((retval = get_next_block(fs, outdir, &block_start))) return retval; offset = 0; } left = fs->blocksize - offset; dirent = (struct ext2_dir_entry *) (block_start + offset); if (offset == 0) { if (ent->hash == prev_hash) outdir->hashes[outdir->num-1] = ent->hash | 1; else outdir->hashes[outdir->num-1] = ent->hash; } dirent->inode = ent->dir->inode; dirent->name_len = ent->dir->name_len; retval = ext2fs_set_rec_len(fs, rec_len, dirent); if (retval) return retval; prev_rec_len = rec_len; memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF); offset += rec_len; left -= rec_len; if (left < slack) { prev_rec_len += left; retval = ext2fs_set_rec_len(fs, prev_rec_len, dirent); if (retval) return retval; offset += left; left = 0; } prev_hash = ent->hash; } if (left) retval = ext2fs_set_rec_len(fs, rec_len + left, dirent); return retval; }
static int link_proc(struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data) { struct link_struct *ls = (struct link_struct *) priv_data; struct ext2_dir_entry *next; unsigned int rec_len, min_rec_len, curr_rec_len; int ret = 0; int csum_size = 0; struct ext2_dir_entry_tail *t; if (ls->done) return DIRENT_ABORT; rec_len = EXT2_DIR_REC_LEN(ls->namelen); ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len); if (ls->err) return DIRENT_ABORT; if (EXT2_HAS_RO_COMPAT_FEATURE(ls->fs->super, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) csum_size = sizeof(struct ext2_dir_entry_tail); /* * See if the following directory entry (if any) is unused; * if so, absorb it into this one. */ next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len); if ((offset + (int) curr_rec_len < blocksize - (8 + csum_size)) && (next->inode == 0) && (offset + (int) curr_rec_len + (int) next->rec_len <= blocksize)) { curr_rec_len += next->rec_len; ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent); if (ls->err) return DIRENT_ABORT; ret = DIRENT_CHANGED; } /* * Since ext2fs_link blows away htree data, we need to be * careful -- if metadata_csum is enabled and we're passed in * a dirent that contains htree data, we need to create the * fake entry at the end of the block that hides the checksum. */ /* De-convert a dx_node block */ if (csum_size && curr_rec_len == ls->fs->blocksize && !dirent->inode) { curr_rec_len -= csum_size; ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent); if (ls->err) return DIRENT_ABORT; t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize); ext2fs_initialize_dirent_tail(ls->fs, t); ret = DIRENT_CHANGED; } /* De-convert a dx_root block */ if (csum_size && curr_rec_len == ls->fs->blocksize - EXT2_DIR_REC_LEN(1) && offset == EXT2_DIR_REC_LEN(1) && dirent->name[0] == '.' && dirent->name[1] == '.') { curr_rec_len -= csum_size; ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent); if (ls->err) return DIRENT_ABORT; t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize); ext2fs_initialize_dirent_tail(ls->fs, t); ret = DIRENT_CHANGED; } /* * If the directory entry is used, see if we can split the * directory entry to make room for the new name. If so, * truncate it and return. */ if (dirent->inode) { min_rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(dirent)); if (curr_rec_len < (min_rec_len + rec_len)) return ret; rec_len = curr_rec_len - min_rec_len; ls->err = ext2fs_set_rec_len(ls->fs, min_rec_len, dirent); if (ls->err) return DIRENT_ABORT; next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len); next->inode = 0; ext2fs_dirent_set_name_len(next, 0); ext2fs_dirent_set_file_type(next, 0); ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next); if (ls->err) return DIRENT_ABORT; return DIRENT_CHANGED; } /* * If we get this far, then the directory entry is not used. * See if we can fit the request entry in. If so, do it. */ if (curr_rec_len < rec_len) return ret; dirent->inode = ls->inode; ext2fs_dirent_set_name_len(dirent, ls->namelen); strncpy(dirent->name, ls->name, ls->namelen); if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) ext2fs_dirent_set_file_type(dirent, ls->flags & 0x7); ls->done++; return DIRENT_ABORT|DIRENT_CHANGED; }
static int link_proc(struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data) { struct link_struct *ls = (struct link_struct *) priv_data; struct ext2_dir_entry *next; unsigned int rec_len, min_rec_len, curr_rec_len; int ret = 0; rec_len = EXT2_DIR_REC_LEN(ls->namelen); ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len); if (ls->err) return DIRENT_ABORT; /* * See if the following directory entry (if any) is unused; * if so, absorb it into this one. */ next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len); if ((offset + (int) curr_rec_len < blocksize - 8) && (next->inode == 0) && (offset + (int) curr_rec_len + (int) next->rec_len <= blocksize)) { curr_rec_len += next->rec_len; ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent); if (ls->err) return DIRENT_ABORT; ret = DIRENT_CHANGED; } /* * If the directory entry is used, see if we can split the * directory entry to make room for the new name. If so, * truncate it and return. */ if (dirent->inode) { min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF); if (curr_rec_len < (min_rec_len + rec_len)) return ret; rec_len = curr_rec_len - min_rec_len; ls->err = ext2fs_set_rec_len(ls->fs, min_rec_len, dirent); if (ls->err) return DIRENT_ABORT; next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len); next->inode = 0; next->name_len = 0; ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next); if (ls->err) return DIRENT_ABORT; return DIRENT_CHANGED; } /* * If we get this far, then the directory entry is not used. * See if we can fit the request entry in. If so, do it. */ if (curr_rec_len < rec_len) return ret; dirent->inode = ls->inode; dirent->name_len = ls->namelen; strncpy(dirent->name, ls->name, ls->namelen); if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) dirent->name_len |= (ls->flags & 0x7) << 8; ls->done++; return DIRENT_ABORT|DIRENT_CHANGED; }
/* * Create new directory block */ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, ext2_ino_t parent_ino, char **block) { struct ext2_dir_entry *dir = NULL; errcode_t retval; char *buf; int rec_len; int filetype = 0; struct ext2_dir_entry_tail *t; int csum_size = 0; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; memset(buf, 0, fs->blocksize); dir = (struct ext2_dir_entry *) buf; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) csum_size = sizeof(struct ext2_dir_entry_tail); retval = ext2fs_set_rec_len(fs, fs->blocksize - csum_size, dir); if (retval) return retval; if (dir_ino) { if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) filetype = EXT2_FT_DIR; /* * Set up entry for '.' */ dir->inode = dir_ino; ext2fs_dirent_set_name_len(dir, 1); ext2fs_dirent_set_file_type(dir, filetype); dir->name[0] = '.'; rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1); dir->rec_len = EXT2_DIR_REC_LEN(1); /* * Set up entry for '..' */ dir = (struct ext2_dir_entry *) (buf + dir->rec_len); retval = ext2fs_set_rec_len(fs, rec_len, dir); if (retval) return retval; dir->inode = parent_ino; ext2fs_dirent_set_name_len(dir, 2); ext2fs_dirent_set_file_type(dir, filetype); dir->name[0] = '.'; dir->name[1] = '.'; } if (csum_size) { t = EXT2_DIRENT_TAIL(buf, fs->blocksize); ext2fs_initialize_dirent_tail(fs, t); } *block = buf; return 0; }
static int link_proc(struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data) { struct link_struct *ls = (struct link_struct *) priv_data; struct ext2_dir_entry *next; unsigned int rec_len, min_rec_len, curr_rec_len; int ret = 0; printf("Trying to link inode %lu at offset %lu\n", ls->inode, offset); rec_len = EXT2_DIR_REC_LEN(ls->namelen); ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len); if (ls->err) { printf("Error: Problem with reading current rec len\n"); return DIRENT_ABORT; } printf("Current rec len: %d\n", curr_rec_len); /* * See if the following directory entry (if any) is unused; * if so, absorb it into this one. */ next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len); if ((offset + curr_rec_len < blocksize - 8) && (next->inode == 0) && (offset + curr_rec_len + next->rec_len <= blocksize)) { printf("Absorbing following dirent into this one\n"); curr_rec_len += next->rec_len; ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent); if (ls->err) { printf("Problem with setting rec len for new dirent\n"); return DIRENT_ABORT; } ret = DIRENT_CHANGED; } /* * If the directory entry is used, see if we can split the * directory entry to make room for the new name. If so, * truncate it and return. */ if (dirent->inode) { printf("Handling used dirent\n"); min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF); if (curr_rec_len < (min_rec_len + rec_len)) return ret; rec_len = curr_rec_len - min_rec_len; ls->err = ext2fs_set_rec_len(ls->fs, min_rec_len, dirent); if (ls->err) return DIRENT_ABORT; next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len); next->inode = 0; next->name_len = 0; ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next); if (ls->err) return DIRENT_ABORT; return DIRENT_CHANGED; } /* * If we get this far, then the directory entry is not used. * See if we can fit the request entry in. If so, do it. */ if (curr_rec_len < rec_len) return ret; printf("Setting dirent based on ls information\n"); dirent->inode = ls->inode; dirent->name_len = ls->namelen; strncpy(dirent->name, ls->name, ls->namelen); if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) dirent->name_len |= (ls->flags & 0x7) << 8; ls->done++; return DIRENT_ABORT|DIRENT_CHANGED; }