]>
Commit | Line | Data |
---|---|---|
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 |
38 | static 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 | ||
79 | ERROR: | |
80 | archive_write_free(a); | |
81 | ||
82 | return NULL; | |
83 | } | |
84 | ||
4667a2ca | 85 | int 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 | ||
170 | OUT: | |
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 | ||
191 | ERROR: | |
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 | 200 | static 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 | 239 | ERROR: |
79824416 MT |
240 | if (archive) |
241 | archive_read_free(archive); | |
2810874a MT |
242 | |
243 | return r; | |
a3fa61f4 | 244 | } |
cb4762c6 | 245 | |
4667a2ca | 246 | int 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 | ||
271 | ERROR: | |
272 | if (repo) | |
273 | pakfire_repo_unref(repo); | |
274 | if (db) | |
275 | pakfire_db_unref(db); | |
276 | ||
277 | return r; | |
278 | } |