Как я могу безопасно записать в заданный путь к файлу в Какао, добавив числовой суффикс при необходимости? - PullRequest
5 голосов
/ 21 апреля 2009

Мы хотим написать в "foo.txt" в данном каталоге. Если «foo.txt» уже существует, мы хотим записать в «foo-1.txt» и т. Д.

Есть несколько фрагментов кода, которые пытаются ответить на этот вопрос, но ни один из них не является вполне удовлетворительным. Например. это решение в CocoaDev использует NSFileManager, чтобы проверить, существует ли путь для создания безопасного пути. Однако это приводит к очевидным расам между получением пути и записью в него. Было бы безопаснее попытаться выполнить атомарную запись и зациклить числовой суффикс при ошибке.

Давай!

Ответы [ 3 ]

1 голос
/ 21 апреля 2009
int fd;
uint32_t counter;
char filename[1024]; // obviously unsafe

sprintf(filename, "foo.txt");
if( (fd = open(filename, O_CREAT | O_EXCL | O_EXLOCK, 0644)) == -1 && errno == EEXIST ) 
{
    for( counter = 1; counter < UINT32_MAX; counter++ ) {
      sprintf(filename, "foo-%u.txt", counter);
      if( (fd = open(filename, O_CREAT | O_EXCL | O_EXLOCK, 0644)) == -1 && errno == EEXIST )
        continue;
      else
        break;
    }
}

if( fd == -1 && counter == UINT32_MAX ) {
    fprintf(stderr, "too many foo-files\n");
} else if( fd == -1 ) {
    fprintf(stderr, "could not open file: %s\n", strerror(errno));
}

// otherwise fd is an open file with an atomically unique name and an
// exclusive lock.
1 голос
/ 21 апреля 2009

Как насчет:

  1. Запишите файл во временную директорию, где вы знаете, что нет риска столкновения
  2. Используйте NSFileManager для перемещения файла в нужное место назначения
  3. Если шаг 3 не выполнен из-за того, что файл уже существует, добавьте / увеличьте числовой суффикс и повторите шаг 2

Вы бы в основном воссоздали элементарную обработку записи файла в Cocoa, но добавили бы функцию обеспечения уникального имени файла. Большим преимуществом этого подхода является то, что в случае отключения питания или сбоя приложения во время записи неполный файл будет спрятан в папке tmp и удален системой; не оставлено для пользователя, чтобы попытаться работать с.

1 голос
/ 21 апреля 2009

Используйте системный вызов open с параметрами O_EXCL и O_CREAT. Если файл еще не существует, open создаст его, откроет и вернет дескриптор файла; если он существует, open не удастся и установите errno на EEXIST.

Оттуда должно быть очевидно, как построить цикл, который пытается увеличивать имена файлов, пока он не вернет дескриптор файла или не создаст имя файла слишком длинное. В последнем пункте, убедитесь, что вы проверяете errno, когда open терпит неудачу - EEXIST и ENAMETOOLONG - это только две ошибки, с которыми вы можете столкнуться.

...