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