Home | History | Annotate | Download | only in Windows
      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 
     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 
     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 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 
    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 
    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 
    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 
    302 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 
    323 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 
    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 
    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
    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