库名 | deb系 | rpm系 |
---|---|---|
udisks2 | apt install libudisks2-dev | yum install libudisks2-devel |
glib2/gio | apt install libglib2.0-dev | yum install glib2-devel |
1、重命名前需要先取消设备挂载,如sudo umount /mnt/user/…
2、对于不同的文件系统格式,格式化的时间长短是不同的。
如常见的ntfs、exfat、fat32格式的U盘重命名时间较短
linux等系统特有的ext2 ext3 ext4格式的设备重名名时间会比较长
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)
编译:make
测试:
1、插入U盘并查看其对应dev设备(假设是/dev/sdb)
2、./rename-udisk /dev/sdb 新名字