[Author: zork]
[google-gears.git] / gears / base / common / file_win32.cc
blob904993749f8bf06fd5eb6d13dabab8f3a3f03a50
1 // Copyright 2007, Google Inc.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
5 //
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)
31 #include <assert.h>
32 #include <windows.h>
33 #include <shlobj.h>
34 #include <shlwapi.h>
35 #include <tchar.h>
36 #include <wininet.h>
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)) {
65 return path;
68 if (StartsWith(path, kSharePrefix)) {
69 return kLongPathPrefixUNC + path.substr(kSharePrefix.length());
70 } else {
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)) {
85 return true;
86 } else if (!StartsWith(path, kLongPathPrefix) &&
87 StartsWith(path, kSharePrefix)) {
88 return true;
90 return false;
93 // Given a file share this function modifies the original path by removing
94 // the server name.
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) {
101 assert(share_path);
102 assert(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());
109 } else {
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(),
126 GENERIC_WRITE,
128 NULL,
129 CREATE_NEW,
130 FILE_ATTRIBUTE_NORMAL,
131 NULL));
132 if (safe_file_handle.get() == INVALID_HANDLE_VALUE) {
133 SetLastFileError(kCreateFileFailedMessage, full_filepath, GetLastError());
134 return false;
136 return true;
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(),
165 GENERIC_READ,
166 FILE_SHARE_READ,
167 NULL,
168 OPEN_EXISTING,
169 FILE_ATTRIBUTE_NORMAL,
170 NULL));
171 if (safe_file_handle.get() == INVALID_HANDLE_VALUE) {
172 return 0;
174 return static_cast<int64>(::GetFileSize(safe_file_handle.get(), NULL));
178 int File::ReadFileSegmentToBuffer(const char16 *full_filepath,
179 uint8* destination,
180 int max_bytes,
181 int64 position) {
182 if (max_bytes <= 0 || position < 0) {
183 return 0;
186 std::string16 long_path = ToLongPath(std::string16(full_filepath));
187 SAFE_HANDLE safe_file_handle(::CreateFileW(long_path.c_str(),
188 GENERIC_READ,
189 FILE_SHARE_READ,
190 NULL,
191 OPEN_EXISTING,
192 FILE_ATTRIBUTE_NORMAL,
193 NULL));
194 if (safe_file_handle.get() == INVALID_HANDLE_VALUE) {
195 return 0;
198 if (::SetFilePointer(safe_file_handle.get(), static_cast<LONG>(position),
199 NULL, FILE_BEGIN) == 0xFFFFFFFF) {
200 return 0;
203 // Read its contents into memory.
204 DWORD bytes_read;
205 if (!::ReadFile(safe_file_handle.get(), destination,
206 max_bytes, &bytes_read, NULL)) {
207 return 0;
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(),
219 GENERIC_READ,
220 FILE_SHARE_READ,
221 NULL,
222 OPEN_EXISTING,
223 FILE_ATTRIBUTE_NORMAL,
224 NULL));
225 if (safe_file_handle.get() == INVALID_HANDLE_VALUE) {
226 return false;
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) {
232 return false;
234 data->resize(file_size);
235 if (data->size() != file_size) {
236 return false;
239 if (file_size > 0) {
240 // Read its contents into memory.
241 DWORD bytes_read;
242 if (!::ReadFile(safe_file_handle.get(), &(*data)[0],
243 file_size, &bytes_read, NULL)
244 || (bytes_read != bytes_read)) {
245 data->clear();
246 return false;
249 return true;
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,
262 int length) {
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(),
266 GENERIC_WRITE,
268 NULL,
269 OPEN_EXISTING,
270 FILE_ATTRIBUTE_NORMAL,
271 NULL));
272 if (safe_file_handle.get() == INVALID_HANDLE_VALUE) {
273 return false;
275 // Write the file.
276 // TODO(michaeln): support large files here, where len > maxInt
277 size_t data_size = length;
278 DWORD bytes_written;
279 unsigned char nothing;
280 if (!::WriteFile(safe_file_handle.get(), (data_size > 0) ? buf : &nothing,
281 static_cast<DWORD>(data_size), &bytes_written, NULL)
282 || (bytes_written != data_size)) {
283 return false;
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
295 // FindFirstFile.
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);
302 find_spec += L"\\*";
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
308 int count = 0;
309 do {
310 if ((wcscmp(find_data.cFileName, L"..") == 0) ||
311 (wcscmp(find_data.cFileName, L".") == 0)) {
312 continue; // don't count parent and current directories
314 ++count;
315 } while (::FindNextFile(find_handle, &find_data) != 0);
316 ::FindClose(find_handle);
317 return count;
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) {
328 return false;
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);
335 if (0 == id) {
336 return false;
338 (*path) = file;
339 return true;
343 bool File::CreateNewTempDirectory(std::string16 *path) {
344 std::string16 temp; // to avoid modifying 'path' if something fails
345 if (!CreateNewTempFile(&temp)) {
346 return false;
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())) {
352 return false;
354 if (0 == ::CreateDirectoryW(temp.c_str(), NULL)) {
355 return false;
357 (*path) = temp;
358 return true;
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)) {
368 return true;
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).
384 } else {
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();
410 ++it) {
412 // Skip zero length components (If \\ was specified when creating path).
413 if (it->empty()) {
414 continue;
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
423 // fatal.
424 if (err != ERROR_ALREADY_EXISTS) {
425 return false;
426 } else if (!File::DirectoryExists(long_path.c_str())) {
427 return false; // We've collided with a file having the same name.
432 return true;
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(),
443 &find_data);
444 if (find_handle == INVALID_HANDLE_VALUE) {
445 return false;
448 do {
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);
458 if (item_is_dir) {
459 if (!DeleteRecursiveImp(child)) {
460 return false;
462 } else {
463 if (::DeleteFileW(child.c_str()) == 0) {
464 return false;
468 } while (::FindNextFile(find_handle, &find_data) != 0);
469 if (::FindClose(find_handle) == 0) {
470 return false;
473 if (::RemoveDirectoryW(del_path.c_str()) == 0) {
474 return false;
476 return true;
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);
487 path_length -= 1;
490 DWORD attributes = ::GetFileAttributesW(dir_to_delete.c_str());
491 if (attributes == INVALID_FILE_ATTRIBUTES) {
492 return false;
494 bool is_dir = (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
495 if (!is_dir) { // We can only operate on a directory.
496 return false;
499 return DeleteRecursiveImp(dir_to_delete);
501 #endif // #if defined(WIN32) && !defined(WINCE)