2021SC@SDUSC
// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/ext2/ialloc.c
*
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
*
* BSD ufs-inspired inode and directory allocation by
* Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
* Big-endian to little-endian byte-swapping/bitmaps by
* David S. Miller (davem@caip.rutgers.edu), 1995
*/
#include <linux/quotaops.h>
#include <linux/sched.h>
#include <linux/backing-dev.h>
#include <linux/buffer_head.h>
#include <linux/random.h>
#include "ext2.h"
#include "xattr.h"
#include "acl.h"
/*
* ialloc.c包含inode分配和回收例程
*/
/*
*空闲的索引节点由位图管理。一个文件系统包含几个块组。每组包含1个位图块用于块,1个位图块用于索引节点,N个块用于索引节点表和数据块。
*
* 文件系统包含位于超级块之后的组描述符。每个描述符包含位图块的数量和块中空闲块的数量。
*/
/*
* 读取一个给定block_group的inode分配位图,读入超级块的位图缓存中的指定槽位。
*
*返回位图的buffer_head或NULL。
*/
static struct buffer_head *
read_inode_bitmap(struct super_block * sb, unsigned long block_group)
{
struct ext2_group_desc *desc;
struct buffer_head *bh = NULL;
/*获得block_group组的组描述符*/
desc = ext2_get_group_desc(sb, block_group, NULL);
if (!desc)
goto error_out;
/*调用设备驱动层的函数,bg_inode_bitmap字段是组内的inode位图的块号,读取这个块的数据,如果读取失败,报错误*/
bh = sb_bread(sb, le32_to_cpu(desc->bg_inode_bitmap));
if (!bh)
ext2_error(sb, "read_inode_bitmap",
"Cannot read inode bitmap - "
"block_group = %lu, inode_bitmap = %u",
block_group, le32_to_cpu(desc->bg_inode_bitmap));
error_out:
return bh;
}
/*释放inode结构体,group是释放的inode在的组号,dir标记释放的inode是不是目录*/
static void ext2_release_inode(struct super_block *sb, int group, int dir)
{
struct ext2_group_desc * desc;
struct buffer_head *bh;
/*先获得组描述符*/
desc = ext2_get_group_desc(sb, group, &bh);
if (!desc) {
ext2_error(sb, "ext2_release_inode",
"can't get descriptor for group %d", group);
return;
}
spin_lock(sb_bgl_lock(EXT2_SB(sb), group));
/*记录空闲块数目的变量bg_free_inodes_count加一*/
le16_add_cpu(&desc->bg_free_inodes_count, 1);
/*如果删除的是目录的inode,记录目录数目的变量bg_used_dirs_count还要减一*/
if (dir)
le16_add_cpu(&desc->bg_used_dirs_count, -1);
spin_unlock(sb_bgl_lock(EXT2_SB(sb), group));
percpu_counter_inc(&EXT2_SB(sb)->s_freeinodes_counter);
/*超级块的记录变量也要减一*/
if (dir)
percpu_counter_dec(&EXT2_SB(sb)->s_dirs_counter);
/*标记为脏*/
mark_buffer_dirty(bh);
}
/*
* NOTE:当我们获得inode时,我们是唯一能够访问它的人,因此不需要担心竞争条件。该inode不在散列列表中,并且无法通过文件系统访问它,因为目录条目已经在前面删除了。
*
* HOWEVER: 我们必须确保没有获得别名,这意味着在索引节点位图中标记没有被使用的索引节点之前我们必须调用“clear_inode()”来清理。否则,新创建的文件可能使用相同的inode号(虽然实际上不是相同的指针),然后我们将有两个inode共享相同的inode号和硬盘空间。
*/
void ext2_free_inode (struct inode * inode)
{
struct super_block * sb = inode->i_sb;
int is_directory;
unsigned long ino;
struct buffer_head *bitmap_bh;
unsigned long block_group;
unsigned long bit;
struct ext2_super_block * es;
/*要删除的inode编号*/
ino = inode->i_ino;
ext2_debug ("freeing inode %lu\n", ino);
/*
* Note: 我们必须在锁定超级块之前释放任何配额,因为将配额写入磁盘可能也需要锁。
*/
/*配额已经在iput()中初始化*/
dquot_free_inode(inode);
dquot_drop(inode);
/*获得ext2硬盘的结构体ext2_super_block*/
es = EXT2_SB(sb)->s_es;
/*看看要删除的inode是不是目录*/
is_directory = S_ISDIR(inode->i_mode);
/*如果要删除的inode编号小于最小的或是大于最大的,也就是说不合法,报严重错误*/
if (ino < EXT2_FIRST_INO(sb) ||
ino > le32_to_cpu(es->s_inodes_count)) {
ext2_error (sb, "ext2_free_inode",
"reserved or nonexistent inode %lu", ino);
return;
}
/*block_group得到inode所在的块组*/
block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb);
/*bit是inode在块组的inodetable内的偏移,主要是用来在inode位图上的偏移*/
bit = (ino - 1) % EXT2_INODES_PER_GROUP(sb);
/*上边刚讲过,获得块组的inode位图*/
bitmap_bh = read_inode_bitmap(sb, block_group);
if (!bitmap_bh)
return;
/*好的,现在我们可以更新inode位图了*/
if (!ext2_clear_bit_atomic(sb_bgl_lock(EXT2_SB(sb), block_group),
bit, (void *) bitmap_bh->b_data))
/*如果之前就是0,说明这个inode之前就已经被释放了,报错;否则释放inode*/
ext2_error (sb, "ext2_free_inode",
"bit already cleared for inode %lu", ino);
else
ext2_release_inode(sb, block_group, is_directory);
/*标记缓冲区为脏,如果文件系统是要求立即更新的,就立即同步*/
mark_buffer_dirty(bitmap_bh);
if (sb->s_flags & SB_SYNCHRONOUS)
sync_dirty_buffer(bitmap_bh);
brelse(bitmap_bh);
}
/*
* 在创建inode时,我们对新inode的inode块执行异步预读,以期望该inode很快会被写回。原因有二:
*
* -当创建大量文件时,异步读取将很好地合并为大型读取
* - 当写入大量的inode时,我们不需要在读取inode块时继续暂停写入。
*
* FIXME: Ext2_get_group_desc()需要简化。
*/
static void ext2_preread_inode(struct inode *inode)
{
unsigned long block_group;
unsigned long offset;
unsigned long block;
struct ext2_group_desc * gdp;
struct backing_dev_info *bdi;
/*获得inode对应的缓冲区的预读写结构体,判断是不是有读写拥挤的情况出现*/
bdi = inode_to_bdi(inode);
if (bdi_rw_congested(bdi))
return;
/*组编号;组描述符*/
block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
gdp = ext2_get_group_desc(inode->i_sb, block_group, NULL);
if (gdp == NULL)
return;
/*
* 计算出块组inode表中的偏移量
* block是得到inode的块号码
*/
offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *
EXT2_INODE_SIZE(inode->i_sb);
block = le32_to_cpu(gdp->bg_inode_table) +
(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
/*提前读取block块到缓冲区*/
sb_breadahead(inode->i_sb, block);
}
/*
* 分配inode有两种策略。如果新的索引节点是一个目录,则对具有空闲空间和低目录-引节点比率的块组进行正向搜索;如果失败,那么在空闲空间高于平均水平的组中,已经选择了拥有最少目录的组。
*
*对于其他的索引节点,从父目录\的块组中查找一个空闲的索引节点。
*/
static int find_group_dir(struct super_block *sb, struct inode *parent)
{
/*获得组的数目,avefreei代表平均每一组拥有的空闲inode数目*/
int ngroups = EXT2_SB(sb)->s_groups_count;
int avefreei = ext2_count_free_inodes(sb) / ngroups;
struct ext2_group_desc *desc, *best_desc = NULL;
int group, best_group = -1;
/*遍历每一组*/
for (group = 0; group < ngroups; group++) {
/*先获得这一组的组描述符*/
desc = ext2_get_group_desc (sb, group, NULL);
/*如果组描述符还没在内存里或者是没有空闲的inode了,就找下一个*/
if (!desc || !desc->bg_free_inodes_count)
continue;
/*如果空闲的inode小于平均值,也找下一个*/
if (le16_to_cpu(desc->bg_free_inodes_count) < avefreei)
continue;
/*如果之前还没有找到最好的组,或者当前组的空闲块数目大于之前找到的最好的组的空闲块数目,就把best_group指向新的最好的组号,best_desc指向新的最好的组描述符,best_bh指向新的缓冲区结构体*/
if (!best_desc ||
(le16_to_cpu(desc->bg_free_blocks_count) >
le16_to_cpu(best_desc->bg_free_blocks_count))) {
best_group = group;
best_desc = desc;
}
}
return best_group;
}
/*
* Orlov的目录分配器。
*
*我们总是尝试扩展第一级目录。
*
* 如果存在空闲索引节点和空闲块计数不低于平均值的块组,则返回目录计数最小的块组。否则,我们只是返回一个随机组。
*
* 其余的规则看起来是这样的:
* 将目录放入组中是可以的,除非
*它已经有太多的目录(max_dirs)或
*剩余的空闲索引节点太少(min_inodes)或
*它的空闲块太少(min_blocks)或
*它已经运行太大的债务(max_debt)。
*父组优先,如果它不满足这些条件,我们循环搜索其余。如果没有一个组看起来不错,我们就寻找一个空闲索引节点多于平均值的组(从父组开始)。
*
*
* debt在每次分配目录时递增,在0—255范围内分配索引节点时递减。
*/
#define INODE_COST 64
#define BLOCK_COST 256
/*sb是文件系统的vfs层的超级块,parent是父目录的inode*/
static int find_group_orlov(struct super_block *sb, struct inode *parent)
{
/*父目录所在的组编号*/
int parent_group = EXT2_I(parent)->i_block_group;
/*存在内存里的ext2_sb_info信息结构体*/
struct ext2_sb_info *sbi = EXT2_SB(sb);
/*ext2文件系统的硬盘存储的超级块*/
struct ext2_super_block *es = sbi->s_es;
/*拥有块组的数目*/
int ngroups = sbi->s_groups_count;
/*每一个组拥有的inode数目*/
int inodes_per_group = EXT2_INODES_PER_GROUP(sb);
int freei;
int avefreei;
int free_blocks;
int avefreeb;
int blocks_per_dir;
int ndirs;
int max_debt, max_dirs, min_blocks, min_inodes;
int group = -1, i;
struct ext2_group_desc *desc;
/*计算平均每一个组的空闲inode数目*/
freei = percpu_counter_read_positive(&sbi->s_freeinodes_counter);
avefreei = freei / ngroups;
/*文件系统的所有的目录数目*/
free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter);
avefreeb = free_blocks / ngroups;
ndirs = percpu_counter_read_positive(&sbi->s_dirs_counter);
/*如果是在文件系统的第一级创建目录,尽量分散开来*/
if ((parent == d_inode(sb->s_root)) ||
(EXT2_I(parent)->i_flags & EXT2_TOPDIR_FL)) {
struct ext2_group_desc *best_desc = NULL;
int best_ndir = inodes_per_group;
int best_group = -1;
/*随机选一个组作为遍历的开始点*/
group = prandom_u32();
parent_group = (unsigned)group % ngroups;
for (i = 0; i < ngroups; i++) {
group = (parent_group + i) % ngroups;
desc = ext2_get_group_desc (sb, group, NULL);
/*如果组描述符为NULL或者空闲inode为0,找下一个*/
if (!desc || !desc->bg_free_inodes_count)
continue;
/*如果使用的目录太多也不行*/
if (le16_to_cpu(desc->bg_used_dirs_count) >= best_ndir)
continue;
/*空闲的inode数目小于平均值*/
if (le16_to_cpu(desc->bg_free_inodes_count) < avefreei)
continue;
/*空闲的块数目小于平均值也不行*/
if (le16_to_cpu(desc->bg_free_blocks_count) < avefreeb)
continue;
/*best的四个变量指向找到的最优组号*/
best_group = group;
best_ndir = le16_to_cpu(desc->bg_used_dirs_count);
best_desc = desc;
}
/*如果找到,就使desc,bh,group指向找到的最优组,跳转到found*/
if (best_group >= 0) {
desc = best_desc;
group = best_group;
goto found;
}
goto fallback;
}
/*如果文件系统没有一个目录,这不太可能.....*/
if (ndirs == 0)
ndirs = 1; /* percpu_counters are approximate... */
/*平均每一个目录的负载的块数目*/
blocks_per_dir = (le32_to_cpu(es->s_blocks_count)-free_blocks) / ndirs;
/*粗略计算出每一个块组最多的目录数目,最少的inode数目和最少的block数目*/
max_dirs = ndirs / ngroups + inodes_per_group / 16;
min_inodes = avefreei - inodes_per_group / 4;
min_blocks = avefreeb - EXT2_BLOCKS_PER_GROUP(sb) / 4;
/*BLOCK_COST是一个目录对应的块的开销,每一个组的最多的块数目除以开销就是最多的块数目*/
max_debt = EXT2_BLOCKS_PER_GROUP(sb) / max(blocks_per_dir, BLOCK_COST);
/*将max_debt字段和inode的块数目比较,如果大的话,就设置为inode的块数除以inode的开销*/
if (max_debt * INODE_COST > inodes_per_group)
max_debt = inodes_per_group / INODE_COST;
if (max_debt > 255)
max_debt = 255;
if (max_debt == 0)
max_debt = 1;
for (i = 0; i < ngroups; i++) {
group = (parent_group + i) % ngroups;
desc = ext2_get_group_desc (sb, group, NULL);
/*如果组描述符为NULL或者是空闲inode为0,下一位*/
if (!desc || !desc->bg_free_inodes_count)
continue;
/*如果当前组的负载过大,下一位*/
if (sbi->s_debts[group] >= max_debt)
continue;
/*如果使用的目录过多,下一位*/
if (le16_to_cpu(desc->bg_used_dirs_count) >= max_dirs)
continue;
/*如果空闲inode少,下一位*/
if (le16_to_cpu(desc->bg_free_inodes_count) < min_inodes)
continue;
/*如果空闲block小于最小值,下一位*/
if (le16_to_cpu(desc->bg_free_blocks_count) < min_blocks)
continue;
goto found;
}
fallback:
/*运行到这里,说明没有找到想要的组,就降低标准*/
for (i = 0; i < ngroups; i++) {
group = (parent_group + i) % ngroups;
desc = ext2_get_group_desc (sb, group, NULL);
/*检查当前遍历到的组是不是合法*/
if (!desc || !desc->bg_free_inodes_count)
continue;
/*只要求这个组的空闲inode比平均值大就可以,如果符合,也可以接受*/
if (le16_to_cpu(desc->bg_free_inodes_count) >= avefreei)
goto found;
}
/*还没找到,那只能随便找一个只要有空闲inode空间就可以,把avefreei变成0,再去寻找*/
if (avefreei) {
/*
* free-inodes计数器是近似的,对于非常小的文件系统,上面的测试可能无法找到任何块组
*/
avefreei = 0;
goto fallback;
}
return -1;
found:
return group;
}
/*为一个目录找一个自目录项的节点对应的组,相对比较简单的查找策略,没有负载这么一说,只要有空间,就放*/
static int find_group_other(struct super_block *sb, struct inode *parent)
{
int parent_group = EXT2_I(parent)->i_block_group;
int ngroups = EXT2_SB(sb)->s_groups_count;
struct ext2_group_desc *desc;
int group, i;
/*
* 尝试将索引节点放在其父目录中
*/
group = parent_group;
desc = ext2_get_group_desc (sb, group, NULL);
/*如果父节点的组有空闲的inode和block就可以*/
if (desc && le16_to_cpu(desc->bg_free_inodes_count) &&
le16_to_cpu(desc->bg_free_blocks_count))
goto found;
/*
*我们将把这个节点放在与其父节点不同的块组中。我们希望将公共目录中的所有文件都放在同一个块组中。但是,我们希望与父目录共享一个块组的不同目录中的文件位于不同的块组中。
*
*因此,将目录的i_ino添加到散列表的起始点中。
*/
group = (group + parent->i_ino) % ngroups;
/*
* 使用二次散列查找具有空闲索引节点和一些空闲块的组
*/
for (i = 1; i < ngroups; i <<= 1) {
group += i;
if (group >= ngroups)
group -= ngroups;
desc = ext2_get_group_desc (sb, group, NULL);
if (desc && le16_to_cpu(desc->bg_free_inodes_count) &&
le16_to_cpu(desc->bg_free_blocks_count))
goto found;
}
/*
* 查找失败:尝试线性搜索一个空闲的索引节点,即使该组没有空闲块。
*/
group = parent_group;
for (i = 0; i < ngroups; i++) {
if (++group >= ngroups)
group = 0;
desc = ext2_get_group_desc (sb, group, NULL);
if (desc && le16_to_cpu(desc->bg_free_inodes_count))
goto found;
}
return -1;
found:
return group;
}
/*这个函数新创建一个inode,dir参数是父目录的inode,mode是文件创建的模式*/
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;
/*new_inode定义在inode.c文件,这个函数做了分配一个inode的底层空间操作*/
inode = new_inode(sb);
if (!inode)
return ERR_PTR(-ENOMEM);
/*获得它的ext2_inode_info,ext2_sb_info和ext2_super_block*/
ei = EXT2_I(inode);
sbi = EXT2_SB(sb);
es = sbi->s_es;
/*判断你要创建的inode是什么*/
if (S_ISDIR(mode)) {
/*要创建一个目录,先查看超级块,看看文件系统的挂载选项的 OLDALLOC位,如果是1,说明使用老旧的分配策略*/
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);
/*获得这个组的inode位图*/
bitmap_bh = read_inode_bitmap(sb, group);
if (!bitmap_bh) {
err = -EIO;
goto fail;
}
ino = 0;
repeat_in_this_group:
/*在这个组内的inode位图寻找为0的位*/
ino = ext2_find_next_zero_bit((unsigned long *)bitmap_bh->b_data,
EXT2_INODES_PER_GROUP(sb), ino);
/*如果没找到,说明这个组没有空闲的inode,那就找下一个*/
if (ino >= EXT2_INODES_PER_GROUP(sb)) {
/*
* Rare race: find_group_xx()决定在这个组中有空闲的 索引节点,但是当我们试图分配一个时,它们都没有了。 这也可能发生,因为find_group_orlov()使用的计数器 是近似的。所以去搜索下一个街区组。
*/
if (++group == sbi->s_groups_count)
group = 0;
continue;
}
/*标记这个位已经为1,代表这个inode已经被使用啦,返回值是当前的值*/
if (ext2_set_bit_atomic(sb_bgl_lock(sbi, group),
ino, bitmap_bh->b_data)) {
/*我们失去了这个节点*/
if (++ino >= EXT2_INODES_PER_GROUP(sb)) {
/*这个组的inode已经被用完了,就尝试下一个组 */
if (++group == sbi->s_groups_count)
group = 0;
continue;
}
/*尝试在同一组中找到空闲的索引节点*/
goto repeat_in_this_group;
}
goto got;
}
/*
*扫描所有块组
*/
brelse(bitmap_bh);
err = -ENOSPC;
goto fail;
got:
/*找到我们想要的块组了,因为我们修改了inode位图的值,所以标记脏了*/
mark_buffer_dirty(bitmap_bh);
/*如果文件系统要求修改立即同步,就同步*/
if (sb->s_flags & SB_SYNCHRONOUS)
sync_dirty_buffer(bitmap_bh);
brelse(bitmap_bh);
/*原来的ino是位图内的位偏移位置,现在的到的是inode的编号*/
ino += group * EXT2_INODES_PER_GROUP(sb) + 1;
/*如果inode编号大于inode数目,小于最小号码,报错误*/
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);
/*如果是目录,记录目录+1*/
if (S_ISDIR(mode))
percpu_counter_inc(&sbi->s_dirs_counter);
spin_lock(sb_bgl_lock(sbi, group));
/*修改组描述符内的空闲inode数目-1*/
le16_add_cpu(&gdp->bg_free_inodes_count, -1);
/*如果是目录,还要修改ext2_sb_info内的负载记录,多一个目录++,多一个非目录文件--,还有目录使用数量也要更新*/
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));
/*超级块已经脏了,bh2存放找到的组描述符*/
mark_buffer_dirty(bh2);
/*如果文件系统有组id,赋值,如果文件的打开模式有S_ISGID也要赋值*/
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);
/*依次给i_mode的inode编号,拥有的块数目,修改时间等字段赋值*/
inode->i_ino = ino;
inode->i_blocks = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
/*修改这个新创建的inode对应的内存信息结构体ext2_inode_info的各项值*/
memset(ei->i_data, 0, sizeof(ei->i_data));
/*赋值父目录的flag*/
ei->i_flags =
ext2_mask_flags(mode, EXT2_I(dir)->i_flags & EXT2_FL_INHERITED);
/*各个字段初始化为0*/
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_inode_info结构体里的flag设置inode结构体的flag*/
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);
/*新创建的inode要插入到hash列表里*/
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;
/*acl控制的初始化*/
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:
/*预额分配失败,把分配的inode释放*/
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);
}
/*ext2的计算空闲的inode的函数*/
unsigned long ext2_count_free_inodes (struct super_block * sb)
{
struct ext2_group_desc *desc;
unsigned long desc_count = 0;
int i;
/*如果配置了EXT2FS_DEBUG宏,就采用下边的操作,否则就采用另一套操作*/
#ifdef EXT2FS_DEBUG
struct ext2_super_block *es;
unsigned long bitmap_count = 0;
struct buffer_head *bitmap_bh = NULL;
/*获得ext2硬盘上存储的ext2_super_block结构体超级块*/
es = EXT2_SB(sb)->s_es;
for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) {
unsigned x;
/*获得当前遍历到的组的组描述符*/
desc = ext2_get_group_desc (sb, i, NULL);
if (!desc)
continue;
/*统计加上当前组的空闲inode数目*/
desc_count += le16_to_cpu(desc->bg_free_inodes_count);
brelse(bitmap_bh);
/*获得这个组的位图*/
bitmap_bh = read_inode_bitmap(sb, i);
if (!bitmap_bh)
continue;
/*在这个位图上查找为0的位数目*/
x = ext2_count_free(bitmap_bh, EXT2_INODES_PER_GROUP(sb) / 8);
printk("group %d: stored = %d, counted = %u\n",
i, le16_to_cpu(desc->bg_free_inodes_count), x);
/*统计加上位图上获得的空闲inode数目*/
bitmap_count += x;
}
brelse(bitmap_bh);
/*即打印记录的空闲inode数目,也打印inode位图上获得的空闲inode数目,确保万无一失吧...*/
printk("ext2_count_free_inodes: stored = %lu, computed = %lu, %lu\n",
(unsigned long)
percpu_counter_read(&EXT2_SB(sb)->s_freeinodes_counter),
desc_count, bitmap_count);
/*最后返回的是组描述符上记录相加得到的*/
return desc_count;
#else
for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) {
desc = ext2_get_group_desc (sb, i, NULL);
if (!desc)
continue;
desc_count += le16_to_cpu(desc->bg_free_inodes_count);
}
return desc_count;
#endif
}
/*在挂载时调用,超级块被锁定,这个函数的作用是统计ext2文件系统的目录数目 */
unsigned long ext2_count_dirs (struct super_block * sb)
{
unsigned long count = 0;
int i;
for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) {
/*获得当前的组的组描述符*/
struct ext2_group_desc *gdp = ext2_get_group_desc (sb, i, NULL);
if (!gdp)
continue;
/*统计每一个组的目录数目,加起来*/
count += le16_to_cpu(gdp->bg_used_dirs_count);
}
return count;
}