1 |
/** |
2 |
* Copyright (c) Members of the EGEE Collaboration. 2010. |
3 |
* See http://www.eu-egee.org/partners/ for details on the copyright |
4 |
* holders. |
5 |
* |
6 |
* Licensed under the Apache License, Version 2.0 (the "License"); |
7 |
* you may not use this file except in compliance with the License. |
8 |
* You may obtain a copy of the License at |
9 |
* |
10 |
* http://www.apache.org/licenses/LICENSE-2.0 |
11 |
* |
12 |
* Unless required by applicable law or agreed to in writing, software |
13 |
* distributed under the License is distributed on an "AS IS" BASIS, |
14 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 |
* See the License for the specific language governing permissions and |
16 |
* limitations under the License. |
17 |
* |
18 |
* Authors: Oscar Koeroo, Mischa Sall\'e, Aram Verstegen |
19 |
* NIKHEF Amsterdam, the Netherlands |
20 |
* <grid-mw-security@nikhef.nl> |
21 |
*/ |
22 |
|
23 |
#include <stdio.h> |
24 |
#include <stdlib.h> |
25 |
#include <string.h> |
26 |
#include <unistd.h> |
27 |
#include <fcntl.h> |
28 |
#include <limits.h> |
29 |
#include <sys/types.h> |
30 |
#include <sys/stat.h> |
31 |
#include <errno.h> |
32 |
|
33 |
/* Make sure at least internally we have a PATH_MAX */ |
34 |
#ifndef PATH_MAX |
35 |
#define PATH_MAX 1024 |
36 |
#endif |
37 |
|
38 |
/* Make sure at least internally we have a SYMLOOP_MAX */ |
39 |
#ifndef SYMLOOP_MAX |
40 |
#define SYMLOOP_MAX _POSIX_SYMLOOP_MAX |
41 |
#endif |
42 |
|
43 |
/*! |
44 |
* Returns the fully canonicalized and absolute path for a diretory or NULL on |
45 |
* error. Memory for the return string is malloced and should be freed by the |
46 |
* caller. Any path cannot be longer than PATH_MAX. Note that it is necessary |
47 |
* that we can chdir to dir. |
48 |
* NOTE: This function is not thread-safe |
49 |
* \param dir non-absolute and/or non-canonical directory |
50 |
* \return canonical absolute directory |
51 |
*/ |
52 |
char *cgul_realdir(const char *dir) { |
53 |
int olddir; |
54 |
char buf[PATH_MAX],*path; |
55 |
|
56 |
/* Safe old dir and then go to the new one */ |
57 |
if ( (olddir=open(".",O_RDONLY))==-1 || |
58 |
chdir(dir)==-1 ) |
59 |
return NULL; |
60 |
/* Get the location, don't exit yet when failed: need to go back */ |
61 |
path=getcwd(buf,PATH_MAX); |
62 |
/* Go back, note that it's really bad if this fails! */ |
63 |
if ( fchdir(olddir) || |
64 |
close(olddir) ) |
65 |
return NULL; |
66 |
/* Make a copy */ |
67 |
if (path) |
68 |
path=strdup(buf); |
69 |
|
70 |
return path; |
71 |
} |
72 |
|
73 |
/*! |
74 |
* Returns the fully canonicalized and absolute path for any path or NULL on |
75 |
* error. Note that this function substitutes the unsafe realpath() or the |
76 |
* GNU-only canonicalize_file_name(). |
77 |
* Memory for the return string is malloced and should be freed by the |
78 |
* caller. Any path cannot be longer than PATH_MAX. Any directory has to be |
79 |
* accessible, including if it's the last element. |
80 |
* NOTE: This function is not thread-safe |
81 |
* \param inpath non-absolute and/or non-canonical path |
82 |
* \param instat optional stat information on the input (for performance, if |
83 |
* already available). When NULL it will be determined locally |
84 |
* when needed. |
85 |
* \return canonical absolute path |
86 |
*/ |
87 |
char *cgul_realpath(const char *inpath, struct stat *instat) { |
88 |
char buf[PATH_MAX], path[PATH_MAX]; |
89 |
char *pos,*canonicdir; |
90 |
int pathlen,filelen,linklen, linkcount=0; |
91 |
struct stat st; |
92 |
|
93 |
/* Check and store the length of the input (incl \0) */ |
94 |
if ( (pathlen=1+strlen(inpath)) > PATH_MAX) { |
95 |
errno=ENAMETOOLONG; |
96 |
return NULL; |
97 |
} |
98 |
/* Check last compo is dir: then just return realdir() */ |
99 |
if (instat==NULL) { |
100 |
if (stat(inpath, &st)==-1) /* Error */ |
101 |
return NULL; |
102 |
instat=&st; |
103 |
} |
104 |
if (S_ISDIR(instat->st_mode)) |
105 |
return cgul_realdir(inpath); |
106 |
|
107 |
/* Make path absolute */ |
108 |
if (inpath[0]!='/') { /* relative */ |
109 |
/* get and add cwd */ |
110 |
if (!getcwd(buf,PATH_MAX)) { |
111 |
if (errno==ERANGE) errno=ENAMETOOLONG; |
112 |
return NULL; |
113 |
} |
114 |
if ( (pathlen=snprintf(path,PATH_MAX,"%s/%s",buf,inpath))>PATH_MAX ) { |
115 |
errno=ENAMETOOLONG; |
116 |
return NULL; |
117 |
} |
118 |
} else /* absolute */ |
119 |
strcpy(path,inpath); |
120 |
|
121 |
/* Now loop over symlink, if any */ |
122 |
/* readlink wants buffersize without \0: PATH_MAX-1 */ |
123 |
while ( (linklen=readlink(path,buf,PATH_MAX-1)) != -1 ) { |
124 |
if (++linkcount > SYMLOOP_MAX) { /* Too many symlinks */ |
125 |
errno=ELOOP; |
126 |
return NULL; |
127 |
} |
128 |
buf[linklen]='\0'; |
129 |
/* Check the symlink */ |
130 |
if (buf[0]=='/') /* absolute: replaces entire path */ |
131 |
strcpy(path,buf); |
132 |
else { /* relative: replaces last component */ |
133 |
pos=strrchr(path,'/'); /* Has to exist, we made it absolute */ |
134 |
filelen=strlen(pos+1); /* length of remainder without \0 */ |
135 |
/* Note: pathlen is with \0, filelen and linklen without, hence |
136 |
* combined as follows is with \0 and should be <= PATH_MAX */ |
137 |
if (pathlen-filelen+linklen>PATH_MAX) { |
138 |
errno=ENAMETOOLONG; |
139 |
return NULL; |
140 |
} |
141 |
strcpy(pos+1,buf); /* It fits, so use strcpy */ |
142 |
} |
143 |
} |
144 |
/* Alway end due to realink: check it's due to non-symlink */ |
145 |
if (errno!=EINVAL) /* i.e. other error */ |
146 |
return NULL; |
147 |
|
148 |
/* Now path is not a symlink */ |
149 |
|
150 |
/* Find dir part */ |
151 |
pos=strrchr(path,'/'); /* Has to exist, we made it absolute */ |
152 |
/* dir: up to pos, file: pos+1 till end */ |
153 |
pos[0]='\0'; |
154 |
canonicdir=cgul_realdir(path); |
155 |
pos[0]='/'; /* Clean but unnecessary */ |
156 |
/* Put them together, canonicdir doesn't end with a / */ |
157 |
pathlen=snprintf(buf,PATH_MAX,"%s/%s",canonicdir,pos+1); |
158 |
free(canonicdir); /* Done with canonicdir */ |
159 |
if (pathlen>PATH_MAX) { |
160 |
errno=ENAMETOOLONG; |
161 |
return NULL; |
162 |
} |
163 |
|
164 |
/* Return duplicate, or NULL when fails */ |
165 |
return strdup(buf); |
166 |
} |