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