1 //===-- FileSystem.cpp ----------------------------------------------------===//
2 //
3 // This source file is part of the Swift.org open source project
4 //
5 // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
6 // Licensed under Apache License v2.0 with Runtime Library Exception
7 //
8 // See http://swift.org/LICENSE.txt for license information
9 // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "llbuild/Basic/FileSystem.h"
14 #include "llbuild/Basic/PlatformUtility.h"
15 #include "llbuild/Basic/Stat.h"
16
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/Support/FileSystem.h"
19 #include "llvm/Support/Path.h"
20 #include "llvm/Support/MemoryBuffer.h"
21
22 #include <cassert>
23 #include <cstring>
24
25 // Cribbed from llvm, where it's been since removed.
26 namespace {
27 using namespace std;
28 using namespace llvm;
29 using namespace llvm::sys::fs;
30
fillStatus(int StatRet,const llbuild::basic::sys::StatStruct & Status,file_status & Result)31 static std::error_code fillStatus(int StatRet, const llbuild::basic::sys::StatStruct &Status,
32 file_status &Result) {
33 if (StatRet != 0) {
34 std::error_code ec(errno, std::generic_category());
35 if (ec == errc::no_such_file_or_directory)
36 Result = file_status(file_type::file_not_found);
37 else
38 Result = file_status(file_type::status_error);
39 return ec;
40 }
41
42 file_type Type = file_type::type_unknown;
43
44 if (S_ISDIR(Status.st_mode))
45 Type = file_type::directory_file;
46 else if (S_ISREG(Status.st_mode))
47 Type = file_type::regular_file;
48 else if (S_ISBLK(Status.st_mode))
49 Type = file_type::block_file;
50 else if (S_ISCHR(Status.st_mode))
51 Type = file_type::character_file;
52 else if (S_ISFIFO(Status.st_mode))
53 Type = file_type::fifo_file;
54 else if (S_ISSOCK(Status.st_mode))
55 Type = file_type::socket_file;
56 else if (S_ISLNK(Status.st_mode))
57 Type = file_type::symlink_file;
58
59 #if defined(_WIN32)
60 Result = file_status(Type);
61 #else
62 perms Perms = static_cast<perms>(Status.st_mode);
63 Result =
64 file_status(Type, Perms, Status.st_dev, Status.st_ino, Status.st_mtime,
65 Status.st_uid, Status.st_gid, Status.st_size);
66 #endif
67
68 return std::error_code();
69 }
70
link_status(const Twine & Path,file_status & Result)71 std::error_code link_status(const Twine &Path, file_status &Result) {
72 SmallString<128> PathStorage;
73 StringRef P = Path.toNullTerminatedStringRef(PathStorage);
74
75 llbuild::basic::sys::StatStruct Status;
76 int StatRet = llbuild::basic::sys::lstat(P.begin(), &Status);
77 return fillStatus(StatRet, Status, Result);
78 }
79
_remove_all_r(StringRef path,file_type ft,uint32_t & count)80 error_code _remove_all_r(StringRef path, file_type ft, uint32_t &count) {
81 if (ft == file_type::directory_file) {
82 error_code ec;
83 directory_iterator i(path, ec);
84 if (ec)
85 return ec;
86
87 for (directory_iterator e; i != e; i.increment(ec)) {
88 if (ec)
89 return ec;
90
91 file_status st;
92
93 if (error_code ec = link_status(i->path(), st))
94 return ec;
95
96 if (error_code ec = _remove_all_r(i->path(), st.type(), count))
97 return ec;
98 }
99
100 if (error_code ec = remove(path, false))
101 return ec;
102
103 ++count; // Include the directory itself in the items removed.
104 } else {
105 if (error_code ec = remove(path, false))
106 return ec;
107
108 ++count;
109 }
110
111 return error_code();
112 }
113 }
114
115 using namespace llbuild;
116 using namespace llbuild::basic;
117
~FileSystem()118 FileSystem::~FileSystem() {}
119
createDirectories(const std::string & path)120 bool FileSystem::createDirectories(const std::string& path) {
121 // Attempt to create the final directory first, to optimize for the common
122 // case where we don't need to recurse.
123 if (createDirectory(path))
124 return true;
125
126 // If that failed, attempt to create the parent.
127 StringRef parent = llvm::sys::path::parent_path(path);
128 if (parent.empty())
129 return false;
130 return createDirectories(parent) && createDirectory(path);
131 }
132
133 namespace {
134
135 class LocalFileSystem : public FileSystem {
136 public:
LocalFileSystem()137 LocalFileSystem() {}
138
139 virtual bool
createDirectory(const std::string & path)140 createDirectory(const std::string& path) override {
141 if (!llbuild::basic::sys::mkdir(path.c_str())) {
142 if (errno != EEXIST) {
143 return false;
144 }
145 }
146 return true;
147 }
148
149 virtual std::unique_ptr<llvm::MemoryBuffer>
getFileContents(const std::string & path)150 getFileContents(const std::string& path) override {
151 auto result = llvm::MemoryBuffer::getFile(path);
152 if (result.getError()) {
153 return nullptr;
154 }
155 return std::unique_ptr<llvm::MemoryBuffer>(result->release());
156 }
157
rm_tree(const char * path)158 bool rm_tree(const char* path) {
159 uint32_t count = 0;
160 return !_remove_all_r(path, file_type::directory_file, count);
161 }
162
remove(const std::string & path)163 virtual bool remove(const std::string& path) override {
164 // Assume `path` is a regular file.
165 if (llbuild::basic::sys::unlink(path.c_str()) == 0) {
166 return true;
167 }
168
169 // Error can't be that `path` is actually a directory (on Linux `EISDIR` will be returned since 2.1.132).
170 if (errno != EPERM && errno != EISDIR) {
171 return false;
172 }
173
174 // Check if `path` is a directory.
175 llbuild::basic::sys::StatStruct statbuf;
176 if (llbuild::basic::sys::lstat(path.c_str(), &statbuf) != 0) {
177 return false;
178 }
179
180 if (S_ISDIR(statbuf.st_mode)) {
181 if (llbuild::basic::sys::rmdir(path.c_str()) == 0) {
182 return true;
183 } else {
184 return rm_tree(path.c_str());
185 }
186 }
187
188 return false;
189 }
190
getFileInfo(const std::string & path)191 virtual FileInfo getFileInfo(const std::string& path) override {
192 return FileInfo::getInfoForPath(path);
193 }
194
getLinkInfo(const std::string & path)195 virtual FileInfo getLinkInfo(const std::string& path) override {
196 return FileInfo::getInfoForPath(path, /*isLink:*/ true);
197 }
198 };
199
200 }
201
createLocalFileSystem()202 std::unique_ptr<FileSystem> basic::createLocalFileSystem() {
203 return llvm::make_unique<LocalFileSystem>();
204 }
205