Cartella temporanea che si è distrutta automaticamente dopo l'uscita dal processo


Risposte:


12

Nel caso di un file temporaneo, il tuo esempio nella domanda lo creerebbe, quindi lo scollegerebbe dalla directory (facendolo "scomparire"), e quando lo script chiude il filedescriptor (probabilmente al termine), lo spazio occupato dal file sarebbe recuperabile dal sistema. Questo è un modo comune per gestire i file temporanei in lingue come C.

Per quanto ne so, non è possibile aprire una directory allo stesso modo, almeno non in alcun modo che renderebbe utilizzabile la directory.

Un modo comune per eliminare file e directory temporanei al termine di uno script è l'installazione di una EXITtrappola di pulizia . Gli esempi di codice riportati di seguito evitano di dover destreggiarsi completamente con i filedescriptor.

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

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

# The rest of the script goes here.

Oppure puoi chiamare una funzione di pulizia:

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

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

trap cleanup EXIT

# The rest of the script goes here.

La EXITtrappola non verrà eseguita alla ricezione del KILLsegnale (che non può essere intrappolato), il che significa che non verrà eseguita alcuna pulizia. Verrà comunque eseguito al termine a causa di un segnale INTo TERM(se in esecuzione con basho ksh, in altre shell potresti voler aggiungere questi segnali dopo EXITnella trapriga di comando) o quando esci normalmente a causa dell'arrivo alla fine dello script o dell'esecuzione di un exitchiamata.


5
Non è solo la shell che non può usare directory temporanee già non collegate, né i programmi C. Il problema è che le directory non collegate non possono contenere file. Puoi avere una directory vuota non collegata come directory di lavoro, ma qualsiasi tentativo di creare un file genererà un errore.
derobert

1
@derobert E una directory così non collegata non ha nemmeno le voci .e ... (Testato su Linux, non so se sia coerente su tutte le piattaforme.)
Kasperd,


1
Si noti che la trap EXIT non viene eseguita neanche se lo script chiama exec another-commandovviamente.
Stéphane Chazelas,


6

Scrivi una funzione shell che verrà eseguita al termine dello script. Nell'esempio seguente lo chiamo 'cleanup' e imposto una trap da eseguire ai livelli di uscita, come: 0 1 2 3 6

trap cleanup 0 1 2 3 6

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

Vedi questo post per maggiori informazioni.


Questi non sono "livelli di uscita" ma numeri di segnale e la risposta alla domanda a cui stai collegando spiega proprio questo. La trappola verrà eseguita cleanupprima di un'uscita pulita (0) e alla ricezione di SIGHUP (1), SIGINT (2), SIGQUIT (3) e SIGABRT (6). essa non eseguito cleanupquando lo script si chiude a causa di SIGTERM, SIGSEGV, SIGKILL, SIGPIPE, ecc Questo è chiaramente carente.
mosvy,

6

Puoi chdir in esso e quindi rimuoverlo, a condizione che non provi a utilizzare i percorsi al suo interno in seguito:

#! /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

Non ho verificato, ma è probabilmente lo stesso problema quando si utilizza openat (2) in C con una directory che non esiste più nel file system.

Se sei root e su Linux, puoi giocare con uno spazio dei nomi separato e mount -t tmpfs tmpfs /diral suo interno.

Le risposte canoniche (imposta una trappola su EXIT) non funzionano se lo script viene forzato in un'uscita impura (ad es. Con SIGKILL); che può lasciare in giro dati sensibili.

Aggiornare:

Ecco una piccola utility che implementa l'approccio dello spazio dei nomi. Dovrebbe essere compilato con

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

e date CAP_SYS_ADMINle capacità dei file (come root) con

setcap CAP_SYS_ADMIN+ep chtmp

Quando eseguito (come un normale) utente come

./chtmp command args ...

annullerà la condivisione del suo spazio dei nomi di filesystem, monterà un filesystem tmpfs /proc/sysvipc, lo inserirà in chdir ed eseguirà commandcon gli argomenti forniti. commandsarà non ereditare le CAP_SYS_ADMINfunzionalità.

Quel filesystem non sarà accessibile da un altro processo da cui non è stato avviato commande scomparirà magicamente (con tutti i file che sono stati creati al suo interno) quando commande i suoi figli muoiono, indipendentemente da come ciò accada. Si noti che si tratta solo di annullare la condivisione dello spazio dei nomi mount - non ci sono barriere tra commande altri processi eseguiti dallo stesso utente; potevano ancora intrufolarsi nel suo spazio dei nomi tramite ptrace(2), /proc/PID/cwdo con altri mezzi.

Il dirottamento dell '"inutile" /proc/sysvipcè, ovviamente, sciocco, ma l'alternativa sarebbe stata quella di inviare spam /tmpcon directory vuote che avrebbero dovuto essere rimosse o complicare notevolmente questo piccolo programma con fork e attese. In alternativa, dirpuò essere modificato ad es. /mnt/chtmpe lo hanno creato da root all'installazione; non renderlo configurabile dall'utente e non impostarlo su un percorso di proprietà dell'utente in quanto ciò potrebbe esporvi a trappole symlink e altre cose pelose su cui non vale la pena spendere tempo.

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
Anche se non sei root, puoi farlo con gli spazi dei nomi creando un nuovo spazio dei nomi utente e facendo montare il tmpfs al suo interno. Il contrabbando di accesso al nuovo dir verso il mondo esterno è un po 'complicato ma dovrebbe essere possibile.
R .. GitHub smette di aiutare ICE il

Ciò richiede ancora CAP_SYS_ADMIN. Ho l'idea di una piccola utility abilitata a setcap che lo farà, aggiornerò la risposta con essa.
qubert

1
A meno che il kernel non sia stato bloccato per impedirlo, la creazione di spazi dei nomi utente non è un'operazione privilegiata. Il design sottostante è tale che dovrebbe essere sicuro per consentire agli utenti ordinari di fare a meno di alcuna capacità speciale. Tuttavia, esiste una superficie di attacco / rischio sufficiente che molte distro lo disabilitano, credo.
R .. GitHub smette di aiutare ICE l'

Ho provato nel terminale. In alcune directory temporanee, rm $PWDlavoro, shell è ancora in quella directory. Ma non è possibile inserire nuovi file in questa "cartella". Solo tu puoi fare è leggere / scrivere con il file & 3, e 4. Quindi questo è ancora "file temporaneo", non "cartella temporanea".
Bob Johnson,

@BobJohnson Non è diverso da quello che stavo già dicendo nella mia risposta ;-)
qubert

0

Hai bisogno di una shell specifica?

Se zsh è un'opzione, leggi zshexpn(1):

Se viene utilizzato = (...) anziché <(...), il file passato come argomento sarà il nome di un file temporaneo contenente l'output del processo di elenco. Questo può essere usato al posto del <modulo per un programma che prevede di lseek(vedere lseek(2)) sul file di input.

[...]

Un altro problema sorge ogni volta che un lavoro con una sostituzione che richiede un file temporaneo viene rinnegato dalla shell, incluso il caso in cui &!o &|appare alla fine di un comando contenente una sostituzione. In tal caso il file temporaneo non verrà ripulito poiché la shell non ha più memoria del lavoro. Una soluzione alternativa consiste nell'utilizzare una subshell, ad esempio,

(mycmd =(myoutput)) &!

poiché la subshell biforcuta attenderà il completamento del comando, quindi rimuoverà il file temporaneo.

Una soluzione generale per garantire che una sostituzione del processo duri per un periodo di tempo appropriato è passarla come parametro a una funzione shell anonima (un pezzo di codice shell che viene eseguito immediatamente con l'ambito della funzione). Ad esempio, questo codice:

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

produce qualcosa di simile al seguente

File /tmp/zsh6nU0kS:
This be the verse

Ad esempio, lo uso in rifle (parte del file manager ranger) per decrittografare un file e quindi eseguo rifle sul file temporaneo, che viene eliminato al termine dei processi secondari. (non dimenticare di impostare $TERMCMD)

# ~/.config/ranger/rifle.conf
...
!ext exe, mime octet-stream$, has gpg, flag t = () { rifle -f F "$1" } =(gpg -dq "$1")
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.