• 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 * 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