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