当前位置: 首页 > news >正文

在内核模块中通过系统调用ioctl获取ATA/SCSI硬盘序列号

open, read, ioctl 这些系统调用本来是提供给用户空间的程序访问的,所以,对传递给它的参数,它默认会认为来自用户空间,在->write()函数中,为了保护内核空间,一般会用get_fs()得到的值来和USER_DS进行比较,从而防止用户空间程序“蓄意”破坏内核空间;

而现在要在内核空间使用系统调用,此时传递给->ioctl()的参数地址就是内核空间的地址了,在USER_DS之上(USER_DS ~ KERNEL_DS),如果不做任何其它处理,在ioctl()函数中,会认为该地址超过了USER_DS范围,所以会认为是用户空间的“蓄意破坏”,从而不允许进一步的执行; 为了解决这个问题; set_fs(KERNEL_DS);将其能访问的空间限制扩大到KERNEL_DS,这样就可以在内核顺利使用系统调用了!

以下是实现代码, dbg.h中主要是DBG_ERROR这些调试宏,所有不列出来了.

#ifdef MODVERSIONS #include <linux/modversions.h> #endif #include <linux/kernel.h> #include <linux/module.h> #include <linux/skbuff.h> #include <linux/netdevice.h> #include <linux/netfilter_ipv4/ip_tables.h> #include <linux/types.h> #include <linux/random.h> #include <linux/vmalloc.h> #include <linux/init.h> #include <net/pkt_sched.h> #include <net/ip.h> #include <net/tcp.h> #include <net/udp.h> #include <net/icmp.h> #include <linux/inet.h> #include <linux/list.h> #include <linux/mm.h> #include <linux/sysctl.h> #include <linux/fs.h> #include <linux/kernel_stat.h> #include <linux/if_vlan.h> #include <linux/netfilter_bridge.h> #include <linux/hdreg.h> #include <scsi/scsi.h> #include <scsi/sg.h> #include <asm/uaccess.h> /* for put_user */ #include <asm/atomic.h> /* for put_user */ #define _rdtsc(x) {asm volatile("rdtsc" : "=A" (x));} #include "dbg.h" #define SCSI_TIMEOUT (2*HZ) #define HDD_IDENTITY_LEN 20 static int verbose = LOG_VERBOSE_LEVEL_INFO; /*****************************************************************************/ static struct file* vfs_open( const char *name ); static int vfs_close( struct file *fp ); static long vfs_ioctl( struct file *filp, u32 cmd, unsigned long arg ); int scsi_get_inquiry( const char *name, char *buf ); int ide_get_identity( const char *name, char *buf ); int hdd_get_identity( char *identity, int len ); /*****************************************************************************/ static struct file* vfs_open( const char *name ) { struct file *fp = filp_open( name , O_RDONLY, 0444 ); if ( IS_ERR( fp ) ) { DBG_ERROR( verbose, "filp_open file %s failed.\n", name ); fp = NULL; } return fp; } static int vfs_close( struct file *fp ) { return filp_close( fp, NULL ); } static long vfs_ioctl( struct file *filp, u32 cmd, unsigned long arg ) { int error = -ENOTTY; if ( !filp->f_op ) goto out; if ( filp->f_op->unlocked_ioctl ) { error = filp->f_op->unlocked_ioctl( filp, cmd, arg ); if ( error == -ENOIOCTLCMD ) error = -EINVAL; goto out; } else if ( filp->f_op->ioctl ) { lock_kernel(); error = filp->f_op->ioctl( filp->f_path.dentry->d_inode, filp, cmd, arg ); unlock_kernel(); } out: return error; } /*****************************************************************************/ /* * 取得scsi设备序列号 * 通过SCSI INQUIRY命令取得Vital Product Data(VPD)页面信息 * Page Code 80h - Unit serial number */ int scsi_get_inquiry( const char *name, char *buf ) { struct file *fp = NULL; mm_segment_t old_fs; sg_io_hdr_t io_hdr; #define VPD_INQUIRY_SIZE 0x00ff u32 data_size = VPD_INQUIRY_SIZE; u8 data[VPD_INQUIRY_SIZE]; u8 cmd[] = { INQUIRY, 1, 0x80, VPD_INQUIRY_SIZE >> 8, VPD_INQUIRY_SIZE & 0xff, 0 }; u32 sense_len = 32; u8 sense_buffer[sense_len]; int len, i, rc = -1; int copy = 0; cmd[3] = ( data_size>>8 )&0xff; cmd[4] = data_size&0xff; fp = vfs_open( name ); if ( NULL == fp ) { return -1; } memset( &io_hdr, 0, sizeof( sg_io_hdr_t ) ); io_hdr.interface_id = 'S'; /* CDB */ io_hdr.cmdp = cmd; io_hdr.cmd_len = sizeof( cmd ); /* Where to store the sense_data, if there was an error */ io_hdr.sbp = sense_buffer; io_hdr.mx_sb_len = sense_len; sense_len = 0; /* Transfer direction, either in or out. Linux does not yet support bidirectional SCSI transfers ? */ io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; /* Where to store the DATA IN/OUT from the device and how big the buffer is */ io_hdr.dxferp = data; io_hdr.dxfer_len = data_size; /* SCSI timeout in ms */ io_hdr.timeout = SCSI_TIMEOUT; // old_fs = get_fs(); // 改变ioctl只能操作用户空间的限制 set_fs( KERNEL_DS ); if ( vfs_ioctl( fp, SG_IO, ( unsigned long )&io_hdr ) < 0 ) { set_fs( old_fs ); DBG_ERROR( verbose, "vfs_ioctl %s SG_IO failed.\n", name ); goto _out; } set_fs( old_fs ); // if ( ( io_hdr.info & SG_INFO_OK_MASK ) != SG_INFO_OK ) { if ( io_hdr.sb_len_wr > 0 ) { sense_len = io_hdr.sb_len_wr; rc = -1; printk ( "INQUIRY sense data: "); for (i = 0; i < io_hdr.sb_len_wr; ++i) { if ((i > 0) && (0 == (i % 10))) printk("\n "); printk("0x%02x ", sense_buffer[i] & 0xff); } printk("\n"); goto _next; } } if ( io_hdr.masked_status ) { DBG_ERROR( verbose, "status=0x%x masked_status=0x%x\n", io_hdr.status, io_hdr.masked_status ); rc = -2; goto _out; } if ( io_hdr.host_status ) { DBG_ERROR( verbose, "host_status=0x%x\n", io_hdr.host_status ); rc = -3; goto _out; } if ( io_hdr.driver_status ) { DBG_ERROR( verbose, "driver_status=0x%x\n", io_hdr.driver_status ); rc = -4; goto _out; } _next: if ( sense_len ) { goto _out; } /* Page Length */ len = data[3]; DBG_INFO( verbose, "%s Unit Serial Number Length: %d\n", name, len ); if ( buf ) { if ( len > HDD_IDENTITY_LEN ) { DBG_ERROR( verbose, "identity buffer length %d is greater than %d\n", len, HDD_IDENTITY_LEN ); copy = 0; } else { copy = 1; } memset( buf, 0x0, HDD_IDENTITY_LEN ); } #if 0 /* Unit Serial Number */ //printk( "%s Unit Serial Number:", name ); for ( i = 4; i < ( len+4 ); i ++ ) { //printk( "%c",data[i]&0xff ); if ( copy && buf ) { *buf = data[i]&0xff; buf ++; } } //printk( "\n" ); #else if ( copy && buf ) { memcpy( buf, data + 4, len ); } #endif rc = 0; _out: vfs_close( fp ); return rc; } /* * 取得ATA设备序列号 * 直接发送HDIO_GET_IDENTITY ioctl code获取信息 */ int ide_get_identity( const char *name, char *buf ) { struct file *fp = NULL; mm_segment_t old_fs; int rc = -1; struct hd_driveid driveid; fp = vfs_open( name ); if ( NULL == fp ) { return -1; } old_fs = get_fs(); // 改变ioctl只能操作用户空间的限制 set_fs( KERNEL_DS ); if ( vfs_ioctl( fp, HDIO_GET_IDENTITY, ( unsigned long )&driveid ) < 0 ) { set_fs( old_fs ); DBG_ERROR( verbose, "vfs_ioctl %s HDIO_GET_IDENTITY failed.\n", name ); goto _out; } set_fs( old_fs ); DBG_INFO( verbose, "%s Model=%.40s, FwRev=%.8s, SerialNo=%.20s\n", name, driveid.model, driveid.fw_rev, driveid.serial_no ); if ( buf ) { memset( buf, 0x0, HDD_IDENTITY_LEN ); memcpy( buf, driveid.serial_no, HDD_IDENTITY_LEN ); } rc = 0; _out: vfs_close( fp ); return rc; } /*****************************************************************************/ int hdd_get_identity( char *identity, int len ) { if ( identity != NULL && len < HDD_IDENTITY_LEN ){ DBG_ERROR( verbose, "identity buffer length is less than %d\n", HDD_IDENTITY_LEN ); } if ( !ide_get_identity( "/dev/hda", identity ) ) { return 0; } if ( !ide_get_identity( "/dev/hdb", identity ) ) { return 0; } if ( !scsi_get_inquiry( "/dev/sda", identity ) ) { return 0; } if ( !scsi_get_inquiry( "/dev/sdb", identity ) ) { return 0; } DBG_ERROR( verbose, "can't found a exist hard disk.\n" ); return -1; } char hdd_sn[HDD_IDENTITY_LEN]; static int __init mod_init( void ) { int err = 0; pr_info( "%s.\n", __func__ ); if ( !hdd_get_identity( hdd_sn, HDD_IDENTITY_LEN) ){ printk( "HDD SerialNo: %-20s\n", hdd_sn ); } return err; } static void __exit mod_fini( void ) { pr_info( "%s.\n", __func__ ); } module_init( mod_init ); module_exit( mod_fini ); MODULE_LICENSE( "GPL" );

Makefile:

ARCH := x86 KSP := /work/linux-2.6.29.6_x86 EXTRA_CFLAGS +=-DDEBUG obj-m += hdd.o hdd-objs = main.o all: clean make -C $(KSP) M=`pwd` modules clean: make -C $(KSP) M=`pwd` clean rm -f Module.symvers rm -f modules.order

参考:
SCSI Inquiry Command
http://en.wikipedia.org/wiki/SCSI_Inquiry_Command
SCSI INQUIRY 命令的详细解说;

The sdparm utility
http://sg.danny.cz/sg/sdparm.html
lsscsi http://sg.danny.cz/scsi/lsscsi.html
The Linux sg3_utils package http://sg.danny.cz/sg/sg3_utils.html
linux下访问SCSI设备参数的几个工具, 对VPD pages有较详细的解说;

The Linux SCSI Generic (sg) HOWTO
http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/index.html
The Linux 2.4 SCSI subsystem HOWTO http://tldp.org/HOWTO/SCSI-2.4-HOWTO/index.html

相关文章:

  • 【转】GTD,高效的时间管理系统
  • 应用了归一化的预测
  • 【原创】《时代》周刊杂志2006年度人物“颁奖词”节译
  • 最简单的径向基网络
  • [导入]MsAjax Lib- Array.indexOf 函数
  • RBF预测模型
  • 软件架构训练之层次及使用
  • 解析phpwind团购模块实现
  • ASP.NET生成静态HTML页面
  • C#自定义结构的强制转换
  • 换上你的右脑袋(zz)
  • 动态添加页面的BODY OnLoad事件
  • 男人眼泪中的Ruby(二)
  • oracle序列生成器(sequence)使用的一点小注意
  • javax.servlet.ServletResponse接口(协议无关版本)
  • php的引用
  • 【Leetcode】104. 二叉树的最大深度
  • 2017 年终总结 —— 在路上
  • angular学习第一篇-----环境搭建
  • E-HPC支持多队列管理和自动伸缩
  • gcc介绍及安装
  • HTTP 简介
  • iOS筛选菜单、分段选择器、导航栏、悬浮窗、转场动画、启动视频等源码
  • JavaScript 事件——“事件类型”中“HTML5事件”的注意要点
  • macOS 中 shell 创建文件夹及文件并 VS Code 打开
  • SpringBoot几种定时任务的实现方式
  • Vim Clutch | 面向脚踏板编程……
  • 聊聊sentinel的DegradeSlot
  • 浏览器缓存机制分析
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 前端学习笔记之观察者模式
  • 写给高年级小学生看的《Bash 指南》
  • 移动端解决方案学习记录
  • 鱼骨图 - 如何绘制?
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • 400多位云计算专家和开发者,加入了同一个组织 ...
  • 带你开发类似Pokemon Go的AR游戏
  • 国内唯一,阿里云入选全球区块链云服务报告,领先AWS、Google ...
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • ​卜东波研究员:高观点下的少儿计算思维
  • ​七周四次课(5月9日)iptables filter表案例、iptables nat表应用
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • (delphi11最新学习资料) Object Pascal 学习笔记---第7章第3节(封装和窗体)
  • (Forward) Music Player: From UI Proposal to Code
  • (MATLAB)第五章-矩阵运算
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • .apk 成为历史!
  • .NET 材料检测系统崩溃分析
  • .NET设计模式(11):组合模式(Composite Pattern)