aboutsummaryrefslogtreecommitdiff
path: root/CPP/Windows/FileLink.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--CPP/Windows/FileLink.cpp244
1 files changed, 160 insertions, 84 deletions
diff --git a/CPP/Windows/FileLink.cpp b/CPP/Windows/FileLink.cpp
index bb380ec..2883c82 100644
--- a/CPP/Windows/FileLink.cpp
+++ b/CPP/Windows/FileLink.cpp
@@ -39,12 +39,24 @@ namespace NFile {
39using namespace NName; 39using namespace NName;
40 40
41/* 41/*
42Win10 Junctions/SymLinks:
43 - (/) slash doesn't work as path separator
44 - Win10 preinstalled junctions don't use tail backslash, but tail backslashes also work.
45 - double backslash works only after drive prefix "c:\\dir1\dir2\",
46 and doesn't work in another places.
47 - absolute path without \??\ prefix doesn't work
48 - absolute path "c:" doesn't work
49*/
50
51/*
42 Reparse Points (Junctions and Symbolic Links): 52 Reparse Points (Junctions and Symbolic Links):
43 struct 53 struct
44 { 54 {
45 UInt32 Tag; 55 UInt32 Tag;
46 UInt16 Size; // not including starting 8 bytes 56 UInt16 Size; // not including starting 8 bytes
47 UInt16 Reserved; // = 0 57 UInt16 Reserved; // = 0, DOCs: // Length, in bytes, of the unparsed portion of
58 // the file name pointed to by the FileName member of the associated file object.
59 // This member is only valid for create operations when the I/O fails with STATUS_REPARSE.
48 60
49 UInt16 SubstituteOffset; // offset in bytes from start of namesChars 61 UInt16 SubstituteOffset; // offset in bytes from start of namesChars
50 UInt16 SubstituteLen; // size in bytes, it doesn't include tailed NUL 62 UInt16 SubstituteLen; // size in bytes, it doesn't include tailed NUL
@@ -68,6 +80,16 @@ using namespace NName;
68 2) Default Order in table: 80 2) Default Order in table:
69 Print Path 81 Print Path
70 Substitute Path 82 Substitute Path
83
84DOCS:
85 The print name SHOULD be an informative pathname, suitable for display
86 to a user, that also identifies the target of the mount point.
87 Neither of these pathnames can contain dot directory names.
88
89reparse tags, with the exception of IO_REPARSE_TAG_SYMLINK,
90are processed on the server and are not processed by a client
91after transmission over the wire.
92Clients SHOULD treat associated reparse data as opaque data.
71*/ 93*/
72 94
73/* 95/*
@@ -93,7 +115,8 @@ static const UInt32 kReparseFlags_Microsoft = ((UInt32)1 << 31);
93#define Get16(p) GetUi16(p) 115#define Get16(p) GetUi16(p)
94#define Get32(p) GetUi32(p) 116#define Get32(p) GetUi32(p)
95 117
96static const wchar_t * const k_LinkPrefix = L"\\??\\"; 118static const char * const k_LinkPrefix = "\\??\\";
119static const char * const k_LinkPrefix_UNC = "\\??\\UNC\\";
97static const unsigned k_LinkPrefix_Size = 4; 120static const unsigned k_LinkPrefix_Size = 4;
98 121
99static bool IsLinkPrefix(const wchar_t *s) 122static bool IsLinkPrefix(const wchar_t *s)
@@ -102,7 +125,7 @@ static bool IsLinkPrefix(const wchar_t *s)
102} 125}
103 126
104/* 127/*
105static const wchar_t * const k_VolumePrefix = L"Volume{"; 128static const char * const k_VolumePrefix = "Volume{";
106static const bool IsVolumeName(const wchar_t *s) 129static const bool IsVolumeName(const wchar_t *s)
107{ 130{
108 return IsString1PrefixedByString2(s, k_VolumePrefix); 131 return IsString1PrefixedByString2(s, k_VolumePrefix);
@@ -118,7 +141,7 @@ static void WriteString(Byte *dest, const wchar_t *path)
118{ 141{
119 for (;;) 142 for (;;)
120 { 143 {
121 wchar_t c = *path++; 144 const wchar_t c = *path++;
122 if (c == 0) 145 if (c == 0)
123 return; 146 return;
124 Set16(dest, (UInt16)c) 147 Set16(dest, (UInt16)c)
@@ -126,62 +149,103 @@ static void WriteString(Byte *dest, const wchar_t *path)
126 } 149 }
127} 150}
128 151
129bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool isWSL) 152#ifdef _WIN32
153void Convert_WinPath_to_WslLinuxPath(FString &s, bool convertDrivePath)
130{ 154{
131 bool isAbs = IsAbsolutePath(path); 155 if (convertDrivePath && IsDrivePath(s))
132 if (!isAbs && !isSymLink)
133 return false;
134
135 if (isWSL)
136 { 156 {
137 // unsupported characters probably use Replacement Character UTF-16 0xFFFD 157 FChar c = s[0];
138 AString utf; 158 c = MyCharLower_Ascii(c);
139 ConvertUnicodeToUTF8(path, utf); 159 s.DeleteFrontal(2);
140 const size_t size = 4 + utf.Len(); 160 s.InsertAtFront(c);
141 if (size != (UInt16)size) 161 s.Insert(0, FTEXT("/mnt/"));
142 return false;
143 dest.Alloc(8 + size);
144 Byte *p = dest;
145 Set32(p, Z7_WIN_IO_REPARSE_TAG_LX_SYMLINK)
146 Set16(p + 4, (UInt16)(size))
147 Set16(p + 6, 0)
148 Set32(p + 8, Z7_WIN_LX_SYMLINK_FLAG)
149 memcpy(p + 12, utf.Ptr(), utf.Len());
150 return true;
151 } 162 }
163 s.Replace(FCHAR_PATH_SEPARATOR, FTEXT('/'));
164}
165#endif
152 166
153 // usual symbolic LINK (NOT WSL)
154 167
155 bool needPrintName = true; 168static const unsigned k_Link_Size_Limit = 1u << 16; // 16-bit field is used for size.
169
170void FillLinkData_WslLink(CByteBuffer &dest, const wchar_t *path)
171{
172 // dest.Free(); // it's empty already
173 // WSL probably uses Replacement Character UTF-16 0xFFFD for unsupported characters?
174 AString utf;
175 ConvertUnicodeToUTF8(path, utf);
176 const unsigned size = 4 + utf.Len();
177 if (size >= k_Link_Size_Limit)
178 return;
179 dest.Alloc(8 + size);
180 Byte *p = dest;
181 Set32(p, Z7_WIN_IO_REPARSE_TAG_LX_SYMLINK)
182 // Set32(p + 4, (UInt32)size)
183 Set16(p + 4, (UInt16)size)
184 Set16(p + 6, 0)
185 Set32(p + 8, Z7_WIN_LX_SYMLINK_VERSION_2)
186 memcpy(p + 12, utf.Ptr(), utf.Len());
187}
188
156 189
157 if (IsSuperPath(path)) 190void FillLinkData_WinLink(CByteBuffer &dest, const wchar_t *path, bool isSymLink)
191{
192 // dest.Free(); // it's empty already
193 bool isAbs = false;
194 if (IS_PATH_SEPAR(path[0]))
158 { 195 {
159 path += kSuperPathPrefixSize; 196 // root paths "\dir1\path" are marked as relative
160 if (!IsDrivePath(path)) 197 if (IS_PATH_SEPAR(path[1]))
161 needPrintName = false; 198 isAbs = true;
199 }
200 else
201 isAbs = IsAbsolutePath(path);
202 if (!isAbs && !isSymLink)
203 {
204 // Win10 allows us to create relative MOUNT_POINT.
205 // But relative MOUNT_POINT will not work when accessing it.
206 // So we prevent useless creation of a relative MOUNT_POINT.
207 return;
162 } 208 }
163 209
164 const unsigned add_Prefix_Len = isAbs ? k_LinkPrefix_Size : 0; 210 bool needPrintName = true;
165 211 UString subs (path);
212 if (isAbs)
213 {
214 const bool isSuperPath = IsSuperPath(path);
215 if (!isSuperPath && NName::IsNetworkPath(us2fs(path)))
216 {
217 subs = k_LinkPrefix_UNC;
218 subs += (path + 2);
219 }
220 else
221 {
222 if (isSuperPath)
223 {
224 // we remove super prefix:
225 path += kSuperPathPrefixSize;
226 // we want to get correct abolute path in PrintName still.
227 if (!IsDrivePath(path))
228 needPrintName = false; // we need "\\server\path" for print name.
229 }
230 subs = k_LinkPrefix;
231 subs += path;
232 }
233 }
234 const size_t len1 = subs.Len() * 2;
166 size_t len2 = (size_t)MyStringLen(path) * 2; 235 size_t len2 = (size_t)MyStringLen(path) * 2;
167 const size_t len1 = len2 + add_Prefix_Len * 2;
168 if (!needPrintName) 236 if (!needPrintName)
169 len2 = 0; 237 len2 = 0;
170 238 size_t totalNamesSize = len1 + len2;
171 size_t totalNamesSize = (len1 + len2);
172
173 /* some WIM imagex software uses old scheme for symbolic links. 239 /* some WIM imagex software uses old scheme for symbolic links.
174 so we can old scheme for byte to byte compatibility */ 240 so we can use old scheme for byte to byte compatibility */
175 241 const bool newOrderScheme = isSymLink;
176 bool newOrderScheme = isSymLink;
177 // newOrderScheme = false; 242 // newOrderScheme = false;
178
179 if (!newOrderScheme) 243 if (!newOrderScheme)
180 totalNamesSize += 2 * 2; 244 totalNamesSize += 2 * 2; // we use NULL terminators in old scheme.
181 245
182 const size_t size = 8 + 8 + (isSymLink ? 4 : 0) + totalNamesSize; 246 const size_t size = 8 + 8 + (isSymLink ? 4 : 0) + totalNamesSize;
183 if (size != (UInt16)size) 247 if (size >= k_Link_Size_Limit)
184 return false; 248 return;
185 dest.Alloc(size); 249 dest.Alloc(size);
186 memset(dest, 0, size); 250 memset(dest, 0, size);
187 const UInt32 tag = isSymLink ? 251 const UInt32 tag = isSymLink ?
@@ -189,6 +253,7 @@ bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool i
189 Z7_WIN_IO_REPARSE_TAG_MOUNT_POINT; 253 Z7_WIN_IO_REPARSE_TAG_MOUNT_POINT;
190 Byte *p = dest; 254 Byte *p = dest;
191 Set32(p, tag) 255 Set32(p, tag)
256 // Set32(p + 4, (UInt32)(size - 8))
192 Set16(p + 4, (UInt16)(size - 8)) 257 Set16(p + 4, (UInt16)(size - 8))
193 Set16(p + 6, 0) 258 Set16(p + 6, 0)
194 p += 8; 259 p += 8;
@@ -204,21 +269,16 @@ bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool i
204 Set16(p + 2, (UInt16)len1) 269 Set16(p + 2, (UInt16)len1)
205 Set16(p + 4, (UInt16)printOffs) 270 Set16(p + 4, (UInt16)printOffs)
206 Set16(p + 6, (UInt16)len2) 271 Set16(p + 6, (UInt16)len2)
207
208 p += 8; 272 p += 8;
209 if (isSymLink) 273 if (isSymLink)
210 { 274 {
211 UInt32 flags = isAbs ? 0 : Z7_WIN_SYMLINK_FLAG_RELATIVE; 275 const UInt32 flags = isAbs ? 0 : Z7_WIN_SYMLINK_FLAG_RELATIVE;
212 Set32(p, flags) 276 Set32(p, flags)
213 p += 4; 277 p += 4;
214 } 278 }
215 279 WriteString(p + subOffs, subs);
216 if (add_Prefix_Len != 0)
217 WriteString(p + subOffs, k_LinkPrefix);
218 WriteString(p + subOffs + add_Prefix_Len * 2, path);
219 if (needPrintName) 280 if (needPrintName)
220 WriteString(p + printOffs, path); 281 WriteString(p + printOffs, path);
221 return true;
222} 282}
223 283
224#endif // defined(_WIN32) && !defined(UNDER_CE) 284#endif // defined(_WIN32) && !defined(UNDER_CE)
@@ -230,7 +290,7 @@ static void GetString(const Byte *p, unsigned len, UString &res)
230 unsigned i; 290 unsigned i;
231 for (i = 0; i < len; i++) 291 for (i = 0; i < len; i++)
232 { 292 {
233 wchar_t c = Get16(p + i * 2); 293 const wchar_t c = Get16(p + (size_t)i * 2);
234 if (c == 0) 294 if (c == 0)
235 break; 295 break;
236 s[i] = c; 296 s[i] = c;
@@ -239,6 +299,7 @@ static void GetString(const Byte *p, unsigned len, UString &res)
239 res.ReleaseBuf_SetLen(i); 299 res.ReleaseBuf_SetLen(i);
240} 300}
241 301
302
242bool CReparseAttr::Parse(const Byte *p, size_t size) 303bool CReparseAttr::Parse(const Byte *p, size_t size)
243{ 304{
244 ErrorCode = (DWORD)ERROR_INVALID_REPARSE_DATA; 305 ErrorCode = (DWORD)ERROR_INVALID_REPARSE_DATA;
@@ -250,7 +311,12 @@ bool CReparseAttr::Parse(const Byte *p, size_t size)
250 return false; 311 return false;
251 Tag = Get32(p); 312 Tag = Get32(p);
252 if (Get16(p + 6) != 0) // padding 313 if (Get16(p + 6) != 0) // padding
253 return false; 314 {
315 // DOCs: Reserved : the field SHOULD be set to 0
316 // and MUST be ignored (by parser).
317 // Win10 ignores it.
318 MinorError = true; // optional
319 }
254 unsigned len = Get16(p + 4); 320 unsigned len = Get16(p + 4);
255 p += 8; 321 p += 8;
256 size -= 8; 322 size -= 8;
@@ -262,8 +328,6 @@ bool CReparseAttr::Parse(const Byte *p, size_t size)
262 (type & kReparseFlags_Microsoft) == 0 || 328 (type & kReparseFlags_Microsoft) == 0 ||
263 (type & 0xFFFF) != 3) 329 (type & 0xFFFF) != 3)
264 */ 330 */
265
266
267 HeaderError = false; 331 HeaderError = false;
268 332
269 if ( Tag != Z7_WIN_IO_REPARSE_TAG_MOUNT_POINT 333 if ( Tag != Z7_WIN_IO_REPARSE_TAG_MOUNT_POINT
@@ -282,8 +346,7 @@ bool CReparseAttr::Parse(const Byte *p, size_t size)
282 { 346 {
283 if (len < 4) 347 if (len < 4)
284 return false; 348 return false;
285 Flags = Get32(p); // maybe it's not Flags 349 if (Get32(p) != Z7_WIN_LX_SYMLINK_VERSION_2)
286 if (Flags != Z7_WIN_LX_SYMLINK_FLAG)
287 return false; 350 return false;
288 len -= 4; 351 len -= 4;
289 p += 4; 352 p += 4;
@@ -291,12 +354,13 @@ bool CReparseAttr::Parse(const Byte *p, size_t size)
291 unsigned i; 354 unsigned i;
292 for (i = 0; i < len; i++) 355 for (i = 0; i < len; i++)
293 { 356 {
294 char c = (char)p[i]; 357 const char c = (char)p[i];
295 s[i] = c; 358 s[i] = c;
296 if (c == 0) 359 if (c == 0)
297 break; 360 break;
298 } 361 }
299 WslName.ReleaseBuf_SetEnd(i); 362 s[i] = 0;
363 WslName.ReleaseBuf_SetLen(i);
300 MinorError = (i != len); 364 MinorError = (i != len);
301 ErrorCode = 0; 365 ErrorCode = 0;
302 return true; 366 return true;
@@ -304,10 +368,10 @@ bool CReparseAttr::Parse(const Byte *p, size_t size)
304 368
305 if (len < 8) 369 if (len < 8)
306 return false; 370 return false;
307 unsigned subOffs = Get16(p); 371 const unsigned subOffs = Get16(p);
308 unsigned subLen = Get16(p + 2); 372 const unsigned subLen = Get16(p + 2);
309 unsigned printOffs = Get16(p + 4); 373 const unsigned printOffs = Get16(p + 4);
310 unsigned printLen = Get16(p + 6); 374 const unsigned printLen = Get16(p + 6);
311 len -= 8; 375 len -= 8;
312 p += 8; 376 p += 8;
313 377
@@ -335,15 +399,17 @@ bool CReparseAttr::Parse(const Byte *p, size_t size)
335 399
336bool CReparseShortInfo::Parse(const Byte *p, size_t size) 400bool CReparseShortInfo::Parse(const Byte *p, size_t size)
337{ 401{
338 const Byte *start = p; 402 const Byte * const start = p;
339 Offset= 0; 403 Offset = 0;
340 Size = 0; 404 Size = 0;
341 if (size < 8) 405 if (size < 8)
342 return false; 406 return false;
343 UInt32 Tag = Get32(p); 407 const UInt32 Tag = Get32(p);
344 UInt32 len = Get16(p + 4); 408 UInt32 len = Get16(p + 4);
409 /*
345 if (len + 8 > size) 410 if (len + 8 > size)
346 return false; 411 return false;
412 */
347 /* 413 /*
348 if ((type & kReparseFlags_Alias) == 0 || 414 if ((type & kReparseFlags_Alias) == 0 ||
349 (type & kReparseFlags_Microsoft) == 0 || 415 (type & kReparseFlags_Microsoft) == 0 ||
@@ -353,16 +419,14 @@ bool CReparseShortInfo::Parse(const Byte *p, size_t size)
353 Tag != Z7_WIN_IO_REPARSE_TAG_SYMLINK) 419 Tag != Z7_WIN_IO_REPARSE_TAG_SYMLINK)
354 // return true; 420 // return true;
355 return false; 421 return false;
356 422 /*
357 if (Get16(p + 6) != 0) // padding 423 if (Get16(p + 6) != 0) // padding
358 return false; 424 return false;
359 425 */
360 p += 8; 426 p += 8;
361 size -= 8; 427 size -= 8;
362
363 if (len != size) // do we need that check? 428 if (len != size) // do we need that check?
364 return false; 429 return false;
365
366 if (len < 8) 430 if (len < 8)
367 return false; 431 return false;
368 unsigned subOffs = Get16(p); 432 unsigned subOffs = Get16(p);
@@ -396,10 +460,14 @@ bool CReparseAttr::IsOkNamePair() const
396{ 460{
397 if (IsLinkPrefix(SubsName)) 461 if (IsLinkPrefix(SubsName))
398 { 462 {
463 if (PrintName == GetPath())
464 return true;
465/*
399 if (!IsDrivePath(SubsName.Ptr(k_LinkPrefix_Size))) 466 if (!IsDrivePath(SubsName.Ptr(k_LinkPrefix_Size)))
400 return PrintName.IsEmpty(); 467 return PrintName.IsEmpty();
401 if (wcscmp(SubsName.Ptr(k_LinkPrefix_Size), PrintName) == 0) 468 if (wcscmp(SubsName.Ptr(k_LinkPrefix_Size), PrintName) == 0)
402 return true; 469 return true;
470*/
403 } 471 }
404 return wcscmp(SubsName, PrintName) == 0; 472 return wcscmp(SubsName, PrintName) == 0;
405} 473}
@@ -415,21 +483,26 @@ bool CReparseAttr::IsVolume() const
415 483
416UString CReparseAttr::GetPath() const 484UString CReparseAttr::GetPath() const
417{ 485{
486 UString s (SubsName);
418 if (IsSymLink_WSL()) 487 if (IsSymLink_WSL())
419 { 488 {
420 UString u;
421 // if (CheckUTF8(attr.WslName) 489 // if (CheckUTF8(attr.WslName)
422 if (!ConvertUTF8ToUnicode(WslName, u)) 490 if (!ConvertUTF8ToUnicode(WslName, s))
423 MultiByteToUnicodeString2(u, WslName); 491 MultiByteToUnicodeString2(s, WslName);
424 return u;
425 } 492 }
426 493 else if (IsLinkPrefix(s))
427 UString s (SubsName);
428 if (IsLinkPrefix(s))
429 { 494 {
430 s.ReplaceOneCharAtPos(1, '\\'); // we normalize prefix from "\??\" to "\\?\" 495 if (IsString1PrefixedByString2_NoCase_Ascii(s.Ptr(), k_LinkPrefix_UNC))
431 if (IsDrivePath(s.Ptr(k_LinkPrefix_Size))) 496 {
432 s.DeleteFrontal(k_LinkPrefix_Size); 497 s.DeleteFrontal(6);
498 s.ReplaceOneCharAtPos(0, '\\');
499 }
500 else
501 {
502 s.ReplaceOneCharAtPos(1, '\\'); // we normalize prefix from "\??\" to "\\?\"
503 if (IsDrivePath(s.Ptr(k_LinkPrefix_Size)))
504 s.DeleteFrontal(k_LinkPrefix_Size);
505 }
433 } 506 }
434 return s; 507 return s;
435} 508}
@@ -468,7 +541,7 @@ bool GetReparseData(CFSTR path, CByteBuffer &reparseData, BY_HANDLE_FILE_INFORMA
468static bool CreatePrefixDirOfFile(CFSTR path) 541static bool CreatePrefixDirOfFile(CFSTR path)
469{ 542{
470 FString path2 (path); 543 FString path2 (path);
471 int pos = path2.ReverseFind_PathSepar(); 544 const int pos = path2.ReverseFind_PathSepar();
472 if (pos < 0) 545 if (pos < 0)
473 return true; 546 return true;
474 #ifdef _WIN32 547 #ifdef _WIN32
@@ -494,6 +567,8 @@ static bool OutIoReparseData(DWORD controlCode, CFSTR path, void *data, DWORD si
494} 567}
495 568
496 569
570// MOUNT_POINT (Junction Point) and LX_SYMLINK (WSL) can be written without administrator rights.
571// SYMLINK requires administrator rights.
497// If there is Reparse data already, it still writes new Reparse data 572// If there is Reparse data already, it still writes new Reparse data
498bool SetReparseData(CFSTR path, bool isDir, const void *data, DWORD size) 573bool SetReparseData(CFSTR path, bool isDir, const void *data, DWORD size)
499{ 574{
@@ -540,10 +615,11 @@ bool DeleteReparseData(CFSTR path)
540 SetLastError(ERROR_INVALID_REPARSE_DATA); 615 SetLastError(ERROR_INVALID_REPARSE_DATA);
541 return false; 616 return false;
542 } 617 }
543 BYTE buf[my_REPARSE_DATA_BUFFER_HEADER_SIZE]; 618 // BYTE buf[my_REPARSE_DATA_BUFFER_HEADER_SIZE];
544 memset(buf, 0, sizeof(buf)); 619 // memset(buf, 0, sizeof(buf));
545 memcpy(buf, reparseData, 4); // tag 620 // memcpy(buf, reparseData, 4); // tag
546 return OutIoReparseData(my_FSCTL_DELETE_REPARSE_POINT, path, buf, sizeof(buf)); 621 memset(reparseData + 4, 0, my_REPARSE_DATA_BUFFER_HEADER_SIZE - 4);
622 return OutIoReparseData(my_FSCTL_DELETE_REPARSE_POINT, path, reparseData, my_REPARSE_DATA_BUFFER_HEADER_SIZE);
547} 623}
548 624
549} 625}