Apache Mesos
archiver.hpp
Go to the documentation of this file.
1 // Licensed to the Apache Software Foundation (ASF) under one
2 // or more contributor license agreements. See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership. The ASF licenses this file
5 // to you under the Apache License, Version 2.0 (the
6 // "License"); you may not use this file except in compliance
7 // with the License. You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #ifndef __STOUT_ARCHIVER_HPP__
18 #define __STOUT_ARCHIVER_HPP__
19 
20 #include <archive.h>
21 #include <archive_entry.h>
22 
23 #include <stout/nothing.hpp>
24 #include <stout/path.hpp>
25 #include <stout/try.hpp>
26 
27 #include <stout/os/close.hpp>
28 #include <stout/os/int_fd.hpp>
29 #include <stout/os/open.hpp>
30 
31 namespace archiver {
32 
33 // Extracts the archive in source to the destination folder (if specified).
34 // If destination is not specified, it will use the working directory.
35 // Flags can be any of (or together):
36 // ARCHIVE_EXTRACT_ACL
37 // ARCHIVE_EXTRACT_FFLAGS
38 // ARCHIVE_EXTRACT_PERM
39 // ARCHIVE_EXTRACT_TIME
41  const std::string& source,
42  const std::string& destination,
43  const int flags = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_SECURE_NODOTDOT)
44 {
45  // Get references to libarchive for reading/handling a compressed file.
46  std::unique_ptr<struct archive, std::function<void(struct archive*)>> reader(
47  archive_read_new(),
48  [](struct archive* p) {
49  archive_read_close(p);
50  archive_read_free(p);
51  });
52 
53  // Enable auto-detection of the archive type/format.
54  archive_read_support_format_all(reader.get());
55  archive_read_support_filter_all(reader.get());
56 
57  std::unique_ptr<struct archive, std::function<void(struct archive*)>> writer(
58  archive_write_disk_new(),
59  [](struct archive* p) {
60  archive_write_close(p);
61  archive_write_free(p);
62  });
63 
64  archive_write_disk_set_options(writer.get(), flags);
65  archive_write_disk_set_standard_lookup(writer.get());
66 
67  // Open the compressed file for decompression.
68  //
69  // We do not use libarchive to open the file to ensure we have proper
70  // file descriptor and long path handling on both Posix and Windows.
71  Try<int_fd> fd = os::open(source, O_RDONLY | O_CLOEXEC);
72  if (fd.isError()) {
73  return Error(fd.error());
74  }
75 
76 #ifdef __WINDOWS__
77  int fd_real = fd->crt();
78 #else
79  int fd_real = fd.get();
80 #endif
81 
82  // Ensure the CRT file descriptor is closed when leaving scope.
83  // NOTE: On Windows, we need to explicitly allocate a CRT file descriptor
84  // because libarchive requires it. Once the CRT fd is allocated, it must
85  // be closed with _close instead of os::close.
86  struct Closer
87  {
88  int fd_value;
89 #ifdef __WINDOWS__
90  ~Closer() { ::_close(fd_value); }
91 #else
92  ~Closer() { os::close(fd_value); }
93 #endif
94  } closer = {fd_real};
95 
96  const size_t archive_block_size = 10240;
97  int result = archive_read_open_fd(reader.get(), fd_real, archive_block_size);
98  if (result != ARCHIVE_OK) {
99  return Error(archive_error_string(reader.get()));
100  }
101 
102  // Loop through file headers in the archive stream.
103  while (true) {
104  // Read the next header from the input stream.
105  struct archive_entry* entry;
106  result = archive_read_next_header(reader.get(), &entry);
107 
108  if (result == ARCHIVE_EOF) {
109  break;
110  } else if (result <= ARCHIVE_WARN) {
111  return Error(
112  std::string("Failed to read archive header: ") +
113  archive_error_string(reader.get()));
114  }
115 
116  // If a destination path is specified, update the entry to reflect it.
117  // We assume the destination directory already exists.
118  if (!destination.empty()) {
119  // NOTE: This will be nullptr if the entry is not a hardlink.
120  const char* hardlink_target = archive_entry_hardlink_utf8(entry);
121 
122  if (hardlink_target != nullptr) {
123  archive_entry_update_hardlink_utf8(
124  entry,
125  path::join(destination, hardlink_target).c_str());
126  }
127 
128  archive_entry_update_pathname_utf8(
129  entry,
130  path::join(destination, archive_entry_pathname_utf8(entry)).c_str());
131  }
132 
133  result = archive_write_header(writer.get(), entry);
134  if (result <= ARCHIVE_WARN) {
135  return Error(
136  std::string("Failed to write archive header: ") +
137  archive_error_string(writer.get()));
138  }
139 
140  if (archive_entry_size(entry) > 0) {
141  const void* buff;
142  size_t size;
143 #if ARCHIVE_VERSION_NUMBER >= 3000000
144  int64_t offset;
145 #else
146  off_t offset;
147 #endif
148 
149  // Loop through file data blocks until end of file.
150  while (true) {
151  result = archive_read_data_block(reader.get(), &buff, &size, &offset);
152  if (result == ARCHIVE_EOF) {
153  break;
154  } else if (result <= ARCHIVE_WARN) {
155  return Error(
156  std::string("Failed to read archive data block: ") +
157  archive_error_string(reader.get()));
158  }
159 
160  result = archive_write_data_block(writer.get(), buff, size, offset);
161  if (result <= ARCHIVE_WARN) {
162  return Error(
163  std::string("Failed to write archive data block: ") +
164  archive_error_string(writer.get()));
165  }
166  }
167  }
168 
169  result = archive_write_finish_entry(writer.get());
170  if (result <= ARCHIVE_WARN) {
171  return Error(
172  std::string("Failed to write archive finish entry: ") +
173  archive_error_string(writer.get()));
174  }
175  }
176 
177  return Nothing();
178 }
179 
180 } // namespace archiver {
181 
182 #endif // __STOUT_ARCHIVER_HPP__
Definition: nothing.hpp:16
Definition: errorbase.hpp:36
Try< Bytes > size(const std::string &path, const FollowSymlink follow=FollowSymlink::FOLLOW_SYMLINK)
Definition: stat.hpp:130
Definition: check.hpp:33
Try< int_fd > open(const std::string &path, int oflag, mode_t mode=0)
Definition: open.hpp:35
std::string join(const std::string &path1, const std::string &path2, const char _separator=os::PATH_SEPARATOR)
Definition: path.hpp:116
constexpr int O_CLOEXEC
Definition: open.hpp:41
Try< Nothing > close(int fd)
Definition: close.hpp:24
#define flags
Definition: decoder.hpp:18
Definition: parse.hpp:33
Try< Nothing > extract(const std::string &source, const std::string &destination, const int flags=ARCHIVE_EXTRACT_TIME|ARCHIVE_EXTRACT_SECURE_NODOTDOT)
Definition: archiver.hpp:40
Definition: archiver.hpp:31