/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include "alloc-util.h" #include "btrfs-util.h" #include "chattr-util.h" #include "errno-util.h" #include "import-util.h" #include "log.h" #include "macro.h" #include "nulstr-util.h" #include "path-util.h" #include "string-table.h" #include "string-util.h" static const char *skip_protocol_and_hostname(const char *url) { const char *d; size_t n; /* A very very lenient implementation of RFC3986 Section 3.2 */ /* Find colon separating protocol and hostname */ d = strchr(url, ':'); if (!d || url == d) return NULL; d++; /* Skip slashes after colon */ d += strspn(d, "/"); /* Skip everything till next slash or end */ n = strcspn(d, "/?#"); if (n == 0) return NULL; return d + n; } int import_url_last_component( const char *url, char **ret) { const char *e, *p, *h; /* This extracts the last path component of the specified URI, i.e. the last non-empty substrings * between two "/" characters. This ignores "Query" and "Fragment" suffixes (as per RFC3986). */ h = skip_protocol_and_hostname(url); if (!h) return -EINVAL; e = h + strcspn(h, "?#"); /* Cut off "Query" and "Fragment" */ while (e > h && e[-1] == '/') /* Eat trailing slashes */ e--; p = e; while (p > h && p[-1] != '/') /* Find component before that */ p--; if (e <= p) /* Empty component? */ return -EADDRNOTAVAIL; if (ret) { char *s; s = strndup(p, e - p); if (!s) return -ENOMEM; *ret = s; } return 0; } int import_url_change_suffix( const char *url, size_t n_drop_components, const char *suffix, char **ret) { const char *e, *h; char *s; assert(url); assert(ret); /* This drops the specified number of path components of the specified URI, i.e. the specified number * of non-empty substring between two "/" characters from the end of the string, and then append the * specified suffix instead. Before doing all this it chops off the "Query" and "Fragment" suffixes * (they are *not* readded to the final URL). Note that n_drop_components may be 0 (in which case the * component are simply added to the end). The suffix may be specified as NULL or empty string in * which case nothing is appended, only the specified number of components chopped off. Note that the * function may be called with n_drop_components == 0 and suffix == NULL, in which case the "Query" * and "Fragment" is chopped off, and ensured the URL ends in a single "/", and that's it. */ h = skip_protocol_and_hostname(url); if (!h) return -EINVAL; e = h + strcspn(h, "?#"); /* Cut off "Query" and "Fragment" */ while (e > h && e[-1] == '/') /* Eat trailing slashes */ e--; /* Drop the specified number of components from the end. Note that this is pretty lenient: if there * are less component we silently drop those and then append the suffix to the top. */ while (n_drop_components > 0) { while (e > h && e[-1] != '/') /* Eat last word (we don't mind if empty) */ e--; while (e > h && e[-1] == '/') /* Eat slashes before the last word */ e--; n_drop_components--; } s = new(char, (e - url) + 1 + strlen_ptr(suffix) + 1); if (!s) return -ENOMEM; strcpy(stpcpy(mempcpy(s, url, e - url), "/"), strempty(suffix)); *ret = s; return 0; } static const char* const import_verify_table[_IMPORT_VERIFY_MAX] = { [IMPORT_VERIFY_NO] = "no", [IMPORT_VERIFY_CHECKSUM] = "checksum", [IMPORT_VERIFY_SIGNATURE] = "signature", }; DEFINE_STRING_TABLE_LOOKUP(import_verify, ImportVerify); int tar_strip_suffixes(const char *name, char **ret) { const char *e; char *s; e = endswith(name, ".tar"); if (!e) e = endswith(name, ".tar.xz"); if (!e) e = endswith(name, ".tar.gz"); if (!e) e = endswith(name, ".tar.bz2"); if (!e) e = endswith(name, ".tgz"); if (!e) e = strchr(name, 0); if (e <= name) return -EINVAL; s = strndup(name, e - name); if (!s) return -ENOMEM; *ret = s; return 0; } int raw_strip_suffixes(const char *p, char **ret) { static const char suffixes[] = ".xz\0" ".gz\0" ".bz2\0" ".raw\0" ".qcow2\0" ".img\0" ".bin\0"; _cleanup_free_ char *q = NULL; q = strdup(p); if (!q) return -ENOMEM; for (;;) { const char *sfx; bool changed = false; NULSTR_FOREACH(sfx, suffixes) { char *e; e = endswith(q, sfx); if (e) { *e = 0; changed = true; } } if (!changed) break; } *ret = TAKE_PTR(q); return 0; } int import_assign_pool_quota_and_warn(const char *path) { int r; assert(path); r = btrfs_subvol_auto_qgroup(path, 0, true); if (r == -ENOTTY) { log_debug_errno(r, "Failed to set up quota hierarchy for %s, as directory is not on btrfs or not a subvolume. Ignoring.", path); return 0; } if (r < 0) return log_error_errno(r, "Failed to set up default quota hierarchy for %s: %m", path); if (r > 0) log_debug("Set up default quota hierarchy for %s.", path); return 0; } int import_set_nocow_and_log(int fd, const char *path) { int r; r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL); if (r < 0) return log_full_errno( ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set file attributes on %s: %m", path); return 0; }