プロセス終了後に自動的に破棄される一時フォルダー


回答:


12

一時ファイルの場合、問題の例ではそれを作成し、それをディレクトリからリンク解除して(「非表示」にします)、スクリプトがファイル記述子を閉じると(おそらく終了時に)、ファイルが占めるスペースシステムによって再利用可能になります。これは、Cなどの言語で一時ファイルを処理する一般的な方法です。

私の知る限り、同じ方法でディレクトリを開くことはできません。少なくとも、ディレクトリを使用できるようにする方法はありません。

スクリプトの終了時に一時ファイルとディレクトリを削除する一般的な方法は、クリーンアップEXITトラップをインストールすることです。以下のコード例では、ファイル記述子を完全に操作する必要がありません。

tmpdir=$(mktemp -d)
tmpfile=$(mktemp)

trap 'rm -f "$tmpfile"; rm -rf "$tmpdir"' EXIT

# The rest of the script goes here.

または、クリーンアップ関数を呼び出すこともできます。

cleanup () {
    rm -f "$tmpfile"
    rm -rf "$tmpdir"
}

tmpdir=$(mktemp -d)
tmpfile=$(mktemp)

trap cleanup EXIT

# The rest of the script goes here.

EXITトラップ受信時に実行されることはありませんKILLないクリーンアップが生じないことを意味する(捕捉することができない)信号は、次に行わ。ただし、INTor TERM信号による終了時(bashまたはkshで実行している場合は、他のシェルEXITではtrapコマンドラインでこれらの信号を追加したい場合)、またはスクリプトの最後に到達したか、exitコール。


5
すでにリンクされていない一時ディレクトリを使用できないのはシェルだけではなく、Cプログラムも使用できません。問題は、リンクされていないディレクトリにファイルを含めることができないことです。リンクされていない空のディレクトリを作業ディレクトリとして使用できますが、ファイルを作成しようとするとエラーが発生します。
デロベルト

1
@derobertそして、そのようなリンクされていないディレクトリには.and ..エントリさえありません。(Linuxでテストされましたが、プラットフォーム間で一貫しているかどうかはわかりません。)
kasperd


1
スクリプトがexec another-command明らかに呼び出した場合も、EXITトラップは実行されないことに注意してください。
ステファンChazelas


6

スクリプトが終了したときに実行されるシェル関数を記述します。以下の例では、これを「クリーンアップ」と呼び、次のように終了レベルで実行されるトラップを設定します。0 1 2 3 6

trap cleanup 0 1 2 3 6

cleanup()
{
  [ -d $TMP ] && rm -rf $TMP
}

詳細については、この投稿を参照してください。


それらはない「終了レベル」が、信号番号、およびあなたがにリンクしている質問への答えだけと説明しています。トラップはcleanup、クリーン終了(0)の前、およびSIGHUP(1)、SIGINT(2)、SIGQUIT(3)、およびSIGABRT(6)を受信すると実行されます。SIGTERM、SIGSEGV、SIGKILL、SIGPIPEなどが原因でスクリプトが終了すると実行されませんcleanup。これは明らかに不十分です。
モビー

6

その後にchdirしてから削除できますが、後でその中のパスを使用しようとしない場合は、次のようにします。

#! /bin/sh
dir=`mktemp -d`
cd "$dir"
exec 4>file 3<file
rm -fr "$dir"

echo yes >&4    # OK
cat <&3         # OK

cat file        # FAIL
echo yes > file # FAIL

私はチェックしていませんが、Cでopenat(2)をファイルシステムに存在しないディレクトリで使用すると、おそらく同じ問題です。

rootでLinuxを使用している場合は、別の名前空間で、そのmount -t tmpfs tmpfs /dir中で遊ぶことができます。

スクリプトが(たとえば、SIGKILLで)不潔な出口に強制された場合、標準的な回答(EXITにトラップを設定)は機能しません。機密データが滞る可能性があります。

更新:

名前空間のアプローチを実装する小さなユーティリティを次に示します。でコンパイルする必要があります

cc -Wall -Os -s chtmp.c -o chtmp

そして与えられたCAP_SYS_ADMINファイル機能(ルートとして)

setcap CAP_SYS_ADMIN+ep chtmp

(通常の)ユーザーとして実行した場合

./chtmp command args ...

ファイルシステムの名前空間の共有を解除し、tmpfsファイルシステムをにマウントし、/proc/sysvipcそこにchdirしてcommand、指定された引数で実行します。commandではない継承するCAP_SYS_ADMIN能力を。

そのファイルシステムはcommand、から開始されていない別のプロセスからアクセスできなくなりcommand、どのようにしてその子が死ぬと、魔法のように(その中に作成されたすべてのファイルとともに)消えます。これはマウントネームスペースの共有を解除するだけであることに注意してくださいcommand。同じユーザーが実行する他のプロセスとの間にハードバリアはありません。それらは、を介してptrace(2)/proc/PID/cwdまたは他の方法で、ネームスペース内に忍び込むことができます。

/proc/sysvipcもちろん、「役に立たない」のハイジャックはばかげていますが、代わり/tmpに、削除する必要のある空のディレクトリでスパムを送信するか、この小さなプログラムをフォークと待機で非常に複雑にする必要がありました。あるいは、dir例えばに変更することができます。/mnt/chtmpインストール時にrootによって作成されます。ユーザー設定可能にしたり、ユーザー所有のパスに設定したりしないでください。シンボリックリンクトラップや、時間を費やす価値のない他の毛深いものにさらされる可能性があります。

chtmp.c

#define _GNU_SOURCE
#include <err.h>
#include <sched.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mount.h>
int main(int argc, char **argv){
        char *dir = "/proc/sysvipc";    /* LOL */
        if(argc < 2 || !argv[1]) errx(1, "usage: %s prog args ...", *argv);
        argv++;
        if(unshare(CLONE_NEWNS)) err(1, "unshare(CLONE_NEWNS)");
        /* "modern" systemd remounts all mount points MS_SHARED
           see the NOTES in mount_namespaces(7); YUCK */
        if(mount("none", "/", 0, MS_REC|MS_PRIVATE, 0))
                err(1, "mount(/, MS_REC|MS_PRIVATE)");
        if(mount("tmpfs", dir, "tmpfs", 0, 0)) err(1, "mount(tmpfs, %s)", dir);
        if(chdir(dir)) err(1, "chdir %s", dir);
        execvp(*argv, argv);
        err(1, "execvp %s", *argv);
}

1
rootでなくても、新しいユーザー名前空間を作成し、その中にtmpfsマウントを行うことで、名前空間でこれを行うことができます。新しいディレクトリへのアクセスを外の世界に密輸することは少し注意が必要ですが、可能であるべきです。
R .. GitHub ICEのヘルプ停止

それでもCAP_SYS_ADMINが必要です。私はそれを行う小さなsetcap対応ユーティリティのアイデアを持っています。それで答えを更新します。
qubert 2018年

1
カーネルが禁止されないようにロックダウンされていない限り、ユーザー名前空間の作成は特権操作ではありません。基本的な設計は、通常のユーザーが特別な機能なしで実行できるように安全であると想定されているようなものです。しかし、多くのディストロがそれを無効にするほど十分な攻撃面/リスクがあると思います。
R .. GitHub ICEのヘルプを停止

端末で試してみました。一部の一時ディレクトリ、rm $PWD作業では、シェルはまだそのディレクトリにあります。ただし、この「フォルダ」に新しいファイルを入れることはできません。ファイル&3、&4を使用した読み取り/書き込みのみが可能です。したがって、これはまだ「一時ファイル」であり、「一時フォルダ」ではありません。
ボブジョンソン

@BobJohnsonそれは私が私の答えですでに言っていたものと
同じです

0

特定のシェルが必要ですか?

zshがオプションの場合は、以下をお読みくださいzshexpn(1)

<(...)の代わりに=(...)を使用すると、引数として渡されるファイルは、リストプロセスの出力を含む一時ファイルの名前になります。これは、入力ファイルでlseek(を参照lseek(2))を期待するプログラムの<フォームの代わりに使用できます。

[...]

もう一つの問題は、いつでも一時ファイルを必要と置換したジョブはケースを含め、シェルによって否認されて発生する&!&|置換を含むコマンドの最後に表示されます。その場合、シェルにはジョブのメモリがなくなるため、一時ファイルはクリーンアップされません。回避策は、たとえばサブシェルを使用することです。

(mycmd =(myoutput)) &!

分岐したサブシェルはコマンドが終了するのを待ち、一時ファイルを削除します。

プロセスの置換が適切な時間持続することを保証する一般的な回避策は、匿名のシェル関数(関数のスコープですぐに実行されるシェルコード)にパラメーターとして渡すことです。たとえば、次のコード:

() {
   print File $1:
   cat $1
} =(print This be the verse)

次のようなものを出力します

File /tmp/zsh6nU0kS:
This be the verse

たとえば、これをライフル(レンジャーファイルマネージャーの一部)で使用してファイルを復号化し、一時ファイルでライフルを実行します。一時ファイルは、サブプロシージャが終了すると削除されます。(設定することを忘れないでください$TERMCMD

# ~/.config/ranger/rifle.conf
...
!ext exe, mime octet-stream$, has gpg, flag t = () { rifle -f F "$1" } =(gpg -dq "$1")
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.