security/sandbox/chromium/base/file_util_win.cc

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

     1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
     2 // Use of this source code is governed by a BSD-style license that can be
     3 // found in the LICENSE file.
     5 #include "base/file_util.h"
     7 #include <windows.h>
     8 #include <psapi.h>
     9 #include <shellapi.h>
    10 #include <shlobj.h>
    11 #include <time.h>
    13 #include <algorithm>
    14 #include <limits>
    15 #include <string>
    17 #include "base/files/file_path.h"
    18 #include "base/logging.h"
    19 #include "base/metrics/histogram.h"
    20 #include "base/process/process_handle.h"
    21 #include "base/rand_util.h"
    22 #include "base/strings/string_number_conversions.h"
    23 #include "base/strings/string_util.h"
    24 #include "base/strings/utf_string_conversions.h"
    25 #include "base/threading/thread_restrictions.h"
    26 #include "base/time/time.h"
    27 #include "base/win/scoped_handle.h"
    28 #include "base/win/windows_version.h"
    30 namespace base {
    32 namespace {
    34 const DWORD kFileShareAll =
    35     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
    37 bool ShellCopy(const FilePath& from_path,
    38                const FilePath& to_path,
    39                bool recursive) {
    40   // WinXP SHFileOperation doesn't like trailing separators.
    41   FilePath stripped_from = from_path.StripTrailingSeparators();
    42   FilePath stripped_to = to_path.StripTrailingSeparators();
    44   ThreadRestrictions::AssertIOAllowed();
    46   // NOTE: I suspect we could support longer paths, but that would involve
    47   // analyzing all our usage of files.
    48   if (stripped_from.value().length() >= MAX_PATH ||
    49       stripped_to.value().length() >= MAX_PATH) {
    50     return false;
    51   }
    53   // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
    54   // so we have to use wcscpy because wcscpy_s writes non-NULLs
    55   // into the rest of the buffer.
    56   wchar_t double_terminated_path_from[MAX_PATH + 1] = {0};
    57   wchar_t double_terminated_path_to[MAX_PATH + 1] = {0};
    58 #pragma warning(suppress:4996)  // don't complain about wcscpy deprecation
    59   wcscpy(double_terminated_path_from, stripped_from.value().c_str());
    60 #pragma warning(suppress:4996)  // don't complain about wcscpy deprecation
    61   wcscpy(double_terminated_path_to, stripped_to.value().c_str());
    63   SHFILEOPSTRUCT file_operation = {0};
    64   file_operation.wFunc = FO_COPY;
    65   file_operation.pFrom = double_terminated_path_from;
    66   file_operation.pTo = double_terminated_path_to;
    67   file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION |
    68                           FOF_NOCONFIRMMKDIR;
    69   if (!recursive)
    70     file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
    72   return (SHFileOperation(&file_operation) == 0);
    73 }
    75 }  // namespace
    77 FilePath MakeAbsoluteFilePath(const FilePath& input) {
    78   ThreadRestrictions::AssertIOAllowed();
    79   wchar_t file_path[MAX_PATH];
    80   if (!_wfullpath(file_path, input.value().c_str(), MAX_PATH))
    81     return FilePath();
    82   return FilePath(file_path);
    83 }
    85 bool DeleteFile(const FilePath& path, bool recursive) {
    86   ThreadRestrictions::AssertIOAllowed();
    88   if (path.value().length() >= MAX_PATH)
    89     return false;
    91   if (!recursive) {
    92     // If not recursing, then first check to see if |path| is a directory.
    93     // If it is, then remove it with RemoveDirectory.
    94     PlatformFileInfo file_info;
    95     if (file_util::GetFileInfo(path, &file_info) && file_info.is_directory)
    96       return RemoveDirectory(path.value().c_str()) != 0;
    98     // Otherwise, it's a file, wildcard or non-existant. Try DeleteFile first
    99     // because it should be faster. If DeleteFile fails, then we fall through
   100     // to SHFileOperation, which will do the right thing.
   101     if (::DeleteFile(path.value().c_str()) != 0)
   102       return true;
   103   }
   105   // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
   106   // so we have to use wcscpy because wcscpy_s writes non-NULLs
   107   // into the rest of the buffer.
   108   wchar_t double_terminated_path[MAX_PATH + 1] = {0};
   109 #pragma warning(suppress:4996)  // don't complain about wcscpy deprecation
   110   if (g_bug108724_debug)
   111     LOG(WARNING) << "copying ";
   112   wcscpy(double_terminated_path, path.value().c_str());
   114   SHFILEOPSTRUCT file_operation = {0};
   115   file_operation.wFunc = FO_DELETE;
   116   file_operation.pFrom = double_terminated_path;
   117   file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION;
   118   if (!recursive)
   119     file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
   120   if (g_bug108724_debug)
   121     LOG(WARNING) << "Performing shell operation";
   122   int err = SHFileOperation(&file_operation);
   123   if (g_bug108724_debug)
   124     LOG(WARNING) << "Done: " << err;
   126   // Since we're passing flags to the operation telling it to be silent,
   127   // it's possible for the operation to be aborted/cancelled without err
   128   // being set (although MSDN doesn't give any scenarios for how this can
   129   // happen).  See MSDN for SHFileOperation and SHFILEOPTSTRUCT.
   130   if (file_operation.fAnyOperationsAborted)
   131     return false;
   133   // Some versions of Windows return ERROR_FILE_NOT_FOUND (0x2) when deleting
   134   // an empty directory and some return 0x402 when they should be returning
   135   // ERROR_FILE_NOT_FOUND. MSDN says Vista and up won't return 0x402.
   136   return (err == 0 || err == ERROR_FILE_NOT_FOUND || err == 0x402);
   137 }
   139 bool DeleteFileAfterReboot(const FilePath& path) {
   140   ThreadRestrictions::AssertIOAllowed();
   142   if (path.value().length() >= MAX_PATH)
   143     return false;
   145   return MoveFileEx(path.value().c_str(), NULL,
   146                     MOVEFILE_DELAY_UNTIL_REBOOT |
   147                         MOVEFILE_REPLACE_EXISTING) != FALSE;
   148 }
   150 bool ReplaceFile(const FilePath& from_path,
   151                  const FilePath& to_path,
   152                  PlatformFileError* error) {
   153   ThreadRestrictions::AssertIOAllowed();
   154   // Try a simple move first.  It will only succeed when |to_path| doesn't
   155   // already exist.
   156   if (::MoveFile(from_path.value().c_str(), to_path.value().c_str()))
   157     return true;
   158   // Try the full-blown replace if the move fails, as ReplaceFile will only
   159   // succeed when |to_path| does exist. When writing to a network share, we may
   160   // not be able to change the ACLs. Ignore ACL errors then
   161   // (REPLACEFILE_IGNORE_MERGE_ERRORS).
   162   if (::ReplaceFile(to_path.value().c_str(), from_path.value().c_str(), NULL,
   163                     REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) {
   164     return true;
   165   }
   166   if (error)
   167     *error = LastErrorToPlatformFileError(GetLastError());
   168   return false;
   169 }
   171 bool CopyDirectory(const FilePath& from_path, const FilePath& to_path,
   172                    bool recursive) {
   173   ThreadRestrictions::AssertIOAllowed();
   175   if (recursive)
   176     return ShellCopy(from_path, to_path, true);
   178   // The following code assumes that from path is a directory.
   179   DCHECK(DirectoryExists(from_path));
   181   // Instead of creating a new directory, we copy the old one to include the
   182   // security information of the folder as part of the copy.
   183   if (!PathExists(to_path)) {
   184     // Except that Vista fails to do that, and instead do a recursive copy if
   185     // the target directory doesn't exist.
   186     if (base::win::GetVersion() >= base::win::VERSION_VISTA)
   187       file_util::CreateDirectory(to_path);
   188     else
   189       ShellCopy(from_path, to_path, false);
   190   }
   192   FilePath directory = from_path.Append(L"*.*");
   193   return ShellCopy(directory, to_path, false);
   194 }
   196 bool PathExists(const FilePath& path) {
   197   ThreadRestrictions::AssertIOAllowed();
   198   return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES);
   199 }
   201 bool PathIsWritable(const FilePath& path) {
   202   ThreadRestrictions::AssertIOAllowed();
   203   HANDLE dir =
   204       CreateFile(path.value().c_str(), FILE_ADD_FILE, kFileShareAll,
   205                  NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
   207   if (dir == INVALID_HANDLE_VALUE)
   208     return false;
   210   CloseHandle(dir);
   211   return true;
   212 }
   214 bool DirectoryExists(const FilePath& path) {
   215   ThreadRestrictions::AssertIOAllowed();
   216   DWORD fileattr = GetFileAttributes(path.value().c_str());
   217   if (fileattr != INVALID_FILE_ATTRIBUTES)
   218     return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
   219   return false;
   220 }
   222 }  // namespace base
   224 // -----------------------------------------------------------------------------
   226 namespace file_util {
   228 using base::DirectoryExists;
   229 using base::FilePath;
   230 using base::kFileShareAll;
   232 bool GetTempDir(FilePath* path) {
   233   base::ThreadRestrictions::AssertIOAllowed();
   235   wchar_t temp_path[MAX_PATH + 1];
   236   DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
   237   if (path_len >= MAX_PATH || path_len <= 0)
   238     return false;
   239   // TODO(evanm): the old behavior of this function was to always strip the
   240   // trailing slash.  We duplicate this here, but it shouldn't be necessary
   241   // when everyone is using the appropriate FilePath APIs.
   242   *path = FilePath(temp_path).StripTrailingSeparators();
   243   return true;
   244 }
   246 bool GetShmemTempDir(FilePath* path, bool executable) {
   247   return GetTempDir(path);
   248 }
   250 bool CreateTemporaryFile(FilePath* path) {
   251   base::ThreadRestrictions::AssertIOAllowed();
   253   FilePath temp_file;
   255   if (!GetTempDir(path))
   256     return false;
   258   if (CreateTemporaryFileInDir(*path, &temp_file)) {
   259     *path = temp_file;
   260     return true;
   261   }
   263   return false;
   264 }
   266 FILE* CreateAndOpenTemporaryShmemFile(FilePath* path, bool executable) {
   267   base::ThreadRestrictions::AssertIOAllowed();
   268   return CreateAndOpenTemporaryFile(path);
   269 }
   271 // On POSIX we have semantics to create and open a temporary file
   272 // atomically.
   273 // TODO(jrg): is there equivalent call to use on Windows instead of
   274 // going 2-step?
   275 FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
   276   base::ThreadRestrictions::AssertIOAllowed();
   277   if (!CreateTemporaryFileInDir(dir, path)) {
   278     return NULL;
   279   }
   280   // Open file in binary mode, to avoid problems with fwrite. On Windows
   281   // it replaces \n's with \r\n's, which may surprise you.
   282   // Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx
   283   return OpenFile(*path, "wb+");
   284 }
   286 bool CreateTemporaryFileInDir(const FilePath& dir,
   287                               FilePath* temp_file) {
   288   base::ThreadRestrictions::AssertIOAllowed();
   290   wchar_t temp_name[MAX_PATH + 1];
   292   if (!GetTempFileName(dir.value().c_str(), L"", 0, temp_name)) {
   293     DPLOG(WARNING) << "Failed to get temporary file name in " << dir.value();
   294     return false;
   295   }
   297   wchar_t long_temp_name[MAX_PATH + 1];
   298   DWORD long_name_len = GetLongPathName(temp_name, long_temp_name, MAX_PATH);
   299   if (long_name_len > MAX_PATH || long_name_len == 0) {
   300     // GetLongPathName() failed, but we still have a temporary file.
   301     *temp_file = FilePath(temp_name);
   302     return true;
   303   }
   305   FilePath::StringType long_temp_name_str;
   306   long_temp_name_str.assign(long_temp_name, long_name_len);
   307   *temp_file = FilePath(long_temp_name_str);
   308   return true;
   309 }
   311 bool CreateTemporaryDirInDir(const FilePath& base_dir,
   312                              const FilePath::StringType& prefix,
   313                              FilePath* new_dir) {
   314   base::ThreadRestrictions::AssertIOAllowed();
   316   FilePath path_to_create;
   318   for (int count = 0; count < 50; ++count) {
   319     // Try create a new temporary directory with random generated name. If
   320     // the one exists, keep trying another path name until we reach some limit.
   321     string16 new_dir_name;
   322     new_dir_name.assign(prefix);
   323     new_dir_name.append(base::IntToString16(::base::GetCurrentProcId()));
   324     new_dir_name.push_back('_');
   325     new_dir_name.append(base::IntToString16(base::RandInt(0, kint16max)));
   327     path_to_create = base_dir.Append(new_dir_name);
   328     if (::CreateDirectory(path_to_create.value().c_str(), NULL)) {
   329       *new_dir = path_to_create;
   330       return true;
   331     }
   332   }
   334   return false;
   335 }
   337 bool CreateNewTempDirectory(const FilePath::StringType& prefix,
   338                             FilePath* new_temp_path) {
   339   base::ThreadRestrictions::AssertIOAllowed();
   341   FilePath system_temp_dir;
   342   if (!GetTempDir(&system_temp_dir))
   343     return false;
   345   return CreateTemporaryDirInDir(system_temp_dir, prefix, new_temp_path);
   346 }
   348 bool CreateDirectoryAndGetError(const FilePath& full_path,
   349                                 base::PlatformFileError* error) {
   350   base::ThreadRestrictions::AssertIOAllowed();
   352   // If the path exists, we've succeeded if it's a directory, failed otherwise.
   353   const wchar_t* full_path_str = full_path.value().c_str();
   354   DWORD fileattr = ::GetFileAttributes(full_path_str);
   355   if (fileattr != INVALID_FILE_ATTRIBUTES) {
   356     if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
   357       DVLOG(1) << "CreateDirectory(" << full_path_str << "), "
   358                << "directory already exists.";
   359       return true;
   360     }
   361     DLOG(WARNING) << "CreateDirectory(" << full_path_str << "), "
   362                   << "conflicts with existing file.";
   363     if (error) {
   364       *error = base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
   365     }
   366     return false;
   367   }
   369   // Invariant:  Path does not exist as file or directory.
   371   // Attempt to create the parent recursively.  This will immediately return
   372   // true if it already exists, otherwise will create all required parent
   373   // directories starting with the highest-level missing parent.
   374   FilePath parent_path(full_path.DirName());
   375   if (parent_path.value() == full_path.value()) {
   376     if (error) {
   377       *error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
   378     }
   379     return false;
   380   }
   381   if (!CreateDirectoryAndGetError(parent_path, error)) {
   382     DLOG(WARNING) << "Failed to create one of the parent directories.";
   383     if (error) {
   384       DCHECK(*error != base::PLATFORM_FILE_OK);
   385     }
   386     return false;
   387   }
   389   if (!::CreateDirectory(full_path_str, NULL)) {
   390     DWORD error_code = ::GetLastError();
   391     if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path)) {
   392       // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we
   393       // were racing with someone creating the same directory, or a file
   394       // with the same path.  If DirectoryExists() returns true, we lost the
   395       // race to create the same directory.
   396       return true;
   397     } else {
   398       if (error)
   399         *error = base::LastErrorToPlatformFileError(error_code);
   400       DLOG(WARNING) << "Failed to create directory " << full_path_str
   401                     << ", last error is " << error_code << ".";
   402       return false;
   403     }
   404   } else {
   405     return true;
   406   }
   407 }
   409 // TODO(rkc): Work out if we want to handle NTFS junctions here or not, handle
   410 // them if we do decide to.
   411 bool IsLink(const FilePath& file_path) {
   412   return false;
   413 }
   415 bool GetFileInfo(const FilePath& file_path, base::PlatformFileInfo* results) {
   416   base::ThreadRestrictions::AssertIOAllowed();
   418   WIN32_FILE_ATTRIBUTE_DATA attr;
   419   if (!GetFileAttributesEx(file_path.value().c_str(),
   420                            GetFileExInfoStandard, &attr)) {
   421     return false;
   422   }
   424   ULARGE_INTEGER size;
   425   size.HighPart = attr.nFileSizeHigh;
   426   size.LowPart = attr.nFileSizeLow;
   427   results->size = size.QuadPart;
   429   results->is_directory =
   430       (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
   431   results->last_modified = base::Time::FromFileTime(attr.ftLastWriteTime);
   432   results->last_accessed = base::Time::FromFileTime(attr.ftLastAccessTime);
   433   results->creation_time = base::Time::FromFileTime(attr.ftCreationTime);
   435   return true;
   436 }
   438 FILE* OpenFile(const FilePath& filename, const char* mode) {
   439   base::ThreadRestrictions::AssertIOAllowed();
   440   std::wstring w_mode = ASCIIToWide(std::string(mode));
   441   return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO);
   442 }
   444 FILE* OpenFile(const std::string& filename, const char* mode) {
   445   base::ThreadRestrictions::AssertIOAllowed();
   446   return _fsopen(filename.c_str(), mode, _SH_DENYNO);
   447 }
   449 int ReadFile(const FilePath& filename, char* data, int size) {
   450   base::ThreadRestrictions::AssertIOAllowed();
   451   base::win::ScopedHandle file(CreateFile(filename.value().c_str(),
   452                                           GENERIC_READ,
   453                                           FILE_SHARE_READ | FILE_SHARE_WRITE,
   454                                           NULL,
   455                                           OPEN_EXISTING,
   456                                           FILE_FLAG_SEQUENTIAL_SCAN,
   457                                           NULL));
   458   if (!file)
   459     return -1;
   461   DWORD read;
   462   if (::ReadFile(file, data, size, &read, NULL) &&
   463       static_cast<int>(read) == size)
   464     return read;
   465   return -1;
   466 }
   468 int WriteFile(const FilePath& filename, const char* data, int size) {
   469   base::ThreadRestrictions::AssertIOAllowed();
   470   base::win::ScopedHandle file(CreateFile(filename.value().c_str(),
   471                                           GENERIC_WRITE,
   472                                           0,
   473                                           NULL,
   474                                           CREATE_ALWAYS,
   475                                           0,
   476                                           NULL));
   477   if (!file) {
   478     DLOG_GETLASTERROR(WARNING) << "CreateFile failed for path "
   479                                << filename.value();
   480     return -1;
   481   }
   483   DWORD written;
   484   BOOL result = ::WriteFile(file, data, size, &written, NULL);
   485   if (result && static_cast<int>(written) == size)
   486     return written;
   488   if (!result) {
   489     // WriteFile failed.
   490     DLOG_GETLASTERROR(WARNING) << "writing file " << filename.value()
   491                                << " failed";
   492   } else {
   493     // Didn't write all the bytes.
   494     DLOG(WARNING) << "wrote" << written << " bytes to "
   495                   << filename.value() << " expected " << size;
   496   }
   497   return -1;
   498 }
   500 int AppendToFile(const FilePath& filename, const char* data, int size) {
   501   base::ThreadRestrictions::AssertIOAllowed();
   502   base::win::ScopedHandle file(CreateFile(filename.value().c_str(),
   503                                           FILE_APPEND_DATA,
   504                                           0,
   505                                           NULL,
   506                                           OPEN_EXISTING,
   507                                           0,
   508                                           NULL));
   509   if (!file) {
   510     DLOG_GETLASTERROR(WARNING) << "CreateFile failed for path "
   511                                << filename.value();
   512     return -1;
   513   }
   515   DWORD written;
   516   BOOL result = ::WriteFile(file, data, size, &written, NULL);
   517   if (result && static_cast<int>(written) == size)
   518     return written;
   520   if (!result) {
   521     // WriteFile failed.
   522     DLOG_GETLASTERROR(WARNING) << "writing file " << filename.value()
   523                                << " failed";
   524   } else {
   525     // Didn't write all the bytes.
   526     DLOG(WARNING) << "wrote" << written << " bytes to "
   527                   << filename.value() << " expected " << size;
   528   }
   529   return -1;
   530 }
   532 // Gets the current working directory for the process.
   533 bool GetCurrentDirectory(FilePath* dir) {
   534   base::ThreadRestrictions::AssertIOAllowed();
   536   wchar_t system_buffer[MAX_PATH];
   537   system_buffer[0] = 0;
   538   DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer);
   539   if (len == 0 || len > MAX_PATH)
   540     return false;
   541   // TODO(evanm): the old behavior of this function was to always strip the
   542   // trailing slash.  We duplicate this here, but it shouldn't be necessary
   543   // when everyone is using the appropriate FilePath APIs.
   544   std::wstring dir_str(system_buffer);
   545   *dir = FilePath(dir_str).StripTrailingSeparators();
   546   return true;
   547 }
   549 // Sets the current working directory for the process.
   550 bool SetCurrentDirectory(const FilePath& directory) {
   551   base::ThreadRestrictions::AssertIOAllowed();
   552   BOOL ret = ::SetCurrentDirectory(directory.value().c_str());
   553   return ret != 0;
   554 }
   556 bool NormalizeFilePath(const FilePath& path, FilePath* real_path) {
   557   base::ThreadRestrictions::AssertIOAllowed();
   558   FilePath mapped_file;
   559   if (!NormalizeToNativeFilePath(path, &mapped_file))
   560     return false;
   561   // NormalizeToNativeFilePath() will return a path that starts with
   562   // "\Device\Harddisk...".  Helper DevicePathToDriveLetterPath()
   563   // will find a drive letter which maps to the path's device, so
   564   // that we return a path starting with a drive letter.
   565   return DevicePathToDriveLetterPath(mapped_file, real_path);
   566 }
   568 bool DevicePathToDriveLetterPath(const FilePath& nt_device_path,
   569                                  FilePath* out_drive_letter_path) {
   570   base::ThreadRestrictions::AssertIOAllowed();
   572   // Get the mapping of drive letters to device paths.
   573   const int kDriveMappingSize = 1024;
   574   wchar_t drive_mapping[kDriveMappingSize] = {'\0'};
   575   if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, drive_mapping)) {
   576     DLOG(ERROR) << "Failed to get drive mapping.";
   577     return false;
   578   }
   580   // The drive mapping is a sequence of null terminated strings.
   581   // The last string is empty.
   582   wchar_t* drive_map_ptr = drive_mapping;
   583   wchar_t device_path_as_string[MAX_PATH];
   584   wchar_t drive[] = L" :";
   586   // For each string in the drive mapping, get the junction that links
   587   // to it.  If that junction is a prefix of |device_path|, then we
   588   // know that |drive| is the real path prefix.
   589   while (*drive_map_ptr) {
   590     drive[0] = drive_map_ptr[0];  // Copy the drive letter.
   592     if (QueryDosDevice(drive, device_path_as_string, MAX_PATH)) {
   593       FilePath device_path(device_path_as_string);
   594       if (device_path == nt_device_path ||
   595           device_path.IsParent(nt_device_path)) {
   596         *out_drive_letter_path = FilePath(drive +
   597             nt_device_path.value().substr(wcslen(device_path_as_string)));
   598         return true;
   599       }
   600     }
   601     // Move to the next drive letter string, which starts one
   602     // increment after the '\0' that terminates the current string.
   603     while (*drive_map_ptr++);
   604   }
   606   // No drive matched.  The path does not start with a device junction
   607   // that is mounted as a drive letter.  This means there is no drive
   608   // letter path to the volume that holds |device_path|, so fail.
   609   return false;
   610 }
   612 bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) {
   613   base::ThreadRestrictions::AssertIOAllowed();
   614   // In Vista, GetFinalPathNameByHandle() would give us the real path
   615   // from a file handle.  If we ever deprecate XP, consider changing the
   616   // code below to a call to GetFinalPathNameByHandle().  The method this
   617   // function uses is explained in the following msdn article:
   618   // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
   619   base::win::ScopedHandle file_handle(
   620       ::CreateFile(path.value().c_str(),
   621                    GENERIC_READ,
   622                    kFileShareAll,
   623                    NULL,
   624                    OPEN_EXISTING,
   625                    FILE_ATTRIBUTE_NORMAL,
   626                    NULL));
   627   if (!file_handle)
   628     return false;
   630   // Create a file mapping object.  Can't easily use MemoryMappedFile, because
   631   // we only map the first byte, and need direct access to the handle. You can
   632   // not map an empty file, this call fails in that case.
   633   base::win::ScopedHandle file_map_handle(
   634       ::CreateFileMapping(file_handle.Get(),
   635                           NULL,
   636                           PAGE_READONLY,
   637                           0,
   638                           1,  // Just one byte.  No need to look at the data.
   639                           NULL));
   640   if (!file_map_handle)
   641     return false;
   643   // Use a view of the file to get the path to the file.
   644   void* file_view = MapViewOfFile(file_map_handle.Get(),
   645                                   FILE_MAP_READ, 0, 0, 1);
   646   if (!file_view)
   647     return false;
   649   // The expansion of |path| into a full path may make it longer.
   650   // GetMappedFileName() will fail if the result is longer than MAX_PATH.
   651   // Pad a bit to be safe.  If kMaxPathLength is ever changed to be less
   652   // than MAX_PATH, it would be nessisary to test that GetMappedFileName()
   653   // not return kMaxPathLength.  This would mean that only part of the
   654   // path fit in |mapped_file_path|.
   655   const int kMaxPathLength = MAX_PATH + 10;
   656   wchar_t mapped_file_path[kMaxPathLength];
   657   bool success = false;
   658   HANDLE cp = GetCurrentProcess();
   659   if (::GetMappedFileNameW(cp, file_view, mapped_file_path, kMaxPathLength)) {
   660     *nt_path = FilePath(mapped_file_path);
   661     success = true;
   662   }
   663   ::UnmapViewOfFile(file_view);
   664   return success;
   665 }
   667 int GetMaximumPathComponentLength(const FilePath& path) {
   668   base::ThreadRestrictions::AssertIOAllowed();
   670   wchar_t volume_path[MAX_PATH];
   671   if (!GetVolumePathNameW(path.NormalizePathSeparators().value().c_str(),
   672                           volume_path,
   673                           arraysize(volume_path))) {
   674     return -1;
   675   }
   677   DWORD max_length = 0;
   678   if (!GetVolumeInformationW(volume_path, NULL, 0, NULL, &max_length, NULL,
   679                              NULL, 0)) {
   680     return -1;
   681   }
   683   // Length of |path| with path separator appended.
   684   size_t prefix = path.StripTrailingSeparators().value().size() + 1;
   685   // The whole path string must be shorter than MAX_PATH. That is, it must be
   686   // prefix + component_length < MAX_PATH (or equivalently, <= MAX_PATH - 1).
   687   int whole_path_limit = std::max(0, MAX_PATH - 1 - static_cast<int>(prefix));
   688   return std::min(whole_path_limit, static_cast<int>(max_length));
   689 }
   691 }  // namespace file_util
   693 namespace base {
   694 namespace internal {
   696 bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) {
   697   ThreadRestrictions::AssertIOAllowed();
   699   // NOTE: I suspect we could support longer paths, but that would involve
   700   // analyzing all our usage of files.
   701   if (from_path.value().length() >= MAX_PATH ||
   702       to_path.value().length() >= MAX_PATH) {
   703     return false;
   704   }
   705   if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(),
   706                  MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0)
   707     return true;
   709   // Keep the last error value from MoveFileEx around in case the below
   710   // fails.
   711   bool ret = false;
   712   DWORD last_error = ::GetLastError();
   714   if (DirectoryExists(from_path)) {
   715     // MoveFileEx fails if moving directory across volumes. We will simulate
   716     // the move by using Copy and Delete. Ideally we could check whether
   717     // from_path and to_path are indeed in different volumes.
   718     ret = internal::CopyAndDeleteDirectory(from_path, to_path);
   719   }
   721   if (!ret) {
   722     // Leave a clue about what went wrong so that it can be (at least) picked
   723     // up by a PLOG entry.
   724     ::SetLastError(last_error);
   725   }
   727   return ret;
   728 }
   730 bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) {
   731   ThreadRestrictions::AssertIOAllowed();
   733   // NOTE: I suspect we could support longer paths, but that would involve
   734   // analyzing all our usage of files.
   735   if (from_path.value().length() >= MAX_PATH ||
   736       to_path.value().length() >= MAX_PATH) {
   737     return false;
   738   }
   739   return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(),
   740                      false) != 0);
   741 }
   743 bool CopyAndDeleteDirectory(const FilePath& from_path,
   744                             const FilePath& to_path) {
   745   ThreadRestrictions::AssertIOAllowed();
   746   if (CopyDirectory(from_path, to_path, true)) {
   747     if (DeleteFile(from_path, true))
   748       return true;
   750     // Like Move, this function is not transactional, so we just
   751     // leave the copied bits behind if deleting from_path fails.
   752     // If to_path exists previously then we have already overwritten
   753     // it by now, we don't get better off by deleting the new bits.
   754   }
   755   return false;
   756 }
   758 }  // namespace internal
   759 }  // namespace base

mercurial