summaryrefslogtreecommitdiff
path: root/fs/ocfs2/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ocfs2/dir.c')
-rw-r--r--fs/ocfs2/dir.c82
1 files changed, 65 insertions, 17 deletions
diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c
index 7799f4d16ce9..8e6b03238327 100644
--- a/fs/ocfs2/dir.c
+++ b/fs/ocfs2/dir.c
@@ -136,7 +136,7 @@ static void ocfs2_init_dir_trailer(struct inode *inode,
struct ocfs2_dir_block_trailer *trailer;
trailer = ocfs2_trailer_from_bh(bh, inode->i_sb);
- strcpy(trailer->db_signature, OCFS2_DIR_TRAILER_SIGNATURE);
+ strscpy(trailer->db_signature, OCFS2_DIR_TRAILER_SIGNATURE);
trailer->db_compat_rec_len =
cpu_to_le16(sizeof(struct ocfs2_dir_block_trailer));
trailer->db_parent_dinode = cpu_to_le64(OCFS2_I(inode)->ip_blkno);
@@ -302,8 +302,21 @@ static int ocfs2_check_dir_entry(struct inode *dir,
unsigned long offset)
{
const char *error_msg = NULL;
- const int rlen = le16_to_cpu(de->rec_len);
- const unsigned long next_offset = ((char *) de - buf) + rlen;
+ unsigned long next_offset;
+ int rlen;
+
+ if (offset > size - OCFS2_DIR_REC_LEN(1)) {
+ /* Dirent is (maybe partially) beyond the buffer
+ * boundaries so touching 'de' members is unsafe.
+ */
+ mlog(ML_ERROR, "directory entry (#%llu: offset=%lu) "
+ "too close to end or out-of-bounds",
+ (unsigned long long)OCFS2_I(dir)->ip_blkno, offset);
+ return 0;
+ }
+
+ rlen = le16_to_cpu(de->rec_len);
+ next_offset = ((char *) de - buf) + rlen;
if (unlikely(rlen < OCFS2_DIR_REC_LEN(1)))
error_msg = "rec_len is smaller than minimal";
@@ -580,7 +593,7 @@ static int ocfs2_validate_dx_root(struct super_block *sb,
mlog(ML_ERROR,
"Checksum failed for dir index root block %llu\n",
(unsigned long long)bh->b_blocknr);
- return ret;
+ goto bail;
}
if (!OCFS2_IS_VALID_DX_ROOT(dx_root)) {
@@ -588,8 +601,32 @@ static int ocfs2_validate_dx_root(struct super_block *sb,
"Dir Index Root # %llu has bad signature %.*s\n",
(unsigned long long)le64_to_cpu(dx_root->dr_blkno),
7, dx_root->dr_signature);
+ goto bail;
}
+ if (!(dx_root->dr_flags & OCFS2_DX_FLAG_INLINE)) {
+ struct ocfs2_extent_list *el = &dx_root->dr_list;
+
+ if (le16_to_cpu(el->l_count) != ocfs2_extent_recs_per_dx_root(sb)) {
+ ret = ocfs2_error(sb,
+ "Dir Index Root # %llu has invalid l_count %u (expected %u)\n",
+ (unsigned long long)le64_to_cpu(dx_root->dr_blkno),
+ le16_to_cpu(el->l_count),
+ ocfs2_extent_recs_per_dx_root(sb));
+ goto bail;
+ }
+
+ if (le16_to_cpu(el->l_next_free_rec) > le16_to_cpu(el->l_count)) {
+ ret = ocfs2_error(sb,
+ "Dir Index Root # %llu has invalid l_next_free_rec %u (l_count %u)\n",
+ (unsigned long long)le64_to_cpu(dx_root->dr_blkno),
+ le16_to_cpu(el->l_next_free_rec),
+ le16_to_cpu(el->l_count));
+ goto bail;
+ }
+ }
+
+bail:
return ret;
}
@@ -791,7 +828,7 @@ static int ocfs2_dx_dir_lookup_rec(struct inode *inode,
if (el->l_tree_depth) {
ret = ocfs2_error(inode->i_sb,
- "Inode %lu has non zero tree depth in btree tree block %llu\n",
+ "Inode %llu has non zero tree depth in btree tree block %llu\n",
inode->i_ino,
(unsigned long long)eb_bh->b_blocknr);
goto out;
@@ -810,10 +847,9 @@ static int ocfs2_dx_dir_lookup_rec(struct inode *inode,
if (!found) {
ret = ocfs2_error(inode->i_sb,
- "Inode %lu has bad extent record (%u, %u, 0) in btree\n",
- inode->i_ino,
- le32_to_cpu(rec->e_cpos),
- ocfs2_rec_clusters(el, rec));
+ "Inode %llu has no extent record for hash %u in btree (next_free_rec %u)\n",
+ inode->i_ino, major_hash,
+ le16_to_cpu(el->l_next_free_rec));
goto out;
}
@@ -2184,14 +2220,14 @@ static struct ocfs2_dir_entry *ocfs2_fill_initial_dirents(struct inode *inode,
de->name_len = 1;
de->rec_len =
cpu_to_le16(OCFS2_DIR_REC_LEN(de->name_len));
- strcpy(de->name, ".");
+ strscpy(de->name, ".");
ocfs2_set_de_type(de, S_IFDIR);
de = (struct ocfs2_dir_entry *) ((char *)de + le16_to_cpu(de->rec_len));
de->inode = cpu_to_le64(OCFS2_I(parent)->ip_blkno);
de->rec_len = cpu_to_le16(size - OCFS2_DIR_REC_LEN(1));
de->name_len = 2;
- strcpy(de->name, "..");
+ strscpy(de->name, "..");
ocfs2_set_de_type(de, S_IFDIR);
return de;
@@ -2349,7 +2385,7 @@ static int ocfs2_dx_dir_attach_index(struct ocfs2_super *osb,
dx_root = (struct ocfs2_dx_root_block *)dx_root_bh->b_data;
memset(dx_root, 0, osb->sb->s_blocksize);
- strcpy(dx_root->dr_signature, OCFS2_DX_ROOT_SIGNATURE);
+ strscpy(dx_root->dr_signature, OCFS2_DX_ROOT_SIGNATURE);
dx_root->dr_suballoc_slot = cpu_to_le16(meta_ac->ac_alloc_slot);
dx_root->dr_suballoc_loc = cpu_to_le64(suballoc_loc);
dx_root->dr_suballoc_bit = cpu_to_le16(dr_suballoc_bit);
@@ -2425,7 +2461,7 @@ static int ocfs2_dx_dir_format_cluster(struct ocfs2_super *osb,
dx_leaf = (struct ocfs2_dx_leaf *) bh->b_data;
memset(dx_leaf, 0, osb->sb->s_blocksize);
- strcpy(dx_leaf->dl_signature, OCFS2_DX_LEAF_SIGNATURE);
+ strscpy(dx_leaf->dl_signature, OCFS2_DX_LEAF_SIGNATURE);
dx_leaf->dl_fs_generation = cpu_to_le32(osb->fs_generation);
dx_leaf->dl_blkno = cpu_to_le64(bh->b_blocknr);
dx_leaf->dl_list.de_count =
@@ -2521,8 +2557,7 @@ static struct buffer_head **ocfs2_dx_dir_kmalloc_leaves(struct super_block *sb,
int num_dx_leaves = ocfs2_clusters_to_blocks(sb, 1);
struct buffer_head **dx_leaves;
- dx_leaves = kcalloc(num_dx_leaves, sizeof(struct buffer_head *),
- GFP_NOFS);
+ dx_leaves = kzalloc_objs(struct buffer_head *, num_dx_leaves, GFP_NOFS);
if (dx_leaves && ret_num_leaves)
*ret_num_leaves = num_dx_leaves;
@@ -3415,6 +3450,14 @@ static int ocfs2_find_dir_space_id(struct inode *dir, struct buffer_head *di_bh,
offset += le16_to_cpu(de->rec_len);
}
+ if (!last_de) {
+ ret = ocfs2_error(sb, "Directory entry (#%llu: size=%lld) "
+ "is unexpectedly short",
+ (unsigned long long)OCFS2_I(dir)->ip_blkno,
+ i_size_read(dir));
+ goto out;
+ }
+
/*
* We're going to require expansion of the directory - figure
* out how many blocks we'll need so that a place for the
@@ -4096,10 +4139,15 @@ static int ocfs2_expand_inline_dx_root(struct inode *dir,
}
dx_root->dr_flags &= ~OCFS2_DX_FLAG_INLINE;
- memset(&dx_root->dr_list, 0, osb->sb->s_blocksize -
- offsetof(struct ocfs2_dx_root_block, dr_list));
+
+ dx_root->dr_list.l_tree_depth = 0;
dx_root->dr_list.l_count =
cpu_to_le16(ocfs2_extent_recs_per_dx_root(osb->sb));
+ dx_root->dr_list.l_next_free_rec = 0;
+ memset(&dx_root->dr_list.l_recs, 0,
+ osb->sb->s_blocksize -
+ (offsetof(struct ocfs2_dx_root_block, dr_list) +
+ offsetof(struct ocfs2_extent_list, l_recs)));
/* This should never fail considering we start with an empty
* dx_root. */