diff --git a/NEWS b/NEWS index 3c9bb542..eb28c263 100644 --- a/NEWS +++ b/NEWS @@ -3,9 +3,7 @@ xbps-0.44 (???): * xbps-pkgdb(8): this now uses the code from libxbps to check the target file of symlinks. This fixes some false positives with symlinks. - * xbps-create(8): relative target file of symlinks aren't converted to absolute - anymore. This just stores what the symlink points to, does not matter if - it's relative or absolute path. + * xbps-create(8): properly detect target of relative symlinks in some cases. * libxbps: make sure that symlinks with relative target are detected and removed properly. Fix #78: https://github.com/voidlinux/xbps/issues/78 diff --git a/bin/xbps-create/main.c b/bin/xbps-create/main.c index 8c47bc64..e089700d 100644 --- a/bin/xbps-create/main.c +++ b/bin/xbps-create/main.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2012-2015 Juan Romero Pardines. + * Copyright (c) 2012-2014 Juan Romero Pardines. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -234,6 +234,8 @@ ftw_cb(const char *fpath, const struct stat *sb, int type, struct FTW *ftwbuf _u { struct xentry *xe = NULL; const char *filep = NULL; + char *buf, *p, *p2, *dname; + ssize_t r; /* Ignore metadata files generated by xbps-src and destdir */ if ((strcmp(fpath, ".") == 0) || @@ -270,10 +272,50 @@ ftw_cb(const char *fpath, const struct stat *sb, int type, struct FTW *ftwbuf _u */ xe->type = strdup("links"); assert(xe->type); - xe->target = xbps_symlink_target(fpath); - if (xe->target == NULL) + buf = malloc(sb->st_size+1); + assert(buf); + r = readlink(fpath, buf, sb->st_size+1); + if (r < 0 || r > sb->st_size) die("failed to process symlink %s:", fpath); + buf[sb->st_size] = '\0'; + /* + * Check if symlink is absolute or relative; on the former + * make it absolute for the target object. + */ + if (strstr(buf, "./")) { + p = realpath(fpath, NULL); + if (p == NULL) { + /* + * This symlink points to an unexistent file, + * which might be provided in another package. + * So let's use the same target. + */ + xe->target = strdup(buf); + } else { + /* + * Sanitize destdir just in case. + */ + if ((p2 = realpath(destdir, NULL)) == NULL) + die("failed to sanitize destdir %s: %s", destdir, strerror(errno)); + + xe->target = strdup(p+strlen(p2)); + free(p2); + free(p); + } + } else if (buf[0] != '/') { + /* relative path */ + p = strdup(filep); + assert(p); + dname = dirname(p); + assert(dname); + xe->target = xbps_xasprintf("%s/%s", dname, buf); + free(p); + } else { + xe->target = strdup(buf); + } + assert(xe->target); + free(buf); } else if (type == FTW_F) { struct xentry *xep; bool hlink = false; diff --git a/bin/xbps-pkgdb/check_pkg_symlinks.c b/bin/xbps-pkgdb/check_pkg_symlinks.c index 94c758d4..1bbbad93 100644 --- a/bin/xbps-pkgdb/check_pkg_symlinks.c +++ b/bin/xbps-pkgdb/check_pkg_symlinks.c @@ -77,7 +77,7 @@ check_pkg_symlinks(struct xbps_handle *xhp, const char *pkgname, void *arg) continue; } snprintf(path, sizeof(path), "%s/%s", xhp->rootdir, file); - if ((lnk = xbps_symlink_target(path)) == NULL) { + if ((lnk = xbps_symlink_target(xhp, path, tgt)) == NULL) { xbps_error_printf("%s: broken symlink %s (target: %s)\n", pkgname, file, tgt); broken = true; continue; diff --git a/include/xbps.h.in b/include/xbps.h.in index 12775a47..23dea55b 100644 --- a/include/xbps.h.in +++ b/include/xbps.h.in @@ -48,7 +48,7 @@ * * This header documents the full API for the XBPS Library. */ -#define XBPS_API_VERSION "20150219-2" +#define XBPS_API_VERSION "20150219-1" #ifndef XBPS_VERSION #define XBPS_VERSION "UNSET" @@ -1945,14 +1945,17 @@ char *xbps_pubkey2fp(struct xbps_handle *xhp, xbps_data_t pubkey); char *xbps_sanitize_path(const char *src); /** - * Returns the target file of the symlink \a symlink. + * Returns a sanitized target file of \a path without the rootdir component. * - * @param[in] symlink The path to a symlink. + * @param[in] xhp The pointer to an xbps_handle struct. + * @param[in] path path component. + * @param[in] target The stored target file of a symlink. * - * @return The target file in a buffer. + * @return The sanitized path in a buffer. * The returned buffer must be free(3)d when it's no longer necessary. */ -char *xbps_symlink_target(const char *symlink); +char *xbps_symlink_target(struct xbps_handle *xhp, const char *path, + const char *target); /*@}*/ diff --git a/lib/package_remove.c b/lib/package_remove.c index 4fdfc6f2..fff1c9d3 100644 --- a/lib/package_remove.c +++ b/lib/package_remove.c @@ -213,7 +213,7 @@ remove_pkg_files(struct xbps_handle *xhp, xbps_dictionary_get_cstring_nocopy(obj, "target", &target); assert(target); - lnk = xbps_symlink_target(path); + lnk = xbps_symlink_target(xhp, path, target); if (lnk == NULL) { xbps_dbg_printf(xhp, "[remove] %s " "symlink_target: %s\n", path, strerror(errno)); diff --git a/lib/util.c b/lib/util.c index e9f84324..066d0a7c 100644 --- a/lib/util.c +++ b/lib/util.c @@ -454,10 +454,10 @@ xbps_sanitize_path(const char *src) } char * -xbps_symlink_target(const char *path) +xbps_symlink_target(struct xbps_handle *xhp, const char *path, const char *tgt) { struct stat sb; - char *lnk = NULL; + char *p, *p1, *dname, *res = NULL, *lnk = NULL; ssize_t r; if (lstat(path, &sb) == -1) @@ -472,5 +472,47 @@ xbps_symlink_target(const char *path) return NULL; } lnk[sb.st_size] = '\0'; - return lnk; + + if (tgt[0] != '/') { + /* + * target file is relative and wasn't converted to absolute by + * xbps-create(8), just compare it as is. + */ + return lnk; + } + + if (strstr(lnk, "./") || lnk[0] != '/') { + /* relative */ + p = strdup(path); + assert(p); + dname = dirname(p); + assert(dname); + p = xbps_xasprintf("%s/%s", dname, lnk); + assert(p); + p1 = xbps_sanitize_path(p); + assert(p1); + free(p); + if ((strstr(p1, "./")) && (p = realpath(p1, NULL))) { + if (strcmp(xhp->rootdir, "/") == 0) + res = strdup(p); + else + res = strdup(p + strlen(xhp->rootdir)); + + free(p); + } + if (res == NULL) { + if (strcmp(xhp->rootdir, "/") == 0) + res = strdup(p1); + else + res = strdup(p1 + strlen(xhp->rootdir)); + } + assert(res); + free(lnk); + free(p1); + } else { + /* absolute */ + res = lnk; + } + + return res; } diff --git a/tests/xbps/xbps-create/basic_test.sh b/tests/xbps/xbps-create/basic_test.sh index 493d6e24..34600889 100644 --- a/tests/xbps/xbps-create/basic_test.sh +++ b/tests/xbps/xbps-create/basic_test.sh @@ -33,7 +33,7 @@ hardlinks_size_body() { atf_test_case symlink_relative_target symlink_relative_target_head() { - atf_set "descr" "xbps-create(8): relative symlinks in destdir" + atf_set "descr" "xbps-create(8): relative symlinks in destdir must be absolute" } symlink_relative_target_body() { @@ -48,7 +48,7 @@ symlink_relative_target_body() { xbps-rindex -d -a repo/*.xbps atf_check_equal $? 0 result="$(xbps-query -r root --repository=repo -f foo|tr -d '\n')" - expected="/usr/include/gsm/gsm.h/usr/include/gsm.h -> gsm/gsm.h" + expected="/usr/include/gsm/gsm.h/usr/include/gsm.h -> /usr/include/gsm/gsm.h" rv=0 if [ "$result" != "$expected" ]; then echo "result: $result" @@ -61,7 +61,7 @@ symlink_relative_target_body() { atf_test_case symlink_relative_target_cwd symlink_relative_target_cwd_head() { - atf_set "descr" "xbps-create(8): relative symlinks to cwd in destdir" + atf_set "descr" "xbps-create(8): relative symlinks to cwd in destdir must be absolute" } symlink_relative_target_cwd_body() { @@ -76,7 +76,7 @@ symlink_relative_target_cwd_body() { xbps-rindex -d -a repo/*.xbps atf_check_equal $? 0 result="$(xbps-query -r root --repository=repo -f foo|tr -d '\n')" - expected="/usr/include/gsm/gsm.h/usr/include/gsm.h -> ./gsm/gsm.h" + expected="/usr/include/gsm/gsm.h/usr/include/gsm.h -> /usr/include/gsm/gsm.h" rv=0 if [ "$result" != "$expected" ]; then echo "result: $result"