358 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			358 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*-
 | 
						|
 * Copyright (c) 2009-2012 Juan Romero Pardines.
 | 
						|
 * All rights reserved.
 | 
						|
 *
 | 
						|
 * Redistribution and use in source and binary forms, with or without
 | 
						|
 * modification, are permitted provided that the following conditions
 | 
						|
 * are met:
 | 
						|
 * 1. Redistributions of source code must retain the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer.
 | 
						|
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer in the
 | 
						|
 *    documentation and/or other materials provided with the distribution.
 | 
						|
 *
 | 
						|
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 | 
						|
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 | 
						|
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 | 
						|
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 | 
						|
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 | 
						|
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
						|
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
						|
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
						|
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 | 
						|
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdbool.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <dirent.h>
 | 
						|
#include <libgen.h>
 | 
						|
#include <sys/utsname.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
 | 
						|
#include <xbps_api.h>
 | 
						|
#include "defs.h"
 | 
						|
 | 
						|
/*
 | 
						|
 * Removes stalled pkg entries in repository's index.plist file, if any
 | 
						|
 * binary package cannot be read (unavailable, not enough perms, etc).
 | 
						|
 */
 | 
						|
static int
 | 
						|
remove_missing_binpkg_entries(const char *repodir)
 | 
						|
{
 | 
						|
	prop_array_t array;
 | 
						|
	prop_dictionary_t pkgd;
 | 
						|
	const char *filen;
 | 
						|
	char *binpkg, *plist;
 | 
						|
	size_t i;
 | 
						|
	int rv = 0;
 | 
						|
	bool found = false;
 | 
						|
 | 
						|
	plist = xbps_pkg_index_plist(repodir);
 | 
						|
	if (plist == NULL)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	array = prop_array_internalize_from_zfile(plist);
 | 
						|
	if (array == NULL) {
 | 
						|
		if (errno != ENOENT) {
 | 
						|
			xbps_error_printf("xbps-repo: cannot read `%s': %s\n",
 | 
						|
			    plist, strerror(errno));
 | 
						|
			exit(EXIT_FAILURE);
 | 
						|
		} else {
 | 
						|
			free(plist);
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
again:
 | 
						|
	for (i = 0; i < prop_array_count(array); i++) {
 | 
						|
		pkgd = prop_array_get(array, i);
 | 
						|
		prop_dictionary_get_cstring_nocopy(pkgd, "filename", &filen);
 | 
						|
		binpkg = xbps_xasprintf("%s/%s", repodir, filen);
 | 
						|
		if (binpkg == NULL) {
 | 
						|
			errno = ENOMEM;
 | 
						|
			rv = -1;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		if (access(binpkg, R_OK) == -1) {
 | 
						|
			xbps_warn_printf("xbps-repo: `%s' unavailable, "
 | 
						|
			    "removing entry from index... (%s)\n",
 | 
						|
			    filen, strerror(errno));
 | 
						|
			prop_array_remove(array, i);
 | 
						|
			free(binpkg);
 | 
						|
			found = true;
 | 
						|
			goto again;
 | 
						|
		}
 | 
						|
		free(binpkg);
 | 
						|
	}
 | 
						|
	if (found) {
 | 
						|
		if (!prop_array_externalize_to_zfile(array, plist))
 | 
						|
			rv = errno;
 | 
						|
	}
 | 
						|
	free(plist);
 | 
						|
 | 
						|
	return rv;
 | 
						|
}
 | 
						|
 | 
						|
static prop_array_t
 | 
						|
repoidx_get(const char *pkgdir)
 | 
						|
{
 | 
						|
	prop_array_t array;
 | 
						|
	char *plist;
 | 
						|
	int rv;
 | 
						|
	/*
 | 
						|
	 * Remove entries in repositories index for unexistent
 | 
						|
	 * packages, i.e dangling entries.
 | 
						|
	 */
 | 
						|
	if ((rv = remove_missing_binpkg_entries(pkgdir)) != 0)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	plist = xbps_pkg_index_plist(pkgdir);
 | 
						|
	if (plist == NULL)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	array = prop_array_internalize_from_zfile(plist);
 | 
						|
	free(plist);
 | 
						|
	if (array == NULL)
 | 
						|
		array = prop_array_create();
 | 
						|
 | 
						|
	return array;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
add_binpkg_to_index(prop_array_t idx,
 | 
						|
		    const char *filedir,
 | 
						|
		    const char *file)
 | 
						|
{
 | 
						|
	prop_dictionary_t newpkgd, curpkgd;
 | 
						|
	struct stat st;
 | 
						|
	const char *pkgname, *version, *regver, *oldfilen, *oldpkgver;
 | 
						|
	char *sha256, *filen, *tmpfilen, *oldfilepath, *buf;
 | 
						|
	int rv = 0;
 | 
						|
 | 
						|
	tmpfilen = strdup(file);
 | 
						|
	if (tmpfilen == NULL)
 | 
						|
		return errno;
 | 
						|
 | 
						|
	filen = basename(tmpfilen);
 | 
						|
	if (strcmp(tmpfilen, filen) == 0) {
 | 
						|
		rv = EINVAL;
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
 | 
						|
	newpkgd = xbps_dictionary_metadata_plist_by_url(file, XBPS_PKGPROPS);
 | 
						|
	if (newpkgd == NULL) {
 | 
						|
		xbps_error_printf("failed to read %s metadata for `%s',"
 | 
						|
		    " skipping!\n", XBPS_PKGPROPS, file);
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
	prop_dictionary_get_cstring_nocopy(newpkgd, "pkgname", &pkgname);
 | 
						|
	prop_dictionary_get_cstring_nocopy(newpkgd, "version", &version);
 | 
						|
	/*
 | 
						|
	 * Check if this package exists already in the index, but first
 | 
						|
	 * checking the version. If current package version is greater
 | 
						|
	 * than current registered package, update the index; otherwise
 | 
						|
	 * pass to the next one.
 | 
						|
	 */
 | 
						|
	curpkgd = xbps_find_pkg_in_array_by_name(idx, pkgname);
 | 
						|
	if (curpkgd == NULL) {
 | 
						|
		if (errno && errno != ENOENT) {
 | 
						|
			prop_object_release(newpkgd);
 | 
						|
			rv = errno;
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
	} else if (curpkgd) {
 | 
						|
		prop_dictionary_get_cstring_nocopy(curpkgd, "version", ®ver);
 | 
						|
		if (xbps_cmpver(version, regver) <= 0) {
 | 
						|
			xbps_warn_printf("skipping `%s', `%s-%s' already "
 | 
						|
			    "registered.\n", filen, pkgname, regver);
 | 
						|
			prop_object_release(newpkgd);
 | 
						|
			rv = EEXIST;
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		/*
 | 
						|
		 * Current binpkg is newer than the one registered
 | 
						|
		 * in package index, remove outdated binpkg file
 | 
						|
		 * and its dictionary from the pkg index.
 | 
						|
		 */
 | 
						|
		prop_dictionary_get_cstring_nocopy(curpkgd,
 | 
						|
		    "filename", &oldfilen);
 | 
						|
		prop_dictionary_get_cstring_nocopy(curpkgd,
 | 
						|
		    "pkgver", &oldpkgver);
 | 
						|
		buf = strdup(oldpkgver);
 | 
						|
		if (buf == NULL) {
 | 
						|
			prop_object_release(newpkgd);
 | 
						|
			rv = ENOMEM;
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		oldfilepath = xbps_xasprintf("%s/%s", filedir, oldfilen);
 | 
						|
		if (oldfilepath == NULL) {
 | 
						|
			rv = errno;
 | 
						|
			prop_object_release(newpkgd);
 | 
						|
			free(buf);
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		if (remove(oldfilepath) == -1) {
 | 
						|
			rv = errno;
 | 
						|
			xbps_error_printf("failed to remove old "
 | 
						|
			    "package file `%s': %s\n", oldfilepath,
 | 
						|
			    strerror(errno));
 | 
						|
			free(oldfilepath);
 | 
						|
			prop_object_release(newpkgd);
 | 
						|
			free(buf);
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		free(oldfilepath);
 | 
						|
		if (!xbps_remove_pkg_from_array_by_name(idx, pkgname)) {
 | 
						|
			xbps_error_printf("failed to remove `%s' "
 | 
						|
			    "from plist index: %s\n", pkgname, strerror(errno));
 | 
						|
			prop_object_release(newpkgd);
 | 
						|
			free(buf);
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		printf("Removed obsolete package entry/binpkg for `%s'.\n",
 | 
						|
		    buf);
 | 
						|
		free(buf);
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * We have the dictionary now, add the required
 | 
						|
	 * objects for the index.
 | 
						|
	 */
 | 
						|
	if (!prop_dictionary_set_cstring(newpkgd, "filename", filen)) {
 | 
						|
		prop_object_release(newpkgd);
 | 
						|
		rv = errno;
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
	sha256 = xbps_file_hash(file);
 | 
						|
	if (sha256 == NULL) {
 | 
						|
		prop_object_release(newpkgd);
 | 
						|
		rv = errno;
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
	if (!prop_dictionary_set_cstring(newpkgd, "filename-sha256", sha256)) {
 | 
						|
		prop_object_release(newpkgd);
 | 
						|
		free(sha256);
 | 
						|
		rv = errno;
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
	free(sha256);
 | 
						|
	if (stat(file, &st) == -1) {
 | 
						|
		prop_object_release(newpkgd);
 | 
						|
		rv = errno;
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
	if (!prop_dictionary_set_uint64(newpkgd, "filename-size",
 | 
						|
	    (uint64_t)st.st_size)) {
 | 
						|
		prop_object_release(newpkgd);
 | 
						|
		rv = errno;
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
	/*
 | 
						|
	 * Add dictionary into the index and update package count.
 | 
						|
	 */
 | 
						|
	if (!xbps_add_obj_to_array(idx, newpkgd)) {
 | 
						|
		rv = EINVAL;
 | 
						|
		goto out;
 | 
						|
	}
 | 
						|
	printf("Registered `%s-%s' (%s) in repository index.\n",
 | 
						|
	    pkgname, version, filen);
 | 
						|
 | 
						|
out:
 | 
						|
	if (tmpfilen)
 | 
						|
		free(tmpfilen);
 | 
						|
 | 
						|
	return rv;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
repo_genindex(const char *pkgdir)
 | 
						|
{
 | 
						|
	prop_array_t idx = NULL;
 | 
						|
	struct dirent *dp;
 | 
						|
	DIR *dirp;
 | 
						|
	char *binfile, *plist;
 | 
						|
	int rv = 0;
 | 
						|
	bool registered_newpkgs = false, foundpkg = false;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Create or read existing package index plist file.
 | 
						|
	 */
 | 
						|
	idx = repoidx_get(pkgdir);
 | 
						|
	if (idx == NULL)
 | 
						|
		return errno;
 | 
						|
 | 
						|
	plist = xbps_pkg_index_plist(pkgdir);
 | 
						|
	if (plist == NULL) {
 | 
						|
		prop_object_release(idx);
 | 
						|
		return errno;
 | 
						|
	}
 | 
						|
 | 
						|
	dirp = opendir(pkgdir);
 | 
						|
	if (dirp == NULL) {
 | 
						|
		xbps_error_printf("xbps-repo: cannot open `%s': %s\n",
 | 
						|
		    pkgdir, strerror(errno));
 | 
						|
		exit(EXIT_FAILURE);
 | 
						|
	}
 | 
						|
 | 
						|
	while ((dp = readdir(dirp)) != NULL) {
 | 
						|
		if ((strcmp(dp->d_name, ".") == 0) ||
 | 
						|
		    (strcmp(dp->d_name, "..") == 0))
 | 
						|
			continue;
 | 
						|
 | 
						|
		/* Ignore unknown files */
 | 
						|
		if (strstr(dp->d_name, ".xbps") == NULL)
 | 
						|
			continue;
 | 
						|
 | 
						|
		foundpkg = true;
 | 
						|
		binfile = xbps_xasprintf("%s/%s", pkgdir, dp->d_name);
 | 
						|
		if (binfile == NULL) {
 | 
						|
			(void)closedir(dirp);
 | 
						|
			rv = errno;
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		rv = add_binpkg_to_index(idx, pkgdir, binfile);
 | 
						|
		free(binfile);
 | 
						|
		if (rv == EEXIST) {
 | 
						|
			rv = 0;
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		else if (rv != 0) {
 | 
						|
			(void)closedir(dirp);
 | 
						|
			goto out;
 | 
						|
		}
 | 
						|
		registered_newpkgs = true;
 | 
						|
	}
 | 
						|
	(void)closedir(dirp);
 | 
						|
 | 
						|
	if (foundpkg == false) {
 | 
						|
		/* No packages were found in directory */
 | 
						|
		rv = ENOENT;
 | 
						|
	} else {
 | 
						|
		/*
 | 
						|
		 * Show total count registered packages.
 | 
						|
		 */
 | 
						|
		printf("%zu packages registered in repository index.\n",
 | 
						|
		    (size_t)prop_array_count(idx));
 | 
						|
		/*
 | 
						|
		 * Don't write plist file if no packages were registered.
 | 
						|
		 */
 | 
						|
		if (registered_newpkgs == false)
 | 
						|
			goto out;
 | 
						|
		/*
 | 
						|
		 * If any package was registered in package index, write
 | 
						|
		 * plist file to storage.
 | 
						|
		 */
 | 
						|
		if (!prop_array_externalize_to_zfile(idx, plist))
 | 
						|
			rv = errno;
 | 
						|
	}
 | 
						|
out:
 | 
						|
	free(plist);
 | 
						|
	prop_object_release(idx);
 | 
						|
 | 
						|
	return rv;
 | 
						|
}
 |