ipc/chromium/src/base/file_util_posix.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial