需求:挂载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;
}