Страницы

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

среда, 22 января 2020 г.

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

#cpp #delphi #windows #winapi #cppbuilder


Есть junction типа такого:

mklink /d /j some-link some-folder-that-never-existed


Насколько я понимаю, в линуксе чтобы по такой ссылке получить путь предназначена
функция readlink из , а nodejs - функция fs.readlink (в windows она работает).

А какие функции следует использовать, чтобы на Си++ получить ту же функциональность?

Использовать CreateFile для получения хэндла, а затем GetFinalPathNameByHandle для
получения имени файла не подходит, т. к. целевой файл не существует. А при использовании
флага FILE_FLAG_OPEN_REPARSE_POINT получается путь к самой ссылке, а не к целевой папке.
    


Ответы

Ответ 1



Стандартным путем для получения имени целевого файла/директории символических ссылок в 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 указывается смещение и длина начала имени файла назначения в буфере в байтах.

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

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