There is a junction like this:

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

As far as I understand, in Linux, for such a link, the readlink function from <unistd.h> is used to get the path, and the nodejs function is fs.readlink (it works in windows).

And what functions should I use to get the same functionality in C ++?

Use CreateFile to get the handle, and then GetFinalPathNameByHandle to get the file name does not match, because the target file does not exist. And using the FILE_FLAG_OPEN_REPARSE_POINT flag results in the path to the link itself, not to the target folder.

  • Comments are not intended for extended discussion; conversation moved to chat . - PashaPash

1 answer 1

The standard way to get the name of the target file / directory of symbolic links in Delphi is to use the TFile.getSymLinkTarget() method from System.IOUtils . However, if the link is incorrect (the target file is missing), then the call to this function causes an exception.

It was also not possible to use CreateFile and GetFinalPathNameByHandle to get the desired result.

As a test example, I created the file d:\data.txt , created a link to it mklink sl.txt data.txt , then renamed the file data.txt . Thus the link becomes incorrect.

As an alternative option, you can use the deviceIoControl() function to get the necessary information, which allows you to send commands directly to devices, which may include a file system. Among the management commands for working with directories is the FSCTL_GET_REPARSE_POINT code for getting information about links and mount points. As a result of a call to this command, a REPARSE_DATA_BUFFER structure is REPARSE_DATA_BUFFER in which the necessary data is not REPARSE_DATA_BUFFER in an entirely logical way.

Unfortunately, my delphi skills have been somewhat lost lately when I didn’t work with it, so the code can be somewhat clumsy. I rewrote the structure as follows (it seems to be in 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; 

I don’t really understand what size the output buffer needs to be transferred, but the calling code must allocate space for it. Otherwise, an error will be received that the size of the structure is insufficient.

 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. 

When the file is FILE_FLAG_OPEN_REPARSE_POINT , the FILE_FLAG_OPEN_REPARSE_POINT flag is FILE_FLAG_OPEN_REPARSE_POINT , which returns the descriptor of the link, not the destination file. For links to directories, FILE_FLAT_BACKUP_SEMANTICS required (honestly peeped in TFile.getSymlinkTarget() ).

The TReparseDataBuffer structure specifies the offset and length of the beginning of the destination file name in the buffer in bytes.

  • If you’re looking for a new one, you’ll have to write it. That is, if it can, it will create a new file, and if it cannot, is it still a mistake? Something is not right ... - Qwertiy
  • @Qwertiy damn I'm a sucker. He really created a new data.txt file: D - teran
  • Thank! And for the future, when editing or restoring the answer, the author of the question is not notified. - Qwertiy
  • one
    @Qwertiy about notifications will know :) strange, in general, of course. About the editing is understandable, but about the restoration, strange. - teran