Upload Tizen:Base source
[framework/base/util-linux-ng.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