1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/sandbox/chromium/base/file_util_win.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,759 @@ 1.4 +// Copyright (c) 2012 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#include "base/file_util.h" 1.9 + 1.10 +#include <windows.h> 1.11 +#include <psapi.h> 1.12 +#include <shellapi.h> 1.13 +#include <shlobj.h> 1.14 +#include <time.h> 1.15 + 1.16 +#include <algorithm> 1.17 +#include <limits> 1.18 +#include <string> 1.19 + 1.20 +#include "base/files/file_path.h" 1.21 +#include "base/logging.h" 1.22 +#include "base/metrics/histogram.h" 1.23 +#include "base/process/process_handle.h" 1.24 +#include "base/rand_util.h" 1.25 +#include "base/strings/string_number_conversions.h" 1.26 +#include "base/strings/string_util.h" 1.27 +#include "base/strings/utf_string_conversions.h" 1.28 +#include "base/threading/thread_restrictions.h" 1.29 +#include "base/time/time.h" 1.30 +#include "base/win/scoped_handle.h" 1.31 +#include "base/win/windows_version.h" 1.32 + 1.33 +namespace base { 1.34 + 1.35 +namespace { 1.36 + 1.37 +const DWORD kFileShareAll = 1.38 + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; 1.39 + 1.40 +bool ShellCopy(const FilePath& from_path, 1.41 + const FilePath& to_path, 1.42 + bool recursive) { 1.43 + // WinXP SHFileOperation doesn't like trailing separators. 1.44 + FilePath stripped_from = from_path.StripTrailingSeparators(); 1.45 + FilePath stripped_to = to_path.StripTrailingSeparators(); 1.46 + 1.47 + ThreadRestrictions::AssertIOAllowed(); 1.48 + 1.49 + // NOTE: I suspect we could support longer paths, but that would involve 1.50 + // analyzing all our usage of files. 1.51 + if (stripped_from.value().length() >= MAX_PATH || 1.52 + stripped_to.value().length() >= MAX_PATH) { 1.53 + return false; 1.54 + } 1.55 + 1.56 + // SHFILEOPSTRUCT wants the path to be terminated with two NULLs, 1.57 + // so we have to use wcscpy because wcscpy_s writes non-NULLs 1.58 + // into the rest of the buffer. 1.59 + wchar_t double_terminated_path_from[MAX_PATH + 1] = {0}; 1.60 + wchar_t double_terminated_path_to[MAX_PATH + 1] = {0}; 1.61 +#pragma warning(suppress:4996) // don't complain about wcscpy deprecation 1.62 + wcscpy(double_terminated_path_from, stripped_from.value().c_str()); 1.63 +#pragma warning(suppress:4996) // don't complain about wcscpy deprecation 1.64 + wcscpy(double_terminated_path_to, stripped_to.value().c_str()); 1.65 + 1.66 + SHFILEOPSTRUCT file_operation = {0}; 1.67 + file_operation.wFunc = FO_COPY; 1.68 + file_operation.pFrom = double_terminated_path_from; 1.69 + file_operation.pTo = double_terminated_path_to; 1.70 + file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION | 1.71 + FOF_NOCONFIRMMKDIR; 1.72 + if (!recursive) 1.73 + file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY; 1.74 + 1.75 + return (SHFileOperation(&file_operation) == 0); 1.76 +} 1.77 + 1.78 +} // namespace 1.79 + 1.80 +FilePath MakeAbsoluteFilePath(const FilePath& input) { 1.81 + ThreadRestrictions::AssertIOAllowed(); 1.82 + wchar_t file_path[MAX_PATH]; 1.83 + if (!_wfullpath(file_path, input.value().c_str(), MAX_PATH)) 1.84 + return FilePath(); 1.85 + return FilePath(file_path); 1.86 +} 1.87 + 1.88 +bool DeleteFile(const FilePath& path, bool recursive) { 1.89 + ThreadRestrictions::AssertIOAllowed(); 1.90 + 1.91 + if (path.value().length() >= MAX_PATH) 1.92 + return false; 1.93 + 1.94 + if (!recursive) { 1.95 + // If not recursing, then first check to see if |path| is a directory. 1.96 + // If it is, then remove it with RemoveDirectory. 1.97 + PlatformFileInfo file_info; 1.98 + if (file_util::GetFileInfo(path, &file_info) && file_info.is_directory) 1.99 + return RemoveDirectory(path.value().c_str()) != 0; 1.100 + 1.101 + // Otherwise, it's a file, wildcard or non-existant. Try DeleteFile first 1.102 + // because it should be faster. If DeleteFile fails, then we fall through 1.103 + // to SHFileOperation, which will do the right thing. 1.104 + if (::DeleteFile(path.value().c_str()) != 0) 1.105 + return true; 1.106 + } 1.107 + 1.108 + // SHFILEOPSTRUCT wants the path to be terminated with two NULLs, 1.109 + // so we have to use wcscpy because wcscpy_s writes non-NULLs 1.110 + // into the rest of the buffer. 1.111 + wchar_t double_terminated_path[MAX_PATH + 1] = {0}; 1.112 +#pragma warning(suppress:4996) // don't complain about wcscpy deprecation 1.113 + if (g_bug108724_debug) 1.114 + LOG(WARNING) << "copying "; 1.115 + wcscpy(double_terminated_path, path.value().c_str()); 1.116 + 1.117 + SHFILEOPSTRUCT file_operation = {0}; 1.118 + file_operation.wFunc = FO_DELETE; 1.119 + file_operation.pFrom = double_terminated_path; 1.120 + file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION; 1.121 + if (!recursive) 1.122 + file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY; 1.123 + if (g_bug108724_debug) 1.124 + LOG(WARNING) << "Performing shell operation"; 1.125 + int err = SHFileOperation(&file_operation); 1.126 + if (g_bug108724_debug) 1.127 + LOG(WARNING) << "Done: " << err; 1.128 + 1.129 + // Since we're passing flags to the operation telling it to be silent, 1.130 + // it's possible for the operation to be aborted/cancelled without err 1.131 + // being set (although MSDN doesn't give any scenarios for how this can 1.132 + // happen). See MSDN for SHFileOperation and SHFILEOPTSTRUCT. 1.133 + if (file_operation.fAnyOperationsAborted) 1.134 + return false; 1.135 + 1.136 + // Some versions of Windows return ERROR_FILE_NOT_FOUND (0x2) when deleting 1.137 + // an empty directory and some return 0x402 when they should be returning 1.138 + // ERROR_FILE_NOT_FOUND. MSDN says Vista and up won't return 0x402. 1.139 + return (err == 0 || err == ERROR_FILE_NOT_FOUND || err == 0x402); 1.140 +} 1.141 + 1.142 +bool DeleteFileAfterReboot(const FilePath& path) { 1.143 + ThreadRestrictions::AssertIOAllowed(); 1.144 + 1.145 + if (path.value().length() >= MAX_PATH) 1.146 + return false; 1.147 + 1.148 + return MoveFileEx(path.value().c_str(), NULL, 1.149 + MOVEFILE_DELAY_UNTIL_REBOOT | 1.150 + MOVEFILE_REPLACE_EXISTING) != FALSE; 1.151 +} 1.152 + 1.153 +bool ReplaceFile(const FilePath& from_path, 1.154 + const FilePath& to_path, 1.155 + PlatformFileError* error) { 1.156 + ThreadRestrictions::AssertIOAllowed(); 1.157 + // Try a simple move first. It will only succeed when |to_path| doesn't 1.158 + // already exist. 1.159 + if (::MoveFile(from_path.value().c_str(), to_path.value().c_str())) 1.160 + return true; 1.161 + // Try the full-blown replace if the move fails, as ReplaceFile will only 1.162 + // succeed when |to_path| does exist. When writing to a network share, we may 1.163 + // not be able to change the ACLs. Ignore ACL errors then 1.164 + // (REPLACEFILE_IGNORE_MERGE_ERRORS). 1.165 + if (::ReplaceFile(to_path.value().c_str(), from_path.value().c_str(), NULL, 1.166 + REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) { 1.167 + return true; 1.168 + } 1.169 + if (error) 1.170 + *error = LastErrorToPlatformFileError(GetLastError()); 1.171 + return false; 1.172 +} 1.173 + 1.174 +bool CopyDirectory(const FilePath& from_path, const FilePath& to_path, 1.175 + bool recursive) { 1.176 + ThreadRestrictions::AssertIOAllowed(); 1.177 + 1.178 + if (recursive) 1.179 + return ShellCopy(from_path, to_path, true); 1.180 + 1.181 + // The following code assumes that from path is a directory. 1.182 + DCHECK(DirectoryExists(from_path)); 1.183 + 1.184 + // Instead of creating a new directory, we copy the old one to include the 1.185 + // security information of the folder as part of the copy. 1.186 + if (!PathExists(to_path)) { 1.187 + // Except that Vista fails to do that, and instead do a recursive copy if 1.188 + // the target directory doesn't exist. 1.189 + if (base::win::GetVersion() >= base::win::VERSION_VISTA) 1.190 + file_util::CreateDirectory(to_path); 1.191 + else 1.192 + ShellCopy(from_path, to_path, false); 1.193 + } 1.194 + 1.195 + FilePath directory = from_path.Append(L"*.*"); 1.196 + return ShellCopy(directory, to_path, false); 1.197 +} 1.198 + 1.199 +bool PathExists(const FilePath& path) { 1.200 + ThreadRestrictions::AssertIOAllowed(); 1.201 + return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES); 1.202 +} 1.203 + 1.204 +bool PathIsWritable(const FilePath& path) { 1.205 + ThreadRestrictions::AssertIOAllowed(); 1.206 + HANDLE dir = 1.207 + CreateFile(path.value().c_str(), FILE_ADD_FILE, kFileShareAll, 1.208 + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 1.209 + 1.210 + if (dir == INVALID_HANDLE_VALUE) 1.211 + return false; 1.212 + 1.213 + CloseHandle(dir); 1.214 + return true; 1.215 +} 1.216 + 1.217 +bool DirectoryExists(const FilePath& path) { 1.218 + ThreadRestrictions::AssertIOAllowed(); 1.219 + DWORD fileattr = GetFileAttributes(path.value().c_str()); 1.220 + if (fileattr != INVALID_FILE_ATTRIBUTES) 1.221 + return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0; 1.222 + return false; 1.223 +} 1.224 + 1.225 +} // namespace base 1.226 + 1.227 +// ----------------------------------------------------------------------------- 1.228 + 1.229 +namespace file_util { 1.230 + 1.231 +using base::DirectoryExists; 1.232 +using base::FilePath; 1.233 +using base::kFileShareAll; 1.234 + 1.235 +bool GetTempDir(FilePath* path) { 1.236 + base::ThreadRestrictions::AssertIOAllowed(); 1.237 + 1.238 + wchar_t temp_path[MAX_PATH + 1]; 1.239 + DWORD path_len = ::GetTempPath(MAX_PATH, temp_path); 1.240 + if (path_len >= MAX_PATH || path_len <= 0) 1.241 + return false; 1.242 + // TODO(evanm): the old behavior of this function was to always strip the 1.243 + // trailing slash. We duplicate this here, but it shouldn't be necessary 1.244 + // when everyone is using the appropriate FilePath APIs. 1.245 + *path = FilePath(temp_path).StripTrailingSeparators(); 1.246 + return true; 1.247 +} 1.248 + 1.249 +bool GetShmemTempDir(FilePath* path, bool executable) { 1.250 + return GetTempDir(path); 1.251 +} 1.252 + 1.253 +bool CreateTemporaryFile(FilePath* path) { 1.254 + base::ThreadRestrictions::AssertIOAllowed(); 1.255 + 1.256 + FilePath temp_file; 1.257 + 1.258 + if (!GetTempDir(path)) 1.259 + return false; 1.260 + 1.261 + if (CreateTemporaryFileInDir(*path, &temp_file)) { 1.262 + *path = temp_file; 1.263 + return true; 1.264 + } 1.265 + 1.266 + return false; 1.267 +} 1.268 + 1.269 +FILE* CreateAndOpenTemporaryShmemFile(FilePath* path, bool executable) { 1.270 + base::ThreadRestrictions::AssertIOAllowed(); 1.271 + return CreateAndOpenTemporaryFile(path); 1.272 +} 1.273 + 1.274 +// On POSIX we have semantics to create and open a temporary file 1.275 +// atomically. 1.276 +// TODO(jrg): is there equivalent call to use on Windows instead of 1.277 +// going 2-step? 1.278 +FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { 1.279 + base::ThreadRestrictions::AssertIOAllowed(); 1.280 + if (!CreateTemporaryFileInDir(dir, path)) { 1.281 + return NULL; 1.282 + } 1.283 + // Open file in binary mode, to avoid problems with fwrite. On Windows 1.284 + // it replaces \n's with \r\n's, which may surprise you. 1.285 + // Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx 1.286 + return OpenFile(*path, "wb+"); 1.287 +} 1.288 + 1.289 +bool CreateTemporaryFileInDir(const FilePath& dir, 1.290 + FilePath* temp_file) { 1.291 + base::ThreadRestrictions::AssertIOAllowed(); 1.292 + 1.293 + wchar_t temp_name[MAX_PATH + 1]; 1.294 + 1.295 + if (!GetTempFileName(dir.value().c_str(), L"", 0, temp_name)) { 1.296 + DPLOG(WARNING) << "Failed to get temporary file name in " << dir.value(); 1.297 + return false; 1.298 + } 1.299 + 1.300 + wchar_t long_temp_name[MAX_PATH + 1]; 1.301 + DWORD long_name_len = GetLongPathName(temp_name, long_temp_name, MAX_PATH); 1.302 + if (long_name_len > MAX_PATH || long_name_len == 0) { 1.303 + // GetLongPathName() failed, but we still have a temporary file. 1.304 + *temp_file = FilePath(temp_name); 1.305 + return true; 1.306 + } 1.307 + 1.308 + FilePath::StringType long_temp_name_str; 1.309 + long_temp_name_str.assign(long_temp_name, long_name_len); 1.310 + *temp_file = FilePath(long_temp_name_str); 1.311 + return true; 1.312 +} 1.313 + 1.314 +bool CreateTemporaryDirInDir(const FilePath& base_dir, 1.315 + const FilePath::StringType& prefix, 1.316 + FilePath* new_dir) { 1.317 + base::ThreadRestrictions::AssertIOAllowed(); 1.318 + 1.319 + FilePath path_to_create; 1.320 + 1.321 + for (int count = 0; count < 50; ++count) { 1.322 + // Try create a new temporary directory with random generated name. If 1.323 + // the one exists, keep trying another path name until we reach some limit. 1.324 + string16 new_dir_name; 1.325 + new_dir_name.assign(prefix); 1.326 + new_dir_name.append(base::IntToString16(::base::GetCurrentProcId())); 1.327 + new_dir_name.push_back('_'); 1.328 + new_dir_name.append(base::IntToString16(base::RandInt(0, kint16max))); 1.329 + 1.330 + path_to_create = base_dir.Append(new_dir_name); 1.331 + if (::CreateDirectory(path_to_create.value().c_str(), NULL)) { 1.332 + *new_dir = path_to_create; 1.333 + return true; 1.334 + } 1.335 + } 1.336 + 1.337 + return false; 1.338 +} 1.339 + 1.340 +bool CreateNewTempDirectory(const FilePath::StringType& prefix, 1.341 + FilePath* new_temp_path) { 1.342 + base::ThreadRestrictions::AssertIOAllowed(); 1.343 + 1.344 + FilePath system_temp_dir; 1.345 + if (!GetTempDir(&system_temp_dir)) 1.346 + return false; 1.347 + 1.348 + return CreateTemporaryDirInDir(system_temp_dir, prefix, new_temp_path); 1.349 +} 1.350 + 1.351 +bool CreateDirectoryAndGetError(const FilePath& full_path, 1.352 + base::PlatformFileError* error) { 1.353 + base::ThreadRestrictions::AssertIOAllowed(); 1.354 + 1.355 + // If the path exists, we've succeeded if it's a directory, failed otherwise. 1.356 + const wchar_t* full_path_str = full_path.value().c_str(); 1.357 + DWORD fileattr = ::GetFileAttributes(full_path_str); 1.358 + if (fileattr != INVALID_FILE_ATTRIBUTES) { 1.359 + if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) { 1.360 + DVLOG(1) << "CreateDirectory(" << full_path_str << "), " 1.361 + << "directory already exists."; 1.362 + return true; 1.363 + } 1.364 + DLOG(WARNING) << "CreateDirectory(" << full_path_str << "), " 1.365 + << "conflicts with existing file."; 1.366 + if (error) { 1.367 + *error = base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; 1.368 + } 1.369 + return false; 1.370 + } 1.371 + 1.372 + // Invariant: Path does not exist as file or directory. 1.373 + 1.374 + // Attempt to create the parent recursively. This will immediately return 1.375 + // true if it already exists, otherwise will create all required parent 1.376 + // directories starting with the highest-level missing parent. 1.377 + FilePath parent_path(full_path.DirName()); 1.378 + if (parent_path.value() == full_path.value()) { 1.379 + if (error) { 1.380 + *error = base::PLATFORM_FILE_ERROR_NOT_FOUND; 1.381 + } 1.382 + return false; 1.383 + } 1.384 + if (!CreateDirectoryAndGetError(parent_path, error)) { 1.385 + DLOG(WARNING) << "Failed to create one of the parent directories."; 1.386 + if (error) { 1.387 + DCHECK(*error != base::PLATFORM_FILE_OK); 1.388 + } 1.389 + return false; 1.390 + } 1.391 + 1.392 + if (!::CreateDirectory(full_path_str, NULL)) { 1.393 + DWORD error_code = ::GetLastError(); 1.394 + if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path)) { 1.395 + // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we 1.396 + // were racing with someone creating the same directory, or a file 1.397 + // with the same path. If DirectoryExists() returns true, we lost the 1.398 + // race to create the same directory. 1.399 + return true; 1.400 + } else { 1.401 + if (error) 1.402 + *error = base::LastErrorToPlatformFileError(error_code); 1.403 + DLOG(WARNING) << "Failed to create directory " << full_path_str 1.404 + << ", last error is " << error_code << "."; 1.405 + return false; 1.406 + } 1.407 + } else { 1.408 + return true; 1.409 + } 1.410 +} 1.411 + 1.412 +// TODO(rkc): Work out if we want to handle NTFS junctions here or not, handle 1.413 +// them if we do decide to. 1.414 +bool IsLink(const FilePath& file_path) { 1.415 + return false; 1.416 +} 1.417 + 1.418 +bool GetFileInfo(const FilePath& file_path, base::PlatformFileInfo* results) { 1.419 + base::ThreadRestrictions::AssertIOAllowed(); 1.420 + 1.421 + WIN32_FILE_ATTRIBUTE_DATA attr; 1.422 + if (!GetFileAttributesEx(file_path.value().c_str(), 1.423 + GetFileExInfoStandard, &attr)) { 1.424 + return false; 1.425 + } 1.426 + 1.427 + ULARGE_INTEGER size; 1.428 + size.HighPart = attr.nFileSizeHigh; 1.429 + size.LowPart = attr.nFileSizeLow; 1.430 + results->size = size.QuadPart; 1.431 + 1.432 + results->is_directory = 1.433 + (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; 1.434 + results->last_modified = base::Time::FromFileTime(attr.ftLastWriteTime); 1.435 + results->last_accessed = base::Time::FromFileTime(attr.ftLastAccessTime); 1.436 + results->creation_time = base::Time::FromFileTime(attr.ftCreationTime); 1.437 + 1.438 + return true; 1.439 +} 1.440 + 1.441 +FILE* OpenFile(const FilePath& filename, const char* mode) { 1.442 + base::ThreadRestrictions::AssertIOAllowed(); 1.443 + std::wstring w_mode = ASCIIToWide(std::string(mode)); 1.444 + return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO); 1.445 +} 1.446 + 1.447 +FILE* OpenFile(const std::string& filename, const char* mode) { 1.448 + base::ThreadRestrictions::AssertIOAllowed(); 1.449 + return _fsopen(filename.c_str(), mode, _SH_DENYNO); 1.450 +} 1.451 + 1.452 +int ReadFile(const FilePath& filename, char* data, int size) { 1.453 + base::ThreadRestrictions::AssertIOAllowed(); 1.454 + base::win::ScopedHandle file(CreateFile(filename.value().c_str(), 1.455 + GENERIC_READ, 1.456 + FILE_SHARE_READ | FILE_SHARE_WRITE, 1.457 + NULL, 1.458 + OPEN_EXISTING, 1.459 + FILE_FLAG_SEQUENTIAL_SCAN, 1.460 + NULL)); 1.461 + if (!file) 1.462 + return -1; 1.463 + 1.464 + DWORD read; 1.465 + if (::ReadFile(file, data, size, &read, NULL) && 1.466 + static_cast<int>(read) == size) 1.467 + return read; 1.468 + return -1; 1.469 +} 1.470 + 1.471 +int WriteFile(const FilePath& filename, const char* data, int size) { 1.472 + base::ThreadRestrictions::AssertIOAllowed(); 1.473 + base::win::ScopedHandle file(CreateFile(filename.value().c_str(), 1.474 + GENERIC_WRITE, 1.475 + 0, 1.476 + NULL, 1.477 + CREATE_ALWAYS, 1.478 + 0, 1.479 + NULL)); 1.480 + if (!file) { 1.481 + DLOG_GETLASTERROR(WARNING) << "CreateFile failed for path " 1.482 + << filename.value(); 1.483 + return -1; 1.484 + } 1.485 + 1.486 + DWORD written; 1.487 + BOOL result = ::WriteFile(file, data, size, &written, NULL); 1.488 + if (result && static_cast<int>(written) == size) 1.489 + return written; 1.490 + 1.491 + if (!result) { 1.492 + // WriteFile failed. 1.493 + DLOG_GETLASTERROR(WARNING) << "writing file " << filename.value() 1.494 + << " failed"; 1.495 + } else { 1.496 + // Didn't write all the bytes. 1.497 + DLOG(WARNING) << "wrote" << written << " bytes to " 1.498 + << filename.value() << " expected " << size; 1.499 + } 1.500 + return -1; 1.501 +} 1.502 + 1.503 +int AppendToFile(const FilePath& filename, const char* data, int size) { 1.504 + base::ThreadRestrictions::AssertIOAllowed(); 1.505 + base::win::ScopedHandle file(CreateFile(filename.value().c_str(), 1.506 + FILE_APPEND_DATA, 1.507 + 0, 1.508 + NULL, 1.509 + OPEN_EXISTING, 1.510 + 0, 1.511 + NULL)); 1.512 + if (!file) { 1.513 + DLOG_GETLASTERROR(WARNING) << "CreateFile failed for path " 1.514 + << filename.value(); 1.515 + return -1; 1.516 + } 1.517 + 1.518 + DWORD written; 1.519 + BOOL result = ::WriteFile(file, data, size, &written, NULL); 1.520 + if (result && static_cast<int>(written) == size) 1.521 + return written; 1.522 + 1.523 + if (!result) { 1.524 + // WriteFile failed. 1.525 + DLOG_GETLASTERROR(WARNING) << "writing file " << filename.value() 1.526 + << " failed"; 1.527 + } else { 1.528 + // Didn't write all the bytes. 1.529 + DLOG(WARNING) << "wrote" << written << " bytes to " 1.530 + << filename.value() << " expected " << size; 1.531 + } 1.532 + return -1; 1.533 +} 1.534 + 1.535 +// Gets the current working directory for the process. 1.536 +bool GetCurrentDirectory(FilePath* dir) { 1.537 + base::ThreadRestrictions::AssertIOAllowed(); 1.538 + 1.539 + wchar_t system_buffer[MAX_PATH]; 1.540 + system_buffer[0] = 0; 1.541 + DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer); 1.542 + if (len == 0 || len > MAX_PATH) 1.543 + return false; 1.544 + // TODO(evanm): the old behavior of this function was to always strip the 1.545 + // trailing slash. We duplicate this here, but it shouldn't be necessary 1.546 + // when everyone is using the appropriate FilePath APIs. 1.547 + std::wstring dir_str(system_buffer); 1.548 + *dir = FilePath(dir_str).StripTrailingSeparators(); 1.549 + return true; 1.550 +} 1.551 + 1.552 +// Sets the current working directory for the process. 1.553 +bool SetCurrentDirectory(const FilePath& directory) { 1.554 + base::ThreadRestrictions::AssertIOAllowed(); 1.555 + BOOL ret = ::SetCurrentDirectory(directory.value().c_str()); 1.556 + return ret != 0; 1.557 +} 1.558 + 1.559 +bool NormalizeFilePath(const FilePath& path, FilePath* real_path) { 1.560 + base::ThreadRestrictions::AssertIOAllowed(); 1.561 + FilePath mapped_file; 1.562 + if (!NormalizeToNativeFilePath(path, &mapped_file)) 1.563 + return false; 1.564 + // NormalizeToNativeFilePath() will return a path that starts with 1.565 + // "\Device\Harddisk...". Helper DevicePathToDriveLetterPath() 1.566 + // will find a drive letter which maps to the path's device, so 1.567 + // that we return a path starting with a drive letter. 1.568 + return DevicePathToDriveLetterPath(mapped_file, real_path); 1.569 +} 1.570 + 1.571 +bool DevicePathToDriveLetterPath(const FilePath& nt_device_path, 1.572 + FilePath* out_drive_letter_path) { 1.573 + base::ThreadRestrictions::AssertIOAllowed(); 1.574 + 1.575 + // Get the mapping of drive letters to device paths. 1.576 + const int kDriveMappingSize = 1024; 1.577 + wchar_t drive_mapping[kDriveMappingSize] = {'\0'}; 1.578 + if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, drive_mapping)) { 1.579 + DLOG(ERROR) << "Failed to get drive mapping."; 1.580 + return false; 1.581 + } 1.582 + 1.583 + // The drive mapping is a sequence of null terminated strings. 1.584 + // The last string is empty. 1.585 + wchar_t* drive_map_ptr = drive_mapping; 1.586 + wchar_t device_path_as_string[MAX_PATH]; 1.587 + wchar_t drive[] = L" :"; 1.588 + 1.589 + // For each string in the drive mapping, get the junction that links 1.590 + // to it. If that junction is a prefix of |device_path|, then we 1.591 + // know that |drive| is the real path prefix. 1.592 + while (*drive_map_ptr) { 1.593 + drive[0] = drive_map_ptr[0]; // Copy the drive letter. 1.594 + 1.595 + if (QueryDosDevice(drive, device_path_as_string, MAX_PATH)) { 1.596 + FilePath device_path(device_path_as_string); 1.597 + if (device_path == nt_device_path || 1.598 + device_path.IsParent(nt_device_path)) { 1.599 + *out_drive_letter_path = FilePath(drive + 1.600 + nt_device_path.value().substr(wcslen(device_path_as_string))); 1.601 + return true; 1.602 + } 1.603 + } 1.604 + // Move to the next drive letter string, which starts one 1.605 + // increment after the '\0' that terminates the current string. 1.606 + while (*drive_map_ptr++); 1.607 + } 1.608 + 1.609 + // No drive matched. The path does not start with a device junction 1.610 + // that is mounted as a drive letter. This means there is no drive 1.611 + // letter path to the volume that holds |device_path|, so fail. 1.612 + return false; 1.613 +} 1.614 + 1.615 +bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) { 1.616 + base::ThreadRestrictions::AssertIOAllowed(); 1.617 + // In Vista, GetFinalPathNameByHandle() would give us the real path 1.618 + // from a file handle. If we ever deprecate XP, consider changing the 1.619 + // code below to a call to GetFinalPathNameByHandle(). The method this 1.620 + // function uses is explained in the following msdn article: 1.621 + // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx 1.622 + base::win::ScopedHandle file_handle( 1.623 + ::CreateFile(path.value().c_str(), 1.624 + GENERIC_READ, 1.625 + kFileShareAll, 1.626 + NULL, 1.627 + OPEN_EXISTING, 1.628 + FILE_ATTRIBUTE_NORMAL, 1.629 + NULL)); 1.630 + if (!file_handle) 1.631 + return false; 1.632 + 1.633 + // Create a file mapping object. Can't easily use MemoryMappedFile, because 1.634 + // we only map the first byte, and need direct access to the handle. You can 1.635 + // not map an empty file, this call fails in that case. 1.636 + base::win::ScopedHandle file_map_handle( 1.637 + ::CreateFileMapping(file_handle.Get(), 1.638 + NULL, 1.639 + PAGE_READONLY, 1.640 + 0, 1.641 + 1, // Just one byte. No need to look at the data. 1.642 + NULL)); 1.643 + if (!file_map_handle) 1.644 + return false; 1.645 + 1.646 + // Use a view of the file to get the path to the file. 1.647 + void* file_view = MapViewOfFile(file_map_handle.Get(), 1.648 + FILE_MAP_READ, 0, 0, 1); 1.649 + if (!file_view) 1.650 + return false; 1.651 + 1.652 + // The expansion of |path| into a full path may make it longer. 1.653 + // GetMappedFileName() will fail if the result is longer than MAX_PATH. 1.654 + // Pad a bit to be safe. If kMaxPathLength is ever changed to be less 1.655 + // than MAX_PATH, it would be nessisary to test that GetMappedFileName() 1.656 + // not return kMaxPathLength. This would mean that only part of the 1.657 + // path fit in |mapped_file_path|. 1.658 + const int kMaxPathLength = MAX_PATH + 10; 1.659 + wchar_t mapped_file_path[kMaxPathLength]; 1.660 + bool success = false; 1.661 + HANDLE cp = GetCurrentProcess(); 1.662 + if (::GetMappedFileNameW(cp, file_view, mapped_file_path, kMaxPathLength)) { 1.663 + *nt_path = FilePath(mapped_file_path); 1.664 + success = true; 1.665 + } 1.666 + ::UnmapViewOfFile(file_view); 1.667 + return success; 1.668 +} 1.669 + 1.670 +int GetMaximumPathComponentLength(const FilePath& path) { 1.671 + base::ThreadRestrictions::AssertIOAllowed(); 1.672 + 1.673 + wchar_t volume_path[MAX_PATH]; 1.674 + if (!GetVolumePathNameW(path.NormalizePathSeparators().value().c_str(), 1.675 + volume_path, 1.676 + arraysize(volume_path))) { 1.677 + return -1; 1.678 + } 1.679 + 1.680 + DWORD max_length = 0; 1.681 + if (!GetVolumeInformationW(volume_path, NULL, 0, NULL, &max_length, NULL, 1.682 + NULL, 0)) { 1.683 + return -1; 1.684 + } 1.685 + 1.686 + // Length of |path| with path separator appended. 1.687 + size_t prefix = path.StripTrailingSeparators().value().size() + 1; 1.688 + // The whole path string must be shorter than MAX_PATH. That is, it must be 1.689 + // prefix + component_length < MAX_PATH (or equivalently, <= MAX_PATH - 1). 1.690 + int whole_path_limit = std::max(0, MAX_PATH - 1 - static_cast<int>(prefix)); 1.691 + return std::min(whole_path_limit, static_cast<int>(max_length)); 1.692 +} 1.693 + 1.694 +} // namespace file_util 1.695 + 1.696 +namespace base { 1.697 +namespace internal { 1.698 + 1.699 +bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { 1.700 + ThreadRestrictions::AssertIOAllowed(); 1.701 + 1.702 + // NOTE: I suspect we could support longer paths, but that would involve 1.703 + // analyzing all our usage of files. 1.704 + if (from_path.value().length() >= MAX_PATH || 1.705 + to_path.value().length() >= MAX_PATH) { 1.706 + return false; 1.707 + } 1.708 + if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(), 1.709 + MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0) 1.710 + return true; 1.711 + 1.712 + // Keep the last error value from MoveFileEx around in case the below 1.713 + // fails. 1.714 + bool ret = false; 1.715 + DWORD last_error = ::GetLastError(); 1.716 + 1.717 + if (DirectoryExists(from_path)) { 1.718 + // MoveFileEx fails if moving directory across volumes. We will simulate 1.719 + // the move by using Copy and Delete. Ideally we could check whether 1.720 + // from_path and to_path are indeed in different volumes. 1.721 + ret = internal::CopyAndDeleteDirectory(from_path, to_path); 1.722 + } 1.723 + 1.724 + if (!ret) { 1.725 + // Leave a clue about what went wrong so that it can be (at least) picked 1.726 + // up by a PLOG entry. 1.727 + ::SetLastError(last_error); 1.728 + } 1.729 + 1.730 + return ret; 1.731 +} 1.732 + 1.733 +bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) { 1.734 + ThreadRestrictions::AssertIOAllowed(); 1.735 + 1.736 + // NOTE: I suspect we could support longer paths, but that would involve 1.737 + // analyzing all our usage of files. 1.738 + if (from_path.value().length() >= MAX_PATH || 1.739 + to_path.value().length() >= MAX_PATH) { 1.740 + return false; 1.741 + } 1.742 + return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(), 1.743 + false) != 0); 1.744 +} 1.745 + 1.746 +bool CopyAndDeleteDirectory(const FilePath& from_path, 1.747 + const FilePath& to_path) { 1.748 + ThreadRestrictions::AssertIOAllowed(); 1.749 + if (CopyDirectory(from_path, to_path, true)) { 1.750 + if (DeleteFile(from_path, true)) 1.751 + return true; 1.752 + 1.753 + // Like Move, this function is not transactional, so we just 1.754 + // leave the copied bits behind if deleting from_path fails. 1.755 + // If to_path exists previously then we have already overwritten 1.756 + // it by now, we don't get better off by deleting the new bits. 1.757 + } 1.758 + return false; 1.759 +} 1.760 + 1.761 +} // namespace internal 1.762 +} // namespace base