在RT-Thread中将USB存储设备设为只读,需要从设备驱动层和文件系统层双重控制。根据您的描述,仅设置设备标志可能不足够,以下是完整的解决方案:
根本原因分析
- 设备层标志(RT_DEVICE_FLAG_RDONLY):仅控制RT-Thread设备框架的访问权限,未传递到底层USB驱动。
- 打开标志(RT_DEVICE_OFLAG_RDONLY):同样被部分USB驱动忽略。
- USB大容量存储协议(MSC):需在SCSI命令层拦截写操作。
解决方案分三步实现
步骤1: 修改USB设备驱动(关键)
在USB MSC协议层拦截写命令,直接返回写保护错误。修改usb_device_msc.c中的SCSI命令处理函数:
/* 在SCSI命令分发函数中添加 */
switch (cbw->CBWCB[0]) {
case SCSI_WRITE10:
case SCSI_WRITE12:
case SCSI_WRITE16:
/* 强制返回写保护状态 */
msc->sense_data = SENSE_DATA(WRITE_PROTECTED);
rt_usbd_ep0_send_status(device); // 返回错误状态
return RT_EOK;
default:
/* 其他命令正常处理 */
break;
}
步骤2: 文件系统只读挂载
在挂载文件系统时指定只读标志:
#include
/* 挂载时添加FS_READONLY标志 */
if (dfs_mount("usbd", "/", "elm", FS_READONLY, 0) != 0) {
rt_kprintf("Mount failed!n");
}
步骤3: 读写访问双重拦截(可选加固)
在块设备操作表中拦截写操作:
static rt_err_t block_write(rt_device_t dev, rt_off_t pos, const void *buf, rt_size_t size) {
/* 强制返回写保护错误 */
return -RT_EPERM;
}
/* 在设备初始化中重写写操作函数 */
static struct rt_device_ops blk_ops = {
.read = block_read,
.write = block_write, // 指向自定义写拦截函数
};
rt_device_register(&blk_dev, "usbd", RT_DEVICE_FLAG_RDONLY);
验证方法
- 驱动层验证:插入USB后尝试写入,观察是否返回
WRITE_PROTECTED SENSE KEY。
- 文件系统验证:执行
echo "test" > /file.txt 应提示Permission denied。
- 应用层验证:在PC端连接设备,尝试格式化/删除文件应失败。
补充建议
- 物理写保护开关:如果硬件支持,可结合GPIO控制USB电源(如检测到写操作时切断供电)。
- 日志审计:在拦截写操作时记录安全事件:
rt_kprintf("[SECURITY] Write attempt blocked at %dn", rt_tick_get());
通过以上三层防护(驱动层+文件系统层+可选块设备层),可实现可靠的黑匣子功能。最终效果取决于USB驱动对SCSI命令的处理,请重点检查驱动中的usb_device_msc.c实现。
在RT-Thread中将USB存储设备设为只读,需要从设备驱动层和文件系统层双重控制。根据您的描述,仅设置设备标志可能不足够,以下是完整的解决方案:
根本原因分析
- 设备层标志(RT_DEVICE_FLAG_RDONLY):仅控制RT-Thread设备框架的访问权限,未传递到底层USB驱动。
- 打开标志(RT_DEVICE_OFLAG_RDONLY):同样被部分USB驱动忽略。
- USB大容量存储协议(MSC):需在SCSI命令层拦截写操作。
解决方案分三步实现
步骤1: 修改USB设备驱动(关键)
在USB MSC协议层拦截写命令,直接返回写保护错误。修改usb_device_msc.c中的SCSI命令处理函数:
/* 在SCSI命令分发函数中添加 */
switch (cbw->CBWCB[0]) {
case SCSI_WRITE10:
case SCSI_WRITE12:
case SCSI_WRITE16:
/* 强制返回写保护状态 */
msc->sense_data = SENSE_DATA(WRITE_PROTECTED);
rt_usbd_ep0_send_status(device); // 返回错误状态
return RT_EOK;
default:
/* 其他命令正常处理 */
break;
}
步骤2: 文件系统只读挂载
在挂载文件系统时指定只读标志:
#include
/* 挂载时添加FS_READONLY标志 */
if (dfs_mount("usbd", "/", "elm", FS_READONLY, 0) != 0) {
rt_kprintf("Mount failed!n");
}
步骤3: 读写访问双重拦截(可选加固)
在块设备操作表中拦截写操作:
static rt_err_t block_write(rt_device_t dev, rt_off_t pos, const void *buf, rt_size_t size) {
/* 强制返回写保护错误 */
return -RT_EPERM;
}
/* 在设备初始化中重写写操作函数 */
static struct rt_device_ops blk_ops = {
.read = block_read,
.write = block_write, // 指向自定义写拦截函数
};
rt_device_register(&blk_dev, "usbd", RT_DEVICE_FLAG_RDONLY);
验证方法
- 驱动层验证:插入USB后尝试写入,观察是否返回
WRITE_PROTECTED SENSE KEY。
- 文件系统验证:执行
echo "test" > /file.txt 应提示Permission denied。
- 应用层验证:在PC端连接设备,尝试格式化/删除文件应失败。
补充建议
- 物理写保护开关:如果硬件支持,可结合GPIO控制USB电源(如检测到写操作时切断供电)。
- 日志审计:在拦截写操作时记录安全事件:
rt_kprintf("[SECURITY] Write attempt blocked at %dn", rt_tick_get());
通过以上三层防护(驱动层+文件系统层+可选块设备层),可实现可靠的黑匣子功能。最终效果取决于USB驱动对SCSI命令的处理,请重点检查驱动中的usb_device_msc.c实现。
举报