test_file_util_posix.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright (c) 2012 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/test/test_file_util.h" 6 7#include <errno.h> 8#include <fcntl.h> 9#include <sys/stat.h> 10#include <sys/types.h> 11 12#include <string> 13 14#include "base/file_util.h" 15#include "base/files/file_path.h" 16#include "base/logging.h" 17#include "base/string_util.h" 18#include "base/utf_string_conversions.h" 19 20namespace file_util { 21 22namespace { 23 24// Deny |permission| on the file |path|. 25bool DenyFilePermission(const base::FilePath& path, mode_t permission) { 26 struct stat stat_buf; 27 if (stat(path.value().c_str(), &stat_buf) != 0) 28 return false; 29 stat_buf.st_mode &= ~permission; 30 31 int rv = HANDLE_EINTR(chmod(path.value().c_str(), stat_buf.st_mode)); 32 return rv == 0; 33} 34 35// Gets a blob indicating the permission information for |path|. 36// |length| is the length of the blob. Zero on failure. 37// Returns the blob pointer, or NULL on failure. 38void* GetPermissionInfo(const base::FilePath& path, size_t* length) { 39 DCHECK(length); 40 *length = 0; 41 42 struct stat stat_buf; 43 if (stat(path.value().c_str(), &stat_buf) != 0) 44 return NULL; 45 46 *length = sizeof(mode_t); 47 mode_t* mode = new mode_t; 48 *mode = stat_buf.st_mode & ~S_IFMT; // Filter out file/path kind. 49 50 return mode; 51} 52 53// Restores the permission information for |path|, given the blob retrieved 54// using |GetPermissionInfo()|. 55// |info| is the pointer to the blob. 56// |length| is the length of the blob. 57// Either |info| or |length| may be NULL/0, in which case nothing happens. 58bool RestorePermissionInfo(const base::FilePath& path, 59 void* info, size_t length) { 60 if (!info || (length == 0)) 61 return false; 62 63 DCHECK_EQ(sizeof(mode_t), length); 64 mode_t* mode = reinterpret_cast<mode_t*>(info); 65 66 int rv = HANDLE_EINTR(chmod(path.value().c_str(), *mode)); 67 68 delete mode; 69 70 return rv == 0; 71} 72 73} // namespace 74 75bool DieFileDie(const base::FilePath& file, bool recurse) { 76 // There is no need to workaround Windows problems on POSIX. 77 // Just pass-through. 78 return file_util::Delete(file, recurse); 79} 80 81// Mostly a verbatim copy of CopyDirectory 82bool CopyRecursiveDirNoCache(const base::FilePath& source_dir, 83 const base::FilePath& dest_dir) { 84 char top_dir[PATH_MAX]; 85 if (base::strlcpy(top_dir, source_dir.value().c_str(), 86 arraysize(top_dir)) >= arraysize(top_dir)) { 87 return false; 88 } 89 90 // This function does not properly handle destinations within the source 91 base::FilePath real_to_path = dest_dir; 92 if (PathExists(real_to_path)) { 93 if (!AbsolutePath(&real_to_path)) 94 return false; 95 } else { 96 real_to_path = real_to_path.DirName(); 97 if (!AbsolutePath(&real_to_path)) 98 return false; 99 } 100 if (real_to_path.value().compare(0, source_dir.value().size(), 101 source_dir.value()) == 0) 102 return false; 103 104 bool success = true; 105 int traverse_type = FileEnumerator::FILES | 106 FileEnumerator::SHOW_SYM_LINKS | FileEnumerator::DIRECTORIES; 107 FileEnumerator traversal(source_dir, true, traverse_type); 108 109 // dest_dir may not exist yet, start the loop with dest_dir 110 FileEnumerator::FindInfo info; 111 base::FilePath current = source_dir; 112 if (stat(source_dir.value().c_str(), &info.stat) < 0) { 113 DLOG(ERROR) << "CopyRecursiveDirNoCache() couldn't stat source directory: " 114 << source_dir.value() << " errno = " << errno; 115 success = false; 116 } 117 118 while (success && !current.empty()) { 119 // |current| is the source path, including source_dir, so paste 120 // the suffix after source_dir onto dest_dir to create the target_path. 121 std::string suffix(¤t.value().c_str()[source_dir.value().size()]); 122 // Strip the leading '/' (if any). 123 if (!suffix.empty()) { 124 DCHECK_EQ('/', suffix[0]); 125 suffix.erase(0, 1); 126 } 127 const base::FilePath target_path = dest_dir.Append(suffix); 128 129 if (S_ISDIR(info.stat.st_mode)) { 130 if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 && 131 errno != EEXIST) { 132 DLOG(ERROR) << "CopyRecursiveDirNoCache() couldn't create directory: " 133 << target_path.value() << " errno = " << errno; 134 success = false; 135 } 136 } else if (S_ISREG(info.stat.st_mode)) { 137 if (CopyFile(current, target_path)) { 138 success = EvictFileFromSystemCache(target_path); 139 DCHECK(success); 140 } else { 141 DLOG(ERROR) << "CopyRecursiveDirNoCache() couldn't create file: " 142 << target_path.value(); 143 success = false; 144 } 145 } else { 146 DLOG(WARNING) << "CopyRecursiveDirNoCache() skipping non-regular file: " 147 << current.value(); 148 } 149 150 current = traversal.Next(); 151 traversal.GetFindInfo(&info); 152 } 153 154 return success; 155} 156 157#if !defined(OS_LINUX) && !defined(OS_MACOSX) 158bool EvictFileFromSystemCache(const base::FilePath& file) { 159 // There doesn't seem to be a POSIX way to cool the disk cache. 160 NOTIMPLEMENTED(); 161 return false; 162} 163#endif 164 165std::wstring FilePathAsWString(const base::FilePath& path) { 166 return UTF8ToWide(path.value()); 167} 168base::FilePath WStringAsFilePath(const std::wstring& path) { 169 return base::FilePath(WideToUTF8(path)); 170} 171 172bool MakeFileUnreadable(const base::FilePath& path) { 173 return DenyFilePermission(path, S_IRUSR | S_IRGRP | S_IROTH); 174} 175 176bool MakeFileUnwritable(const base::FilePath& path) { 177 return DenyFilePermission(path, S_IWUSR | S_IWGRP | S_IWOTH); 178} 179 180PermissionRestorer::PermissionRestorer(const base::FilePath& path) 181 : path_(path), info_(NULL), length_(0) { 182 info_ = GetPermissionInfo(path_, &length_); 183 DCHECK(info_ != NULL); 184 DCHECK_NE(0u, length_); 185} 186 187PermissionRestorer::~PermissionRestorer() { 188 if (!RestorePermissionInfo(path_, info_, length_)) 189 NOTREACHED(); 190} 191 192} // namespace file_util 193