运用于嵌入式系统的 automount

需求:挂载U盘分区文件系统到指定目录,用于热拔插事件产生时调用。
     插入U盘时,把新分区挂载到"\media\E", "\media\F" ... "\media\Z"等目录下。
     拔U盘,需要删除对应的目录。

     目的是,用户通过"\media"下面的“\E","\F",..."\Z"等目录访问到插入的U盘。

实现分析:
     1. 从/proc/partitions中找到sd*设备,记录到partitions_list[]中
     2. 对/etc/mtab中每一个挂载的sd*设备,
        如果设备存在并且挂载到"\media"下面,则对应的partitions_list[]项记录为无需重新mount,同时记录对应的目录。
        如果设备已经不存在,unmount挂载点,如果对应的目录在"\media"下面,需要删除
     3. 为partitions_list[]设置对应的目录名
     4. 挂载
        直接用busybox提供的mount(该mount将尝试/proc/filesystems里面指明的内核支持的所以文件系统),如果失败,则尝试ntfs-3g。


#include <stdio.h>
#include <mntent.h>
#include <stdbool.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define MOUNT_POINT  "/home/qianjiang/tmp/automount/media"
#define START_DISK_CHAR   'E'
#define END_DISK_CHAR     'Z'
#define MAX_PARTITION_ITEMS    (END_DISK_CHAR - START_DISK_CHAR + 1) //from E: to Z:

typedef struct{
    char devname[120];
    bool bNeedMount;
    char  disk_char;
}stPartitionItem;

static int partition_num;
static stPartitionItem partitions_list[MAX_PARTITION_ITEMS];

#define DEBUG

static void build_partitions_list(void)
{
    FILE *procpt;
    char line[100], ptname[100]/*, *s*/;
    int ma, mi, sz;

    procpt = fopen("/proc/partitions", "r");

    partition_num = 0;
    while (fgets(line, sizeof(line), procpt)) {
        if (sscanf(line, " %u %u %u %[^\n ]",
                &ma, &mi, &sz, ptname) != 4)
            continue;
        if(ptname[0] != 's' || ptname[1] != 'd') continue; //we only care about sd*
        #if 0
        for (s = ptname; *s; s++)
            continue;
        if (!isdigit(s[-1]))
            continue;
        #endif
        if(partition_num < MAX_PARTITION_ITEMS)
        {
            strcpy(partitions_list[partition_num].devname, ptname);
            partitions_list[partition_num].bNeedMount = true;
            partition_num ++;
        }
    }
    fclose(procpt);
}

//check whether dir is matched $MONT_POINT/[E-Z]
static bool reserved_dir(char * dir)
{
    char * s;

    if(strstr(dir, MOUNT_POINT) != dir) return false;
    s = dir + strlen(MOUNT_POINT);
    while(*s == '/' || *s == '\\') s ++;
    if(*s < START_DISK_CHAR || *s > END_DISK_CHAR) return false;
    s ++;
    if(*s != '\0' && *s != '/' && *s != '\\') return false;
    return true;
}

static char get_disk_char(char * dir)
{
    char * s;

    s = dir + strlen(MOUNT_POINT);
    while(*s == '/' || *s == '\\') s ++;
    return *s;
}

static stPartitionItem * find_device(char * device)
{
    int i;

    for(i = 0; i < partition_num; i ++)
    {
        if(strcmp(partitions_list[i].devname, device) == 0) return &partitions_list[i];
    }
    return NULL;
}

static void scan_mount_points(void)
{
    struct mntent  * mnt;
    FILE           * mntfd;
    struct stat buf;
    pid_t child;
    int status;

    mntfd= setmntent("/etc/mtab", "r");
    if(mntfd == NULL) return;

    while(( mnt= getmntent(mntfd)) != NULL)
    {
        if(strstr(mnt->mnt_fsname, "/dev/sd")) //we only care about sd*
        {
            if(stat(mnt->mnt_fsname, &buf)) //not exist, we should umount this point
            {
                #ifdef DEBUG
                printf("%s is not exist, unmount %s\n", mnt->mnt_fsname, mnt->mnt_dir);
                #endif
//                umount(mnt->mnt_dir);
                if(!(child = fork())){
                    execlp("umount", "umount", mnt->mnt_dir, NULL);
                }
                waitpid(child, &status, WUNTRACED);

                //check whether we should remove this dir
                if(reserved_dir(mnt->mnt_dir))
                {
                    rmdir(mnt->mnt_dir);
                }
            }
            else
            {
                //check whether this device is already mount to $MOUNT_POINT
                if(reserved_dir(mnt->mnt_dir))
                {
                    stPartitionItem * item;
                    //set flag in partitions_list[] we don't need do mount again
                    item = find_device(mnt->mnt_fsname + 5); //strlen("/dev/") is 5
                    if(item)
                    {
                        item->bNeedMount = false;
                        item->disk_char = get_disk_char(mnt->mnt_dir);
                    }
                }
            }
        }
    }                                          
               
    endmntent(mntfd);
}

static void assign_disk_char(void)
{
    int i, j;
    stPartitionItem * item;
    char disk_char = START_DISK_CHAR;
    char path[120];
   
    for(i = 0; i < partition_num; i ++)
    {
        item = &partitions_list[i];
        if(item->bNeedMount)
        {
            for(j = 0; j < partition_num; j ++)
            {
                if(item->bNeedMount == false && disk_char == item->disk_char)
                {
                    disk_char ++;
                }
            }
            //remove this dir before we use this disk_char
            sprintf(path, "%s/%c", MOUNT_POINT, disk_char);
            rmdir(path);
            item->disk_char = disk_char;
        }
        disk_char ++;
    }
}

static void do_mount(stPartitionItem * item)
{
    pid_t child;
    int status;
    char path[120];
    char devicename[100];

    sprintf(path, "%s/%c", MOUNT_POINT, item->disk_char);
    mkdir( path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH );

    sprintf(devicename, "/dev/%s", item->devname);
    //use system mount, which will try all filesystem from /proc/filesystems
    if(!(child = fork())){
        execlp("mount", "mount", devicename, path, NULL);
    }
    waitpid(child, &status, WUNTRACED);
    if(!WIFEXITED(status)) //not exited normally, maybe user stop it
    {
        rmdir(path);
        return;
    }
    if(WEXITSTATUS(status) == 0) //mount successfully
        return;
#if 0
    //try userspace filesystem: ntfs-3g
    if(!(child = fork())){
        execlp("ntfs-3g", "ntfs-3g", devicename, path, NULL);
    }
    waitpid(child, &status, WUNTRACED);
    if(WEXITSTATUS(status) == 0) //mount successfully
        return;
#endif
    //
    rmdir(path);
}


static void mount_all(void)
{
    int i;
    stPartitionItem * item;
    for(i = 0; i < partition_num; i ++)
    {
        item = &partitions_list[i];

        if(item->bNeedMount)
        {
#ifdef DEBUG
             printf("do_mount /dev/%s %s/%c\n", item->devname, MOUNT_POINT, item->disk_char);
#endif
             do_mount(item);
        }
    }
}

int main(int argc, char * argv[])
{
    build_partitions_list();
    scan_mount_points();
    assign_disk_char();
    mount_all();
    return 0;
}




 

你可能感兴趣的:(职场,休闲,automount)