我正在尝试在 Ubuntu 14.04 桌面版本上使用 Linux 内核模块来挂钩一些系统调用。
然而,当我hookwrite(unsigned int fd, const char __user *buf, size_t count)
并转为fd
filename时,我发现当我复制/home/user/1.txt
粘贴到/home/user/folder/
nautilus中时,这个文件夹中没有调用write。但是,如果我使用cp /home/user/1.txt /home/user/folder
,我可以注意到write
被调用并且文件名正是/home/user/folder/1.txt
.
我也尝试过 hook pwrite
,但在使用 nautilus 粘贴文件时仍然没有检测到调用它。
write
那么,当目的地没有调用系统调用时,nautilus 如何复制文件并粘贴到特定文件夹?
答案1
看起来 Nautilus 使用不同的方法来实现优化目的。
/ntest/testfile
假设我有一个内部有 45 个字节的测试文件:
Lorem ipsum dolor sit amet
Leroooy Jeeenkins
我想将它移动到目录中/ntest2
。为了追踪 Nautilus 到底做了什么,我可以像这样启动它(实际上,我进行了多次启动,限制不太严格,但这是一个好的开始):
strace -f -P '/ntest/testfile' -P '/ntest2/testfile' -qq nautilus
本质上,以下摘录解释了发生的情况(请注意,pipe2()
上面的命令未捕获调用 - 我根据其他跟踪会话插入了它):
openat(AT_FDCWD, "/ntest/testfile", O_RDONLY) = 35
openat(AT_FDCWD, "/ntest2/testfile", O_WRONLY|O_CREAT|O_EXCL, 0644) = 36
pipe2([37, 38], O_CLOEXEC) = 0
stat("/ntest2/testfile", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
splice(35, [0], 38, NULL, 1048576, SPLICE_F_MORE) = 45
splice(37, NULL, 36, [0], 45, SPLICE_F_MORE) = 45
close(35) = 0
close(36) = 0
Nautilus 使用splice(2)
它允许在fd
s 之间传输一些数据,而无需在内核和用户空间之间复制数据。因为man 2 splice
要求一端是管道,所以 Nautilus 创建一个带有输入文件描述符38
和输出文件描述符的管道37
。打开源文件和目标文件并创建管道后,Nautilus 用于splice()
将数据从源文件读取到管道的输入;然后第二个splice()
用于将数据从该管道写入输出文件。这种方法不像普通方法那样涉及内核到用户和用户到内核的数据read()
转换write()
。
请注意,此行为并不是 Nautilus 特有的,而是它使用的库 (glib) 特有的。看起来像这是 splice() 调用我们观察到,正如 Nautilus 使用 glib 的g_file_copy()反过来,这又调用file_copy_fallback()->splice_stream_with_progress()->do_splice()。