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