1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 
5 #include "alloc-util.h"
6 #include "btrfs-util.h"
7 #include "chattr-util.h"
8 #include "errno-util.h"
9 #include "import-util.h"
10 #include "log.h"
11 #include "macro.h"
12 #include "nulstr-util.h"
13 #include "path-util.h"
14 #include "string-table.h"
15 #include "string-util.h"
16 
skip_protocol_and_hostname(const char * url)17 static const char *skip_protocol_and_hostname(const char *url) {
18         const char *d;
19         size_t n;
20 
21         /* A very very lenient implementation of RFC3986 Section 3.2 */
22 
23         /* Find colon separating protocol and hostname */
24         d = strchr(url, ':');
25         if (!d || url == d)
26                 return NULL;
27         d++;
28 
29         /* Skip slashes after colon */
30         d += strspn(d, "/");
31 
32         /* Skip everything till next slash or end */
33         n = strcspn(d, "/?#");
34         if (n == 0)
35                 return NULL;
36 
37         return d + n;
38 }
39 
import_url_last_component(const char * url,char ** ret)40 int import_url_last_component(
41                 const char *url,
42                 char **ret) {
43 
44         const char *e, *p, *h;
45 
46         /* This extracts the last path component of the specified URI, i.e. the last non-empty substrings
47          * between two "/" characters. This ignores "Query" and "Fragment" suffixes (as per RFC3986). */
48 
49         h = skip_protocol_and_hostname(url);
50         if (!h)
51                 return -EINVAL;
52 
53         e = h + strcspn(h, "?#"); /* Cut off "Query" and "Fragment" */
54 
55         while (e > h && e[-1] == '/') /* Eat trailing slashes */
56                 e--;
57 
58         p = e;
59         while (p > h && p[-1] != '/') /* Find component before that */
60                 p--;
61 
62         if (e <= p) /* Empty component? */
63                 return -EADDRNOTAVAIL;
64 
65         if (ret) {
66                 char *s;
67 
68                 s = strndup(p, e - p);
69                 if (!s)
70                         return -ENOMEM;
71 
72                 *ret = s;
73         }
74 
75         return 0;
76 }
77 
import_url_change_suffix(const char * url,size_t n_drop_components,const char * suffix,char ** ret)78 int import_url_change_suffix(
79                 const char *url,
80                 size_t n_drop_components,
81                 const char *suffix,
82                 char **ret) {
83 
84         const char *e, *h;
85         char *s;
86 
87         assert(url);
88         assert(ret);
89 
90         /* This drops the specified number of path components of the specified URI, i.e. the specified number
91          * of non-empty substring between two "/" characters from the end of the string, and then append the
92          * specified suffix instead. Before doing all this it chops off the "Query" and "Fragment" suffixes
93          * (they are *not* re-added to the final URL). Note that n_drop_components may be 0 (in which case the
94          * component are simply added to the end). The suffix may be specified as NULL or empty string in
95          * which case nothing is appended, only the specified number of components chopped off. Note that the
96          * function may be called with n_drop_components == 0 and suffix == NULL, in which case the "Query"
97          * and "Fragment" is chopped off, and ensured the URL ends in a single "/", and that's it. */
98 
99         h = skip_protocol_and_hostname(url);
100         if (!h)
101                 return -EINVAL;
102 
103         e = h + strcspn(h, "?#"); /* Cut off "Query" and "Fragment" */
104 
105         while (e > h && e[-1] == '/') /* Eat trailing slashes */
106                 e--;
107 
108         /* Drop the specified number of components from the end. Note that this is pretty lenient: if there
109          * are less component we silently drop those and then append the suffix to the top. */
110         while (n_drop_components > 0) {
111                 while (e > h && e[-1] != '/') /* Eat last word (we don't mind if empty) */
112                         e--;
113 
114                 while (e > h && e[-1] == '/') /* Eat slashes before the last word */
115                         e--;
116 
117                 n_drop_components--;
118         }
119 
120         s = new(char, (e - url) + 1 + strlen_ptr(suffix) + 1);
121         if (!s)
122                 return -ENOMEM;
123 
124         strcpy(stpcpy(mempcpy(s, url, e - url), "/"), strempty(suffix));
125         *ret = s;
126         return 0;
127 }
128 
129 static const char* const import_verify_table[_IMPORT_VERIFY_MAX] = {
130         [IMPORT_VERIFY_NO] = "no",
131         [IMPORT_VERIFY_CHECKSUM] = "checksum",
132         [IMPORT_VERIFY_SIGNATURE] = "signature",
133 };
134 
135 DEFINE_STRING_TABLE_LOOKUP(import_verify, ImportVerify);
136 
tar_strip_suffixes(const char * name,char ** ret)137 int tar_strip_suffixes(const char *name, char **ret) {
138         const char *e;
139         char *s;
140 
141         e = endswith(name, ".tar");
142         if (!e)
143                 e = endswith(name, ".tar.xz");
144         if (!e)
145                 e = endswith(name, ".tar.gz");
146         if (!e)
147                 e = endswith(name, ".tar.bz2");
148         if (!e)
149                 e = endswith(name, ".tgz");
150         if (!e)
151                 e = strchr(name, 0);
152 
153         if (e <= name)
154                 return -EINVAL;
155 
156         s = strndup(name, e - name);
157         if (!s)
158                 return -ENOMEM;
159 
160         *ret = s;
161         return 0;
162 }
163 
raw_strip_suffixes(const char * p,char ** ret)164 int raw_strip_suffixes(const char *p, char **ret) {
165 
166         static const char suffixes[] =
167                 ".xz\0"
168                 ".gz\0"
169                 ".bz2\0"
170                 ".raw\0"
171                 ".qcow2\0"
172                 ".img\0"
173                 ".bin\0";
174 
175         _cleanup_free_ char *q = NULL;
176 
177         q = strdup(p);
178         if (!q)
179                 return -ENOMEM;
180 
181         for (;;) {
182                 const char *sfx;
183                 bool changed = false;
184 
185                 NULSTR_FOREACH(sfx, suffixes) {
186                         char *e;
187 
188                         e = endswith(q, sfx);
189                         if (e) {
190                                 *e = 0;
191                                 changed = true;
192                         }
193                 }
194 
195                 if (!changed)
196                         break;
197         }
198 
199         *ret = TAKE_PTR(q);
200 
201         return 0;
202 }
203 
import_assign_pool_quota_and_warn(const char * path)204 int import_assign_pool_quota_and_warn(const char *path) {
205         int r;
206 
207         assert(path);
208 
209         r = btrfs_subvol_auto_qgroup(path, 0, true);
210         if (r == -ENOTTY) {
211                 log_debug_errno(r, "Failed to set up quota hierarchy for %s, as directory is not on btrfs or not a subvolume. Ignoring.", path);
212                 return 0;
213         }
214         if (r < 0)
215                 return log_error_errno(r, "Failed to set up default quota hierarchy for %s: %m", path);
216         if (r > 0)
217                 log_debug("Set up default quota hierarchy for %s.", path);
218 
219         return 0;
220 }
221 
import_set_nocow_and_log(int fd,const char * path)222 int import_set_nocow_and_log(int fd, const char *path) {
223         int r;
224 
225         r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
226         if (r < 0)
227                 return log_full_errno(
228                                 ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING,
229                                 r, "Failed to set file attributes on %s: %m", path);
230 
231         return 0;
232 }
233