chiark / gitweb /
import: add a simple scheme for validating the SHA256 sums of downloaded raw files
[elogind.git] / src / import / import-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2015 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "util.h"
23 #include "strv.h"
24 #include "copy.h"
25 #include "btrfs-util.h"
26 #include "import-util.h"
27
28 #define FILENAME_ESCAPE "/.#\"\'"
29
30 bool http_etag_is_valid(const char *etag) {
31         if (!endswith(etag, "\""))
32                 return false;
33
34         if (!startswith(etag, "\"") && !startswith(etag, "W/\""))
35                 return false;
36
37         return true;
38 }
39
40 int import_find_old_etags(const char *url, const char *image_root, int dt, const char *prefix, const char *suffix, char ***etags) {
41         _cleanup_free_ char *escaped_url = NULL;
42         _cleanup_closedir_ DIR *d = NULL;
43         _cleanup_strv_free_ char **l = NULL;
44         struct dirent *de;
45         int r;
46
47         assert(url);
48         assert(etags);
49
50         if (!image_root)
51                 image_root = "/var/lib/machines";
52
53         escaped_url = xescape(url, FILENAME_ESCAPE);
54         if (!escaped_url)
55                 return -ENOMEM;
56
57         d = opendir(image_root);
58         if (!d) {
59                 if (errno == ENOENT) {
60                         *etags = NULL;
61                         return 0;
62                 }
63
64                 return -errno;
65         }
66
67         FOREACH_DIRENT_ALL(de, d, return -errno) {
68                 const char *a, *b;
69                 char *u;
70
71                 if (de->d_type != DT_UNKNOWN &&
72                     de->d_type != dt)
73                         continue;
74
75                 if (prefix) {
76                         a = startswith(de->d_name, prefix);
77                         if (!a)
78                                 continue;
79                 } else
80                         a = de->d_name;
81
82                 a = startswith(a, escaped_url);
83                 if (!a)
84                         continue;
85
86                 a = startswith(a, ".");
87                 if (!a)
88                         continue;
89
90                 if (suffix) {
91                         b = endswith(de->d_name, suffix);
92                         if (!b)
93                                 continue;
94                 } else
95                         b = strchr(de->d_name, 0);
96
97                 if (a >= b)
98                         continue;
99
100                 u = cunescape_length(a, b - a);
101                 if (!u)
102                         return -ENOMEM;
103
104                 if (!http_etag_is_valid(u)) {
105                         free(u);
106                         continue;
107                 }
108
109                 r = strv_consume(&l, u);
110                 if (r < 0)
111                         return r;
112         }
113
114         *etags = l;
115         l = NULL;
116
117         return 0;
118 }
119
120 int import_make_local_copy(const char *final, const char *image_root, const char *local, bool force_local) {
121         const char *p;
122         int r;
123
124         assert(final);
125         assert(local);
126
127         if (!image_root)
128                 image_root = "/var/lib/machines";
129
130         p = strappenda(image_root, "/", local);
131
132         if (force_local) {
133                 (void) btrfs_subvol_remove(p);
134                 (void) rm_rf_dangerous(p, false, true, false);
135         }
136
137         r = btrfs_subvol_snapshot(final, p, false, false);
138         if (r == -ENOTTY) {
139                 r = copy_tree(final, p, false);
140                 if (r < 0)
141                         return log_error_errno(r, "Failed to copy image: %m");
142         } else if (r < 0)
143                 return log_error_errno(r, "Failed to create local image: %m");
144
145         log_info("Created new local image '%s'.", local);
146
147         return 0;
148 }
149
150 int import_make_read_only_fd(int fd) {
151         int r;
152
153         assert(fd >= 0);
154
155         /* First, let's make this a read-only subvolume if it refers
156          * to a subvolume */
157         r = btrfs_subvol_set_read_only_fd(fd, true);
158         if (r == -ENOTTY || r == -ENOTDIR || r == -EINVAL) {
159                 struct stat st;
160
161                 /* This doesn't refer to a subvolume, or the file
162                  * system isn't even btrfs. In that, case fall back to
163                  * chmod()ing */
164
165                 r = fstat(fd, &st);
166                 if (r < 0)
167                         return log_error_errno(errno, "Failed to stat temporary image: %m");
168
169                 /* Drop "w" flag */
170                 if (fchmod(fd, st.st_mode & 07555) < 0)
171                         return log_error_errno(errno, "Failed to chmod() final image: %m");
172
173                 return 0;
174
175         } else if (r < 0)
176                 return log_error_errno(r, "Failed to make subvolume read-only: %m");
177
178         return 0;
179 }
180
181 int import_make_read_only(const char *path) {
182         _cleanup_close_ int fd = 1;
183
184         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
185         if (fd < 0)
186                 return log_error_errno(errno, "Failed to open %s: %m", path);
187
188         return import_make_read_only_fd(fd);
189 }
190
191 int import_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret) {
192         _cleanup_free_ char *escaped_url = NULL;
193         char *path;
194
195         assert(url);
196         assert(ret);
197
198         if (!image_root)
199                 image_root = "/var/lib/machines";
200
201         escaped_url = xescape(url, FILENAME_ESCAPE);
202         if (!escaped_url)
203                 return -ENOMEM;
204
205         if (etag) {
206                 _cleanup_free_ char *escaped_etag = NULL;
207
208                 escaped_etag = xescape(etag, FILENAME_ESCAPE);
209                 if (!escaped_etag)
210                         return -ENOMEM;
211
212                 path = strjoin(image_root, "/", strempty(prefix), escaped_url, ".", escaped_etag, strempty(suffix), NULL);
213         } else
214                 path = strjoin(image_root, "/", strempty(prefix), escaped_url, strempty(suffix), NULL);
215         if (!path)
216                 return -ENOMEM;
217
218         *ret = path;
219         return 0;
220 }
221
222 int import_url_last_component(const char *url, char **ret) {
223         const char *e, *p;
224         char *s;
225
226         e = strchrnul(url, '?');
227
228         while (e > url && e[-1] == '/')
229                 e--;
230
231         p = e;
232         while (p > url && p[-1] != '/')
233                 p--;
234
235         if (e <= p)
236                 return -EINVAL;
237
238         s = strndup(p, e - p);
239         if (!s)
240                 return -ENOMEM;
241
242         *ret = s;
243         return 0;
244 }
245
246
247 int import_url_change_last_component(const char *url, const char *suffix, char **ret) {
248         const char *e;
249         char *s;
250
251         assert(url);
252         assert(ret);
253
254         e = strchrnul(url, '?');
255
256         while (e > url && e[-1] == '/')
257                 e--;
258
259         while (e > url && e[-1] != '/')
260                 e--;
261
262         if (e <= url)
263                 return -EINVAL;
264
265         s = new(char, (e - url) + strlen(suffix) + 1);
266         if (!s)
267                 return -ENOMEM;
268
269         strcpy(mempcpy(s, url, e - url), suffix);
270         *ret = s;
271         return 0;
272 }