0

I'm trying to hook some system calls using linux kernel module on Ubuntu 14.04 Desktop version.

However, when I hooked write(unsigned int fd, const char __user *buf, size_t count) and turned fd into filename, I found that when I copy /home/user/1.txt and paste to /home/user/folder/ in nautilus, no write was called in this folder. However, if I use cp /home/user/1.txt /home/user/folder, I can notice write is called and the filename is exactly /home/user/folder/1.txt.

I've tried hook pwrite also but still no detection of calling it when paste file using nautilus.

So, how does nautilus copy file and paste to specific folder when no write system call is called on destination?

1

Looks like Nautilus uses different approach for optimization purposes.

Say I have a test file /ntest/testfile with 45 bytes inside:

Lorem ipsum dolor sit amet
Leroooy Jeeenkins

I want to move it into directory /ntest2. To trace what exactly Nautilus does, I can launch it like this (actually, I did multiple launches with less strict limitations, but this is a good start):

strace -f -P '/ntest/testfile' -P '/ntest2/testfile' -qq nautilus

Essentially, the following excerpt explains what happens (note that pipe2() call is not captured by the command above - I inserted it based on other tracing sessions):

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 uses splice(2) which allows transferring some data between fds without copying it between kernel and user spaces. Because man 2 splice requires one of ends to be a pipe, Nautilus creates a pipe with input file descriptor 38 and output file descriptor 37. After opening source and destination files and creating the pipe, Nautilus uses splice() to read data from source file to the pipe's input; then the second splice() is used to write data from this pipe to the output file. This approach does not involve kernel-to-user and user-to-kernel data transitions as it would be with ordinary read()-write() approach.

Note, that this behavior is not specific to Nautilus, but rather to the library it uses (glib). It looks like this is the splice() call we observe, as Nautilus uses glib's g_file_copy() which, in turn, calls file_copy_fallback() -> splice_stream_with_progress() -> do_splice().

  • Thanks man! Is there a way to get the data from splice? – user6456568 Nov 25 '18 at 14:06
  • Splice is not an object, it is an operation, so you can't get data from it. At the beginning of first splice() call the data is not even read from original file, and at the end of the second splice() call the data is already written to the destination file. – Danila Kiver Nov 25 '18 at 19:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.