2013-06-15 3 views
14

Se ho un handle per un file aperto, è possibile creare un collegamento fisico a quel file dopo che tutti i riferimenti sono stati rimossi dal filesystem?Creare un collegamento fisico da un handle di file su Unix?

Per esempio, qualcosa di simile:

fd = fopen("/tmp/foo", "w"); 
unlink("/tmp/foo"); 
fwrite(fd, "Hello, world!\n"); 
create_link_from_fd(fd, "/tmp/hello"); 
fclose(fd); 

In particolare, mi piacerebbe fare questo in modo che posso tranquillamente scrivere su file di grandi dimensioni, poi spostarli in posizione atomicamente, senza doversi preoccupare di pulizia su di me stesso se il mio programma viene ucciso nel mezzo della scrittura del file.

+0

buona domanda, +1 – nsd

risposta

2

Non genere, no. [Modifica: da Linux 3.11 c'è ora linkat; vedi safsaf32's answer. Questo non funziona su sistemi POSIX, in generale, in quanto POSIX linkat è limitato ai soli indici] Non ci sono considerazioni di sicurezza qui:. Qualcuno può passare a voi un descrittore di file aperto che non si poteva normalmente open da soli, ad esempio:

mkdir lock; chmod 700 lock 
echo secret contents > lock/in 
sudoish cmd < lock/in 

Qui cmd viene eseguito come utente che non dispone dell'autorizzazione per open il file di input (lock/in) per nome, ma può ancora leggere da esso. Se cmd potrebbe creare un nuovo nome sullo stesso file system, potrebbe passare il contenuto del file a un processo successivo. (Ovviamente può copiare quei contenuti, quindi questo problema è più di un "passare il contenuto per errore" che "passare il contenuto su, apposta".)

Detto questo, le persone hanno escogitato modi di "ricollegando" file internamente/vnode internamente (è piuttosto facile da fare all'interno della maggior parte dei file system), così si potrebbe fare la propria chiamata di sistema privata per questo. Il descrittore deve fare riferimento a un file reale sul punto di montaggio appropriato, ovviamente: non c'è modo di "ricollegare" una pipe o socket o dispositivo per diventare un file normale.

Altrimenti sei bloccato con "segnali di cattura e ripulisci e spera per il meglio", o un trucco simile, "esca da un sottoprocesso, eseguilo, e se riesce/fallisce, prendi la mossa/pulizia appropriata su azione ".


Modifica per aggiungere nota storica: il sopra lock esempio non è particolarmente buona, ma nei giorni di V6 Unix, MDQS utilizzato una versione più elaborata di questo trucco. Bit e pezzi di MDQS sopravvivono in varie forme oggi.

2

Su Linux, si potrebbe provare il trucco portabile di utilizzare /proc/self/fd cercando di chiamare

char pbuf[64]; 
snprintf (pbuf, sizeof(pbuf), "/proc/self/fd/%d", fd); 
link(pbuf, "/tmp/hello"); 

Sarei sorpreso se quel trucco ha funzionato dopo un unlink("/tmp/foo") ... Non ho provato questo.

Un modo più portabile (ma meno robusto) sarebbe quello di generare un "percorso temporaneo unico" forse come

int p = (int) getpid(); 
int t = (int) time(0); 
int r = (int) random(); 
sprintf(pbuf, sizeof(pbuf), "/tmp/out-p%d-r%d-t%d.tmp", p, r, t); 
int fd = open (pbuf, O_CREAT|O_WRONLY); 

volta che il file è stato scritto e chiuso, si rename(2) a qualche percorso più ragionevole. È possibile utilizzare atexit nel programma per eseguire la ridenominazione (o la rimozione).

e avere qualche cron lavoro per pulire il [vecchia] /tmp/out*.tmp ogni ora ...

+0

Questo trucco non funziona, purtroppo (almeno per me!). – Celada

8

Il nuovo linux rilasciato.11 offre una soluzione a questo problema con il nuovo flag O_TMPFILEopen(2). Con questo flag è possibile creare un file "invisibile" (cioè un inode senza collegamenti fissi) in alcuni file system (specificato da una directory in quel file system). Quindi, dopo aver configurato completamente il file, è possibile creare un collegamento fisico utilizzando linkat. Funziona in questo modo:

fd = open("/tmp", O_TMPFILE | O_RDWR, 0600); 
// write something to the file here 
// fchown()/fchmod() it 
linkat(fd, "", AT_FDCWD, "/tmp/test", AT_EMPTY_PATH); 

Nota che, oltre al requisito del kernel> = 3.11, questo richiede anche il supporto dal file system sottostante (ho provato il frammento di codice in ext3 e ha funzionato, ma non sembrava lavorare su btrfs).

+0

Che questo funzioni, o usando '/ proc/self/fd/n', è supportato da http://comments.gmane.org/gmane.linux.file-systems/76553 –