FileLink.cpp (10185B)
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 * const 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 * const 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 #if defined(_WIN32) && !defined(UNDER_CE) 106 107 bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink) 108 { 109 bool isAbs = IsAbsolutePath(path); 110 if (!isAbs && !isSymLink) 111 return false; 112 113 bool needPrintName = true; 114 115 if (IsSuperPath(path)) 116 { 117 path += kSuperPathPrefixSize; 118 if (!IsDrivePath(path)) 119 needPrintName = false; 120 } 121 122 const unsigned add_Prefix_Len = isAbs ? k_LinkPrefix_Size : 0; 123 124 unsigned len2 = MyStringLen(path) * 2; 125 const unsigned len1 = len2 + add_Prefix_Len * 2; 126 if (!needPrintName) 127 len2 = 0; 128 129 unsigned totalNamesSize = (len1 + len2); 130 131 /* some WIM imagex software uses old scheme for symbolic links. 132 so we can old scheme for byte to byte compatibility */ 133 134 bool newOrderScheme = isSymLink; 135 // newOrderScheme = false; 136 137 if (!newOrderScheme) 138 totalNamesSize += 2 * 2; 139 140 const size_t size = 8 + 8 + (isSymLink ? 4 : 0) + totalNamesSize; 141 dest.Alloc(size); 142 memset(dest, 0, size); 143 const UInt32 tag = isSymLink ? 144 _my_IO_REPARSE_TAG_SYMLINK : 145 _my_IO_REPARSE_TAG_MOUNT_POINT; 146 Byte *p = dest; 147 Set32(p, tag); 148 Set16(p + 4, (UInt16)(size - 8)); 149 Set16(p + 6, 0); 150 p += 8; 151 152 unsigned subOffs = 0; 153 unsigned printOffs = 0; 154 if (newOrderScheme) 155 subOffs = len2; 156 else 157 printOffs = len1 + 2; 158 159 Set16(p + 0, (UInt16)subOffs); 160 Set16(p + 2, (UInt16)len1); 161 Set16(p + 4, (UInt16)printOffs); 162 Set16(p + 6, (UInt16)len2); 163 164 p += 8; 165 if (isSymLink) 166 { 167 UInt32 flags = isAbs ? 0 : _my_SYMLINK_FLAG_RELATIVE; 168 Set32(p, flags); 169 p += 4; 170 } 171 172 if (add_Prefix_Len != 0) 173 WriteString(p + subOffs, k_LinkPrefix); 174 WriteString(p + subOffs + add_Prefix_Len * 2, path); 175 if (needPrintName) 176 WriteString(p + printOffs, path); 177 return true; 178 } 179 180 #endif 181 182 static void GetString(const Byte *p, unsigned len, UString &res) 183 { 184 wchar_t *s = res.GetBuf(len); 185 unsigned i; 186 for (i = 0; i < len; i++) 187 { 188 wchar_t c = Get16(p + i * 2); 189 if (c == 0) 190 break; 191 s[i] = c; 192 } 193 s[i] = 0; 194 res.ReleaseBuf_SetLen(i); 195 } 196 197 bool CReparseAttr::Parse(const Byte *p, size_t size, DWORD &errorCode) 198 { 199 errorCode = ERROR_INVALID_REPARSE_DATA; 200 if (size < 8) 201 return false; 202 Tag = Get32(p); 203 UInt32 len = Get16(p + 4); 204 if (len + 8 > size) 205 return false; 206 /* 207 if ((type & kReparseFlags_Alias) == 0 || 208 (type & kReparseFlags_Microsoft) == 0 || 209 (type & 0xFFFF) != 3) 210 */ 211 if (Tag != _my_IO_REPARSE_TAG_MOUNT_POINT && 212 Tag != _my_IO_REPARSE_TAG_SYMLINK) 213 { 214 errorCode = ERROR_REPARSE_TAG_MISMATCH; // ERROR_REPARSE_TAG_INVALID 215 return false; 216 } 217 218 if (Get16(p + 6) != 0) // padding 219 return false; 220 221 p += 8; 222 size -= 8; 223 224 if (len != size) // do we need that check? 225 return false; 226 227 if (len < 8) 228 return false; 229 unsigned subOffs = Get16(p); 230 unsigned subLen = Get16(p + 2); 231 unsigned printOffs = Get16(p + 4); 232 unsigned printLen = Get16(p + 6); 233 len -= 8; 234 p += 8; 235 236 Flags = 0; 237 if (Tag == _my_IO_REPARSE_TAG_SYMLINK) 238 { 239 if (len < 4) 240 return false; 241 Flags = Get32(p); 242 len -= 4; 243 p += 4; 244 } 245 246 if ((subOffs & 1) != 0 || subOffs > len || len - subOffs < subLen) 247 return false; 248 if ((printOffs & 1) != 0 || printOffs > len || len - printOffs < printLen) 249 return false; 250 GetString(p + subOffs, subLen >> 1, SubsName); 251 GetString(p + printOffs, printLen >> 1, PrintName); 252 253 errorCode = 0; 254 return true; 255 } 256 257 bool CReparseShortInfo::Parse(const Byte *p, size_t size) 258 { 259 const Byte *start = p; 260 Offset= 0; 261 Size = 0; 262 if (size < 8) 263 return false; 264 UInt32 Tag = Get32(p); 265 UInt32 len = Get16(p + 4); 266 if (len + 8 > size) 267 return false; 268 /* 269 if ((type & kReparseFlags_Alias) == 0 || 270 (type & kReparseFlags_Microsoft) == 0 || 271 (type & 0xFFFF) != 3) 272 */ 273 if (Tag != _my_IO_REPARSE_TAG_MOUNT_POINT && 274 Tag != _my_IO_REPARSE_TAG_SYMLINK) 275 // return true; 276 return false; 277 278 if (Get16(p + 6) != 0) // padding 279 return false; 280 281 p += 8; 282 size -= 8; 283 284 if (len != size) // do we need that check? 285 return false; 286 287 if (len < 8) 288 return false; 289 unsigned subOffs = Get16(p); 290 unsigned subLen = Get16(p + 2); 291 unsigned printOffs = Get16(p + 4); 292 unsigned printLen = Get16(p + 6); 293 len -= 8; 294 p += 8; 295 296 // UInt32 Flags = 0; 297 if (Tag == _my_IO_REPARSE_TAG_SYMLINK) 298 { 299 if (len < 4) 300 return false; 301 // Flags = Get32(p); 302 len -= 4; 303 p += 4; 304 } 305 306 if ((subOffs & 1) != 0 || subOffs > len || len - subOffs < subLen) 307 return false; 308 if ((printOffs & 1) != 0 || printOffs > len || len - printOffs < printLen) 309 return false; 310 311 Offset = (unsigned)(p - start) + subOffs; 312 Size = subLen; 313 return true; 314 } 315 316 bool CReparseAttr::IsOkNamePair() const 317 { 318 if (IsLinkPrefix(SubsName)) 319 { 320 if (!IsDrivePath(SubsName.Ptr(k_LinkPrefix_Size))) 321 return PrintName.IsEmpty(); 322 if (wcscmp(SubsName.Ptr(k_LinkPrefix_Size), PrintName) == 0) 323 return true; 324 } 325 return wcscmp(SubsName, PrintName) == 0; 326 } 327 328 /* 329 bool CReparseAttr::IsVolume() const 330 { 331 if (!IsLinkPrefix(SubsName)) 332 return false; 333 return IsVolumeName(SubsName.Ptr(k_LinkPrefix_Size)); 334 } 335 */ 336 337 UString CReparseAttr::GetPath() const 338 { 339 UString s (SubsName); 340 if (IsLinkPrefix(s)) 341 { 342 s.ReplaceOneCharAtPos(1, '\\'); 343 if (IsDrivePath(s.Ptr(k_LinkPrefix_Size))) 344 s.DeleteFrontal(k_LinkPrefix_Size); 345 } 346 return s; 347 } 348 349 350 #ifdef SUPPORT_DEVICE_FILE 351 352 namespace NSystem 353 { 354 bool MyGetDiskFreeSpace(CFSTR rootPath, UInt64 &clusterSize, UInt64 &totalSize, UInt64 &freeSize); 355 } 356 #endif 357 358 #ifndef UNDER_CE 359 360 namespace NIO { 361 362 bool GetReparseData(CFSTR path, CByteBuffer &reparseData, BY_HANDLE_FILE_INFORMATION *fileInfo) 363 { 364 reparseData.Free(); 365 CInFile file; 366 if (!file.OpenReparse(path)) 367 return false; 368 369 if (fileInfo) 370 file.GetFileInformation(fileInfo); 371 372 const unsigned kBufSize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE; 373 CByteArr buf(kBufSize); 374 DWORD returnedSize; 375 if (!file.DeviceIoControlOut(my_FSCTL_GET_REPARSE_POINT, buf, kBufSize, &returnedSize)) 376 return false; 377 reparseData.CopyFrom(buf, returnedSize); 378 return true; 379 } 380 381 static bool CreatePrefixDirOfFile(CFSTR path) 382 { 383 FString path2 (path); 384 int pos = path2.ReverseFind_PathSepar(); 385 if (pos < 0) 386 return true; 387 #ifdef _WIN32 388 if (pos == 2 && path2[1] == L':') 389 return true; // we don't create Disk folder; 390 #endif 391 path2.DeleteFrom(pos); 392 return NDir::CreateComplexDir(path2); 393 } 394 395 // If there is Reprase data already, it still writes new Reparse data 396 bool SetReparseData(CFSTR path, bool isDir, const void *data, DWORD size) 397 { 398 NFile::NFind::CFileInfo fi; 399 if (fi.Find(path)) 400 { 401 if (fi.IsDir() != isDir) 402 { 403 ::SetLastError(ERROR_DIRECTORY); 404 return false; 405 } 406 } 407 else 408 { 409 if (isDir) 410 { 411 if (!NDir::CreateComplexDir(path)) 412 return false; 413 } 414 else 415 { 416 CreatePrefixDirOfFile(path); 417 COutFile file; 418 if (!file.Create(path, CREATE_NEW)) 419 return false; 420 } 421 } 422 423 COutFile file; 424 if (!file.Open(path, 425 FILE_SHARE_WRITE, 426 OPEN_EXISTING, 427 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS)) 428 return false; 429 430 DWORD returnedSize; 431 if (!file.DeviceIoControl(my_FSCTL_SET_REPARSE_POINT, (void *)data, size, NULL, 0, &returnedSize)) 432 return false; 433 return true; 434 } 435 436 } 437 438 #endif 439 440 }}