Запись файлов и liza-copy
Реализовано в 0.0.25. Файловая запись — внешний эффект с возможным
частичным результатом. Ошибка не откатывает файл. Полученные File остаются
move-only ресурсами, которые требуется закрыть на всех нормальных путях.
Создать новый файл
path: Path сохраняет платформенное представление: байты на POSIX,
UTF-16 на Windows. Успех создаёт новый пустой обычный файл, открытый только
для записи. Существующий destination не усечётся и не будет открыт для записи.
Проверка и создание выполняются одной операцией ОС, без предварительного
«проверить существование, затем открыть».
AlreadyExists означает отказ ОС из-за существующего имени. Другие ошибки
оборачиваются в Failure с прежним FileError. Его набор вариантов не
расширен: существующие исчерпывающие match остаются допустимыми.
Проверки доступа и допустимости имени по-прежнему выполняет ОС.
На POSIX используются O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, права
0666 ограничиваются текущим umask. Существующая конечная symlink, включая
висячую, не разыменовывается для перезаписи. На Windows используется
CreateFileW с CREATE_NEW, GENERIC_WRITE и FILE_SHARE_READ.
Права и метаданные source не копируются; отсутствующие каталоги не создаются.
Перезапись, append, атомарная замена, удаление и нормализация пути в этот API не входят. Произвольные конкурентные изменения файловой системы другим процессом не превращаются в изолированную транзакцию.
Записать чанк
output: File заимствуется на время вызова, а не потребляется. data: View<u8>
предоставляет readonly-доступ к байтам; runtime не сохраняет этот view.
После вызова файл по-прежнему обязан быть закрыт. Запись идёт с текущей позиции
файла и продвигает её на успешно записанные байты.
enum FileWriteError {
InvalidHandle; PermissionDenied; NoSpace; WouldBlock;
WriteZero; TooLarge; Other
}
struct FileWriteFailure { kind:FileWriteError; written:u32 }
Это справочные объявления библиотечных типов, не код для повторного объявления.
- Успех:
Ok(data.len), все байты приняты ОС. - Короткая успешная системная запись: runtime продолжает с оставшегося суффикса.
- Ошибка:
Errсодержит причину иwritten— подтверждённое число байтов за этот вызов, не общий размер файла и не число байтов в долговременном хранилище. - Успешная системная запись нуля байтов при непустом остатке:
WriteZero, без бесконечного повторения. - Пустой view:
Ok(0)без системного вызова и изменения позиции.
Каждый запрос к ОС ограничен 1 МиБ; длина view остаётся u32.
На POSIX EINTR до записи байтов повторяется. Windows учитывает число байтов,
сообщённое WriteFile, в том числе при отказе. Режим асинхронного I/O не используется.
NoSpace включает исчерпание места/дисковой квоты, TooLarge — файловый
лимит размера. Конкретные ошибки доступа и допустимости операции зависят от
ОС; например, передача файла, открытого только для чтения, не даёт права записи.
Тип File пока не разделён на отдельные типы прав доступа.
POSIX runtime временно блокирует SIGPIPE и SIGXFSZ в вызывающем потоке,
чтобы соответствующий отказ можно было вернуть как значение. Он восстанавливает
маску и сохраняет сигналы, уже ожидавшие обработки до вызова. Диспозиции
сигналов процесса не изменяются.
При ошибке подтверждённый префикс не откатывается; другие изменения внешнего состояния не исключаются. Повторная запись всего view после частичного успеха может продублировать префикс. Автоматического восстановления после ошибки или возврата файловой позиции нет.
Синхронизация и закрытие
file.flush(output) // Result<u32,FileWriteError>, io; borrow
file.close(move output) // Result<u32,FileError>, io; consume
flush запрашивает синхронизацию данных и метаданных файла через fsync на
POSIX или FlushFileBuffers на Windows. Успех возвращает Ok(0).
Операция не закрывает файл и не снимает обязанность владения. EINTR для
POSIX-синхронизации повторяется. Она предназначена для записываемого файла;
доступность операции для других дескрипторов определяется ОС.
Успешная запись означает приём данных ОС, а не гарантированное сохранение
после отключения питания. Успешный flush означает выполнение запроса
синхронизации по контракту ОС/файловой системы. Это не универсальная защита
от аппаратных сбоев, не атомарный commit и не синхронизация каталога, содержащего
новое имя. В частности, liza-copy не обещает crash-atomic публикацию destination.
close потребляет ресурс даже при ошибке. Повторять закрытие через ту же
привязку нельзя. На ошибочном пути запись/flush не освобождают от попытки
закрыть файл; при trap пользовательский cleanup по-прежнему не обещается.
Платформенные основания: POSIX open, write, fsync, Windows WriteFile и FlushFileBuffers.
liza-copy
python3 -m liza build examples/apps/liza_copy.liza -o build/liza-copy
./build/liza-copy source.bin destination.bin
На Windows результат — build/liza-copy.exe. Через компилятор:
Destination должен отсутствовать. Source открывается первым; если destination существует, в том числе является source или hard link на него, утилита отказывает без усечения. Программа копирует содержимое бинарно, включая NUL, невалидный UTF-8 и любые переводы строк. Не выполняются преобразования текста, копирование атрибутов, рекурсивный обход или чтение stdin.
Порядок: чтение по 2 КиБ → полная запись чанка → повторение до EOF → flush
destination → закрытие destination → закрытие source. Объём памяти не растёт
с размером файла. После ошибки чтения/записи дальнейший перенос прекращается,
flush пропускается, но оба ресурса закрываются.
| Код | Значение | Потоки |
|---|---|---|
0 |
Всё скопировано, синхронизировано и закрыто | stdout/stderr пусты |
2 |
Неверное число аргументов | stderr: Usage: liza-copy <source> <destination>\n |
3 |
Ошибка открытия, создания, чтения, записи, flush, close или диагностики | stdout пуст; stderr по возможности сообщает причину |
\n означает один LF. Для существующего destination:
Для ошибки после частичной записи:
Cannot write destination; bytes written in current chunk: 32
Destination may be incomplete; it has not been removed
Первичная причина сохраняется. Дополнительные ошибки закрытия выводятся после
неё как Also failed to close destination / Also failed to close source.
Оба ресурса потребляются до вывода диагностики, поэтому даже отказ stderr
не пропускает cleanup. При отказе записи stderr сообщение может быть неполным;
код остаётся 3.
После ошибки, произошедшей после успешного создания, файл не удаляется. Он может быть пустым, частично записанным или полным, но без подтверждённой синхронизации/закрытия. Последующий запуск с тем же destination вновь откажет из-за существующего имени. Решение о сохранении или удалении остаётся у пользователя. При неудаче открытия source или создания destination предупреждение о частичном файле не выводится.
Исходник и проверки стоимости
file.write_chunk не создаёт дополнительный буфер содержимого и не выделяет
heap-память. Он передаёт указатель на текущий суффикс view платформенной
реализации записи. Это не обещание zero-copy внутри ОС или отсутствия всех
копий фиксированного значения FileChunk при чтении и передаче между функциями.
O0/O2 проверяют точные бинарные копии, пустые и большие файлы, защиту существующих имён, короткие/нулевые записи, отказ после префикса и ошибки синхронизации/закрытия. POSIX-проверка с реальным файловым лимитом подтверждает возврат ошибки, сохранение 32 записанных байтов и отсутствие усечения при повторном запуске. Инъекция ошибок проверяет порядок и однократное закрытие обоих ресурсов, включая отказ stderr. Отдельно проверяются исходный адрес байтов при коротких записях и отсутствие аллокаций/копирования содержимого в выбранном тракте LLVM IR и машинного кода.