天天看點

Linux下簡單的mount指令實作(自動識别檔案系統類型)

背景簡述

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增加可執行權限後,可以如下方法運作:

繼續閱讀