Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
fetch: add -s/--symlink option for mirror mode
Vsevolod Stakhov committed 3 months ago
commit 5a98c3ff4714ccee4ed1ac6fd5c79d44bea05b7d
parent 625cb59
8 files changed +80 -24
modified docs/pkg-fetch.8
@@ -30,7 +30,7 @@
.Nm
.Op Fl r Ar reponame
.Op Fl o Ar destdir
-
.Op Fl dqUy
+
.Op Fl dqsUy
.Fl a
.Nm
.Op Fl r Ar reponame
@@ -40,13 +40,13 @@
.Nm
.Op Cm --repository Ar reponame
.Op Cm --output Ar destdir
-
.Op Cm --{dependencies,quiet,no-repo-update,yes}
+
.Op Cm --{dependencies,quiet,symlink,no-repo-update,yes}
.Op Cm --{case-sensitive,glob,case-insensitive,regex}
.Ar pkg-name
.Op ...
.Nm
.Op Cm --repository Ar reponame
-
.Op Cm --{dependencies,quiet,no-repo-update,yes}
+
.Op Cm --{dependencies,quiet,symlink,no-repo-update,yes}
.Cm --all
.Nm
.Op Cm --repository Ar reponame
@@ -110,6 +110,17 @@ Fetches packages from the given
if multiple repo support is enabled.
See
.Xr pkg.conf 5 .
+
.It Fl s , Cm --symlink
+
Create a symlink to the fetched package in the
+
.Pa All
+
directory when using
+
.Fl o
+
to fetch packages for distribution.
+
This is useful when the remote repository stores packages in a hashed
+
directory structure
+
.Pa ( All/Hashed/xx/ )
+
but local tools expect packages directly in
+
.Pa All/ .
.It Fl u , Cm --available-updates
Fetch all available updates for the currently installed packages.
.It Fl U , Cm --no-repo-update
@@ -130,9 +141,14 @@ Assume yes when asked for confirmation before fetching packages.
.Sh ENVIRONMENT
The following environment variables affect the execution of
.Nm :
-
.Bl -tag -width ".Ev NO_DESCRIPTIONS"
+
.Bl -tag -width ".Ev PKG_REPO_SYMLINK"
.It Ev PKG_DBDIR
.It Ev CASE_SENSITIVE_MATCH
+
.It Ev PKG_REPO_SYMLINK
+
If set, enables symlink creation when fetching packages with
+
.Fl o ,
+
equivalent to specifying
+
.Fl s .
.El
.Pp
See
modified libpkg/pkg.h.in
@@ -417,7 +417,8 @@ typedef enum _pkg_flags {
	PKG_FLAG_UPGRADE_VULNERABLE = (1U << 13),
	PKG_FLAG_NOEXEC = (1U << 14),
	PKG_FLAG_KEEPFILES = (1U << 15),
-
	PKG_FLAG_REGISTER_ONLY = (1U << 16)
+
	PKG_FLAG_REGISTER_ONLY = (1U << 16),
+
	PKG_FLAG_FETCH_SYMLINK = (1U << 17)
} pkg_flags;

typedef enum _pkg_stats_t {
modified libpkg/pkg_jobs.c
@@ -2218,6 +2218,7 @@ pkg_jobs_fetch(struct pkg_jobs *j)
	const char *cachedir = NULL;
	char cachedpath[MAXPATHLEN];
	bool mirror = (j->flags & PKG_FLAG_FETCH_MIRROR) ? true : false;
+
	bool symlink = (j->flags & PKG_FLAG_FETCH_SYMLINK) ? true : false;
	int retcode;


@@ -2301,7 +2302,7 @@ pkg_jobs_fetch(struct pkg_jobs *j)
				continue;

			if (mirror) {
-
				retcode = pkg_repo_mirror_package(p, cachedir);
+
				retcode = pkg_repo_mirror_package(p, cachedir, symlink);
				if (retcode != EPKG_OK)
					return (retcode);
			}
modified libpkg/pkg_repo.c
@@ -1346,7 +1346,7 @@ pkg_repo_fetch_package(struct pkg *pkg)
}

int
-
pkg_repo_mirror_package(struct pkg *pkg, const char *destdir)
+
pkg_repo_mirror_package(struct pkg *pkg, const char *destdir, bool symlink)
{
	struct pkg_repo *repo;

@@ -1361,7 +1361,7 @@ pkg_repo_mirror_package(struct pkg *pkg, const char *destdir)
		return (EPKG_FATAL);
	}

-
	return (repo->ops->mirror_pkg(repo, pkg, destdir));
+
	return (repo->ops->mirror_pkg(repo, pkg, destdir, symlink));
}

int
modified libpkg/private/pkg.h
@@ -518,7 +518,7 @@ struct pkg_repo_ops {
					char *dest, size_t destlen);
	int (*fetch_pkg)(struct pkg_repo *, struct pkg *);
	int (*mirror_pkg)(struct pkg_repo *repo, struct pkg *pkg,
-
		const char *destdir);
+
		const char *destdir, bool symlink);
};

struct pkg_key {
@@ -683,7 +683,7 @@ int pkg_fetch_file_to_fd(struct pkg_repo *repo, int dest, struct fetch_item *,
    bool silent);
int pkg_repo_open(struct pkg_repo *repo);
int pkg_repo_fetch_package(struct pkg *pkg);
-
int pkg_repo_mirror_package(struct pkg *pkg, const char *destdir);
+
int pkg_repo_mirror_package(struct pkg *pkg, const char *destdir, bool symlink);
int pkg_repo_fetch_remote_extract_fd(struct pkg_repo *repo, struct pkg_repo_content *);
int pkg_repo_meta_dump_fd(struct pkg_repo_meta *target, const int fd);
int pkg_repo_fetch_meta(struct pkg_repo *repo, time_t *t);
modified libpkg/repo/binary/binary.h
@@ -65,6 +65,6 @@ int pkg_repo_binary_fetch(struct pkg_repo *repo, struct pkg *pkg);
int pkg_repo_binary_get_cached_name(struct pkg_repo *repo, struct pkg *pkg,
	char *dest, size_t destlen);
int pkg_repo_binary_mirror(struct pkg_repo *repo, struct pkg *pkg,
-
	const char *destdir);
+
	const char *destdir, bool symlink);

#endif /* BINARY_H_ */
modified libpkg/repo/binary/fetch.c
@@ -97,6 +97,9 @@ pkg_repo_binary_create_symlink(struct pkg *pkg, const char *fname,
{
	const char *ext, *dest_fname;
	char link_dest_tmp[MAXPATHLEN], link_dest[MAXPATHLEN];
+
	char link_target[MAXPATHLEN];
+
	struct stat st;
+
	ssize_t link_len;

	/* Create symlink from full pkgname */
	ext = strrchr(fname, '.');
@@ -104,12 +107,29 @@ pkg_repo_binary_create_symlink(struct pkg *pkg, const char *fname,
		dir, pkg, pkg, ext ? ext : "");
	snprintf(link_dest_tmp, sizeof(link_dest_tmp), "%s.new", link_dest);

-
	/* Ignore errors here */
-
	(void)unlink(link_dest_tmp);
-

-
	/* Trim the path to just the filename. */
	if ((dest_fname = strrchr(fname, '/')) != NULL)
		++dest_fname;
+
	else
+
		dest_fname = fname;
+

+
	/* Check if symlink already exists and points to the same target */
+
	if (lstat(link_dest, &st) == 0) {
+
		if (S_ISLNK(st.st_mode)) {
+
			link_len = readlink(link_dest, link_target,
+
			    sizeof(link_target) - 1);
+
			if (link_len > 0) {
+
				link_target[link_len] = '\0';
+
				if (strcmp(link_target, dest_fname) == 0)
+
					return (EPKG_OK);
+
			}
+
			pkg_debug(1, "Replacing symlink %s (was -> %s, now -> %s)",
+
			    link_dest, link_target, dest_fname);
+
		}
+
		(void)unlink(link_dest);
+
	}
+

+
	(void)unlink(link_dest_tmp);
+

	if (symlink(dest_fname, link_dest_tmp) == -1) {
		pkg_emit_errno("symlink", link_dest);
		return (EPKG_FATAL);
@@ -126,11 +146,12 @@ pkg_repo_binary_create_symlink(struct pkg *pkg, const char *fname,

static int
pkg_repo_binary_try_fetch(struct pkg_repo *repo, struct pkg *pkg,
-
	bool already_tried, bool mirror, const char *destdir)
+
	bool already_tried, bool mirror, const char *destdir, bool symlink)
{
	char dest[MAXPATHLEN];
	char url[MAXPATHLEN];
	char *dir = NULL;
+
	char *alldir = NULL;
	bool fetched = false;
	struct stat st;
	const char *packagesite = NULL;
@@ -226,7 +247,7 @@ checksum:
		pkg_emit_error("cached package %s-%s: "
		    "missing or size mismatch, fetching from remote",
		    pkg->name, pkg->version);
-
		return (pkg_repo_binary_try_fetch(repo, pkg, true, mirror, destdir));
+
		return (pkg_repo_binary_try_fetch(repo, pkg, true, mirror, destdir, symlink));
	}
	retval = pkg_checksum_validate_file(dest, pkg->sum);
	if (retval == ENOENT) {
@@ -244,7 +265,7 @@ checksum:
			    "checksum mismatch, fetching from remote",
			    pkg->name, pkg->version);
			unlink(dest);
-
			return (pkg_repo_binary_try_fetch(repo, pkg, true, mirror, destdir));
+
			return (pkg_repo_binary_try_fetch(repo, pkg, true, mirror, destdir, symlink));
		}
	}

@@ -255,8 +276,18 @@ cleanup:
	else if (!mirror && dir != NULL) {
		(void)pkg_repo_binary_create_symlink(pkg, dest, dir);
	}
+
	else if (mirror && symlink && dir != NULL) {
+
		alldir = strdup(dir);
+
		if (alldir != NULL) {
+
			char *hashed = strstr(alldir, "/Hashed");
+
			if (hashed != NULL) {
+
				*hashed = '\0';
+
				(void)pkg_repo_binary_create_symlink(pkg, dest, alldir);
+
			}
+
			free(alldir);
+
		}
+
	}

-
	/* allowed even if dir is NULL */
	free(dir);

	return (retcode);
@@ -265,12 +296,12 @@ cleanup:
int
pkg_repo_binary_fetch(struct pkg_repo *repo, struct pkg *pkg)
{
-
	return (pkg_repo_binary_try_fetch(repo, pkg, false, false, NULL));
+
	return (pkg_repo_binary_try_fetch(repo, pkg, false, false, NULL, false));
}

int
pkg_repo_binary_mirror(struct pkg_repo *repo, struct pkg *pkg,
-
	const char *destdir)
+
	const char *destdir, bool symlink)
{
-
	return (pkg_repo_binary_try_fetch(repo, pkg, false, true, destdir));
+
	return (pkg_repo_binary_try_fetch(repo, pkg, false, true, destdir, symlink));
}
modified src/fetch.c
@@ -44,7 +44,7 @@
void
usage_fetch(void)
{
-
	fprintf(stderr, "Usage: pkg fetch [-r reponame] [-o destdir] [-dqUy] "
+
	fprintf(stderr, "Usage: pkg fetch [-r reponame] [-o destdir] [-dqsUy] "
					"[-Cgix] <pkg-name> <...>\n");
	fprintf(stderr, "       pkg fetch [-r reponame] [-dqUy] -a\n");
	fprintf(stderr, "       pkg fetch [-r reponame] [-dqUy] -u\n\n");
@@ -66,6 +66,9 @@ exec_fetch(int argc, char **argv)
	pkg_flags	 f = PKG_FLAG_NONE;
	c_charv_t	reponames = vec_init();

+
	if (getenv("PKG_REPO_SYMLINK") != NULL)
+
		f |= PKG_FLAG_FETCH_SYMLINK;
+

	struct option longopts[] = {
		{ "all",		no_argument,		NULL,	'a' },
		{ "case-sensitive",	no_argument,		NULL,	'C' },
@@ -74,6 +77,7 @@ exec_fetch(int argc, char **argv)
		{ "case-insensitive",	no_argument,		NULL,	'i' },
		{ "quiet",		no_argument,		NULL,	'q' },
		{ "repository",		required_argument,	NULL,	'r' },
+
		{ "symlink",		no_argument,		NULL,	's' },
		{ "available-updates",	no_argument,		NULL,	'u' },
		{ "no-repo-update",	no_argument,		NULL,	'U' },
		{ "regex",		no_argument,		NULL,	'x' },
@@ -82,7 +86,7 @@ exec_fetch(int argc, char **argv)
		{ NULL,			0,			NULL,	0   },
	};

-
	while ((ch = getopt_long(argc, argv, "+aCdgiqr:Uuxyo:", longopts, NULL)) != -1) {
+
	while ((ch = getopt_long(argc, argv, "+aCdgiqr:sUuxyo:", longopts, NULL)) != -1) {
		switch (ch) {
		case 'a':
			match = MATCH_ALL;
@@ -105,6 +109,9 @@ exec_fetch(int argc, char **argv)
		case 'r':
			vec_push(&reponames, optarg);
			break;
+
		case 's':
+
			f |= PKG_FLAG_FETCH_SYMLINK;
+
			break;
		case 'u':
			f |= PKG_FLAG_UPGRADES_FOR_INSTALLED;
			upgrades_for_installed = true;