]> git.ipfire.org Git - people/ms/pakfire.git/blame - src/libpakfire/snapshot.c
compress: Create a unified extraction function
[people/ms/pakfire.git] / src / libpakfire / snapshot.c
CommitLineData
a3fa61f4
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2021 Pakfire development team #
5# #
6# This program is free software: you can redistribute it and/or modify #
7# it under the terms of the GNU General Public License as published by #
8# the Free Software Foundation, either version 3 of the License, or #
9# (at your option) any later version. #
10# #
11# This program is distributed in the hope that it will be useful, #
12# but WITHOUT ANY WARRANTY; without even the implied warranty of #
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14# GNU General Public License for more details. #
15# #
16# You should have received a copy of the GNU General Public License #
17# along with this program. If not, see <http://www.gnu.org/licenses/>. #
18# #
19#############################################################################*/
20
21#include <errno.h>
097b6ca6 22#include <linux/limits.h>
a3fa61f4 23#include <stdio.h>
2810874a 24#include <stdlib.h>
a3fa61f4
MT
25
26#include <archive.h>
27
79824416 28#include <pakfire/compress.h>
cb4762c6 29#include <pakfire/db.h>
a3fa61f4
MT
30#include <pakfire/file.h>
31#include <pakfire/filelist.h>
2d8abc60 32#include <pakfire/i18n.h>
a3fa61f4 33#include <pakfire/logging.h>
cb4762c6 34#include <pakfire/repo.h>
a3fa61f4 35#include <pakfire/snapshot.h>
88124868 36#include <pakfire/util.h>
a3fa61f4 37
4667a2ca
MT
38static struct archive* pakfire_snapshot_create_archive(
39 struct pakfire* pakfire, const char* path) {
a3fa61f4
MT
40 struct archive* a = archive_write_new();
41 if (!a) {
42 ERROR(pakfire, "archive_write_new() failed\n");
43 return NULL;
44 }
45
46 // Use the PAX format
47 int r = archive_write_set_format_pax(a);
48 if (r) {
49 ERROR(pakfire, "Could not set format to PAX: %s\n", archive_error_string(a));
50 goto ERROR;
51 }
52
53 // Enable Zstd
54 r = archive_write_add_filter_zstd(a);
55 if (r) {
56 ERROR(pakfire, "Could not enable Zstandard compression: %s\n",
57 archive_error_string(a));
58 goto ERROR;
d4bd6b2c
MT
59 }
60
61 // Set compression level to fastest
62 r = archive_write_set_filter_option(a, NULL, "compression-level", "1");
63 if (r) {
64 ERROR(pakfire, "Could not set Zstandard compression level: %s\n",
65 archive_error_string(a));
66 goto ERROR;
a3fa61f4 67 }
097a0064
MT
68
69 // Do not pad the last block
70 archive_write_set_bytes_in_last_block(a, 1);
a3fa61f4
MT
71
72 // Write archive to file
4667a2ca 73 r = archive_write_open_filename(a, path);
a3fa61f4
MT
74 if (r)
75 goto ERROR;
76
77 return a;
78
79ERROR:
80 archive_write_free(a);
81
82 return NULL;
83}
84
4667a2ca 85int pakfire_snapshot_create(struct pakfire* pakfire, const char* path) {
1ea7b360
MT
86 struct pakfire_filelist* filelist = NULL;
87 struct archive* a = NULL;
88124868
MT
88 int r = 1;
89
4667a2ca
MT
90 // Check input
91 if (!path) {
5b25bf0b
MT
92 errno = EINVAL;
93 return 1;
94 }
95
1ea7b360
MT
96 // Create a new filelist
97 r = pakfire_filelist_create(&filelist, pakfire);
98 if (r)
99 goto ERROR;
100
88124868 101 const char* root = pakfire_get_path(pakfire);
a3fa61f4 102
4667a2ca 103 INFO(pakfire, "Creating snapshot of %s to %s...\n", root, path);
a3fa61f4 104
1ea7b360
MT
105 // Scan for all files
106 r = pakfire_filelist_scan(filelist, root, NULL, NULL);
107 if (r)
a3fa61f4
MT
108 goto ERROR;
109
1ea7b360
MT
110 const size_t size = pakfire_filelist_size(filelist);
111
112 // Check if we have any files
113 if (!size) {
114 ERROR(pakfire, "The snapshot is unexpectedly empty\n");
115 r = 1;
a3fa61f4
MT
116 goto ERROR;
117 }
118
1ea7b360
MT
119 // Sort the filelist in place
120 pakfire_filelist_sort(filelist);
457db621 121
4667a2ca 122 a = pakfire_snapshot_create_archive(pakfire, path);
1ea7b360
MT
123 if (!a) {
124 ERROR(pakfire, "Could not open archive for writing\n");
125 goto ERROR;
126 }
a3fa61f4 127
1ea7b360
MT
128 for (unsigned int i = 0; i < size; i++) {
129 struct pakfire_file* file = pakfire_filelist_get(filelist, i);
130 if (!file)
457db621 131 continue;
457db621 132
1ea7b360 133 FILE* f = NULL;
88124868 134
1ea7b360
MT
135 // Make archive entry
136 struct archive_entry* entry = pakfire_file_archive_entry(file);
137 if (!entry) {
138 ERROR(pakfire, "Could not make archive entry from file: %m\n");
139 r = 1;
140 goto OUT;
141 }
88124868
MT
142
143 // Write header
a3fa61f4
MT
144 r = archive_write_header(a, entry);
145 if (r) {
88124868 146 ERROR(pakfire, "Could not write header: %s\n", archive_error_string(a));
1ea7b360 147 goto OUT;
a3fa61f4
MT
148 }
149
f83c5d1a
MT
150 // Copy payload
151 if (archive_entry_filetype(entry) == AE_IFREG) {
1ea7b360
MT
152 f = pakfire_file_open(file);
153 if (!f) {
154 r = 1;
155 goto OUT;
156 }
157
158 r = pakfire_archive_copy_data_from_file(pakfire, a, f);
88124868
MT
159 if (r) {
160 ERROR(pakfire, "Could not copy %s\n", archive_entry_pathname(entry));
1ea7b360 161 goto OUT;
88124868 162 }
a3fa61f4
MT
163 }
164
88124868
MT
165 // Write trailer
166 r = archive_write_finish_entry(a);
1ea7b360
MT
167 if (r)
168 goto OUT;
169
170OUT:
171 if (file)
172 pakfire_file_unref(file);
173 if (f)
174 fclose(f);
175
176 // Move on to ERROR
88124868
MT
177 if (r)
178 goto ERROR;
a3fa61f4
MT
179 }
180
2810874a
MT
181 // Close archive
182 r = archive_write_close(a);
183 if (r) {
184 ERROR(pakfire, "Could not close archive: %s\n", archive_error_string(a));
185 goto ERROR;
186 }
187
a3fa61f4
MT
188 // Success
189 r = 0;
190
191ERROR:
1ea7b360
MT
192 if (filelist)
193 pakfire_filelist_unref(filelist);
194 if (a)
195 archive_write_free(a);
a3fa61f4
MT
196
197 return r;
198}
199
4667a2ca 200static int pakfire_snapshot_extract(struct pakfire* pakfire, const char* path) {
2d8abc60 201 struct stat st;
2810874a
MT
202 int r = 1;
203
4667a2ca
MT
204 // Check input
205 if (!path) {
206 errno = EINVAL;
207 return 1;
208 }
209
2d8abc60 210 // Stat the input file
4667a2ca 211 r = stat(path, &st);
2d8abc60 212 if (r) {
b1772bfb 213 ERROR(pakfire, "Could not stat snapshot: %m\n");
2d8abc60
MT
214 return 1;
215 }
216
79824416
MT
217 struct archive* archive = archive_read_new();
218 if (!archive)
2810874a
MT
219 return 1;
220
221 // All snapshots are tarballs
79824416 222 archive_read_support_format_tar(archive);
2810874a
MT
223
224 // And they are compressed using ZSTD
79824416 225 archive_read_support_filter_zstd(archive);
2810874a 226
2810874a 227 // Open the given file for reading
79824416 228 r = archive_read_open_filename(archive, path, 64 * 1024);
2810874a 229 if (r) {
79824416 230 ERROR(pakfire, "Could not open archive: %s\n", archive_error_string(archive));
2810874a
MT
231 goto ERROR;
232 }
233
79824416
MT
234 // Extract snapshot
235 r = pakfire_extract(pakfire, archive, st.st_size, NULL, _("Extracting snapshot..."), 0);
2d8abc60
MT
236 if (r)
237 goto ERROR;
238
2810874a 239ERROR:
79824416
MT
240 if (archive)
241 archive_read_free(archive);
2810874a
MT
242
243 return r;
a3fa61f4 244}
cb4762c6 245
4667a2ca 246int pakfire_snapshot_restore(struct pakfire* pakfire, const char* path) {
cb4762c6
MT
247 struct pakfire_db* db = NULL;
248
4667a2ca
MT
249 // Check input
250 if (!path) {
5b25bf0b
MT
251 errno = EINVAL;
252 return 1;
253 }
254
cb4762c6 255 // Extract the archive
4667a2ca 256 int r = pakfire_snapshot_extract(pakfire, path);
cb4762c6
MT
257 if (r)
258 return r;
259
4651122b 260 struct pakfire_repo* repo = pakfire_get_installed_repo(pakfire);
cb4762c6
MT
261 if (!repo)
262 goto ERROR;
263
264 // Reload the database
265 r = pakfire_db_open(&db, pakfire, PAKFIRE_DB_READWRITE);
266 if (r)
267 goto ERROR;
268
269 r = pakfire_db_load(db, repo);
270
271ERROR:
272 if (repo)
273 pakfire_repo_unref(repo);
274 if (db)
275 pakfire_db_unref(db);
276
277 return r;
278}