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