postgresql/src/port/dirmod.c

474 lines
9.8 KiB
C
Raw Normal View History

2003-11-12 00:52:45 +01:00
/*-------------------------------------------------------------------------
*
* dirmod.c
* rename/unlink()
*
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
2003-11-12 00:52:45 +01:00
* Portions Copyright (c) 1994, Regents of the University of California
*
* These are replacement versions of unlink and rename that work on
2003-08-04 02:43:34 +02:00
* Win32 (NT, Win2k, XP). replace() doesn't work on Win95/98/Me.
2003-11-12 00:52:45 +01:00
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/port/dirmod.c,v 1.38 2005/08/02 15:14:47 tgl Exp $
2003-11-12 00:52:45 +01:00
*
*-------------------------------------------------------------------------
*/
2003-08-04 02:43:34 +02:00
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif
2004-08-01 08:19:26 +02:00
/* Don't modify declarations in system headers */
#if defined(WIN32) || defined(__CYGWIN__)
#undef rename
#undef unlink
#endif
2004-08-01 08:19:26 +02:00
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#if defined(WIN32) || defined(__CYGWIN__)
2004-09-10 04:49:37 +02:00
#ifndef __CYGWIN__
#include <winioctl.h>
#else
#include <windows.h>
#include <w32api/winioctl.h>
#endif
#endif
#ifndef FRONTEND
/*
* On Windows, call non-macro versions of palloc; we can't reference
* CurrentMemoryContext in this file because of DLLIMPORT conflict.
*/
#if defined(WIN32) || defined(__CYGWIN__)
#undef palloc
#undef pstrdup
#define palloc(sz) pgport_palloc(sz)
#define pstrdup(str) pgport_pstrdup(str)
#endif
#else /* FRONTEND */
/*
* In frontend, fake palloc behavior with these
*/
#undef palloc
#undef pstrdup
#define palloc(sz) fe_palloc(sz)
#define pstrdup(str) fe_pstrdup(str)
#define repalloc(pointer,sz) fe_repalloc(pointer,sz)
#define pfree(pointer) free(pointer)
static void *
fe_palloc(Size size)
{
void *res;
if ((res = malloc(size)) == NULL)
{
fprintf(stderr, _("out of memory\n"));
exit(1);
}
return res;
}
static char *
fe_pstrdup(const char *string)
{
char *res;
if ((res = strdup(string)) == NULL)
{
fprintf(stderr, _("out of memory\n"));
exit(1);
}
return res;
}
static void *
fe_repalloc(void *pointer, Size size)
{
void *res;
if ((res = realloc(pointer, size)) == NULL)
{
fprintf(stderr, _("out of memory\n"));
exit(1);
}
return res;
}
#endif /* FRONTEND */
#if defined(WIN32) || defined(__CYGWIN__)
/*
* pgrename
*/
2003-07-27 19:10:07 +02:00
int
pgrename(const char *from, const char *to)
2003-04-22 04:18:48 +02:00
{
2003-08-04 02:43:34 +02:00
int loops = 0;
2003-04-22 04:18:48 +02:00
/* Is this loop even necessary now that we have win32_open()? */
2004-09-09 02:59:49 +02:00
#if defined(WIN32) && !defined(__CYGWIN__)
2003-04-22 04:18:48 +02:00
while (!MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING))
#endif
#ifdef __CYGWIN__
2004-08-29 07:07:03 +02:00
while (rename(from, to) < 0)
#endif
2004-08-29 07:07:03 +02:00
{
2004-09-09 02:59:49 +02:00
#if defined(WIN32) && !defined(__CYGWIN__)
2004-08-29 07:07:03 +02:00
if (GetLastError() != ERROR_ACCESS_DENIED)
#endif
#ifdef __CYGWIN__
2004-08-29 07:07:03 +02:00
if (errno != EACCES)
#endif
2004-08-29 07:07:03 +02:00
/* set errno? */
return -1;
pg_usleep(100000); /* us */
if (loops == 30)
2003-04-22 04:18:48 +02:00
#ifndef FRONTEND
2004-08-29 07:07:03 +02:00
elog(LOG, "could not rename \"%s\" to \"%s\", continuing to try",
from, to);
2003-04-22 04:18:48 +02:00
#else
fprintf(stderr, _("could not rename \"%s\" to \"%s\", continuing to try\n"),
2004-08-29 07:07:03 +02:00
from, to);
2003-04-22 04:18:48 +02:00
#endif
2004-08-29 07:07:03 +02:00
loops++;
}
2003-04-22 04:18:48 +02:00
if (loops > 30)
2003-04-22 04:18:48 +02:00
#ifndef FRONTEND
2003-07-27 19:10:07 +02:00
elog(LOG, "completed rename of \"%s\" to \"%s\"", from, to);
2003-04-22 04:18:48 +02:00
#else
fprintf(stderr, _("completed rename of \"%s\" to \"%s\"\n"), from, to);
2003-04-22 04:18:48 +02:00
#endif
return 0;
}
/*
* pgunlink
*/
2003-07-27 19:10:07 +02:00
int
pgunlink(const char *path)
2003-04-22 04:18:48 +02:00
{
2003-08-04 02:43:34 +02:00
int loops = 0;
2003-04-22 04:18:48 +02:00
/* Is this loop even necessary now that we have win32_open()? */
2003-04-22 04:18:48 +02:00
while (unlink(path))
{
if (errno != EACCES)
/* set errno? */
return -1;
2004-08-01 08:19:26 +02:00
pg_usleep(100000); /* us */
if (loops == 30)
2003-04-22 04:18:48 +02:00
#ifndef FRONTEND
2003-07-27 19:10:07 +02:00
elog(LOG, "could not unlink \"%s\", continuing to try",
path);
2003-04-22 04:18:48 +02:00
#else
fprintf(stderr, _("could not unlink \"%s\", continuing to try\n"),
2003-07-27 19:10:07 +02:00
path);
2003-04-22 04:18:48 +02:00
#endif
loops++;
}
if (loops > 30)
2003-04-22 04:18:48 +02:00
#ifndef FRONTEND
2003-07-27 19:10:07 +02:00
elog(LOG, "completed unlink of \"%s\"", path);
2003-04-22 04:18:48 +02:00
#else
fprintf(stderr, _("completed unlink of \"%s\"\n"), path);
2003-04-22 04:18:48 +02:00
#endif
return 0;
}
#ifdef WIN32 /* Cygwin has its own symlinks */
/*
* pgsymlink support:
*
* This struct is a replacement for REPARSE_DATA_BUFFER which is defined in VC6 winnt.h
* but omitted in later SDK functions.
* We only need the SymbolicLinkReparseBuffer part of the original struct's union.
*/
typedef struct
{
2004-08-29 07:07:03 +02:00
DWORD ReparseTag;
WORD ReparseDataLength;
WORD Reserved;
/* SymbolicLinkReparseBuffer */
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
WCHAR PathBuffer[1];
} REPARSE_JUNCTION_DATA_BUFFER;
#define REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE \
FIELD_OFFSET(REPARSE_JUNCTION_DATA_BUFFER, SubstituteNameOffset)
/*
* pgsymlink - uses Win32 junction points
*
* For reference: http://www.codeproject.com/w2k/junctionpoints.asp
*/
int
pgsymlink(const char *oldpath, const char *newpath)
{
2004-08-29 07:07:03 +02:00
HANDLE dirhandle;
DWORD len;
char buffer[MAX_PATH * sizeof(WCHAR) + sizeof(REPARSE_JUNCTION_DATA_BUFFER)];
char nativeTarget[MAX_PATH];
char *p = nativeTarget;
REPARSE_JUNCTION_DATA_BUFFER *reparseBuf = (REPARSE_JUNCTION_DATA_BUFFER *) buffer;
CreateDirectory(newpath, 0);
2004-08-29 07:07:03 +02:00
dirhandle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE,
0, 0, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0);
if (dirhandle == INVALID_HANDLE_VALUE)
return -1;
2004-08-29 07:07:03 +02:00
/* make sure we have an unparsed native win32 path */
if (memcmp("\\??\\", oldpath, 4))
sprintf(nativeTarget, "\\??\\%s", oldpath);
else
strcpy(nativeTarget, oldpath);
2004-08-29 07:07:03 +02:00
while ((p = strchr(p, '/')) != 0)
*p++ = '\\';
len = strlen(nativeTarget) * sizeof(WCHAR);
reparseBuf->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
reparseBuf->ReparseDataLength = len + 12;
reparseBuf->Reserved = 0;
reparseBuf->SubstituteNameOffset = 0;
reparseBuf->SubstituteNameLength = len;
2004-08-29 07:07:03 +02:00
reparseBuf->PrintNameOffset = len + sizeof(WCHAR);
reparseBuf->PrintNameLength = 0;
MultiByteToWideChar(CP_ACP, 0, nativeTarget, -1,
reparseBuf->PathBuffer, MAX_PATH);
2004-08-29 07:07:03 +02:00
/*
2004-08-29 07:07:03 +02:00
* FSCTL_SET_REPARSE_POINT is coded differently depending on SDK
* version; we use our own definition
*/
2004-08-29 07:07:03 +02:00
if (!DeviceIoControl(dirhandle,
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_ANY_ACCESS),
reparseBuf,
reparseBuf->ReparseDataLength + REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE,
0, 0, &len, 0))
{
2004-08-29 07:07:03 +02:00
LPSTR msg;
2004-08-29 07:07:03 +02:00
errno = 0;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
2004-08-29 07:07:03 +02:00
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
2004-08-29 07:07:03 +02:00
(LPSTR) & msg, 0, NULL);
2004-08-08 07:04:41 +02:00
#ifndef FRONTEND
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not set junction for \"%s\": %s",
nativeTarget, msg)));
2004-08-08 07:04:41 +02:00
#else
fprintf(stderr, _("could not set junction for \"%s\": %s\n"),
nativeTarget, msg);
2004-08-08 03:31:15 +02:00
#endif
LocalFree(msg);
2004-08-29 07:07:03 +02:00
CloseHandle(dirhandle);
RemoveDirectory(newpath);
return -1;
}
CloseHandle(dirhandle);
return 0;
}
#endif /* WIN32 */
#endif /* defined(WIN32) || defined(__CYGWIN__) */
2003-04-22 04:18:48 +02:00
/* We undefined this above, so we redefine it */
2004-08-01 08:19:26 +02:00
#if defined(WIN32) || defined(__CYGWIN__)
#define unlink(path) pgunlink(path)
2004-08-01 08:19:26 +02:00
#endif
2004-08-01 08:19:26 +02:00
/*
* fnames
*
* return a list of the names of objects in the argument directory
*/
static char **
fnames(char *path)
{
DIR *dir;
struct dirent *file;
char **filenames;
int numnames = 0;
int fnsize = 200; /* enough for many small dbs */
dir = opendir(path);
if (dir == NULL)
{
#ifndef FRONTEND
elog(WARNING, "could not open directory \"%s\": %m", path);
#else
fprintf(stderr, _("could not open directory \"%s\": %s\n"),
path, strerror(errno));
#endif
return NULL;
}
filenames = (char **) palloc(fnsize * sizeof(char *));
errno = 0;
while ((file = readdir(dir)) != NULL)
{
if (strcmp(file->d_name, ".") != 0 && strcmp(file->d_name, "..") != 0)
{
if (numnames+1 >= fnsize)
{
fnsize *= 2;
filenames = (char **) repalloc(filenames,
fnsize * sizeof(char *));
}
filenames[numnames++] = pstrdup(file->d_name);
}
errno = 0;
}
#ifdef WIN32
/*
* This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but
* not in released version
*/
if (GetLastError() == ERROR_NO_MORE_FILES)
errno = 0;
#endif
if (errno)
{
#ifndef FRONTEND
elog(WARNING, "could not read directory \"%s\": %m", path);
#else
fprintf(stderr, _("could not read directory \"%s\": %s\n"),
path, strerror(errno));
#endif
}
filenames[numnames] = NULL;
closedir(dir);
return filenames;
}
/*
* fnames_cleanup
*
* deallocate memory used for filenames
2004-08-01 08:19:26 +02:00
*/
static void
fnames_cleanup(char **filenames)
2004-08-01 08:19:26 +02:00
{
2004-08-29 07:07:03 +02:00
char **fn;
2004-08-01 08:19:26 +02:00
for (fn = filenames; *fn; fn++)
pfree(*fn);
pfree(filenames);
2004-08-01 08:19:26 +02:00
}
2004-08-01 08:19:26 +02:00
/*
* rmtree
*
* Delete a directory tree recursively.
* Assumes path points to a valid directory.
* Deletes everything under path.
* If rmtopdir is true deletes the directory too.
2004-08-01 08:19:26 +02:00
*/
bool
rmtree(char *path, bool rmtopdir)
{
char pathbuf[MAXPGPATH];
char *filepath;
2004-08-01 08:19:26 +02:00
char **filenames;
char **filename;
struct stat statbuf;
/*
* we copy all the names out of the directory before we start
* modifying it.
*/
filenames = fnames(path);
2004-08-01 08:19:26 +02:00
if (filenames == NULL)
2004-08-01 08:19:26 +02:00
return false;
/* now we have the names we can start removing things */
filepath = pathbuf;
2004-08-01 08:19:26 +02:00
for (filename = filenames; *filename; filename++)
{
snprintf(filepath, MAXPGPATH, "%s/%s", path, *filename);
if (stat(filepath, &statbuf) != 0)
goto report_and_fail;
2004-08-01 08:19:26 +02:00
if (S_ISDIR(statbuf.st_mode))
{
/* call ourselves recursively for a directory */
if (!rmtree(filepath, true))
{
/* we already reported the error */
fnames_cleanup(filenames);
2004-08-01 08:19:26 +02:00
return false;
}
}
else
{
if (unlink(filepath) != 0)
goto report_and_fail;
2004-08-01 08:19:26 +02:00
}
}
if (rmtopdir)
{
filepath = path;
if (rmdir(filepath) != 0)
goto report_and_fail;
2004-08-01 08:19:26 +02:00
}
fnames_cleanup(filenames);
2004-08-01 08:19:26 +02:00
return true;
report_and_fail:
#ifndef FRONTEND
elog(WARNING, "could not remove file or directory \"%s\": %m", filepath);
#else
fprintf(stderr, _("could not remove file or directory \"%s\": %s\n"),
filepath, strerror(errno));
#endif
fnames_cleanup(filenames);
return false;
2004-08-01 08:19:26 +02:00
}