ПОДДЕРЖКА ДЛИННЫХ ИМЕН
ФАЙЛОВ ДЛЯ “BARE DOS” 
НА ОСНОВЕ НЕДОКУМЕНТИРОВАННЫХ ФУНКЦИЙ
 
Маргулис М.Б., Мацак С.А.
Кафедра ЭВМ  ДГТУ
 
Abstract
      
        Margulis  M.B., Matsak S.A.   Long file names for “bare DOS”.  Since Windows95 come out users have had
ability to use long file names. This article tells how Windows9x store long
file names and contents a description of methods how write a driver to work
with them  in “bare DOS”.    
 
Введение
 
В течение многих лет
пользователи DOS и Windows испытывали неудобства, связанные с тем, что имена
файлов не могли иметь длину более восьми символов плюс три символа на
расширение. В операционных системах для Macintosh, в OS/2 и Windows NT длинные
имена  LFN (Long File Name) были
предусмотрены  гораздо раньше. С выходом
Windows 95,  такую возможность получила
файловая система VFAT (Virtual File Allocation Table). 
Структура записи для
хранения на диске файлов с именами в формате 8.3 хорошо документирована. Об
особенностях же хранения длинных имен сообщается очень мало.     
Несмотря на то, что
существует встроенная поддержка LFN в DOS 7  в виде набора
функций, они доступны лишь при запушенном драйвере IFSMgr Другими словами,
поддержка LFN обеспечивается  только в оболочке Windows.
В практике работы с
компьютером и, особенно, при ликвидации нестандартных ситуаций, возникает
необходимость произвести операции над файлами или каталогами, без
активизации  Windows  - в однозадачном режиме. Здесь
предлагаются  способы реализации такой
поддержки LFN.
 
1. Формат хранения длинных имен файлов в Windows 9x
 
В FAT каждому файлу 
соответствует 32-байтный элемент каталога, содержащий имя файла, его атрибуты
(скрытый, системный, только для чтения и т.д.), дату и время, а также прочую
информацию.
Прикладные программы  обращаются к ОС за именами файлов и
каталогов не путем прямого считывания с диска соответствующих записей, а через
специальные, встроенные в ОС функции 
(за исключением специальных утилит, использующих для обращения к диску
операции низкого уровня  - типа  Norton Disk Doctor). 
Совместимость
файловых систем VFAT и FAT обеспечивается тем, что для каждого файла и каталога
имеются два имени: короткое, "понятное" всем прикладным программам, и
длинное - для приложений Windows 9x и тех
программ, в которых предусмотрена возможность работы с длинными именами. Для хранения
коротких имен в формате 8.3 используются обычные 32-байтные записи. Короткие
имена Windows создает из длинных имен, отсекая шесть старших символов и
добавляя в конце этого базового имени "~1". Если же существует еще
одно имя, состоящее из тех же шести символов, то этот номер увеличивается на
единицу. Расширение файла сохраняется прежним. 
Длинные имена
хранятся в специально отформатированных 32-байтных записях, байт атрибутов у которых
равен 0Fh (это нереальное сочетание атрибутов). Положение поля атрибутов в
записях каталога как для LFN, так и
формата 8.3 одинаково. Объясняется это тем, что, до тех пор пока файловая
система не ознакомится с содержимым байта атрибутов, она не "знает",
с каким типом записи она имеет дело в данный момент. 
Для конкретного файла
или подкаталога непосредственно перед его единственной записью каталога с его
именем в формате 8.3 находится группа из одной или нескольких записей,
представляющих длинное имя. Каждая такая запись содержит часть длинного имени
файла не более 13-ти символов, т.е. всего может
существовать 20 порций имени (255 div 13). 
LFN хранятся в формате Unicode, т.
е. для каждого символа выделяется 2 байта. Если запись каталога с последней порцией
длинного имени занята не полностью, сразу за последним символом имени должен следовать
завершающий ноль, далее позиции 
заполняются кодом FFh.
Длинное и короткое
имя файла связаны между собой двумя способами: во-первых, короткое имя следует
непосредственно за длинным именем, во-вторых, в каждой порции длинного имени
присутствует контрольная сумма короткого имени. В Windows 9x контрольная сумма используется для выявления
"осиротевших" или испорченных записей в каталоге для длинных имен. 
Формирование записи
для длинного имени происходит даже в том случае, если оно достаточно коротко и
годится для формата 8.3. Структура элемента каталога для длинного имени
показана в приложении 1.
 
2. Набор функций для работы DOS-приложений с LFN
 
Для работы с LFN в DOS 7
предназначены функции 71xxh прерывания
21h [1]:
INT 21 71-- - Windows95 - LONG FILENAME
FUNCTIONS
  
AH = 71h
  
AL = function
     
0Dh reset drive (see AX=710Dh)
     
39h create directory (see AX=7139h)
     
3Ah remove directory (see AX=713Ah)
и т.д.
Эти функции в основном являются аналогами “старых”
функций прерывания 21h для работы с
файлами(AH=39h, AH=3Ah, AH=3Bh  ...).
 
3. Реализация поддержки LFN для “bare DOS”
 
Возможно несколько
путей для решения задачи обеспечения поддержки LFN. 
Первый и наиболее
простой метод - это создание (использование) некой программы менеджера файлов
или нескольких отдельных программ с собственной реализацией алгоритмов чтения
информации с носителей. Недостатком такой реализации является ограниченность
функций и невозможность использования других программ в том числе и внутренних
команд DOS, с поддержкой длинных имен.
Исследования
показали, что в MS DOS 7x  при обработке каждой команды проверяется
поддержка функций 71xxh прерывания 21h. В случае их наличия выполняются процедуры с
поддержкой LFN, иначе вызываются “старые”
функции. Таким образом, логично предположить, что если реализовать обработчики
функций 71xxh прерывания 21h, которые отсутствуют в “bare DOS”, то таким
способом  обеспечивается работоспособность
внутренних команд DOS и других программ с поддержкой
LFN. В этом и заключается второй метод, основные идеи
реализации которого представлена здесь. 
Поставленная задача
достаточно актуальна, т.к. несмотря на то, что имеются попытки разработки этой
темы некоторыми авторами (Приложение 3), в настоящий момент не существует
работоспособного алгоритмического и программного обеспечения этой задачи. 
Для создания
обработчиков функций 71xxh прерывания
21h существуют различные способы. Можно организовать
функции работы с файлами (открытия, создания и т.д.) путем чтения “напрямую” с
дисков, т.е. используя только прерывания 13h или 25h/26h (absolute disk read, absolute disk write). Однако, в этом случае необходимо
правильно заполнять множество внутренних таблиц DOS, что затруднено отсутствием соответствующей
документации. 
Для того чтобы обойти
эту проблему при вызове функций 71xxh
прерывания 21h, можно вызывать “старые” функций
прерывания 21h (работающие с короткими именами).
При этом нужно осуществлять необходимые преобразования параметров.
Следует заметить, что
при этом несколько снизится производительность системы: при каждом вызове
функций 71xxh  должен происходить поиск и преобразование длинного имени файла в
короткое или наоборот, и далее вызываемая “старая” функция еще раз будет искать
элемент каталога с полученным коротким именем.
Пример реализации
функции 713Bh – Смена Текущего Каталога :
int21h_713B:
; Обработчик
функции :
;INT 21 713B - Windows95 - LONG FILENAME - CHANGE
DIRECTORY
;Inp.:   AX =
713Bh
;        DS:DX
-> ASCIZ long name of directory to make current
;Return: CF clear if successful
;        CF set
on error
;            AX
= error code 
         push      
bp
         mov       
bp,sp                  ;
сохраняем указатель стека
;-Создание
короткого пути
         push      
ds                     ; сохраняем используемые
         push       dx                     ;
регистры
        
push       es                     ;
        
push       si                     ;
         push      
di                     ;
; Вызов
процедуры преобразования в путь с коротким именем
;prMakeShortPath 
;Inp.:
;  DS:SI ->
ASCIZ long filename or path
;  ES:DI ->
(possibly 128-byte) buffer for short filename
         mov       
si,dx                  ;
Формирование указателей
         les        di,dword ptr pFspec    ;  
        
call       prMakeShortPath        ; Вызов процедуры
         pop       
di
        
pop        si
         pop       
es
;-Вызов
“старой” функции
;INT 21 3B-- - DOS 2+ - "CHDIR" - SET
CURRENT DIRECTORY
;Inp.:
;        AH =
3Bh
;        DS:DX
-> ASCIZ pathname to become current directory 
;Return: CF clear if successful
;            AX
destroyed
;        CF set
on error
;            AX
= error code 
        
lds        dx,dword ptr pFspec    ; Формирование
        
mov        ah,3Bh
        
int        21h
        
pop        dx
        
pop        ds
        
jnc        iRet713B
iError3B:
        
or         word ptr [bp+6],00001h
; yстановить CF
        
jmp        iExt713B
iRet713B:
        
and        word ptr [bp+6],0FFFEh
; сбpосить CF
iExt713B:
        
pop        bp
         iret
Таким образом, для
организации работы функции смены текущего каталога необходимо лишь осуществить
преобразование пути с длинным именем в путь с коротким.
 
  
 
    
  
   
    
 
    
Упрощенный алгоритм преобразования пути с
длинным именем в путь с коротким или наоборот выглядит следующим образом:
Рис.1.
Рис. 
Структурная схема алгоритма преобразования пути с длинным именем в путь с
коротким или наоборот
где:
 prGetPartOfPath – процедура выделения порции пути
и формирования маски для поиска короткого имени
 prWildcardCompare – процедура сравнения двух строк
(строки и маски)
 prGetDPB – процедура получения Drive
Parameter Block (DPB)
 prGetLFN – процедура вычисления и чтения номера сектора с коротким именем
- по номеру кластера из недокументированного поля Disk Transfer Area заполненного
функцией 4Eh/4Fh int21h (см. Приложение2), а также по
номеру элемента каталога; далее, если это необходимо, происходит формирование LFN.
Процедуры  prFindFist714E,FindNext714F  используются также в обработчике функции 714Eh/714Fh прерывания 21h.
Таким образом, процедура prMakeFindData будет иметь вид:
prMakeFindData proc near
        
mov        byte ptr
[LFNOk_MFD],01h ; set lfn error flag =ok
;--get "current" drive from FindFirst data
block(int 21 4Eh/4Fh)
        
lds        si,dword ptr
pCurFindFileHandle   ; указатель на data block
        
mov        al,byte ptr ds:[si]               ; drive
        
and        al,7Fh
;--if new current drive == old drive then do not read
DPB
        
cmp        al,[CurDrive]         ; текущий диск
        
je         l21_MFD
        
mov        [CurDrive],al
l2_MFD:
        
call       prGetDPB              ; чтение Drive Parameter Block (DPB)
        
jc         ErrorMFD
l21_MFD: call      
prGetLFN              ; чтение длинного имени 
         jnc        l4_MFD                ; carry=1 если была ошибка или 
l3_MFD:                                   ;
это короткое имя
         mov        byte ptr [LFNOk_MFD],00h ; error
geting LFN
l4_MFD:
;Заполнение
FindData
record для 714Eh/714Fh int21h(приведено в сводной таблице1)
Таблица   Сводная таблица форматов FindData record для 714Eh/714Fh int21h и 
FindFirst data block для 4Eh/4Fh int21h.
| Format
  of LFN FindData record | Format
  of FindFirst data block | Description = идентичны ~  условно равны  *  новое поле | |||
| Offset | Size | Offset | Size | ||
| 00h | DWORD | 15h | BYTE | = | file attributes bits 0-6 standard DOS attributes bit 8: temporary file | 
| 04h | QWORD | 16h 18h | WORD WORD | ~ | file creation time/date (if SI=0 number of 100ns intervals
  since 1/1/1601) | 
| 0Ch | QWORD |   |   | * | last access time | 
| 14h | QWORD |   |   | * | last modification time | 
| 1Ch | DWORD |   |   | * | file size (high 32 bits) | 
| 20h | DWORD | 1Ah | DWORD | = | file size (low 32 bits) | 
| 24h | 8 BYTEs |   |   | ? | Reserved | 
| 2Ch | 260 BYTEs |   |   | * | ASCIZ full filename =long or short | 
| 130h | 14 BYTEs | 1Eh | 13 BYTEs | ~ | ASCIZ short filename if long ^ or 0h
  if short | 
        
lds        si,dword ptr
pCurFindFileHandle
        
add        si,15h
        
les        di,dword ptr pFindData
; далее идет копирование одинаковых реквизитов из FindFirst data block
; (int 21 4Eh/4Fh) (даты, времени, размера)
...............
;--Если
длинное имя не было сформировано то копируется короткое
         cmp        byte ptr [LFNOk_MFD],01h ;lfn error
flag
        
je         lOk_MFD
        
mov        cx,13
         rep
movsb      ; store short file name
        
add        di,0F7h
        
stosb  ;0h
        
jmp        RetMFD
lOk_MFD:                     ;--store short filename to end if lfn=ok
        
add        di,260   ; skip lfn
        
mov        cx,13
         rep
movsb           ; store short file name
        
jmp        RetMFD
ErrorMFD:stc
        
jmp        ExtGetLFN
RetMFD:  clc
ExtMFD:  ret
LFNOk_MFD          
db  00h ; set lfn error flag
clear
         endp       prMakeFindData
Приведенный здесь
алгоритм чтения LFN, позволяет избавится от работы
с таблицей FAT, и поиска кластера каталога. В
случае любой ошибки, результат представляется в виде короткого имени. 
 
Заключение
Современные методы
работы файловых систем невозможны без использования расширенного способа записи
имен файлов и каталогов – LFN. Эти
требования долны выполнятся в любых современных операционных системах. 
Несмотря на то, что в
наиболее распространненой ОС Windows9x реализована поддержка LFN, в некоторых критически
ситуациях возникает необходимость обрабатывать файлы с LFN под управлением MS DOS – без
участия Windows9x (режим “bare DOS”).
Литература 
1.     
Interrupt List (c) by Ralf Brown Release 57 (http://www.pobox.com/~ralf)
2.     
Джефф Просис,  “Как Windows 95 хранит длинные имена файлов”
, PC Magazine,
June 25, 1996, p. 217
 
Приложение 1
Порядок следования (1 байт)
   Биты 0-4: Порядковый номер (1-31)
   Бит 5: По видимому, не используется (всегда
0)
   Бит 6: 1=конечный элемент текущего длинного
имени
   Бит 7: По видимому, не используется (всегда
0)
   │                                              Номер начального
кластера
   │                                              (2 байта, всегда 0)
   │                                                   │
│ Первые пять символов Следующие шесть символов│ Следующие два символа
   │ длинного имени (10 байт)  длинного имени (12 байт)│  длинного
имени(4 байт)
   │
┌───────┴─────────┐       ┌──────────┴──────────┐
├─┐ ┌──┴──┐
 
┌┴┬┴┬─┬─┬─┬─┬─┬─┬─┬─┬┴┬─┬─┬─┬┴┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬┴┬┴┬┴┬┴┬─┬─┬┴┐
 
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴┬┴┬┴┬┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
                         │ │
│
                         │ │
Контрольная сумма (1 байт)
                         │ Указатель
типа (1 байт, всегда 0)
Атрибуты файла (1 байт, всегда 0Fh)
 
Рис. 2 Элемент каталога для длинного имени
 
Приложение 2
Таблица 2 Формат FindFirst data block для 4Eh/4Fh int21h с недокументированными полями
| Offset | Size | Description | 
| ---PC-DOS
  3.10, PC-DOS 4.01, MS-DOS 3.2/3.3/5.0--- | ||
| 00h | BYTE | drive letter (bits 0-6),
  remote if bit 7 set | 
| 01h | 11
  BYTEs | search template | 
| 0Ch | BYTE | search attributes | 
| 0Dh | WORD | entry count within directory | 
| 0Fh | WORD | cluster number of start of
  parent directory | 
| ---
  MS-DOS 7.0--- | ||
| 11h | WORD | hi cluster number of start
  of parent directory | 
| 13h | WORD | =3D08 ??? | 
| ---all
  versions, documented fields--- | ||
| 15h | BYTE | attribute of file found | 
| 16h | WORD | file time | 
| 18h | WORD | file date | 
| 1Ah | DWORD | file size | 
| 1Eh | 13
  BYTEs | ASCIZ filename+extension | 
 
Приложение 3
Ссылки в Internet:
Odi's LFN Tools 1.43 by Ortwin
Glueck (with source) 
http://odi.webjump.com/ 
ADIR v1.05  (c) 1998 Chris Jones 
http://members.xoom.com/dosuser/dosutils.htm
LFNDir v1.0 (c) 1998
Ziff-Davis Publishing Company by Rick Knoblaugh (with source)
http://www.zdnet.com/pcmag/pctech/content/17/09/ut1709.001.html
OpenDOS Long File Name (LFN)
Support Beta 3 (c) 1997 Caldera, Inc.
LFNDOS: DOS LFN driver (c)
1998, 1999 Chris Jones
http://members.xoom.com/dosuser