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 | |
33 | namespace Aws |
34 | { |
35 | namespace FileSystem |
36 | { |
37 | |
38 | static 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 | |
164 | Aws::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 | |
205 | bool 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 | |
232 | bool 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 | |
241 | bool 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 | |
249 | bool 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 | |
259 | Aws::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 | |
272 | Aws::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 | |
291 | Aws::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 | |