![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
С незапамятных времён существует такая вещь, как "POSIX-семантика удаления". Это означает, что в Linux или другой Unix-подобной ОС любая программа или пользователь может удалить файл или даже каталог, одновременно открытый в другой программе. Соответствующая запись будет удалена из родительского каталога, но занятые файлом или каталогом блоки не будут освобождены на диске, покуда последний хэндл (или дескриптор) файла не будет закрыт. Разумеется, открытому файлу могут выделяться и новые блоки. Если в условиях наличия открытых хэндлов на "файлы-невидимки" произойдёт незапланированная перезагрузка, тогда такие "файлы-невидимки" будут удалены при следующем монтировании файловой системы в контексте драйвера.
У Windows NT традиционно сложные отношения с POSIX-семантикой удаления. Подсистема Win32 вроде бы поддерживает её для файлов, но только при условии, что для всех открытых хэндлов данного файла был задан share-флаг FILE_SHARE_DELETE (по умолчанию он сброшен, потому что так было в DOS). Даже в таком случае, старое имя по-прежнему возвращается функциями GetFileInformationByHandleEx() и NtQueryInformationFile(); покуда не закрыт старый, нельзя создать новый файл или каталог с тем же именем; каталог-хозяин "невидимого файла" может оказаться в "интересном положении", когда он вроде бы пуст и закрыт, но удалить или переименовать его нельзя. Учитывая вышеизложенное, можно сказать, что Win32-подсистема Windows NT в целом не поддерживает POSIX-семантику удаления. Другое дело — POSIX-подсистема: там всё удаляется так, как положено по стандарту. Ну, не совсем удаляется, а переносится в скрытый подкаталог в корневом каталоге тома с одновременной установкой FILE_FLAG_DELETE_ON_CLOSE. (Если далее произошла незапланированная перезагрузка, то вам как бы предлагается вмешаться и вручную поудалять всех потеряшек.) Я специально не изучал, как новая подсистема WSL/LXSS натягивает сову на глобус, но подозреваю, что через аналогичную задницу.
Тем временем в Linux, где хэндлы открытых файлов архитектурно привязаны не к именам, а к inode'ам, эта концепция продолжает логически развиваться. Начиная с версии ядра 3.11, "файл-невидимку" можно получить не только удалением существующего файла, а изначально: с помощью флага O_TMPFILE у системного вызова open(2). Позже, когда файл будет наполнен данными, его можно "проявить" в файловой системе через linkat(2), используя "/proc/self/fd/%d" в качестве "старого имени" и AT_FDCWD в качестве файлового дескриптора старого каталога. Ещё до "проявления", все необходимые атрибуты можно задать с помощью fchown(2), fchmod(2) и т.д. Таким образом, можно атомарно создавать заполненные файлы в файловой системе. Этот странный интерфейс не годится для POSIX, но сама концепция интересная. Недавно низкоуровневой интерфейс драйверов файловых систем в Linux был специально доработан для того, чтобы всё это стало реальностью.
Сам стандарт POSIX и его реализации тоже, конечно, содержат несуразности и глупости, даже если ограничиться только той частью, которая касается файловых систем. Так, попытка перезаписать с помощью open(2) с флагом O_CREAT и без флага O_EXCL исполняемый файл, отображённый в один из выполняющихся процессов с помощью mmap(2), порождает странную ошибку ETXTBSY (Text file busy). Во-первых, речь идёт не о текстовом файле, а о секции .text в бинарном исполняемом файле, которая традиционно содержит машинный код. Во-вторых, ошибка тривиально обходится удалением файла (unlink) и созданием нового файла на его месте. Отображение в другом процессе при этом будет ссылаться на старый файл, незавимо от того, полон или неполон новый файл и что в нём содержится. Однако, по каким-то лунным причинам open при перезаписи файла ведёт себя иначе, чем unlink + open на пустом месте.
Ещё одна забавная особенность или отклонение от ожидаемого поведения: Windows всегда реализует POSIX-семантику удаления для альтернативных потоков данных файловой системы NTFS. Например, вы можете создать текстовый файл с расширением .txt, в ADSе которого разместить DLL-ку, загрузить её с помощью LoadLibrary, и затем удалить оригинальный текстовый файл (включительно со всеми ADSками). До вызова FreeLibrary DLLка останется загруженной в виртуальную память, а место на диске — распределённым, по всем правилам POSIX-семантики, включая возможность создания другого текстового файла с тем же именем и с тем же именем ADS-потока без затрагивания "невидимого оригинала". Создать изначально невидимый ADS в Windows, к сожалению, нельзя. Передавать владение существующим ADSом между разными файлами тоже нельзя.
У Windows NT традиционно сложные отношения с POSIX-семантикой удаления. Подсистема Win32 вроде бы поддерживает её для файлов, но только при условии, что для всех открытых хэндлов данного файла был задан share-флаг FILE_SHARE_DELETE (по умолчанию он сброшен, потому что так было в DOS). Даже в таком случае, старое имя по-прежнему возвращается функциями GetFileInformationByHandleEx() и NtQueryInformationFile(); покуда не закрыт старый, нельзя создать новый файл или каталог с тем же именем; каталог-хозяин "невидимого файла" может оказаться в "интересном положении", когда он вроде бы пуст и закрыт, но удалить или переименовать его нельзя. Учитывая вышеизложенное, можно сказать, что Win32-подсистема Windows NT в целом не поддерживает POSIX-семантику удаления. Другое дело — POSIX-подсистема: там всё удаляется так, как положено по стандарту. Ну, не совсем удаляется, а переносится в скрытый подкаталог в корневом каталоге тома с одновременной установкой FILE_FLAG_DELETE_ON_CLOSE. (Если далее произошла незапланированная перезагрузка, то вам как бы предлагается вмешаться и вручную поудалять всех потеряшек.) Я специально не изучал, как новая подсистема WSL/LXSS натягивает сову на глобус, но подозреваю, что через аналогичную задницу.
Тем временем в Linux, где хэндлы открытых файлов архитектурно привязаны не к именам, а к inode'ам, эта концепция продолжает логически развиваться. Начиная с версии ядра 3.11, "файл-невидимку" можно получить не только удалением существующего файла, а изначально: с помощью флага O_TMPFILE у системного вызова open(2). Позже, когда файл будет наполнен данными, его можно "проявить" в файловой системе через linkat(2), используя "/proc/self/fd/%d" в качестве "старого имени" и AT_FDCWD в качестве файлового дескриптора старого каталога. Ещё до "проявления", все необходимые атрибуты можно задать с помощью fchown(2), fchmod(2) и т.д. Таким образом, можно атомарно создавать заполненные файлы в файловой системе. Этот странный интерфейс не годится для POSIX, но сама концепция интересная. Недавно низкоуровневой интерфейс драйверов файловых систем в Linux был специально доработан для того, чтобы всё это стало реальностью.
Сам стандарт POSIX и его реализации тоже, конечно, содержат несуразности и глупости, даже если ограничиться только той частью, которая касается файловых систем. Так, попытка перезаписать с помощью open(2) с флагом O_CREAT и без флага O_EXCL исполняемый файл, отображённый в один из выполняющихся процессов с помощью mmap(2), порождает странную ошибку ETXTBSY (Text file busy). Во-первых, речь идёт не о текстовом файле, а о секции .text в бинарном исполняемом файле, которая традиционно содержит машинный код. Во-вторых, ошибка тривиально обходится удалением файла (unlink) и созданием нового файла на его месте. Отображение в другом процессе при этом будет ссылаться на старый файл, незавимо от того, полон или неполон новый файл и что в нём содержится. Однако, по каким-то лунным причинам open при перезаписи файла ведёт себя иначе, чем unlink + open на пустом месте.
Ещё одна забавная особенность или отклонение от ожидаемого поведения: Windows всегда реализует POSIX-семантику удаления для альтернативных потоков данных файловой системы NTFS. Например, вы можете создать текстовый файл с расширением .txt, в ADSе которого разместить DLL-ку, загрузить её с помощью LoadLibrary, и затем удалить оригинальный текстовый файл (включительно со всеми ADSками). До вызова FreeLibrary DLLка останется загруженной в виртуальную память, а место на диске — распределённым, по всем правилам POSIX-семантики, включая возможность создания другого текстового файла с тем же именем и с тем же именем ADS-потока без затрагивания "невидимого оригинала". Создать изначально невидимый ADS в Windows, к сожалению, нельзя. Передавать владение существующим ADSом между разными файлами тоже нельзя.