• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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