1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-05 07:21:24 +03:00

Be more conservative about removing tablespace "symlinks".

Don't apply rmtree(), which will gleefully remove an entire subtree,
and don't even apply unlink() unless it's symlink or a directory,
the only things that we expect to find.

Amit Kapila, with minor tweaks by me, per extensive discussions
involving Andrew Dunstan, Fujii Masao, and Heikki Linnakangas,
at least some of whom also reviewed the code.
This commit is contained in:
Robert Haas
2015-06-26 15:53:13 -04:00
parent c66bc72e8a
commit 8f15f74a44
3 changed files with 69 additions and 45 deletions

View File

@ -627,31 +627,9 @@ create_tablespace_directories(const char *location, const Oid tablespaceoid)
/*
* In recovery, remove old symlink, in case it points to the wrong place.
*
* On Windows, junction points act like directories so we must be able to
* apply rmdir; in general it seems best to make this code work like the
* symlink removal code in destroy_tablespace_directories, except that
* failure to remove is always an ERROR.
*/
if (InRecovery)
{
if (lstat(linkloc, &st) == 0 && S_ISDIR(st.st_mode))
{
if (rmdir(linkloc) < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not remove directory \"%s\": %m",
linkloc)));
}
else
{
if (unlink(linkloc) < 0 && errno != ENOENT)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not remove symbolic link \"%s\": %m",
linkloc)));
}
}
remove_tablespace_symlink(linkloc);
/*
* Create the symlink under PGDATA
@ -802,7 +780,8 @@ remove_symlink:
errmsg("could not remove directory \"%s\": %m",
linkloc)));
}
else
#ifdef S_ISLNK
else if (S_ISLNK(st.st_mode))
{
if (unlink(linkloc) < 0)
{
@ -814,6 +793,15 @@ remove_symlink:
linkloc)));
}
}
#endif
else
{
/* Refuse to remove anything that's not a directory or symlink */
ereport(redo ? LOG : ERROR,
(ERRCODE_SYSTEM_ERROR,
errmsg("not a directory or symbolic link: \"%s\"",
linkloc)));
}
pfree(linkloc_with_version_dir);
pfree(linkloc);
@ -848,6 +836,59 @@ directory_is_empty(const char *path)
return true;
}
/*
* remove_tablespace_symlink
*
* This function removes symlinks in pg_tblspc. On Windows, junction points
* act like directories so we must be able to apply rmdir. This function
* works like the symlink removal code in destroy_tablespace_directories,
* except that failure to remove is always an ERROR. But if the file doesn't
* exist at all, that's OK.
*/
void
remove_tablespace_symlink(const char *linkloc)
{
struct stat st;
if (lstat(linkloc, &st) != 0)
{
if (errno == ENOENT)
return;
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not stat \"%s\": %m", linkloc)));
}
if (S_ISDIR(st.st_mode))
{
/*
* This will fail if the directory isn't empty, but not
* if it's a junction point.
*/
if (rmdir(linkloc) < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not remove directory \"%s\": %m",
linkloc)));
}
#ifdef S_ISLNK
else if (S_ISLNK(st.st_mode))
{
if (unlink(linkloc) < 0 && errno != ENOENT)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not remove symbolic link \"%s\": %m",
linkloc)));
}
#endif
else
{
/* Refuse to remove anything that's not a directory or symlink */
ereport(ERROR,
(errmsg("not a directory or symbolic link: \"%s\"",
linkloc)));
}
}
/*
* Rename a tablespace