tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FileLink.cpp (10185B)


      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 
     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 
     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 
    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 
    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 
    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 
    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 
    316 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 
    337 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 
    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 
    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
    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 }}