1 // Windows/FileLink.cpp 2 3 #include "StdAfx.h" 4 5 #include "../../C/CpuArch.h" 6 7 #ifdef SUPPORT_DEVICE_FILE 8 #include "../../C/Alloc.h" 9 #endif 10 11 #include "FileDir.h" 12 #include "FileFind.h" 13 #include "FileIO.h" 14 #include "FileName.h" 15 16 #ifndef _UNICODE 17 extern bool g_IsNT; 18 #endif 19 20 namespace NWindows { 21 namespace NFile { 22 23 using namespace NName; 24 25 /* 26 Reparse Points (Junctions and Symbolic Links): 27 struct 28 { 29 UInt32 Tag; 30 UInt16 Size; // not including starting 8 bytes 31 UInt16 Reserved; // = 0 32 33 UInt16 SubstituteOffset; // offset in bytes from start of namesChars 34 UInt16 SubstituteLen; // size in bytes, it doesn't include tailed NUL 35 UInt16 PrintOffset; // offset in bytes from start of namesChars 36 UInt16 PrintLen; // size in bytes, it doesn't include tailed NUL 37 38 [UInt32] Flags; // for Symbolic Links only. 39 40 UInt16 namesChars[] 41 } 42 43 MOUNT_POINT (Junction point): 44 1) there is NUL wchar after path 45 2) Default Order in table: 46 Substitute Path 47 Print Path 48 3) pathnames can not contain dot directory names 49 50 SYMLINK: 51 1) there is no NUL wchar after path 52 2) Default Order in table: 53 Print Path 54 Substitute Path 55 */ 56 57 /* 58 static const UInt32 kReparseFlags_Alias = (1 << 29); 59 static const UInt32 kReparseFlags_HighLatency = (1 << 30); 60 static const UInt32 kReparseFlags_Microsoft = ((UInt32)1 << 31); 61 62 #define _my_IO_REPARSE_TAG_HSM (0xC0000004L) 63 #define _my_IO_REPARSE_TAG_HSM2 (0x80000006L) 64 #define _my_IO_REPARSE_TAG_SIS (0x80000007L) 65 #define _my_IO_REPARSE_TAG_WIM (0x80000008L) 66 #define _my_IO_REPARSE_TAG_CSV (0x80000009L) 67 #define _my_IO_REPARSE_TAG_DFS (0x8000000AL) 68 #define _my_IO_REPARSE_TAG_DFSR (0x80000012L) 69 */ 70 71 #define Get16(p) GetUi16(p) 72 #define Get32(p) GetUi32(p) 73 74 #define Set16(p, v) SetUi16(p, v) 75 #define Set32(p, v) SetUi32(p, v) 76 77 static const wchar_t *k_LinkPrefix = L"\\??\\"; 78 static const unsigned k_LinkPrefix_Size = 4; 79 80 static const bool IsLinkPrefix(const wchar_t *s) 81 { 82 return IsString1PrefixedByString2(s, k_LinkPrefix); 83 } 84 85 /* 86 static const wchar_t *k_VolumePrefix = L"Volume{"; 87 static const bool IsVolumeName(const wchar_t *s) 88 { 89 return IsString1PrefixedByString2(s, k_VolumePrefix); 90 } 91 */ 92 93 void WriteString(Byte *dest, const wchar_t *path) 94 { 95 for (;;) 96 { 97 wchar_t c = *path++; 98 if (c == 0) 99 return; 100 Set16(dest, (UInt16)c); 101 dest += 2; 102 } 103 } 104 105 bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink) 106 { 107 bool isAbs = IsAbsolutePath(path); 108 if (!isAbs && !isSymLink) 109 return false; 110 111 bool needPrintName = true; 112 113 if (IsSuperPath(path)) 114 { 115 path += kSuperPathPrefixSize; 116 if (!IsDrivePath(path)) 117 needPrintName = false; 118 } 119 120 const unsigned add_Prefix_Len = isAbs ? k_LinkPrefix_Size : 0; 121 122 unsigned len2 = MyStringLen(path) * 2; 123 const unsigned len1 = len2 + add_Prefix_Len * 2; 124 if (!needPrintName) 125 len2 = 0; 126 127 unsigned totalNamesSize = (len1 + len2); 128 129 /* some WIM imagex software uses old scheme for symbolic links. 130 so we can old scheme for byte to byte compatibility */ 131 132 bool newOrderScheme = isSymLink; 133 // newOrderScheme = false; 134 135 if (!newOrderScheme) 136 totalNamesSize += 2 * 2; 137 138 const size_t size = 8 + 8 + (isSymLink ? 4 : 0) + totalNamesSize; 139 dest.Alloc(size); 140 memset(dest, 0, size); 141 const UInt32 tag = isSymLink ? 142 _my_IO_REPARSE_TAG_SYMLINK : 143 _my_IO_REPARSE_TAG_MOUNT_POINT; 144 Byte *p = dest; 145 Set32(p, tag); 146 Set16(p + 4, (UInt16)(size - 8)); 147 Set16(p + 6, 0); 148 p += 8; 149 150 unsigned subOffs = 0; 151 unsigned printOffs = 0; 152 if (newOrderScheme) 153 subOffs = len2; 154 else 155 printOffs = len1 + 2; 156 157 Set16(p + 0, (UInt16)subOffs); 158 Set16(p + 2, (UInt16)len1); 159 Set16(p + 4, (UInt16)printOffs); 160 Set16(p + 6, (UInt16)len2); 161 162 p += 8; 163 if (isSymLink) 164 { 165 UInt32 flags = isAbs ? 0 : _my_SYMLINK_FLAG_RELATIVE; 166 Set32(p, flags); 167 p += 4; 168 } 169 170 if (add_Prefix_Len != 0) 171 WriteString(p + subOffs, k_LinkPrefix); 172 WriteString(p + subOffs + add_Prefix_Len * 2, path); 173 if (needPrintName) 174 WriteString(p + printOffs, path); 175 return true; 176 } 177 178 static void GetString(const Byte *p, unsigned len, UString &res) 179 { 180 wchar_t *s = res.GetBuffer(len); 181 for (unsigned i = 0; i < len; i++) 182 s[i] = Get16(p + i * 2); 183 s[len] = 0; 184 res.ReleaseBuffer(); 185 } 186 187 bool CReparseAttr::Parse(const Byte *p, size_t size) 188 { 189 if (size < 8) 190 return false; 191 Tag = Get32(p); 192 UInt32 len = Get16(p + 4); 193 if (len + 8 > size) 194 return false; 195 /* 196 if ((type & kReparseFlags_Alias) == 0 || 197 (type & kReparseFlags_Microsoft) == 0 || 198 (type & 0xFFFF) != 3) 199 */ 200 if (Tag != _my_IO_REPARSE_TAG_MOUNT_POINT && 201 Tag != _my_IO_REPARSE_TAG_SYMLINK) 202 // return true; 203 return false; 204 205 if (Get16(p + 6) != 0) // padding 206 return false; 207 208 p += 8; 209 size -= 8; 210 211 if (len != size) // do we need that check? 212 return false; 213 214 if (len < 8) 215 return false; 216 unsigned subOffs = Get16(p); 217 unsigned subLen = Get16(p + 2); 218 unsigned printOffs = Get16(p + 4); 219 unsigned printLen = Get16(p + 6); 220 len -= 8; 221 p += 8; 222 223 Flags = 0; 224 if (Tag == _my_IO_REPARSE_TAG_SYMLINK) 225 { 226 if (len < 4) 227 return false; 228 Flags = Get32(p); 229 len -= 4; 230 p += 4; 231 } 232 233 if ((subOffs & 1) != 0 || subOffs > len || len - subOffs < subLen) 234 return false; 235 if ((printOffs & 1) != 0 || printOffs > len || len - printOffs < printLen) 236 return false; 237 GetString(p + subOffs, subLen >> 1, SubsName); 238 GetString(p + printOffs, printLen >> 1, PrintName); 239 240 return true; 241 } 242 243 bool CReparseShortInfo::Parse(const Byte *p, size_t size) 244 { 245 const Byte *start = p; 246 Offset= 0; 247 Size = 0; 248 if (size < 8) 249 return false; 250 UInt32 Tag = Get32(p); 251 UInt32 len = Get16(p + 4); 252 if (len + 8 > size) 253 return false; 254 /* 255 if ((type & kReparseFlags_Alias) == 0 || 256 (type & kReparseFlags_Microsoft) == 0 || 257 (type & 0xFFFF) != 3) 258 */ 259 if (Tag != _my_IO_REPARSE_TAG_MOUNT_POINT && 260 Tag != _my_IO_REPARSE_TAG_SYMLINK) 261 // return true; 262 return false; 263 264 if (Get16(p + 6) != 0) // padding 265 return false; 266 267 p += 8; 268 size -= 8; 269 270 if (len != size) // do we need that check? 271 return false; 272 273 if (len < 8) 274 return false; 275 unsigned subOffs = Get16(p); 276 unsigned subLen = Get16(p + 2); 277 unsigned printOffs = Get16(p + 4); 278 unsigned printLen = Get16(p + 6); 279 len -= 8; 280 p += 8; 281 282 // UInt32 Flags = 0; 283 if (Tag == _my_IO_REPARSE_TAG_SYMLINK) 284 { 285 if (len < 4) 286 return false; 287 // Flags = Get32(p); 288 len -= 4; 289 p += 4; 290 } 291 292 if ((subOffs & 1) != 0 || subOffs > len || len - subOffs < subLen) 293 return false; 294 if ((printOffs & 1) != 0 || printOffs > len || len - printOffs < printLen) 295 return false; 296 297 Offset = (unsigned)(p - start) + subOffs; 298 Size = subLen; 299 return true; 300 } 301 302 bool CReparseAttr::IsOkNamePair() const 303 { 304 if (IsLinkPrefix(SubsName)) 305 { 306 if (!IsDrivePath(SubsName.Ptr(k_LinkPrefix_Size))) 307 return PrintName.IsEmpty(); 308 if (wcscmp(SubsName.Ptr(k_LinkPrefix_Size), PrintName) == 0) 309 return true; 310 } 311 return wcscmp(SubsName, PrintName) == 0; 312 } 313 314 /* 315 bool CReparseAttr::IsVolume() const 316 { 317 if (!IsLinkPrefix(SubsName)) 318 return false; 319 return IsVolumeName(SubsName.Ptr(k_LinkPrefix_Size)); 320 } 321 */ 322 323 UString CReparseAttr::GetPath() const 324 { 325 UString s = SubsName; 326 if (IsLinkPrefix(s)) 327 { 328 s.ReplaceOneCharAtPos(1, '\\'); 329 if (IsDrivePath(s.Ptr(k_LinkPrefix_Size))) 330 s.DeleteFrontal(k_LinkPrefix_Size); 331 } 332 return s; 333 } 334 335 336 #ifdef SUPPORT_DEVICE_FILE 337 338 namespace NSystem 339 { 340 bool MyGetDiskFreeSpace(CFSTR rootPath, UInt64 &clusterSize, UInt64 &totalSize, UInt64 &freeSize); 341 } 342 #endif 343 344 #ifndef UNDER_CE 345 346 namespace NIO { 347 348 bool GetReparseData(CFSTR path, CByteBuffer &reparseData, BY_HANDLE_FILE_INFORMATION *fileInfo) 349 { 350 reparseData.Free(); 351 CInFile file; 352 if (!file.OpenReparse(path)) 353 return false; 354 355 if (fileInfo) 356 file.GetFileInformation(fileInfo); 357 358 const unsigned kBufSize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE; 359 CByteArr buf(kBufSize); 360 DWORD returnedSize; 361 if (!file.DeviceIoControlOut(my_FSCTL_GET_REPARSE_POINT, buf, kBufSize, &returnedSize)) 362 return false; 363 reparseData.CopyFrom(buf, returnedSize); 364 return true; 365 } 366 367 static bool CreatePrefixDirOfFile(CFSTR path) 368 { 369 FString path2 = path; 370 int pos = path2.ReverseFind(FCHAR_PATH_SEPARATOR); 371 if (pos < 0) 372 return true; 373 #ifdef _WIN32 374 if (pos == 2 && path2[1] == L':') 375 return true; // we don't create Disk folder; 376 #endif 377 path2.DeleteFrom(pos); 378 return NDir::CreateComplexDir(path2); 379 } 380 381 // If there is Reprase data already, it still writes new Reparse data 382 bool SetReparseData(CFSTR path, bool isDir, const void *data, DWORD size) 383 { 384 NFile::NFind::CFileInfo fi; 385 if (fi.Find(path)) 386 { 387 if (fi.IsDir() != isDir) 388 { 389 ::SetLastError(ERROR_DIRECTORY); 390 return false; 391 } 392 } 393 else 394 { 395 if (isDir) 396 { 397 if (!NDir::CreateComplexDir(path)) 398 return false; 399 } 400 else 401 { 402 CreatePrefixDirOfFile(path); 403 COutFile file; 404 if (!file.Create(path, CREATE_NEW)) 405 return false; 406 } 407 } 408 409 COutFile file; 410 if (!file.Open(path, 411 FILE_SHARE_WRITE, 412 OPEN_EXISTING, 413 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS)) 414 return false; 415 416 DWORD returnedSize; 417 if (!file.DeviceIoControl(my_FSCTL_SET_REPARSE_POINT, (void *)data, size, NULL, 0, &returnedSize)) 418 return false; 419 return true; 420 } 421 422 } 423 424 #endif 425 426 }} 427