SPH
FileSystem.cpp
Go to the documentation of this file.
1 #include "io/FileSystem.h"
3 #include <fstream>
4 #include <sstream>
5 #include <sys/file.h>
6 #include <sys/stat.h>
7 #include <sys/types.h>
8 #ifdef SPH_USE_STD_EXPERIMENTAL
9 #include <experimental/filesystem>
10 #endif
11 
13 
14 std::string FileSystem::readFile(const Path& path) {
15  std::ifstream t(path.native().c_str());
16  std::stringstream buffer;
17  buffer << t.rdbuf();
18  return buffer.str();
19 }
20 
21 bool FileSystem::pathExists(const Path& path) {
22  struct stat buffer;
23  if (path.empty()) {
24  return false;
25  }
26  return (stat(path.native().c_str(), &buffer) == 0);
27 }
28 
30  std::ifstream ifs(path.native(), std::ifstream::ate | std::ifstream::binary);
31  SPH_ASSERT(ifs);
32  return ifs.tellg();
33 }
34 
35 bool FileSystem::isPathWritable(const Path& path) {
36  return access(path.native().c_str(), W_OK) == 0;
37 }
38 
40  const char* homeDir = getenv("HOME");
41  if (homeDir != nullptr) {
42  return Path(homeDir);
43  } else {
44  return makeUnexpected<Path>("Cannot obtain home directory");
45  }
46 }
47 
48 Path FileSystem::getAbsolutePath(const Path& relativePath) {
49  char realPath[PATH_MAX];
50  realpath(relativePath.native().c_str(), realPath);
51  return Path(realPath);
52 }
53 
55  if (path.empty()) {
56  return makeUnexpected<PathType>("Path is empty");
57  }
58  struct stat buffer;
59  if (stat(path.native().c_str(), &buffer) != 0) {
61  return makeUnexpected<PathType>("Cannot retrieve type of the path");
62  }
63  if (S_ISREG(buffer.st_mode)) {
64  return PathType::FILE;
65  }
66  if (S_ISDIR(buffer.st_mode)) {
67  return PathType::DIRECTORY;
68  }
69  if (S_ISLNK(buffer.st_mode)) {
70  return PathType::SYMLINK;
71  }
72  return PathType::OTHER;
73 }
74 
75 
76 static Outcome createSingleDirectory(const Path& path, const Flags<FileSystem::CreateDirectoryFlag> flags) {
77  SPH_ASSERT(!path.empty());
78  const bool result = mkdir(path.native().c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0;
79  if (!result) {
80  switch (errno) {
81  case EACCES:
82  return makeFailed(
83  "Search permission is denied on a component of the path prefix, or write permission is "
84  "denied on the parent directory of the directory to be created.");
85  case EEXIST:
87  return SUCCESS;
88  } else {
89  return makeFailed("The named file exists.");
90  }
91  case ELOOP:
92  return makeFailed(
93  "A loop exists in symbolic links encountered during resolution of the path argument.");
94  case EMLINK:
95  return makeFailed("The link count of the parent directory would exceed {LINK_MAX}.");
96  case ENAMETOOLONG:
97  return makeFailed(
98  "The length of the path argument exceeds {PATH_MAX} or a pathname component is longer "
99  "than {NAME_MAX}.");
100  case ENOENT:
101  return makeFailed(
102  "A component of the path prefix specified by path does not name an existing directory "
103  "or path is an empty string.");
104  case ENOSPC:
105  return makeFailed(
106  "The file system does not contain enough space to hold the contents of the new "
107  "directory or to extend the parent directory of the new directory.");
108  case ENOTDIR:
109  return makeFailed("A component of the path prefix is not a directory.");
110  case EROFS:
111  return makeFailed("The parent directory resides on a read-only file system.");
112  default:
113  return makeFailed("Unknown error");
114  }
115  }
116  return SUCCESS;
117 }
118 
120 #ifdef SPH_USE_STD_EXPERIMENTAL
121  return (Outcome)std::experimental::filesystem::create_directories(path);
122 #else
123  if (path.empty()) {
124  return SUCCESS;
125  }
126  const Path parentPath = path.parentPath();
127  if (!parentPath.empty() && !pathExists(parentPath)) {
128  const Outcome result = createDirectory(parentPath, flags);
129  if (!result) {
130  return result;
131  }
132  }
133  return createSingleDirectory(path, flags);
134 #endif
135 }
136 
138 #ifdef SPH_USE_STD_EXPERIMENTAL
139  try {
140  std::experimental::filesystem::remove_all(path);
141  } catch (std::experimental::filesystem::filesystem_error& e) {
142  return e.what();
143  }
144  return SUCCESS;
145 #else
146  if (path.empty()) {
147  return makeFailed("Attempting to remove an empty path");
148  }
149  if (path.isRoot()) {
150  return makeFailed("Attemping to remove root. Pls, don't ...");
151  }
152  if (!pathExists(path)) {
153  return makeFailed("Attemping to remove nonexisting path");
154  }
155  const Expected<PathType> type = pathType(path);
156  if (!type) {
157  return makeFailed(type.error());
158  }
159  if (type.value() == PathType::DIRECTORY && flags.has(RemovePathFlag::RECURSIVE)) {
160  for (Path child : iterateDirectory(path)) {
161  Outcome result = removePath(path / child, flags);
162  if (!result) {
163  return result;
164  }
165  }
166  }
167 
168  const bool result = (remove(path.native().c_str()) == 0);
169  if (!result) {
170  switch (errno) {
171  case EACCES:
172  return makeFailed(
173  "Write access to the directory containing pathname was not allowed, or one of the "
174  "directories in the path prefix of pathname did not allow search permission.");
175  case EBUSY:
176  return makeFailed(
177  "Path " + path.native() +
178  "is currently in use by the system or some process that prevents its removal.On "
179  "Linux this means pathname is currently used as a mount point or is the root directory of "
180  "the calling process.");
181  case EFAULT:
182  return makeFailed("Path " + path.native() + " points outside your accessible address space.");
183  case EINVAL:
184  return makeFailed("Path " + path.native() + " has . as last component.");
185  case ELOOP:
186  return makeFailed("Too many symbolic links were encountered in resolving path " + path.native());
187  case ENAMETOOLONG:
188  return makeFailed("Path " + path.native() + " was too long.");
189  case ENOENT:
190  return makeFailed("A directory component in path " + path.native() +
191  " does not exist or is a dangling symbolic link.");
192  case ENOMEM:
193  return makeFailed("Insufficient kernel memory was available.");
194  case ENOTDIR:
195  return makeFailed(
196  "Path " + path.native() +
197  " or a component used as a directory in pathname, is not, in fact, a directory.");
198  case ENOTEMPTY:
199  return makeFailed(
200  "Path " + path.native() +
201  " contains entries other than . and ..; or, pathname has .. as its final component.");
202  case EPERM:
203  return makeFailed(
204  "The directory containing path " + path.native() +
205  " has the sticky bit(S_ISVTX) set and the process's "
206  "effective user ID is neither the user ID of the file to be deleted nor that of the "
207  "directory containing it, and the process is not privileged (Linux: does not have the "
208  "CAP_FOWNER capability).");
209  case EROFS:
210  return makeFailed("Path " + path.native() + " refers to a directory on a read-only file system.");
211  default:
212  return makeFailed("Unknown error for path " + path.native());
213  }
214  }
215  return SUCCESS;
216 #endif
217 }
218 
219 Outcome FileSystem::copyFile(const Path& from, const Path& to) {
220  SPH_ASSERT(pathType(from).valueOr(PathType::OTHER) == PathType::FILE);
221  // there doesn't seem to be any system function for copying, so let's do it by hand
222  std::ifstream ifs(from.native().c_str());
223  if (!ifs) {
224  return makeFailed("Cannon open file " + from.native() + " for reading");
225  }
226  Outcome result = createDirectory(to.parentPath());
227  if (!result) {
228  return result;
229  }
230  std::ofstream ofs(to.native());
231  if (!ofs) {
232  return makeFailed("Cannot open file " + to.native() + " for writing");
233  }
234 
236  do {
237  ifs.read(&buffer[0], buffer.size());
238  ofs.write(&buffer[0], ifs.gcount());
239  if (!ofs) {
240  return makeFailed("Failed from copy the file");
241  }
242  } while (ifs);
243  return SUCCESS;
244 }
245 
246 
247 Outcome FileSystem::copyDirectory(const Path& from, const Path& to) {
248  SPH_ASSERT(pathType(from).valueOr(PathType::OTHER) == PathType::DIRECTORY);
249  Outcome result = createDirectory(to);
250  if (!result) {
251  return result;
252  }
253  for (Path path : iterateDirectory(from)) {
254  const PathType type = pathType(from / path).valueOr(PathType::OTHER);
255  switch (type) {
256  case PathType::FILE:
257  result = copyFile(from / path, to / path);
258  break;
259  case PathType::DIRECTORY:
260  result = copyDirectory(from / path, to / path);
261  break;
262  default:
263  // just ignore the rest
264  result = SUCCESS;
265  }
266  if (!result) {
267  return result;
268  }
269  }
270  return SUCCESS;
271 }
272 
274  SPH_ASSERT(pathType(path).valueOr(PathType::OTHER) == PathType::DIRECTORY);
275  chdir(path.native().c_str());
276 }
277 
278 FileSystem::DirectoryIterator::DirectoryIterator(DIR* dir)
279  : dir(dir) {
280  if (dir) {
281  // find first file
282  this->operator++();
283  }
284 }
285 
287  if (!dir) {
288  return *this;
289  }
290  do {
291  entry = readdir(dir);
292  } while (entry && (**this == Path(".") || **this == Path("..")));
293  return *this;
294 }
295 
297  SPH_ASSERT(entry);
298  return Path(entry->d_name);
299 }
300 
302  return (!entry && !other.entry) || entry == other.entry;
303 }
304 
306  // returns false if both are nullptr to end the loop for nonexisting dirs
307  return (entry || other.entry) && entry != other.entry;
308 }
309 
311  SPH_ASSERT(pathType(directory).valueOr(PathType::OTHER) == PathType::DIRECTORY);
312  if (!pathExists(directory)) {
313  dir = nullptr;
314  return;
315  }
316  dir = opendir(directory.native().c_str());
317 }
318 
320  if (dir) {
321  closedir(dir);
322  }
323 }
324 
326  : dir(other.dir) {
327  other.dir = nullptr;
328 }
329 
331  return DirectoryIterator(dir);
332 }
333 
335  return DirectoryIterator(nullptr);
336 }
337 
339  return DirectoryAdapter(directory);
340 }
341 
343  Array<Path> paths;
344  for (Path path : iterateDirectory(directory)) {
345  paths.push(path);
346  }
347  return paths;
348 }
349 
351  handle = open(path.native().c_str(), O_RDONLY);
352  TODO("check that the file exists and was correctly opened");
353 }
354 
356  flock(handle, LOCK_EX | LOCK_NB);
357 }
358 
360  flock(handle, LOCK_UN | LOCK_NB);
361 }
362 
363 bool FileSystem::isFileLocked(const Path& UNUSED(path)) {
365 }
366 
367 
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
#define TODO(x)
Definition: Assert.h:84
#define NOT_IMPLEMENTED
Helper macro marking missing implementation.
Definition: Assert.h:100
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
uint32_t Size
Integral type used to index arrays (by default).
Definition: Globals.h:16
#define UNUSED(x)
Definition: Object.h:37
#define NAMESPACE_SPH_END
Definition: Object.h:12
const SuccessTag SUCCESS
Global constant for successful outcome.
Definition: Outcome.h:141
INLINE Outcome makeFailed(TArgs &&... args)
Constructs failed object with error message.
Definition: Outcome.h:157
Generic dynamically allocated resizable storage.
Definition: Array.h:43
INLINE void push(U &&u)
Adds new element to the end of the array, resizing the array if necessary.
Definition: Array.h:306
Wrapper of type that either contains a value of given type, or an error message.
Definition: Expected.h:25
const Error & error() const
Returns the error message.
Definition: Expected.h:94
Type & value()
Returns the reference to expected value.
Definition: Expected.h:69
Object providing begin and end directory iterator for given directory path.
Definition: FileSystem.h:145
DirectoryIterator begin() const
Returns the directory iterator to the first entry in the directory.
Definition: FileSystem.cpp:330
DirectoryIterator end() const
Returns the directory iterator to the one-past-last entry in the directory.
Definition: FileSystem.cpp:334
DirectoryAdapter(const Path &directory)
Creates the directory adapter for given path.
Definition: FileSystem.cpp:310
Iterator allowing to enumerate files and subdirectories in given directory.
Definition: FileSystem.h:112
Path operator*() const
Return the path of the file. The path is returned relative to the parent directory.
Definition: FileSystem.cpp:296
DirectoryIterator & operator++()
Moves to the next file in the directory.
Definition: FileSystem.cpp:286
bool operator==(const DirectoryIterator &other) const
Checks for equality. Returns true in case both iterators are nullptr.
Definition: FileSystem.cpp:301
bool operator!=(const DirectoryIterator &other) const
Checks for inequality. Returns false in case both iterators are nullptr.
Definition: FileSystem.cpp:305
FileLock(const Path &path)
Definition: FileSystem.cpp:350
Wrapper of an integral value providing functions for reading and modifying individual bits.
Definition: Flags.h:20
constexpr INLINE bool has(const TEnum flag) const
Checks if the object has a given flag.
Definition: Flags.h:77
Object representing a path on a filesystem.
Definition: Path.h:17
std::string native() const
Returns the native version of the path.
Definition: Path.cpp:71
bool isRoot() const
Checks if the object holds root path.
Definition: Path.cpp:27
bool empty() const
Checks if the path is empty.
Definition: Path.cpp:10
Path parentPath() const
Returns the parent directory. If the path is empty or root, return empty path.
Definition: Path.cpp:35
Array with fixed number of allocated elements.
Definition: StaticArray.h:19
INLINE TCounter size() const
Returns the current size of the array (number of constructed elements).
Definition: StaticArray.h:147
bool isPathWritable(const Path &path)
Checks whether the given file is writable.
Definition: FileSystem.cpp:35
bool pathExists(const Path &path)
Checks if a file or directory exists (or more precisely, if a file or directory is accessible).
Definition: FileSystem.cpp:21
@ OTHER
Pipe, socket, ...
@ DIRECTORY
Directory.
bool isFileLocked(const Path &path)
void setWorkingDirectory(const Path &path)
Changes the current working directory.
Definition: FileSystem.cpp:273
Outcome removePath(const Path &path, const Flags< RemovePathFlag > flags=EMPTY_FLAGS)
Definition: FileSystem.cpp:137
Array< Path > getFilesInDirectory(const Path &directory)
Alternatitve to iterateDirectory, returning all files in directory in an array.
Definition: FileSystem.cpp:342
Outcome copyDirectory(const Path &from, const Path &to)
Copies a directory (and all files and subdirectories it contains) to a different path.
Definition: FileSystem.cpp:247
@ ALLOW_EXISTING
If the named directory already exists, function returns SUCCESS instead of error message.
Size fileSize(const Path &path)
Returns the size of a file.
Definition: FileSystem.cpp:29
Outcome createDirectory(const Path &path, const Flags< CreateDirectoryFlag > flags=CreateDirectoryFlag::ALLOW_EXISTING)
Creates a directory with given path. Creates all parent directories as well.
Definition: FileSystem.cpp:119
Outcome copyFile(const Path &from, const Path &to)
Copies a file on given path to a different path.
Definition: FileSystem.cpp:219
DirectoryAdapter iterateDirectory(const Path &directory)
Definition: FileSystem.cpp:338
Expected< PathType > pathType(const Path &path)
Returns the type of the given path, or error message if the function fails.
Definition: FileSystem.cpp:54
std::string readFile(const Path &path)
Reads the whole file into the string.
Definition: FileSystem.cpp:14
Expected< Path > getHomeDirectory()
Returns the home directory of the current user.
Definition: FileSystem.cpp:39
Path getAbsolutePath(const Path &relativePath)
Returns the absolute path to the file.
Definition: FileSystem.cpp:48