背景簡述
mount()/umount()為Linux下挂載和解除安裝磁盤分區的系統調用,函數原型分别如下:
int mount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags, const void *data);
int umount(const char *target);
mount()的filesystemtype這個參數需要填入需要挂載的磁盤分區的檔案系統類型,比如需要挂載fat32,那麼這個參數需要填寫為“vfat”,分區檔案系統類型為ext2,需要填寫為“ext2”;如果挂載成功,傳回0,挂載失敗,傳回-1;
是以,這就需要我們擷取目前要挂載的磁盤分區的檔案系統類型;在shell上我們可以通過fdisk /dev/sda 這樣類似的指令,然後敲p列印出現目前的磁盤分區資訊,但是如果想直接通過函數調用的方式擷取(非system()系統調用),還得另外找辦法;
hexdump讀取分區資訊
我們利用hexdump這個工具,分别讀取ext2/fat32/ntfs分區的資訊,如下方法:
hexdump讀fat32分區:
# hexdump -C -n 256 sda1
eb fe d f e |...MSDOS5.0... .|
f8 f ff |........?.......|
e0 d7 d2 a |....T:..........|
|................|
d e f e d |..)vm..NO NAME |
| FAT32 ......|
|................|
有看到0x52 Offset開始的位置有字元串“FAT32”;
hexdump讀ntfs分區:
# hexdump -C -n 256 /dev/sda2
eb e |.R.NTFS .....|
f8 f ff |........?... ...|
df f |.........?......|
c |................|
f6 d e f9 c b3 f9 c |........m..l..l.|
fa c0 e d0 bc c fb c0 |.........|.h..|
f e cb e e e |..hf......f.>..N|
b4 bb aa cd c fb |TFSu..A..U..r...|
aa f7 c1 e9 dd e ec |U.u.....u.......|
a b4 a e b f4 f cd |.h...H..........|
a0 f c4 e f e1 b b db a3 |.....X.r.;...u..|
b0 f c1 e f e a db b9 b c8 |........Z3... +.|
c0 ff f e c2 ff e8 |f...............|
d0 b b c8 ef b8 bb cd a c0 d |K.+.w......f#.u-|
fb f9 e |f..TCPAu$....r..|
f0 bb e |h...hp..h..fSfSf|
在Offset ox3處有發現“NTFS”這樣的字元串;
ext2/ext3格式不太一樣,直接讀前面256bytes讀不到有特别能說明檔案系統類型的字元串。有參考Linux 檔案系統的 Superblock, Inode, Dentry 和 File這篇blog,這篇blog裡面有較長的描述Linux下ext2的Inode和Block,其中有superblock的說明,結構體類型如下:
struct ext3_super_block {
/*00*/ __u32 s_inodes_count; /* inodes 計數 */
__u32 s_blocks_count; /* blocks 計數 */
__u32 s_r_blocks_count; /* 保留的 blocks 計數 */
__u32 s_free_blocks_count; /* 空閑的 blocks 計數 */
/*10*/ __u32 s_free_inodes_count; /* 空閑的 inodes 計數 */
__u32 s_first_data_block; /* 第一個資料 block */
__u32 s_log_block_size; /* block 的大小 */
__s32 s_log_frag_size; /* 可以忽略 */
/*20*/ __u32 s_blocks_per_group; /* 每 block group 的 block 數量 */
__u32 s_frags_per_group; /* 可以忽略 */
__u32 s_inodes_per_group; /* 每 block group 的 inode 數量 */
__u32 s_mtime; /* Mount time */
/*30*/ __u32 s_wtime; /* Write time */
__u16 s_mnt_count; /* Mount count */
__s16 s_max_mnt_count; /* Maximal mount count */
__u16 s_magic; /* Magic 簽名 */
__u16 s_state; /* File system state */
__u16 s_errors; /* Behaviour when detecting errors */
__u16 s_minor_rev_level; /* minor revision level */
/*40*/ __u32 s_lastcheck; /* time of last check */
__u32 s_checkinterval; /* max. time between checks */
__u32 s_creator_os; /* 可以忽略 */
__u32 s_rev_level; /* Revision level */
/*50*/ __u16 s_def_resuid; /* Default uid for reserved blocks */
__u16 s_def_resgid; /* Default gid for reserved blocks */
__u32 s_first_ino; /* First non-reserved inode */
__u16 s_inode_size; /* size of inode structure */
__u16 s_block_group_nr; /* block group # of this superblock */
__u32 s_feature_compat; /* compatible feature set */
/*60*/ __u32 s_feature_incompat; /* incompatible feature set */
__u32 s_feature_ro_compat; /* readonly-compatible feature set */
/*68*/ __u8 s_uuid[]; /* 128-bit uuid for volume */
/*78*/ char s_volume_name[]; /* volume name */
/*88*/ char s_last_mounted[]; /* directory where last mounted */
/*C8*/ __u32 s_algorithm_usage_bitmap; /* 可以忽略 */
__u8 s_prealloc_blocks; /* 可以忽略 */
__u8 s_prealloc_dir_blocks; /* 可以忽略 */
__u16 s_padding1; /* 可以忽略 */
/*D0*/ __u8 s_journal_uuid[]; /* uuid of journal superblock */
/*E0*/ __u32 s_journal_inum; /* 日志檔案的 inode 号數 */
__u32 s_journal_dev; /* 日志檔案的裝置号 */
__u32 s_last_orphan; /* start of list of inodes to delete */
/*EC*/ __u32 s_reserved[]; /* 可以忽略 */
};
其中s_magic在ext2/ext3上固定為0xEF53。這個superblock放在分區的superblock1上(一個superblock大小為0x400bytes),是以可以通過s_magic去判定是否是ext2或者ext3,通過hexdump列印ext2的superblock1如下:
# hexdump -C -n 1024 -s 1024 sda3
ce fc ff c1 a |@....'#.....q.".|
ce |3...............|
c0 f ca |[email protected]|
d ef |].....%.S.......|
e ed |.....N..........|
b |............ ...|
f f da f fb |............A.F.|
ef e f fa db |..r./..%........|
|................|
*
e0 d f7 b5 |..............h.|
f0 f1 ca aa |..D........A....|
|................|
|................|
*
c c |................|
|................|
|................|
C實作擷取檔案系統類型
根據上面的實驗結果,所實作的擷取分區檔案系統函數如下:
typedef enum
{
FS_NTFS,
FS_FAT32,
FS_EXT2,
FS_ERROR_TYPE,
}FILE_SYS_TYPE;
/**
* [_getFSType get the filesystem type of partion]
* @param devFD [the handle of usb partition device from open()]
* @return [the filesystem type]
*/
static FILE_SYS_TYPE _getFSType(int devFD)
{
unsigned char tmpBuffer[];
unsigned char* pOffset = NULL;
unsigned int readSize = ;
pOffset = &tmpBuffer[];
memset(pOffset, , );
//read the tag data for ntfs/fat32
readSize = read(devFD, pOffset, );
if(!readSize)
{
printf("read device file failed!\n");
return FS_ERROR_TYPE;
}
if(!memcmp((const void*)(pOffset+), (const void*)"FAT32", ))//th offset with 0x52 is fat32 tag;
{
printf("filesystem:fat32!\n");
return FS_FAT32;
}
else if(!memcmp((const void*)(pOffset+), (const void*)"NTFS", ))//the offset with 0x3 is ntfs tag;
{
printf("filesystem:ntfs!\n");
return FS_NTFS;
}
else
{
memset(pOffset, , );
lseek(devFD,, SEEK_SET); //seek to superblock1;
readSize = read(devFD, pOffset, );//read the superblock1 to buffer;
if(!readSize)
{
printf("read1 device file failed!\n");
return FS_ERROR_TYPE;
}
pOffset = pOffset+; //the offset 0x438 is the tag of ext2:0x53 0xef;
if((pOffset[] == ) && (pOffset[] == ))
{
printf("filesystem:ext2 or ext3!\n");
return FS_EXT2;
}
else
{
printf("the offset 0x438 vaule is:0x%x 0x%x!\n", pOffset[], pOffset[]);
}
}
printf("Unknown filesystem type!\n");
return FS_ERROR_TYPE;
}
上面的函數要求傳入分區對應的handle(用open()函數得到),然後傳回檔案系統類型;
測試函數
測試函數代碼auto_mount.c如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
/**
* [main auto mount usb partions]
* @param argc [the arguments number]
* @param argv [the arguments pointer]
* @return [the error code]
*/
int main(int argc, char *argv[])
{
char devPath[];
char mountPath[];
int devFD = ;
int ret = -;
FILE_SYS_TYPE fsType = FS_ERROR_TYPE;
if(argc != )
{
printf("error paramter!\n");
return -;
}
snprintf(devPath,,"%s", argv[]);
snprintf(mountPath,,"%s", argv[]);
printf("devPath:%s, mountPath:%s\n", devPath, mountPath);
devFD = open(devPath, O_RDONLY);
if(!devFD)
{
printf("Open device failed!\n");
return -;
}
fsType = _getFSType(devFD);
printf("fs type:%d\n", fsType);
switch(fsType)
{
case FS_EXT2:
ret=mount((const char*)devPath, (const char*)mountPath, (const char*)"ext2", , NULL);
break;
case FS_NTFS:
ret=mount((const char*)devPath, (const char*)mountPath, (const char*)"ntfs3g", , NULL);
break;
case FS_FAT32:
ret=mount((const char*)devPath, (const char*)mountPath, (const char*)"vfat", , NULL);
break;
default:
printf("Unknown filesystem type!\n");
}
printf("mount result:%d\n", ret);
close(devFD);
return ;
}
測試函數可以如下方法編譯:
gcc auto_mount.c -o auto_mount;
在sudo chmod +x auto_mount增加可執行權限後,可以如下方法運作: