|
1 // Copyright (c) 2006-2008 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/file_util.h" |
|
6 |
|
7 #include <windows.h> |
|
8 #include <shellapi.h> |
|
9 #include <shlobj.h> |
|
10 #include <time.h> |
|
11 #include <string> |
|
12 |
|
13 #include "base/file_path.h" |
|
14 #include "base/logging.h" |
|
15 #include "base/scoped_handle.h" |
|
16 #include "base/string_util.h" |
|
17 #include "base/time.h" |
|
18 #include "base/win_util.h" |
|
19 |
|
20 namespace file_util { |
|
21 |
|
22 bool AbsolutePath(FilePath* path) { |
|
23 wchar_t file_path_buf[MAX_PATH]; |
|
24 if (!_wfullpath(file_path_buf, path->value().c_str(), MAX_PATH)) |
|
25 return false; |
|
26 *path = FilePath(file_path_buf); |
|
27 return true; |
|
28 } |
|
29 |
|
30 bool Delete(const FilePath& path, bool recursive) { |
|
31 if (path.value().length() >= MAX_PATH) |
|
32 return false; |
|
33 |
|
34 // If we're not recursing use DeleteFile; it should be faster. DeleteFile |
|
35 // fails if passed a directory though, which is why we fall through on |
|
36 // failure to the SHFileOperation. |
|
37 if (!recursive && DeleteFile(path.value().c_str()) != 0) |
|
38 return true; |
|
39 |
|
40 // SHFILEOPSTRUCT wants the path to be terminated with two NULLs, |
|
41 // so we have to use wcscpy because wcscpy_s writes non-NULLs |
|
42 // into the rest of the buffer. |
|
43 wchar_t double_terminated_path[MAX_PATH + 1] = {0}; |
|
44 #pragma warning(suppress:4996) // don't complain about wcscpy deprecation |
|
45 wcscpy(double_terminated_path, path.value().c_str()); |
|
46 |
|
47 SHFILEOPSTRUCT file_operation = {0}; |
|
48 file_operation.wFunc = FO_DELETE; |
|
49 file_operation.pFrom = double_terminated_path; |
|
50 file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION; |
|
51 if (!recursive) |
|
52 file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY; |
|
53 int err = SHFileOperation(&file_operation); |
|
54 // Some versions of Windows return ERROR_FILE_NOT_FOUND when |
|
55 // deleting an empty directory. |
|
56 return (err == 0 || err == ERROR_FILE_NOT_FOUND); |
|
57 } |
|
58 |
|
59 bool CopyFile(const FilePath& from_path, const FilePath& to_path) { |
|
60 // NOTE: I suspect we could support longer paths, but that would involve |
|
61 // analyzing all our usage of files. |
|
62 if (from_path.value().length() >= MAX_PATH || |
|
63 to_path.value().length() >= MAX_PATH) { |
|
64 return false; |
|
65 } |
|
66 return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(), |
|
67 false) != 0); |
|
68 } |
|
69 |
|
70 bool ShellCopy(const FilePath& from_path, const FilePath& to_path, |
|
71 bool recursive) { |
|
72 // NOTE: I suspect we could support longer paths, but that would involve |
|
73 // analyzing all our usage of files. |
|
74 if (from_path.value().length() >= MAX_PATH || |
|
75 to_path.value().length() >= MAX_PATH) { |
|
76 return false; |
|
77 } |
|
78 |
|
79 // SHFILEOPSTRUCT wants the path to be terminated with two NULLs, |
|
80 // so we have to use wcscpy because wcscpy_s writes non-NULLs |
|
81 // into the rest of the buffer. |
|
82 wchar_t double_terminated_path_from[MAX_PATH + 1] = {0}; |
|
83 wchar_t double_terminated_path_to[MAX_PATH + 1] = {0}; |
|
84 #pragma warning(suppress:4996) // don't complain about wcscpy deprecation |
|
85 wcscpy(double_terminated_path_from, from_path.value().c_str()); |
|
86 #pragma warning(suppress:4996) // don't complain about wcscpy deprecation |
|
87 wcscpy(double_terminated_path_to, to_path.value().c_str()); |
|
88 |
|
89 SHFILEOPSTRUCT file_operation = {0}; |
|
90 file_operation.wFunc = FO_COPY; |
|
91 file_operation.pFrom = double_terminated_path_from; |
|
92 file_operation.pTo = double_terminated_path_to; |
|
93 file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION | |
|
94 FOF_NOCONFIRMMKDIR; |
|
95 if (!recursive) |
|
96 file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY; |
|
97 |
|
98 return (SHFileOperation(&file_operation) == 0); |
|
99 } |
|
100 |
|
101 bool CopyDirectory(const FilePath& from_path, const FilePath& to_path, |
|
102 bool recursive) { |
|
103 if (recursive) |
|
104 return ShellCopy(from_path, to_path, true); |
|
105 |
|
106 // Instead of creating a new directory, we copy the old one to include the |
|
107 // security information of the folder as part of the copy. |
|
108 if (!PathExists(to_path)) { |
|
109 // Except that Vista fails to do that, and instead do a recursive copy if |
|
110 // the target directory doesn't exist. |
|
111 if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) |
|
112 CreateDirectory(to_path); |
|
113 else |
|
114 ShellCopy(from_path, to_path, false); |
|
115 } |
|
116 |
|
117 FilePath directory = from_path.Append(L"*.*"); |
|
118 return ShellCopy(directory, to_path, false); |
|
119 } |
|
120 |
|
121 bool PathExists(const FilePath& path) { |
|
122 return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES); |
|
123 } |
|
124 |
|
125 bool PathIsWritable(const FilePath& path) { |
|
126 HANDLE dir = |
|
127 CreateFile(path.value().c_str(), FILE_ADD_FILE, |
|
128 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
|
129 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); |
|
130 |
|
131 if (dir == INVALID_HANDLE_VALUE) |
|
132 return false; |
|
133 |
|
134 CloseHandle(dir); |
|
135 return true; |
|
136 } |
|
137 |
|
138 bool DirectoryExists(const FilePath& path) { |
|
139 DWORD fileattr = GetFileAttributes(path.value().c_str()); |
|
140 if (fileattr != INVALID_FILE_ATTRIBUTES) |
|
141 return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0; |
|
142 return false; |
|
143 } |
|
144 |
|
145 bool GetTempDir(FilePath* path) { |
|
146 wchar_t temp_path[MAX_PATH + 1]; |
|
147 DWORD path_len = ::GetTempPath(MAX_PATH, temp_path); |
|
148 if (path_len >= MAX_PATH || path_len <= 0) |
|
149 return false; |
|
150 // TODO(evanm): the old behavior of this function was to always strip the |
|
151 // trailing slash. We duplicate this here, but it shouldn't be necessary |
|
152 // when everyone is using the appropriate FilePath APIs. |
|
153 std::wstring path_str(temp_path); |
|
154 TrimTrailingSeparator(&path_str); |
|
155 *path = FilePath(path_str); |
|
156 return true; |
|
157 } |
|
158 |
|
159 bool GetShmemTempDir(FilePath* path) { |
|
160 return GetTempDir(path); |
|
161 } |
|
162 |
|
163 bool CreateTemporaryFileName(FilePath* path) { |
|
164 std::wstring temp_path, temp_file; |
|
165 |
|
166 if (!GetTempDir(&temp_path)) |
|
167 return false; |
|
168 |
|
169 if (CreateTemporaryFileNameInDir(temp_path, &temp_file)) { |
|
170 *path = FilePath(temp_file); |
|
171 return true; |
|
172 } |
|
173 |
|
174 return false; |
|
175 } |
|
176 |
|
177 FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) { |
|
178 return CreateAndOpenTemporaryFile(path); |
|
179 } |
|
180 |
|
181 // On POSIX we have semantics to create and open a temporary file |
|
182 // atomically. |
|
183 // TODO(jrg): is there equivalent call to use on Windows instead of |
|
184 // going 2-step? |
|
185 FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { |
|
186 std::wstring wstring_path; |
|
187 if (!CreateTemporaryFileNameInDir(dir.value(), &wstring_path)) { |
|
188 return NULL; |
|
189 } |
|
190 *path = FilePath(wstring_path); |
|
191 // Open file in binary mode, to avoid problems with fwrite. On Windows |
|
192 // it replaces \n's with \r\n's, which may surprise you. |
|
193 // Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx |
|
194 return OpenFile(*path, "wb+"); |
|
195 } |
|
196 |
|
197 bool CreateTemporaryFileNameInDir(const std::wstring& dir, |
|
198 std::wstring* temp_file) { |
|
199 wchar_t temp_name[MAX_PATH + 1]; |
|
200 |
|
201 if (!GetTempFileName(dir.c_str(), L"", 0, temp_name)) |
|
202 return false; // fail! |
|
203 |
|
204 DWORD path_len = GetLongPathName(temp_name, temp_name, MAX_PATH); |
|
205 if (path_len > MAX_PATH + 1 || path_len == 0) |
|
206 return false; // fail! |
|
207 |
|
208 temp_file->assign(temp_name, path_len); |
|
209 return true; |
|
210 } |
|
211 |
|
212 bool CreateNewTempDirectory(const FilePath::StringType& prefix, |
|
213 FilePath* new_temp_path) { |
|
214 FilePath system_temp_dir; |
|
215 if (!GetTempDir(&system_temp_dir)) |
|
216 return false; |
|
217 |
|
218 FilePath path_to_create; |
|
219 srand(static_cast<uint32_t>(time(NULL))); |
|
220 |
|
221 int count = 0; |
|
222 while (count < 50) { |
|
223 // Try create a new temporary directory with random generated name. If |
|
224 // the one exists, keep trying another path name until we reach some limit. |
|
225 path_to_create = system_temp_dir; |
|
226 std::wstring new_dir_name; |
|
227 new_dir_name.assign(prefix); |
|
228 new_dir_name.append(IntToWString(rand() % kint16max)); |
|
229 path_to_create = path_to_create.Append(new_dir_name); |
|
230 |
|
231 if (::CreateDirectory(path_to_create.value().c_str(), NULL)) |
|
232 break; |
|
233 count++; |
|
234 } |
|
235 |
|
236 if (count == 50) { |
|
237 return false; |
|
238 } |
|
239 |
|
240 *new_temp_path = path_to_create; |
|
241 return true; |
|
242 } |
|
243 |
|
244 bool CreateDirectory(const FilePath& full_path) { |
|
245 if (DirectoryExists(full_path)) |
|
246 return true; |
|
247 int err = SHCreateDirectoryEx(NULL, full_path.value().c_str(), NULL); |
|
248 return err == ERROR_SUCCESS; |
|
249 } |
|
250 |
|
251 bool GetFileInfo(const FilePath& file_path, FileInfo* results) { |
|
252 WIN32_FILE_ATTRIBUTE_DATA attr; |
|
253 if (!GetFileAttributesEx(file_path.ToWStringHack().c_str(), |
|
254 GetFileExInfoStandard, &attr)) { |
|
255 return false; |
|
256 } |
|
257 |
|
258 ULARGE_INTEGER size; |
|
259 size.HighPart = attr.nFileSizeHigh; |
|
260 size.LowPart = attr.nFileSizeLow; |
|
261 results->size = size.QuadPart; |
|
262 |
|
263 results->is_directory = |
|
264 (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; |
|
265 return true; |
|
266 } |
|
267 |
|
268 FILE* OpenFile(const FilePath& filename, const char* mode) { |
|
269 std::wstring w_mode = ASCIIToWide(std::string(mode)); |
|
270 FILE* file; |
|
271 if (_wfopen_s(&file, filename.value().c_str(), w_mode.c_str()) != 0) { |
|
272 return NULL; |
|
273 } |
|
274 return file; |
|
275 } |
|
276 |
|
277 FILE* OpenFile(const std::string& filename, const char* mode) { |
|
278 FILE* file; |
|
279 if (fopen_s(&file, filename.c_str(), mode) != 0) { |
|
280 return NULL; |
|
281 } |
|
282 return file; |
|
283 } |
|
284 |
|
285 int ReadFile(const FilePath& filename, char* data, int size) { |
|
286 ScopedHandle file(CreateFile(filename.value().c_str(), |
|
287 GENERIC_READ, |
|
288 FILE_SHARE_READ | FILE_SHARE_WRITE, |
|
289 NULL, |
|
290 OPEN_EXISTING, |
|
291 FILE_FLAG_SEQUENTIAL_SCAN, |
|
292 NULL)); |
|
293 if (file == INVALID_HANDLE_VALUE) |
|
294 return -1; |
|
295 |
|
296 int ret_value; |
|
297 DWORD read; |
|
298 if (::ReadFile(file, data, size, &read, NULL) && read == size) { |
|
299 ret_value = static_cast<int>(read); |
|
300 } else { |
|
301 ret_value = -1; |
|
302 } |
|
303 |
|
304 return ret_value; |
|
305 } |
|
306 |
|
307 int WriteFile(const FilePath& filename, const char* data, int size) { |
|
308 ScopedHandle file(CreateFile(filename.value().c_str(), |
|
309 GENERIC_WRITE, |
|
310 0, |
|
311 NULL, |
|
312 CREATE_ALWAYS, |
|
313 0, |
|
314 NULL)); |
|
315 if (file == INVALID_HANDLE_VALUE) { |
|
316 CHROMIUM_LOG(WARNING) << "CreateFile failed for path " << filename.value() << |
|
317 " error code=" << GetLastError() << |
|
318 " error text=" << win_util::FormatLastWin32Error(); |
|
319 return -1; |
|
320 } |
|
321 |
|
322 DWORD written; |
|
323 BOOL result = ::WriteFile(file, data, size, &written, NULL); |
|
324 if (result && written == size) |
|
325 return static_cast<int>(written); |
|
326 |
|
327 if (!result) { |
|
328 // WriteFile failed. |
|
329 CHROMIUM_LOG(WARNING) << "writing file " << filename.value() << |
|
330 " failed, error code=" << GetLastError() << |
|
331 " description=" << win_util::FormatLastWin32Error(); |
|
332 } else { |
|
333 // Didn't write all the bytes. |
|
334 CHROMIUM_LOG(WARNING) << "wrote" << written << " bytes to " << |
|
335 filename.value() << " expected " << size; |
|
336 } |
|
337 return -1; |
|
338 } |
|
339 |
|
340 // Gets the current working directory for the process. |
|
341 bool GetCurrentDirectory(FilePath* dir) { |
|
342 wchar_t system_buffer[MAX_PATH]; |
|
343 system_buffer[0] = 0; |
|
344 DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer); |
|
345 if (len == 0 || len > MAX_PATH) |
|
346 return false; |
|
347 // TODO(evanm): the old behavior of this function was to always strip the |
|
348 // trailing slash. We duplicate this here, but it shouldn't be necessary |
|
349 // when everyone is using the appropriate FilePath APIs. |
|
350 std::wstring dir_str(system_buffer); |
|
351 file_util::TrimTrailingSeparator(&dir_str); |
|
352 *dir = FilePath(dir_str); |
|
353 return true; |
|
354 } |
|
355 |
|
356 // Sets the current working directory for the process. |
|
357 bool SetCurrentDirectory(const FilePath& directory) { |
|
358 BOOL ret = ::SetCurrentDirectory(directory.value().c_str()); |
|
359 return ret != 0; |
|
360 } |
|
361 |
|
362 // Deprecated functions ---------------------------------------------------- |
|
363 |
|
364 void InsertBeforeExtension(std::wstring* path_str, |
|
365 const std::wstring& suffix) { |
|
366 FilePath path(*path_str); |
|
367 InsertBeforeExtension(&path, suffix); |
|
368 path_str->assign(path.value()); |
|
369 } |
|
370 void ReplaceExtension(std::wstring* file_name, const std::wstring& extension) { |
|
371 FilePath path(*file_name); |
|
372 ReplaceExtension(&path, extension); |
|
373 file_name->assign(path.value()); |
|
374 } |
|
375 } // namespace file_util |