Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit11a0a8b

Browse files
committed
Implement find_my_exec()'s path normalization using realpath(3).
Replace the symlink-chasing logic in find_my_exec with realpath(3),which has been required by POSIX since SUSv2. (Windows lacksrealpath(), but there we can use _fullpath() which is functionallyequivalent.) The main benefit of this is that -- on all modernplatforms at least -- realpath() avoids the chdir() shenaniganswe used to perform while interpreting symlinks. That had variouscorner-case failure modes so it's good to get rid of it.There is still ongoing discussion about whether we could skip thereplacement of symlinks in some cases, but that's really matterfor a separate patch. Meanwhile I want to push this before we gettoo close to feature freeze, so that we can find out if there areshowstopper portability issues.Discussion:https://postgr.es/m/797232.1662075573@sss.pgh.pa.us
1 parenteb2618a commit11a0a8b

File tree

1 file changed

+91
-115
lines changed

1 file changed

+91
-115
lines changed

‎src/common/exec.c

Lines changed: 91 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@
1414
*-------------------------------------------------------------------------
1515
*/
1616

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+
1726
#ifndefFRONTEND
1827
#include"postgres.h"
1928
#else
@@ -58,11 +67,8 @@ extern int_CRT_glob = 0;/* 0 turns off globbing; 1 turns it on */
5867
(fprintf(stderr, __VA_ARGS__), fputc('\n', stderr))
5968
#endif
6069

61-
#ifdef_MSC_VER
62-
#definegetcwd(cwd,len) GetCurrentDirectory(len, cwd)
63-
#endif
64-
65-
staticintresolve_symlinks(char*path);
70+
staticintnormalize_exec_path(char*path);
71+
staticchar*pg_realpath(constchar*fname);
6672

6773
#ifdefWIN32
6874
staticBOOLGetTokenUser(HANDLEhToken,PTOKEN_USER*ppTokenUser);
@@ -87,7 +93,7 @@ validate_exec(const char *path)
8793
charpath_exe[MAXPGPATH+sizeof(".exe")-1];
8894

8995
/* Win32 requires a .exe suffix for stat() */
90-
if (strlen(path)>=strlen(".exe")&&
96+
if (strlen(path)<strlen(".exe")||
9197
pg_strcasecmp(path+strlen(path)-strlen(".exe"),".exe")!=0)
9298
{
9399
strlcpy(path_exe,path,sizeof(path_exe)-4);
@@ -135,46 +141,32 @@ validate_exec(const char *path)
135141

136142

137143
/*
138-
* find_my_exec -- find an absolute path toa valid executable
144+
* find_my_exec -- find an absolute path tothis program's executable
139145
*
140146
*argv0 is the name passed on the command line
141147
*retpath is the output area (must be of size MAXPGPATH)
142148
*Returns 0 if OK, -1 if error.
143149
*
144150
* The reason we have to work so hard to find an absolute path is that
145151
* on some platforms we can't do dynamic loading unless we know the
146-
* executable's location. Also, we needa full path not a relative
147-
* path because wewill later change working directory. Finally, we want
152+
* executable's location. Also, we needan absolute path not a relative
153+
* path because wemay later change working directory. Finally, we want
148154
* a true path not a symlink location, so that we can locate other files
149155
* that are part of our installation relative to the executable.
150156
*/
151157
int
152158
find_my_exec(constchar*argv0,char*retpath)
153159
{
154-
charcwd[MAXPGPATH],
155-
test_path[MAXPGPATH];
156160
char*path;
157161

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-
165162
/*
166163
* If argv0 contains a separator, then PATH wasn't used.
167164
*/
168-
if (first_dir_separator(argv0)!=NULL)
165+
strlcpy(retpath,argv0,MAXPGPATH);
166+
if (first_dir_separator(retpath)!=NULL)
169167
{
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-
176168
if (validate_exec(retpath)==0)
177-
returnresolve_symlinks(retpath);
169+
returnnormalize_exec_path(retpath);
178170

179171
log_error(errcode(ERRCODE_WRONG_OBJECT_TYPE),
180172
_("invalid binary \"%s\": %m"),retpath);
@@ -183,9 +175,8 @@ find_my_exec(const char *argv0, char *retpath)
183175

184176
#ifdefWIN32
185177
/* Win32 checks the current directory first for names without slashes */
186-
join_path_components(retpath,cwd,argv0);
187178
if (validate_exec(retpath)==0)
188-
returnresolve_symlinks(retpath);
179+
returnnormalize_exec_path(retpath);
189180
#endif
190181

191182
/*
@@ -208,21 +199,15 @@ find_my_exec(const char *argv0, char *retpath)
208199
if (!endp)
209200
endp=startp+strlen(startp);/* point to end */
210201

211-
strlcpy(test_path,startp,Min(endp-startp+1,MAXPGPATH));
202+
strlcpy(retpath,startp,Min(endp-startp+1,MAXPGPATH));
212203

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);
220205
canonicalize_path(retpath);
221206

222207
switch (validate_exec(retpath))
223208
{
224209
case0:/* found ok */
225-
returnresolve_symlinks(retpath);
210+
returnnormalize_exec_path(retpath);
226211
case-1:/* wasn't even a candidate, keep looking */
227212
break;
228213
case-2:/* found but disqualified */
@@ -241,105 +226,96 @@ find_my_exec(const char *argv0, char *retpath)
241226

242227

243228
/*
244-
*resolve_symlinks - resolve symlinksto the underlying file
229+
*normalize_exec_path - resolve symlinksand convert to absolute path
245230
*
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.
247233
*
234+
* On success, replaces the contents of "path" with the absolute path.
235+
* ("path" is assumed to be of size MAXPGPATH.)
248236
* 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.
257237
*/
258238
staticint
259-
resolve_symlinks(char*path)
239+
normalize_exec_path(char*path)
260240
{
261-
#ifdefHAVE_READLINK
262-
structstatbuf;
263-
charorig_wd[MAXPGPATH],
264-
link_buf[MAXPGPATH];
265-
char*fname;
266-
267241
/*
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.
278244
*/
279-
if (!getcwd(orig_wd,MAXPGPATH))
245+
char*abspath=pg_realpath(path);
246+
247+
if (abspath==NULL)
280248
{
281249
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);
283252
return-1;
284253
}
254+
strlcpy(path,abspath,MAXPGPATH);
255+
free(abspath);
285256

286-
for (;;)
287-
{
288-
char*lsep;
289-
intrllen;
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+
#ifdefWIN32
258+
/* On Windows, be sure to convert '\' to '/' */
259+
canonicalize_path(path);
260+
#endif
305261

306-
if (lstat(fname,&buf)<0||
307-
!S_ISLNK(buf.st_mode))
308-
break;
262+
return0;
263+
}
309264

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-
}
321265

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+
staticchar*
280+
pg_realpath(constchar*fname)
281+
{
282+
char*path;
324283

325-
if (!getcwd(path,MAXPGPATH))
284+
#ifndefWIN32
285+
path=realpath(fname,NULL);
286+
if (path==NULL&&errno==EINVAL)
326287
{
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);
333293

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+
returnNULL;/* assume errno is set */
296+
path=realpath(fname,buf);
297+
if (path==NULL)/* don't leak memory */
298+
{
299+
intsave_errno=errno;
300+
301+
free(buf);
302+
errno=save_errno;
303+
}
339304
}
340-
#endif/*HAVE_READLINK */
305+
#else/*WIN32 */
341306

342-
return0;
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+
returnpath;
343319
}
344320

345321

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp