|
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 <dirent.h> |
|
8 #include <errno.h> |
|
9 #include <fcntl.h> |
|
10 #include <fnmatch.h> |
|
11 #ifndef ANDROID |
|
12 #include <fts.h> |
|
13 #endif |
|
14 #include <libgen.h> |
|
15 #include <stdio.h> |
|
16 #include <string.h> |
|
17 #include <sys/errno.h> |
|
18 #include <sys/mman.h> |
|
19 #define _DARWIN_USE_64_BIT_INODE // Use 64-bit inode data structures |
|
20 #include <sys/stat.h> |
|
21 #include <sys/types.h> |
|
22 #include <time.h> |
|
23 #include <unistd.h> |
|
24 |
|
25 #include <fstream> |
|
26 #include <string> |
|
27 #include <vector> |
|
28 |
|
29 #include "base/basictypes.h" |
|
30 #include "base/eintr_wrapper.h" |
|
31 #include "base/file_path.h" |
|
32 #include "base/logging.h" |
|
33 #include "base/string_util.h" |
|
34 #include "base/time.h" |
|
35 |
|
36 namespace file_util { |
|
37 |
|
38 #if defined(GOOGLE_CHROME_BUILD) |
|
39 static const char* kTempFileName = "com.google.chrome.XXXXXX"; |
|
40 #else |
|
41 static const char* kTempFileName = "org.chromium.XXXXXX"; |
|
42 #endif |
|
43 |
|
44 bool AbsolutePath(FilePath* path) { |
|
45 char full_path[PATH_MAX]; |
|
46 if (realpath(path->value().c_str(), full_path) == NULL) |
|
47 return false; |
|
48 *path = FilePath(full_path); |
|
49 return true; |
|
50 } |
|
51 |
|
52 // TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*" |
|
53 // which works both with and without the recursive flag. I'm not sure we need |
|
54 // that functionality. If not, remove from file_util_win.cc, otherwise add it |
|
55 // here. |
|
56 bool Delete(const FilePath& path, bool recursive) { |
|
57 const char* path_str = path.value().c_str(); |
|
58 struct stat file_info; |
|
59 int test = stat(path_str, &file_info); |
|
60 if (test != 0) { |
|
61 // The Windows version defines this condition as success. |
|
62 bool ret = (errno == ENOENT || errno == ENOTDIR); |
|
63 return ret; |
|
64 } |
|
65 if (!S_ISDIR(file_info.st_mode)) |
|
66 return (unlink(path_str) == 0); |
|
67 if (!recursive) |
|
68 return (rmdir(path_str) == 0); |
|
69 |
|
70 #ifdef ANDROID |
|
71 // XXX Need ftsless impl for bionic |
|
72 return false; |
|
73 #else |
|
74 bool success = true; |
|
75 int ftsflags = FTS_PHYSICAL | FTS_NOSTAT; |
|
76 char top_dir[PATH_MAX]; |
|
77 if (base::strlcpy(top_dir, path_str, |
|
78 arraysize(top_dir)) >= arraysize(top_dir)) { |
|
79 return false; |
|
80 } |
|
81 char* dir_list[2] = { top_dir, NULL }; |
|
82 FTS* fts = fts_open(dir_list, ftsflags, NULL); |
|
83 if (fts) { |
|
84 FTSENT* fts_ent = fts_read(fts); |
|
85 while (success && fts_ent != NULL) { |
|
86 switch (fts_ent->fts_info) { |
|
87 case FTS_DNR: |
|
88 case FTS_ERR: |
|
89 // log error |
|
90 success = false; |
|
91 continue; |
|
92 break; |
|
93 case FTS_DP: |
|
94 success = (rmdir(fts_ent->fts_accpath) == 0); |
|
95 break; |
|
96 case FTS_D: |
|
97 break; |
|
98 case FTS_NSOK: |
|
99 case FTS_F: |
|
100 case FTS_SL: |
|
101 case FTS_SLNONE: |
|
102 success = (unlink(fts_ent->fts_accpath) == 0); |
|
103 break; |
|
104 default: |
|
105 DCHECK(false); |
|
106 break; |
|
107 } |
|
108 fts_ent = fts_read(fts); |
|
109 } |
|
110 fts_close(fts); |
|
111 } |
|
112 return success; |
|
113 #endif |
|
114 } |
|
115 |
|
116 bool Move(const FilePath& from_path, const FilePath& to_path) { |
|
117 if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0) |
|
118 return true; |
|
119 |
|
120 if (!CopyDirectory(from_path, to_path, true)) |
|
121 return false; |
|
122 |
|
123 Delete(from_path, true); |
|
124 return true; |
|
125 } |
|
126 |
|
127 bool CopyDirectory(const FilePath& from_path, |
|
128 const FilePath& to_path, |
|
129 bool recursive) { |
|
130 // Some old callers of CopyDirectory want it to support wildcards. |
|
131 // After some discussion, we decided to fix those callers. |
|
132 // Break loudly here if anyone tries to do this. |
|
133 // TODO(evanm): remove this once we're sure it's ok. |
|
134 DCHECK(to_path.value().find('*') == std::string::npos); |
|
135 DCHECK(from_path.value().find('*') == std::string::npos); |
|
136 |
|
137 char top_dir[PATH_MAX]; |
|
138 if (base::strlcpy(top_dir, from_path.value().c_str(), |
|
139 arraysize(top_dir)) >= arraysize(top_dir)) { |
|
140 return false; |
|
141 } |
|
142 |
|
143 #ifdef ANDROID |
|
144 // XXX Need ftsless impl for bionic |
|
145 return false; |
|
146 #else |
|
147 char* dir_list[] = { top_dir, NULL }; |
|
148 FTS* fts = fts_open(dir_list, FTS_PHYSICAL | FTS_NOSTAT, NULL); |
|
149 if (!fts) { |
|
150 CHROMIUM_LOG(ERROR) << "fts_open failed: " << strerror(errno); |
|
151 return false; |
|
152 } |
|
153 |
|
154 int error = 0; |
|
155 FTSENT* ent; |
|
156 while (!error && (ent = fts_read(fts)) != NULL) { |
|
157 // ent->fts_path is the source path, including from_path, so paste |
|
158 // the suffix after from_path onto to_path to create the target_path. |
|
159 std::string suffix(&ent->fts_path[from_path.value().size()]); |
|
160 // Strip the leading '/' (if any). |
|
161 if (!suffix.empty()) { |
|
162 DCHECK_EQ('/', suffix[0]); |
|
163 suffix.erase(0, 1); |
|
164 } |
|
165 const FilePath target_path = to_path.Append(suffix); |
|
166 switch (ent->fts_info) { |
|
167 case FTS_D: // Preorder directory. |
|
168 // If we encounter a subdirectory in a non-recursive copy, prune it |
|
169 // from the traversal. |
|
170 if (!recursive && ent->fts_level > 0) { |
|
171 if (fts_set(fts, ent, FTS_SKIP) != 0) |
|
172 error = errno; |
|
173 continue; |
|
174 } |
|
175 |
|
176 // Try creating the target dir, continuing on it if it exists already. |
|
177 // Rely on the user's umask to produce correct permissions. |
|
178 if (mkdir(target_path.value().c_str(), 0777) != 0) { |
|
179 if (errno != EEXIST) |
|
180 error = errno; |
|
181 } |
|
182 break; |
|
183 case FTS_F: // Regular file. |
|
184 case FTS_NSOK: // File, no stat info requested. |
|
185 errno = 0; |
|
186 if (!CopyFile(FilePath(ent->fts_path), target_path)) |
|
187 error = errno ? errno : EINVAL; |
|
188 break; |
|
189 case FTS_DP: // Postorder directory. |
|
190 case FTS_DOT: // "." or ".." |
|
191 // Skip it. |
|
192 continue; |
|
193 case FTS_DC: // Directory causing a cycle. |
|
194 // Skip this branch. |
|
195 if (fts_set(fts, ent, FTS_SKIP) != 0) |
|
196 error = errno; |
|
197 break; |
|
198 case FTS_DNR: // Directory cannot be read. |
|
199 case FTS_ERR: // Error. |
|
200 case FTS_NS: // Stat failed. |
|
201 // Abort with the error. |
|
202 error = ent->fts_errno; |
|
203 break; |
|
204 case FTS_SL: // Symlink. |
|
205 case FTS_SLNONE: // Symlink with broken target. |
|
206 CHROMIUM_LOG(WARNING) << "CopyDirectory() skipping symbolic link: " << |
|
207 ent->fts_path; |
|
208 continue; |
|
209 case FTS_DEFAULT: // Some other sort of file. |
|
210 CHROMIUM_LOG(WARNING) << "CopyDirectory() skipping file of unknown type: " << |
|
211 ent->fts_path; |
|
212 continue; |
|
213 default: |
|
214 NOTREACHED(); |
|
215 continue; // Hope for the best! |
|
216 } |
|
217 } |
|
218 // fts_read may have returned NULL and set errno to indicate an error. |
|
219 if (!error && errno != 0) |
|
220 error = errno; |
|
221 |
|
222 if (!fts_close(fts)) { |
|
223 // If we already have an error, let's use that error instead of the error |
|
224 // fts_close set. |
|
225 if (!error) |
|
226 error = errno; |
|
227 } |
|
228 |
|
229 if (error) { |
|
230 CHROMIUM_LOG(ERROR) << "CopyDirectory(): " << strerror(error); |
|
231 return false; |
|
232 } |
|
233 return true; |
|
234 #endif |
|
235 } |
|
236 |
|
237 bool PathExists(const FilePath& path) { |
|
238 struct stat file_info; |
|
239 return (stat(path.value().c_str(), &file_info) == 0); |
|
240 } |
|
241 |
|
242 bool PathIsWritable(const FilePath& path) { |
|
243 FilePath test_path(path); |
|
244 struct stat file_info; |
|
245 if (stat(test_path.value().c_str(), &file_info) != 0) { |
|
246 // If the path doesn't exist, test the parent dir. |
|
247 test_path = test_path.DirName(); |
|
248 // If the parent dir doesn't exist, then return false (the path is not |
|
249 // directly writable). |
|
250 if (stat(test_path.value().c_str(), &file_info) != 0) |
|
251 return false; |
|
252 } |
|
253 if (S_IWOTH & file_info.st_mode) |
|
254 return true; |
|
255 if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode)) |
|
256 return true; |
|
257 if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode)) |
|
258 return true; |
|
259 return false; |
|
260 } |
|
261 |
|
262 bool DirectoryExists(const FilePath& path) { |
|
263 struct stat file_info; |
|
264 if (stat(path.value().c_str(), &file_info) == 0) |
|
265 return S_ISDIR(file_info.st_mode); |
|
266 return false; |
|
267 } |
|
268 |
|
269 bool ReadFromFD(int fd, char* buffer, size_t bytes) { |
|
270 size_t total_read = 0; |
|
271 while (total_read < bytes) { |
|
272 ssize_t bytes_read = |
|
273 HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read)); |
|
274 if (bytes_read <= 0) |
|
275 break; |
|
276 total_read += bytes_read; |
|
277 } |
|
278 return total_read == bytes; |
|
279 } |
|
280 |
|
281 // Creates and opens a temporary file in |directory|, returning the |
|
282 // file descriptor. |path| is set to the temporary file path. |
|
283 // Note TODO(erikkay) comment in header for BlahFileName() calls; the |
|
284 // intent is to rename these files BlahFile() (since they create |
|
285 // files, not filenames). This function does NOT unlink() the file. |
|
286 int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) { |
|
287 *path = directory.Append(kTempFileName); |
|
288 const std::string& tmpdir_string = path->value(); |
|
289 // this should be OK since mkstemp just replaces characters in place |
|
290 char* buffer = const_cast<char*>(tmpdir_string.c_str()); |
|
291 |
|
292 return mkstemp(buffer); |
|
293 } |
|
294 |
|
295 bool CreateTemporaryFileName(FilePath* path) { |
|
296 FilePath directory; |
|
297 if (!GetTempDir(&directory)) |
|
298 return false; |
|
299 int fd = CreateAndOpenFdForTemporaryFile(directory, path); |
|
300 if (fd < 0) |
|
301 return false; |
|
302 close(fd); |
|
303 return true; |
|
304 } |
|
305 |
|
306 FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) { |
|
307 FilePath directory; |
|
308 if (!GetShmemTempDir(&directory)) |
|
309 return NULL; |
|
310 |
|
311 return CreateAndOpenTemporaryFileInDir(directory, path); |
|
312 } |
|
313 |
|
314 FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { |
|
315 int fd = CreateAndOpenFdForTemporaryFile(dir, path); |
|
316 if (fd < 0) |
|
317 return NULL; |
|
318 |
|
319 return fdopen(fd, "a+"); |
|
320 } |
|
321 |
|
322 bool CreateTemporaryFileNameInDir(const std::wstring& dir, |
|
323 std::wstring* temp_file) { |
|
324 // Not implemented yet. |
|
325 NOTREACHED(); |
|
326 return false; |
|
327 } |
|
328 |
|
329 bool CreateNewTempDirectory(const FilePath::StringType& prefix, |
|
330 FilePath* new_temp_path) { |
|
331 FilePath tmpdir; |
|
332 if (!GetTempDir(&tmpdir)) |
|
333 return false; |
|
334 tmpdir = tmpdir.Append(kTempFileName); |
|
335 std::string tmpdir_string = tmpdir.value(); |
|
336 #ifdef ANDROID |
|
337 char* dtemp = NULL; |
|
338 #else |
|
339 // this should be OK since mkdtemp just replaces characters in place |
|
340 char* buffer = const_cast<char*>(tmpdir_string.c_str()); |
|
341 char* dtemp = mkdtemp(buffer); |
|
342 #endif |
|
343 if (!dtemp) |
|
344 return false; |
|
345 *new_temp_path = FilePath(dtemp); |
|
346 return true; |
|
347 } |
|
348 |
|
349 bool CreateDirectory(const FilePath& full_path) { |
|
350 std::vector<FilePath> subpaths; |
|
351 |
|
352 // Collect a list of all parent directories. |
|
353 FilePath last_path = full_path; |
|
354 subpaths.push_back(full_path); |
|
355 for (FilePath path = full_path.DirName(); |
|
356 path.value() != last_path.value(); path = path.DirName()) { |
|
357 subpaths.push_back(path); |
|
358 last_path = path; |
|
359 } |
|
360 |
|
361 // Iterate through the parents and create the missing ones. |
|
362 for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin(); |
|
363 i != subpaths.rend(); ++i) { |
|
364 if (!DirectoryExists(*i)) { |
|
365 if (mkdir(i->value().c_str(), 0777) != 0) |
|
366 return false; |
|
367 } |
|
368 } |
|
369 return true; |
|
370 } |
|
371 |
|
372 bool GetFileInfo(const FilePath& file_path, FileInfo* results) { |
|
373 struct stat file_info; |
|
374 if (stat(file_path.value().c_str(), &file_info) != 0) |
|
375 return false; |
|
376 results->is_directory = S_ISDIR(file_info.st_mode); |
|
377 results->size = file_info.st_size; |
|
378 return true; |
|
379 } |
|
380 |
|
381 FILE* OpenFile(const std::string& filename, const char* mode) { |
|
382 return OpenFile(FilePath(filename), mode); |
|
383 } |
|
384 |
|
385 FILE* OpenFile(const FilePath& filename, const char* mode) { |
|
386 return fopen(filename.value().c_str(), mode); |
|
387 } |
|
388 |
|
389 int ReadFile(const FilePath& filename, char* data, int size) { |
|
390 int fd = open(filename.value().c_str(), O_RDONLY); |
|
391 if (fd < 0) |
|
392 return -1; |
|
393 |
|
394 int ret_value = HANDLE_EINTR(read(fd, data, size)); |
|
395 HANDLE_EINTR(close(fd)); |
|
396 return ret_value; |
|
397 } |
|
398 |
|
399 int WriteFile(const FilePath& filename, const char* data, int size) { |
|
400 int fd = creat(filename.value().c_str(), 0666); |
|
401 if (fd < 0) |
|
402 return -1; |
|
403 |
|
404 // Allow for partial writes |
|
405 ssize_t bytes_written_total = 0; |
|
406 do { |
|
407 ssize_t bytes_written_partial = |
|
408 HANDLE_EINTR(write(fd, data + bytes_written_total, |
|
409 size - bytes_written_total)); |
|
410 if (bytes_written_partial < 0) { |
|
411 HANDLE_EINTR(close(fd)); |
|
412 return -1; |
|
413 } |
|
414 bytes_written_total += bytes_written_partial; |
|
415 } while (bytes_written_total < size); |
|
416 |
|
417 HANDLE_EINTR(close(fd)); |
|
418 return bytes_written_total; |
|
419 } |
|
420 |
|
421 // Gets the current working directory for the process. |
|
422 bool GetCurrentDirectory(FilePath* dir) { |
|
423 char system_buffer[PATH_MAX] = ""; |
|
424 if (!getcwd(system_buffer, sizeof(system_buffer))) { |
|
425 NOTREACHED(); |
|
426 return false; |
|
427 } |
|
428 *dir = FilePath(system_buffer); |
|
429 return true; |
|
430 } |
|
431 |
|
432 // Sets the current working directory for the process. |
|
433 bool SetCurrentDirectory(const FilePath& path) { |
|
434 int ret = chdir(path.value().c_str()); |
|
435 return !ret; |
|
436 } |
|
437 |
|
438 #if !defined(OS_MACOSX) |
|
439 bool GetTempDir(FilePath* path) { |
|
440 const char* tmp = getenv("TMPDIR"); |
|
441 if (tmp) |
|
442 *path = FilePath(tmp); |
|
443 else |
|
444 *path = FilePath("/tmp"); |
|
445 return true; |
|
446 } |
|
447 |
|
448 bool GetShmemTempDir(FilePath* path) { |
|
449 #if defined(OS_LINUX) && !defined(ANDROID) |
|
450 *path = FilePath("/dev/shm"); |
|
451 return true; |
|
452 #else |
|
453 return GetTempDir(path); |
|
454 #endif |
|
455 } |
|
456 |
|
457 bool CopyFile(const FilePath& from_path, const FilePath& to_path) { |
|
458 int infile = open(from_path.value().c_str(), O_RDONLY); |
|
459 if (infile < 0) |
|
460 return false; |
|
461 |
|
462 int outfile = creat(to_path.value().c_str(), 0666); |
|
463 if (outfile < 0) { |
|
464 close(infile); |
|
465 return false; |
|
466 } |
|
467 |
|
468 const size_t kBufferSize = 32768; |
|
469 std::vector<char> buffer(kBufferSize); |
|
470 bool result = true; |
|
471 |
|
472 while (result) { |
|
473 ssize_t bytes_read = HANDLE_EINTR(read(infile, &buffer[0], buffer.size())); |
|
474 if (bytes_read < 0) { |
|
475 result = false; |
|
476 break; |
|
477 } |
|
478 if (bytes_read == 0) |
|
479 break; |
|
480 // Allow for partial writes |
|
481 ssize_t bytes_written_per_read = 0; |
|
482 do { |
|
483 ssize_t bytes_written_partial = HANDLE_EINTR(write( |
|
484 outfile, |
|
485 &buffer[bytes_written_per_read], |
|
486 bytes_read - bytes_written_per_read)); |
|
487 if (bytes_written_partial < 0) { |
|
488 result = false; |
|
489 break; |
|
490 } |
|
491 bytes_written_per_read += bytes_written_partial; |
|
492 } while (bytes_written_per_read < bytes_read); |
|
493 } |
|
494 |
|
495 if (HANDLE_EINTR(close(infile)) < 0) |
|
496 result = false; |
|
497 if (HANDLE_EINTR(close(outfile)) < 0) |
|
498 result = false; |
|
499 |
|
500 return result; |
|
501 } |
|
502 #endif // !defined(OS_MACOSX) |
|
503 |
|
504 } // namespace file_util |