diff --git a/src/bin/initdb/nls.mk b/src/bin/initdb/nls.mk
index 8febf18440..377e75970b 100644
--- a/src/bin/initdb/nls.mk
+++ b/src/bin/initdb/nls.mk
@@ -1,5 +1,5 @@
 # src/bin/initdb/nls.mk
 CATALOG_NAME     = initdb
 AVAIL_LANGUAGES  = cs de es fr it ja pl pt_BR ru zh_CN
-GETTEXT_FILES    = findtimezone.c initdb.c ../../common/exec.c ../../common/fe_memutils.c ../../common/pgfnames.c ../../common/wait_error.c ../../port/dirmod.c
+GETTEXT_FILES    = findtimezone.c initdb.c ../../common/exec.c ../../common/fe_memutils.c ../../common/pgfnames.c ../../common/rmtree.c ../../common/wait_error.c ../../port/dirmod.c
 GETTEXT_TRIGGERS = simple_prompt
diff --git a/src/common/Makefile b/src/common/Makefile
index 575a48a94d..d4ef2f1a30 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -23,7 +23,7 @@ include $(top_builddir)/src/Makefile.global
 override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
 LIBS += $(PTHREAD_LIBS)
 
-OBJS_COMMON = exec.o pgfnames.o relpath.o wait_error.o
+OBJS_COMMON = exec.o pgfnames.o relpath.o rmtree.o wait_error.o
 
 OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o
 
diff --git a/src/common/rmtree.c b/src/common/rmtree.c
new file mode 100644
index 0000000000..ec7bce8e6d
--- /dev/null
+++ b/src/common/rmtree.c
@@ -0,0 +1,131 @@
+/*-------------------------------------------------------------------------
+ *
+ * rmtree.c
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/common/rmtree.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include <unistd.h>
+#include <sys/stat.h>
+
+
+/*
+ *	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.
+ *	Returns true if successful, false if there was any problem.
+ *	(The details of the problem are reported already, so caller
+ *	doesn't really have to say anything more, but most do.)
+ */
+bool
+rmtree(const char *path, bool rmtopdir)
+{
+	bool		result = true;
+	char		pathbuf[MAXPGPATH];
+	char	  **filenames;
+	char	  **filename;
+	struct stat statbuf;
+
+	/*
+	 * we copy all the names out of the directory before we start modifying
+	 * it.
+	 */
+	filenames = pgfnames(path);
+
+	if (filenames == NULL)
+		return false;
+
+	/* now we have the names we can start removing things */
+	for (filename = filenames; *filename; filename++)
+	{
+		snprintf(pathbuf, MAXPGPATH, "%s/%s", path, *filename);
+
+		/*
+		 * It's ok if the file is not there anymore; we were just about to
+		 * delete it anyway.
+		 *
+		 * This is not an academic possibility. One scenario where this
+		 * happens is when bgwriter has a pending unlink request for a file in
+		 * a database that's being dropped. In dropdb(), we call
+		 * ForgetDatabaseFsyncRequests() to flush out any such pending unlink
+		 * requests, but because that's asynchronous, it's not guaranteed that
+		 * the bgwriter receives the message in time.
+		 */
+		if (lstat(pathbuf, &statbuf) != 0)
+		{
+			if (errno != ENOENT)
+			{
+#ifndef FRONTEND
+				elog(WARNING, "could not stat file or directory \"%s\": %m",
+					 pathbuf);
+#else
+				fprintf(stderr, _("could not stat file or directory \"%s\": %s\n"),
+						pathbuf, strerror(errno));
+#endif
+				result = false;
+			}
+			continue;
+		}
+
+		if (S_ISDIR(statbuf.st_mode))
+		{
+			/* call ourselves recursively for a directory */
+			if (!rmtree(pathbuf, true))
+			{
+				/* we already reported the error */
+				result = false;
+			}
+		}
+		else
+		{
+			if (unlink(pathbuf) != 0)
+			{
+				if (errno != ENOENT)
+				{
+#ifndef FRONTEND
+					elog(WARNING, "could not remove file or directory \"%s\": %m",
+						 pathbuf);
+#else
+					fprintf(stderr, _("could not remove file or directory \"%s\": %s\n"),
+							pathbuf, strerror(errno));
+#endif
+					result = false;
+				}
+			}
+		}
+	}
+
+	if (rmtopdir)
+	{
+		if (rmdir(path) != 0)
+		{
+#ifndef FRONTEND
+			elog(WARNING, "could not remove file or directory \"%s\": %m",
+				 path);
+#else
+			fprintf(stderr, _("could not remove file or directory \"%s\": %s\n"),
+					path, strerror(errno));
+#endif
+			result = false;
+		}
+	}
+
+	pgfnames_cleanup(filenames);
+
+	return result;
+}
diff --git a/src/port/dirmod.c b/src/port/dirmod.c
index a18d2ef7ba..0a630ff697 100644
--- a/src/port/dirmod.c
+++ b/src/port/dirmod.c
@@ -351,116 +351,6 @@ pgwin32_is_junction(char *path)
 #endif   /* defined(WIN32) && !defined(__CYGWIN__) */
 
 
-/*
- *	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.
- *	Returns true if successful, false if there was any problem.
- *	(The details of the problem are reported already, so caller
- *	doesn't really have to say anything more, but most do.)
- */
-bool
-rmtree(const char *path, bool rmtopdir)
-{
-	bool		result = true;
-	char		pathbuf[MAXPGPATH];
-	char	  **filenames;
-	char	  **filename;
-	struct stat statbuf;
-
-	/*
-	 * we copy all the names out of the directory before we start modifying
-	 * it.
-	 */
-	filenames = pgfnames(path);
-
-	if (filenames == NULL)
-		return false;
-
-	/* now we have the names we can start removing things */
-	for (filename = filenames; *filename; filename++)
-	{
-		snprintf(pathbuf, MAXPGPATH, "%s/%s", path, *filename);
-
-		/*
-		 * It's ok if the file is not there anymore; we were just about to
-		 * delete it anyway.
-		 *
-		 * This is not an academic possibility. One scenario where this
-		 * happens is when bgwriter has a pending unlink request for a file in
-		 * a database that's being dropped. In dropdb(), we call
-		 * ForgetDatabaseFsyncRequests() to flush out any such pending unlink
-		 * requests, but because that's asynchronous, it's not guaranteed that
-		 * the bgwriter receives the message in time.
-		 */
-		if (lstat(pathbuf, &statbuf) != 0)
-		{
-			if (errno != ENOENT)
-			{
-#ifndef FRONTEND
-				elog(WARNING, "could not stat file or directory \"%s\": %m",
-					 pathbuf);
-#else
-				fprintf(stderr, _("could not stat file or directory \"%s\": %s\n"),
-						pathbuf, strerror(errno));
-#endif
-				result = false;
-			}
-			continue;
-		}
-
-		if (S_ISDIR(statbuf.st_mode))
-		{
-			/* call ourselves recursively for a directory */
-			if (!rmtree(pathbuf, true))
-			{
-				/* we already reported the error */
-				result = false;
-			}
-		}
-		else
-		{
-			if (unlink(pathbuf) != 0)
-			{
-				if (errno != ENOENT)
-				{
-#ifndef FRONTEND
-					elog(WARNING, "could not remove file or directory \"%s\": %m",
-						 pathbuf);
-#else
-					fprintf(stderr, _("could not remove file or directory \"%s\": %s\n"),
-							pathbuf, strerror(errno));
-#endif
-					result = false;
-				}
-			}
-		}
-	}
-
-	if (rmtopdir)
-	{
-		if (rmdir(path) != 0)
-		{
-#ifndef FRONTEND
-			elog(WARNING, "could not remove file or directory \"%s\": %m",
-				 path);
-#else
-			fprintf(stderr, _("could not remove file or directory \"%s\": %s\n"),
-					path, strerror(errno));
-#endif
-			result = false;
-		}
-	}
-
-	pgfnames_cleanup(filenames);
-
-	return result;
-}
-
-
 #if defined(WIN32) && !defined(__CYGWIN__)
 
 #undef stat
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 2dc8b1d881..0e84348e4d 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -74,7 +74,7 @@ sub mkvcbuild
 	  win32error.c win32setlocale.c);
 
 	our @pgcommonallfiles = qw(
-	  exec.c pgfnames.c relpath.c wait_error.c);
+	  exec.c pgfnames.c relpath.c rmtree.c wait_error.c);
 
 	our @pgcommonfrontendfiles = (@pgcommonallfiles, qw(fe_memutils.c));