]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/canonicalize.c
mount: move realpath.c code to lib/
[thirdparty/util-linux.git] / lib / canonicalize.c
1 /*
2 * canonicalize.c -- canonicalize pathname by removing symlinks
3 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Library Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Library Public License for more details.
14 *
15 */
16
17 /*
18 * This routine is part of libc. We include it nevertheless,
19 * since the libc version has some security flaws.
20 *
21 * TODO: use canonicalize_file_name() when exist in glibc
22 */
23 #include <unistd.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <stdlib.h>
27
28 #include "canonicalize.h"
29
30 #ifndef MAXSYMLINKS
31 # define MAXSYMLINKS 256
32 #endif
33
34 static char *
35 myrealpath(const char *path, char *resolved_path, int maxreslth) {
36 int readlinks = 0;
37 char *npath;
38 char link_path[PATH_MAX+1];
39 int n;
40 char *buf = NULL;
41
42 npath = resolved_path;
43
44 /* If it's a relative pathname use getcwd for starters. */
45 if (*path != '/') {
46 if (!getcwd(npath, maxreslth-2))
47 return NULL;
48 npath += strlen(npath);
49 if (npath[-1] != '/')
50 *npath++ = '/';
51 } else {
52 *npath++ = '/';
53 path++;
54 }
55
56 /* Expand each slash-separated pathname component. */
57 while (*path != '\0') {
58 /* Ignore stray "/" */
59 if (*path == '/') {
60 path++;
61 continue;
62 }
63 if (*path == '.' && (path[1] == '\0' || path[1] == '/')) {
64 /* Ignore "." */
65 path++;
66 continue;
67 }
68 if (*path == '.' && path[1] == '.' &&
69 (path[2] == '\0' || path[2] == '/')) {
70 /* Backup for ".." */
71 path += 2;
72 while (npath > resolved_path+1 &&
73 (--npath)[-1] != '/')
74 ;
75 continue;
76 }
77 /* Safely copy the next pathname component. */
78 while (*path != '\0' && *path != '/') {
79 if (npath-resolved_path > maxreslth-2) {
80 errno = ENAMETOOLONG;
81 goto err;
82 }
83 *npath++ = *path++;
84 }
85
86 /* Protect against infinite loops. */
87 if (readlinks++ > MAXSYMLINKS) {
88 errno = ELOOP;
89 goto err;
90 }
91
92 /* See if last pathname component is a symlink. */
93 *npath = '\0';
94 n = readlink(resolved_path, link_path, PATH_MAX);
95 if (n < 0) {
96 /* EINVAL means the file exists but isn't a symlink. */
97 if (errno != EINVAL)
98 goto err;
99 } else {
100 int m;
101 char *newbuf;
102
103 /* Note: readlink doesn't add the null byte. */
104 link_path[n] = '\0';
105 if (*link_path == '/')
106 /* Start over for an absolute symlink. */
107 npath = resolved_path;
108 else
109 /* Otherwise back up over this component. */
110 while (*(--npath) != '/')
111 ;
112
113 /* Insert symlink contents into path. */
114 m = strlen(path);
115 newbuf = malloc(m + n + 1);
116 if (!newbuf)
117 goto err;
118 memcpy(newbuf, link_path, n);
119 memcpy(newbuf + n, path, m + 1);
120 free(buf);
121 path = buf = newbuf;
122 }
123 *npath++ = '/';
124 }
125 /* Delete trailing slash but don't whomp a lone slash. */
126 if (npath != resolved_path+1 && npath[-1] == '/')
127 npath--;
128 /* Make sure it's null terminated. */
129 *npath = '\0';
130
131 free(buf);
132 return resolved_path;
133
134 err:
135 free(buf);
136 return NULL;
137 }
138
139 char *
140 canonicalize_path(const char *path) {
141 char canonical[PATH_MAX+2];
142
143 if (path == NULL)
144 return NULL;
145
146 if (myrealpath (path, canonical, PATH_MAX+1))
147 return strdup(canonical);
148
149 return strdup(path);
150 }
151
152