diff options
Diffstat (limited to '')
-rw-r--r-- | CPP/Windows/FileLink.cpp | 613 |
1 files changed, 613 insertions, 0 deletions
diff --git a/CPP/Windows/FileLink.cpp b/CPP/Windows/FileLink.cpp new file mode 100644 index 0000000..8ce634f --- /dev/null +++ b/CPP/Windows/FileLink.cpp | |||
@@ -0,0 +1,613 @@ | |||
1 | // Windows/FileLink.cpp | ||
2 | |||
3 | #include "StdAfx.h" | ||
4 | |||
5 | #include "../../C/CpuArch.h" | ||
6 | |||
7 | #ifndef _WIN32 | ||
8 | #include <unistd.h> | ||
9 | #endif | ||
10 | |||
11 | #ifdef SUPPORT_DEVICE_FILE | ||
12 | #include "../../C/Alloc.h" | ||
13 | #endif | ||
14 | |||
15 | #include "../Common/UTFConvert.h" | ||
16 | #include "../Common/StringConvert.h" | ||
17 | |||
18 | #include "FileDir.h" | ||
19 | #include "FileFind.h" | ||
20 | #include "FileIO.h" | ||
21 | #include "FileName.h" | ||
22 | |||
23 | #ifndef _UNICODE | ||
24 | extern bool g_IsNT; | ||
25 | #endif | ||
26 | |||
27 | namespace NWindows { | ||
28 | namespace NFile { | ||
29 | |||
30 | using namespace NName; | ||
31 | |||
32 | /* | ||
33 | Reparse Points (Junctions and Symbolic Links): | ||
34 | struct | ||
35 | { | ||
36 | UInt32 Tag; | ||
37 | UInt16 Size; // not including starting 8 bytes | ||
38 | UInt16 Reserved; // = 0 | ||
39 | |||
40 | UInt16 SubstituteOffset; // offset in bytes from start of namesChars | ||
41 | UInt16 SubstituteLen; // size in bytes, it doesn't include tailed NUL | ||
42 | UInt16 PrintOffset; // offset in bytes from start of namesChars | ||
43 | UInt16 PrintLen; // size in bytes, it doesn't include tailed NUL | ||
44 | |||
45 | [UInt32] Flags; // for Symbolic Links only. | ||
46 | |||
47 | UInt16 namesChars[] | ||
48 | } | ||
49 | |||
50 | MOUNT_POINT (Junction point): | ||
51 | 1) there is NUL wchar after path | ||
52 | 2) Default Order in table: | ||
53 | Substitute Path | ||
54 | Print Path | ||
55 | 3) pathnames can not contain dot directory names | ||
56 | |||
57 | SYMLINK: | ||
58 | 1) there is no NUL wchar after path | ||
59 | 2) Default Order in table: | ||
60 | Print Path | ||
61 | Substitute Path | ||
62 | */ | ||
63 | |||
64 | /* | ||
65 | Win10 WSL2: | ||
66 | admin rights + sudo: it creates normal windows symbolic link. | ||
67 | in another cases : it creates IO_REPARSE_TAG_LX_SYMLINK repare point. | ||
68 | */ | ||
69 | |||
70 | /* | ||
71 | static const UInt32 kReparseFlags_Alias = (1 << 29); | ||
72 | static const UInt32 kReparseFlags_HighLatency = (1 << 30); | ||
73 | static const UInt32 kReparseFlags_Microsoft = ((UInt32)1 << 31); | ||
74 | |||
75 | #define _my_IO_REPARSE_TAG_HSM (0xC0000004L) | ||
76 | #define _my_IO_REPARSE_TAG_HSM2 (0x80000006L) | ||
77 | #define _my_IO_REPARSE_TAG_SIS (0x80000007L) | ||
78 | #define _my_IO_REPARSE_TAG_WIM (0x80000008L) | ||
79 | #define _my_IO_REPARSE_TAG_CSV (0x80000009L) | ||
80 | #define _my_IO_REPARSE_TAG_DFS (0x8000000AL) | ||
81 | #define _my_IO_REPARSE_TAG_DFSR (0x80000012L) | ||
82 | */ | ||
83 | |||
84 | #define Get16(p) GetUi16(p) | ||
85 | #define Get32(p) GetUi32(p) | ||
86 | |||
87 | static const wchar_t * const k_LinkPrefix = L"\\??\\"; | ||
88 | static const unsigned k_LinkPrefix_Size = 4; | ||
89 | |||
90 | static bool IsLinkPrefix(const wchar_t *s) | ||
91 | { | ||
92 | return IsString1PrefixedByString2(s, k_LinkPrefix); | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | static const wchar_t * const k_VolumePrefix = L"Volume{"; | ||
97 | static const bool IsVolumeName(const wchar_t *s) | ||
98 | { | ||
99 | return IsString1PrefixedByString2(s, k_VolumePrefix); | ||
100 | } | ||
101 | */ | ||
102 | |||
103 | #if defined(_WIN32) && !defined(UNDER_CE) | ||
104 | |||
105 | #define Set16(p, v) SetUi16(p, v) | ||
106 | #define Set32(p, v) SetUi32(p, v) | ||
107 | |||
108 | static void WriteString(Byte *dest, const wchar_t *path) | ||
109 | { | ||
110 | for (;;) | ||
111 | { | ||
112 | wchar_t c = *path++; | ||
113 | if (c == 0) | ||
114 | return; | ||
115 | Set16(dest, (UInt16)c); | ||
116 | dest += 2; | ||
117 | } | ||
118 | } | ||
119 | |||
120 | bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool isWSL) | ||
121 | { | ||
122 | bool isAbs = IsAbsolutePath(path); | ||
123 | if (!isAbs && !isSymLink) | ||
124 | return false; | ||
125 | |||
126 | if (isWSL) | ||
127 | { | ||
128 | // unsupported characters probably use Replacement Character UTF-16 0xFFFD | ||
129 | AString utf; | ||
130 | ConvertUnicodeToUTF8(path, utf); | ||
131 | const size_t size = 4 + utf.Len(); | ||
132 | if (size != (UInt16)size) | ||
133 | return false; | ||
134 | dest.Alloc(8 + size); | ||
135 | Byte *p = dest; | ||
136 | Set32(p, _my_IO_REPARSE_TAG_LX_SYMLINK); | ||
137 | Set16(p + 4, (UInt16)(size)); | ||
138 | Set16(p + 6, 0); | ||
139 | Set32(p + 8, _my_LX_SYMLINK_FLAG); | ||
140 | memcpy(p + 12, utf.Ptr(), utf.Len()); | ||
141 | return true; | ||
142 | } | ||
143 | |||
144 | // usual symbolic LINK (NOT WSL) | ||
145 | |||
146 | bool needPrintName = true; | ||
147 | |||
148 | if (IsSuperPath(path)) | ||
149 | { | ||
150 | path += kSuperPathPrefixSize; | ||
151 | if (!IsDrivePath(path)) | ||
152 | needPrintName = false; | ||
153 | } | ||
154 | |||
155 | const unsigned add_Prefix_Len = isAbs ? k_LinkPrefix_Size : 0; | ||
156 | |||
157 | size_t len2 = (size_t)MyStringLen(path) * 2; | ||
158 | const size_t len1 = len2 + add_Prefix_Len * 2; | ||
159 | if (!needPrintName) | ||
160 | len2 = 0; | ||
161 | |||
162 | size_t totalNamesSize = (len1 + len2); | ||
163 | |||
164 | /* some WIM imagex software uses old scheme for symbolic links. | ||
165 | so we can old scheme for byte to byte compatibility */ | ||
166 | |||
167 | bool newOrderScheme = isSymLink; | ||
168 | // newOrderScheme = false; | ||
169 | |||
170 | if (!newOrderScheme) | ||
171 | totalNamesSize += 2 * 2; | ||
172 | |||
173 | const size_t size = 8 + 8 + (isSymLink ? 4 : 0) + totalNamesSize; | ||
174 | if (size != (UInt16)size) | ||
175 | return false; | ||
176 | dest.Alloc(size); | ||
177 | memset(dest, 0, size); | ||
178 | const UInt32 tag = isSymLink ? | ||
179 | _my_IO_REPARSE_TAG_SYMLINK : | ||
180 | _my_IO_REPARSE_TAG_MOUNT_POINT; | ||
181 | Byte *p = dest; | ||
182 | Set32(p, tag); | ||
183 | Set16(p + 4, (UInt16)(size - 8)); | ||
184 | Set16(p + 6, 0); | ||
185 | p += 8; | ||
186 | |||
187 | unsigned subOffs = 0; | ||
188 | unsigned printOffs = 0; | ||
189 | if (newOrderScheme) | ||
190 | subOffs = (unsigned)len2; | ||
191 | else | ||
192 | printOffs = (unsigned)len1 + 2; | ||
193 | |||
194 | Set16(p + 0, (UInt16)subOffs); | ||
195 | Set16(p + 2, (UInt16)len1); | ||
196 | Set16(p + 4, (UInt16)printOffs); | ||
197 | Set16(p + 6, (UInt16)len2); | ||
198 | |||
199 | p += 8; | ||
200 | if (isSymLink) | ||
201 | { | ||
202 | UInt32 flags = isAbs ? 0 : _my_SYMLINK_FLAG_RELATIVE; | ||
203 | Set32(p, flags); | ||
204 | p += 4; | ||
205 | } | ||
206 | |||
207 | if (add_Prefix_Len != 0) | ||
208 | WriteString(p + subOffs, k_LinkPrefix); | ||
209 | WriteString(p + subOffs + add_Prefix_Len * 2, path); | ||
210 | if (needPrintName) | ||
211 | WriteString(p + printOffs, path); | ||
212 | return true; | ||
213 | } | ||
214 | |||
215 | #endif // defined(_WIN32) && !defined(UNDER_CE) | ||
216 | |||
217 | |||
218 | static void GetString(const Byte *p, unsigned len, UString &res) | ||
219 | { | ||
220 | wchar_t *s = res.GetBuf(len); | ||
221 | unsigned i; | ||
222 | for (i = 0; i < len; i++) | ||
223 | { | ||
224 | wchar_t c = Get16(p + i * 2); | ||
225 | if (c == 0) | ||
226 | break; | ||
227 | s[i] = c; | ||
228 | } | ||
229 | s[i] = 0; | ||
230 | res.ReleaseBuf_SetLen(i); | ||
231 | } | ||
232 | |||
233 | bool CReparseAttr::Parse(const Byte *p, size_t size) | ||
234 | { | ||
235 | ErrorCode = (DWORD)ERROR_INVALID_REPARSE_DATA; | ||
236 | HeaderError = true; | ||
237 | TagIsUnknown = true; | ||
238 | MinorError = false; | ||
239 | |||
240 | if (size < 8) | ||
241 | return false; | ||
242 | Tag = Get32(p); | ||
243 | UInt32 len = Get16(p + 4); | ||
244 | if (len + 8 != size) | ||
245 | // if (len + 8 > size) | ||
246 | return false; | ||
247 | /* | ||
248 | if ((type & kReparseFlags_Alias) == 0 || | ||
249 | (type & kReparseFlags_Microsoft) == 0 || | ||
250 | (type & 0xFFFF) != 3) | ||
251 | */ | ||
252 | |||
253 | if (Get16(p + 6) != 0) // padding | ||
254 | return false; | ||
255 | |||
256 | HeaderError = false; | ||
257 | |||
258 | if ( Tag != _my_IO_REPARSE_TAG_MOUNT_POINT | ||
259 | && Tag != _my_IO_REPARSE_TAG_SYMLINK | ||
260 | && Tag != _my_IO_REPARSE_TAG_LX_SYMLINK) | ||
261 | { | ||
262 | // for unsupported reparse points | ||
263 | ErrorCode = (DWORD)ERROR_REPARSE_TAG_INVALID; // ERROR_REPARSE_TAG_MISMATCH | ||
264 | // errorCode = ERROR_REPARSE_TAG_MISMATCH; // ERROR_REPARSE_TAG_INVALID | ||
265 | return false; | ||
266 | } | ||
267 | |||
268 | TagIsUnknown = false; | ||
269 | |||
270 | p += 8; | ||
271 | size -= 8; | ||
272 | |||
273 | if (Tag == _my_IO_REPARSE_TAG_LX_SYMLINK) | ||
274 | { | ||
275 | if (len < 4) | ||
276 | return false; | ||
277 | Flags = Get32(p); // maybe it's not Flags | ||
278 | if (Flags != _my_LX_SYMLINK_FLAG) | ||
279 | return false; | ||
280 | len -= 4; | ||
281 | p += 4; | ||
282 | char *s = WslName.GetBuf(len); | ||
283 | unsigned i; | ||
284 | for (i = 0; i < len; i++) | ||
285 | { | ||
286 | char c = (char)p[i]; | ||
287 | s[i] = c; | ||
288 | if (c == 0) | ||
289 | break; | ||
290 | } | ||
291 | WslName.ReleaseBuf_SetEnd(i); | ||
292 | MinorError = (i != len); | ||
293 | ErrorCode = 0; | ||
294 | return true; | ||
295 | } | ||
296 | |||
297 | if (len < 8) | ||
298 | return false; | ||
299 | unsigned subOffs = Get16(p); | ||
300 | unsigned subLen = Get16(p + 2); | ||
301 | unsigned printOffs = Get16(p + 4); | ||
302 | unsigned printLen = Get16(p + 6); | ||
303 | len -= 8; | ||
304 | p += 8; | ||
305 | |||
306 | Flags = 0; | ||
307 | if (Tag == _my_IO_REPARSE_TAG_SYMLINK) | ||
308 | { | ||
309 | if (len < 4) | ||
310 | return false; | ||
311 | Flags = Get32(p); | ||
312 | len -= 4; | ||
313 | p += 4; | ||
314 | } | ||
315 | |||
316 | if ((subOffs & 1) != 0 || subOffs > len || len - subOffs < subLen) | ||
317 | return false; | ||
318 | if ((printOffs & 1) != 0 || printOffs > len || len - printOffs < printLen) | ||
319 | return false; | ||
320 | GetString(p + subOffs, subLen >> 1, SubsName); | ||
321 | GetString(p + printOffs, printLen >> 1, PrintName); | ||
322 | |||
323 | ErrorCode = 0; | ||
324 | return true; | ||
325 | } | ||
326 | |||
327 | |||
328 | bool CReparseShortInfo::Parse(const Byte *p, size_t size) | ||
329 | { | ||
330 | const Byte *start = p; | ||
331 | Offset= 0; | ||
332 | Size = 0; | ||
333 | if (size < 8) | ||
334 | return false; | ||
335 | UInt32 Tag = Get32(p); | ||
336 | UInt32 len = Get16(p + 4); | ||
337 | if (len + 8 > size) | ||
338 | return false; | ||
339 | /* | ||
340 | if ((type & kReparseFlags_Alias) == 0 || | ||
341 | (type & kReparseFlags_Microsoft) == 0 || | ||
342 | (type & 0xFFFF) != 3) | ||
343 | */ | ||
344 | if (Tag != _my_IO_REPARSE_TAG_MOUNT_POINT && | ||
345 | Tag != _my_IO_REPARSE_TAG_SYMLINK) | ||
346 | // return true; | ||
347 | return false; | ||
348 | |||
349 | if (Get16(p + 6) != 0) // padding | ||
350 | return false; | ||
351 | |||
352 | p += 8; | ||
353 | size -= 8; | ||
354 | |||
355 | if (len != size) // do we need that check? | ||
356 | return false; | ||
357 | |||
358 | if (len < 8) | ||
359 | return false; | ||
360 | unsigned subOffs = Get16(p); | ||
361 | unsigned subLen = Get16(p + 2); | ||
362 | unsigned printOffs = Get16(p + 4); | ||
363 | unsigned printLen = Get16(p + 6); | ||
364 | len -= 8; | ||
365 | p += 8; | ||
366 | |||
367 | // UInt32 Flags = 0; | ||
368 | if (Tag == _my_IO_REPARSE_TAG_SYMLINK) | ||
369 | { | ||
370 | if (len < 4) | ||
371 | return false; | ||
372 | // Flags = Get32(p); | ||
373 | len -= 4; | ||
374 | p += 4; | ||
375 | } | ||
376 | |||
377 | if ((subOffs & 1) != 0 || subOffs > len || len - subOffs < subLen) | ||
378 | return false; | ||
379 | if ((printOffs & 1) != 0 || printOffs > len || len - printOffs < printLen) | ||
380 | return false; | ||
381 | |||
382 | Offset = (unsigned)(p - start) + subOffs; | ||
383 | Size = subLen; | ||
384 | return true; | ||
385 | } | ||
386 | |||
387 | bool CReparseAttr::IsOkNamePair() const | ||
388 | { | ||
389 | if (IsLinkPrefix(SubsName)) | ||
390 | { | ||
391 | if (!IsDrivePath(SubsName.Ptr(k_LinkPrefix_Size))) | ||
392 | return PrintName.IsEmpty(); | ||
393 | if (wcscmp(SubsName.Ptr(k_LinkPrefix_Size), PrintName) == 0) | ||
394 | return true; | ||
395 | } | ||
396 | return wcscmp(SubsName, PrintName) == 0; | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | bool CReparseAttr::IsVolume() const | ||
401 | { | ||
402 | if (!IsLinkPrefix(SubsName)) | ||
403 | return false; | ||
404 | return IsVolumeName(SubsName.Ptr(k_LinkPrefix_Size)); | ||
405 | } | ||
406 | */ | ||
407 | |||
408 | UString CReparseAttr::GetPath() const | ||
409 | { | ||
410 | if (IsSymLink_WSL()) | ||
411 | { | ||
412 | UString u; | ||
413 | // if (CheckUTF8(attr.WslName) | ||
414 | if (!ConvertUTF8ToUnicode(WslName, u)) | ||
415 | MultiByteToUnicodeString2(u, WslName); | ||
416 | return u; | ||
417 | } | ||
418 | |||
419 | UString s (SubsName); | ||
420 | if (IsLinkPrefix(s)) | ||
421 | { | ||
422 | s.ReplaceOneCharAtPos(1, '\\'); // we normalize prefix from "\??\" to "\\?\" | ||
423 | if (IsDrivePath(s.Ptr(k_LinkPrefix_Size))) | ||
424 | s.DeleteFrontal(k_LinkPrefix_Size); | ||
425 | } | ||
426 | return s; | ||
427 | } | ||
428 | |||
429 | #ifdef SUPPORT_DEVICE_FILE | ||
430 | |||
431 | namespace NSystem | ||
432 | { | ||
433 | bool MyGetDiskFreeSpace(CFSTR rootPath, UInt64 &clusterSize, UInt64 &totalSize, UInt64 &freeSize); | ||
434 | } | ||
435 | #endif // SUPPORT_DEVICE_FILE | ||
436 | |||
437 | #if defined(_WIN32) && !defined(UNDER_CE) | ||
438 | |||
439 | namespace NIO { | ||
440 | |||
441 | bool GetReparseData(CFSTR path, CByteBuffer &reparseData, BY_HANDLE_FILE_INFORMATION *fileInfo) | ||
442 | { | ||
443 | reparseData.Free(); | ||
444 | CInFile file; | ||
445 | if (!file.OpenReparse(path)) | ||
446 | return false; | ||
447 | |||
448 | if (fileInfo) | ||
449 | file.GetFileInformation(fileInfo); | ||
450 | |||
451 | const unsigned kBufSize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE; | ||
452 | CByteArr buf(kBufSize); | ||
453 | DWORD returnedSize; | ||
454 | if (!file.DeviceIoControlOut(my_FSCTL_GET_REPARSE_POINT, buf, kBufSize, &returnedSize)) | ||
455 | return false; | ||
456 | reparseData.CopyFrom(buf, returnedSize); | ||
457 | return true; | ||
458 | } | ||
459 | |||
460 | static bool CreatePrefixDirOfFile(CFSTR path) | ||
461 | { | ||
462 | FString path2 (path); | ||
463 | int pos = path2.ReverseFind_PathSepar(); | ||
464 | if (pos < 0) | ||
465 | return true; | ||
466 | #ifdef _WIN32 | ||
467 | if (pos == 2 && path2[1] == L':') | ||
468 | return true; // we don't create Disk folder; | ||
469 | #endif | ||
470 | path2.DeleteFrom((unsigned)pos); | ||
471 | return NDir::CreateComplexDir(path2); | ||
472 | } | ||
473 | |||
474 | |||
475 | static bool OutIoReparseData(DWORD controlCode, CFSTR path, void *data, DWORD size) | ||
476 | { | ||
477 | COutFile file; | ||
478 | if (!file.Open(path, | ||
479 | FILE_SHARE_WRITE, | ||
480 | OPEN_EXISTING, | ||
481 | FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS)) | ||
482 | return false; | ||
483 | |||
484 | DWORD returnedSize; | ||
485 | return file.DeviceIoControl(controlCode, data, size, NULL, 0, &returnedSize); | ||
486 | } | ||
487 | |||
488 | |||
489 | // If there is Reparse data already, it still writes new Reparse data | ||
490 | bool SetReparseData(CFSTR path, bool isDir, const void *data, DWORD size) | ||
491 | { | ||
492 | NFile::NFind::CFileInfo fi; | ||
493 | if (fi.Find(path)) | ||
494 | { | ||
495 | if (fi.IsDir() != isDir) | ||
496 | { | ||
497 | ::SetLastError(ERROR_DIRECTORY); | ||
498 | return false; | ||
499 | } | ||
500 | } | ||
501 | else | ||
502 | { | ||
503 | if (isDir) | ||
504 | { | ||
505 | if (!NDir::CreateComplexDir(path)) | ||
506 | return false; | ||
507 | } | ||
508 | else | ||
509 | { | ||
510 | CreatePrefixDirOfFile(path); | ||
511 | COutFile file; | ||
512 | if (!file.Create(path, CREATE_NEW)) | ||
513 | return false; | ||
514 | } | ||
515 | } | ||
516 | |||
517 | return OutIoReparseData(my_FSCTL_SET_REPARSE_POINT, path, (void *)(const Byte *)(data), size); | ||
518 | } | ||
519 | |||
520 | |||
521 | bool DeleteReparseData(CFSTR path) | ||
522 | { | ||
523 | CByteBuffer reparseData; | ||
524 | if (!GetReparseData(path, reparseData, NULL)) | ||
525 | return false; | ||
526 | /* MSDN: The tag specified in the ReparseTag member of this structure | ||
527 | must match the tag of the reparse point to be deleted, | ||
528 | and the ReparseDataLength member must be zero */ | ||
529 | #define my_REPARSE_DATA_BUFFER_HEADER_SIZE 8 | ||
530 | if (reparseData.Size() < my_REPARSE_DATA_BUFFER_HEADER_SIZE) | ||
531 | { | ||
532 | SetLastError(ERROR_INVALID_REPARSE_DATA); | ||
533 | return false; | ||
534 | } | ||
535 | BYTE buf[my_REPARSE_DATA_BUFFER_HEADER_SIZE]; | ||
536 | memset(buf, 0, sizeof(buf)); | ||
537 | memcpy(buf, reparseData, 4); // tag | ||
538 | return OutIoReparseData(my_FSCTL_DELETE_REPARSE_POINT, path, buf, sizeof(buf)); | ||
539 | } | ||
540 | |||
541 | } | ||
542 | |||
543 | #endif // defined(_WIN32) && !defined(UNDER_CE) | ||
544 | |||
545 | |||
546 | #ifndef _WIN32 | ||
547 | |||
548 | namespace NIO { | ||
549 | |||
550 | bool GetReparseData(CFSTR path, CByteBuffer &reparseData) | ||
551 | { | ||
552 | reparseData.Free(); | ||
553 | |||
554 | #define MAX_PATHNAME_LEN 1024 | ||
555 | char buf[MAX_PATHNAME_LEN + 2]; | ||
556 | const size_t request = sizeof(buf) - 1; | ||
557 | |||
558 | // printf("\nreadlink() path = %s \n", path); | ||
559 | const ssize_t size = readlink(path, buf, request); | ||
560 | // there is no tail zero | ||
561 | |||
562 | if (size < 0) | ||
563 | return false; | ||
564 | if ((size_t)size >= request) | ||
565 | { | ||
566 | SetLastError(EINVAL); // check it: ENAMETOOLONG | ||
567 | return false; | ||
568 | } | ||
569 | |||
570 | // printf("\nreadlink() res = %s size = %d \n", buf, (int)size); | ||
571 | reparseData.CopyFrom((const Byte *)buf, (size_t)size); | ||
572 | return true; | ||
573 | } | ||
574 | |||
575 | |||
576 | /* | ||
577 | // If there is Reparse data already, it still writes new Reparse data | ||
578 | bool SetReparseData(CFSTR path, bool isDir, const void *data, DWORD size) | ||
579 | { | ||
580 | // AString s; | ||
581 | // s.SetFrom_CalcLen(data, size); | ||
582 | // return (symlink(s, path) == 0); | ||
583 | UNUSED_VAR(path) | ||
584 | UNUSED_VAR(isDir) | ||
585 | UNUSED_VAR(data) | ||
586 | UNUSED_VAR(size) | ||
587 | SetLastError(ENOSYS); | ||
588 | return false; | ||
589 | } | ||
590 | */ | ||
591 | |||
592 | bool SetSymLink(CFSTR from, CFSTR to) | ||
593 | { | ||
594 | // printf("\nsymlink() %s -> %s\n", from, to); | ||
595 | int ir; | ||
596 | // ir = unlink(path); | ||
597 | // if (ir == 0) | ||
598 | ir = symlink(to, from); | ||
599 | return (ir == 0); | ||
600 | } | ||
601 | |||
602 | bool SetSymLink_UString(CFSTR from, const UString &to) | ||
603 | { | ||
604 | AString utf; | ||
605 | ConvertUnicodeToUTF8(to, utf); | ||
606 | return SetSymLink(from, utf); | ||
607 | } | ||
608 | |||
609 | } | ||
610 | |||
611 | #endif // !_WIN32 | ||
612 | |||
613 | }} | ||