]> git.ipfire.org Git - thirdparty/util-linux.git/blame - mount/realpath.c
blkid: fix typo (syntax error)
[thirdparty/util-linux.git] / mount / realpath.c
CommitLineData
6dbe3af9
KZ
1/*
2 * realpath.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
df1dddf9 16#define resolve_symlinks
364cda48 17
6dbe3af9 18/*
fd6b7a7f
KZ
19 * This routine is part of libc. We include it nevertheless,
20 * since the libc version has some security flaws.
6dbe3af9 21 */
6dbe3af9 22#include <unistd.h>
6dbe3af9 23#include <string.h>
6dbe3af9 24#include <errno.h>
eb63b9b8
KZ
25#include "realpath.h"
26#include "sundries.h" /* for xstrdup */
6dbe3af9 27
4717ea4a
KZ
28#ifndef MAXSYMLINKS
29# define MAXSYMLINKS 256
30#endif
6dbe3af9 31
f1b3f066
KZ
32int
33is_pseudo_fs(const char *type)
34{
35 if (type == NULL || *type == '/')
36 return 0;
37 if (streq(type, "none") ||
38 streq(type, "proc") ||
39 streq(type, "tmpfs") ||
40 streq(type, "sysfs") ||
41 streq(type, "devpts"))
42 return 1;
43 return 0;
44}
89fde75b
KZ
45
46/* Make a canonical pathname from PATH. Returns a freshly malloced string.
47 It is up the *caller* to ensure that the PATH is sensible. i.e.
48 canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.''
49 is not a legal pathname for ``/dev/fd0''. Anything we cannot parse
50 we return unmodified. */
51char *
f1b3f066
KZ
52canonicalize_spec (const char *path)
53{
89fde75b
KZ
54 if (path == NULL)
55 return NULL;
f1b3f066 56 if (is_pseudo_fs(path))
89fde75b 57 return xstrdup(path);
89fde75b
KZ
58 return canonicalize(path);
59}
60
61char *
62canonicalize (const char *path) {
63 char canonical[PATH_MAX+2];
64
65 if (path == NULL)
66 return NULL;
67
68 if (myrealpath (path, canonical, PATH_MAX+1))
69 return xstrdup(canonical);
70
71 return xstrdup(path);
72}
73
eb63b9b8
KZ
74char *
75myrealpath(const char *path, char *resolved_path, int maxreslth) {
6dbe3af9 76 int readlinks = 0;
364cda48
KZ
77 char *npath;
78 char link_path[PATH_MAX+1];
79 int n;
d26aa358 80 char *buf = NULL;
6dbe3af9 81
eb63b9b8 82 npath = resolved_path;
fd6b7a7f 83
eb63b9b8 84 /* If it's a relative pathname use getcwd for starters. */
6dbe3af9 85 if (*path != '/') {
22853e4a
KZ
86 if (!getcwd(npath, maxreslth-2))
87 return NULL;
eb63b9b8
KZ
88 npath += strlen(npath);
89 if (npath[-1] != '/')
90 *npath++ = '/';
91 } else {
92 *npath++ = '/';
6dbe3af9
KZ
93 path++;
94 }
eb63b9b8 95
6dbe3af9
KZ
96 /* Expand each slash-separated pathname component. */
97 while (*path != '\0') {
eb63b9b8 98 /* Ignore stray "/" */
6dbe3af9
KZ
99 if (*path == '/') {
100 path++;
101 continue;
102 }
eb63b9b8
KZ
103 if (*path == '.' && (path[1] == '\0' || path[1] == '/')) {
104 /* Ignore "." */
105 path++;
106 continue;
107 }
108 if (*path == '.' && path[1] == '.' &&
109 (path[2] == '\0' || path[2] == '/')) {
110 /* Backup for ".." */
111 path += 2;
112 while (npath > resolved_path+1 &&
113 (--npath)[-1] != '/')
114 ;
115 continue;
6dbe3af9
KZ
116 }
117 /* Safely copy the next pathname component. */
118 while (*path != '\0' && *path != '/') {
eb63b9b8 119 if (npath-resolved_path > maxreslth-2) {
6dbe3af9 120 errno = ENAMETOOLONG;
d26aa358 121 goto err;
6dbe3af9 122 }
eb63b9b8 123 *npath++ = *path++;
6dbe3af9 124 }
eb63b9b8 125
6dbe3af9 126 /* Protect against infinite loops. */
4717ea4a 127 if (readlinks++ > MAXSYMLINKS) {
6dbe3af9 128 errno = ELOOP;
d26aa358 129 goto err;
6dbe3af9 130 }
eb63b9b8 131
364cda48 132 /* See if last pathname component is a symlink. */
eb63b9b8
KZ
133 *npath = '\0';
134 n = readlink(resolved_path, link_path, PATH_MAX);
6dbe3af9
KZ
135 if (n < 0) {
136 /* EINVAL means the file exists but isn't a symlink. */
137 if (errno != EINVAL)
d26aa358 138 goto err;
eb63b9b8 139 } else {
364cda48 140#ifdef resolve_symlinks /* Richard Gooch dislikes sl resolution */
d26aa358 141 int m;
a9d6150d 142 char *newbuf;
d26aa358 143
6dbe3af9
KZ
144 /* Note: readlink doesn't add the null byte. */
145 link_path[n] = '\0';
146 if (*link_path == '/')
147 /* Start over for an absolute symlink. */
eb63b9b8 148 npath = resolved_path;
6dbe3af9
KZ
149 else
150 /* Otherwise back up over this component. */
eb63b9b8 151 while (*(--npath) != '/')
6dbe3af9 152 ;
22853e4a 153
6dbe3af9 154 /* Insert symlink contents into path. */
22853e4a 155 m = strlen(path);
a9d6150d
NB
156 newbuf = xmalloc(m + n + 1);
157 memcpy(newbuf, link_path, n);
158 memcpy(newbuf + n, path, m + 1);
71c445db 159 free(buf);
a9d6150d 160 path = buf = newbuf;
364cda48 161#endif
6dbe3af9 162 }
eb63b9b8 163 *npath++ = '/';
6dbe3af9
KZ
164 }
165 /* Delete trailing slash but don't whomp a lone slash. */
eb63b9b8
KZ
166 if (npath != resolved_path+1 && npath[-1] == '/')
167 npath--;
6dbe3af9 168 /* Make sure it's null terminated. */
eb63b9b8 169 *npath = '\0';
d26aa358 170
71c445db 171 free(buf);
6dbe3af9 172 return resolved_path;
d26aa358
KZ
173
174 err:
71c445db 175 free(buf);
d26aa358 176 return NULL;
6dbe3af9 177}