pkg_alternatives: pass if the desired symlink already exists
authorYousong Zhou <yszhou4tech@gmail.com>
Tue, 11 Jul 2017 03:07:23 +0000 (11:07 +0800)
committerYousong Zhou <yszhou4tech@gmail.com>
Tue, 11 Jul 2017 10:44:28 +0000 (18:44 +0800)
This can happen when opkg installs, then configures multiple
alternatives in a batch.  Symlink to the highest prio alternative will
be created in the initial configuring stage causing later symlink call
fail with EEXIST

    Configuring busybox.
    ...
    symlink("/sbin/ip-full", "/home/yousong/j/t/lede-imagebuilder-mvebu.Linux-x86_64/build_dir/target-arm_cortex-a9+vfpv3_musl_eabi/root-mvebu/sbin/ip") = 0
    ...
    Configuring ip-full.
    ...
    symlink("/sbin/ip-full", "/home/yousong/j/t/lede-imagebuilder-mvebu.Linux-x86_64/build_dir/target-arm_cortex-a9+vfpv3_musl_eabi/root-mvebu/sbin/ip") = -1 EEXIST (File exists)
    ...

While at it, "mkdir -p" dirname(path_in_dest) before symlink in case the
following symlink call may fail with ENOENT

Ref: https://github.com/openwrt/packages/issues/4567
Reported-by: Aner Andros <aa@anerandros.info>
Signed-off-by: Yousong Zhou <yszhou4tech@gmail.com>
libopkg/pkg_alternatives.c

index 890b510669374350f288d5e58c42a0f4102adea4..50e9d128ef20b6bda30ed6e7129e82ad79cdec44 100644 (file)
 #include <stdio.h>
 #include <sys/types.h>         /* stat */
 #include <sys/stat.h>
+#include <libgen.h>                    /* dirname */
 #include <unistd.h>
 
+#include "file_util.h"
 #include "libbb/libbb.h"
 #include "opkg_message.h"
 #include "pkg.h"
@@ -76,9 +78,27 @@ static int pkg_alternatives_update_path(pkg_t *pkg, const pkg_vec_t *installed,
                } else if (errno != ENOENT) {
                        goto out;
                }
-               r = symlink(the_alt->altpath, path_in_dest);
-               if (r)
-                       opkg_msg(ERROR, "failed symlinking %s -> %s\n", path_in_dest, the_alt->altpath);
+               {
+                       char *path_copy = xstrdup(path_in_dest);
+                       char *path_parent = dirname(path_copy);
+
+                       r = file_mkdir_hier(path_parent, 0755);
+                       free(path_copy);
+                       if (r) {
+                               goto out;
+                       }
+                       r = symlink(the_alt->altpath, path_in_dest);
+                       if (r && errno == EEXIST) {
+                               /*
+                                * the strcmp & unlink check above will make sure that if EEXIST
+                                * happens, the symlink target also matches
+                                */
+                               r = 0;
+                       }
+                       if (r) {
+                               opkg_perror(ERROR, "failed symlinking %s -> %s", path_in_dest, the_alt->altpath);
+                       }
+               }
        } else {
                unlink(path_in_dest);
                r = 0;