busybox/libbb/xreadlink.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * xreadlink.c - safe implementation of readlink.
   4 * Returns a NULL on failure.
   5 *
   6 * Licensed under GPLv2, see file LICENSE in this source tree.
   7 */
   8
   9#include "libbb.h"
  10
  11/* Some systems (eg Hurd) do not have MAXSYMLINKS definition,
  12 * set it to some reasonable value if it isn't defined */
  13#ifndef MAXSYMLINKS
  14# define MAXSYMLINKS 20
  15#endif
  16
  17/*
  18 * NOTE: This function returns a malloced char* that you will have to free
  19 * yourself.
  20 */
  21char* FAST_FUNC xmalloc_readlink(const char *path)
  22{
  23        enum { GROWBY = 80 }; /* how large we will grow strings by */
  24
  25        char *buf = NULL;
  26        int bufsize = 0, readsize = 0;
  27
  28        do {
  29                bufsize += GROWBY;
  30                buf = xrealloc(buf, bufsize);
  31                readsize = readlink(path, buf, bufsize);
  32                if (readsize == -1) {
  33                        free(buf);
  34                        return NULL;
  35                }
  36        } while (bufsize < readsize + 1);
  37
  38        buf[readsize] = '\0';
  39
  40        return buf;
  41}
  42
  43/*
  44 * This routine is not the same as realpath(), which
  45 * canonicalizes the given path completely. This routine only
  46 * follows trailing symlinks until a real file is reached and
  47 * returns its name. If the path ends in a dangling link or if
  48 * the target doesn't exist, the path is returned in any case.
  49 * Intermediate symlinks in the path are not expanded -- only
  50 * those at the tail.
  51 * A malloced char* is returned, which must be freed by the caller.
  52 */
  53char* FAST_FUNC xmalloc_follow_symlinks(const char *path)
  54{
  55        char *buf;
  56        char *lpc;
  57        char *linkpath;
  58        int bufsize;
  59        int looping = MAXSYMLINKS + 1;
  60
  61        buf = xstrdup(path);
  62        goto jump_in;
  63
  64        while (1) {
  65                linkpath = xmalloc_readlink(buf);
  66                if (!linkpath) {
  67                        /* not a symlink, or doesn't exist */
  68                        if (errno == EINVAL || errno == ENOENT)
  69                                return buf;
  70                        goto free_buf_ret_null;
  71                }
  72
  73                if (!--looping) {
  74                        free(linkpath);
  75 free_buf_ret_null:
  76                        free(buf);
  77                        return NULL;
  78                }
  79
  80                if (*linkpath != '/') {
  81                        bufsize += strlen(linkpath);
  82                        buf = xrealloc(buf, bufsize);
  83                        lpc = bb_get_last_path_component_strip(buf);
  84                        strcpy(lpc, linkpath);
  85                        free(linkpath);
  86                } else {
  87                        free(buf);
  88                        buf = linkpath;
  89 jump_in:
  90                        bufsize = strlen(buf) + 1;
  91                }
  92        }
  93}
  94
  95char* FAST_FUNC xmalloc_readlink_or_warn(const char *path)
  96{
  97        char *buf = xmalloc_readlink(path);
  98        if (!buf) {
  99                /* EINVAL => "file: Invalid argument" => puzzled user */
 100                const char *errmsg = "not a symlink";
 101                int err = errno;
 102                if (err != EINVAL)
 103                        errmsg = strerror(err);
 104                bb_error_msg("%s: cannot read link: %s", path, errmsg);
 105        }
 106        return buf;
 107}
 108
 109char* FAST_FUNC xmalloc_realpath(const char *path)
 110{
 111/* NB: uclibc also defines __GLIBC__
 112 * Therefore the test "if glibc, or uclibc >= 0.9.31" looks a bit weird:
 113 */
 114#if defined(__GLIBC__) && \
 115    (!defined(__UCLIBC__) || UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 31))
 116        /* glibc provides a non-standard extension */
 117        /* new: POSIX.1-2008 specifies this behavior as well */
 118        return realpath(path, NULL);
 119#else
 120        char buf[PATH_MAX+1];
 121
 122        /* on error returns NULL (xstrdup(NULL) == NULL) */
 123        return xstrdup(realpath(path, buf));
 124#endif
 125}
 126