Lets update-alternatives manage symlinks inside a cross-arch root filesystem in a directory specified by DPKG_ROOT.
Note: SYSCONFDIR has to be set to a value relative to DPKG_ROOT. Signed-off-by: Andreas Oberritter <[email protected]> --- v4: Now using DPKG_ROOT variable (--force-script-chrootless option). Rebased onto master. v3 [https://lists.debian.org/debian-dpkg/2015/03/msg00004.html]: Rebased onto master. v2 [https://lists.debian.org/debian-dpkg/2014/09/msg00012.html]: altdb_get_namelist() didn't respect DPKG_INSTDIR. v1 [https://lists.debian.org/debian-dpkg/2014/08/msg00052.html]: utils/Makefile.am | 1 + utils/update-alternatives.c | 175 ++++++++++++++++++++++++++++---------------- 2 files changed, 114 insertions(+), 62 deletions(-) diff --git a/utils/Makefile.am b/utils/Makefile.am index 83378ad..1f821c5 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -27,6 +27,7 @@ update_alternatives_SOURCES = \ update_alternatives_CPPFLAGS = \ -DALT_TMP_EXT=\".dpkg-tmp\" \ -DADMINDIR_ENVVAR=\"DPKG_ADMINDIR\" \ + -DINSTDIR_ENVVAR=\"DPKG_ROOT\" \ $(AM_CPPFLAGS) update_alternatives_LDADD = \ diff --git a/utils/update-alternatives.c b/utils/update-alternatives.c index 5dc3213..2b49819 100644 --- a/utils/update-alternatives.c +++ b/utils/update-alternatives.c @@ -51,6 +51,7 @@ #define PROGNAME "update-alternatives" static const char *altdir = SYSCONFDIR "/alternatives"; +static const char *instdir; static const char *admdir; static const char *prog_path = "update-alternatives"; @@ -64,6 +65,27 @@ static int opt_verbose = 0; static int opt_force = 0; /* + * Types. + */ + +enum alternative_path_status { + ALT_PATH_SYMLINK, + ALT_PATH_MISSING, + ALT_PATH_OTHER, +}; + + +/* + * Predeclarations. + */ + +static char * DPKG_ATTR_PRINTF(1) +xasprintf(const char *fmt, ...); + +static enum alternative_path_status +alternative_path_classify(const char *linkname); + +/* * Functions. */ @@ -270,7 +292,7 @@ xstrdup(const char *str) } static char * -areadlink(const char *linkname) +_areadlink(const char *linkname) { struct stat st; char *buf; @@ -303,6 +325,19 @@ areadlink(const char *linkname) } static char * +areadlink(const char *linkname) +{ + char *instdir_linkname; + char *ret; + + instdir_linkname = xasprintf("%s%s", instdir, linkname); + ret = _areadlink(instdir_linkname); + free(instdir_linkname); + + return ret; +} + +static char * xreadlink(const char *linkname) { char *buf; @@ -348,9 +383,22 @@ set_action(const char *new_action) } static const char * +instdir_init(void) +{ + const char *dpkg_instdir; + + dpkg_instdir = getenv(INSTDIR_ENVVAR); + if (dpkg_instdir) + return dpkg_instdir; + + return ""; +} + +static const char * admindir_init(void) { const char *basedir, *basedir_env; + size_t length; /* Try to get the admindir from an environment variable, usually set * by the system package manager. */ @@ -360,6 +408,12 @@ admindir_init(void) else basedir = ADMINDIR; + /* If instdir is set and admindir is below instdir, treat admindir + * as relative. */ + length = strlen(instdir); + if (strncmp(basedir, instdir, length) == 0) + basedir += length; + return xasprintf("%s/%s", basedir, "alternatives"); } @@ -432,25 +486,43 @@ rename_mv(const char *src, const char *dst) static void checked_symlink(const char *filename, const char *linkname) { - if (symlink(filename, linkname)) + char *instdir_linkname; + + instdir_linkname = xasprintf("%s%s", instdir, linkname); + + if (symlink(filename, instdir_linkname)) syserr(_("error creating symbolic link '%.255s'"), linkname); + + free(instdir_linkname); } static void checked_mv(const char *src, const char *dst) { - if (!rename_mv(src, dst)) + char *instdir_src; + char *instdir_dst; + + instdir_src = xasprintf("%s%s", instdir, src); + instdir_dst = xasprintf("%s%s", instdir, dst); + + if (!rename_mv(instdir_src, instdir_dst)) syserr(_("unable to install '%.250s' as '%.250s'"), src, dst); + + free(instdir_src); + free(instdir_dst); } static void checked_rm(const char *f) { - if (!unlink(f)) - return; + char *instdir_f; - if (errno != ENOENT) + instdir_f = xasprintf("%s%s", instdir, f); + + if (unlink(instdir_f) && errno != ENOENT) syserr(_("unable to remove '%s'"), f); + + free(instdir_f); } static void DPKG_ATTR_PRINTF(1) @@ -567,16 +639,11 @@ fileset_has_slave(struct fileset *fs, const char *name) static bool fileset_can_install_slave(struct fileset *fs, const char *slave_name) { - struct stat st; - /* Decide whether the slave alternative must be setup */ if (fileset_has_slave(fs, slave_name)) { const char *slave = fileset_get_slave(fs, slave_name); - errno = 0; - if (stat(slave, &st) == -1 && errno != ENOENT) - syserr(_("cannot stat file '%s'"), slave); - if (errno == 0) + if (alternative_path_classify(slave) != ALT_PATH_MISSING) return true; } @@ -1039,10 +1106,15 @@ static int altdb_get_namelist(struct dirent ***table) { int count; + char *instdir_admdir; - count = scandir(admdir, table, altdb_filter_namelist, alphasort); + instdir_admdir = xasprintf("%s%s", instdir, admdir); + + count = scandir(instdir_admdir, table, altdb_filter_namelist, alphasort); if (count < 0) - syserr(_("cannot scan directory '%.255s'"), admdir); + syserr(_("cannot scan directory '%.255s'"), instdir_admdir); + + free(instdir_admdir); return count; } @@ -1167,7 +1239,6 @@ alternative_parse_fileset(struct alternative *a, struct altdb_context *ctx) { struct fileset *fs; struct slave_link *sl; - struct stat st; char *master_file; master_file = altdb_get_line(ctx, _("master file")); @@ -1180,12 +1251,9 @@ alternative_parse_fileset(struct alternative *a, struct altdb_context *ctx) if (fs) ctx->bad_format(ctx, _("duplicate path %s"), master_file); - if (stat(master_file, &st)) { + if (alternative_path_classify(master_file) == ALT_PATH_MISSING) { char *junk; - if (errno != ENOENT) - syserr(_("cannot stat file '%s'"), master_file); - /* File not found - remove. */ if (ctx->flags & ALTDB_WARN_PARSER) warning(_("alternative %s (part of link group %s) " @@ -1248,7 +1316,7 @@ alternative_load(struct alternative *a, enum altdb_flags flags) ctx.bad_format = altdb_parse_stop; else ctx.bad_format = altdb_parse_error; - ctx.filename = xasprintf("%s/%s", admdir, a->master_name); + ctx.filename = xasprintf("%s%s/%s", instdir, admdir, a->master_name); /* Open the alternative file. */ ctx.fh = fopen(ctx.filename, "r"); @@ -1340,7 +1408,7 @@ alternative_save(struct alternative *a) file = xasprintf("%s/%s", admdir, a->master_name); filenew = xasprintf("%s" ALT_TMP_EXT, file); - ctx.filename = filenew; + ctx.filename = xasprintf("%s%s", instdir, filenew); ctx.fh = fopen(ctx.filename, "w"); if (ctx.fh == NULL) syserr(_("unable to create file '%s'"), ctx.filename); @@ -1379,6 +1447,7 @@ alternative_save(struct alternative *a) syserr(_("unable to sync file '%s'"), ctx.filename); if (fclose(ctx.fh)) syserr(_("unable to close file '%s'"), ctx.filename); + free(ctx.filename); /* Put in place atomically. */ checked_mv(filenew, file); @@ -1399,7 +1468,6 @@ alternative_set_current(struct alternative *a, char *new_choice) static const char * alternative_get_current(struct alternative *a) { - struct stat st; char *curlink; char *file; @@ -1407,12 +1475,9 @@ alternative_get_current(struct alternative *a) return a->current; curlink = xasprintf("%s/%s", altdir, a->master_name); - if (lstat(curlink, &st)) { - if (errno == ENOENT) { - free(curlink); - return alternative_set_current(a, NULL); - } - syserr(_("cannot stat file '%s'"), curlink); + if (alternative_path_classify(curlink) == ALT_PATH_MISSING) { + free(curlink); + return alternative_set_current(a, NULL); } file = xreadlink(curlink); @@ -1674,14 +1739,8 @@ alternative_commit(struct alternative *a) alternative_commit_operations_free(a); } -enum alternative_path_status { - ALT_PATH_SYMLINK, - ALT_PATH_MISSING, - ALT_PATH_OTHER, -}; - static enum alternative_path_status -alternative_path_classify(const char *linkname) +_alternative_path_classify(const char *linkname) { struct stat st; @@ -1697,6 +1756,19 @@ alternative_path_classify(const char *linkname) } } +static enum alternative_path_status +alternative_path_classify(const char *linkname) +{ + enum alternative_path_status ret; + char *instdir_linkname; + + instdir_linkname = xasprintf("%s%s", instdir, linkname); + ret = _alternative_path_classify(instdir_linkname); + free(instdir_linkname); + + return ret; +} + static bool alternative_path_can_remove(const char *linkname) { @@ -2120,13 +2192,7 @@ alternative_select_mode(struct alternative *a, const char *current_choice) if (current_choice) { /* Detect manually modified alternative, switch to manual. */ if (!alternative_has_choice(a, current_choice)) { - struct stat st; - - errno = 0; - if (stat(current_choice, &st) == -1 && errno != ENOENT) - syserr(_("cannot stat file '%s'"), current_choice); - - if (errno == ENOENT) { + if (alternative_path_classify(current_choice) == ALT_PATH_MISSING) { warning(_("%s/%s is dangling; it will be updated " "with best choice"), altdir, a->master_name); alternative_set_status(a, ALT_ST_AUTO); @@ -2150,7 +2216,6 @@ alternative_evolve_slave(struct alternative *a, const char *cur_choice, struct slave_link *sl, struct fileset *fs) { struct slave_link *sl_old; - struct stat st; char *new_file = NULL; const char *old, *new; @@ -2174,17 +2239,7 @@ alternative_evolve_slave(struct alternative *a, const char *cur_choice, } if (strcmp(old, new) != 0 && alternative_path_classify(old) == ALT_PATH_SYMLINK) { - bool rename_link = false; - - if (new_file) { - errno = 0; - if (stat(new_file, &st) == -1 && errno != ENOENT) - syserr(_("cannot stat file '%s'"), - new_file); - rename_link = (errno == 0); - } - - if (rename_link) { + if (new_file && alternative_path_classify(new_file) != ALT_PATH_MISSING) { info(_("renaming %s slave link from %s to %s"), sl->name, old, new); checked_mv(old, new); @@ -2474,7 +2529,6 @@ alternative_check_install_args(struct alternative *inst_alt, struct alternative_map *alt_map_links, *alt_map_parent; struct alternative *found; struct slave_link *sl; - struct stat st; alternative_check_name(inst_alt->master_name); alternative_check_link(inst_alt->master_link); @@ -2499,13 +2553,9 @@ alternative_check_install_args(struct alternative *inst_alt, inst_alt->master_link, found->master_name); } - if (stat(fileset->master_file, &st) == -1) { - if (errno == ENOENT) - error(_("alternative path %s doesn't exist"), - fileset->master_file); - else - syserr(_("cannot stat file '%s'"), fileset->master_file); - } + if (alternative_path_classify(fileset->master_file) == ALT_PATH_MISSING) + error(_("alternative path %s doesn't exist"), + fileset->master_file); for (sl = inst_alt->slaves; sl; sl = sl->next) { const char *file = fileset_get_slave(fileset, sl->name); @@ -2580,6 +2630,7 @@ main(int argc, char **argv) bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); + instdir = instdir_init(); admdir = admindir_init(); if (setvbuf(stdout, NULL, _IONBF, 0))

