|
| 1 | +unit Junctions; |
| 2 | + |
| 3 | +// http://progmatix.blogspot.com/2010/10/get-target-of-symlink-in-delphi.html |
| 4 | +// https://delphisources.ru/pages/faq/base/hardlink_symbolic_link.html |
| 5 | +// http://www.flexhex.com/docs/articles/hard-links.phtml#junctions |
| 6 | +// https://fossil.2of4.net/zaap/artifact/ad9fc313554aea05 |
| 7 | + |
| 8 | +interface |
| 9 | + |
| 10 | +const |
| 11 | + FILE_ATTRIBUTE_REPARSE_POINT = 1024; |
| 12 | + |
| 13 | +function GetSymLinkTarget(const AFilename: Widestring): Widestring; |
| 14 | +function CreateJunction(const ALink,ADest:WideString): Boolean; |
| 15 | + |
| 16 | +implementation |
| 17 | + |
| 18 | +uses Windows; |
| 19 | + |
| 20 | +const |
| 21 | + MAX_REPARSE_SIZE = 17000; |
| 22 | + MAX_NAME_LENGTH = 1024; |
| 23 | + REPARSE_MOUNTPOINT_HEADER_SIZE = 8; |
| 24 | + IO_REPARSE_TAG_MOUNT_POINT = $0A0000003; |
| 25 | + FILE_FLAG_OPEN_REPARSE_POINT = $00200000; |
| 26 | + FILE_DEVICE_FILE_SYSTEM = $0009; |
| 27 | + FILE_ANY_ACCESS = 0; |
| 28 | + METHOD_BUFFERED = 0; |
| 29 | + FSCTL_SET_REPARSE_POINT = (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or (41 shl 2) or (METHOD_BUFFERED); |
| 30 | + FSCTL_GET_REPARSE_POINT = (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or (42 shl 2) or (METHOD_BUFFERED); |
| 31 | + |
| 32 | +type |
| 33 | + REPARSE_DATA_BUFFER = packed record |
| 34 | + ReparseTag: DWORD; |
| 35 | + ReparseDataLength: Word; |
| 36 | + Reserved: Word; |
| 37 | + SubstituteNameOffset: Word; |
| 38 | + SubstituteNameLength: Word; |
| 39 | + PrintNameOffset: Word; |
| 40 | + PrintNameLength: Word; |
| 41 | + PathBuffer: array[0..0] of WideChar; |
| 42 | + end; |
| 43 | + TReparseDataBuffer = REPARSE_DATA_BUFFER; |
| 44 | + PReparseDataBuffer = ^TReparseDataBuffer; |
| 45 | + |
| 46 | + REPARSE_MOUNTPOINT_DATA_BUFFER = packed record |
| 47 | + ReparseTag: DWORD; |
| 48 | + ReparseDataLength: DWORD; |
| 49 | + Reserved: Word; |
| 50 | + ReparseTargetLength: Word; |
| 51 | + ReparseTargetMaximumLength: Word; |
| 52 | + Reserved1: Word; |
| 53 | + ReparseTarget: array[0..0] of WideChar; |
| 54 | + end; |
| 55 | + TReparseMountPointDataBuffer = REPARSE_MOUNTPOINT_DATA_BUFFER; |
| 56 | + PReparseMountPointDataBuffer = ^TReparseMountPointDataBuffer; |
| 57 | + |
| 58 | + Function CreateSymbolicLinkW(Src,Target:PWideChar;Flags:Cardinal):BOOL; Stdcall; External 'kernel32.dll'; |
| 59 | + |
| 60 | +function OpenDirectory(const ADir:WideString;bReadWrite:Boolean):THandle; |
| 61 | +var |
| 62 | + token:THandle; |
| 63 | + tp:TTokenPrivileges; |
| 64 | + bp:WideString; |
| 65 | + dw,access:DWORD; |
| 66 | +begin |
| 67 | + // Obtain backup/restore privilege in case we don't have it |
| 68 | + OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, token); |
| 69 | + If bReadWrite Then bp:='SeRestorePrivilege' else bp:='SeBackupPrivilege'; |
| 70 | + LookupPrivilegeValueW(NIL, PWideChar(bp), tp.Privileges[0].Luid); |
| 71 | + tp.PrivilegeCount := 1; |
| 72 | + tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; |
| 73 | + AdjustTokenPrivileges(token, FALSE, tp, sizeof(TOKEN_PRIVILEGES), NIL, dw); |
| 74 | + CloseHandle(token); |
| 75 | + |
| 76 | + // Open the directory |
| 77 | + access:=GENERIC_READ; |
| 78 | + if bReadWrite then access:=access or GENERIC_WRITE; |
| 79 | + Result := CreateFileW(PWideChar(ADir), access, 0, NIL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT or FILE_FLAG_BACKUP_SEMANTICS, 0); |
| 80 | +end; |
| 81 | + |
| 82 | +function GetSymLinkTarget(const AFilename: WideString): Widestring; |
| 83 | +var |
| 84 | + hDir:THandle; |
| 85 | + nRes:DWORD; |
| 86 | + reparseInfo: PReparseDataBuffer; |
| 87 | + name2: array[0..MAX_NAME_LENGTH-1] of WideChar; |
| 88 | +begin |
| 89 | + Result := ''; |
| 90 | + hDir:= OpenDirectory(AFilename,False); |
| 91 | + if hDir = INVALID_HANDLE_VALUE then Exit; |
| 92 | + GetMem(reparseInfo,MAX_REPARSE_SIZE); |
| 93 | + if DeviceIoControl(hDir, FSCTL_GET_REPARSE_POINT, nil, 0, reparseInfo, MAX_REPARSE_SIZE, nRes, nil) Then |
| 94 | + If reparseInfo.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT then |
| 95 | + Begin |
| 96 | + FillChar(name2, SizeOf(name2), 0); |
| 97 | + lstrcpynW(name2, reparseInfo.PathBuffer + reparseInfo.SubstituteNameOffset, reparseInfo.SubstituteNameLength); |
| 98 | + Result:= Copy(name2,5,Length(name2)); // remove the '\??\' prefix |
| 99 | + end; |
| 100 | + FreeMem(reparseInfo,MAX_REPARSE_SIZE); |
| 101 | + CloseHandle(hDir); |
| 102 | +end; |
| 103 | + |
| 104 | +// target must NOT begin with "\??\" - it will be added automatically |
| 105 | +Function CreateJunction(const ALink,ADest:WideString):Boolean; |
| 106 | +Const |
| 107 | + LinkPrefix: WideString = '\??\'; |
| 108 | +var |
| 109 | + Buffer: PReparseMountPointDataBuffer; |
| 110 | + BufSize: integer; |
| 111 | + TargetName: WideString; |
| 112 | + hDir:THandle; |
| 113 | + dw:DWORD; |
| 114 | +Begin |
| 115 | + Result:=False; |
| 116 | + hDir:=OpenDirectory(ALink,True); |
| 117 | + If hDir = INVALID_HANDLE_VALUE then Exit; |
| 118 | + If Pos(LinkPrefix,ADest)=1 then TargetName:=ADest else TargetName:=LinkPrefix+ADest; |
| 119 | + BufSize:=(Length(TargetName)+1)*SizeOf(WideChar) + REPARSE_MOUNTPOINT_HEADER_SIZE + 12; |
| 120 | + GetMem(Buffer,BufSize); |
| 121 | + FillChar(Buffer^,BufSize,#0); |
| 122 | + With Buffer^ Do |
| 123 | + Begin |
| 124 | + Move(TargetName[1], ReparseTarget, (Length(TargetName)+1)*SizeOf(WideChar)); |
| 125 | + ReparseTag:= IO_REPARSE_TAG_MOUNT_POINT; |
| 126 | + ReparseTargetLength:= Length(TargetName)*SizeOf(WideChar); |
| 127 | + ReparseTargetMaximumLength:= ReparseTargetLength+2; |
| 128 | + ReparseDataLength:= ReparseTargetLength+12; |
| 129 | + end; |
| 130 | + Result:=DeviceIoControl(hDir,FSCTL_SET_REPARSE_POINT,Buffer,Buffer.ReparseDataLength + REPARSE_MOUNTPOINT_HEADER_SIZE,Nil,0,dw,Nil); |
| 131 | + FreeMem(Buffer,BufSize); |
| 132 | + CloseHandle(hDir); |
| 133 | +end; |
| 134 | + |
| 135 | +end. |
0 commit comments