7

I have a script, this script creates a dump in /path/of/directory and deletes it instantly. I want to copy that dump before it gets deleted.

I tried;

dump=$(ls -a | grep -E "^[0-9]+$")
cp $dump fulldump

But I only got a half of the script, not the full one.

Erikli
  • 429
  • 2
  • 6
  • 11
  • You could probably create an eBPF program that would hook the unlink syscall and prevent the file from being deleted (assuming that you have root). – Nonny Moose Sep 05 '22 at 02:46
  • 10
    This question is nonsensical. Edit the script so it doesn't delete the file. – miken32 Sep 05 '22 at 15:05
  • 4
    attention answerers: the file in question is actually a named pipe, according to a comment by OP. – Esther Sep 05 '22 at 17:46
  • 7
    Result of the comments: I recommend to open this as a new question, but with the following info stated in the question: a) The file in question is actually a named pipe, and b) The script in question is a third-party script that is not allowed to be edited. A fitting title might be "How to sniff and dump traffic in a named pipe without changing the script that sets up and uses the pipe". (Prepare for answers like "it's impossible", but maaybe someone knows a trick or two more than me.) – orithena Sep 05 '22 at 18:28
  • Could you recover the deleted file to a safe location? https://unix.stackexchange.com/questions/80270/unix-linux-undelete-recover-deleted-files – MonkeyZeus Sep 06 '22 at 19:38
  • You can use inotify (see https://unix.stackexchange.com/questions/134615/using-inotify-to-monitor-access-to-a-file) to see when the file gets saved to, and then suspend or freeze the other process, (see https://unix.stackexchange.com/questions/2107/how-to-suspend-and-resume-processes) - copy the file, and then unfreeze the other process. – abcsec Sep 05 '22 at 12:43
  • This question is one big XY problem. You've concealed the actual issue (how to tap into a named pipe, I think?) behind a made up problem (how to copy a file which gets deleted instantly). The question you've asked was answered yet you're not even a bit closer to solving your original problem. – gronostaj Sep 07 '22 at 19:15
  • @gronostaj The actual question has been posted. You've described the real problem quite well ("how to tap into a named pipe"); would you mind editing that question to be clearer? – wizzwizz4 Sep 08 '22 at 16:52

4 Answers4

20

You can use chattr to prevent file deletion (while allowing file creation and modification), and other handy permissions

For directory:

sudo chattr -R +a /TEMP/

or if just the (existing) file:

sudo chattr +a /TEMP/youCantDeleteMe.txt

To revert permissions use -a in place of +a

$ mkdir Documents/TEMP && cd $_
$ echo can edit > youCantDeleteMe.txt 

$ sudo chattr -R +a ../TEMP/

$ echo after plusA on file>> youCantDeleteMe.txt $ cat youCantDeleteMe.txt can edit after plusA on file

$ sudo rm youCantDeleteMe.txt rm: cannot remove 'youCantDeleteMe.txt': Operation not permitted

$ ls -lA -rw-rw-r-- 1 v v 25 Sep 5 05:59 youCantDeleteMe.txt $ echo . > youCantDeleteMe.txt bash: youCantDeleteMe.txt: Operation not permitted

$ touch somebody $ ls -lA -rw-rw-r-- 1 v v 0 Sep 5 06:04 somebody -rw-rw-r-- 1 v v 25 Sep 5 05:59 youCantDeleteMe.txt

$ sudo rm somebody rm: cannot remove 'somebody': Operation not permitted

$ sudo chattr +a ./undel.txt chattr: No such file or directory while trying to stat ./undel.txt

And to later remove the file

$ sudo chattr -R -a ../TEMP/
$ ls -lA
drwxrwxr-x 2 v v 4096 Sep  5 06:17 .
drwxr-xr-x 7 v v 4096 Sep  5 05:57 ..
-rw-rw-r-- 1 v v    0 Sep  5 06:04 somebody
-rw-rw-r-- 1 v v   25 Sep  5 05:59 youCantDeleteMe.txt

$ rm -rf ./* $ ls -la drwxrwxr-x 2 v v 4096 Sep 5 06:17 . drwxr-xr-x 7 v v 4096 Sep 5 05:57 ..

  • It worked and didn't delete the file. But since my dump is fifo (pipe) file, I can't read/write/copy anything in it. Is there a way to read/write/copy? – Erikli Sep 06 '22 at 09:56
  • 1
    @Erikli I recommend asking that as a separate question; it's an interesting one, but the answer is very different. – wizzwizz4 Sep 07 '22 at 22:11
  • @wizzwizz4 I have created it; https://askubuntu.com/q/1428335/1614432 – Erikli Sep 08 '22 at 16:30
19

Congratulations, seems like you have created a race condition.

  • Two processes are racing to perform operations upon the same file.

  • Generally, race conditions are considered a bug for exactly the reason you encountered -- the results are unpredictable.

There are several ways to fix a race condition (semaphores, lockfiles, delays etc.) Some can involve fairly substantial coding.

However, in your specific case, you asked about a script. Happily, you can edit scripts. The simplest solution is to edit your script to place the dump in your preferred location.

user535733
  • 68,830
  • The script isn't open source, I can't edit it. – Erikli Sep 04 '22 at 15:22
  • 4
    Then you cannot fix the race condition. File a bug with the developers of that software. Or keep trying to copy the file repeatedly -- you will get a different amount of the dump each time. At some point, you might get the whole thing. – user535733 Sep 04 '22 at 15:24
  • I guess I have got the whole file with mv $dump fulldump command but I cannot see it because it says its a pipe file. – Erikli Sep 04 '22 at 15:25
  • 15
    A "pipe file" would be a named pipe (search for that term; great tutorials are out there). That's vital information that needs to be in your Question above, it changes the possible answers entirely: Accessing the data might be as simple as cat < $dump – user535733 Sep 04 '22 at 15:32
  • 12
    Adding to the comment by @user535733: It also changes the chances of getting the file contents (without stealing that data from the script!) to zero: It isn't a file, it's a named pipe. Which can be read from and written to like a file, but there's a crucial difference: The data is never written to disk and everything can only be read once. So if OP manages to read (partial) data from that named pipe, this data is then lost to the pipe consumer in the script! – orithena Sep 05 '22 at 10:58
  • 4
    @Erikli A script implies readable plain text, not a binary executable. If that's true, can you copy it, change the copy, and change the system to use your copy instead of the original? That might be as simple as renaming the original (as a backup - don't delete it) and giving your version that same name in the same location instead. (now the same code runs your version, even if it's hard-coded) – AaronD Sep 06 '22 at 15:22
15

The strace utility is commonly used to show the system calls made by a process, but it can also intercept them.

Try:

strace -f -e inject=unlink,unlinkat:retval=0 /the/script script-args...

Then every time the process or its children invokes the unlink or unlinkat system calls (which are the normal ways to delete a file), strace will intercept it and cause it to return 0, indicating success, but without actually doing anything.

There will be a lot of output tracing all the system calls. You can add -e trace=unlink,unlinkat to display those calls only, or -o logfile to send the whole output to a file (possibly /dev/null). Make sure these arguments go before /the/script on the command line.

Note this will prevent the script from deleting any files at all; hopefully that's okay. There's a -P option to strace that is supposed to restrict the tracing to calls that access a given file, but I couldn't get it to work correctly in a simple test.

Other variants are also possible: for instance, you can make the unlink/unlinkat system calls fail with an error code, or have them send SIGSTOP to the process so you can poke around before continuing it. See the strace man page for the details on all its capabilities.

  • Wow! Great information. If I had read the man page for strace closely, I still probably would not have realized how useful that could be. – Mark Stewart Sep 07 '22 at 22:43
6

Depending on the speed with which the dump is opened and then written, you may be able to create a hard link to it while it is being written. This hardlink will ensure that the data space does not get deallocated when the file is deleted using its original name. E.g.:

your-script &
ln /path/of/directory/dumpfile /some/other/path/on/the/same/filesystem
wait   #  wait until your-script exits
do-something-with /some/other/path/on/the/same/filesystem