2015-07-29 2 views
3

Я пытаюсь сделать что-то такое, как:Как безопасно записывать файл, если он не существует в C?

FILE* f = fopen_unless_exists("example.txt"); 

if (f != NULL) { 
    fprintf(f, "foo bar baz\n"); 
} else { 
    // f should be NULL if example.txt already exists 
    fprintf(stderr, "Error: cannot write to file or file already exists"); 
} 

Я мог бы, конечно, использовать one of the methods mentioned in a related question, но, как указано в комментариях к ответам там, это было бы состояние гонки (в частности, TOCTOU) ,

Каков самый простой способ безопасного создания и записи в файл, , если файл уже существует, не создавая условия гонки?

+1

использовать open() с флагом O_EXCL. O_EXCL Убедитесь, что этот вызов создает файл: если этот флаг указан вместе с O_CREAT, а путь уже существует, то функция open() завершится с ошибкой. – Alon

+0

@Alon 'O_EXCL' не переносится. – Dai

ответ

0

Существует нет кросс-платформенного, переносного способа сделать это в чистом C, поскольку стандартная библиотека C не имеет расширенных операций ввода-вывода, таких как блокировка файлов или общесистемная библиотека мьютексов. C даже не имеет функции «списки каталогов».

Я считаю, что лучшим решением было бы обработать условие ошибки, предложив пользователю вмешаться и позволить им выбирать, как действовать (например, удалить файл, принудительно перезаписать и т. Д.).

Более полнофункциональная библиотека ввода-вывода присутствует в C++ 11 (... наконец), но это означает отказ от pure-C. Другой альтернативой является использование кросс-платформенной библиотеки-обертки, которая обертывает функции ввода-вывода для конкретной платформы.

+1

Это будет иметь смысл, но все равно потребуются два отдельных системных вызовов ('if (fileexists (foo)) {prompt ...} else {fprintf (foo, bar);}' или что-то в этом роде), который по-прежнему делает гонку состояние возможно. Как я могу это сделать, не создавая условия гонки? – Doorknob

+0

@Doorknob Нет, каждая (современная) платформа предлагает API-интерфейс для блокировки файлов, и вы можете использовать их с C, но нет возможности сделать это изнутри Portable-C. – Dai

+0

Хорошо, не могли бы вы предоставить ссылку или какой-то ресурс, который показывает, как сделать файловую перекрестную платформу? Благодаря! – Doorknob

0

Если вы используете библиотеку GNU C, то вызов fopen("filename", "xw+"), вероятно, делает то, что вы хотите. Строка x выполняет следующие действия:

x: Откройте файл исключительно (например, флаг O_EXCL открытого (2)). Если файл уже существует, fopen() терпит неудачу и устанавливает errno в EEXIST. Этот флаг игнорируется для fdopen().

Функция другой опции:

w+: Открыть для чтения и записи. Файл создается, если он не существует, в противном случае он усекается. Поток расположен в начале файла.

Вы также можете использовать:

a: Открыть для дописывания (записи в конец файла). Файл создается, если он не существует. Поток расположен в конце файла.

a+: Открыт для чтения и добавления (запись в конце файла). Файл создается, если он не существует. Начальная позиция файла для чтения находится в начале файла, но вывод всегда добавляется в конец файла.

Есть еще больше вариантов. См fopen подробности

1

Вы должны использовать (2) системный вызов открыт с O_EXCL|O_CREAT|O_WRONLY, а затем вызвать fdopen (3) на этом дескрипторе.

#include <sys/errno.h> 
#include <fcntl.h> 

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 

int main(int argc, char *argv[]) { 
    char *program = argv[0]; 
    char *filename = argv[1]; 

    if (argc != 2) { 
     fprintf(stderr, "%s: expected a single file argument.\n", program); 
     exit(1); 
    } 

    int flags = O_EXCL|O_CREAT|O_WRONLY; 
    int mode = 0666; 

    int fd; 
    if ((fd = open(filename, flags, mode)) == -1) { 
     fprintf(stderr, "%s: cannot open %s with flags 0x%04X: %s\n", 
      program, filename, flags, strerror(errno)); 
     exit(2); 
    } 

    FILE *fp = fdopen(fd, "w"); 
    if (fp == NULL) { 
     fprintf(stderr, "%s: cannot fdopen file descriptor %d: %s\n", 
      program, fd, strerror(errno)); 
     exit(3); 
    } 

    if (fprintf(fp, "12345\n") == EOF) { 
     fprintf(stderr, "%s: cannot write to %s: %s\n", 
      program, filename, strerror(errno)); 
     exit(4); 
    } 

    if (fclose(fp) == EOF) { 
     fprintf(stderr, "%s: cannot close %s: %s\n", 
      program, filename, strerror(errno)); 
     exit(5); 
    } 

    exit(0); 
} 

Те открытых (2) флаги являются одними из немногих guaranteed by POSIX.

Это не гарантирует надежную работу на удаленных файловых системах. В частности, NFS нарушает правила POSIX об атомах.[sic]

Смежные вопросы