戲說文件系統之ext2【續】

    通過前面兩篇博文,我們對ext2fs應該有了一個宏觀上的認識。但是這些所謂的superblock、block、group、group descriptor和ionde等等,它們到底有什麼用呢?今天我們簡單熱個身,來研究一下在一個磁盤分區上如何根據文件的inode號來訪問文件的內容?

    在我們將某個分區格式化成ext2/ext3文件系統時,block的大小一定是確定的,即使用戶沒有手工指定,block也會有個缺省值。在分區總大小一定的情況,每個分區所包含的block數也就固定了,通過superblock我麼可以知道分區中一共有多個少group以及每個group中所包含的block數。

    當我們拿到一個inode號時,首先要確定該inode屬於哪個group,即計算出該inode所在的group號,然後在組描述符數組中找到該group的描述信息,進而就可以獲取該inode所表示的文件內容了。

    我們設計的結構體信息如下:

struct fdata {

         unsigned long inode_num;    //用戶輸入的inode

         unsigned long i_blk_size;     //block大小,從superblock裏獲取

         unsigned int i_grp_num;      //inode 所在group

         unsigned long i_nt_blk_num;  //每個groupinode table所在的第一個block

         struct ext2_super_block sb;   //superblock的拷貝

         struct ext2_group_desc gd;   //group descriptor的拷貝

         struct ext2_inode i_data;     //inode的拷貝

};

   最終的測試代碼如下rd_file_by_inode.c

#include

#include

#include

#include

#include

#include

#include

#include

 

#define EXT2_SB_SIZE 1024 //ext2文件系統superblock的大小

 

struct fdata {

         unsigned long inode_num;   //inode number

         unsigned long i_blk_size;    //block size

         unsigned int i_grp_num;    //group number to which inode belongs

         unsigned long i_nt_blk_num;

         struct ext2_super_block sb;

         struct ext2_group_desc gd;

         struct ext2_inode i_data;

};

 

//獲取文件系統的block大小,並計算inode所在的group

int get_blk_size(int fd,int offset,struct fdata* ret){

         if(-1 == fd){

                   printf("file descriptor invalid!\n");

                   return 0;

         }

 

         char *buf=(char*)malloc(EXT2_SB_SIZE);

 

         if(NULL == buf){

                   printf("can't alloc memory!\n");

                   return 0;

         }

         memset(buf,0, EXT2_SB_SIZE);

         if(-1 == lseek(fd,offset,0)){

                   printf("lseek error!\n");

                   return 0;

         }

        

         if(read(fd,buf, EXT2_SB_SIZE)<0){

                   printf("read file error!\n");

                   return 0;

         }

        //superblock的信息複製一份到fdata.sb裏。

         memcpy((char*)&(ret->sb),buf, EXT2_SB_SIZE);

  //還記得如何根據superblock計算block的真實大小麼?

         ret->i_blk_size = 1<<(ret->sb.s_log_block_size+10);

        /*我們知道inode號在一個分區上是全局唯一且順序增長的,每個group中所包含的inode總數也是固定,

        所以根據inode號就可以求得其所在group號。group可是從0開始編號的哦。*/

         ret->i_grp_num = ret->inode_num/ret->sb.s_inodes_per_group;

 

         free(buf);

         buf=NULL;

        

         return 1;

}

 

//獲取inode所在group的組描述符。

int get_grp_descriptor(int fd,int offset,struct fdata* ret){

         int ds_size = sizeof(struct ext2_group_desc);

        

         char *buf=(char*)malloc(ds_size);

         lseek(fd,offset+ret-> i_grp_num *ds_size,0); 

        

         read(fd,buf,ds_size);

        //inode所在的組的組描述符複製一份到fdata.gd裏。

         memcpy((char*)&(ret->gd),buf,ds_size);

        //inode所在組中,inode在該組裏所佔的第一個block號。注意,inode有可能會連續佔據好幾個block,這一點我們前面也瞭解到。

         ret->i_nt_blk_num=ret->gd.bg_inode_table;

 

         free(buf);

         buf=NULL;

         return 1;

}

 

//這裏纔是根據inode號讀取該inode本身的128字節的數據。

int get_inode(int fd,int offset,struct fdata* ret){

         int inode_size = sizeof(struct ext2_inode);     

        /*知道了block大小,以及inode所佔block的起始號,inode大小也知道,那麼根據inode號就可以直接定位到所要操作的inode

        磁盤分區上的偏移量,如下。而inode是從1開始編號,所以計算偏移量要留心。*/

         offset=ret->i_nt_blk_num*ret->i_blk_size+(ret->inode_num-1)*inode_size;

 

         char *buf=(char*)malloc(inode_size);

         lseek(fd,offset,0);

         read(fd,buf,inode_size);

 

         memcpy((char*)&(ret->i_data),buf,inode_size);

 

         free(buf);

         buf=NULL;

         return 1; 

}

 

/*拿到inode裏的信息後,我們就可以獲取其所表示的文件的內容數據了。

這裏我用了遞歸算法來讀取各級block數據指針所指向的block中的內容。

算法有待優化,呵呵。*/

int read_data(int fd,int block_s,int dblock_num,int fsize,int level,char* dbuf){

         int offset=0,rdbytes,left;

         char *buf=(char*)malloc(block_s);

         lseek(fd,block_s*dblock_num,0);    

         read(fd,buf,block_s);

         unsigned int *pdblk=(unsigned int *)buf;

         while(fsize>0 && ((char*)pdblk-buf)

                   if(level > 2){

                            rdbytes=read_data(fd,block_s,*pdblk,fsize,level-1,dbuf+offset);                           

                   }else{

                   rdbytes=(fsize >= block_s? block_s:fsize);

                   lseek(fd,(*pdblk)*block_s,0);

                   read(fd,dbuf+offset,rdbytes);

                   }

                   offset += rdbytes;

                   fsize -= rdbytes;

                   pdblk++;

         }

         free(buf);

         return offset;

}

 

//對上面讀取inode中數據block的函數進行一次封裝,如下:

int get_data(int fd,struct fdata* ret,char* output_fileName){

         int rdbytes,offset=0,i=0,file_size = ret->i_data.i_size,Blk;

         int blk_size = ret->i_blk_size;

         char *buf=(char*)malloc(file_size);

         memset(buf,0,file_size);

 

         while(file_size>0){

                   rdbytes=(file_size >= blk_size? blk_size:(file_size));

                   if(i<12){ //讀取數據的直接block指針所指向的數據塊裏的內容

                            lseek(fd,ret->i_data.i_block[i]*blk_size,0);

                            Blk=read(fd,buf+offset,rdbytes);

                   }else if(i == 12){ //兩級block數據指針

                            Blk=read_data(fd,blk_size,ret->i_data.i_block[i],file_size,2,buf+offset);

                   }else if(i == 13){//三級block數據指針

                            Blk=read_data(fd,blk_size,ret->i_data.i_block[i],file_size,3,buf+offset);

                   }else{//四級block數據指針

                            Blk=read_data(fd,blk_size,ret->i_data.i_block[i],file_size,4,buf+offset);

                   }

                   offset += Blk;

                   file_size -= Blk;

                   i++;

         }

 

         int fdo = open(output_fileName,O_CREAT|O_WRONLY,0777);

         write(fdo,buf,ret->i_data.i_size);

         close(fdo);

         return 1;

}

int main(int argc,char** argv){

         int fd = -1;

         struct fdata mf_data;

 

         if(4 != argc){

                   printf("Usage: %s /dev/partationLabel inode_number output_fileName \n");

                   return 0;

         }

 

         if(-1 == (fd=open(argv[1],O_RDONLY,0777))){

                   printf("open file error!\n");

                   return 1;

         }       

 

         mf_data.inode_num = atol(argv[2]);

         if(!get_blk_size(fd, EXT2_SB_SIZE,&mf_data)){

                   printf("get superblock failed!\n");

                   close(fd);

                   return 1;

         }

 

         get_grp_descriptor(fd,mf_data. i_blk_size,&mf_data);

         get_inode(fd,0,&mf_data);

         get_data(fd,&mf_data,argv[3]);

        

         printf("inode : %d\n",mf_data.inode_num);

         printf("block size: %d\n",mf_data.i_blk_size);

         printf("file size : %d Byte(s)\n",mf_data.i_data.i_size);

 

         close(fd);

         return 0;

}

    編譯:


    我的一塊虛擬硬盤/dev/hdd1只有一個分區,格式是ext2掛載在/mnt/ided目錄下。該分區裏有幾個文件,如下所示。


    然後我們依次執行如下命令:


    根據文件的inode號分別讀取/mnt/ided目錄下的bzImage(大小bzImage字節),klinux-2.6.18.tar.gz(大小156992521字節)和VMwareTools-7.8.6-185404.i386.rpm(大小102939982字節)的內容。驗證一下我們讀取到的文件內容是否正確:

    md5sum算出來的摘要一模一樣。有些童鞋可能心裏犯嘀咕:“MD5摘要加密算法不是早在2005年就已經被山東大學王小云教授的團隊破解了,證明是不可靠的麼?那麼md5sum命令的輸出結果會不會是忽悠人呢?”

懷疑纔會使人進步和成長。那麼下面這個操作肯定100%值得信賴:

    我們把源文件和我們通過inode讀取到的文件內容,以十六進制形式輸出,然後再比較看其是否差異。理論和實際均證明我們的算法是正確的。

    雖然文件系統不是這樣來根據inode讀取文件內容的,但是我們自己通過一番摸索,對文件系統中的各種術語和名詞以及它們的作用的認識和掌握又加深了一個層次。

    本文沒啥技術含量,但對編程基本功底是個鍛鍊。感興趣的朋友可以將我上面讀取文件內容的“醜陋”代碼優化一下吧。

<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
閱讀(4434) | 評論(6) | 轉發(12) |
給主人留下些什麼吧!~~
58_avatar_small.jpg

wjlkoorey2582013-02-27 23:26:31

shangbaogen:又出新博文啦,學習~,67.gif

65.gif共同交流

回覆 | 舉報
58_avatar_small.jpg

wjlkoorey2582013-02-27 23:25:59

lmnos:我更期待兄弟出個關於btrfs的27.gif

其實我是“水貨”,呵呵
期待東哥你來...

回覆 | 舉報
19_avatar_small.jpg

shangbaogen2013-02-27 18:19:46

又出新博文啦,學習~,67.gif

28_avatar_small.jpg

lmnos2013-02-27 14:31:10

我更期待兄弟出個關於btrfs的27.gif

58_avatar_small.jpg

wjlkoorey2582013-02-27 13:18:24

Bean_lee:兄弟再戰江湖啊。呵呵。

要給江東父老一個交代啊27.gif

回覆 | 舉報
評論熱議
發佈了87 篇原創文章 · 獲贊 14 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章