Есть ли файл, который никогда не будет существовать?

Мне удалось решить мою проблему, сначала переустановив QEMU из исходников (см.https://askubuntu.com/questions/1067722/how-do-i-install-qemu-3-0-on-ubuntu-18-04):

sudo apt-get purge "qemu*"
sudo apt-get autoremove
sudo apt-get build-dep qemu

wget https://download.qemu.org/qemu-3.1.0.tar.xz
tar -xf qemu-3.1.0.tar.xz
rm qemu-3.1.0.tar.xz
cd qemu-3.1.0
./configure
make

sudo apt-get install checkinstall
sudo checkinstall make install

Затем убедитесь, что QEMU работает от имени root, добавив следующее в/etc/libvirt/qemu.conf:

user = "root"
group = "root"

И, наконец, перезагрузка модулей KVM:

sudo rmmod kvm_intel
sudo rmmod kvm
sudo modprobe kvm
sudo modprobe kvm_intel
sudo systemctl restart libvirtd.service

Мне кажется, что запускать QEMU от имени root не обязательно, но я просто хотел, чтобы это заработало.

62
06.04.2021, 15:28
8 ответов

В качестве альтернативы я бы предложил вашему сценарию создать временный каталог, а затем искать в нем имя файла. Таким образом, вы на 100% уверены, что файл не существует, и у вас есть полный контроль и вы можете легко убрать за собой. Что-то вроде:

dir=$(mktemp -d)
if [ -e "$dir"/somefile ]; then
    echo "Something is seriously wrong here, '$dir/somefile' exists!"
fi
rmdir "$dir"

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

104
28.04.2021, 22:54

/dev/null/foo не может существовать, если /dev/nullне является каталогом.

POSIX требует, чтобы/dev/nullбыл «пустым источником данных и бесконечным приемником данных». Я не уверен, что каталог с такими характеристиками вообще невозможен. Тем не менее, я думаю, что вполне безопасно предположить, что /dev/nullне является каталогом в вашем *nix.

Обратите внимание, что если вы попытаетесь открыть /dev/null/foo, вы получитеENOTDIR(не каталог ), а неENOENT(нет такого файла или каталога ). Это может быть или не быть приемлемым для ваших целей тестирования.

87
28.04.2021, 22:54

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

. предполагая, что у вас есть рабочий/dev/urandom(и вы должны ), что-то вроде этого сгенерирует в fдействительное имя файла на основе 128 -битного случайного числа:

f=/$(head -c 16 /dev/urandom |base64 |tr /,)

Вывод выглядит примерно так /B90sYd,aNrcw7d7Itcb8fQ==. (Начальная косая черта фиксирована и предназначена для того, чтобы сделать ее абсолютным путем. Хвост ==также исправлен благодаря заполнению Base64. Их можно игнорировать.)

Системе, генерирующей случайные имена файлов со скоростью 1 триллион в секунду, потребуются триллионы лет, чтобы сгенерировать имя, сгенерированное вашим скриптом. Обратите внимание, что любой коллизии недостаточно, поэтому атака дня рождения не применяется. Это в основном то же самое, что и перебор 128-битного -симметричного ключа.

См. также, например,. :Сколько времени потребуется для перебора ключа AES -128?

Обратите внимание, что для этого требуется рабочий /dev/urandom. Это не сработает, если кто-то заменил его статическим файлом, содержащим некоторую известную строку, например, например. три байта \x86\x89\x9e, которые при кодировании Base64 -создают строку home; или если вы, например,. контекст chrooted, где /dev/urandomнедоступен. Также, например. встроенные системы без реальных средств для инициализации системного генератора случайных чисел могут столкнуться с проблемами. Не используйте это в подобных ситуациях, но также не генерируйте никаких криптографических ключей в подобных ситуациях .


В качестве альтернативы вы можете использовать пустую строку. По крайней мере, в Linux попытка использовать его в качестве имени файла просто выдает ошибку :

.
$ cat ""
cat: '': No such file or directory
$ touch ""
touch: cannot touch '': No such file or directory

(Между прочим, я нахожу ошибку, которую он дает (ENOENT), несколько забавной. Можно подумать, что это имя недопустимо, а не не существует .)

Обратите внимание: если вы поместите это в переменную, вам действительно нужно помнить кавычки при ее расширении!Например. f=; cat $fбудет просто читать со стандартного ввода.

$ f=
$ cat "$f"
cat: '': No such file or directory
$ touch "$f"
touch: cannot touch '': No such file or directory

Однако, если вы выполните cd ""в оболочке, он просто переключится на текущий каталог. POSIX говорит , что «Если [данный путь] является пустой строкой, результаты не указаны». . Все оболочки, которые я пробовал, явно используют текущий путь в вызове chdir(), например.:

/tmp$ strace -etrace=chdir zsh -c 'cd ""'
chdir("/tmp")                           = 0
+++ exited with 0 +++
/tmp$

Я получил эту идею на основе более раннего (теперь удаленного)ответа. Это может быть или не быть специфичным для системы -, я пробовал только в Linux. Пусть покупатель будет бдителен.


Кроме того, как упоминалось в комментариях, если вам все равно, какую именно ошибку вы получаете, или используете что-то, что даже не говорит вам, например. [ -f... ]или [ -e... ]в оболочке, вы можете просто создать более длинное имя файла -.

Почти во всех файловых системах максимальная длина одного файла составляет 255 или меньше (см. таблицу в Сравнение файловых систем в Википедии ). Полный путь может быть длиннее, но одно имя файла размером 256 байт невозможно и даетENAMETOOLONG:

$ f=$(printf %256s x | tr ' ' x)
$ touch "$f"
touch: cannot touch 'xxx...xxx': File name too long

Но выполнение if [ -e "$f" ]; then...работает без ошибок (и тест не проходит )во всех оболочках, которые я пробовал.

определении POSIX говорится, что -eявляется «ложным, если имя пути не может быть разрешено», но явно не упоминает диагностику. Так что, возможно, какая-то реализация могла дать ошибку в какой-то ситуации, я не уверен. Скажите, если найдете такой случай.)

(В таблице в Википедии действительно упоминаются две файловые системы Linux с большей длиной файла на -, но я сомневаюсь, что они широко используются в настоящее время, и я также понимаю, что Linux имеет ограничение в 255 -байт в целом, независимо от файловая система.)

33
28.04.2021, 22:54

terdon уже дал хороший ответ:usemktemp -d. Этот пост рассматривает вопрос более фундаментально и объясняет , почему эта команда обычно является лучшим ответом.

Не существует постоянного пути, который никогда не будет существовать. Но, судя по вашему предложению о тестировании скриптов, похоже, что путь переменной так же хорош, при условии, что скрипт может его сгенерировать сам.

Единственный способ сгенерировать такой путь — продолжать генерировать пути до тех пор, пока сгенерированный путь не перестанет существовать. К сожалению, это может привести к состоянию гонки:какая-то другая программа может создать файл после проверки его существования, но до вы выполняете любую операцию, зависящую от того, что файл не существует.

Лучший способ избежать этого состояния гонки — использовать путь, которого должны избегать другие программы, например путь под /tmp. К сожалению, /tmpимеет тенденцию быть -доступным для записи во всем мире, так что теперь у вас есть еще большая проблема, :какой-то другой пользователь может создать файл.

Что вам действительно нужно, так это временный каталог, которого другие программы должны избегать и к которому другие пользователи не имеют доступа. Еще лучше, если каталог пуст, поэтому вы можете просто использовать любой путь в этом каталоге.

mktemp -dсоздает каталог, отвечающий всем критериям, изложенным в предыдущем абзаце, защищая себя от собственных условий гонки. Когда вы закончите, вы можете использовать rmdir, чтобы удалить каталог :

.
dir="$(mktemp -d)"
# Use "$dir"/foo as a nonexistent path.
rmdir "$dir"
13
28.04.2021, 22:54

Если вы строго соблюдаете требование файла , вы можете прочитать абсолютный путь к текущему рабочему каталогу, а затем использовать его для проверкиобычного файла.

Чтобы обойти вероятность того, что скрипт удалит текущий рабочий каталог и создаст файл с тем же путем/именем перед выполнением проверки,возможно, мы можем рассмотреть родительский каталог самого скрипта.

Тривиальным случаем может быть проверка пути «/», который всегда гарантированно является каталогом, а не обычным файлом.

1
28.04.2021, 22:54

Я люблю, чтобы все было просто. Вместо того, чтобы использовать путь, который не может существовать, я использую тот, который никогда бы не создал и не могу представить, чтобы кто-то создал.

Мои тесты, которым нужен несуществующий -путь, используют что-то вроде /thisfiledoesnotexist. Я считаю это разумным, поскольку:

  • Имя файла является само -документируемым.
  • Для создания этого файла требуются права суперпользователя, поэтому он вряд ли будет создан случайно.
  • Я бы никогда не создал его.
  • Я не могу себе представить, почему кто-то еще когда-либо мог. Системные администраторы Linux любят содержать корневой каталог в чистоте

Если вы действительно параноик,тогда ваша тестовая система может сначала проверить, что файл не существует, и потерпеть неудачу, если он существует. Только если файл не существует, он продолжит тест.

9
28.04.2021, 22:54

Поскольку идентификаторы процессов никогда не бывают отрицательными, /proc/-1никогда не будет существовать.

Это также работает, даже если ваш вариант Unix не поддерживает procfs (или если он не смонтирован )!


Аналогичный метод с использованием файловых дескрипторов:/dev/fd/-1

Я полагаю, что какой-то вариант Unix теоретически может разрешить файловый дескриптор -1, но это нарушит большую часть существующего кода.


Есть, вероятно, много других подобных возможностей с автоматически -генерируемыми файловыми системами.

17
28.04.2021, 22:54

Предположим, критерии:

  • тест должен работать без права записи на устройство (поэтому нет временной папки );
  • должен возвращать E_NOENT(, поэтому нельзя использовать известное -существующее имя файла в качестве родительской папки, так как это даетE_NOTDIR).

В этом случае устаревшие функции библиотеки C char *tmpnam(char *str)и char *tempnam(const char *dir, const char *pfx);генерируют и возвращают короткое действительное временное имя файла, которое гарантированно не существует на данный момент времени .

Вполне возможно, что этого может быть достаточно для вашего варианта использования, но, конечно, методы устарели по причине . Существует состояние гонки между запуском tmpnamи проверкой существования файла, во время которого файл может быть создан. Злоумышленник также может намеренно создавать имена файлов с гораздо более высокой вероятностью коллизии, чем 1 в TMP _MAX. Даже при наличии нескольких не -вредоносных процессов возникает проблема дня рождения .

Кроме того, эти библиотечные вызовы требуют C, на котором ваши тесты вряд ли будут написаны. Мы могли бы реализовать аналогичную функциональность в тестовом коде :сгенерировать имя файла, посмотреть, существует ли оно, повторять до тех пор, пока одного не существует.

Однако наивная реализация будет не только уязвима к описанному выше условию гонки, но также будет иметь очень длительное состояние сбоя, :если E _NOENT никогда не возвращается правильно, поэтому каждый файл кажется, существует, он будет пытаться бесконечно. Это совершенно неправильное состояние сбоя, при написании теста для E _NOENT :это означает, что обнаружение сбоя занимает вечность!

Однако, как пишет @ikkachu, при соответствующем создании более длинных имен файлов (, например UUID, или подходе, который предлагает ikkachu ),должно быть криптографически невозможно, чтобы наш случайный файл уже существовал, поэтому нам нужно попробовать только один раз. Заманчиво попробовать дважды , на всякий случай. Но это эквивалентно добавлению одного дополнительного бита энтропии к случайной длине нашего имени файла.

2
28.04.2021, 22:54

Теги

Похожие вопросы