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
00000000  eb fe 90 4d 53 44 4f 53  35 2e 30 00 02 10 20 00  |...MSDOS5.0... .|
00000010  02 00 00 00 00 f8 00 00  3f 00 ff 00 00 00 00 00  |........?.......|
00000020  e0 d7 d2 01 54 3a 00 00  00 00 00 00 02 00 00 00  |....T:..........|
00000030  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  00 01 29 76 6d 01 00 4e  4f 20 4e 41 4d 45 20 20  |..)vm..NO NAME  |
00000050  20 20 46 41 54 33 32 20  20 20 00 00 00 00 00 00  |  FAT32   ......|
00000060  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

有看到0x52 Offset开始的位置有字符串“FAT32”;

hexdump读ntfs分区:

# hexdump -C -n 256 /dev/sda2
00000000  eb 52 90 4e 54 46 53 20  20 20 20 00 02 08 00 00  |.R.NTFS    .....|
00000010  00 00 00 00 00 f8 00 00  3f 00 ff 00 20 00 00 00  |........?... ...|
00000020  00 00 00 00 80 00 00 00  df 3f 19 01 00 00 00 00  |.........?......|
00000030  00 00 0c 00 00 00 00 00  02 00 00 00 00 00 00 00  |................|
00000040  f6 00 00 00 01 00 00 00  6d 8e f9 6c b3 f9 6c 96  |........m..l..l.|
00000050  00 00 00 00 fa 33 c0 8e  d0 bc 00 7c fb 68 c0 07  |.....3.....|.h..|
00000060  1f 1e 68 66 00 cb 88 16  0e 00 66 81 3e 03 00 4e  |..hf......f.>..N|
00000070  54 46 53 75 15 b4 41 bb  aa 55 cd 13 72 0c 81 fb  |TFSu..A..U..r...|
00000080  55 aa 75 06 f7 c1 01 00  75 03 e9 dd 00 1e 83 ec  |U.u.....u.......|
00000090  18 68 1a 00 b4 48 8a 16  0e 00 8b f4 16 1f cd 13  |.h...H..........|
000000a0  9f 83 c4 18 9e 58 1f 72  e1 3b 06 0b 00 75 db a3  |.....X.r.;...u..|
000000b0  0f 00 c1 2e 0f 00 04 1e  5a 33 db b9 00 20 2b c8  |........Z3... +.|
000000c0  66 ff 06 11 00 03 16 0f  00 8e c2 ff 06 16 00 e8  |f...............|
000000d0  4b 00 2b c8 77 ef b8 00  bb cd 1a 66 23 c0 75 2d  |K.+.w......f#.u-|
000000e0  66 81 fb 54 43 50 41 75  24 81 f9 02 01 72 1e 16  |f..TCPAu$....r..|
000000f0  68 07 bb 16 68 70 0e 16  68 09 00 66 53 66 53 66  |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[16];          /* 128-bit uuid for volume */
/*78*/ char  s_volume_name[16];   /* volume name */
/*88*/ char  s_last_mounted[64];  /* 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[16]; /* 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[197];    /* 可以忽略 */
};

其中s_magic在ext2/ext3上固定为0xEF53。这个superblock放在分区的superblock1上(一个superblock大小为0x400bytes),所以可以通过s_magic去判定是否是ext2或者ext3,通过hexdump打印ext2的superblock1如下:

# hexdump -C -n 1024 -s 1024 sda3
00000400  40 ce 08 00 fc 27 23 00  ff c1 01 00 71 9a 22 00  |@....'#.....q.".|
00000410  33 ce 08 00 00 00 00 00  02 00 00 00 02 00 00 00  |3...............|
00000420  00 80 00 00 00 80 00 00  c0 1f 00 00 78 40 ca 59  |[email protected]|
00000430  5d 00 00 00 05 00 25 00  53 ef 00 00 01 00 00 00  |].....%.S.......|
00000440  06 04 00 00 00 4e ed 00  00 00 00 00 01 00 00 00  |.....N..........|
00000450  00 00 00 00 0b 00 00 00  00 01 00 00 20 00 00 00  |............ ...|
00000460  02 00 00 00 01 00 00 00  14 1f 7f da 41 8f 46 fb  |............A.F.|
00000470  81 ef 72 2e 2f fa db 25  00 00 00 00 00 00 00 00  |..r./..%........|
00000480  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000004e0  00 00 00 00 00 00 00 00  00 00 00 00 9d f7 68 b5  |..............h.|
000004f0  f1 14 44 86 93 ca 80 84  aa 93 05 41 01 00 00 00  |..D........A....|
00000500  00 00 00 00 00 00 00 00  06 04 00 00 00 00 00 00  |................|
00000510  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000550  00 00 00 00 00 00 00 00  00 00 00 00 1c 00 1c 00  |................|
00000560  02 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000570  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

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[0x400];
    unsigned char* pOffset = NULL;
    unsigned int readSize = 0;

    pOffset = &tmpBuffer[0];
    memset(pOffset, 0x00, 0x400);

    //read the tag data for ntfs/fat32
    readSize = read(devFD, pOffset, 0x100);
    if(!readSize)
    {
        printf("read device file failed!\n");
        return FS_ERROR_TYPE;
    }
    if(!memcmp((const void*)(pOffset+0x52), (const void*)"FAT32", 5))//th offset with 0x52 is fat32 tag;
    {
        printf("filesystem:fat32!\n");
        return FS_FAT32;
    }
    else if(!memcmp((const void*)(pOffset+0x3), (const void*)"NTFS", 4))//the offset with 0x3 is ntfs tag;
    {
        printf("filesystem:ntfs!\n");
        return FS_NTFS;
    }
    else
    {
        memset(pOffset, 0x00, 0x400);
        lseek(devFD,0x400, SEEK_SET); //seek to superblock1;
        readSize = read(devFD, pOffset, 0x400);//read the superblock1 to buffer;
        if(!readSize)
        {
            printf("read1 device file failed!\n");
            return FS_ERROR_TYPE;
        }

        pOffset = pOffset+0x38; //the offset 0x438 is the tag of ext2:0x53 0xef;
        if((pOffset[0] == 0x53) && (pOffset[1] == 0xef))
        {
            printf("filesystem:ext2 or ext3!\n");
            return FS_EXT2;
        }
        else
        {
            printf("the offset 0x438 vaule is:0x%x 0x%x!\n", pOffset[0], pOffset[1]);
        }
    }

    printf("Unknown filesystem type!\n");
    return FS_ERROR_TYPE;
}

上面的函数要求传入分区对应的handle(用open()函数得到),然后返回文件系统类型;

测试函数

测试函数代码auto_mount.c如下:

#include 
#include 
#include 

#include 
#include 

/**
 * [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[512];
    char mountPath[512];

    int devFD = 0;
    int ret = -1;
    FILE_SYS_TYPE fsType = FS_ERROR_TYPE;

    if(argc != 3)
    {
        printf("error paramter!\n");
        return -1;
    }

    snprintf(devPath,511,"%s", argv[1]);
    snprintf(mountPath,511,"%s", argv[2]);
    printf("devPath:%s, mountPath:%s\n", devPath, mountPath);
    devFD = open(devPath, O_RDONLY);
    if(!devFD)
    {
        printf("Open device failed!\n");
        return -1;
    }
    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", 0, NULL);
            break;
        case FS_NTFS:
            ret=mount((const char*)devPath, (const char*)mountPath, (const char*)"ntfs3g", 0, NULL);
            break;
        case FS_FAT32:
            ret=mount((const char*)devPath, (const char*)mountPath, (const char*)"vfat", 0, NULL);
            break;
        default:
            printf("Unknown filesystem type!\n");
    }

    printf("mount result:%d\n", ret);
    close(devFD);
    return 0;
}

测试函数可以如下方法编译:
gcc auto_mount.c -o auto_mount;

在sudo chmod +x auto_mount增加可执行权限后,可以如下方法运行:

./auto_mount /dev/sda1 /mnt/sda1; //将/dev/sda1这个设备mount到/mnt/sda1这个目录;

你可能感兴趣的:(C/C++)