X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=libbb%2Fxreadlink.c;h=a18dd0748dd1793f16bce6eead9c5b3e34860930;hb=67e1529b921416d6c3f33fb43691bc9919e3eacc;hp=b3e3eda2bf8fc400bf05eab887dd5072eb13d66c;hpb=c1ef7bdd8d002ae0889efcf883d0e1b7faa938d4;p=oweals%2Fbusybox.git diff --git a/libbb/xreadlink.c b/libbb/xreadlink.c index b3e3eda2b..a18dd0748 100644 --- a/libbb/xreadlink.c +++ b/libbb/xreadlink.c @@ -1,37 +1,183 @@ +/* vi: set sw=4 ts=4: */ /* - * xreadlink.c - safe implementation of readlink. - * Returns a NULL on failure... + * xreadlink.c - safe implementation of readlink. + * Returns a NULL on failure. + * + * Licensed under GPLv2, see file LICENSE in this source tree. */ +#include "libbb.h" -#include +/* Some systems (eg Hurd) do not have MAXSYMLINKS definition, + * set it to some reasonable value if it isn't defined */ +#ifndef MAXSYMLINKS +# define MAXSYMLINKS 20 +#endif /* * NOTE: This function returns a malloced char* that you will have to free - * yourself. You have been warned. + * yourself. */ - -#include -#include "libbb.h" - -extern char *xreadlink(const char *path) +char* FAST_FUNC xmalloc_readlink(const char *path) { - static const int GROWBY = 80; /* how large we will grow strings by */ + enum { GROWBY = 80 }; /* how large we will grow strings by */ char *buf = NULL; int bufsize = 0, readsize = 0; do { - buf = xrealloc(buf, bufsize += GROWBY); - readsize = readlink(path, buf, bufsize); /* 1st try */ + bufsize += GROWBY; + buf = xrealloc(buf, bufsize); + readsize = readlink(path, buf, bufsize); if (readsize == -1) { - bb_perror_msg("%s", path); free(buf); return NULL; } - } - while (bufsize < readsize + 1); + } while (bufsize < readsize + 1); buf[readsize] = '\0'; return buf; } + +/* + * This routine is not the same as realpath(), which + * canonicalizes the given path completely. This routine only + * follows trailing symlinks until a real file is reached and + * returns its name. If the path ends in a dangling link or if + * the target doesn't exist, the path is returned in any case. + * Intermediate symlinks in the path are not expanded -- only + * those at the tail. + * A malloced char* is returned, which must be freed by the caller. + */ +char* FAST_FUNC xmalloc_follow_symlinks(const char *path) +{ + char *buf; + char *lpc; + char *linkpath; + int bufsize; + int looping = MAXSYMLINKS + 1; + + buf = xstrdup(path); + goto jump_in; + + while (1) { + linkpath = xmalloc_readlink(buf); + if (!linkpath) { + /* not a symlink, or doesn't exist */ + if (errno == EINVAL || errno == ENOENT) + return buf; + goto free_buf_ret_null; + } + + if (!--looping) { + free(linkpath); + free_buf_ret_null: + free(buf); + return NULL; + } + + if (*linkpath != '/') { + bufsize += strlen(linkpath); + buf = xrealloc(buf, bufsize); + lpc = bb_get_last_path_component_strip(buf); + strcpy(lpc, linkpath); + free(linkpath); + } else { + free(buf); + buf = linkpath; + jump_in: + bufsize = strlen(buf) + 1; + } + } +} + +char* FAST_FUNC xmalloc_readlink_or_warn(const char *path) +{ + char *buf = xmalloc_readlink(path); + if (!buf) { + /* EINVAL => "file: Invalid argument" => puzzled user */ + const char *errmsg = "not a symlink"; + int err = errno; + if (err != EINVAL) + errmsg = strerror(err); + bb_error_msg("%s: cannot read link: %s", path, errmsg); + } + return buf; +} + +char* FAST_FUNC xmalloc_realpath(const char *path) +{ +/* NB: uclibc also defines __GLIBC__ + * Therefore the test "if glibc, or uclibc >= 0.9.31" looks a bit weird: + */ +#if defined(__GLIBC__) && \ + (!defined(__UCLIBC__) || UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 31)) + /* glibc provides a non-standard extension */ + /* new: POSIX.1-2008 specifies this behavior as well */ + return realpath(path, NULL); +#else + char buf[PATH_MAX+1]; + + /* on error returns NULL (xstrdup(NULL) == NULL) */ + return xstrdup(realpath(path, buf)); +#endif +} + +char* FAST_FUNC xmalloc_realpath_coreutils(const char *path) +{ + char *buf; + + errno = 0; + buf = xmalloc_realpath(path); + /* + * There is one case when "readlink -f" and + * "realpath" from coreutils succeed, + * even though file does not exist, such as: + * /tmp/file_does_not_exist + * (the directory must exist). + */ + if (!buf && errno == ENOENT) { + char *last_slash = strrchr(path, '/'); + if (last_slash) { + *last_slash++ = '\0'; + buf = xmalloc_realpath(path); + if (buf) { + unsigned len = strlen(buf); + buf = xrealloc(buf, len + strlen(last_slash) + 2); + buf[len++] = '/'; + strcpy(buf + len, last_slash); + } + } else { + char *target = xmalloc_readlink(path); + if (target) { + char *cwd; + if (target[0] == '/') { + /* + * $ ln -s /bin/qwe symlink # note: /bin is a link to /usr/bin + * $ readlink -f symlink + * /usr/bin/qwe/target_does_not_exist + * $ realpath symlink + * /usr/bin/qwe/target_does_not_exist + */ + buf = xmalloc_realpath_coreutils(target); + free(target); + return buf; + } + /* + * $ ln -s target_does_not_exist symlink + * $ readlink -f symlink + * /CURDIR/target_does_not_exist + * $ realpath symlink + * /CURDIR/target_does_not_exist + */ + cwd = xrealloc_getcwd_or_warn(NULL); + buf = concat_path_file(cwd, target); + free(cwd); + free(target); + return buf; + } + } + } + + return buf; +}