分类: LINUX
2007-07-11 00:30:09
对于磁盘读写加解密实现,现在不得不摒弃前面的一些想法。
但总体思路仍然是:高速缓存中保证是明文,写入设备时,数据加密存储。
对于修改generic_make_request这个函数。
读操作中,如果要从设备读的话 readpage->block_read_full_page->submit_bh->generic_make_request
写操作中,(除开一些进程强制刷新调用sync方法等)都是用
bdflush->write_locked_buffers->submit_bh->generic_make_request
两者都用到了generic_make_request。
开始希望在generic_make_request分按传入的参数是写or读的方法分别进行加解密。那么也可以省去read、write需要从设备读这些情况下的解密操作了。
可是事实上我忽略了一个问题。generic_make_request只是将buffers读写请求提交给驱程,如果是写,可以先加密再提交。而读,这时是还没有从设备上读出来的。所以这个思路不对。
还是回到了原来的一个想法,在b_end_io上做文章,很感谢suoguang特意发邮件给我一些建议,并且很肯定地也提到了这个方法:即调用buffer_head结构进行读写,bh的b_end_io成员函数如果被设置,那么操作系统接收到磁盘读完成的中断后,会调用b_end_io, 进行后期处理.那么我们可以在这里做一些修改。
读操作需要从设备读的时候解密:
方法1:利用b_end_io方法
在block_read_full_page中
for (i = 0; i < nr; i++) {
struct buffer_head * bh = arr[i];
lock_buffer(bh);
set_buffer_async_io(bh);
}
/* Stage 3: start the IO */
for (i = 0; i < nr; i++) {
struct buffer_head * bh = arr[i];
...
submit_bh(READ, bh);
}
调用了set_buffer_async_io(bh)为提交给submit_bh(READ, bh)的bh设置b_end_io的方法:设置了bh->b_end_io = end_buffer_io_async(异步读)
我们可以把这里的b_end_io改成包含有”解密”功能的I/O操作完毕对bh操作的自定义方法。
需要修改block_read_full_pag函数(不知道还有没有其他机制会调用这个函数?)
方法2:修改do_generic_file_read中的readpage代码段
readpage:
/* ... and start the actual read. The read will unlock the page. */
error = mapping->a_ops->readpage(filp, page);
if (!error) {
if (Page_Uptodate(page))
goto page_ok;
/* Again, try some read-ahead while waiting for the page to finish.. */
generic_file_readahead(reada_ok, filp, inode, page);
wait_on_page(page);
if (Page_Uptodate(page))
goto page_ok;
error = -EIO;
}
在我理解是调用了readpage(filp, page)方法后,而readpage并没有等到读完就返回了,实际上它只是发出读取请求,真正读完要到下面的:
if (!error) {
...
wait_on_page(page);
...
}
wait_on_page,会等到该页面被真正读入完成后才返回,在这期间会睡眠,将cpu让给其它进程。 那么是否可以在wait_on_page后,再来解密page->buffers。(但是不确定这里buffers是否都已经读完毕了,和异步读是否有冲突,值得考虑一下。)
写操作需要考虑两个方面:
Ⅰ.写中有读,欲写先读
在prepare_write中,当需要写的块没有被缓存的时候,会先调用ll_rw_block(READ, 1, &bh)来从磁盘读入块的内容。
if (!buffer_uptodate(bh) &&
(block_start < from || block_end > to)) {
ll_rw_block(READ, 1, &bh);
*wait_bh++=bh;
但是这里的bh->b_end_io是在ll_rw_block函数中定义的,为end_buffer_io_sync(同步读)。那么这里我们也可以模仿read操作,自定义b_end_io方法来达到解密功能。
1. 重新自定义一个ll_rw_block()
与原ll_rw_block()均相同,除了在其中自定义个b_end_io方法
2. 修改成
ll_rw_block(READ, 1, &bh);
bh->b_end_io = 自定义IO方法;
因为ll_rw_block也只是调用函数将bh提交给驱程,是否这里后面对b_end_io的定义可以覆盖ll_rw_block中定义的?(还在悬疑中)
Ⅱ.写操作加密
加密后写入设备不是很难做到,可以修改generic_make_request函数,在bh没有提交给驱程之前,将其加密。
可是加密后刷新到块设备的同时,buffers里的数据也变成密文了。而我们需要保证高速缓存中的应当为明文。这里如何把buffers中的密文改回来呢?
解密-〉加密后的buffers数据
1. 利用b_end_io方法
bdflush中调用write_locked_buffers:
bh->b_end_io = end_buffer_io_sync;
submit_bh(WRITE, bh);
自定义bh ->b_end_io方法中去做解密,修改generic_make_request完成加密功能,在加密写入设备完成后,便会调用自定义b_end_io方法解密buffers中的数据。
2. 分配新buffers(参考create_bounce)
在写入设备前,在generic_make_request中分配new_buffer,和new_bh.让new_bh指向原bh。new_bh和原来的bh几乎相同,让原bh所指的数据区内容填充new_bh的数据区内容,再将new_bh的数据区加密后将new_bh传给__make_request提交给驱程,且需要自定义new_bh的b_end_io方法.这样写入设备的便是加密后的数据,而高速缓存中仍然是明文。
因为我们最终实现加解密功能都是调用加解密设备。
这里2种方法的利弊是:
对于方法1:
1. 在generic_make_request做加密时需要调用一次fpga设备进行加密
2. 把加密的数据写入sd卡
3. 在b_end_io方法中解密时再次调用fpga设备进行解密
共访问外设3次,速度大大降低
需要修改write_locked_buffers函数(不知道还有没有其他机制会调用这个函数?)
对于方法2:
1. 对于原bh,本来是需要加密写入设备的。可是利用了new_bh,那么原bh就不应该加密且写入设备了。在传入generic_make_request前,原bh的状态为BH_Locked = 1,BH_Dirty = 0.且设置了b_end_io方法。那么应该将其在加密传入__make_request之前就直接执行b_end_io方法,将其状态锁释放,并从链表中移除,使其不用写入设备。可是这里一旦释放了锁,是否会有别的进程会写它?而与写入设备的new_bh的数据不一致了?
2. 对于new_bh,b_end_io方法是否和原bh同样就可以了,还是需要自定义一个新的end_io方法来实现一些细节。有待详细考虑。
需要处理细节太多。对原bh和new_bh需要不同特殊处理。可是只需要访问外设2次。
并且b_end_io我们要确定这个函数可以调用驱动来实现硬件机制加解密