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
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)197 bool CReparseAttr::Parse(const Byte *p, size_t size)
198 {
199 if (size < 8)
200 return false;
201 Tag = Get32(p);
202 UInt32 len = Get16(p + 4);
203 if (len + 8 > size)
204 return false;
205 /*
206 if ((type & kReparseFlags_Alias) == 0 ||
207 (type & kReparseFlags_Microsoft) == 0 ||
208 (type & 0xFFFF) != 3)
209 */
210 if (Tag != _my_IO_REPARSE_TAG_MOUNT_POINT &&
211 Tag != _my_IO_REPARSE_TAG_SYMLINK)
212 // return true;
213 return false;
214
215 if (Get16(p + 6) != 0) // padding
216 return false;
217
218 p += 8;
219 size -= 8;
220
221 if (len != size) // do we need that check?
222 return false;
223
224 if (len < 8)
225 return false;
226 unsigned subOffs = Get16(p);
227 unsigned subLen = Get16(p + 2);
228 unsigned printOffs = Get16(p + 4);
229 unsigned printLen = Get16(p + 6);
230 len -= 8;
231 p += 8;
232
233 Flags = 0;
234 if (Tag == _my_IO_REPARSE_TAG_SYMLINK)
235 {
236 if (len < 4)
237 return false;
238 Flags = Get32(p);
239 len -= 4;
240 p += 4;
241 }
242
243 if ((subOffs & 1) != 0 || subOffs > len || len - subOffs < subLen)
244 return false;
245 if ((printOffs & 1) != 0 || printOffs > len || len - printOffs < printLen)
246 return false;
247 GetString(p + subOffs, subLen >> 1, SubsName);
248 GetString(p + printOffs, printLen >> 1, PrintName);
249
250 return true;
251 }
252
Parse(const Byte * p,size_t size)253 bool CReparseShortInfo::Parse(const Byte *p, size_t size)
254 {
255 const Byte *start = p;
256 Offset= 0;
257 Size = 0;
258 if (size < 8)
259 return false;
260 UInt32 Tag = Get32(p);
261 UInt32 len = Get16(p + 4);
262 if (len + 8 > size)
263 return false;
264 /*
265 if ((type & kReparseFlags_Alias) == 0 ||
266 (type & kReparseFlags_Microsoft) == 0 ||
267 (type & 0xFFFF) != 3)
268 */
269 if (Tag != _my_IO_REPARSE_TAG_MOUNT_POINT &&
270 Tag != _my_IO_REPARSE_TAG_SYMLINK)
271 // return true;
272 return false;
273
274 if (Get16(p + 6) != 0) // padding
275 return false;
276
277 p += 8;
278 size -= 8;
279
280 if (len != size) // do we need that check?
281 return false;
282
283 if (len < 8)
284 return false;
285 unsigned subOffs = Get16(p);
286 unsigned subLen = Get16(p + 2);
287 unsigned printOffs = Get16(p + 4);
288 unsigned printLen = Get16(p + 6);
289 len -= 8;
290 p += 8;
291
292 // UInt32 Flags = 0;
293 if (Tag == _my_IO_REPARSE_TAG_SYMLINK)
294 {
295 if (len < 4)
296 return false;
297 // Flags = Get32(p);
298 len -= 4;
299 p += 4;
300 }
301
302 if ((subOffs & 1) != 0 || subOffs > len || len - subOffs < subLen)
303 return false;
304 if ((printOffs & 1) != 0 || printOffs > len || len - printOffs < printLen)
305 return false;
306
307 Offset = (unsigned)(p - start) + subOffs;
308 Size = subLen;
309 return true;
310 }
311
IsOkNamePair() const312 bool CReparseAttr::IsOkNamePair() const
313 {
314 if (IsLinkPrefix(SubsName))
315 {
316 if (!IsDrivePath(SubsName.Ptr(k_LinkPrefix_Size)))
317 return PrintName.IsEmpty();
318 if (wcscmp(SubsName.Ptr(k_LinkPrefix_Size), PrintName) == 0)
319 return true;
320 }
321 return wcscmp(SubsName, PrintName) == 0;
322 }
323
324 /*
325 bool CReparseAttr::IsVolume() const
326 {
327 if (!IsLinkPrefix(SubsName))
328 return false;
329 return IsVolumeName(SubsName.Ptr(k_LinkPrefix_Size));
330 }
331 */
332
GetPath() const333 UString CReparseAttr::GetPath() const
334 {
335 UString s = SubsName;
336 if (IsLinkPrefix(s))
337 {
338 s.ReplaceOneCharAtPos(1, '\\');
339 if (IsDrivePath(s.Ptr(k_LinkPrefix_Size)))
340 s.DeleteFrontal(k_LinkPrefix_Size);
341 }
342 return s;
343 }
344
345
346 #ifdef SUPPORT_DEVICE_FILE
347
348 namespace NSystem
349 {
350 bool MyGetDiskFreeSpace(CFSTR rootPath, UInt64 &clusterSize, UInt64 &totalSize, UInt64 &freeSize);
351 }
352 #endif
353
354 #ifndef UNDER_CE
355
356 namespace NIO {
357
GetReparseData(CFSTR path,CByteBuffer & reparseData,BY_HANDLE_FILE_INFORMATION * fileInfo)358 bool GetReparseData(CFSTR path, CByteBuffer &reparseData, BY_HANDLE_FILE_INFORMATION *fileInfo)
359 {
360 reparseData.Free();
361 CInFile file;
362 if (!file.OpenReparse(path))
363 return false;
364
365 if (fileInfo)
366 file.GetFileInformation(fileInfo);
367
368 const unsigned kBufSize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
369 CByteArr buf(kBufSize);
370 DWORD returnedSize;
371 if (!file.DeviceIoControlOut(my_FSCTL_GET_REPARSE_POINT, buf, kBufSize, &returnedSize))
372 return false;
373 reparseData.CopyFrom(buf, returnedSize);
374 return true;
375 }
376
CreatePrefixDirOfFile(CFSTR path)377 static bool CreatePrefixDirOfFile(CFSTR path)
378 {
379 FString path2 = path;
380 int pos = path2.ReverseFind_PathSepar();
381 if (pos < 0)
382 return true;
383 #ifdef _WIN32
384 if (pos == 2 && path2[1] == L':')
385 return true; // we don't create Disk folder;
386 #endif
387 path2.DeleteFrom(pos);
388 return NDir::CreateComplexDir(path2);
389 }
390
391 // If there is Reprase data already, it still writes new Reparse data
SetReparseData(CFSTR path,bool isDir,const void * data,DWORD size)392 bool SetReparseData(CFSTR path, bool isDir, const void *data, DWORD size)
393 {
394 NFile::NFind::CFileInfo fi;
395 if (fi.Find(path))
396 {
397 if (fi.IsDir() != isDir)
398 {
399 ::SetLastError(ERROR_DIRECTORY);
400 return false;
401 }
402 }
403 else
404 {
405 if (isDir)
406 {
407 if (!NDir::CreateComplexDir(path))
408 return false;
409 }
410 else
411 {
412 CreatePrefixDirOfFile(path);
413 COutFile file;
414 if (!file.Create(path, CREATE_NEW))
415 return false;
416 }
417 }
418
419 COutFile file;
420 if (!file.Open(path,
421 FILE_SHARE_WRITE,
422 OPEN_EXISTING,
423 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS))
424 return false;
425
426 DWORD returnedSize;
427 if (!file.DeviceIoControl(my_FSCTL_SET_REPARSE_POINT, (void *)data, size, NULL, 0, &returnedSize))
428 return false;
429 return true;
430 }
431
432 }
433
434 #endif
435
436 }}
437