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