Blue Brain BioExplorer
ArchiveLoader.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018 EPFL/Blue Brain Project
3  * All rights reserved. Do not distribute without permission.
4  * Responsible Author: Jonas Karlsson <jonas.karlsson@epfl.ch>
5  *
6  * This file is part of Blue Brain BioExplorer <https://github.com/BlueBrain/BioExplorer>
7  *
8  * This library is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU Lesser General Public License version 3.0 as published
10  * by the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #include "ArchiveLoader.h"
23 
29 
30 #include <fstream>
31 
32 #include <archive.h>
33 #include <archive_entry.h>
34 
35 namespace
36 {
37 bool isSupportedArchiveType(const std::string& extension)
38 {
39  // No way to get all supported types from libarchive...
40  // Extend this list if you feel your favorite archive type should be here
41  const std::set<std::string> extensions{"zip", "gz", "tgz", "bz2", "rar"};
42  return extensions.find(extension) != extensions.end();
43 }
44 
45 archive* _openArchive(const std::string& filename)
46 {
47  auto archive = archive_read_new();
48  archive_read_support_format_all(archive);
49  archive_read_support_filter_all(archive);
50 
51  // non-tar archives like gz, bzip2, ... need to be added as raw
52  auto extension = core::extractExtension(filename);
53  if (!extension.empty())
54  {
55  if (isSupportedArchiveType(extension))
56  archive_read_support_format_raw(archive);
57  }
58  if (archive_read_open_filename(archive, filename.c_str(), 10240) == ARCHIVE_OK)
59  {
60  return archive;
61  }
62  archive_read_free(archive);
63  return nullptr;
64 }
65 
66 archive* _openArchive(const core::Blob& blob)
67 {
68  auto archive = archive_read_new();
69  archive_read_support_format_all(archive);
70  archive_read_support_filter_all(archive);
71 
72  // non-tar archives like gz, bzip2, ... need to be added as raw
73  auto extension = core::extractExtension(blob.name);
74  if (!extension.empty())
75  {
76  if (isSupportedArchiveType(extension))
77  archive_read_support_format_raw(archive);
78  }
79  if (archive_read_open_memory(archive, (void*)blob.data.data(), blob.data.size()) == ARCHIVE_OK)
80  {
81  return archive;
82  }
83  archive_read_free(archive);
84  return nullptr;
85 }
86 
87 int copy_data(struct archive* ar, struct archive* aw)
88 {
89  for (;;)
90  {
91  const void* buff;
92  size_t size;
93  int64_t offset;
94 
95  auto r = archive_read_data_block(ar, &buff, &size, &offset);
96  if (r == ARCHIVE_EOF)
97  return ARCHIVE_OK;
98  if (r < ARCHIVE_OK)
99  return r;
100  r = archive_write_data_block(aw, buff, size, offset);
101  if (r < ARCHIVE_OK)
102  {
103  std::cerr << archive_error_string(aw) << std::endl;
104  return (r);
105  }
106  }
107 }
108 
109 void _extractArchive(archive* archive, const std::string& filename, const std::string& destination)
110 {
111  auto writer = archive_write_disk_new();
112  archive_write_disk_set_options(writer, 0);
113  archive_write_disk_set_standard_lookup(writer);
114 
115  for (;;)
116  {
117  archive_entry* entry;
118  auto r = archive_read_next_header(archive, &entry);
119  if (r == ARCHIVE_EOF)
120  break;
121  if (r < ARCHIVE_OK)
122  std::cerr << archive_error_string(archive) << std::endl;
123  if (r < ARCHIVE_WARN)
124  {
125  throw std::runtime_error(std::string("Error reading file from archive: ") + archive_error_string(archive));
126  }
127  const char* currentFile = archive_entry_pathname(entry);
128 
129  // magic 'data' file for gzip archives is useless to us, so rename it
130  if (std::string(currentFile) == "data")
131  currentFile = filename.c_str();
132  const std::string fullOutputPath = destination + "/" + currentFile;
133  archive_entry_set_pathname(entry, fullOutputPath.c_str());
134  r = archive_write_header(writer, entry);
135  if (r < ARCHIVE_OK)
136  std::cerr << archive_error_string(writer) << std::endl;
137  else if (archive_entry_size(entry) > 0)
138  {
139  r = copy_data(archive, writer);
140  if (r < ARCHIVE_OK)
141  std::cerr << archive_error_string(writer) << std::endl;
142  if (r < ARCHIVE_WARN)
143  throw std::runtime_error(std::string("Error writing file from archive to disk: ") +
144  archive_error_string(archive));
145  }
146  r = archive_write_finish_entry(writer);
147  if (r < ARCHIVE_OK)
148  std::cerr << archive_error_string(writer) << std::endl;
149  if (r < ARCHIVE_WARN)
150  throw std::runtime_error(std::string("Error finishing current file: ") + archive_error_string(archive));
151  }
152  archive_read_free(archive);
153  archive_write_close(writer);
154  archive_write_free(writer);
155 }
156 
157 void extractFile(const std::string& filename, const std::string& destination)
158 {
159  auto archive = _openArchive(filename);
160  if (!archive)
161  throw std::runtime_error(filename + " is not a supported archive type");
162  _extractArchive(archive, fs::path(filename).stem().string(), destination);
163 }
164 
165 void extractBlob(core::Blob&& blob, const std::string& destination)
166 {
167  auto archive = _openArchive(blob);
168  if (!archive)
169  throw std::runtime_error("Blob is not a supported archive type");
170  _extractArchive(archive, fs::path(blob.name).stem().string(), destination);
171 }
172 
173 const auto LOADER_NAME = "archive";
174 
175 struct TmpFolder
176 {
177  TmpFolder()
178  {
179  if (!mkdtemp((char*)path.data()))
180  throw std::runtime_error("Could not create temporary directory");
181  }
182  ~TmpFolder() { fs::remove_all(path); }
183  std::string path{"/tmp/brayns_extracted_XXXXXX"};
184 };
185 } // namespace
186 
187 namespace core
188 {
190  : Loader(scene)
191  , _registry(registry)
192 {
193 }
194 
195 bool ArchiveLoader::isSupported(const std::string& storage, const std::string& extension) const
196 {
197  return isSupportedArchiveType(extension);
198 }
199 
200 ModelDescriptorPtr ArchiveLoader::loadExtracted(const std::string& path, const LoaderProgress& callback,
201  const PropertyMap& properties) const
202 {
203  const auto loaderName = properties.getProperty<std::string>("loaderName", "");
204  const Loader* loader = loaderName.empty() ? nullptr : &_registry.getSuitableLoader("", "", loaderName);
205 
206  for (const auto& i : fs::directory_iterator(path))
207  {
208  const std::string currPath = i.path().string();
209  const std::string extension = extractExtension(currPath);
210 
211  if (loader && loader->isSupported(currPath, extension))
212  {
213  return loader->importFromFile(currPath, callback, properties);
214  }
215  else if (!loader && _registry.isSupportedFile(currPath))
216  {
217  const auto& loaderMatch = _registry.getSuitableLoader(currPath, "", "");
218  return loaderMatch.importFromFile(currPath, callback, properties);
219  }
220  }
221  throw std::runtime_error("No loader found for archive.");
222 }
223 
225  const PropertyMap& properties) const
226 {
227  TmpFolder tmpFolder;
228  extractBlob(std::move(blob), tmpFolder.path);
229  return loadExtracted(tmpFolder.path, callback, properties);
230 }
231 
232 ModelDescriptorPtr ArchiveLoader::importFromStorage(const std::string& storage, const LoaderProgress& callback,
233  const PropertyMap& properties) const
234 {
235  TmpFolder tmpFolder;
236  extractFile(filename, tmpFolder.path);
237  return loadExtracted(tmpFolder.path, callback, properties);
238 }
239 
240 std::string ArchiveLoader::getName() const
241 {
242  return LOADER_NAME;
243 }
244 
245 std::vector<std::string> ArchiveLoader::getSupportedStorage() const
246 {
247  return {"zip", "gz", "tgz", "bz2", "rar"};
248 }
249 } // namespace core
ModelDescriptorPtr importFromStorage(const std::string &storage, const LoaderProgress &callback, const PropertyMap &properties) const final
ArchiveLoader(Scene &scene, LoaderRegistry &registry)
ModelDescriptorPtr importFromBlob(Blob &&blob, const LoaderProgress &callback, const PropertyMap &properties) const final
bool isSupported(const std::string &storage, const std::string &extension) const final
std::string getName() const final
std::vector< std::string > getSupportedStorage() const final
bool isSupportedFile(const std::string &filename) const
const Loader & getSuitableLoader(const std::string &filename, const std::string &filetype, const std::string &loaderName) const
T getProperty(const std::string &name, T valIfNotFound) const
Definition: PropertyMap.h:323
Scene object This object contains collections of geometries, materials and light sources that are use...
Definition: Scene.h:43
const std::string LOADER_NAME
Definition: AtlasLoader.cpp:42
std::shared_ptr< ModelDescriptor > ModelDescriptorPtr
Definition: Types.h:112
std::string extractExtension(const std::string &filename)
Definition: Utils.cpp:64
std::string name
Definition: Types.h:336
uint8_ts data
Definition: Types.h:337