softwarecontainer  0.18.0-739e8d7 2017-05-04
filetoolkitwithundo.cpp
1 /*
2  * Copyright (C) 2016-2017 Pelagicore AB
3  *
4  * Permission to use, copy, modify, and/or distribute this software for
5  * any purpose with or without fee is hereby granted, provided that the
6  * above copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
9  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
10  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
11  * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
12  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
13  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
14  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15  * SOFTWARE.
16  *
17  * For further information see LICENSE
18  */
19 
20 #include <sys/stat.h>
21 #include <sys/mount.h>
22 
23 #include "filetoolkitwithundo.h"
24 
25 #include "directorycleanuphandler.h"
26 #include "filecleanuphandler.h"
27 #include "mountcleanuphandler.h"
28 #include "overlaysynccleanuphandler.h"
29 
30 #include "recursivecopy.h"
31 
32 namespace softwarecontainer {
33 
34 FileToolkitWithUndo::~FileToolkitWithUndo()
35 {
36  bool success = true;
37  // Clean up all created directories, files, and mount points
38 
39  while (!m_cleanupHandlers.empty()) {
40  std::unique_ptr<CleanUpHandler> c = std::move(m_cleanupHandlers.back());
41  m_cleanupHandlers.pop_back();
42 
43  if (!c->clean()) {
44  success = false;
45  }
46  }
47 
48  if(!success) {
49  log_warning() << "One or more cleanup handlers returned error status, please check the log";
50  }
51 }
52 
53 bool FileToolkitWithUndo::bindMount(const std::string &src,
54  const std::string &dst,
55  const std::string &tmpContainerRoot,
56  bool readOnly,
57  bool writeBufferEnabled)
58 {
59  unsigned long flags = 0;
60  std::string fstype;
61  int mountRes;
62  std::unique_ptr<CreateDir> createDirInstance = std::unique_ptr<CreateDir>(new CreateDir());
63 
64  if (!existsInFileSystem(src)) {
65  log_error() << src << " does not exist on the host, can not bindMount";
66  return false;
67  }
68 
69  if (!existsInFileSystem(dst)) {
70  log_error() << dst << " does not exist on the host, can not bindMount";
71  return false;
72  }
73 
74  log_debug() << "Bind-mounting " << src << " in " << dst << ", flags: " << flags;
75 
76  if(writeBufferEnabled && isDirectory(src)) {
77  std::string upperDir , workDir;
78 
79  // In case the tmpContainerRoot is set to nothing we need to create a
80  // good bindmount directory.
81  if (tmpContainerRoot == "") {
82  upperDir = createDirInstance->createTempDirectoryFromTemplate("/tmp/sc-bindmount-upper-XXXXXX");
83  workDir = createDirInstance->createTempDirectoryFromTemplate("/tmp/sc-bindmount-work-XXXXXX");
84  } else {
85  upperDir = createDirInstance->createTempDirectoryFromTemplate(tmpContainerRoot + "/bindmount-upper-XXXXXX");
86  workDir = createDirInstance->createTempDirectoryFromTemplate(tmpContainerRoot + "/bindmount-work-XXXXXX");
87  }
88  fstype.assign("overlay");
89 
90  std::ostringstream os;
91  os << "lowerdir=" << src << ",upperdir=" << upperDir << ",workdir=" << workDir;
92 
93  log_debug() << "writeBufferEnabled, config: " << os.str();
94 
95  mountRes = mount("overlay", dst.c_str(), fstype.c_str(), flags, os.str().c_str());
96  log_debug() << "mountRes: " << mountRes;
97  } else {
98  const void *data = nullptr;
99  flags = MS_BIND;
100  mountRes = mount(src.c_str(), dst.c_str(), fstype.c_str(), flags, data);
101  }
102 
103  if (mountRes == 0) {
104  log_verbose() << "Bind-mounted folder " << src << " in " << dst;
105  } else {
106  log_error() << "Could not mount into container: src=" << src
107  << " , dst=" << dst << " err=" << strerror(errno);
108  return false;
109  }
110 
111  if (readOnly && !writeBufferEnabled) {
112  const void *data = nullptr;
113 
114  flags = MS_REMOUNT | MS_RDONLY | MS_BIND;
115 
116  log_debug() << "Re-mounting read-only" << src << " in "
117  << dst << ", flags: " << flags;
118  mountRes = mount(src.c_str(), dst.c_str(), fstype.c_str(), flags, data);
119  if (mountRes != 0) {
120  // Failure
121  log_error() << "Could not re-mount " << src << " , read-only on "
122  << dst << " err=" << strerror(errno);
123  return false;
124  }
125  }
126  m_createDirList.push_back(std::move(createDirInstance));
127  return true;
128 }
129 
130 bool FileToolkitWithUndo::overlayMount(const std::string &lower,
131  const std::string &upper,
132  const std::string &work,
133  const std::string &dst)
134 {
135  std::string fstype = "overlay";
136  unsigned long flags = 0;
137  std::unique_ptr<CreateDir> createDirInstance = std::unique_ptr<CreateDir>(new CreateDir());
138 
139  if (!createDirInstance->createDirectory(lower)
140  || !createDirInstance->createDirectory(upper)
141  || !createDirInstance->createDirectory(work)
142  || !createDirInstance->createDirectory(dst))
143  {
144  log_error() << "Failed to create lower/upper/work directory for overlayMount. lowerdir=" <<
145  lower << ", upperdir=" << upper << ", workdir=" << work;
146  return false;
147  }
148 
149  std::string mountoptions = logging::StringBuilder() << "lowerdir=" << lower
150  << ",upperdir=" << upper
151  << ",workdir=" << work;
152 
153  int mountRes = mount("overlay", dst.c_str(), fstype.c_str(), flags, mountoptions.c_str());
154 
155  if (mountRes == 0) {
156  log_verbose() << "overlayMounted folder " << lower << " in " << dst;
157  m_cleanupHandlers.emplace_back(new MountCleanUpHandler(dst));
158  m_cleanupHandlers.emplace_back(new OverlaySyncCleanupHandler(upper, lower));
159  } else {
160  log_error() << "Could not mount into container: upper=" << upper
161  << ",lower=" << lower
162  << ",work=" << work
163  << " at dst=" << dst << " err=" << strerror(errno);
164  return false;
165  }
166 
167  m_createDirList.push_back(std::move(createDirInstance));
168  return true;
169 }
170 
171 bool FileToolkitWithUndo::syncOverlayMount(const std::string &lower,
172  const std::string &upper)
173 {
174  return RecursiveCopy::getInstance().copy(upper, lower);
175 }
176 
177 bool FileToolkitWithUndo::tmpfsMount(const std::string dst, const int maxSize)
178 {
179  std::string fstype = "tmpfs";
180  unsigned long flags = 0;
181  std::unique_ptr<CreateDir> createDirInstance = std::unique_ptr<CreateDir>(new CreateDir());
182 
183  if (!createDirInstance->createDirectory(dst)) {
184  log_error() << "Failed to create " << dst << " directory for tmpfs mount";
185  return false;
186  }
187 
188  std::string mountoptions = logging::StringBuilder() << "size=" << maxSize;
189 
190  bool directoryIsEmpty = isDirectoryEmpty(dst);
191 
192  int mountRes = mount("tmpfs", dst.c_str(), fstype.c_str(), flags, mountoptions.c_str());
193 
194  if (0 == mountRes) {
195  log_verbose() << "tmpfs mounted in " << dst;
196  m_cleanupHandlers.emplace_back(new MountCleanUpHandler(dst));
197  } else {
198  log_error() << "Could not mount tmpfs into directory: " << dst << " size=" << maxSize;
199  return false;
200  }
201 
202  m_createDirList.push_back(std::move(createDirInstance));
203  return true;
204 }
205 
206 bool FileToolkitWithUndo::createSharedMountPoint(const std::string &path)
207 {
208  auto mountRes = mount(path.c_str(), path.c_str(), "", MS_BIND, nullptr);
209  if (mountRes != 0) {
210  log_error() << "Could not bind mount " << path << " to itself";
211  return false;
212  }
213 
214  mountRes = mount(path.c_str(), path.c_str(), "", MS_UNBINDABLE, nullptr);
215  if (mountRes != 0) {
216  log_error() << "Could not make " << path << " unbindable";
217  return false;
218  }
219 
220  mountRes = mount(path.c_str(), path.c_str(), "", MS_SHARED, nullptr);
221  if (mountRes != 0) {
222  log_error() << "Could not make " << path << " shared";
223  return false;
224  }
225 
226  m_cleanupHandlers.emplace_back(new MountCleanUpHandler(path));
227  log_debug() << "Created shared mount point at " << path;
228 
229  return true;
230 }
231 
232 bool FileToolkitWithUndo::pathInList(const std::string path)
233 {
234 
235  for (auto &element : m_cleanupHandlers) {
236  if (element->queryName() == path) {
237  return true;
238  }
239  }
240  return false;
241 }
242 
243 bool FileToolkitWithUndo::writeToFile(const std::string &path, const std::string &content)
244 {
245  if (!softwarecontainer::writeToFile(path, content)) {
246  return false;
247  }
248 
249  if (!pathInList(path)) {
250  m_cleanupHandlers.emplace_back(new FileCleanUpHandler(path));
251  }
252  log_debug() << "Successfully wrote to " << path;
253  return true;
254 }
255 
256 void FileToolkitWithUndo::markFileForDeletion(const std::string &path)
257 {
258  if (!pathInList(path)) {
259  m_cleanupHandlers.emplace_back(new FileCleanUpHandler(path));
260  }
261 }
262 
263 } // namespace softwarecontainer
std::vector< std::unique_ptr< CleanUpHandler > > m_cleanupHandlers
m_cleanupHandlers A vector of cleanupHandlers added during the lifetime of the FileToolKitWithUndo th...
bool isDirectoryEmpty(const std::string &path)
isDirectoryEmpty Check if path is empty
bool tmpfsMount(const std::string dst, const int maxSize)
tmpfsMount Mount a tmpfs in the dst path and limit size of the tmpfs to maxSize
The FileCleanUpHandler class is a subclass of CleanUpHandler that deletes a file. ...
The CreateDir class is responsible for creating new directories and removing them when it is necessar...
Definition: createdir.h:33
std::vector< std::unique_ptr< CreateDir > > m_createDirList
m_createDirList A vector of CreateDir classes.
bool overlayMount(const std::string &lower, const std::string &upper, const std::string &work, const std::string &dst)
overlayMount Mount a directory with an overlay on top of it.
bool pathInList(const std::string path)
checks whether given path is already added to clean up handlers or not
The OverlaySyncCleanupHandler class is used to copy files on cleanup and can be added to the CleanupH...
bool isDirectory(const std::string &path)
isDirectory Check if path is a directory
bool createSharedMountPoint(const std::string &path)
createSharedMountPoint Make the mount point shared, ie new mount points created in one bind mount wil...
bool bindMount(const std::string &src, const std::string &dst, const std::string &tmpContainerRoot, bool readOnly, bool writeBufferEnabled=false)
bindMount Bind mount a src directory to another position dst.
bool existsInFileSystem(const std::string &path)
existsInFileSystem Check if path exists
bool syncOverlayMount(const std::string &lower, const std::string &upper)
syncOverlayMount Copy the directory structure from upper layer to the lower layer ...
static RecursiveCopy & getInstance()
getInstance Gets the RecursiveCopy instance.
bool copy(std::string src, std::string dst)
copy Copy files from src to dst
Developers guide to adding a config item: