FileLink.cpp revision cd66d540cead3f8200b0c73bad9c276d67896c3d
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
17extern bool g_IsNT;
18#endif
19
20namespace NWindows {
21namespace NFile {
22
23using 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/*
58static const UInt32 kReparseFlags_Alias       = (1 << 29);
59static const UInt32 kReparseFlags_HighLatency = (1 << 30);
60static 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
77static const wchar_t *k_LinkPrefix = L"\\??\\";
78static const unsigned k_LinkPrefix_Size = 4;
79
80static const bool IsLinkPrefix(const wchar_t *s)
81{
82  return IsString1PrefixedByString2(s, k_LinkPrefix);
83}
84
85/*
86static const wchar_t *k_VolumePrefix = L"Volume{";
87static const bool IsVolumeName(const wchar_t *s)
88{
89  return IsString1PrefixedByString2(s, k_VolumePrefix);
90}
91*/
92
93void 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
105bool 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
178static 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
187bool 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
243bool 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
302bool 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/*
315bool CReparseAttr::IsVolume() const
316{
317  if (!IsLinkPrefix(SubsName))
318    return false;
319  return IsVolumeName(SubsName.Ptr(k_LinkPrefix_Size));
320}
321*/
322
323UString 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
338namespace NSystem
339{
340bool MyGetDiskFreeSpace(CFSTR rootPath, UInt64 &clusterSize, UInt64 &totalSize, UInt64 &freeSize);
341}
342#endif
343
344#ifndef UNDER_CE
345
346namespace NIO {
347
348bool 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
367static 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
382bool 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