Есть junction типа такого:
mklink /d /j some-link some-folder-that-never-existed
Насколько я понимаю, в линуксе чтобы по такой ссылке получить путь предназначена функция readlink из
А какие функции следует использовать, чтобы на Си++ получить ту же функциональность?
Использовать 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 указывается смещение и длина начала имени файла назначения в буфере в байтах.
Комментариев нет:
Отправить комментарий