Make sure that modified symlinks are preserved.
When removing a package, symlinks owned by this package that have been modified should be preserved, unless the force flag is set via xbps-remove(8).
This commit is contained in:
		| @@ -92,6 +92,43 @@ check_remove_pkg_files(struct xbps_handle *xhp, | ||||
| 	return fail; | ||||
| } | ||||
|  | ||||
| static char * | ||||
| symlink_target(struct xbps_handle *xhp, const char *path) | ||||
| { | ||||
| 	struct stat sb; | ||||
| 	char *lnk, *res; | ||||
| 	ssize_t r; | ||||
|  | ||||
| 	if (lstat(path, &sb) == -1) | ||||
| 		return NULL; | ||||
|  | ||||
| 	lnk = malloc(sb.st_size + 1); | ||||
| 	assert(lnk); | ||||
|  | ||||
| 	r = readlink(path, lnk, sb.st_size + 1); | ||||
| 	if (r < 0 || r > sb.st_size) { | ||||
| 		free(lnk); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	lnk[sb.st_size] = '\0'; | ||||
| 	if ((strncmp(lnk, "../", 3) == 0) || strchr(lnk, '/') == NULL) { | ||||
| 		char *p, *dname; | ||||
|  | ||||
| 		/* relative */ | ||||
| 		p = strdup(path); | ||||
| 		assert(p); | ||||
| 		dname = dirname(p); | ||||
| 		assert(dname); | ||||
| 		dname += strlen(xhp->rootdir) + 1; | ||||
| 		res = xbps_xasprintf("%s/%s", dname, lnk); | ||||
| 		free(lnk); | ||||
| 	} else { | ||||
| 		/* absolute */ | ||||
| 		res = lnk; | ||||
| 	} | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
| static int | ||||
| remove_pkg_files(struct xbps_handle *xhp, | ||||
| 		 xbps_dictionary_t dict, | ||||
| @@ -101,7 +138,7 @@ remove_pkg_files(struct xbps_handle *xhp, | ||||
| 	xbps_array_t array; | ||||
| 	xbps_object_iterator_t iter; | ||||
| 	xbps_object_t obj; | ||||
| 	const char *file, *sha256, *curobj = NULL; | ||||
| 	const char *curobj = NULL; | ||||
| 	/* These are symlinks in Void and must not be removed */ | ||||
| 	const char *basesymlinks[] = { | ||||
| 		"/bin", | ||||
| @@ -113,9 +150,7 @@ remove_pkg_files(struct xbps_handle *xhp, | ||||
| 		"/usr/lib64", | ||||
| 		"/var/run", | ||||
| 	}; | ||||
| 	char path[PATH_MAX]; | ||||
| 	int rv = 0; | ||||
| 	bool found; | ||||
|  | ||||
| 	assert(xbps_object_type(dict) == XBPS_TYPE_DICTIONARY); | ||||
| 	assert(key != NULL); | ||||
| @@ -140,6 +175,10 @@ remove_pkg_files(struct xbps_handle *xhp, | ||||
| 	xbps_object_iterator_reset(iter); | ||||
|  | ||||
| 	while ((obj = xbps_object_iterator_next(iter))) { | ||||
| 		const char *file, *sha256; | ||||
| 		char path[PATH_MAX]; | ||||
| 		bool found; | ||||
|  | ||||
| 		xbps_dictionary_get_cstring_nocopy(obj, "file", &file); | ||||
| 		snprintf(path, sizeof(path), "%s/%s", xhp->rootdir, file); | ||||
|  | ||||
| @@ -205,6 +244,29 @@ remove_pkg_files(struct xbps_handle *xhp, | ||||
| 		if (found) { | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (strcmp(key, "links") == 0) { | ||||
| 			const char *target = NULL; | ||||
| 			char *lnk; | ||||
|  | ||||
| 			lnk = symlink_target(xhp, path); | ||||
| 			if (lnk == NULL) { | ||||
| 				xbps_dbg_printf(xhp, "[remove] %s " | ||||
| 				    "symlink_target: %s\n", path, strerror(errno)); | ||||
| 				continue; | ||||
| 			} | ||||
| 			xbps_dictionary_get_cstring_nocopy(obj, "target", &target); | ||||
| 			assert(target); | ||||
| 			if (strcmp(lnk, target)) { | ||||
| 				xbps_dbg_printf(xhp, "[remove] %s symlink " | ||||
| 				    "modified (stored %s current %s)\n", path, | ||||
| 				    target, lnk); | ||||
| 				if ((xhp->flags & XBPS_FLAG_FORCE_REMOVE_FILES) == 0) { | ||||
| 					free(lnk); | ||||
| 					continue; | ||||
| 				} | ||||
| 			} | ||||
| 			free(lnk); | ||||
| 		} | ||||
| 		/* | ||||
| 		 * Remove the object if possible. | ||||
| 		 */ | ||||
|   | ||||
| @@ -105,6 +105,69 @@ remove_symlinks_from_root_body() { | ||||
| 	atf_check_equal $rv 0 | ||||
| } | ||||
|  | ||||
| atf_test_case keep_modified_symlinks | ||||
|  | ||||
| keep_modified_symlinks_head() { | ||||
| 	atf_set "descr" "Tests for package removal: keep modified symlinks in rootdir" | ||||
| } | ||||
|  | ||||
| keep_modified_symlinks_body() { | ||||
| 	mkdir some_repo | ||||
| 	mkdir -p pkg_A/bin | ||||
| 	ln -sf /bin/bash pkg_A/bin/ash | ||||
| 	ln -sf /bin/bash pkg_A/bin/sh | ||||
|  | ||||
| 	cd some_repo | ||||
| 	xbps-create -A noarch -n A-1.0_1 -s "A pkg" ../pkg_A | ||||
| 	atf_check_equal $? 0 | ||||
| 	xbps-rindex -a *.xbps | ||||
| 	atf_check_equal $? 0 | ||||
| 	cd .. | ||||
| 	xbps-install -r root -C null.conf --repository=$PWD/some_repo -y A | ||||
| 	atf_check_equal $? 0 | ||||
| 	ln -sf /bin/foo root/bin/sh | ||||
| 	ln -sf foo root/bin/ash | ||||
|  | ||||
| 	xbps-remove -r root -yvd A | ||||
| 	atf_check_equal $? 0 | ||||
| 	rv=1 | ||||
| 	if [ -h root/bin/sh -a -h root/bin/ash ]; then | ||||
| 	        rv=0 | ||||
| 	fi | ||||
| 	atf_check_equal $rv 0 | ||||
| } | ||||
|  | ||||
| atf_test_case remove_symlinks_modified | ||||
|  | ||||
| remove_symlinks_modified_head() { | ||||
| 	atf_set "descr" "Tests for package removal: force remove modified symlinks in rootdir" | ||||
| } | ||||
|  | ||||
| remove_symlinks_modified_body() { | ||||
| 	mkdir some_repo | ||||
| 	mkdir -p pkg_A/bin | ||||
| 	ln -sf /bin/bash pkg_A/bin/ash | ||||
| 	ln -sf /bin/bash pkg_A/bin/sh | ||||
|  | ||||
| 	cd some_repo | ||||
| 	xbps-create -A noarch -n A-1.0_1 -s "A pkg" ../pkg_A | ||||
| 	atf_check_equal $? 0 | ||||
| 	xbps-rindex -a *.xbps | ||||
| 	atf_check_equal $? 0 | ||||
| 	cd .. | ||||
| 	xbps-install -r root -C null.conf --repository=$PWD/some_repo -y A | ||||
| 	atf_check_equal $? 0 | ||||
| 	ln -sf /bin/foo root/bin/sh | ||||
|  | ||||
| 	xbps-remove -r root -yvfd A | ||||
| 	atf_check_equal $? 0 | ||||
| 	rv=0 | ||||
| 	if [ -h root/bin/sh -o -h root/bin/ash ]; then | ||||
| 	        rv=1 | ||||
| 	fi | ||||
| 	atf_check_equal $rv 0 | ||||
| } | ||||
|  | ||||
| atf_test_case remove_readonly_files | ||||
|  | ||||
| remove_readonly_files_head() { | ||||
| @@ -160,8 +223,10 @@ remove_dups_body() { | ||||
|  | ||||
| atf_init_test_cases() { | ||||
| 	atf_add_test_case keep_base_symlinks | ||||
| 	atf_add_test_case keep_modified_symlinks | ||||
| 	atf_add_test_case remove_readonly_files | ||||
| 	atf_add_test_case remove_symlinks | ||||
| 	atf_add_test_case remove_symlinks_from_root | ||||
| 	atf_add_test_case remove_symlinks_modified | ||||
| 	atf_add_test_case remove_dups | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user