プロセスによって作成され削除された/tmp/ディレクトリをどのようにコピーしますか?

プロセスによって作成され削除された/tmp/ディレクトリをどのようにコピーしますか?

私はOracle Linux 9(XFSファイルシステム)でバイナリの動作を研究しています。プロセスがこのバイナリを呼び出すと、その下にディレクトリが作成され、/tmp一部のファイルがそのディレクトリにコピーされます。このディレクトリは、プロセスが実行されるたびにランダムな名前(キーワード+ GUID)を取得します。

その後、そのディレクトリをすぐに削除します。ディレクトリを削除する前にディレクトリに含まれているファイルにアクセスしたいのですが、コマンドを実行するにはプロセス全体が早すぎます。

ディレクトリを削除する前にディレクトリを「傍受」してコピーする方法はありますか?

答え1

いつでも次の場所でアプリケーションを実行できます。

gdb --args /path/to/your/your-program and its args

unlink()unlinkat()次に、、rmdir()関数、またはシステムコールにブレークポイントを追加します。

catch syscall unlink
catch syscall unlinkat
catch syscall rmdir
run

その後、ブレークポイントに達するたびにそのディレクトリのファイルを削除することを確認し、その中のファイルを確認するか、別の場所にコピーします。cont実行を再開するには(次のブレークポイントまで)、gdbに入力してください。

rm -rf:

$ gdb -q --args rm -rf /tmp/tmp.HudBncQ4Ni
Reading symbols from rm...
Reading symbols from /usr/lib/debug/.build-id/f6/7ac1d7304650a51950992d074f98ec88fe2f49.debug...
(gdb) catch syscall unlink
Catchpoint 1 (syscall 'unlink' [87])
(gdb) catch syscall unlinkat
Catchpoint 2 (syscall 'unlinkat' [263])
(gdb) catch syscall rmdir
Catchpoint 3 (syscall 'rmdir' [84])
(gdb) run
Starting program: /bin/rm -rf /tmp/tmp.HudBncQ4Ni

Catchpoint 2 (call to syscall unlinkat), 0x00007ffff7eb6fa7 in __GI_unlinkat () at ../sysdeps/unix/syscall-template.S:120
120     ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) info registers
rax            0xffffffffffffffda  -38
rbx            0x555555569830      93824992319536
rcx            0x7ffff7eb6fa7      140737352789927
rdx            0x0                 0
rsi            0x555555569938      93824992319800
rdi            0x4                 4
rbp            0x555555568440      0x555555568440
rsp            0x7fffffffda48      0x7fffffffda48
r8             0x3                 3
r9             0x0                 0
r10            0xfffffffffffffa9c  -1380
r11            0x206               518
r12            0x0                 0
r13            0x7fffffffdc30      140737488346160
r14            0x0                 0
r15            0x555555569830      93824992319536
rip            0x7ffff7eb6fa7      0x7ffff7eb6fa7 <__GI_unlinkat+7>
eflags         0x206               [ PF IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
(gdb) x/s $rsi
0x555555569938: "test"
(gdb) info proc
process 7524
cmdline = '/bin/rm -rf /tmp/tmp.HudBncQ4Ni'
cwd = '/export/home/stephane'
exe = '/bin/rm'
(gdb) !readlink /proc/7524/fd/4
/tmp/tmp.HudBncQ4Ni
(gdb) !find /tmp/tmp.HudBncQ4Ni -ls
  1875981      4 drwx------   2 stephane stephane     4096 Aug  8 09:30 /tmp/tmp.HudBncQ4Ni
  1835128      4 -rw-r--r--   1 stephane stephane        5 Aug  8 09:30 /tmp/tmp.HudBncQ4Ni/test

ここで、ブレークポイントはunlinkat()x86_64 Linux システム内部エントリのシステムコールに置かれます。ここで、システムコールの最初の2つのパラメータはおよびレジスタにあります。test/tmp/tmp.HudBncQ4Nirdirsi

straceシステムコールを呼び出すと、プロセスにシグナル(一時停止など)を注入できますが、strace -e inject=unlink,unlinkat,rmdir:signal=STOPAFAICTは常にそうです。後ろにファイルが削除されると、システムコールが返されます。

Ctrlただし、+を使用して手動で一時停止できるように入力を遅らせることができますZ。たとえば、次のようになります。

$ strace -e inject=unlink,unlinkat,rmdir:delay_enter=5s -e unlink,unlinkat,rmdir rm -rf /tmp/tmp.HudBncQ4Ni
unlinkat(4, "test", 0^Z
zsh: suspended  strace -e inject=unlink,unlinkat,rmdir:delay_enter=10s -e  rm -rf

または、@PhilippWendlerが提案したように、次のものを使用できます。

strace -e inject=unlink,unlinkat,rmdir:retval=0 -e unlink,unlinkat,rmdir ...

または:

strace -e inject=unlink,unlinkat,rmdir:error=EACCES -e unlink,unlinkat,rmdir ...

システムコールをハイジャックし、成功(使用retval=0)または失敗(EACCESここでは意味)したふりをします。許可が拒否されました)実際に電話せずに。

gdbstraceすでに実行中のプロセスにそれぞれ使用/接続できます。また、フォークと実行を追跡し、子項目を追跡して親項目に接続し、子項目の切断を監視またはハイジャックするように指示することもできます(およびの設定を参照)。--pid <the-process-id>-p <the-process-id>-fstracefollow-*gdb

答え2

私が望むことを正確に行うinotify-toolsを使ってこのシェルスクリプトを見つけました(著者:https://unix.stackexchange.com/a/265995/536771):

#!/bin/sh

TMP_DIR=/tmp
CLONE_DIR=/tmp/clone
mkdir -p $CLONE_DIR

wait_dir() {
  inotifywait -mr --format='%w%f' -e create "$1" 2>/dev/null | while read file; do
    echo $file
    DIR=`dirname "$file"`
    mkdir -p "${CLONE_DIR}/${DIR#$TMP_DIR/}"
    cp -rl "$file" "${CLONE_DIR}/${file#$TMP_DIR/}"
  done
}

trap "trap - TERM && kill -- -$$" INT TERM EXIT

inotifywait -m --format='%w%f' -e create "$TMP_DIR" | while read file; do
  if ! [ -d "$file" ]; then
    continue
  fi

  echo "setting up wait for $file"
  wait_dir "$file" &
done

私にとっては、単純な解決策はスクリプトよりはるかに優れています。 chattr +a /tmp

これは、バイナリがフォルダの代わりに/ tmpの下に単一のファイルを生成するとスクリプトが失敗するためです。バイナリが/ tmpの下に複数のフォルダを作成しても失敗します。

編集:機能するより簡単な解決策は、次のことを実行することです。

cp -rp /source /clone

chattrが私が確認する内容を妨げます。最初のスクリプトは、/ tmpの下に作成されたディレクトリには機能しますが、/ tmpの下に生成されたファイルには機能しません。

答え3

私は過去にも同様の状況に直面していました。chattr -R -a /tmp本質的にちょうど追加された同様のものを実行したことを曖昧に覚えています/tmp。プロセスはファイル/ディレクトリを作成できますが、削除することはできません。コマンドを実行する前に慎重に確認し、できるだけ早く属性をキャンセルしてください。

答え4

私がさまざまな機能(リスニング)に使用した解決策は、興味のある機能(unlinkやfopenなど)をオーバーライドする単純な動的ライブラリを作成することです。

コンパイルしてリンクして-fPIC動的ライブラリを作成し、バイナリに挿入します。

LD_PRELOAD=/path/to/mylib.so ./binary

関連情報