14
14
*-------------------------------------------------------------------------
15
15
*/
16
16
17
+ /*
18
+ * On macOS, "man realpath" avers:
19
+ * Defining _DARWIN_C_SOURCE or _DARWIN_BETTER_REALPATH before including
20
+ * stdlib.h will cause the provided implementation of realpath() to use
21
+ * F_GETPATH from fcntl(2) to discover the path.
22
+ * This should be harmless everywhere else.
23
+ */
24
+ #define _DARWIN_BETTER_REALPATH
25
+
17
26
#ifndef FRONTEND
18
27
#include "postgres.h"
19
28
#else
@@ -58,11 +67,8 @@ extern int _CRT_glob = 0; /* 0 turns off globbing; 1 turns it on */
58
67
(fprintf(stderr, __VA_ARGS__), fputc('\n', stderr))
59
68
#endif
60
69
61
- #ifdef _MSC_VER
62
- #define getcwd (cwd ,len ) GetCurrentDirectory(len, cwd)
63
- #endif
64
-
65
- static int resolve_symlinks (char * path );
70
+ static int normalize_exec_path (char * path );
71
+ static char * pg_realpath (const char * fname );
66
72
67
73
#ifdef WIN32
68
74
static BOOL GetTokenUser (HANDLE hToken , PTOKEN_USER * ppTokenUser );
@@ -87,7 +93,7 @@ validate_exec(const char *path)
87
93
char path_exe [MAXPGPATH + sizeof (".exe" ) - 1 ];
88
94
89
95
/* Win32 requires a .exe suffix for stat() */
90
- if (strlen (path ) >= strlen (".exe" ) &&
96
+ if (strlen (path ) < strlen (".exe" ) ||
91
97
pg_strcasecmp (path + strlen (path ) - strlen (".exe" ), ".exe" ) != 0 )
92
98
{
93
99
strlcpy (path_exe , path , sizeof (path_exe ) - 4 );
@@ -135,46 +141,32 @@ validate_exec(const char *path)
135
141
136
142
137
143
/*
138
- * find_my_exec -- find an absolute path to a valid executable
144
+ * find_my_exec -- find an absolute path to this program's executable
139
145
*
140
146
* argv0 is the name passed on the command line
141
147
* retpath is the output area (must be of size MAXPGPATH)
142
148
* Returns 0 if OK, -1 if error.
143
149
*
144
150
* The reason we have to work so hard to find an absolute path is that
145
151
* on some platforms we can't do dynamic loading unless we know the
146
- * executable's location. Also, we need a full path not a relative
147
- * path because we will later change working directory. Finally, we want
152
+ * executable's location. Also, we need an absolute path not a relative
153
+ * path because we may later change working directory. Finally, we want
148
154
* a true path not a symlink location, so that we can locate other files
149
155
* that are part of our installation relative to the executable.
150
156
*/
151
157
int
152
158
find_my_exec (const char * argv0 , char * retpath )
153
159
{
154
- char cwd [MAXPGPATH ],
155
- test_path [MAXPGPATH ];
156
160
char * path ;
157
161
158
- if (!getcwd (cwd , MAXPGPATH ))
159
- {
160
- log_error (errcode_for_file_access (),
161
- _ ("could not identify current directory: %m" ));
162
- return -1 ;
163
- }
164
-
165
162
/*
166
163
* If argv0 contains a separator, then PATH wasn't used.
167
164
*/
168
- if (first_dir_separator (argv0 ) != NULL )
165
+ strlcpy (retpath , argv0 , MAXPGPATH );
166
+ if (first_dir_separator (retpath ) != NULL )
169
167
{
170
- if (is_absolute_path (argv0 ))
171
- strlcpy (retpath , argv0 , MAXPGPATH );
172
- else
173
- join_path_components (retpath , cwd , argv0 );
174
- canonicalize_path (retpath );
175
-
176
168
if (validate_exec (retpath ) == 0 )
177
- return resolve_symlinks (retpath );
169
+ return normalize_exec_path (retpath );
178
170
179
171
log_error (errcode (ERRCODE_WRONG_OBJECT_TYPE ),
180
172
_ ("invalid binary \"%s\": %m" ), retpath );
@@ -183,9 +175,8 @@ find_my_exec(const char *argv0, char *retpath)
183
175
184
176
#ifdef WIN32
185
177
/* Win32 checks the current directory first for names without slashes */
186
- join_path_components (retpath , cwd , argv0 );
187
178
if (validate_exec (retpath ) == 0 )
188
- return resolve_symlinks (retpath );
179
+ return normalize_exec_path (retpath );
189
180
#endif
190
181
191
182
/*
@@ -208,21 +199,15 @@ find_my_exec(const char *argv0, char *retpath)
208
199
if (!endp )
209
200
endp = startp + strlen (startp ); /* point to end */
210
201
211
- strlcpy (test_path , startp , Min (endp - startp + 1 , MAXPGPATH ));
202
+ strlcpy (retpath , startp , Min (endp - startp + 1 , MAXPGPATH ));
212
203
213
- if (is_absolute_path (test_path ))
214
- join_path_components (retpath , test_path , argv0 );
215
- else
216
- {
217
- join_path_components (retpath , cwd , test_path );
218
- join_path_components (retpath , retpath , argv0 );
219
- }
204
+ join_path_components (retpath , retpath , argv0 );
220
205
canonicalize_path (retpath );
221
206
222
207
switch (validate_exec (retpath ))
223
208
{
224
209
case 0 : /* found ok */
225
- return resolve_symlinks (retpath );
210
+ return normalize_exec_path (retpath );
226
211
case -1 : /* wasn't even a candidate, keep looking */
227
212
break ;
228
213
case -2 : /* found but disqualified */
@@ -241,105 +226,96 @@ find_my_exec(const char *argv0, char *retpath)
241
226
242
227
243
228
/*
244
- * resolve_symlinks - resolve symlinks to the underlying file
229
+ * normalize_exec_path - resolve symlinks and convert to absolute path
245
230
*
246
- * Replace "path" by the absolute path to the referenced file.
231
+ * Given a path that refers to an executable, chase through any symlinks
232
+ * to find the real file location; then convert that to an absolute path.
247
233
*
234
+ * On success, replaces the contents of "path" with the absolute path.
235
+ * ("path" is assumed to be of size MAXPGPATH.)
248
236
* Returns 0 if OK, -1 if error.
249
- *
250
- * Note: we are not particularly tense about producing nice error messages
251
- * because we are not really expecting error here; we just determined that
252
- * the symlink does point to a valid executable.
253
- *
254
- * Here we test HAVE_READLINK, which excludes Windows. There's no point in
255
- * using our junction point-based replacement code for this, because that only
256
- * works for directories.
257
237
*/
258
238
static int
259
- resolve_symlinks (char * path )
239
+ normalize_exec_path (char * path )
260
240
{
261
- #ifdef HAVE_READLINK
262
- struct stat buf ;
263
- char orig_wd [MAXPGPATH ],
264
- link_buf [MAXPGPATH ];
265
- char * fname ;
266
-
267
241
/*
268
- * To resolve a symlink properly, we have to chdir into its directory and
269
- * then chdir to where the symlink points; otherwise we may fail to
270
- * resolve relative links correctly (consider cases involving mount
271
- * points, for example). After following the final symlink, we use
272
- * getcwd() to figure out where the heck we're at.
273
- *
274
- * One might think we could skip all this if path doesn't point to a
275
- * symlink to start with, but that's wrong. We also want to get rid of
276
- * any directory symlinks that are present in the given path. We expect
277
- * getcwd() to give us an accurate, symlink-free path.
242
+ * We used to do a lot of work ourselves here, but now we just let
243
+ * realpath(3) do all the heavy lifting.
278
244
*/
279
- if (!getcwd (orig_wd , MAXPGPATH ))
245
+ char * abspath = pg_realpath (path );
246
+
247
+ if (abspath == NULL )
280
248
{
281
249
log_error (errcode_for_file_access (),
282
- _ ("could not identify current directory: %m" ));
250
+ _ ("could not resolve path \"%s\" to absolute form: %m" ),
251
+ path );
283
252
return -1 ;
284
253
}
254
+ strlcpy (path , abspath , MAXPGPATH );
255
+ free (abspath );
285
256
286
- for (;;)
287
- {
288
- char * lsep ;
289
- int rllen ;
290
-
291
- lsep = last_dir_separator (path );
292
- if (lsep )
293
- {
294
- * lsep = '\0' ;
295
- if (chdir (path ) == -1 )
296
- {
297
- log_error (errcode_for_file_access (),
298
- _ ("could not change directory to \"%s\": %m" ), path );
299
- return -1 ;
300
- }
301
- fname = lsep + 1 ;
302
- }
303
- else
304
- fname = path ;
257
+ #ifdef WIN32
258
+ /* On Windows, be sure to convert '\' to '/' */
259
+ canonicalize_path (path );
260
+ #endif
305
261
306
- if (lstat (fname , & buf ) < 0 ||
307
- !S_ISLNK (buf .st_mode ))
308
- break ;
262
+ return 0 ;
263
+ }
309
264
310
- errno = 0 ;
311
- rllen = readlink (fname , link_buf , sizeof (link_buf ));
312
- if (rllen < 0 || rllen >= sizeof (link_buf ))
313
- {
314
- log_error (errcode_for_file_access (),
315
- _ ("could not read symbolic link \"%s\": %m" ), fname );
316
- return -1 ;
317
- }
318
- link_buf [rllen ] = '\0' ;
319
- strcpy (path , link_buf );
320
- }
321
265
322
- /* must copy final component out of 'path' temporarily */
323
- strlcpy (link_buf , fname , sizeof (link_buf ));
266
+ /*
267
+ * pg_realpath() - realpath(3) with POSIX.1-2008 semantics
268
+ *
269
+ * This is equivalent to realpath(fname, NULL), in that it returns a
270
+ * malloc'd buffer containing the absolute path equivalent to fname.
271
+ * On error, returns NULL with errno set.
272
+ *
273
+ * On Windows, what you get is spelled per platform conventions,
274
+ * so you probably want to apply canonicalize_path() to the result.
275
+ *
276
+ * For now, this is needed only here so mark it static. If you choose to
277
+ * move it into its own file, move the _DARWIN_BETTER_REALPATH #define too!
278
+ */
279
+ static char *
280
+ pg_realpath (const char * fname )
281
+ {
282
+ char * path ;
324
283
325
- if (!getcwd (path , MAXPGPATH ))
284
+ #ifndef WIN32
285
+ path = realpath (fname , NULL );
286
+ if (path == NULL && errno == EINVAL )
326
287
{
327
- log_error (errcode_for_file_access (),
328
- _ ("could not identify current directory: %m" ));
329
- return -1 ;
330
- }
331
- join_path_components (path , path , link_buf );
332
- canonicalize_path (path );
288
+ /*
289
+ * Cope with old-POSIX systems that require a user-provided buffer.
290
+ * Assume MAXPGPATH is enough room on all such systems.
291
+ */
292
+ char * buf = malloc (MAXPGPATH );
333
293
334
- if (chdir (orig_wd ) == -1 )
335
- {
336
- log_error (errcode_for_file_access (),
337
- _ ("could not change directory to \"%s\": %m" ), orig_wd );
338
- return -1 ;
294
+ if (buf == NULL )
295
+ return NULL ; /* assume errno is set */
296
+ path = realpath (fname , buf );
297
+ if (path == NULL ) /* don't leak memory */
298
+ {
299
+ int save_errno = errno ;
300
+
301
+ free (buf );
302
+ errno = save_errno ;
303
+ }
339
304
}
340
- #endif /* HAVE_READLINK */
305
+ #else /* WIN32 */
341
306
342
- return 0 ;
307
+ /*
308
+ * Microsoft is resolutely non-POSIX, but _fullpath() does the same thing.
309
+ * The documentation claims it reports errors by setting errno, which is a
310
+ * bit surprising for Microsoft, but we'll believe that until it's proven
311
+ * wrong. Clear errno first, though, so we can at least tell if a failure
312
+ * occurs and doesn't set it.
313
+ */
314
+ errno = 0 ;
315
+ path = _fullpath (NULL , fname , 0 );
316
+ #endif
317
+
318
+ return path ;
343
319
}
344
320
345
321
0 commit comments