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