1/*
2 * Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15#include <aws/core/platform/FileSystem.h>
16
17#include <aws/core/platform/Environment.h>
18#include <aws/core/platform/Platform.h>
19#include <aws/core/utils/DateTime.h>
20#include <aws/core/utils/logging/LogMacros.h>
21#include <aws/core/utils/StringUtils.h>
22#include <aws/core/utils/UUID.h>
23
24#include <unistd.h>
25#include <pwd.h>
26#include <sys/stat.h>
27#include <dirent.h>
28#include <errno.h>
29#include <climits>
30
31#include <cassert>
32
33namespace Aws
34{
35namespace FileSystem
36{
37
38static const char* FILE_SYSTEM_UTILS_LOG_TAG = "FileSystemUtils";
39
40 class PosixDirectory : public Directory
41 {
42 public:
43 PosixDirectory(const Aws::String& path, const Aws::String& relativePath) : Directory(path, relativePath), m_dir(nullptr)
44 {
45 m_dir = opendir(m_directoryEntry.path.c_str());
46 AWS_LOGSTREAM_TRACE(FILE_SYSTEM_UTILS_LOG_TAG, "Entering directory " << m_directoryEntry.path);
47
48 if(m_dir)
49 {
50 AWS_LOGSTREAM_TRACE(FILE_SYSTEM_UTILS_LOG_TAG, "Successfully opened directory " << m_directoryEntry.path);
51 m_directoryEntry.fileType = FileType::Directory;
52 }
53 else
54 {
55 AWS_LOGSTREAM_ERROR(FILE_SYSTEM_UTILS_LOG_TAG, "Could not load directory " << m_directoryEntry.path << " with error code " << errno);
56 }
57 }
58
59 ~PosixDirectory()
60 {
61 if (m_dir)
62 {
63 closedir(m_dir);
64 }
65 }
66
67 operator bool() const override { return m_directoryEntry.operator bool() && m_dir != nullptr; }
68
69 DirectoryEntry Next() override
70 {
71 assert(m_dir);
72 DirectoryEntry entry;
73
74 dirent* dirEntry;
75 bool invalidEntry(true);
76
77 while(invalidEntry)
78 {
79 if ((dirEntry = readdir(m_dir)))
80 {
81 Aws::String entryName = dirEntry->d_name;
82 if(entryName != ".." && entryName != ".")
83 {
84 entry = ParseFileInfo(dirEntry, true);
85 invalidEntry = false;
86 }
87 else
88 {
89 AWS_LOGSTREAM_DEBUG(FILE_SYSTEM_UTILS_LOG_TAG, "skipping . or ..");
90 }
91 }
92 else
93 {
94 break;
95 }
96 }
97
98 return entry;
99 }
100
101 private:
102 DirectoryEntry ParseFileInfo(dirent* dirEnt, bool computePath)
103 {
104 DirectoryEntry entry;
105
106 if(computePath)
107 {
108 Aws::StringStream ss;
109 ss << m_directoryEntry.path << PATH_DELIM << dirEnt->d_name;
110 entry.path = ss.str();
111
112 ss.str("");
113 if(m_directoryEntry.relativePath.empty())
114 {
115 ss << dirEnt->d_name;
116 }
117 else
118 {
119 ss << m_directoryEntry.relativePath << PATH_DELIM << dirEnt->d_name;
120 }
121 entry.relativePath = ss.str();
122 }
123 else
124 {
125 entry.path = m_directoryEntry.path;
126 entry.relativePath = m_directoryEntry.relativePath;
127 }
128
129 AWS_LOGSTREAM_TRACE(FILE_SYSTEM_UTILS_LOG_TAG, "Calling stat on path " << entry.path);
130
131 struct stat dirInfo;
132 if(!lstat(entry.path.c_str(), &dirInfo))
133 {
134 if(S_ISDIR(dirInfo.st_mode))
135 {
136 AWS_LOGSTREAM_DEBUG(FILE_SYSTEM_UTILS_LOG_TAG, "type directory detected");
137 entry.fileType = FileType::Directory;
138 }
139 else if(S_ISLNK(dirInfo.st_mode))
140 {
141 AWS_LOGSTREAM_DEBUG(FILE_SYSTEM_UTILS_LOG_TAG, "type symlink detected");
142 entry.fileType = FileType::Symlink;
143 }
144 else if(S_ISREG(dirInfo.st_mode))
145 {
146 AWS_LOGSTREAM_DEBUG(FILE_SYSTEM_UTILS_LOG_TAG, "type file detected");
147 entry.fileType = FileType::File;
148 }
149
150 entry.fileSize = static_cast<int64_t>(dirInfo.st_size);
151 AWS_LOGSTREAM_DEBUG(FILE_SYSTEM_UTILS_LOG_TAG, "file size detected as " << entry.fileSize);
152 }
153 else
154 {
155 AWS_LOGSTREAM_ERROR(FILE_SYSTEM_UTILS_LOG_TAG, "Failed to stat file path " << entry.path << " with error code " << errno);
156 }
157
158 return entry;
159 }
160
161 DIR* m_dir;
162 };
163
164Aws::String GetHomeDirectory()
165{
166 static const char* HOME_DIR_ENV_VAR = "HOME";
167
168 AWS_LOGSTREAM_TRACE(FILE_SYSTEM_UTILS_LOG_TAG, "Checking " << HOME_DIR_ENV_VAR << " for the home directory.");
169
170 Aws::String homeDir = Aws::Environment::GetEnv(HOME_DIR_ENV_VAR);
171
172 AWS_LOGSTREAM_DEBUG(FILE_SYSTEM_UTILS_LOG_TAG, "Environment value for variable " << HOME_DIR_ENV_VAR << " is " << homeDir);
173
174 if(homeDir.empty())
175 {
176 AWS_LOGSTREAM_WARN(FILE_SYSTEM_UTILS_LOG_TAG, "Home dir not stored in environment, trying to fetch manually from the OS.");
177
178 passwd pw;
179 passwd *p_pw = nullptr;
180 char pw_buffer[4096];
181 getpwuid_r(getuid(), &pw, pw_buffer, sizeof(pw_buffer), &p_pw);
182 if(p_pw && p_pw->pw_dir)
183 {
184 homeDir = p_pw->pw_dir;
185 }
186
187 AWS_LOGSTREAM_INFO(FILE_SYSTEM_UTILS_LOG_TAG, "Pulled " << homeDir << " as home directory from the OS.");
188 }
189
190 Aws::String retVal = homeDir.size() > 0 ? Aws::Utils::StringUtils::Trim(homeDir.c_str()) : "";
191 if(!retVal.empty())
192 {
193 if(retVal.at(retVal.length() - 1) != PATH_DELIM)
194 {
195 AWS_LOGSTREAM_DEBUG(FILE_SYSTEM_UTILS_LOG_TAG, "Home directory is missing the final " << PATH_DELIM << " appending one to normalize");
196 retVal += PATH_DELIM;
197 }
198 }
199
200 AWS_LOGSTREAM_DEBUG(FILE_SYSTEM_UTILS_LOG_TAG, "Final Home Directory is " << retVal);
201
202 return retVal;
203}
204
205bool CreateDirectoryIfNotExists(const char* path, bool createParentDirs)
206{
207 Aws::String directoryName = path;
208 AWS_LOGSTREAM_INFO(FILE_SYSTEM_UTILS_LOG_TAG, "Creating directory " << directoryName);
209
210 for (size_t i = (createParentDirs ? 0 : directoryName.size() - 1); i < directoryName.size(); i++)
211 {
212 // Create the parent directory if we find a delimiter and the delimiter is not the first char, or if this is the target directory.
213 if (i != 0 && (directoryName[i] == FileSystem::PATH_DELIM || i == directoryName.size() - 1))
214 {
215 if (directoryName[i] == FileSystem::PATH_DELIM)
216 {
217 directoryName[i] = '\0';
218 }
219 int errorCode = mkdir(directoryName.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
220 if (errorCode != 0 && errno != EEXIST)
221 {
222 AWS_LOGSTREAM_ERROR(FILE_SYSTEM_UTILS_LOG_TAG, "Creation of directory " << directoryName.c_str() << " returned code: " << errno);
223 return false;
224 }
225 AWS_LOGSTREAM_DEBUG(FILE_SYSTEM_UTILS_LOG_TAG, "Creation of directory " << directoryName.c_str() << " returned code: " << errno);
226 directoryName[i] = FileSystem::PATH_DELIM;
227 }
228 }
229 return true;
230}
231
232bool RemoveFileIfExists(const char* path)
233{
234 AWS_LOGSTREAM_INFO(FILE_SYSTEM_UTILS_LOG_TAG, "Deleting file: " << path);
235
236 int errorCode = unlink(path);
237 AWS_LOGSTREAM_DEBUG(FILE_SYSTEM_UTILS_LOG_TAG, "Deletion of file: " << path << " Returned error code: " << errno);
238 return errorCode == 0 || errno == ENOENT;
239}
240
241bool RemoveDirectoryIfExists(const char* path)
242{
243 AWS_LOGSTREAM_INFO(FILE_SYSTEM_UTILS_LOG_TAG, "Deleting directory: " << path);
244 int errorCode = rmdir(path);
245 AWS_LOGSTREAM_DEBUG(FILE_SYSTEM_UTILS_LOG_TAG, "Deletion of directory: " << path << " Returned error code: " << errno);
246 return errorCode == 0 || errno == ENOTDIR || errno == ENOENT;
247}
248
249bool RelocateFileOrDirectory(const char* from, const char* to)
250{
251 AWS_LOGSTREAM_INFO(FILE_SYSTEM_UTILS_LOG_TAG, "Moving file at " << from << " to " << to);
252
253 int errorCode = std::rename(from, to);
254
255 AWS_LOGSTREAM_DEBUG(FILE_SYSTEM_UTILS_LOG_TAG, "The moving operation of file at " << from << " to " << to << " Returned error code of " << errno);
256 return errorCode == 0;
257}
258
259Aws::String CreateTempFilePath()
260{
261 Aws::StringStream ss;
262 auto dt = Aws::Utils::DateTime::Now();
263
264 ss << dt.ToGmtString("%Y%m%dT%H%M%S") << dt.Millis() << Aws::String(Aws::Utils::UUID::RandomUUID());
265 Aws::String tempFile(ss.str());
266
267 AWS_LOGSTREAM_DEBUG(FILE_SYSTEM_UTILS_LOG_TAG, "CreateTempFilePath generated: " << tempFile);
268
269 return tempFile;
270}
271
272Aws::String GetExecutableDirectory()
273{
274 char dest[PATH_MAX];
275 size_t destSize = sizeof(dest);
276 memset(dest, 0, destSize);
277
278 if(readlink("/proc/self/exe", dest, destSize))
279 {
280 Aws::String executablePath(dest);
281 auto lastSlash = executablePath.find_last_of('/');
282 if(lastSlash != std::string::npos)
283 {
284 return executablePath.substr(0, lastSlash);
285 }
286 }
287
288 return "./";
289}
290
291Aws::UniquePtr<Directory> OpenDirectory(const Aws::String& path, const Aws::String& relativePath)
292{
293 return Aws::MakeUnique<PosixDirectory>(FILE_SYSTEM_UTILS_LOG_TAG, path, relativePath);
294}
295
296} // namespace FileSystem
297} // namespace Aws
298