]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/canonicalize.c
libblkid: convert GPT partition LBA to 512-byte sectors
[thirdparty/util-linux.git] / lib / canonicalize.c
CommitLineData
6dbe3af9 1/*
74a9c6f7 2 * canonicalize.c -- canonicalize pathname by removing symlinks
6dbe3af9
KZ
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.
74a9c6f7 14 *
6dbe3af9
KZ
15 */
16
17/*
fd6b7a7f
KZ
18 * This routine is part of libc. We include it nevertheless,
19 * since the libc version has some security flaws.
74a9c6f7
KZ
20 *
21 * TODO: use canonicalize_file_name() when exist in glibc
6dbe3af9 22 */
6dbe3af9 23#include <unistd.h>
6dbe3af9 24#include <string.h>
6dbe3af9 25#include <errno.h>
74a9c6f7
KZ
26#include <stdlib.h>
27
28#include "canonicalize.h"
6dbe3af9 29
4717ea4a
KZ
30#ifndef MAXSYMLINKS
31# define MAXSYMLINKS 256
32#endif
6dbe3af9 33
74a9c6f7 34static char *
eb63b9b8 35myrealpath(const char *path, char *resolved_path, int maxreslth) {
6dbe3af9 36 int readlinks = 0;
364cda48
KZ
37 char *npath;
38 char link_path[PATH_MAX+1];
39 int n;
d26aa358 40 char *buf = NULL;
6dbe3af9 41
eb63b9b8 42 npath = resolved_path;
fd6b7a7f 43
eb63b9b8 44 /* If it's a relative pathname use getcwd for starters. */
6dbe3af9 45 if (*path != '/') {
22853e4a
KZ
46 if (!getcwd(npath, maxreslth-2))
47 return NULL;
eb63b9b8
KZ
48 npath += strlen(npath);
49 if (npath[-1] != '/')
50 *npath++ = '/';
51 } else {
52 *npath++ = '/';
6dbe3af9
KZ
53 path++;
54 }
eb63b9b8 55
6dbe3af9
KZ
56 /* Expand each slash-separated pathname component. */
57 while (*path != '\0') {
eb63b9b8 58 /* Ignore stray "/" */
6dbe3af9
KZ
59 if (*path == '/') {
60 path++;
61 continue;
62 }
eb63b9b8
KZ
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;
6dbe3af9
KZ
76 }
77 /* Safely copy the next pathname component. */
78 while (*path != '\0' && *path != '/') {
eb63b9b8 79 if (npath-resolved_path > maxreslth-2) {
6dbe3af9 80 errno = ENAMETOOLONG;
d26aa358 81 goto err;
6dbe3af9 82 }
eb63b9b8 83 *npath++ = *path++;
6dbe3af9 84 }
eb63b9b8 85
6dbe3af9 86 /* Protect against infinite loops. */
4717ea4a 87 if (readlinks++ > MAXSYMLINKS) {
6dbe3af9 88 errno = ELOOP;
d26aa358 89 goto err;
6dbe3af9 90 }
eb63b9b8 91
364cda48 92 /* See if last pathname component is a symlink. */
eb63b9b8
KZ
93 *npath = '\0';
94 n = readlink(resolved_path, link_path, PATH_MAX);
6dbe3af9
KZ
95 if (n < 0) {
96 /* EINVAL means the file exists but isn't a symlink. */
97 if (errno != EINVAL)
d26aa358 98 goto err;
eb63b9b8 99 } else {
d26aa358 100 int m;
a9d6150d 101 char *newbuf;
d26aa358 102
6dbe3af9
KZ
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. */
eb63b9b8 107 npath = resolved_path;
6dbe3af9
KZ
108 else
109 /* Otherwise back up over this component. */
eb63b9b8 110 while (*(--npath) != '/')
6dbe3af9 111 ;
22853e4a 112
6dbe3af9 113 /* Insert symlink contents into path. */
22853e4a 114 m = strlen(path);
74a9c6f7
KZ
115 newbuf = malloc(m + n + 1);
116 if (!newbuf)
117 goto err;
a9d6150d
NB
118 memcpy(newbuf, link_path, n);
119 memcpy(newbuf + n, path, m + 1);
71c445db 120 free(buf);
a9d6150d 121 path = buf = newbuf;
6dbe3af9 122 }
eb63b9b8 123 *npath++ = '/';
6dbe3af9
KZ
124 }
125 /* Delete trailing slash but don't whomp a lone slash. */
eb63b9b8
KZ
126 if (npath != resolved_path+1 && npath[-1] == '/')
127 npath--;
6dbe3af9 128 /* Make sure it's null terminated. */
eb63b9b8 129 *npath = '\0';
d26aa358 130
71c445db 131 free(buf);
6dbe3af9 132 return resolved_path;
d26aa358
KZ
133
134 err:
71c445db 135 free(buf);
d26aa358 136 return NULL;
6dbe3af9 137}
74a9c6f7
KZ
138
139char *
140canonicalize_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