2 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
4 * Contact: Lukasz Pawelczyk <l.pawelczyk@partner.samsung.com>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License
21 * @author Lukasz Pawelczyk (l.pawelczyk@partner.samsung.com)
22 * @brief File utility functions
26 #include "logger/logger.hpp"
27 #include "utils/fs.hpp"
28 #include "utils/paths.hpp"
29 #include "utils/exception.hpp"
31 #include <boost/filesystem.hpp>
38 #include <sys/mount.h>
39 #include <sys/types.h>
45 namespace security_containers {
49 std::string readFileContent(const std::string& path)
52 if (!readFileContent(path, result)) {
53 throw UtilsException();
58 bool readFileContent(const std::string& path, std::string& result)
60 std::ifstream file(path);
63 LOGD(path << ": could not open for reading");
67 file.seekg(0, std::ios::end);
68 std::streampos length = file.tellg();
70 LOGD(path << ": tellg failed");
73 result.resize(static_cast<size_t>(length));
74 file.seekg(0, std::ios::beg);
76 file.read(&result[0], length);
78 LOGD(path << ": read error");
86 bool saveFileContent(const std::string& path, const std::string& content)
88 std::ofstream file(path);
90 LOGD(path << ": could not open for writing");
93 file.write(content.data(), static_cast<std::streamsize>(content.size()));
95 LOGD(path << ": could not write to");
101 bool isCharDevice(const std::string& path)
104 return ::stat(path.c_str(), &s) == 0 && S_IFCHR == (s.st_mode & S_IFMT);
108 // NOTE: Should be the same as in systemd/src/core/mount-setup.c
109 const std::string RUN_MOUNT_POINT_OPTIONS = "mode=755,smackfstransmute=System::Run";
110 const std::string RUN_MOUNT_POINT_OPTIONS_NO_SMACK = "mode=755";
111 const unsigned long RUN_MOUNT_POINT_FLAGS = MS_NOSUID | MS_NODEV | MS_STRICTATIME;
113 bool mountTmpfs(const std::string& path, unsigned long flags, const std::string& options)
115 if (::mount("tmpfs", path.c_str(), "tmpfs", flags, options.c_str()) != 0) {
116 LOGD("Mount failed for '" << path << "', options=" << options << ": " << strerror(errno));
124 bool mountRun(const std::string& path)
126 return utils::mountTmpfs(path, RUN_MOUNT_POINT_FLAGS, RUN_MOUNT_POINT_OPTIONS)
127 || utils::mountTmpfs(path, RUN_MOUNT_POINT_FLAGS, RUN_MOUNT_POINT_OPTIONS_NO_SMACK);
130 bool umount(const std::string& path)
132 if (::umount(path.c_str()) != 0) {
133 LOGD("Umount failed for '" << path << "': " << strerror(errno));
139 bool isMountPoint(const std::string& path, bool& result)
141 std::string parentPath = dirName(path);
143 bool ret = hasSameMountPoint(path, parentPath, newResult);
149 bool hasSameMountPoint(const std::string& path1, const std::string& path2, bool& result)
153 if (::stat(path1.c_str(), &s1)) {
154 LOGD("Failed to get stat of " << path1 << ": " << strerror(errno));
158 if (::stat(path2.c_str(), &s2)) {
159 LOGD("Failed to get stat of " << path2 << ": " << strerror(errno));
163 result = (s1.st_dev == s2.st_dev);
167 bool moveFile(const std::string& src, const std::string& dst)
171 namespace fs = boost::filesystem;
172 boost::system::error_code error;
174 // The destination has to be a full path (including a file name)
175 // so it doesn't exist yet, we need to check upper level dir instead.
176 if (!hasSameMountPoint(src, dirName(dst), bResult)) {
177 LOGE("Failed to check the files' mount points");
182 fs::rename(src, dst, error);
184 LOGE("Failed to rename the file: " << error);
188 fs::copy_file(src, dst, error);
190 LOGE("Failed to copy the file: " << error);
193 fs::remove(src, error);
195 LOGE("Failed to remove the file: " << error);
196 fs::remove(dst, error);
206 bool copyDirContentsRec(const boost::filesystem::path& src, const boost::filesystem::path& dst)
208 namespace fs = boost::filesystem;
210 // TODO: Right now this function skips files which produce error when copying. Errors show up
212 // a) fs::directory_iterator file(src) is created
213 // b) fs::copy(...) is called
214 // In both cases lack of permissions is the issue.
216 // In a) case we can't do much - SCS won't be able to read the directory and its contents. Such
217 // directories are not common in the filesystem, so they *probably* can be skipped.
219 // In b) case multiple directories have too strict permissions to be directly copied. This
220 // is a problem for some files crucial to container launch (ex. we cannot copy
221 // /usr/lib/systemd/systemd because /usr/lib has 555 permissions).
222 // To fix b) issue, copying must be done in two steps:
223 // 1. Copy file contents without permissions (this probably can be achieved by opening two
224 // files in-code with fstream and programatically copying data from one file to another).
225 // 2. Apply all available file attributes from source (permissions, owner UID/GID, xattrs...)
228 for (fs::directory_iterator file(src);
229 file != fs::directory_iterator();
231 fs::path current(file->path());
233 boost::system::error_code ec;
234 fs::copy(current, dst / current.filename(), ec);
235 if(ec.value() != boost::system::errc::success) {
236 LOGW("Failed to copy " << current << ": " << ec.message());
239 if (!fs::is_symlink(current) && fs::is_directory(current)) {
240 if (!copyDirContentsRec(current, dst / current.filename())) {
245 } catch (fs::filesystem_error& e) {
254 bool copyDirContents(const std::string& src, const std::string& dst)
256 namespace fs = boost::filesystem;
258 return copyDirContentsRec(fs::path(src), fs::path(dst));
261 bool createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem::perms mode)
263 namespace fs = boost::filesystem;
265 fs::path dirPath(path);
266 boost::system::error_code ec;
267 bool runDirCreated = false;
268 if (!fs::exists(dirPath)) {
269 if (!fs::create_directory(dirPath, ec)) {
270 LOGE("Failed to create directory '" << path << "': "
274 runDirCreated = true;
275 } else if (!fs::is_directory(dirPath)) {
276 LOGE("Path '" << path << " already exists");
281 fs::permissions(dirPath, mode, ec);
282 if (fs::status(dirPath).permissions() != mode) {
283 LOGE("Failed to set permissions to '" << path << "': "
289 if (::chown(path.c_str(), uid, gid) != 0) {
290 // remove the directory only if it hadn't existed before
294 LOGE("chown() failed for path '" << path << "': " << strerror(errno));
301 bool createEmptyDir(const std::string& path)
303 namespace fs = boost::filesystem;
305 fs::path dirPath(path);
306 boost::system::error_code ec;
307 bool cleanDirCreated = false;
309 if (!fs::exists(dirPath)) {
310 if (!fs::create_directory(dirPath, ec)) {
311 LOGE("Failed to create dir. Error: " << ec.message());
314 cleanDirCreated = true;
315 } else if (!fs::is_directory(dirPath)) {
316 LOGE("Provided path already exists and is not a dir, cannot create.");
320 if (!cleanDirCreated) {
321 // check if directory is empty if it was already created
322 if (!fs::is_empty(dirPath)) {
323 LOGE("Directory has some data inside, cannot be used.");
332 } // namespace security_containers