函数概览
- 位置:
fs/ext2/ialloc.c
- 目标:在
ext2
文件系统中为目录或普通文件分配新的 inode(从 inode 位图中查找空闲 inode并初始化相关结构) - 返回值:成功时返回新分配的
inode
结构指针;失败时返回一个错误指针。
函数源码
struct inode *ext2_new_inode(struct inode *dir, umode_t mode,
const struct qstr *qstr)
{
struct super_block *sb;
struct buffer_head *bitmap_bh = NULL;
struct buffer_head *bh2;
int group, i;
ino_t ino = 0;
struct inode * inode;
struct ext2_group_desc *gdp;
struct ext2_super_block *es;
struct ext2_inode_info *ei;
struct ext2_sb_info *sbi;
int err;
sb = dir->i_sb;
inode = new_inode(sb);
if (!inode)
return ERR_PTR(-ENOMEM);
ei = EXT2_I(inode);
sbi = EXT2_SB(sb);
es = sbi->s_es;
if (S_ISDIR(mode)) {
if (test_opt(sb, OLDALLOC))
group = find_group_dir(sb, dir);
else
group = find_group_orlov(sb, dir);
} else
group = find_group_other(sb, dir);
if (group == -1) {
err = -ENOSPC;
goto fail;
}
for (i = 0; i < sbi->s_groups_count; i++) {
gdp = ext2_get_group_desc(sb, group, &bh2);
if (!gdp) {
if (++group == sbi->s_groups_count)
group = 0;
continue;
}
brelse(bitmap_bh);
bitmap_bh = read_inode_bitmap(sb, group);
if (!bitmap_bh) {
err = -EIO;
goto fail;
}
ino = 0;
repeat_in_this_group:
ino = ext2_find_next_zero_bit((unsigned long *)bitmap_bh->b_data,
EXT2_INODES_PER_GROUP(sb), ino);
if (ino >= EXT2_INODES_PER_GROUP(sb)) {
/*
* Rare race: find_group_xx() decided that there were
* free inodes in this group, but by the time we tried
* to allocate one, they're all gone. This can also
* occur because the counters which find_group_orlov()
* uses are approximate. So just go and search the
* next block group.
*/
if (++group == sbi->s_groups_count)
group = 0;
continue;
}
if (ext2_set_bit_atomic(sb_bgl_lock(sbi, group),
ino, bitmap_bh->b_data)) {
/* we lost this inode */
if (++ino >= EXT2_INODES_PER_GROUP(sb)) {
/* this group is exhausted, try next group */
if (++group == sbi->s_groups_count)
group = 0;
continue;
}
/* try to find free inode in the same group */
goto repeat_in_this_group;
}
goto got;
}
/*
* Scanned all blockgroups.
*/
brelse(bitmap_bh);
err = -ENOSPC;
goto fail;
got:
mark_buffer_dirty(bitmap_bh);
if (sb->s_flags & SB_SYNCHRONOUS)
sync_dirty_buffer(bitmap_bh);
brelse(bitmap_bh);
ino += group * EXT2_INODES_PER_GROUP(sb) + 1;
if (ino < EXT2_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) {
ext2_error (sb, "ext2_new_inode",
"reserved inode or inode > inodes count - "
"block_group = %d,inode=%lu", group,
(unsigned long) ino);
err = -EIO;
goto fail;
}
percpu_counter_dec(&sbi->s_freeinodes_counter);
if (S_ISDIR(mode))
percpu_counter_inc(&sbi->s_dirs_counter);
spin_lock(sb_bgl_lock(sbi, group));
le16_add_cpu(&gdp->bg_free_inodes_count, -1);
if (S_ISDIR(mode)) {
if (sbi->s_debts[group] < 255)
sbi->s_debts[group]++;
le16_add_cpu(&gdp->bg_used_dirs_count, 1);
} else {
if (sbi->s_debts[group])
sbi->s_debts[group]--;
}
spin_unlock(sb_bgl_lock(sbi, group));
mark_buffer_dirty(bh2);
if (test_opt(sb, GRPID)) {
inode->i_mode = mode;
inode->i_uid = current_fsuid();
inode->i_gid = dir->i_gid;
} else
inode_init_owner(&init_user_ns, inode, dir, mode);
inode->i_ino = ino;
inode->i_blocks = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
memset(ei->i_data, 0, sizeof(ei->i_data));
ei->i_flags =
ext2_mask_flags(mode, EXT2_I(dir)->i_flags & EXT2_FL_INHERITED);
ei->i_faddr = 0;
ei->i_frag_no = 0;
ei->i_frag_size = 0;
ei->i_file_acl = 0;
ei->i_dir_acl = 0;
ei->i_dtime = 0;
ei->i_block_alloc_info = NULL;
ei->i_block_group = group;
ei->i_dir_start_lookup = 0;
ei->i_state = EXT2_STATE_NEW;
ext2_set_inode_flags(inode);
spin_lock(&sbi->s_next_gen_lock);
inode->i_generation = sbi->s_next_generation++;
spin_unlock(&sbi->s_next_gen_lock);
if (insert_inode_locked(inode) < 0) {
ext2_error(sb, "ext2_new_inode",
"inode number already in use - inode=%lu",
(unsigned long) ino);
err = -EIO;
goto fail;
}
err = dquot_initialize(inode);
if (err)
goto fail_drop;
err = dquot_alloc_inode(inode);
if (err)
goto fail_drop;
err = ext2_init_acl(inode, dir);
if (err)
goto fail_free_drop;
err = ext2_init_security(inode, dir, qstr);
if (err)
goto fail_free_drop;
mark_inode_dirty(inode);
ext2_debug("allocating inode %lu\n", inode->i_ino);
ext2_preread_inode(inode);
return inode;
fail_free_drop:
dquot_free_inode(inode);
fail_drop:
dquot_drop(inode);
inode->i_flags |= S_NOQUOTA;
clear_nlink(inode);
discard_new_inode(inode);
return ERR_PTR(err);
fail:
make_bad_inode(inode);
iput(inode);
return ERR_PTR(err);
}
函数参数
-
struct inode *dir
:
指向目标目录的 inode,表示新 inode 将被分配到的目录。 -
umode_t mode
:
指定新 inode 的模式(文件类型和权限)。 -
const struct qstr *qstr
:
与目录条目相关的名字字符串,用于安全性初始化等。
主要变量
-
struct super_block *sb
:
当前文件系统的超级块指针。 -
struct buffer_head *bitmap_bh
:
指向 inode 位图的缓冲区头,用于跟踪 inode 分配。 -
struct ext2_group_desc *gdp
:
表示块组描述符,用于获取块组相关的元数据。 -
struct ext2_sb_info *sbi
:
扩展超级块信息,包含了文件系统的全局信息。 -
ino_t ino
:
新分配的 inode 号。
主要逻辑
1. 初始化 inode 结构
sb = dir->i_sb:
inode = new_inode(sb);
if (!inode)
return ERR_PTR(-ENOMEM);
- 获取超级块。
- 调用
new_inode
分配新的struct inode
结构。如果失败返回错误指针。
2. 选择目标块组
if (S_ISDIR(mode)) {
if (test_opt(sb, OLDALLOC))
group = find_group_dir(sb, dir);
else
group = find_group_orlov(sb, dir);
} else
group = find_group_other(sb, dir);
- 如果是目录(
S_ISDIR(mode)
为真),采用适当的块组选择策略。- 旧分配策略(
OLDALLOC
):选择离父目录最近的块组。 - Orlov 策略:更复杂,基于空间利用率和性能。
- 旧分配策略(
- 如果不是目录,调用
find_group_other
。
3. 从位图中分配 inode
bitmap_bh = read_inode_bitmap(sb, group);
ino = ext2_find_next_zero_bit(...);
if (ext2_set_bit_atomic(...)) {
...
goto repeat_in_this_group;
}
- 调用
read_inode_bitmap
读取目标块组的 inode 位图。 - 用
ext2_find_next_zero_bit
找到第一个未分配的 inode。 - 使用
ext2_set_bit_atomic
设置位图中的 inode 为已分配,确保原子性。
如果当前块组的 inode 分配失败,继续搜索下一个块组。
4. 更新全局和块组计数器
percpu_counter_dec(&sbi->s_freeinodes_counter);
if (S_ISDIR(mode))
percpu_counter_inc(&sbi->s_dirs_counter);
le16_add_cpu(&gdp->bg_free_inodes_count, -1);
if (S_ISDIR(mode))
le16_add_cpu(&gdp->bg_used_dirs_count, 1);
- 减少全局空闲 inode 计数器。
- 如果是目录,增加目录计数器。
- 更新目标块组的空闲 inode 计数器和目录计数器。
5. 初始化新 inode
inode->i_ino = ino;
inode->i_blocks = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
...
ext2_set_inode_flags(inode);
- 设置 inode 编号、时间戳、数据块计数等。
- 调用
ext2_set_inode_flags
设置 inode 的标志。
6. 挂载安全属性和配额
err = ext2_init_acl(inode, dir);
if (err)
goto fail_free_drop;
err = ext2_init_security(inode, dir, qstr);
if (err)
goto fail_free_drop;
- 初始化访问控制列表(ACL)。
- 初始化安全属性(如 SELinux 标签)。
7. 错误处理
fail_free_drop:
dquot_free_inode(inode);
fail_drop:
dquot_drop(inode);
...
fail:
make_bad_inode(inode);
iput(inode);
return ERR_PTR(err);
- 出现错误时,释放资源并标记 inode 为坏节点。
8. 成功返回
mark_inode_dirty(inode);
return inode;
- 标记 inode 为脏(需要写回磁盘)。
- 返回已初始化的 inode。
功能总结
ext2_new_inode
的主要任务是:
- 根据分配策略选择块组。
- 在 inode 位图中找到一个空闲 inode。
- 初始化 inode 结构和相关的文件系统元数据。
- 处理配额、ACL 和安全性。
- 返回新分配的 inode 或错误代码。