libfoedus-core
FOEDUS Core Library
filesystem.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014-2015, Hewlett-Packard Development Company, LP.
3  * This program is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU General Public License as published by the Free
5  * Software Foundation; either version 2 of the License, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11  * more details. You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the Free Software
13  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14  *
15  * HP designates this particular file as subject to the "Classpath" exception
16  * as provided by HP in the LICENSE.txt file that accompanied this code.
17  */
18 #include "foedus/fs/filesystem.hpp"
19 
20 #include <dirent.h>
21 #include <fcntl.h>
22 #include <stdint.h>
23 #include <unistd.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <sys/vfs.h>
27 
28 #include <chrono>
29 #include <cstdio>
30 #include <cstdlib>
31 #include <ctime>
32 #include <iostream>
33 #include <string>
34 #include <vector>
35 
36 #include "foedus/assert_nd.hpp"
37 #include "foedus/engine.hpp"
41 #include "foedus/fs/path.hpp"
42 
43 namespace foedus {
44 namespace fs {
45 FileStatus status(const Path& p) {
46  struct stat path_stat;
47  int ret = ::stat(p.c_str(), &path_stat);
48  if (ret != 0) {
49  // This is quite normal as we use this to check if a file exists. No message for it.
50  if (errno == ENOENT || errno == ENOTDIR) {
51  return FileStatus(kFileNotFound);
52  }
53  return FileStatus(kStatusError);
54  } else if (S_ISDIR(path_stat.st_mode)) {
55  return FileStatus(kDirectoryFile);
56  } else if (S_ISREG(path_stat.st_mode)) {
57  return FileStatus(kRegularFile);
58  }
59  return FileStatus(kTypeUnknown);
60 }
61 
62 Path absolute(const std::string& p) {
63  return Path(p);
64 }
65 
67  Path cur;
68  for (size_t path_max = 128;; path_max *=2) { // loop 'til buffer large enough
69  std::vector<char> buf(path_max);
70  if (::getcwd(&buf[0], path_max) == 0) {
71  ASSERT_ND(errno == ERANGE);
72  } else {
73  cur = std::string(&buf[0]);
74  break;
75  }
76  }
77  return cur;
78 }
79 
81  const char *home = ::getenv("HOME");
82  if (home) {
83  return Path(home);
84  } else {
85  return Path();
86  }
87 }
88 
89 bool create_directories(const Path& p, bool sync) {
90  if (exists(p)) {
91  return true;
92  }
93  if (create_directory(p, sync)) {
94  return true;
95  }
96  // if failed, create parent then try again
97  Path parent = p.parent_path();
98  if (parent.empty()) {
99  return false;
100  }
101  if (!create_directories(parent, sync) && !exists(parent)) {
102  return false;
103  }
104  // now ancestors exist.
105  return create_directory(p, sync);
106 }
107 
108 bool create_directory(const Path& p, bool sync) {
109  int ret = ::mkdir(p.c_str(), S_IRWXU);
110  if (ret != 0) {
111  return false;
112  }
113  if (sync) {
114  return fsync(p, true);
115  } else {
116  return true;
117  }
118 }
119 
120 uint64_t file_size(const Path& p) {
121  struct stat path_stat;
122  int ret = ::stat(p.c_str(), &path_stat);
123  if (ret != 0) {
124  return static_cast<uint64_t>(-1);
125  }
126  if (!S_ISREG(path_stat.st_mode)) {
127  return static_cast<uint64_t>(-1);
128  }
129  return static_cast<uint64_t>(path_stat.st_size);
130 }
131 
132 bool remove(const Path& p) {
133  FileStatus s = status(p);
134  if (!s.exists()) {
135  return 0;
136  } else if (s.is_regular_file()) {
137  return std::remove(p.c_str()) == 0;
138  } else {
139  int ret = ::rmdir(p.c_str());
140  if (ret == 0) {
141  return true;
142  } else {
143  return false;
144  }
145  }
146 }
147 
148 uint64_t remove_all(const Path& p) {
149  uint64_t count = 1;
150  std::vector< Path > child_paths = p.child_paths();
151  for (Path child : child_paths) {
152  count += remove_all(child);
153  }
154  remove(p);
155  return count;
156 }
157 
158 SpaceInfo space(const Path& p) {
159  struct statfs vfs;
160  SpaceInfo info;
161  int ret = ::statfs(p.c_str(), &vfs);
162  if (ret == 0) {
163  info.capacity_ = static_cast<uint64_t>(vfs.f_blocks) * vfs.f_bsize;
164  info.free_ = static_cast<uint64_t>(vfs.f_bfree) * vfs.f_bsize;
165  info.available_ = static_cast<uint64_t>(vfs.f_bavail) * vfs.f_bsize;
166  } else {
167  info.available_ = 0;
168  info.capacity_ = 0;
169  info.free_ = 0;
170  }
171  return info;
172 }
173 
174 std::string unique_name(uint64_t differentiator) {
175  return unique_name("%%%%-%%%%-%%%%-%%%%", differentiator);
176 }
177 std::string unique_name(const std::string& model, uint64_t differentiator) {
178  const char* kHexChars = "0123456789abcdef";
179  uint64_t seed64 = std::chrono::high_resolution_clock::now().time_since_epoch().count();
180  seed64 += ::getpid(); // further use process ID to randomize. may help.
181  seed64 ^= differentiator;
182  uint32_t seed32 = (seed64 >> 32) ^ seed64;
183  std::string s(model);
184  for (size_t i = 0; i < s.size(); ++i) {
185  if (s[i] == '%') { // digit request
186  seed32 = ::rand_r(&seed32);
187  s[i] = kHexChars[seed32 & 0xf]; // convert to hex digit and replace
188  }
189  }
190  return s;
191 }
192 
193 std::ostream& operator<<(std::ostream& o, const SpaceInfo& v) {
194  o << "SpaceInfo: available_=" << v.available_ << ", capacity_=" << v.capacity_
195  << ", free_=" << v.free_;
196  return o;
197 }
198 
199 bool atomic_rename(const Path& old_path, const Path& new_path) {
200  return ::rename(old_path.c_str(), new_path.c_str()) == 0;
201 }
202 
203 bool fsync(const Path& path, bool sync_parent_directory) {
204  int sync_ret;
205  if (!is_directory(path)) {
206  int descriptor = ::open(path.c_str(), O_RDONLY);
207  if (descriptor < 0) {
208  return false;
209  }
210  sync_ret = ::fsync(descriptor);
211  ::close(descriptor);
212  } else {
213  DIR *dir = ::opendir(path.c_str());
214  if (!dir) {
215  return false;
216  }
217  int descriptor = ::dirfd(dir);
218  if (descriptor < 0) {
219  return false;
220  }
221 
222  sync_ret = ::fsync(descriptor);
223  ::closedir(dir);
224  }
225 
226  if (sync_ret != 0) {
227  return false;
228  }
229 
230  if (sync_parent_directory) {
231  return fsync(path.parent_path(), false);
232  } else {
233  return true;
234  }
235 }
236 bool durable_atomic_rename(const Path& old_path, const Path& new_path) {
237  if (!fsync(old_path, false)) {
238  return false;
239  }
240  if (!atomic_rename(old_path, new_path)) {
241  return false;
242  }
243  return fsync(new_path.parent_path(), false);
244 }
245 
246 } // namespace fs
247 } // namespace foedus
bool remove(const Path &p)
Deletes a regular file or an empty directory.
Definition: filesystem.cpp:132
std::ostream & operator<<(std::ostream &o, const DirectIoFile &v)
uint64_t available_
Less than free_.
Definition: filesystem.hpp:115
bool is_directory(const Path &p)
Returns if the file is a directory.
Definition: filesystem.hpp:133
Analogue of boost::filesystem::space_info.
Definition: filesystem.hpp:110
uint64_t remove_all(const Path &p)
Recursively deletes a directory.
Definition: filesystem.cpp:148
Root package of FOEDUS (Fast Optimistic Engine for Data Unification Services).
Definition: assert_nd.hpp:44
Path home_path()
Returns the absolute path of the home directory of the user running this process. ...
Definition: filesystem.cpp:80
FileStatus status(const Path &p)
Returns the status of the file.
Definition: filesystem.cpp:45
Path current_path()
Returns the current working directory.
Definition: filesystem.cpp:66
bool create_directory(const Path &p, bool sync=false)
mkdir.
Definition: filesystem.cpp:108
bool empty() const
Definition: path.hpp:75
std::vector< Path > child_paths() const
Definition: path.cpp:64
Analogue of boost::filesystem::path.
Definition: path.hpp:37
bool durable_atomic_rename(const Path &old_path, const Path &new_path)
fsync() on source file before rename, then fsync() on the parent folder after rename.
Definition: filesystem.cpp:236
Path absolute(const std::string &p)
Returns the absolue path of the specified path.
Definition: filesystem.cpp:62
bool create_directories(const Path &p, bool sync=false)
Recursive mkdir (mkdirs).
Definition: filesystem.cpp:89
bool exists(const Path &p)
Returns if the file exists.
Definition: filesystem.hpp:128
SpaceInfo space(const Path &p)
Returns free space information for the device the file is on.
Definition: filesystem.cpp:158
Path parent_path() const
Definition: path.cpp:55
bool is_regular_file() const
Definition: filesystem.hpp:98
bool rename(const Path &old_path, const Path &new_path)
Just a synonym of atomic_rename() to avoid confusion.
Definition: filesystem.hpp:286
uint64_t file_size(const Path &p)
Returns size of the file.
Definition: filesystem.cpp:120
const char * c_str() const
Definition: path.hpp:64
bool exists() const
Definition: filesystem.hpp:97
std::string unique_name(uint64_t differentiator=0)
Equivalent to unique_path("%%%%-%%%%-%%%%-%%%%").
Definition: filesystem.cpp:174
Analogue of boost::filesystem::file_status.
Definition: filesystem.hpp:89
bool atomic_rename(const Path &old_path, const Path &new_path)
Renames the old file to the new file with the POSIX atomic-rename semantics.
Definition: filesystem.cpp:199
#define ASSERT_ND(x)
A warning-free wrapper macro of assert() that has no performance effect in release mode even when 'x'...
Definition: assert_nd.hpp:72
bool fsync(const Path &path, bool sync_parent_directory=false)
Makes the content and metadata of the file durable all the way up to devices.
Definition: filesystem.cpp:203
uint64_t free_
Less than capacity_.
Definition: filesystem.hpp:113