Найти в Дзене
File Energy

Подводные камни VFAT на Linux - как не потерять файлы при работе с Windows-разделами

Флешка отформатирована под Windows, воткнута в Linux-машину - и всё как будто работает. Файлы копируются, директории видны, ошибок нет. Но стоит вернуть накопитель обратно в Windows, как часть имён превращается в нечитаемые символы или вовсе теряется. Знакомая история? За этой, казалось бы, мелкой неприятностью прячется целый пласт технических нюансов, которые мало кто изучает до первой неприятности. VFAT - это не просто "старый FAT с длинными именами". Это сложная надстройка над классическим FAT, которая хранит два имени для одного файла одновременно: короткое в формате 8.3 и длинное в Unicode. Именно это двойное хранение и порождает большинство проблем совместимости между Linux и Windows. Разобраться в механике этого процесса - значит раз и навсегда избавиться от загадочных кракозябр в именах файлов. Архитектура имён в VFAT восходит к временам Windows 95, когда Microsoft нужно было сохранить обратную совместимость с DOS и при этом дать пользователям возможность называть файлы по-чело
Оглавление

Флешка отформатирована под Windows, воткнута в Linux-машину - и всё как будто работает. Файлы копируются, директории видны, ошибок нет. Но стоит вернуть накопитель обратно в Windows, как часть имён превращается в нечитаемые символы или вовсе теряется. Знакомая история? За этой, казалось бы, мелкой неприятностью прячется целый пласт технических нюансов, которые мало кто изучает до первой неприятности.

VFAT - это не просто "старый FAT с длинными именами". Это сложная надстройка над классическим FAT, которая хранит два имени для одного файла одновременно: короткое в формате 8.3 и длинное в Unicode. Именно это двойное хранение и порождает большинство проблем совместимости между Linux и Windows. Разобраться в механике этого процесса - значит раз и навсегда избавиться от загадочных кракозябр в именах файлов.

Как устроено хранение имён в VFAT и почему это важно

Архитектура имён в VFAT восходит к временам Windows 95, когда Microsoft нужно было сохранить обратную совместимость с DOS и при этом дать пользователям возможность называть файлы по-человечески. Решение оказалось элегантным в своей двойственности: каждый файл с длинным именем получает запись LFN (Long File Name) в кодировке UTF-16, а рядом хранится сгенерированный алиас формата 8.3 - тот самый "shortname", закодированный в кодовой странице DOS.

Длинные имена хранятся на диске в UTF-16LE. Windows использует UTF-16 нативно, поэтому на её стороне проблем нет. Linux же работает с UTF-8, и ядру приходится транслировать имена "на лету" при каждой операции с файловой системой. Именно здесь и зарождается большинство коллизий.

Короткое имя живёт по своим законам. Для него ядро использует кодовую страницу (codepage), которая отвечает за набор допустимых символов. Если имя файла состоит только из ASCII-символов в верхнем регистре и вписывается в 8.3, shortname совпадает с именем файла. Если нет - генерируется алиас вида LONGFI~1.TXT, и это поведение управляется через параметр shortname.

Параметр codepage - фундамент правильного монтирования

Параметр codepage определяет, какую кодовую страницу ядро использует при работе с короткими именами. По умолчанию для большинства дистрибутивов установлен codepage=437 - это базовая IBM PC-кодировка, покрывающая латиницу и основные символы США и Западной Европы. Для русскоязычных систем ситуация иная: кириллические shortname-имена требуют codepage=866, иначе короткие имена, созданные под DOS/Windows с русскими символами, будут отображаться некорректно.

Посмотреть, какие кодовые страницы вкомпилированы в ядро, можно так:

zcat /proc/config.gz | grep FAT_DEFAULT

Типичный вывод выглядит следующим образом:

CONFIG_FAT_DEFAULT_CODEPAGE=437
CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"

Это значения по умолчанию, которые применяются, если при монтировании не передано других параметров. Переопределить их можно непосредственно в команде монтирования или в /etc/fstab.

Важный нюанс: кодовая страница влияет только на короткие имена. Длинные имена хранятся в UTF-16 независимо от codepage, и именно они являются основным механизмом хранения в современных системах. Практически все файлы, создаваемые сегодня, получают LFN-запись, поэтому codepage=437 работает нормально даже для кириллических имён - при условии правильно настроенного iocharset или utf8.

shortname - скрытый источник несовместимости

Если codepage - это вопрос правильного чтения, то shortname - это вопрос правильного создания и отображения. Параметр принимает четыре значения, и каждое из них ведёт себя принципиально по-разному.

Режим lower конвертирует короткие имена в нижний регистр при отображении и следует правилам Windows 95 при создании. Режим win95 полностью эмулирует поведение Windows 95 как для отображения, так и для создания. Режим winnt эмулирует Windows NT: отображает имена как есть, не создавая LFN-запись для файлов с именами в нижнем регистре. Режим mixed - гибрид: отображение по правилам NT, создание по правилам 95. Именно mixed является значением по умолчанию начиная с ядра 2.6.32.

Режим winnt особенно коварен. Файл bootx64.efi, созданный под winnt, будет сохранён на диске только как BOOTX64.EFI без LFN-записи. При следующем монтировании с другими параметрами этот файл либо не найдётся, либо отобразится с изменённым регистром. Для EFI System Partition это критично: загрузчик просто не найдёт файл по ожидаемому пути.

Режим mixed при монтировании выглядит так:

mount -t vfat -o shortname=mixed /dev/sdb1 /mnt/usb

Верификацию текущих параметров монтирования можно выполнить командой:

mount | grep vfat

Вывод покажет полный список активных опций для каждого смонтированного VFAT-раздела.

iocharset, utf8 и тонкое различие между ними

Здесь начинается область, которую даже опытные администраторы нередко путают. iocharset и utf8 решают похожую задачу, но разными способами, и их одновременное использование может давать неожиданные результаты.

iocharset задаёт кодировку для преобразования длинных имён между их Unicode-представлением на диске и тем, что видит пользователь. Параметр iocharset=iso8859-1 говорит ядру: "конвертируй Unicode-имена в Latin-1 при показе". Это исторически безопасный вариант, который сохраняет файловую систему регистронезависимой.

Опция utf8 работает иначе. Она включает UTF-8-трансляцию непосредственно в драйвере VFAT, без промежуточной кодировки. Именно её рекомендует документация ядра для современных UTF-8-локалей. При этом iocharset=utf8 - это не то же самое, что utf8. Второй вариант предпочтительнее: первый может нарушить регистронезависимость файловой системы, что создаёт неожиданные проблемы в приложениях, которые полагаются на case-insensitive поведение FAT.

# Правильно для UTF-8 локали
mount -t vfat -o utf8,codepage=437,shortname=mixed /dev/sdb1 /mnt/usb

# Нежелательно - может нарушить регистронезависимость
mount -t vfat -o iocharset=utf8,codepage=437 /dev/sdb1 /mnt/usb

Типовая запись в /etc/fstab для USB-накопителя, которым пользуются и Linux, и Windows:

UUID=XXXX-XXXX /mnt/usb vfat rw,uid=1000,gid=1000,fmask=0022,dmask=0022,codepage=437,shortname=mixed,utf8,errors=remount-ro 0 0

Параметр errors=remount-ro защищает данные: если при записи возникнет ошибка файловой системы, раздел автоматически перемонтируется в режиме только для чтения вместо того, чтобы продолжать записывать поверх потенциально повреждённых структур.

Проверка и восстановление с fsck.fat

VFAT лишён журналирования. Это означает, что любое некорректное размонтирование - отключение питания, принудительное извлечение накопителя - оставляет на диске "грязный бит" (dirty bit) в загрузочном секторе. Windows умеет его читать и предлагает запустить chkdsk при следующем подключении. Linux обрабатывает его через fsck.fat, который является частью пакета dosfstools.

Главное правило: fsck.fat запускается только на размонтированных разделах. Запуск на смонтированной файловой системе - прямой путь к повреждению данных.

Базовая проверка без изменений:

sudo fsck.fat -n /dev/sdb1

Флаг -n означает "no-operation" - инструмент сканирует и сообщает об ошибках, но ничего не пишет на диск. Это первый шаг перед любым восстановлением.

Автоматическое исправление с минимальными разрушениями:

sudo fsck.fat -a /dev/sdb1

При наличии нескольких способов исправить одну проблему fsck.fat всегда выбирает наименее деструктивный. Интерактивный режим, где пользователь сам принимает решения:

sudo fsck.fat -r /dev/sdb1

Развёрнутая диагностика с листингом файлов и маркировкой битых кластеров:

sudo fsck.fat -l -v -a -t /dev/sdb1

Флаг -t помечает нечитаемые кластеры как "плохие", исключая их из использования новыми файлами - поведение аналогично тому, что делает chkdsk в Windows. Восстановленные фрагменты данных из оборванных цепочек кластеров сохраняются в корне раздела под именами fsck0000.rec, fsck0001.rec и так далее.

Верификационный проход, который повторяет проверку дважды:

sudo fsck.fat -V /dev/sdb1

Второй проход не должен находить исправимых ошибок - если находит, значит, первый проход не справился полностью, и нужно анализировать ситуацию глубже. Флаг -c задаёт конкретную кодовую страницу для декодирования коротких имён при проверке:

sudo fsck.fat -c 866 /dev/sdb1

Это полезно, когда накопитель создавался в русскоязычной Windows и shortname-записи закодированы в cp866.

Работа с грязным битом и сложные случаи

Грязный бит - самая частая причина отказа Linux смонтировать VFAT-раздел, особенно после работы с Windows с включённым Fast Startup. Windows с включённой функцией быстрого запуска не выполняет полное размонтирование файловых систем при гибернации, оставляя dirty bit взведённым намеренно. Linux видит это как признак некорректного отключения.

Быстрая очистка грязного бита:

sudo fsck.fat -a /dev/sdb1

Если раздел смонтирован с errors=remount-ro и перешёл в режим только для чтения из-за ошибки, последовательность восстановления выглядит так:

# Размонтировать
sudo umount /mnt/usb

# Проверить и исправить
sudo fsck.fat -a /dev/sdb1

# Смонтировать заново
sudo mount /mnt/usb

Стоит проверить состояние ядерных модулей NLS, если при монтировании появляются ошибки вида FAT: codepage cp866 not found:

# Проверить наличие модулей NLS
ls /lib/modules/$(uname -r)/kernel/fs/nls/ | grep nls_cp

# Загрузить нужный модуль вручную
sudo modprobe nls_cp866
sudo modprobe nls_utf8

Если нужного модуля нет, потребуется пересборка ядра с включённой поддержкой нужной кодовой страницы в разделе "File systems - Native Language Support".

Итоговая конфигурация и здравый смысл

Честно говоря, большинство проблем с VFAT решаются правильным /etc/fstab и пониманием трёх ключевых параметров. Для накопителя, который курсирует между Linux и Windows, рабочая конфигурация выглядит так:

# /etc/fstab - USB-накопитель для работы с Windows
/dev/disk/by-uuid/XXXX-XXXX /mnt/shared vfat \
rw,relatime,uid=1000,gid=1000,\
fmask=0022,dmask=0022,\
codepage=437,\
shortname=mixed,\
utf8,\
errors=remount-ro 0 0

codepage=437 обслуживает короткие имена. shortname=mixed гарантирует создание LFN-записей для файлов с именами в нижнем регистре, сохраняя совместимость. utf8 обеспечивает корректное отображение кириллицы и других не-ASCII символов в длинных именах без нарушения регистронезависимости.

Есть один принцип, который стоит усвоить раз и навсегда: VFAT без LFN-записи - это мина замедленного действия. Режим winnt экономит дисковое место, не создавая дополнительных записей для "простых" имён, но ломает совместимость в самый неожиданный момент. Режим mixed тратит чуть больше пространства в директорном слоте, зато работает предсказуемо на обеих платформах.

VFAT - это файловая система, которая пережила несколько поколений операционных систем и до сих пор остаётся самым надёжным форматом для обмена данными между любыми устройствами. Флешка, вставленная в телевизор, автомагнитолу, фотоаппарат или чужой ноутбук с Windows, - везде её поймут. За эту универсальность и приходится платить осознанной настройкой монтирования, понимая, что происходит под капотом.

https://fileenergy.com/linux