1 |
msalle |
1884 |
/** |
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 |
|
|
} |