Home | History | Annotate | Download | only in base
      1 /*
      2  *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "webrtc/base/win32filesystem.h"
     12 
     13 #include "webrtc/base/win32.h"
     14 #include <shellapi.h>
     15 #include <shlobj.h>
     16 #include <tchar.h>
     17 
     18 #include "webrtc/base/arraysize.h"
     19 #include "webrtc/base/fileutils.h"
     20 #include "webrtc/base/pathutils.h"
     21 #include "webrtc/base/scoped_ptr.h"
     22 #include "webrtc/base/stream.h"
     23 #include "webrtc/base/stringutils.h"
     24 
     25 // In several places in this file, we test the integrity level of the process
     26 // before calling GetLongPathName. We do this because calling GetLongPathName
     27 // when running under protected mode IE (a low integrity process) can result in
     28 // a virtualized path being returned, which is wrong if you only plan to read.
     29 // TODO: Waiting to hear back from IE team on whether this is the
     30 // best approach; IEIsProtectedModeProcess is another possible solution.
     31 
     32 namespace rtc {
     33 
     34 bool Win32Filesystem::CreateFolder(const Pathname &pathname) {
     35   if (pathname.pathname().empty() || !pathname.filename().empty())
     36     return false;
     37 
     38   std::wstring path16;
     39   if (!Utf8ToWindowsFilename(pathname.pathname(), &path16))
     40     return false;
     41 
     42   DWORD res = ::GetFileAttributes(path16.c_str());
     43   if (res != INVALID_FILE_ATTRIBUTES) {
     44     // Something exists at this location, check if it is a directory
     45     return ((res & FILE_ATTRIBUTE_DIRECTORY) != 0);
     46   } else if ((GetLastError() != ERROR_FILE_NOT_FOUND)
     47               && (GetLastError() != ERROR_PATH_NOT_FOUND)) {
     48     // Unexpected error
     49     return false;
     50   }
     51 
     52   // Directory doesn't exist, look up one directory level
     53   if (!pathname.parent_folder().empty()) {
     54     Pathname parent(pathname);
     55     parent.SetFolder(pathname.parent_folder());
     56     if (!CreateFolder(parent)) {
     57       return false;
     58     }
     59   }
     60 
     61   return (::CreateDirectory(path16.c_str(), NULL) != 0);
     62 }
     63 
     64 FileStream *Win32Filesystem::OpenFile(const Pathname &filename,
     65                                       const std::string &mode) {
     66   FileStream *fs = new FileStream();
     67   if (fs && !fs->Open(filename.pathname().c_str(), mode.c_str(), NULL)) {
     68     delete fs;
     69     fs = NULL;
     70   }
     71   return fs;
     72 }
     73 
     74 bool Win32Filesystem::CreatePrivateFile(const Pathname &filename) {
     75   // To make the file private to the current user, we first must construct a
     76   // SECURITY_DESCRIPTOR specifying an ACL. This code is mostly based upon
     77   // http://msdn.microsoft.com/en-us/library/ms707085%28VS.85%29.aspx
     78 
     79   // Get the current process token.
     80   HANDLE process_token = INVALID_HANDLE_VALUE;
     81   if (!::OpenProcessToken(::GetCurrentProcess(),
     82                           TOKEN_QUERY,
     83                           &process_token)) {
     84     LOG_ERR(LS_ERROR) << "OpenProcessToken() failed";
     85     return false;
     86   }
     87 
     88   // Get the size of its TOKEN_USER structure. Return value is not checked
     89   // because we expect it to fail.
     90   DWORD token_user_size = 0;
     91   (void)::GetTokenInformation(process_token,
     92                               TokenUser,
     93                               NULL,
     94                               0,
     95                               &token_user_size);
     96 
     97   // Get the TOKEN_USER structure.
     98   scoped_ptr<char[]> token_user_bytes(new char[token_user_size]);
     99   PTOKEN_USER token_user = reinterpret_cast<PTOKEN_USER>(
    100       token_user_bytes.get());
    101   memset(token_user, 0, token_user_size);
    102   BOOL success = ::GetTokenInformation(process_token,
    103                                        TokenUser,
    104                                        token_user,
    105                                        token_user_size,
    106                                        &token_user_size);
    107   // We're now done with this.
    108   ::CloseHandle(process_token);
    109   if (!success) {
    110     LOG_ERR(LS_ERROR) << "GetTokenInformation() failed";
    111     return false;
    112   }
    113 
    114   if (!IsValidSid(token_user->User.Sid)) {
    115     LOG_ERR(LS_ERROR) << "Current process has invalid user SID";
    116     return false;
    117   }
    118 
    119   // Compute size needed for an ACL that allows access to just this user.
    120   int acl_size = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) +
    121       GetLengthSid(token_user->User.Sid);
    122 
    123   // Allocate it.
    124   scoped_ptr<char[]> acl_bytes(new char[acl_size]);
    125   PACL acl = reinterpret_cast<PACL>(acl_bytes.get());
    126   memset(acl, 0, acl_size);
    127   if (!::InitializeAcl(acl, acl_size, ACL_REVISION)) {
    128     LOG_ERR(LS_ERROR) << "InitializeAcl() failed";
    129     return false;
    130   }
    131 
    132   // Allow access to only the current user.
    133   if (!::AddAccessAllowedAce(acl,
    134                              ACL_REVISION,
    135                              GENERIC_READ | GENERIC_WRITE | STANDARD_RIGHTS_ALL,
    136                              token_user->User.Sid)) {
    137     LOG_ERR(LS_ERROR) << "AddAccessAllowedAce() failed";
    138     return false;
    139   }
    140 
    141   // Now make the security descriptor.
    142   SECURITY_DESCRIPTOR security_descriptor;
    143   if (!::InitializeSecurityDescriptor(&security_descriptor,
    144                                       SECURITY_DESCRIPTOR_REVISION)) {
    145     LOG_ERR(LS_ERROR) << "InitializeSecurityDescriptor() failed";
    146     return false;
    147   }
    148 
    149   // Put the ACL in it.
    150   if (!::SetSecurityDescriptorDacl(&security_descriptor,
    151                                    TRUE,
    152                                    acl,
    153                                    FALSE)) {
    154     LOG_ERR(LS_ERROR) << "SetSecurityDescriptorDacl() failed";
    155     return false;
    156   }
    157 
    158   // Finally create the file.
    159   SECURITY_ATTRIBUTES security_attributes;
    160   security_attributes.nLength = sizeof(security_attributes);
    161   security_attributes.lpSecurityDescriptor = &security_descriptor;
    162   security_attributes.bInheritHandle = FALSE;
    163   HANDLE handle = ::CreateFile(
    164       ToUtf16(filename.pathname()).c_str(),
    165       GENERIC_READ | GENERIC_WRITE,
    166       FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
    167       &security_attributes,
    168       CREATE_NEW,
    169       0,
    170       NULL);
    171   if (INVALID_HANDLE_VALUE == handle) {
    172     LOG_ERR(LS_ERROR) << "CreateFile() failed";
    173     return false;
    174   }
    175   if (!::CloseHandle(handle)) {
    176     LOG_ERR(LS_ERROR) << "CloseFile() failed";
    177     // Continue.
    178   }
    179   return true;
    180 }
    181 
    182 bool Win32Filesystem::DeleteFile(const Pathname &filename) {
    183   LOG(LS_INFO) << "Deleting file " << filename.pathname();
    184   if (!IsFile(filename)) {
    185     ASSERT(IsFile(filename));
    186     return false;
    187   }
    188   return ::DeleteFile(ToUtf16(filename.pathname()).c_str()) != 0;
    189 }
    190 
    191 bool Win32Filesystem::DeleteEmptyFolder(const Pathname &folder) {
    192   LOG(LS_INFO) << "Deleting folder " << folder.pathname();
    193 
    194   std::string no_slash(folder.pathname(), 0, folder.pathname().length()-1);
    195   return ::RemoveDirectory(ToUtf16(no_slash).c_str()) != 0;
    196 }
    197 
    198 bool Win32Filesystem::GetTemporaryFolder(Pathname &pathname, bool create,
    199                                          const std::string *append) {
    200   wchar_t buffer[MAX_PATH + 1];
    201   if (!::GetTempPath(arraysize(buffer), buffer))
    202     return false;
    203   if (!IsCurrentProcessLowIntegrity() &&
    204       !::GetLongPathName(buffer, buffer, arraysize(buffer)))
    205     return false;
    206   size_t len = strlen(buffer);
    207   if ((len > 0) && (buffer[len-1] != '\\')) {
    208     len += strcpyn(buffer + len, arraysize(buffer) - len, L"\\");
    209   }
    210   if (len >= arraysize(buffer) - 1)
    211     return false;
    212   pathname.clear();
    213   pathname.SetFolder(ToUtf8(buffer));
    214   if (append != NULL) {
    215     ASSERT(!append->empty());
    216     pathname.AppendFolder(*append);
    217   }
    218   return !create || CreateFolder(pathname);
    219 }
    220 
    221 std::string Win32Filesystem::TempFilename(const Pathname &dir,
    222                                           const std::string &prefix) {
    223   wchar_t filename[MAX_PATH];
    224   if (::GetTempFileName(ToUtf16(dir.pathname()).c_str(),
    225                         ToUtf16(prefix).c_str(), 0, filename) != 0)
    226     return ToUtf8(filename);
    227   ASSERT(false);
    228   return "";
    229 }
    230 
    231 bool Win32Filesystem::MoveFile(const Pathname &old_path,
    232                                const Pathname &new_path) {
    233   if (!IsFile(old_path)) {
    234     ASSERT(IsFile(old_path));
    235     return false;
    236   }
    237   LOG(LS_INFO) << "Moving " << old_path.pathname()
    238                << " to " << new_path.pathname();
    239   return ::MoveFile(ToUtf16(old_path.pathname()).c_str(),
    240                     ToUtf16(new_path.pathname()).c_str()) != 0;
    241 }
    242 
    243 bool Win32Filesystem::MoveFolder(const Pathname &old_path,
    244                                  const Pathname &new_path) {
    245   if (!IsFolder(old_path)) {
    246     ASSERT(IsFolder(old_path));
    247     return false;
    248   }
    249   LOG(LS_INFO) << "Moving " << old_path.pathname()
    250                << " to " << new_path.pathname();
    251   if (::MoveFile(ToUtf16(old_path.pathname()).c_str(),
    252                ToUtf16(new_path.pathname()).c_str()) == 0) {
    253     if (::GetLastError() != ERROR_NOT_SAME_DEVICE) {
    254       LOG_GLE(LS_ERROR) << "Failed to move file";
    255       return false;
    256     }
    257     if (!CopyFolder(old_path, new_path))
    258       return false;
    259     if (!DeleteFolderAndContents(old_path))
    260       return false;
    261   }
    262   return true;
    263 }
    264 
    265 bool Win32Filesystem::IsFolder(const Pathname &path) {
    266   WIN32_FILE_ATTRIBUTE_DATA data = {0};
    267   if (0 == ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
    268                                  GetFileExInfoStandard, &data))
    269     return false;
    270   return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ==
    271       FILE_ATTRIBUTE_DIRECTORY;
    272 }
    273 
    274 bool Win32Filesystem::IsFile(const Pathname &path) {
    275   WIN32_FILE_ATTRIBUTE_DATA data = {0};
    276   if (0 == ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
    277                                  GetFileExInfoStandard, &data))
    278     return false;
    279   return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
    280 }
    281 
    282 bool Win32Filesystem::IsAbsent(const Pathname& path) {
    283   WIN32_FILE_ATTRIBUTE_DATA data = {0};
    284   if (0 != ::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
    285                                  GetFileExInfoStandard, &data))
    286     return false;
    287   DWORD err = ::GetLastError();
    288   return (ERROR_FILE_NOT_FOUND == err || ERROR_PATH_NOT_FOUND == err);
    289 }
    290 
    291 bool Win32Filesystem::CopyFile(const Pathname &old_path,
    292                                const Pathname &new_path) {
    293   return ::CopyFile(ToUtf16(old_path.pathname()).c_str(),
    294                     ToUtf16(new_path.pathname()).c_str(), TRUE) != 0;
    295 }
    296 
    297 bool Win32Filesystem::IsTemporaryPath(const Pathname& pathname) {
    298   TCHAR buffer[MAX_PATH + 1];
    299   if (!::GetTempPath(arraysize(buffer), buffer))
    300     return false;
    301   if (!IsCurrentProcessLowIntegrity() &&
    302       !::GetLongPathName(buffer, buffer, arraysize(buffer)))
    303     return false;
    304   return (::strnicmp(ToUtf16(pathname.pathname()).c_str(),
    305                      buffer, strlen(buffer)) == 0);
    306 }
    307 
    308 bool Win32Filesystem::GetFileSize(const Pathname &pathname, size_t *size) {
    309   WIN32_FILE_ATTRIBUTE_DATA data = {0};
    310   if (::GetFileAttributesEx(ToUtf16(pathname.pathname()).c_str(),
    311                             GetFileExInfoStandard, &data) == 0)
    312   return false;
    313   *size = data.nFileSizeLow;
    314   return true;
    315 }
    316 
    317 bool Win32Filesystem::GetFileTime(const Pathname& path, FileTimeType which,
    318                                   time_t* time) {
    319   WIN32_FILE_ATTRIBUTE_DATA data = {0};
    320   if (::GetFileAttributesEx(ToUtf16(path.pathname()).c_str(),
    321                             GetFileExInfoStandard, &data) == 0)
    322     return false;
    323   switch (which) {
    324   case FTT_CREATED:
    325     FileTimeToUnixTime(data.ftCreationTime, time);
    326     break;
    327   case FTT_MODIFIED:
    328     FileTimeToUnixTime(data.ftLastWriteTime, time);
    329     break;
    330   case FTT_ACCESSED:
    331     FileTimeToUnixTime(data.ftLastAccessTime, time);
    332     break;
    333   default:
    334     return false;
    335   }
    336   return true;
    337 }
    338 
    339 bool Win32Filesystem::GetAppPathname(Pathname* path) {
    340   TCHAR buffer[MAX_PATH + 1];
    341   if (0 == ::GetModuleFileName(NULL, buffer, arraysize(buffer)))
    342     return false;
    343   path->SetPathname(ToUtf8(buffer));
    344   return true;
    345 }
    346 
    347 bool Win32Filesystem::GetAppDataFolder(Pathname* path, bool per_user) {
    348   ASSERT(!organization_name_.empty());
    349   ASSERT(!application_name_.empty());
    350   TCHAR buffer[MAX_PATH + 1];
    351   int csidl = per_user ? CSIDL_LOCAL_APPDATA : CSIDL_COMMON_APPDATA;
    352   if (!::SHGetSpecialFolderPath(NULL, buffer, csidl, TRUE))
    353     return false;
    354   if (!IsCurrentProcessLowIntegrity() &&
    355       !::GetLongPathName(buffer, buffer, arraysize(buffer)))
    356     return false;
    357   size_t len = strcatn(buffer, arraysize(buffer), __T("\\"));
    358   len += strcpyn(buffer + len, arraysize(buffer) - len,
    359                  ToUtf16(organization_name_).c_str());
    360   if ((len > 0) && (buffer[len-1] != __T('\\'))) {
    361     len += strcpyn(buffer + len, arraysize(buffer) - len, __T("\\"));
    362   }
    363   len += strcpyn(buffer + len, arraysize(buffer) - len,
    364                  ToUtf16(application_name_).c_str());
    365   if ((len > 0) && (buffer[len-1] != __T('\\'))) {
    366     len += strcpyn(buffer + len, arraysize(buffer) - len, __T("\\"));
    367   }
    368   if (len >= arraysize(buffer) - 1)
    369     return false;
    370   path->clear();
    371   path->SetFolder(ToUtf8(buffer));
    372   return CreateFolder(*path);
    373 }
    374 
    375 bool Win32Filesystem::GetAppTempFolder(Pathname* path) {
    376   if (!GetAppPathname(path))
    377     return false;
    378   std::string filename(path->filename());
    379   return GetTemporaryFolder(*path, true, &filename);
    380 }
    381 
    382 bool Win32Filesystem::GetDiskFreeSpace(const Pathname& path,
    383                                        int64_t* free_bytes) {
    384   if (!free_bytes) {
    385     return false;
    386   }
    387   char drive[4];
    388   std::wstring drive16;
    389   const wchar_t* target_drive = NULL;
    390   if (path.GetDrive(drive, sizeof(drive))) {
    391     drive16 = ToUtf16(drive);
    392     target_drive = drive16.c_str();
    393   } else if (path.folder().substr(0, 2) == "\\\\") {
    394     // UNC path, fail.
    395     // TODO: Handle UNC paths.
    396     return false;
    397   } else {
    398     // The path is probably relative.  GetDriveType and GetDiskFreeSpaceEx
    399     // use the current drive if NULL is passed as the drive name.
    400     // TODO: Add method to Pathname to determine if the path is relative.
    401     // TODO: Add method to Pathname to convert a path to absolute.
    402   }
    403   UINT drive_type = ::GetDriveType(target_drive);
    404   if ((drive_type == DRIVE_REMOTE) || (drive_type == DRIVE_UNKNOWN)) {
    405     LOG(LS_VERBOSE) << "Remote or unknown drive: " << drive;
    406     return false;
    407   }
    408 
    409   int64_t total_number_of_bytes;       // receives the number of bytes on disk
    410   int64_t total_number_of_free_bytes;  // receives the free bytes on disk
    411   // make sure things won't change in 64 bit machine
    412   // TODO replace with compile time assert
    413   ASSERT(sizeof(ULARGE_INTEGER) == sizeof(uint64_t));  // NOLINT
    414   if (::GetDiskFreeSpaceEx(target_drive,
    415                            (PULARGE_INTEGER)free_bytes,
    416                            (PULARGE_INTEGER)&total_number_of_bytes,
    417                            (PULARGE_INTEGER)&total_number_of_free_bytes)) {
    418     return true;
    419   } else {
    420     LOG(LS_VERBOSE) << "GetDiskFreeSpaceEx returns error.";
    421     return false;
    422   }
    423 }
    424 
    425 Pathname Win32Filesystem::GetCurrentDirectory() {
    426   Pathname cwd;
    427   int path_len = 0;
    428   scoped_ptr<wchar_t[]> path;
    429   do {
    430     int needed = ::GetCurrentDirectory(path_len, path.get());
    431     if (needed == 0) {
    432       // Error.
    433       LOG_GLE(LS_ERROR) << "::GetCurrentDirectory() failed";
    434       return cwd;  // returns empty pathname
    435     }
    436     if (needed <= path_len) {
    437       // It wrote successfully.
    438       break;
    439     }
    440     // Else need to re-alloc for "needed".
    441     path.reset(new wchar_t[needed]);
    442     path_len = needed;
    443   } while (true);
    444   cwd.SetFolder(ToUtf8(path.get()));
    445   return cwd;
    446 }
    447 
    448 // TODO: Consider overriding DeleteFolderAndContents for speed and potentially
    449 // better OS integration (recycle bin?)
    450 /*
    451   std::wstring temp_path16 = ToUtf16(temp_path.pathname());
    452   temp_path16.append(1, '*');
    453   temp_path16.append(1, '\0');
    454 
    455   SHFILEOPSTRUCT file_op = { 0 };
    456   file_op.wFunc = FO_DELETE;
    457   file_op.pFrom = temp_path16.c_str();
    458   file_op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
    459   return (0 == SHFileOperation(&file_op));
    460 */
    461 
    462 }  // namespace rtc
    463