Удаление файлов при чтении каталога с помощью 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);

(этот код не проверен и является просто примером)

Дело в том, разрешено ли удалять файл в каталоге при прохождении через каталог с помощью readdir()? Или readdir() все еще найдет удаленный файл .log?

3 ответа

Цитата из POSIX readdir:

Если файл удален или добавлен в каталог после последнего вызов opendir() или rewinddir(), будет ли последующий вызов readdir() возвращает запись для этого файла не определено.

Итак, моя догадка... это зависит.

Это зависит от ОС, от времени суток, от относительного порядка добавленных/удаленных файлов,...

И, как еще один момент, между тем, как функция readdir() вернется и вы попытаетесь unlink() файла, какой-то другой процесс мог удалить этот файл, а ваш unlink() терпит неудачу.

Изменить

Я тестировал эту программу:

#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;
}
</unistd.h></sys></string.h></stdlib.h></stdio.h></dirent.h>

На моем компьютере readdir() находит удаленные файлы и не находит файлы, созданные между opendir() и readdir(). Но это может быть другим на другом компьютере; на моем компьютере может быть другим, если я компилирую с различными параметрами; это может быть другим, если я обновляю ядро;...


Я тестирую свой новый справочник по Linux. Интерфейс программирования Linux от Michael Kerrisk и он говорит следующее:

SUSv3 явно отмечает, что не указано, будет ли readdir() возвращать имя файла, которое было добавлено или удалено с момента последнего с момента последнего вызова opendir() или rewinddir(). Все имена файлов, которые не были добавлены или удалены с момента последнего такого вызова, будут гарантированы.

Я думаю, что то, что неуказано, - это то, что происходит с не проверенными ранее. После того, как запись была возвращена, на 100% гарантировано, что она больше не будет возвращена, независимо от того, отсоединяете ли вы текущий дирент или нет.

Также обратите внимание на гарантию, предусмотренную вторым предложением. Поскольку вы оставляете в покое другие файлы и только отменяете текущую запись для zip файла, SUSv3 гарантирует, что все остальные файлы будут возвращены. Что происходит с файлом журнала, это undefined. он может быть или не быть возвращен readdir(), но в вашем случае это не должно быть вредным.

Причина, по которой я исследовал вопрос, заключается в том, чтобы найти эффективный способ закрыть файловые дескрипторы в дочернем процессе до exec().

Предлагаемый способ в APUE от Stevens состоит в следующем:

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

но я думаю, используя код, аналогичный тому, что находится в OP для сканирования /dev/fd/directory, чтобы точно знать, какие fds мне нужно закрыть. (Особое примечание для меня, пропустите dirfd, содержащиеся в дескрипторе DIR.)


Я нашел следующую страницу, описывающую решение этой проблемы.

https://support.apple.com/kb/TA21420

licensed under cc by-sa 3.0 with attribution.