2
0

dcwindows.pas 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. {
  2. Double commander
  3. -------------------------------------------------------------------------
  4. This unit contains Windows specific functions
  5. Copyright (C) 2015-2019 Alexander Koblov ([email protected])
  6. This library is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU Lesser General Public
  8. License as published by the Free Software Foundation; either
  9. version 2.1 of the License, or (at your option) any later version.
  10. This library is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. Lesser General Public License for more details.
  14. You should have received a copy of the GNU Lesser General Public
  15. License along with this library; if not, write to the Free Software
  16. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. }
  18. unit DCWindows;
  19. {$mode objfpc}{$H+}
  20. interface
  21. uses
  22. Windows;
  23. {en
  24. Converts file name in UTF-8 encoding to file name
  25. with UTF-16 encoding with extended-length path prefix
  26. }
  27. function UTF16LongName(const FileName: String): UnicodeString;
  28. {en
  29. Enable a privilege
  30. @param(hToken Access token handle)
  31. @param(lpszPrivilege Name of privilege to enable)
  32. @returns(The function returns @true if successful, @false otherwise)
  33. }
  34. function EnablePrivilege(hToken: HANDLE; lpszPrivilege: LPCTSTR): Boolean;
  35. {en
  36. Copy permissions specific to the NTFS file system,
  37. like read and write permissions, and the file owner
  38. }
  39. function CopyNtfsPermissions(const Source, Target: String): Boolean;
  40. {en
  41. Copy extended attributes
  42. specific to the NTFS file system, like FILE_ATTRIBUTE_COMPRESSED
  43. }
  44. function mbFileCopyXattr(const Source, Target: String): Boolean;
  45. {en
  46. Retrieves the final path for the specified file
  47. }
  48. function GetFinalPathNameByHandle(hFile: THandle): UnicodeString;
  49. {en
  50. Retrieves the file system type name
  51. }
  52. function GetFileSystemType(const Path: String): UnicodeString;
  53. implementation
  54. uses
  55. SysUtils, JwaAclApi, JwaWinNT, JwaAccCtrl, JwaWinBase, JwaWinType, JwaNative,
  56. JwaNtStatus, DCConvertEncoding;
  57. var
  58. GetFinalPathNameByHandleW: function(hFile: HANDLE; lpszFilePath: LPWSTR; cchFilePath: DWORD; dwFlags: DWORD): DWORD; stdcall;
  59. NtQueryObject: function(ObjectHandle : HANDLE; ObjectInformationClass : OBJECT_INFORMATION_CLASS; ObjectInformation : PVOID; ObjectInformationLength : ULONG; ReturnLength : PULONG): NTSTATUS; stdcall;
  60. function UTF16LongName(const FileName: String): UnicodeString;
  61. var
  62. Temp: PWideChar;
  63. begin
  64. if Pos('\\', FileName) = 0 then
  65. Result := '\\?\' + CeUtf8ToUtf16(FileName)
  66. else begin
  67. Result := '\\?\UNC\' + CeUtf8ToUtf16(Copy(FileName, 3, MaxInt));
  68. end;
  69. Temp := Pointer(Result) + 4;
  70. while Temp^ <> #0 do
  71. begin
  72. if Temp^ = '/' then Temp^:= '\';
  73. Inc(Temp);
  74. end;
  75. if ((Temp - 1)^ = DriveSeparator) then Result:= Result + '\';
  76. end;
  77. function EnablePrivilege(hToken: HANDLE; lpszPrivilege: LPCTSTR): Boolean;
  78. var
  79. tp: TTokenPrivileges;
  80. luid: TLuid = (LowPart: 0; HighPart: 0);
  81. begin
  82. if (not LookupPrivilegeValue(nil, lpszPrivilege, luid)) then
  83. Exit(False);
  84. tp.PrivilegeCount:= 1;
  85. tp.Privileges[0].Luid:= luid;
  86. tp.Privileges[0].Attributes:= SE_PRIVILEGE_ENABLED;
  87. // Enable privilege in the specified access token
  88. if (not AdjustTokenPrivileges(hToken, False, @tp, SizeOf(TTokenPrivileges), nil, nil)) then
  89. Exit(False);
  90. // Not all privileges or groups referenced are assigned to the caller
  91. Result:= not (GetLastError() = ERROR_NOT_ALL_ASSIGNED);
  92. end;
  93. function CopyNtfsPermissions(const Source, Target: String): Boolean;
  94. const
  95. DisabledPrivilege: Boolean = True;
  96. var
  97. Dacl, Sacl: PACL;
  98. lpdwRevision: DWORD = 0;
  99. ProcessToken: HANDLE = 0;
  100. SidOwner, SidGroup: PSID;
  101. SecDescPtr: PSECURITY_DESCRIPTOR = nil;
  102. SecDescCtl: SECURITY_DESCRIPTOR_CONTROL = 0;
  103. SecurityInfo: SECURITY_INFORMATION = DACL_SECURITY_INFORMATION or SACL_SECURITY_INFORMATION or
  104. OWNER_SECURITY_INFORMATION or GROUP_SECURITY_INFORMATION;
  105. begin
  106. if DisabledPrivilege then
  107. begin
  108. DisabledPrivilege:= False;
  109. Result:= OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, ProcessToken);
  110. if not Result then
  111. Exit(False)
  112. else begin
  113. EnablePrivilege(ProcessToken, SE_BACKUP_NAME);
  114. EnablePrivilege(ProcessToken, SE_RESTORE_NAME);
  115. EnablePrivilege(ProcessToken, SE_SECURITY_NAME);
  116. CloseHandle(ProcessToken);
  117. end;
  118. end;
  119. Result:= GetNamedSecurityInfoW(PWideChar(CeUtf8ToUtf16(Source)), SE_FILE_OBJECT, SecurityInfo,
  120. @SidOwner, @SidGroup, @Dacl, @Sacl, SecDescPtr) = ERROR_SUCCESS;
  121. if Result then
  122. begin
  123. if GetSecurityDescriptorControl(SecDescPtr, SecDescCtl, lpdwRevision) then
  124. begin
  125. // Need to copy DACL inheritance
  126. if (SecDescCtl and SE_DACL_PROTECTED <> 0) then
  127. SecurityInfo:= SecurityInfo or PROTECTED_DACL_SECURITY_INFORMATION
  128. else begin
  129. SecurityInfo:= SecurityInfo or UNPROTECTED_DACL_SECURITY_INFORMATION;
  130. end;
  131. // Need to copy SACL inheritance
  132. if (SecDescCtl and SE_SACL_PROTECTED <> 0) then
  133. SecurityInfo:= SecurityInfo or PROTECTED_SACL_SECURITY_INFORMATION
  134. else begin
  135. SecurityInfo:= SecurityInfo or UNPROTECTED_SACL_SECURITY_INFORMATION;
  136. end;
  137. Result:= SetNamedSecurityInfoW(PWideChar(CeUtf8ToUtf16(Target)), SE_FILE_OBJECT,
  138. SecurityInfo, SidOwner, SidGroup, Dacl, Sacl) = ERROR_SUCCESS;
  139. end;
  140. {$PUSH}{$HINTS OFF}{$WARNINGS OFF}
  141. LocalFree(HLOCAL(SecDescPtr));
  142. {$POP}
  143. end;
  144. end;
  145. function mbFileCopyXattr(const Source, Target: String): Boolean;
  146. const
  147. FSCTL_SET_COMPRESSION = $9C040;
  148. COMPRESSION_FORMAT_DEFAULT = 1;
  149. var
  150. dwFlags: DWORD;
  151. Handle: THandle;
  152. LastError: DWORD;
  153. BytesReturned: DWORD;
  154. dwFileAttributes: DWORD;
  155. Format: UInt16 = COMPRESSION_FORMAT_DEFAULT;
  156. lpszVolumePathName: array[0..maxSmallint] of WideChar;
  157. begin
  158. Result:= True;
  159. dwFileAttributes:= GetFileAttributesW(PWideChar(UTF16LongName(Source)));
  160. if (dwFileAttributes and FILE_ATTRIBUTE_COMPRESSED <> 0) then
  161. begin
  162. if (dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> 0) then
  163. dwFlags:= FILE_FLAG_BACKUP_SEMANTICS
  164. else begin
  165. dwFlags:= 0;
  166. end;
  167. dwFileAttributes:= GetFileAttributesW(PWideChar(UTF16LongName(Target)));
  168. if (dwFileAttributes and FILE_ATTRIBUTE_COMPRESSED <> 0) or
  169. (dwFileAttributes and FILE_ATTRIBUTE_ENCRYPTED <> 0) then
  170. Exit;
  171. if GetVolumePathNameW(PWideChar(UTF16LongName(Target)), PWideChar(lpszVolumePathName), maxSmallint) then
  172. begin
  173. if GetVolumeInformationW(lpszVolumePathName, nil, 0, nil, LastError, dwFileAttributes, nil, 0) then
  174. begin
  175. if (dwFileAttributes and FILE_FILE_COMPRESSION = 0) then Exit;
  176. end;
  177. end;
  178. Handle:= CreateFileW(PWideChar(UTF16LongName(Target)), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, dwFlags, 0);
  179. if Handle <> INVALID_HANDLE_VALUE then
  180. begin
  181. Result:= DeviceIoControl(Handle, FSCTL_SET_COMPRESSION, @Format, SizeOf(Format), nil, 0, @BytesReturned, nil);
  182. if not Result then LastError:= GetLastError;
  183. CloseHandle(Handle);
  184. end;
  185. end;
  186. if not Result then SetLastError(LastError);
  187. end;
  188. function GetFinalPathNameByHandle(hFile: THandle): UnicodeString;
  189. const
  190. VOLUME_NAME_NT = $02;
  191. MAX_SIZE = SizeOf(TObjectNameInformation) + MAXWORD;
  192. var
  193. ReturnLength : ULONG;
  194. ObjectInformation : PObjectNameInformation;
  195. begin
  196. if (Win32MajorVersion > 5) then
  197. begin
  198. SetLength(Result, maxSmallint + 1);
  199. SetLength(Result, GetFinalPathNameByHandleW(hFile, PWideChar(Result), maxSmallint, VOLUME_NAME_NT));
  200. end
  201. else begin
  202. ObjectInformation:= GetMem(MAX_SIZE);
  203. if (NtQueryObject(hFile, ObjectNameInformation, ObjectInformation, MAXWORD, @ReturnLength) <> STATUS_SUCCESS) then
  204. Result:= EmptyWideStr
  205. else begin
  206. SetLength(Result, ObjectInformation^.Name.Length div SizeOf(WideChar));
  207. Move(ObjectInformation^.Name.Buffer^, Result[1], ObjectInformation^.Name.Length);
  208. end;
  209. FreeMem(ObjectInformation);
  210. end;
  211. end;
  212. function GetFileSystemType(const Path: String): UnicodeString;
  213. var
  214. lpFileSystemFlags: DWORD = 0;
  215. lpMaximumComponentLength: DWORD = 0;
  216. lpFileSystemNameBuffer: array [Byte] of WideChar;
  217. begin
  218. if GetVolumeInformationW(PWideChar(CeUtf8ToUtf16(ExtractFileDrive(Path)) + PathDelim),
  219. nil, 0, nil, lpMaximumComponentLength, lpFileSystemFlags,
  220. lpFileSystemNameBuffer, SizeOf(lpFileSystemNameBuffer)) then
  221. Result:= lpFileSystemNameBuffer
  222. else
  223. Result:= EmptyWideStr;
  224. end;
  225. procedure Initialize;
  226. begin
  227. if Win32MajorVersion < 6 then
  228. Pointer(NtQueryObject):= GetProcAddress(GetModuleHandleW(ntdll), 'NtQueryObject')
  229. else begin
  230. Pointer(GetFinalPathNameByHandleW):= GetProcAddress(GetModuleHandleW(kernel32), 'GetFinalPathNameByHandleW');
  231. end;
  232. end;
  233. initialization
  234. Initialize;
  235. end.