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(&current.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