当前位置: 首页 > 工具软件 > Gio > 使用案例 >

linux glib2/gio + udisks2 库处理移动设备重命名

邴俊达
2023-12-01

1、本文开发环境搭建:

库名deb系rpm系
udisks2apt install libudisks2-devyum install libudisks2-devel
glib2/gioapt install libglib2.0-devyum install glib2-devel

2、前述:重命名注意事项

1、重命名前需要先取消设备挂载,如sudo umount /mnt/user/…
2、对于不同的文件系统格式,格式化的时间长短是不同的。

如常见的ntfs、exfat、fat32格式的U盘重命名时间较短
linux等系统特有的ext2 ext3 ext4格式的设备重名名时间会比较长

3、C语言代码

1、func.h

#ifndef _FUNC_H
#define _FUNC_H

#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
#include <gio/gio.h>
#include <udisks/udisks.h>

void unmount_device(char *device,char* name);
GVolume* lookup_device_from_system_volume_monitor(char *device);
void unmount_device_finished(GMount *mount,GAsyncResult *res,gpointer data);
void rename_device(char *device);
void rename_device_finished(UDisksFilesystem *filesystem,
                                                         GAsyncResult     *res,
                                                         gpointer         data);
UDisksObject *
get_object_from_block_device (UDisksClient *client,
                              const gchar *block_device);

#endif

2、func.c

#include "func.h"
#include <sys/stat.h>
#include <string.h>

char name_str[256] = {0};//全局变量,存放命令行的第二个参数

/* @func   卸载设备:(如U盘)
 * @device 设备文件,如/dev/sdb
 * @name   命令行传入的第二个参数,即要设置的新名字
 */
void unmount_device(char *device,char* name){
	GVolume *volume;
	GMount  *mount;

	if(NULL == device)
		return;

	if(strlen(name) > 0)
		memcpy(name_str,name,256);

	if(!g_file_test(device,G_FILE_TEST_EXISTS)){	//使用glib api检测文件是否存在
		fprintf(stdout,"device doesn't exists!\n");
		exit(1);				//不存在则直接退出程序
	}

	volume = lookup_device_from_system_volume_monitor(device);
	if(NULL == volume){
		fprintf(stdout,"device cann't founded in memery!");//内存中未找到对应的物理设备
		exit(1);
	}

	mount = g_volume_get_mount(volume);			   //从卷设备查询对应的挂载信息
	if(NULL != mount){					   //设备处于挂载状态
		g_mount_unmount_with_operation(mount,G_MOUNT_UNMOUNT_NONE,NULL,
									   NULL,(GAsyncReadyCallback)unmount_device_finished,device);
	}
	
}

/* @func   使用glib2/gio库从系统磁盘监视器寻找设备@device
 * @device 设备文件,如/dev/sdb
 * @return 返回系统中@device对应的内存表示结构体
 */
GVolume* lookup_device_from_system_volume_monitor(char *device){
	if(NULL == device)
		return NULL;

	GVolumeMonitor *monitor;	//系统磁盘监控器
	GList *all_volumes;		//系统中现在所有的磁盘卷设备(GList是glib库的链表结构体)
	GList *l;			//用于遍历的指针变量
	GVolume *volume;		//保存遍历过程中的临时变量
	GVolume *match_volume = NULL;   //保存找到的卷设备
	char *path;

	monitor = g_volume_monitor_get();
	all_volumes = g_volume_monitor_get_volumes(monitor);

	for(l = all_volumes; l != NULL; l = l->next){
		volume = l->data;					//链表的数据域存放的是 GVolume* 变量
		path = g_volume_get_identifier(volume,"unix-device");	//查询卷设备的路径信息,查询结果如:"/dev/sdc1"
		if(0 == strncmp(path,device,strlen(device))){
			match_volume = g_object_ref(volume);		//增加引用计数
			g_free(path);
			break;
		}
		g_free(path);
	}

	//遍历完成,释放链表内存
	g_list_foreach(all_volumes,(GFunc)g_object_unref,NULL);		//释放链表的每个节点内存
	g_list_free(all_volumes);					//释放链表自身的内存
	g_object_unref(monitor);					//释放磁盘监控器(实际上是引用计数-1)

	return match_volume;						//返回找到的卷设备
}

/* @func  检测卸载操作是否成功,如果卸载失败则打印错误信息并退出程序
 * @mount glib2/gio库封装的结构体,存放设备的挂载点信息
 * 注:   typedef void* gpointer;
 */
void unmount_device_finished(GMount *mount,GAsyncResult *res,gpointer data){
	GError *error = NULL;
	char *device = (char*)data;
	//使用下列函数判断是否卸载成功
	if(!g_mount_unmount_with_operation_finish(G_MOUNT(mount),res,&error)){
		if(NULL != error){
			printf("%s\n",error->message);
			g_error_free(error);
			exit(0);
		}
		printf("It needs unmount device before rename,but failed!\n");
		exit(0);
	}
	//如果能走到这里,表示设备已经成功卸载了,下面开始重命名
	rename_device(device);
}

/* @func 使用udisks2 API为设置卷设备新卷标
 * @device 一般指可移动设备,如插入的U盘:/dev/sdb
 */
void rename_device(char *device){
	if(NULL == device)
		return;

	UDisksClient *client = NULL;
	UDisksBlock  *block  = NULL;
	UDisksObject *object = NULL;
	UDisksFilesystem *filesystem = NULL;

	client = udisks_client_new_sync(NULL,NULL);
	object = get_object_from_block_device(client,device);
	block  = udisks_object_get_block(object);
	filesystem = udisks_object_peek_filesystem(object);

	//udisks2 设置卷标
	udisks_filesystem_call_set_label(filesystem,name_str,
					 g_variant_new("a{sv}",NULL),
					 NULL,
				         (GAsyncReadyCallback)rename_device_finished,//回调函数
					 NULL);//传给rename_device_finished函数的参数为NULL
}

/* @func 检测设置新卷标是否成功,如果失败则打印错误信息
 */
void rename_device_finished(UDisksFilesystem *filesystem,
			    GAsyncResult     *res,
			    gpointer         data){
	GError *error = NULL;
	if(!udisks_filesystem_call_set_label_finish(filesystem,res,&error)){
		if(NULL != error){
			printf("%s\n",error->message);
			g_error_free(error);
			exit(0);
		}
		printf("Rename device failed!\n");
		exit(0);
	}
	//重命名成功,退出程序
	exit(1);
}

UDisksObject *
get_object_from_block_device (UDisksClient *client,
                              const gchar *block_device)
{
        struct stat statbuf;
        const gchar *crypto_backing_device;
        UDisksObject *object, *crypto_backing_object;
        UDisksBlock *block;

        object = NULL;

        if (stat (block_device, &statbuf) != 0)
                return object;

        block = udisks_client_get_block_for_dev (client, statbuf.st_rdev);
        if (block == NULL)
                return object;

        object = UDISKS_OBJECT (g_dbus_interface_dup_object (G_DBUS_INTERFACE (block)));
        g_object_unref (block);

        crypto_backing_device = udisks_block_get_crypto_backing_device ((udisks_object_peek_block (object)));
        crypto_backing_object = udisks_client_get_object (client, crypto_backing_device);
        if (crypto_backing_object != NULL){
                g_object_unref (object);
                object = crypto_backing_object;
        }
        return object;
}

3、test.c

#include <stdio.h>
#include <stdlib.h>
#include "func.h"

/* 代码中增加GMainLoop循环到目的在于:
 *	该程序是单线程程序,而glib2库是基于信号触发回调的,
 *	需要增加循环保证主要的功能代码能够成功执行
 */
int main(int argc, char *argv[]){
	GMainLoop *loop;

	//程序运行示例
	if(argc < 2){
		printf("eg: ./a.out /dev/sdb4 新名字\n");
		exit(1);
	}

	//创建一个主循环,NULL表示不传入任何上下文,FALSE表示不立即运行该主循环
	loop = g_main_loop_new(NULL,FALSE);
	//重命名前需要先卸载设备,后续的重命名操作放在unmount_device()内部处理
	unmount_device(argv[1],argv[2]);
	//开始运行主循环
	g_main_loop_run(loop);

	return 0;
}

4、Makefile

RM = rm
CC = gcc

#项目中需要引用glib2/gio库和udisks2库
CFLAGS = `pkg-config --cflags glib-2.0 gio-2.0 udisks2 `
LIBS = `pkg-config --libs glib-2.0 gio-2.0 udisks2`

SRC = func.c test.c
OBJECT = func.o test.o
TARGET = rename-udisk

#生成最后的二进制文件需依赖2个.o文件
$(TARGET) : $(OBJECT)
	$(CC) $^ -o $@ $(LIBS)

#依赖.c文件生产对应的.o文件
%.o : %.c
	$(CC) -c $< $(CFLAGS) -I./
#-I./表示代码里#include "func.h"需要从当前目录下查找func.h

.PHONY:clean

#make clean删除.o文件以及二进制文件
clean:
	$(RM) $(OBJECT) $(TARGET)

4、编译与运行测试

编译:make
测试:
1、插入U盘并查看其对应dev设备(假设是/dev/sdb)
2、./rename-udisk /dev/sdb 新名字

 类似资料: