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
IsLinkPrefix(const wchar_t * s)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
WriteString(Byte * dest,const wchar_t * path)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
FillLinkData(CByteBuffer & dest,const wchar_t * path,bool isSymLink)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
GetString(const Byte * p,unsigned len,UString & res)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
Parse(const Byte * p,size_t size,DWORD & errorCode)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
Parse(const Byte * p,size_t size)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
IsOkNamePair() const316 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
GetPath() const337 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
GetReparseData(CFSTR path,CByteBuffer & reparseData,BY_HANDLE_FILE_INFORMATION * fileInfo)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
CreatePrefixDirOfFile(CFSTR path)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
SetReparseData(CFSTR path,bool isDir,const void * data,DWORD size)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 }}
441