1 // Copyright 2007, Google Inc.
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
6 // 1. Redistributions of source code must retain the above copyright notice,
7 // this list of conditions and the following disclaimer.
8 // 2. Redistributions in binary form must reproduce the above copyright notice,
9 // this list of conditions and the following disclaimer in the documentation
10 // and/or other materials provided with the distribution.
11 // 3. Neither the name of Google Inc. nor the names of its contributors may be
12 // used to endorse or promote products derived from this software without
13 // specific prior written permission.
15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 // The methods of the File class with a Windows-specific implementation.
28 // TODO(cprince): remove platform-specific #ifdef guards when OS-specific
29 // sources (e.g. WIN32_CPPSRCS) are implemented
30 #if defined(WIN32) && !defined(WINCE)
38 #include "common/genfiles/product_constants.h" // from OUTDIR
39 #include "gears/base/common/common.h"
40 #include "gears/base/common/file.h"
41 #include "gears/base/common/int_types.h"
42 #include "gears/base/common/paths.h"
43 #include "gears/base/common/png_utils.h"
44 #include "gears/base/common/scoped_win32_handles.h"
45 #include "gears/base/common/security_model.h"
46 #include "gears/base/common/string16.h"
47 #include "gears/base/common/string_utils.h"
48 #include "gears/base/common/url_utils.h"
49 #include "gears/localserver/common/http_constants.h"
51 // Long Path Prefix - '\\?\' used for handling paths longer than MAX_PATH.
52 static const std::string16
kLongPathPrefix(STRING16(L
"\\\\?\\"));
54 // Long Path Prefix - '\\?\UNC\' used for network shares.
55 static const std::string16
kLongPathPrefixUNC(STRING16(L
"\\\\?\\UNC\\"));
57 // '\\' Prefix for network shares.
58 static const std::string16
kSharePrefix(STRING16(L
"\\\\"));
60 // Prepend long path prefix onto pathname so we can handle long filenames.
61 // This function does nothing if the prefix already exists.
62 static std::string16
ToLongPath(const std::string16
&path
) {
63 if (StartsWith(path
, kLongPathPrefix
) ||
64 StartsWith(path
, kLongPathPrefixUNC
)) {
68 if (StartsWith(path
, kSharePrefix
)) {
69 return kLongPathPrefixUNC
+ path
.substr(kSharePrefix
.length());
71 return kLongPathPrefix
+ path
;
75 // Joins 2 paths together.
76 static std::string16
JoinPath(const std::string16
&path1
,
77 const std::string16
&path2
) {
78 static std::string16
path_sep(&kPathSeparator
, 1);
79 return path1
+ path_sep
+ path2
;
82 // Is this path a file share?
83 static bool IsFileShare(const std::string16
&path
) {
84 if (StartsWith(path
, kLongPathPrefixUNC
)) {
86 } else if (!StartsWith(path
, kLongPathPrefix
) &&
87 StartsWith(path
, kSharePrefix
)) {
93 // Given a file share this function modifies the original path by removing
96 // Input: Full path of windows share passed in, in path parameter e.g.
97 // Output: server_name - '\\server' part of share path.
98 // share_path - '\\server' prefix is stripped off.
99 static void SplitServerNameOffShare(std::string16
*share_path
,
100 std::string16
*server_name
) {
103 assert(share_path
->length() > (kSharePrefix
.length() + 2));
105 if (StartsWith(*share_path
, kLongPathPrefixUNC
)) {
106 // Paths of the form '\\?\UNC\server_name\rest_of_path'.
107 // Strip off everything before server_name.
108 *share_path
= share_path
->substr(kLongPathPrefixUNC
.length());
110 // Paths of the form '\\server_name\rest_of_path'
111 // Strip \\ off the '\\' prefix.
112 *share_path
= share_path
->substr(kSharePrefix
.length());
115 size_t end_of_servername_ofs
= share_path
->find(kPathSeparator
);
116 assert(end_of_servername_ofs
!= std::string16::npos
);
118 *server_name
= kSharePrefix
+ share_path
->substr(0, end_of_servername_ofs
);
119 share_path
->erase(0, end_of_servername_ofs
);
122 bool File::CreateNewFile(const char16
*full_filepath
) {
123 std::string16 long_path
= ToLongPath(full_filepath
);
124 // Create a new file, if a file already exists this will fail
125 SAFE_HANDLE
safe_file_handle(::CreateFileW(long_path
.c_str(),
130 FILE_ATTRIBUTE_NORMAL
,
132 if (safe_file_handle
.get() == INVALID_HANDLE_VALUE
) {
133 SetLastFileError(kCreateFileFailedMessage
, full_filepath
, GetLastError());
140 bool File::Delete(const char16
*full_filepath
) {
141 std::string16 long_path
= ToLongPath(std::string16(full_filepath
));
142 return ::DeleteFileW(long_path
.c_str()) ? true : false;
146 bool File::Exists(const char16
*full_filepath
) {
147 std::string16 long_path
= ToLongPath(std::string16(full_filepath
));
148 DWORD attrs
= ::GetFileAttributesW(long_path
.c_str());
149 return (attrs
!= INVALID_FILE_ATTRIBUTES
) &&
150 ((attrs
& FILE_ATTRIBUTE_DIRECTORY
) == 0);
154 bool File::DirectoryExists(const char16
*full_dirpath
) {
155 std::string16 long_path
= ToLongPath(std::string16(full_dirpath
));
156 DWORD attrs
= ::GetFileAttributesW(long_path
.c_str());
157 return (attrs
!= INVALID_FILE_ATTRIBUTES
) &&
158 ((attrs
& FILE_ATTRIBUTE_DIRECTORY
) == FILE_ATTRIBUTE_DIRECTORY
);
162 int64
File::GetFileSize(const char16
*full_filepath
) {
163 std::string16 long_path
= ToLongPath(std::string16(full_filepath
));
164 SAFE_HANDLE
safe_file_handle(::CreateFileW(long_path
.c_str(),
169 FILE_ATTRIBUTE_NORMAL
,
171 if (safe_file_handle
.get() == INVALID_HANDLE_VALUE
) {
174 return static_cast<int64
>(::GetFileSize(safe_file_handle
.get(), NULL
));
178 int File::ReadFileSegmentToBuffer(const char16
*full_filepath
,
182 if (max_bytes
<= 0 || position
< 0) {
186 std::string16 long_path
= ToLongPath(std::string16(full_filepath
));
187 SAFE_HANDLE
safe_file_handle(::CreateFileW(long_path
.c_str(),
192 FILE_ATTRIBUTE_NORMAL
,
194 if (safe_file_handle
.get() == INVALID_HANDLE_VALUE
) {
198 if (::SetFilePointer(safe_file_handle
.get(), static_cast<LONG
>(position
),
199 NULL
, FILE_BEGIN
) == 0xFFFFFFFF) {
203 // Read its contents into memory.
205 if (!::ReadFile(safe_file_handle
.get(), destination
,
206 max_bytes
, &bytes_read
, NULL
)) {
210 return static_cast<int>(bytes_read
);
214 bool File::ReadFileToVector(const char16
*full_filepath
,
215 std::vector
<uint8
> *data
) {
216 // Open the file for reading.
217 std::string16 long_path
= ToLongPath(std::string16(full_filepath
));
218 SAFE_HANDLE
safe_file_handle(::CreateFileW(long_path
.c_str(),
223 FILE_ATTRIBUTE_NORMAL
,
225 if (safe_file_handle
.get() == INVALID_HANDLE_VALUE
) {
228 // Resize our buffer to fit the size of the file.
229 // TODO(michaeln): support large files here, where len > maxInt
230 DWORD file_size
= ::GetFileSize(safe_file_handle
.get(), NULL
);
231 if (file_size
== INVALID_FILE_SIZE
) {
234 data
->resize(file_size
);
235 if (data
->size() != file_size
) {
240 // Read its contents into memory.
242 if (!::ReadFile(safe_file_handle
.get(), &(*data
)[0],
243 file_size
, &bytes_read
, NULL
)
244 || (bytes_read
!= bytes_read
)) {
253 bool File::WriteVectorToFile(const char16
*full_filepath
,
254 const std::vector
<uint8
> *data
) {
255 const uint8
*first_byte
= data
->size() ? &(data
->at(0)) : NULL
;
256 return WriteBytesToFile(full_filepath
, first_byte
,
257 static_cast<int>(data
->size()));
261 bool File::WriteBytesToFile(const char16
*full_filepath
, const uint8
*buf
,
263 // Open the file for writing.
264 std::string16 long_path
= ToLongPath(std::string16(full_filepath
));
265 SAFE_HANDLE
safe_file_handle(::CreateFileW(long_path
.c_str(),
270 FILE_ATTRIBUTE_NORMAL
,
272 if (safe_file_handle
.get() == INVALID_HANDLE_VALUE
) {
276 // TODO(michaeln): support large files here, where len > maxInt
277 size_t data_size
= length
;
279 unsigned char nothing
;
280 if (!::WriteFile(safe_file_handle
.get(), (data_size
> 0) ? buf
: ¬hing
,
281 static_cast<DWORD
>(data_size
), &bytes_written
, NULL
)
282 || (bytes_written
!= data_size
)) {
285 // Explicitly set EOF to truncate pre-existing content beyond the end of
286 // the newly written content
287 return SetEndOfFile(safe_file_handle
.get()) ? true : false;
291 int File::GetDirectoryFileCount(const char16
*full_dirpath
) {
292 std::string16 find_spec
= ToLongPath(full_dirpath
);
294 // If path ends with trailing '\' then sanitize it before passing into
296 size_t find_spec_length
= find_spec
.length();
297 if (find_spec_length
> 1 &&
298 find_spec
[find_spec_length
- 1] == kPathSeparator
) {
299 find_spec
.erase(find_spec_length
- 1);
303 WIN32_FIND_DATA find_data
;
304 HANDLE find_handle
= ::FindFirstFileW(find_spec
.c_str(), &find_data
);
305 if (find_handle
== INVALID_HANDLE_VALUE
) {
306 return 0; // expected if the directory does not exist
310 if ((wcscmp(find_data
.cFileName
, L
"..") == 0) ||
311 (wcscmp(find_data
.cFileName
, L
".") == 0)) {
312 continue; // don't count parent and current directories
315 } while (::FindNextFile(find_handle
, &find_data
) != 0);
316 ::FindClose(find_handle
);
321 bool File::CreateNewTempFile(std::string16
*path
) {
322 static const char16
*kTempFilePrefix
= STRING16(PRODUCT_SHORT_NAME
);
324 // Get the system temp directory.
325 wchar_t root
[MAX_PATH
];
326 DWORD chars
= ::GetTempPathW(MAX_PATH
, root
);
327 if (chars
>= MAX_PATH
) {
331 // Create a uniquely named temp file in that directory.
332 // Note: GetTempFileName() uses 3 chars max of the suggested prefix
333 wchar_t file
[MAX_PATH
];
334 UINT id
= ::GetTempFileNameW(root
, kTempFilePrefix
, 0, file
);
343 bool File::CreateNewTempDirectory(std::string16
*path
) {
344 std::string16 temp
; // to avoid modifying 'path' if something fails
345 if (!CreateNewTempFile(&temp
)) {
349 // Delete that file, and create a directory with the same name,
350 // now that we know it's unique.
351 if (0 == ::DeleteFileW(temp
.c_str())) {
354 if (0 == ::CreateDirectoryW(temp
.c_str(), NULL
)) {
362 bool File::RecursivelyCreateDir(const char16
*full_dirpath
) {
363 // In order to handle long path names reliably we have to do this ourselves
364 // rather than call SHCreateDirectoryEx().
366 // If directory already exists, no need to do anything.
367 if(File::DirectoryExists(full_dirpath
)) {
371 // Init long path with the \\?\ prefix in case of file or \\?\UNC\server_name
372 // in case of a file share. This so we can accumulate the path for the
373 // directories without prepending the prefix each time.
374 std::string16 long_path
;
375 std::string16
full_path_to_split(full_dirpath
);
377 // Strip off server name from file share.
378 if (IsFileShare(full_dirpath
)) {
379 std::string16 server_name
;
380 SplitServerNameOffShare(&full_path_to_split
, &server_name
);
381 long_path
= ToLongPath(server_name
);
382 // Long path is now \\?\UNC\server_name, and full_path_to_split is the
383 // remainder of the original path (with the \\server prefix stripped off).
385 long_path
= kLongPathPrefix
;
387 // Strip long file prefix if it exists.
388 if (StartsWith(full_path_to_split
, kLongPathPrefix
)) {
389 full_path_to_split
.erase(0, kLongPathPrefix
.length());
393 File::PathComponents path_components
;
394 File::SplitPath(full_path_to_split
, &path_components
);
396 // If first component in path is drive letter then remove that, as we
397 // definitely don't need to create it.
398 if (path_components
.size() > 0 && path_components
[0].length() == 2) {
399 const std::string16
&drv_letter
= path_components
[0];
401 if (iswalpha(drv_letter
[0]) && drv_letter
[1] == L
':') {
402 long_path
+= drv_letter
;
403 path_components
.erase(path_components
.begin());
407 // Recursively create directories.
408 for (File::PathComponents::const_iterator it
= path_components
.begin();
409 it
!= path_components
.end();
412 // Skip zero length components (If \\ was specified when creating path).
417 long_path
= JoinPath(long_path
, *it
);
419 if (!CreateDirectoryW(long_path
.c_str(), NULL
)) {
420 DWORD err
= GetLastError();
422 // Any error value other than "directory already exists" is considered
424 if (err
!= ERROR_ALREADY_EXISTS
) {
426 } else if (!File::DirectoryExists(long_path
.c_str())) {
427 return false; // We've collided with a file having the same name.
435 // Recursive internal function for use by DeleteRecursively.
436 static bool DeleteRecursiveImp(const std::string16
&del_path
) {
437 assert(StartsWith(del_path
, kLongPathPrefix
));
439 // First recursively delete directory contents
440 std::string16 find_spec
= del_path
+ L
"\\*";
441 WIN32_FIND_DATA find_data
;
442 HANDLE find_handle
= ::FindFirstFileW(find_spec
.c_str(),
444 if (find_handle
== INVALID_HANDLE_VALUE
) {
449 if ((wcscmp(find_data
.cFileName
, L
"..") == 0) ||
450 (wcscmp(find_data
.cFileName
, L
".") == 0)) {
451 continue; // nothing to do for parent or current directory
454 bool item_is_dir
= (find_data
.dwFileAttributes
&
455 FILE_ATTRIBUTE_DIRECTORY
) != 0;
457 std::string16 child
= JoinPath(del_path
, find_data
.cFileName
);
459 if (!DeleteRecursiveImp(child
)) {
463 if (::DeleteFileW(child
.c_str()) == 0) {
468 } while (::FindNextFile(find_handle
, &find_data
) != 0);
469 if (::FindClose(find_handle
) == 0) {
473 if (::RemoveDirectoryW(del_path
.c_str()) == 0) {
479 bool File::DeleteRecursively(const char16
*full_dirpath
) {
480 std::string16
dir_to_delete(full_dirpath
);
481 dir_to_delete
= ToLongPath(dir_to_delete
);
483 // Cut off trailing slashes.
484 size_t path_length
= dir_to_delete
.length();
485 while (path_length
> 0 && dir_to_delete
[path_length
- 1] == kPathSeparator
) {
486 dir_to_delete
.erase(path_length
- 1);
490 DWORD attributes
= ::GetFileAttributesW(dir_to_delete
.c_str());
491 if (attributes
== INVALID_FILE_ATTRIBUTES
) {
494 bool is_dir
= (attributes
& FILE_ATTRIBUTE_DIRECTORY
) != 0;
495 if (!is_dir
) { // We can only operate on a directory.
499 return DeleteRecursiveImp(dir_to_delete
);
501 #endif // #if defined(WIN32) && !defined(WINCE)