2009-11-04 10 views
5

Mi código es algo como esto:Eliminar archivos mientras se lee directorio con readdir()

DIR* pDir = opendir("/path/to/my/dir"); 
struct dirent pFile = NULL; 
while ((pFile = readdir())) { 
    // Check if it is a .zip file 
    if (subrstr(pFile->d_name,".zip") { 
     // It is a .zip file, delete it, and the matching log file 
     char zipname[200]; 
     snprintf(zipname, sizeof(zipname), "/path/to/my/dir/%s", pFile->d_name); 
     unlink(zipname); 
     char* logname = subsstr(zipname, 0, strlen(pFile->d_name)-4); // Strip of .zip 
     logname = appendstring(&logname, ".log"); // Append .log 
     unlink(logname); 
} 
closedir(pDir); 

(este código no se ha probado y puramente un ejemplo)

La cuestión es: ¿Está permitido eliminar una archivo en un directorio mientras recorre el directorio con readdir()? O ¿readdir() todavía encontrará el archivo .log eliminado?

Respuesta

5

Presupuesto de POSIX readdir:

Si un archivo se elimina o añade a el directorio después de la más reciente llamada a opendir() o rewinddir(), si una llamada posterior a READDIR() devuelve una entrada para ese archivo es no especificado.

Por lo tanto, yo creo que es ... depende.

Depende del sistema operativo, de la hora del día, en el orden relativo de los archivos añadidos/eliminados, ...

Y, como un punto más, entre el tiempo de los readdir() devuelve la función y se intente unlink() el archivo, algún otro proceso podría haber eliminado ese archivo y su unlink() falla.


Edición

Probé con este programa:

#include <dirent.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/types.h> 
#include <unistd.h> 

int main(void) { 
    struct dirent *de; 
    DIR *dd; 

    /* create files `one.zip` and `one.log` before entering the readdir() loop */ 
    printf("creating `one.log` and `one.zip`\n"); 
    system("touch one.log"); /* assume it worked */ 
    system("touch one.zip"); /* assume it worked */ 

    dd = opendir("."); /* assume it worked */ 
    while ((de = readdir(dd)) != NULL) { 
    printf("found %s\n", de->d_name); 
    if (strstr(de->d_name, ".zip")) { 
     char logname[1200]; 
     size_t i; 
     if (*de->d_name == 'o') { 
     /* create `two.zip` and `two.log` when the program finds `one.zip` */ 
     printf("creating `two.zip` and `two.log`\n"); 
     system("touch two.zip"); /* assume it worked */ 
     system("touch two.log"); /* assume it worked */ 
     } 
     printf("unlinking %s\n", de->d_name); 
     if (unlink(de->d_name)) perror("unlink"); 
     strcpy(logname, de->d_name); 
     i = strlen(logname); 
     logname[i-3] = 'l'; 
     logname[i-2] = 'o'; 
     logname[i-1] = 'g'; 
     printf("unlinking %s\n", logname); 
     if (unlink(logname)) perror("unlink"); 
    } 
    } 
    closedir(dd); /* assume it worked */ 
    return 0; 
} 

En mi ordenador, readdir() encuentra los archivos borrados y no encuentra archivos creados entre opendir() y readdir(). Pero puede ser diferente en otra computadora; puede ser diferente en mi computadora si compilo con diferentes opciones; puede ser diferente si actualizo el kernel; ...

+1

LOL @ 'man 2 readdir':" Esta no es la función que le interesa ". – pmg

+1

La misma página de manual dice: "Las entradas de directorio representan archivos; los archivos pueden eliminarse de un directorio o agregarse a un directorio de forma asincrónica a la operación de readdir()" ¡Pero quizás sea mejor evitar esto !? – To1ne

1

Estoy probando mi nuevo libro de referencia Linux. La interfaz de programación de Linux por Michael Kerrisk y que dice lo siguiente:

SUSv3 señala explícitamente que no se especifica si readdir() devuelve un nombre de archivo que se ha añadido o eliminado de desde la última desde la última llamada a opendir() o rewinddir(). Todos los nombres de archivo que no han sido agregados ni eliminados desde la última llamada de este tipo son con garantía de que se devolverán.

creo que lo que es no especificado es lo que ocurre con dirents aún no explorados. Una vez que se devuelve una entrada, se garantiza al 100% que ya no se devolverá, ya sea que desenlazar o no la dirección actual.

También tenga en cuenta la garantía proporcionada por la segunda oración.Como está dejando solos los otros archivos y solo desvincula la entrada actual del archivo comprimido, SUSv3 garantiza que se devolverán todos los demás archivos. Lo que sucede con el archivo de registro no está definido. puede o no ser devuelto por readdir() pero en su caso, no debería ser dañino.

La razón por la que he explorado la cuestión es encontrar una forma eficiente de cerrar descriptores de archivos en un proceso secundario antes de exec().

La forma sugerida en APUE de Stevens es hacer lo siguiente:

int max_open = sysconf(_SC_OPEN_MAX); 
for (int i = 0; i < max_open; ++i) 
    close(i); 

pero estoy pensando en el uso de un código similar a lo que se encuentra en el PO para escanear/dev/fd/directorio para saber exactamente qué fds necesito cerrar. (Nota especial para mí, omita el dirfd contenido en el controlador DIR.)

Cuestiones relacionadas