今天,我们来看ext2文件系统源代码里边最大的一个文件inode.c。
在讲这个文件之前,我需要仙鹤大家说一个概念,就是间接块的概念,ext2文件系统在块大小为4096的时候可以支持的最大文件大小是4T大小,而ext2_inode结构体,就是ext2文件系统的inode在硬盘上的存储格式,__le32 i_block[EXT2_N_BLOCKS];EXT2_N_BLOCKS是15,如果每一个指针指向一个块,顶多也就是15*4K就是60K字节,这是肯定不够的,ext2是怎么实现的呢?ext2有一个间接块的概念,在15个指针里有0-11是直接指针,12是一级间接指针,13是二级间接指针,14是三级间接指针。直接指针就是一个指针指向的block就是文件的内容,就是一个指针指向4K的块。一级间接指针就是这个指针指向一个block,这个block内的不是文件的内容,而是4096/4=1024个指针,每一个指针指向一个block,这个block内就是文件的内容。二级间接块就是指针指向的block内部是指针,每一个指针指向一个block,这个block内部是1024个指针,每一个指针指向一个block,这个block内的数据就是文件的内容,三级间接块大家应该就明白了。
所以,直接寻址指针大小是1*4K=4K,一级间接寻址指针是(4096/4)*4K=4M,二级间接指针是(4096/4)*(4096/4)*4K=4G,所以三级间接指针就是4T,大家应该明白了吧。
这个文件有1300+行,看起来真的很费力,但是万丈高楼平地起,不去想多么困难,直接开始吧!

/* 作者信息*  linux/fs/ext2/inode.c* 又是这个大神,我记住了,Remy Card,如果将来有机会,我一定要去拜访下这个人* Copyright (C) 1992, 1993, 1994, 1995* Remy Card (card@masi.ibp.fr)* Laboratoire MASI - Institut Blaise Pascal* Universite Pierre et Marie Curie (Paris VI)**  from**  linux/fs/minix/inode.c**  Copyright (C) 1991, 1992  Linus Torvalds**  Goal-directed block allocation by Stephen Tweedie*  (sct@dcs.ed.ac.uk), 1993, 1998*  Big-endian to little-endian byte-swapping/bitmaps by*        David S. Miller (davem@caip.rutgers.edu), 1995*  64-bit file support on 64-bit platforms by Jakub Jelinek*  (jj@sunsite.ms.mff.cuni.cz)**  Assorted race fixes, rewrite of ext2_get_block() by Al Viro, 2000*/#include <linux/smp_lock.h>
#include <linux/time.h>
#include <linux/highuid.h>
#include <linux/pagemap.h>
#include <linux/quotaops.h>
#include <linux/module.h>
#include <linux/writeback.h>
#include <linux/buffer_head.h>
#include <linux/mpage.h>
#include "ext2.h"
#include "acl.h"
#include "xip.h"MODULE_AUTHOR("Remy Card and others");
MODULE_DESCRIPTION("Second Extended Filesystem");
MODULE_LICENSE("GPL");static int ext2_update_inode(struct inode * inode, int do_sync);/*检测一个inode是不是一个快速符号链接,快速符号链接是一个ext2文件系统的特性,要求符号链接指向的文件名是一个短的路径名,小于等于60字节,就把它的路径名放在一个inode里,而不用通过一个数据块来进行转换*/
static inline int ext2_inode_is_fast_symlink(struct inode *inode)
{/*先获得inode对应的内存的信息结构体ext2_inode_info,看看有没有acl控制*/int ea_blocks = EXT2_I(inode)->i_file_acl ?/*s_blocksize是文件的块大小,右移9位就是占有的磁道数目*/(inode->i_sb->s_blocksize >> 9) : 0;/*判断i_mode,还要求这个符号链接不占用一个块*/return (S_ISLNK(inode->i_mode) &&inode->i_blocks - ea_blocks == 0);
}/*在iput()的时候调用,inode可能是坏的,所以我们需要检查*/
void ext2_put_inode(struct inode *inode)
{/*只要不是坏的,释放掉inode申请的预分配块空间*/if (!is_bad_inode(inode))ext2_discard_prealloc(inode);
}/*在最后一个iput()的时候调用,当引用计数为0的时候*/
void ext2_delete_inode (struct inode * inode)
{/*内存管理系统的函数,把inode对应的页删除掉*/truncate_inode_pages(&inode->i_data, 0);/*如果坏页,就跳转到no_delete*/if (is_bad_inode(inode))goto no_delete;/*删除时间登记*/EXT2_I(inode)->i_dtime   = get_seconds();/*只要修改,就标记脏*/mark_inode_dirty(inode);/*更新*/ext2_update_inode(inode, inode_needs_sync(inode));/*删除后大小为0*/inode->i_size = 0;/*如果占有块*/if (inode->i_blocks)/*释放那些块*/ext2_truncate (inode);/*释放inode,之前讲过的*/ext2_free_inode (inode);return;
no_delete:/*清理inode*/clear_inode(inode);
}
/*把预分配的块都释放掉*/
void ext2_discard_prealloc (struct inode * inode)
{
/*如果配置了EXT2_PREALLOCATE才会有预分配*/
#ifdef EXT2_PREALLOCATE/*ext2_inode_info结构体记录了预分配的信息*/struct ext2_inode_info *ei = EXT2_I(inode);/*对于ext2_inode_info的书写要上写锁*/write_lock(&ei->i_meta_lock);/*如果有预分配菜释放,没有就不释放了*/if (ei->i_prealloc_count) {/*预分配的块数目*/unsigned short total = ei->i_prealloc_count;/*预分配的开始块号*/unsigned long block = ei->i_prealloc_block;/*修改为0,,标记没有预分配块啦*/ei->i_prealloc_count = 0;ei->i_prealloc_block = 0;write_unlock(&ei->i_meta_lock);/*之前讲过的函数,释放预分配的从block开始一共total个块*/ext2_free_blocks (inode, block, total);return;} elsewrite_unlock(&ei->i_meta_lock);
#endif
}
/*ext2为一个数据块分配空间,goal是期望预分配的块号*/
static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err)
{
#ifdef EXT2FS_DEBUGstatic unsigned long alloc_hits, alloc_attempts;
#endifunsigned long result;/*如果配置了预分配宏*/
#ifdef EXT2_PREALLOCATE/*先获得要分配块的文件的inode的内存里的结构体ext2_inode_info*/struct ext2_inode_info *ei = EXT2_I(inode);/*对ext2_inode_info结构体书写之前必须上锁*/write_lock(&ei->i_meta_lock);/*如果之前已经预分配了块,并且还没用完,并且传入的参数goal就是预分配的块号或者之前的块号*/if (ei->i_prealloc_count &&(goal == ei->i_prealloc_block || goal + 1 == ei->i_prealloc_block)){/*result就是要返回的块号,然后ext2_inode_info结构体指示的i_prealloc_block字段,也就是预分配的块的第一块的块号要加一,i_prealloc_count字段,也就是可用的块数减一*/result = ei->i_prealloc_block++;ei->i_prealloc_count--;/*写完以后要解锁*/write_unlock(&ei->i_meta_lock);ext2_debug ("preallocation hit (%lu/%lu).\n",++alloc_hits, ++alloc_attempts);} else {/*运行到这里说明预分配块已经不足了或者是和goal参数期望的不一致,我们就按用户期望的来分配*/write_unlock(&ei->i_meta_lock);/*既然用户不想使用预分配的块,我们就把预分配的块全部释放掉*/ext2_discard_prealloc (inode);ext2_debug ("preallocation miss (%lu/%lu).\n",alloc_hits, ++alloc_attempts);/*如果这是一个普通文件的话,在用户的goal参数附近预分配块*/if (S_ISREG(inode->i_mode))result = ext2_new_block (inode, goal, &ei->i_prealloc_count,&ei->i_prealloc_block, err);else/*ext2的策略,其他的文件不用预分配,否则,就在goal处分配一个块*/result = ext2_new_block(inode, goal, NULL, NULL, err);}
#else/*如果没有定义预分配宏,直接在goal处分配一个块*/result = ext2_new_block (inode, goal, 0, 0, err);
#endif/*最后返回得到的块号*/return result;
}
/*间接指向的结构体,用于ext2的间接块的实现*/
typedef struct {/*指针,对应映射表中对应下标处的指针*/__le32  *p;/*指针的值,对应就是物理块号,如果为0就说明还没有映射下一个块*/__le32   key;/*当前间接块的内存缓冲,如果是直接映射,这个指针是NULL*/struct buffer_head *bh;
} Indirect;
/*增加链接,把间接指针指向这个内存缓冲区,v就是15个指针之一的指针的值*/
static inline void add_chain(Indirect *p, struct buffer_head *bh, __le32 *v)
{p->key = *(p->p = v);p->bh = bh;
}
/*给定两个indirect结构体,检查这两个结构体是不是构成一条有效的映射链,每从设备上读入一个块的时候都要进行检查,因为读入是费时间的操作,可能已经有块的内容被改变了,可能会有其他的程序吧这个文件的后边的块删除掉,每一项都是检查映射表里的内容是不是更改了*/
static inline int verify_chain(Indirect *from, Indirect *to)
{/*从from开始,只要from->key == *from->p,就是对的,对的就下一位*/while (from <= to && from->key == *from->p)from++;/*检查结束,如果from > to,说明都正确,返回非零值*/return (from > to);
}/**ext2文件系统的根据文件的一个数据块号返回这个数据块在ext2内的存储地址,因为ext2的数据块存储是12个直接指针加上三个间接指针的,所以需要一个长度为4的数组作为返回值。inode是我们要查的block所在的文件,i_block是文件内的逻辑块号,offsets是长度为四的数组,返回值是路径的长度,如果这个block在直接块指向的块上,返回长度就是0,数组后三个值就没有意义,数组的第一个值就是块所在的直接指针的偏移值加一。如果这个block在二级指针指向的间接块上,那么返回值就是2,offset[2]就是指向第三层数据块的偏移。如果要寻找的block后边是一个间接块的话,就把设为非零值*/
static int ext2_block_to_path(struct inode *inode,long i_block, int offsets[4], int *boundary)
{/*每一个块可以存放的指针的数目*/int ptrs = EXT2_ADDR_PER_BLOCK(inode->i_sb);/*每一个块可以存放的指针的数目的二进制位数目*/int ptrs_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);/*direct_blocks是直接块的数目*/const long direct_blocks = EXT2_NDIR_BLOCKS,/*一级间接块指针可以指向的块的数目*/indirect_blocks = ptrs,/*二级间接块指针可以指向的block的数目*/double_blocks = (1 << (ptrs_bits * 2));int n = 0;int final = 0;/*如果块号小于0,这肯定是有问题的*/if (i_block < 0) {ext2_warning (inode->i_sb, "ext2_block_to_path", "block < 0");/*如果块号码小于直接指针的数目12,就说明这个数据块在直接指针指向的块里边*/} else if (i_block < direct_blocks) {/*offset[0]就是对应的偏移,12个直接指针的第i_block个就指向我们得到的这个数据块,final指向对应的最后一个块号*/offsets[n++] = i_block;final = direct_blocks;/*如果块号大于12但是小于一级间接块可以指向的块的数目1024,就说明这个块在一级指针指向的数据块里*/} else if ( (i_block -= direct_blocks) < indirect_blocks) {/*offset[0]就是第一个进入的指针,就是一级间接指针*/offsets[n++] = EXT2_IND_BLOCK;/*offset[1]就是第二个进入的指针,就是减去之前的额2个直接指针的i_block*/offsets[n++] = i_block;/*final就是所在的间接块内最后一块*/final = ptrs;/*如果i_block大于一级间接块指向的块数目,小于二级间接块指向的块数目,说明在二级间接指针指向的块里边*/} else if ((i_block -= indirect_blocks) < double_blocks) {/*第一个进入的指针,二级间接指针*/offsets[n++] = EXT2_DIND_BLOCK;/*路径中第二个进入的指针,右移12位就是二级间接指针指向的block里的对应的间接指针的排名*/offsets[n++] = i_block >> ptrs_bits;/*第三个要进入的路径指针,最后的块内的偏移排名*/offsets[n++] = i_block & (ptrs - 1);/*块内有1024个,最后一个就是1024*/final = ptrs;/*如果块号大于二级指针指向的块的数目,小于三级指针指向的块的数目*/} else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {/*第一个要进入的指针,三级指针*/offsets[n++] = EXT2_TIND_BLOCK;/*三级指针指向的块内排名*/offsets[n++] = i_block >> (ptrs_bits * 2);/*三级指针指向的块内指针的下一个块内排名*/offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1);/*最后的指向的块内排名*/offsets[n++] = i_block & (ptrs - 1);/*块内有1024个,最后一个就是1024*/final = ptrs;} else {ext2_warning (inode->i_sb, "ext2_block_to_path", "block > big");}/*如果要寻找的块是在块内的最后一个指针指向的,*boundary的值就是1*/if (boundary)*boundary = (i_block & (ptrs - 1)) == (final - 1);return n;
}/** ext2_get_branch函数,根据传入的路径偏移值获得对应的chain结构体指向对应块的数据,offset是传入的参数,offset内是每一级的在块内的偏移排名,depth是路径的层数。所以这个得到的映射链就像一棵树上的一个枝干一样,也就是我们这个函数的名字的由来,自此,ext2_get_branch函数和上边的ext2_block_to_path函数完成了从文件内的块号到物理块号的映射*/
static Indirect *ext2_get_branch(struct inode *inode,int depth,int *offsets,Indirect chain[4],int *err)
{/*对应的文件系统超级块*/struct super_block *sb = inode->i_sb;Indirect *p = chain;struct buffer_head *bh;*err = 0;/*使得间接块结构体指向路径的第一级,EXT2_I(inode)获得inode的内存里的信息结构体ext2_inode_info,然后从i_data获得15个指针的值,加上offset就得到了偏移*/add_chain (chain, NULL, EXT2_I(inode)->i_data + *offsets);/*如果key=0,说明这个指针没有指向一个块,说明逻辑块的指针还没有和数据块产生映射关系*/if (!p->key)goto no_block;/*一层一层解决*/while (--depth) {/*驱动层的读写函数读取上一层找到的间接块的数据*/bh = sb_bread(sb, le32_to_cpu(p->key));if (!bh)goto failure;read_lock(&EXT2_I(inode)->i_meta_lock);/*检验建立的这个间接结构体有没有问题*/if (!verify_chain(chain, p))goto changed;/*建立下一层的chain结构体,(__le32*)bh->b_data + *++offsets的意思是,上边得到bh缓冲区是间接块的缓冲区,加上偏移就是下一级的入口*/add_chain(++p, bh, (__le32*)bh->b_data + *++offsets);read_unlock(&EXT2_I(inode)->i_meta_lock);/*如果下一级没有块了*/if (!p->key)goto no_block;}return NULL;changed:/*说明在我们建立chain的时候,这个chain被修改了,再来一遍*/read_unlock(&EXT2_I(inode)->i_meta_lock);brelse(bh);*err = -EAGAIN;goto no_block;
failure:*err = -EIO;
no_block:return p;
}/** 为分配找一个有充足位置的地点,参数ind是希望的返回值是要分配的地点。分配的规则是,首先寻找这个块所在的指针块上面之前的近的块,如果没找到,就去参数ind所在的间接块内找,如果还没有找到,就去inode所在的磁道块组内找一个。*/
static unsigned long ext2_find_near(struct inode *inode, Indirect *ind)
{/*由inode获得内存里的ext2_inode_info结构体*/struct ext2_inode_info *ei = EXT2_I(inode);/*首先把start指向当前所在的映射块的起点*/__le32 *start = ind->bh ? (__le32 *) ind->bh->b_data : ei->i_data;__le32 *p;unsigned long bg_start;unsigned long colour;/* 遍历从start到ind,就是映射断裂的地方,找映射空洞后边的第一个块 */for (p = ind->p - 1; p >= start; p--)if (*p)return le32_to_cpu(*p);/* 没有,就找一个间接块*/if (ind->bh)return ind->bh->b_blocknr;/*去磁道所在的组找一个块,bg_start得到的是文件所在的组开始块号*/bg_start = (ei->i_block_group * EXT2_BLOCKS_PER_GROUP(inode->i_sb)) +le32_to_cpu(EXT2_SB(inode->i_sb)->s_es->s_first_data_block);/*colour是组内的偏移,根据进程用户的pid来随机,是为了防止不同的用户发生冲突*/colour = (current->pid % 16) *(EXT2_BLOCKS_PER_GROUP(inode->i_sb) / 16);return bg_start + colour;
}/** 寻找分配地点,inode是要分配的块所属的inode,block是文件内的逻辑块号,chain是间接块数组,partial是chain的最后一项,goal用来返回建议的块号,成功的话,返回0,失败返回错误码*/
static inline int ext2_find_goal(struct inode *inode,long block,Indirect chain[4],Indirect *partial,unsigned long *goal)
{/*块分配里常用的ext2_inode_info结构体*/struct ext2_inode_info *ei = EXT2_I(inode);write_lock(&ei->i_meta_lock);/*i_next_alloc_block代表这个文件的下一次要分配的的文件内逻辑块号,i_next_alloc_goal是下一次可以分配的物理块号,一般情况下,文件内的逻辑块号是一块一块的顺序分配的*/if ((block == ei->i_next_alloc_block + 1) && ei->i_next_alloc_goal) {ei->i_next_alloc_block++;ei->i_next_alloc_goal++;} /*说明块分配是跳跃的,新的逻辑块号与原先的最后一个逻辑块号之间有空洞*/if (verify_chain(chain, partial)) {/* 要求顺序的下一个块,那就返回希望分配的块*/if (block == ei->i_next_alloc_block)*goal = ei->i_next_alloc_goal;/*如果goal是0,说明用户没有希望分配的点,那我们就自己找一个距离映射链断裂的地方近一点的点*/if (!*goal)*goal = ext2_find_near(inode, partial);write_unlock(&ei->i_meta_lock);return 0;}write_unlock(&ei->i_meta_lock);return -EAGAIN;
}/** 为映射链分配空间,并初始化这个块的地址chain。inode是文件的主人,num是要建立的chain的层数,表示还有几层需要建立,offset是要存放的偏移地址的后一个,branch是要存放的chain的地址,就是之前的映射断裂的地方。这个函数分配num个块,除了最后一个都会初始化为0,然后同步, */
static int ext2_alloc_branch(struct inode *inode,int num,unsigned long goal,int *offsets,Indirect *branch)
{/*块大小*/int blocksize = inode->i_sb->s_blocksize;int n = 0;int err;int i;/*上边刚讲过,在goal附近分配一个块*/int parent = ext2_alloc_block(inode, goal, &err);/*建立开始的第一个的索引key,块号*/branch[0].key = cpu_to_le32(parent);/*逐层建立对应的chain*/if (parent) for (n = 1; n < num; n++) {struct buffer_head *bh;/* 分配下一块 */int nr = ext2_alloc_block(inode, parent, &err);if (!nr)break;/*下一层的块号*/branch[n].key = cpu_to_le32(nr);/*得到父节点的块缓冲区,初始化为0,设置指针指向新的一个把父写入硬盘 */bh = sb_getblk(inode->i_sb, parent);if (!bh) {err = -EIO;break;}/*写缓冲区之前都要锁住*/lock_buffer(bh);/*初始化缓冲区为0*/memset(bh->b_data, 0, blocksize);/*当前路径链的间接块缓冲区,p执行间接块上的offset对应的偏移地址*/branch[n].bh = bh;branch[n].p = (__le32 *) bh->b_data + offsets[n];*branch[n].p = branch[n].key;/*更新缓冲区*/set_buffer_uptodate(bh);unlock_buffer(bh);/*标记为脏*/mark_buffer_dirty_inode(bh, inode);/* 目录的同步*/if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode))sync_dirty_buffer(bh);parent = nr;}/*如果分配够了,就返回*/if (n == num)return 0;/* 分配失败,就释放我们之前分配的块,bforget函数是内存管理系统的释放buffer的函数*/for (i = 1; i < n; i++)bforget(branch[i].bh);/*然后释放这间接块所占用的数据块*/for (i = 0; i < n; i++)ext2_free_blocks(inode, le32_to_cpu(branch[i].key), 1);return err;
}/** 粘结分配的分支,inode是属于的文件inode,block是逻辑块号,chain是间接块链,where是迷失的节点位置,num是增加的块数目*/
static inline int ext2_splice_branch(struct inode *inode,long block,Indirect chain[4],Indirect *where,int num)
{struct ext2_inode_info *ei = EXT2_I(inode);int i;/* 检查我们要粘结的位置有没有变*/write_lock(&ei->i_meta_lock);if (!verify_chain(chain, where-1) || *where->p)goto changed;/*就是这里,断的那一点的p指向key,i_next_alloc_block是下一次分配的文件内的逻辑号,i_next_alloc_goal是希望分配的物理块号*/*where->p = where->key;ei->i_next_alloc_block = block;ei->i_next_alloc_goal = le32_to_cpu(where[num-1].key);write_unlock(&ei->i_meta_lock);/*主要工作ok,做一些小工作,修改时间*/inode->i_ctime = CURRENT_TIME_SEC;/* 是间接块?*/if (where->bh)mark_buffer_dirty_inode(where->bh, inode);mark_inode_dirty(inode);return 0;changed:/*释放掉*/write_unlock(&ei->i_meta_lock);for (i = 1; i < num; i++)bforget(where[i].bh);for (i = 0; i < num; i++)ext2_free_blocks(inode, le32_to_cpu(where[i].key), 1);return -EAGAIN;
}/* 根据文件的逻辑块号得到文件的物理块号,iblock是块在文件内的逻辑块号,参数create标识是否需要创建*/
int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create)
{int err = -EIO;int offsets[4];Indirect chain[4];Indirect *partial;unsigned long goal;int left;int boundary = 0;/*前边讲过的函数,根据文件的inode和文件的文件内块号,得到这个文件的存储地址,返回值是深度,offset是偏移数组*/int depth = ext2_block_to_path(inode, iblock, offsets, &boundary);/*如果返回0,说明错误*/if (depth == 0)goto out;reread:/*根据传入的offset得到对应的chain数组*/partial = ext2_get_branch(inode, depth, offsets, chain, &err);/* 最好的情况,找到了这个块,不需要分配*/if (!partial) {
got_it:/*建立bh_result缓冲区和inode所在文件系统和对应的数据块的映射关系*/map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key));/*如果是边界,设置缓冲区边界*/if (boundary)set_buffer_boundary(bh_result);/* partial指向最后一个,然后清理 */partial = chain+depth-1; goto cleanup;}/* 检查下,如果参数要求不创建块或者是读取失败的话,就直接清理了*/if (!create || err == -EIO) {
cleanup:while (partial > chain) {brelse(partial->bh);partial--;}
out:return err;}/* 运行到这里,说明间接块可能被删除了,如果删除了,就跳转到changed*/if (err == -EAGAIN)goto changed;/*说明映射链断裂了,那就找一个块分配先*/goal = 0;/*寻找一个分配的块号*/if (ext2_find_goal(inode, iblock, chain, partial, &goal) < 0)goto changed;/*left是chain的最后地址距离partial*/left = (chain + depth) - partial;/*从断裂的地方补齐映射链*/err = ext2_alloc_branch(inode, left, goal,offsets+(partial-chain), partial);if (err)goto cleanup;/*判断文件系统是不是使用xip*/if (ext2_use_xip(inode->i_sb)) {/*需要清理块*/err = ext2_clear_xip_target (inode,le32_to_cpu(chain[depth-1].key));if (err)goto cleanup;}/*把刚刚分配的树枝粘结到树上*/if (ext2_splice_branch(inode, iblock, chain, partial, left) < 0)goto changed;set_buffer_new(bh_result);goto got_it;/*被修改了,返回错误*/
changed:while (partial > chain) {brelse(partial->bh);partial--;}goto reread;
}
/*地址空间的操作函数,写入一个page*/
static int ext2_writepage(struct page *page, struct writeback_control *wbc)
{return block_write_full_page(page, ext2_get_block, wbc);
}
/*读取一个页到内存*/
static int ext2_readpage(struct file *file, struct page *page)
{return mpage_readpage(page, ext2_get_block);
}
/*读取多个页,参数nr_pages是页数*/
static int
ext2_readpages(struct file *file, struct address_space *mapping,struct list_head *pages, unsigned nr_pages)
{return mpage_readpages(mapping, pages, nr_pages, ext2_get_block);
}
/*写入准备,都是调用文件系统上层的函数*/
static int
ext2_prepare_write(struct file *file, struct page *page,unsigned from, unsigned to)
{return block_prepare_write(page,from,to,ext2_get_block);
}
/*无缓冲区的写入准备*/
static int
ext2_nobh_prepare_write(struct file *file, struct page *page,unsigned from, unsigned to)
{return nobh_prepare_write(page,from,to,ext2_get_block);
}
/*无缓冲区模式的写入页*/
static int ext2_nobh_writepage(struct page *page,struct writeback_control *wbc)
{return nobh_writepage(page, ext2_get_block, wbc);
}
/*ext2的块的映射*/
static sector_t ext2_bmap(struct address_space *mapping, sector_t block)
{return generic_block_bmap(mapping,block,ext2_get_block);
}
/*块设备直接IO操作*/
static ssize_t
ext2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,loff_t offset, unsigned long nr_segs)
{struct file *file = iocb->ki_filp;struct inode *inode = file->f_mapping->host;return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,offset, nr_segs, ext2_get_block, NULL);
}
/*写入page*/
static int
ext2_writepages(struct address_space *mapping, struct writeback_control *wbc)
{return mpage_writepages(mapping, wbc, ext2_get_block);
}
/*地址空间操作函数*/
const struct address_space_operations ext2_aops = {.readpage       = ext2_readpage,.readpages     = ext2_readpages,.writepage        = ext2_writepage,.sync_page        = block_sync_page,.prepare_write       = ext2_prepare_write,.commit_write     = generic_commit_write,.bmap           = ext2_bmap,.direct_IO     = ext2_direct_IO,.writepages       = ext2_writepages,.migratepage     = buffer_migrate_page,
};
/*片内执行的ext2文件系统对应的地址空间操作函数集合*/
const struct address_space_operations ext2_aops_xip = {.bmap           = ext2_bmap,.get_xip_page      = ext2_get_xip_page,
};
/*没有缓冲区的地址空间集合*/
const struct address_space_operations ext2_nobh_aops = {.readpage      = ext2_readpage,.readpages     = ext2_readpages,.writepage        = ext2_nobh_writepage,.sync_page       = block_sync_page,.prepare_write       = ext2_nobh_prepare_write,.commit_write        = nobh_commit_write,.bmap          = ext2_bmap,.direct_IO     = ext2_direct_IO,.writepages       = ext2_writepages,.migratepage     = buffer_migrate_page,
};/* 寻找第一个非零值,在p到q之间,如果都是0,返回1*/
static inline int all_zeroes(__le32 *p, __le32 *q)
{while (p < q)if (*p++)return 0;return 1;
}/** 为局部阶段寻找一个间接块,inode是文件的inode,depth是要截断chain的深度,offsets是偏移数组,chain用来存放指向局部间接块的指针*/
static Indirect *ext2_find_shared(struct inode *inode,int depth,int offsets[4],Indirect chain[4],__le32 *top)
{Indirect *partial, *p;int k, err;*top = 0;/*找到要截断的位置*/for (k = depth; k > 1 && !offsets[k-1]; k--);/*获得截断位置前边的映射链*/partial = ext2_get_branch(inode, k, offsets, chain, &err);/*如果都没有问题,partial指向最后的节点*/if (!partial)partial = chain + k-1;/*partial还没有映射*/write_lock(&EXT2_I(inode)->i_meta_lock);if (!partial->key && *partial->p) {write_unlock(&EXT2_I(inode)->i_meta_lock);goto no_top;}/*找到间接块不为0的链接段*/for (p=partial; p>chain && all_zeroes((__le32*)p->bh->b_data,p->p); p--);/* 找到了要删除的点 ,如果p就是要删除的点,p指针指向之前的块*/if (p == chain + k - 1 && p > chain) {p->p--;} else {/*top返回枝干的头部*/*top = *p->p;*p->p = 0;}write_unlock(&EXT2_I(inode)->i_meta_lock);while(partial > p){brelse(partial->bh);partial--;}
no_top:/*枝干的头没有映射,返回枝干没有映射的头部*/return partial;
}/** 释放一连串的数据块,inode数所属的文件,p是数据块的数组,q指向结尾*/
static inline void ext2_free_data(struct inode *inode, __le32 *p, __le32 *q)
{unsigned long block_to_free = 0, count = 0;unsigned long nr;/*从p到q*/for ( ; p < q ; p++) {/*数组上的数据都是按照小端32字节排序的,先获得块号*/nr = le32_to_cpu(*p);if (nr) {*p = 0;/* 计算块的数目,如果是连续的就一起释放,如果count为0,先别急着释放,看看下边的是不是连续的*/if (count == 0)goto free_this;/*如果是连续的,count加一*/else if (block_to_free == nr - count)count++;/*不连续,就一起释放*/else {mark_inode_dirty(inode);ext2_free_blocks (inode, block_to_free, count);/*先标记下来*/free_this:block_to_free = nr;count = 1;}}}/*收尾*/if (count > 0) {mark_inode_dirty(inode);ext2_free_blocks (inode, block_to_free, count);}
}/** 递归函数,逐层释放,释放一组映射链上的枝干,p是块的数组,q是数组结尾,depth是释放的枝干的深度 */
static void ext2_free_branches(struct inode *inode, __le32 *p, __le32 *q, int depth)
{struct buffer_head * bh;unsigned long nr;if (depth--) {/*每一个块存放的指针数目*/int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);/*遍历p到q*/for ( ; p < q ; p++) {/*当前遍历到的块号*/nr = le32_to_cpu(*p);if (!nr)continue;*p = 0;/*先把这块读入到内存*/bh = sb_bread(inode->i_sb, nr);/*失败就释放缓冲区 */ if (!bh) {ext2_error(inode->i_sb, "ext2_free_branches","Read failure, inode=%ld, block=%ld",inode->i_ino, nr);continue;}/*释放本层的枝干*/ext2_free_branches(inode,(__le32*)bh->b_data,(__le32*)bh->b_data + addr_per_block,depth);bforget(bh);ext2_free_blocks(inode, nr, 1);mark_inode_dirty(inode);}} else/*只有一层的时候,直接释放*/ext2_free_data(inode, p, q);
}
/*从映射树上截断*/
void ext2_truncate (struct inode * inode)
{/*映射指针所在的额数组*/__le32 *i_data = EXT2_I(inode)->i_data;/*没块存放的指针数目*/int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);int offsets[4];Indirect chain[4];Indirect *partial;__le32 nr = 0;int n;long iblock;unsigned blocksize;/*目录或者链接文件不支持截断*/if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||S_ISLNK(inode->i_mode)))return;/*快速链接也不行*/if (ext2_inode_is_fast_symlink(inode))return;/*只能后续加入和不可修改的文件不可以*/if (IS_APPEND(inode) || IS_IMMUTABLE(inode))return;/*把预分配的都释放掉吧*/ext2_discard_prealloc(inode);/*块大小*/blocksize = inode->i_sb->s_blocksize;/*iblock是截断的位置*/iblock = (inode->i_size + blocksize-1)>> EXT2_BLOCK_SIZE_BITS(inode->i_sb);/*根据映射方式,选择页截断*/if (mapping_is_xip(inode->i_mapping))xip_truncate_page(inode->i_mapping, inode->i_size);else if (test_opt(inode->i_sb, NOBH))nobh_truncate_page(inode->i_mapping, inode->i_size);elseblock_truncate_page(inode->i_mapping,inode->i_size, ext2_get_block);/*获得iblock对应的树叶到树根的路径*/n = ext2_block_to_path(inode, iblock, offsets, NULL);if (n == 0)return;/*如果从直接映射块开始截断,先释放第一层后边的叶子块,然后在释放后边的间接块*/if (n == 1) {ext2_free_data(inode, i_data+offsets[0],i_data + EXT2_NDIR_BLOCKS);goto do_indirects;}/*找到要截断的块所在的枝干*/partial = ext2_find_shared(inode, n, offsets, chain, &nr);/* 释放,如果是 */if (nr) {if (partial == chain)mark_inode_dirty(inode);elsemark_buffer_dirty_inode(partial->bh, inode);ext2_free_branches(inode, &nr, &nr+1, (chain+n-1) - partial);}/* 释放共享的枝干上的间接块 */while (partial > chain) {ext2_free_branches(inode,partial->p + 1,(__le32*)partial->bh->b_data+addr_per_block,(chain+n-1) - partial);mark_buffer_dirty_inode(partial->bh, inode);brelse (partial->bh);partial--;}
do_indirects:/* 开始释放间接映射块,因为offsets[0]得值是编号,所以会大1*/switch (offsets[0]) {default:/*先获得一级间接块的编号,释放一级间接块*/nr = i_data[EXT2_IND_BLOCK];if (nr) {i_data[EXT2_IND_BLOCK] = 0;mark_inode_dirty(inode);/*释放一级间接块树*/ext2_free_branches(inode, &nr, &nr+1, 1);}case EXT2_IND_BLOCK:/*一级间接块*//*释放二级间接块*/nr = i_data[EXT2_DIND_BLOCK];if (nr) {i_data[EXT2_DIND_BLOCK] = 0;mark_inode_dirty(inode);/*释放二级间接块树*/ext2_free_branches(inode, &nr, &nr+1, 2);}case EXT2_DIND_BLOCK:/*二级间接块*/nr = i_data[EXT2_TIND_BLOCK];if (nr) {i_data[EXT2_TIND_BLOCK] = 0;mark_inode_dirty(inode);/*释放三级间接块树*/ext2_free_branches(inode, &nr, &nr+1, 3);}case EXT2_TIND_BLOCK:/*三级间接块*/;}/*记录修改时间*/inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;/*更新*/if (inode_needs_sync(inode)) {sync_mapping_buffers(inode->i_mapping);ext2_sync_inode (inode);} else {mark_inode_dirty(inode);}
}
/*ext2文件系统获得一个inode,ino是希望获得inode的号码,p指向inode缓冲区*/
static struct ext2_inode *ext2_get_inode(struct super_block *sb, ino_t ino,struct buffer_head **p)
{struct buffer_head * bh;unsigned long block_group;unsigned long block;unsigned long offset;struct ext2_group_desc * gdp;*p = NULL;/*检查ino编号是不是合法,小于最小编号,大于编号数目都不行*/if ((ino != EXT2_ROOT_INO && ino < EXT2_FIRST_INO(sb)) ||ino > le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count))goto Einval;/*block_group获得ino在的组号码*/block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb);/*获得这个组的组描述符*/gdp = ext2_get_group_desc(sb, block_group, &bh);if (!gdp)goto Egdp;/*搞明白我们想要的inode在组内的inodetable上的所在块内偏移,*/offset = ((ino - 1) % EXT2_INODES_PER_GROUP(sb)) * EXT2_INODE_SIZE(sb);/*inode在inodetable上的第几个块*/block = le32_to_cpu(gdp->bg_inode_table) +(offset >> EXT2_BLOCK_SIZE_BITS(sb));/*读取这个块*/if (!(bh = sb_bread(sb, block)))goto Eio;/*p指向inode所在的缓冲区*/*p = bh;offset &= (EXT2_BLOCK_SIZE(sb) - 1);/*返回指向对应的inode结构体的位置指针*/return (struct ext2_inode *) (bh->b_data + offset);Einval:ext2_error(sb, "ext2_get_inode", "bad inode number: %lu",(unsigned long) ino);return ERR_PTR(-EINVAL);
Eio:ext2_error(sb, "ext2_get_inode","unable to read inode block - inode=%lu, block=%lu",(unsigned long) ino, block);
Egdp:return ERR_PTR(-EIO);
}
/*设置inode的flag标志,因为ext2_inode_info和inode结构体上的标志虽然意义一样,但是韦德位置不一样,所以要转化一下*/
void ext2_set_inode_flags(struct inode *inode)
{/*先获得ext2_inode_info上的flag*/unsigned int flags = EXT2_I(inode)->i_flags;/*转化成inode上的*/inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);if (flags & EXT2_SYNC_FL)inode->i_flags |= S_SYNC;if (flags & EXT2_APPEND_FL)inode->i_flags |= S_APPEND;if (flags & EXT2_IMMUTABLE_FL)inode->i_flags |= S_IMMUTABLE;if (flags & EXT2_NOATIME_FL)inode->i_flags |= S_NOATIME;if (flags & EXT2_DIRSYNC_FL)inode->i_flags |= S_DIRSYNC;
}/* 和上边的函数正好相反,这个函数把inode结构体的flag转化成ext2_inode_info结构体上的*/
void ext2_get_inode_flags(struct ext2_inode_info *ei)
{/*获得inode结构体的flag*/unsigned int flags = ei->vfs_inode.i_flags;/*转化*/ei->i_flags &= ~(EXT2_SYNC_FL|EXT2_APPEND_FL|EXT2_IMMUTABLE_FL|EXT2_NOATIME_FL|EXT2_DIRSYNC_FL);if (flags & S_SYNC)ei->i_flags |= EXT2_SYNC_FL;if (flags & S_APPEND)ei->i_flags |= EXT2_APPEND_FL;if (flags & S_IMMUTABLE)ei->i_flags |= EXT2_IMMUTABLE_FL;if (flags & S_NOATIME)ei->i_flags |= EXT2_NOATIME_FL;if (flags & S_DIRSYNC)ei->i_flags |= EXT2_DIRSYNC_FL;
}
/*阅读inode的内容,参数是想要的inode*/
void ext2_read_inode (struct inode * inode)
{struct ext2_inode_info *ei = EXT2_I(inode);ino_t ino = inode->i_ino;struct buffer_head * bh;/*从硬盘上获得ext2_inode结构体*/struct ext2_inode * raw_inode = ext2_get_inode(inode->i_sb, ino, &bh);int n;/*配置了acl控制,ext2_inode_info结构体的acl值设置为没有缓存*/
#ifdef CONFIG_EXT2_FS_POSIX_ACLei->i_acl = EXT2_ACL_NOT_CACHED;ei->i_default_acl = EXT2_ACL_NOT_CACHED;
#endif/*如果读取进来的是坏的,直接退出*/if (IS_ERR(raw_inode))goto bad_inode;/*开始从ext2文件系统上存储的ext2_inode读取数据转化成vfs层的inode数据*//*文件打开模式*/inode->i_mode = le16_to_cpu(raw_inode->i_mode);/*文件的uid和gid*/inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);/*如果没有NO_UID32位*/if (!(test_opt (inode->i_sb, NO_UID32))) {inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;}/*引用计数,文件大小*/inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);inode->i_size = le32_to_cpu(raw_inode->i_size);/*三种修改时间*/inode->i_atime.tv_sec = (signed)le32_to_cpu(raw_inode->i_atime);inode->i_ctime.tv_sec = (signed)le32_to_cpu(raw_inode->i_ctime);inode->i_mtime.tv_sec = (signed)le32_to_cpu(raw_inode->i_mtime);inode->i_atime.tv_nsec = inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = 0;ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);/* 如果引用计数为0,,并且mode为0,说明文件已经被删除了 */if (inode->i_nlink == 0 && (inode->i_mode == 0 || ei->i_dtime)) {brelse (bh);goto bad_inode;}/*inode文件的块数目*/inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);/*flag标志等字段的读取*/ei->i_flags = le32_to_cpu(raw_inode->i_flags);ei->i_faddr = le32_to_cpu(raw_inode->i_faddr);ei->i_frag_no = raw_inode->i_frag;ei->i_frag_size = raw_inode->i_fsize;ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl);ei->i_dir_acl = 0;if (S_ISREG(inode->i_mode))inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;elseei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);/*读取和运行时赋值的字段,先赋为0*/ei->i_dtime = 0;inode->i_generation = le32_to_cpu(raw_inode->i_generation);ei->i_state = 0;ei->i_next_alloc_block = 0;ei->i_next_alloc_goal = 0;ei->i_prealloc_count = 0;ei->i_block_group = (ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);ei->i_dir_start_lookup = 0;/* 复制i_data,就是文件映射指针 */for (n = 0; n < EXT2_N_BLOCKS; n++)ei->i_data[n] = raw_inode->i_block[n];/*根据文件的类型不同,设置文件的操作函数集合*//*普通文件*/if (S_ISREG(inode->i_mode)) {inode->i_op = &ext2_file_inode_operations;if (ext2_use_xip(inode->i_sb)) {inode->i_mapping->a_ops = &ext2_aops_xip;inode->i_fop = &ext2_xip_file_operations;} else if (test_opt(inode->i_sb, NOBH)) {inode->i_mapping->a_ops = &ext2_nobh_aops;inode->i_fop = &ext2_file_operations;} else {inode->i_mapping->a_ops = &ext2_aops;inode->i_fop = &ext2_file_operations;}} else if (S_ISDIR(inode->i_mode)) {/*目录文件*/inode->i_op = &ext2_dir_inode_operations;inode->i_fop = &ext2_dir_operations;if (test_opt(inode->i_sb, NOBH))inode->i_mapping->a_ops = &ext2_nobh_aops;elseinode->i_mapping->a_ops = &ext2_aops;} else if (S_ISLNK(inode->i_mode)) {/*链接文件*/if (ext2_inode_is_fast_symlink(inode))inode->i_op = &ext2_fast_symlink_inode_operations;else {inode->i_op = &ext2_symlink_inode_operations;if (test_opt(inode->i_sb, NOBH))inode->i_mapping->a_ops = &ext2_nobh_aops;elseinode->i_mapping->a_ops = &ext2_aops;}} else {/*其他文件*/inode->i_op = &ext2_special_inode_operations;if (raw_inode->i_block[0])init_special_inode(inode, inode->i_mode,old_decode_dev(le32_to_cpu(raw_inode->i_block[0])));else init_special_inode(inode, inode->i_mode,new_decode_dev(le32_to_cpu(raw_inode->i_block[1])));}brelse (bh);ext2_set_inode_flags(inode);return;bad_inode:/*标记这个inode是坏的*/make_bad_inode(inode);return;
}
/*同步inode数据,把inode的数据写入到硬盘里*/
static int ext2_update_inode(struct inode * inode, int do_sync)
{struct ext2_inode_info *ei = EXT2_I(inode);struct super_block *sb = inode->i_sb;ino_t ino = inode->i_ino;uid_t uid = inode->i_uid;gid_t gid = inode->i_gid;struct buffer_head * bh;/*读取硬盘上的ext2_inode*/struct ext2_inode * raw_inode = ext2_get_inode(sb, ino, &bh);int n;int err = 0;/*如果读取的inode有问题,返回IO读取错误*/if (IS_ERR(raw_inode))return -EIO;/* 如果这个inode是新的,全部设置为0 */if (ei->i_state & EXT2_STATE_NEW)memset(raw_inode, 0, EXT2_SB(sb)->s_inode_size);/*转化ext2_inode_info的flag到inode上边*/ext2_get_inode_flags(ei);/*开始用inode的数据填充raw_inode*//*文件打开模式*/raw_inode->i_mode = cpu_to_le16(inode->i_mode);/*uid和gid*/if (!(test_opt(sb, NO_UID32))) {raw_inode->i_uid_low = cpu_to_le16(low_16_bits(uid));raw_inode->i_gid_low = cpu_to_le16(low_16_bits(gid));if (!ei->i_dtime) {raw_inode->i_uid_high = cpu_to_le16(high_16_bits(uid));raw_inode->i_gid_high = cpu_to_le16(high_16_bits(gid));} else {raw_inode->i_uid_high = 0;raw_inode->i_gid_high = 0;}} else {raw_inode->i_uid_low = cpu_to_le16(fs_high2lowuid(uid));raw_inode->i_gid_low = cpu_to_le16(fs_high2lowgid(gid));raw_inode->i_uid_high = 0;raw_inode->i_gid_high = 0;}/*链接计数,文件大小,修改时间*/raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);raw_inode->i_size = cpu_to_le32(inode->i_size);raw_inode->i_atime = cpu_to_le32(inode->i_atime.tv_sec);raw_inode->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec);raw_inode->i_mtime = cpu_to_le32(inode->i_mtime.tv_sec);/*文件块数目,删除时间,标志*/raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);raw_inode->i_flags = cpu_to_le32(ei->i_flags);/*帧地址*/raw_inode->i_faddr = cpu_to_le32(ei->i_faddr);/*文件碎片的信息*/raw_inode->i_frag = ei->i_frag_no;raw_inode->i_fsize = ei->i_frag_size;/*文件的acl控制*/raw_inode->i_file_acl = cpu_to_le32(ei->i_file_acl);/*如果不是普通文件*/if (!S_ISREG(inode->i_mode))/*赋值目录的acl控制*/raw_inode->i_dir_acl = cpu_to_le32(ei->i_dir_acl);else {raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32);if (inode->i_size > 0x7fffffffULL) {if (!EXT2_HAS_RO_COMPAT_FEATURE(sb,EXT2_FEATURE_RO_COMPAT_LARGE_FILE) ||EXT2_SB(sb)->s_es->s_rev_level ==cpu_to_le32(EXT2_GOOD_OLD_REV)) {/* If this is the first large file* created, add a flag to the superblock.*/lock_kernel();ext2_update_dynamic_rev(sb);EXT2_SET_RO_COMPAT_FEATURE(sb,EXT2_FEATURE_RO_COMPAT_LARGE_FILE);unlock_kernel();ext2_write_super(sb);}}}/*文件系统版本*/raw_inode->i_generation = cpu_to_le32(inode->i_generation);/*字符设备或者是块设备文件*/if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {/*老的设备和新的标识方法不一样*/if (old_valid_dev(inode->i_rdev)) {raw_inode->i_block[0] =cpu_to_le32(old_encode_dev(inode->i_rdev));raw_inode->i_block[1] = 0;} else {raw_inode->i_block[0] = 0;raw_inode->i_block[1] =cpu_to_le32(new_encode_dev(inode->i_rdev));raw_inode->i_block[2] = 0;}} else for (n = 0; n < EXT2_N_BLOCKS; n++)raw_inode->i_block[n] = ei->i_data[n];/*标记缓冲区是脏的*/mark_buffer_dirty(bh);if (do_sync) {sync_dirty_buffer(bh);if (buffer_req(bh) && !buffer_uptodate(bh)) {printk ("IO error syncing ext2 inode [%s:%08lx]\n",sb->s_id, (unsigned long) ino);err = -EIO;}}/*取消EXT2_STATE_NEW标志位*/ei->i_state &= ~EXT2_STATE_NEW;brelse (bh);return err;
}
/*调用上边的函数,将inode写入到硬盘上*/
int ext2_write_inode(struct inode *inode, int wait)
{return ext2_update_inode(inode, wait);
}
/*同步inode,调用fs层的函数,同步inode*/
int ext2_sync_inode(struct inode *inode)
{struct writeback_control wbc = {.sync_mode = WB_SYNC_ALL,.nr_to_write = 0,  /* sys_fsync did this */};return sync_inode(inode, &wbc);
}
/*设置文件属性*/
int ext2_setattr(struct dentry *dentry, struct iattr *iattr)
{struct inode *inode = dentry->d_inode;int error;/*检查文件属性有没有错误*/error = inode_change_ok(inode, iattr);if (error)return error;/*合法且uid和gid一样才可以设置*/if ((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) ||(iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)) {error = DQUOT_TRANSFER(inode, iattr) ? -EDQUOT : 0;if (error)return error;}/*调用inode_setattr来设置属性*/error = inode_setattr(inode, iattr);/*定义了ATTR_MODE位,就改变acl*/if (!error && (iattr->ia_valid & ATTR_MODE))error = ext2_acl_chmod(inode);return error;
}

ext2文件系统源代码之inode.c相关推荐

  1. ext2文件系统源代码之dir.c

    今天我们来看ext2源代码的dir.c文件,这个文件是做和目录有关的操作的,也是很重要的一个文件,大家认真看哦,有什么和我不一致的大家一起探讨~~ 在讲这个之前我觉得还是先说下ext2文件系统的目录方 ...

  2. ext2文件系统源代码之super.c

    今天来看一个ext2文件系统里的super.c文件,这个文件的内容是文件系统里的超级块的相关函数,对于文件系统有很大的重要性,并且这个文件也很长,好啦,我们开始看吧. /* 日常作者信息* linux ...

  3. linux之EXT2文件系统--理解block/block group/索引结点inode/索引位图

    0. 文件系统和图书馆 在linux上操作文件,和在图书馆借书是非常相似的. 硬盘上的文件系统,好比图书馆的书架:而vfs则是图书馆的管理系统. 内核的工作: 1. 维护一个文件的目录树(dentry ...

  4. Linux ext2文件系统小结

    本文来源:http://learn.akae.cn/media/ch29s02.html 有修改 1.ext2文件系统整体布局 一个磁盘可以划分成多个分区,每个分区必须先用格式化工具(例如某种mkfs ...

  5. ext2文件系统了解

    一个磁盘可以划分成多个分区,每个分区必须先用格式化工具(例如mkfs命令)格式化成某种格式的文件系统,然后才能存储文件,格式化的过程会在磁盘上写一些管理存储布局的信息.下以ext2文件系统为例说明文件 ...

  6. Linux 文件系统及 ext2 文件系统

    linux 支持的文件系统类型 Ext2:     有点像 UNIX 文件系统.有 blocks,inodes,directories 的概念. Ext3:     Ext2 的加强版,添加了日志的功 ...

  7. ext2文件系统 - mke2fs

      上一遍博文的重点其实将ext2整体的组织框架,我们知道了ext2文件系统由块组组成,每个块组里面的组织形式.我们甚至直接把超级块和组描述符里面的内容,用十六进制形式展现了出来.这篇博文主要讲述如何 ...

  8. linux VFS 虚拟文件系统 简介 super_block inode dentry file

    1.简介 1.Linux 中允许众多不同的文件系统共存,如 ext2, ext3, vfat 等.通过使用同一套文件 I/O 系统 调用即可对 Linux 中的任意文件进行操作而无需考虑其所在的具体文 ...

  9. Linux文件系统基础之inode和dentry

    inode和dentry在VFS和实体的文件系统(ext2.ext3等)中都是比较重要的概念.首先看一下虚拟文件系统的标准定义:虚拟文件系统(英语:Virtual file system,缩写为VFS ...

  10. 转:ext2文件系统详解

    第一部分磁盘的物理组成 磁盘的基本概念: 扇区为最小的物理存储单位,每个扇区为512字节. 将扇区组成一个圆,那就是柱面,柱面是分区的最小单位. 第一个扇区很重要,里面有硬盘主引导记录(Masterb ...

最新文章

  1. mysql报196271错误_超过mysql最大连接的异常
  2. 完全背包问题(信息学奥数一本通-T1268)
  3. pythonfilelist_Python 列表list使用介绍
  4. arcgis 属性表中起点终点创建线_【ArcGIS开发】shapefile矢量数据遍历、求交、与属性更新...
  5. 如何将Eclipse的javaWeb项目改为IDEA的maven项目
  6. 小白程序员的成长之路《时间管理大师》
  7. Docker 开启镜像加速 (网易和阿里)
  8. SPI通信协议简要介绍
  9. 公司研制的CPU卡燃气表正式投放北京市场
  10. 史上最全:Mac搭建Airtest IDE +IOS 测试环境
  11. 模块一 day09 文件操作相关
  12. Ubuntu下键盘输入错乱问题,输入自动变为希腊字母和符号
  13. android 播放器架构,Android播放器框架设计系列-1
  14. PHP+ajaxfileupload 实现用户头像上传
  15. SAP中图文展示分摊和分配的区别
  16. 用手机对电脑进行远程关机
  17. 《雪盈王》,一本漫画,一个游戏,一场梦……
  18. 常用国名与首都(英文)
  19. 使用Matlab提取ADC采样数据中的噪声
  20. js 字符串去除特殊符号,去重

热门文章

  1. 算法导论 25.3 Johnson算法
  2. UE4蓝图教程(1)
  3. de4dot 脱壳工具
  4. python拼接sql语句字符串 无效字符,Python拼接SQL字符串的方法
  5. 弹性力学方程 有限差分法matlab,泊松方程的有限差分法的MATLAB实现
  6. 中文乱码解决办法(java)
  7. CMMI3 评估内容解读
  8. 达梦数据库DM7手把手安装教程
  9. android好用的窗口小工具下载,股票窗口小工具,安卓股票小插件
  10. WPF_界面_图片/界面/文字模糊解决之道整理