Страницы

Поиск по вопросам

среда, 13 марта 2019 г.

Узнать, куда указывает символьная ссылка

Есть junction типа такого:
mklink /d /j some-link some-folder-that-never-existed
Насколько я понимаю, в линуксе чтобы по такой ссылке получить путь предназначена функция readlink из , а nodejs - функция fs.readlink (в windows она работает).
А какие функции следует использовать, чтобы на Си++ получить ту же функциональность?
Использовать CreateFile для получения хэндла, а затем GetFinalPathNameByHandle для получения имени файла не подходит, т. к. целевой файл не существует. А при использовании флага FILE_FLAG_OPEN_REPARSE_POINT получается путь к самой ссылке, а не к целевой папке.


Ответ

Стандартным путем для получения имени целевого файла/директории символических ссылок в Delphi используется метод TFile.getSymLinkTarget() из System.IOUtils. Однако, если ссылка некорректная (целевой файл отсутствует) то вызов сей функции вызывает исключение.
Использовать CreateFile и GetFinalPathNameByHandle для получения желаемого результата тоже не удалось.
В качестве тестового примера я создал файл d:\data.txt, создал на него ссылку mklink sl.txt data.txt, затем переименовал файл data.txt. Таким образом ссылка становится некорректной.
В качества альтернативного варианта для получения нужной информации можно воспользоваться функцией deviceIoControl(), позволяющей отправлять команды непосредственно устройствам, среди которых может быть файловая система. Среди управляющих команд работы с директориями есть код FSCTL_GET_REPARSE_POINT для получения сведений о ссылках и точках монтирования. В качестве результата вызова этой команды возвращается структура REPARSE_DATA_BUFFER в которой не совсем логичным образом представлены необходимые данные.
К сожалению мои навыки работы с delphi несколько утратились за последнее время когда я с ним не работал, так что код может быть несколько корявым. Структуру переписал следующим образом (она вроде есть в JVCL):
type REPARSE_DATA_BUFFER = packed record ReparseTag: Cardinal; ReparseDataLength: Word; Reserved: Word; SubstituteNameOffset: Word; SubstituteNameLength: Word; PrintNameOffset: Word; PrintNameLength: Word; Flags: Cardinal; PathBuffer: array[0..1000] of WideChar; end;
TReparseDataBuffer = REPARSE_DATA_BUFFER; PReparseDataBuffer = ^TReparseDataBuffer;
Не особо мне понятно, какого размера буфер для выходных данных необходимо передавать, но выделить место под него должен вызывающий код. В противном случае будет получена ошибка, что недостаточный размер структуры.
const SL_NAME = 'D:\sl.txt'; var h : THandle; path : string; rdb : TReparseDataBuffer; br : cardinal; res : Longbool; begin ZeroMemory(@rdb, sizeof(rdb));;
h := CreateFile(SL_NAME, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT + FILE_FLAG_BACKUP_SEMANTICS, 0);
res := DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, nil, 0, @rdb, sizeof(rdb), br, nil);
SetString(path, pchar(@rdb.PathBuffer[rdb.PrintNameOffset div sizeof(WideChar)]), rdb.PrintNameLength div sizeof(WideChar));
writeln('target:', path); CloseHandle(h);
readln; end.
При открытии файла добавляется флаг FILE_FLAG_OPEN_REPARSE_POINT, возвращающий дескриптор ссылки, а не файла назначения. Для ссылок на каталоги необходим FILE_FLAT_BACKUP_SEMANTICS (честно подсмотрено в кодe TFile.getSymlinkTarget()).
В структуре TReparseDataBuffer указывается смещение и длина начала имени файла назначения в буфере в байтах.

Комментариев нет:

Отправить комментарий