diff --git a/cli/cli.c b/cli/cli.c
index 44e4816..8e3e025 100644
--- a/cli/cli.c
+++ b/cli/cli.c
@@ -28,6 +28,7 @@
 #include <signal.h>
 
 #include <libtransmission/transmission.h>
+#include <libtransmission/error.h>
 #include <libtransmission/file.h>
 #include <libtransmission/tr-getopt.h>
 #include <libtransmission/utils.h> /* tr_wait_msec */
@@ -268,11 +269,11 @@ main (int argc, char ** argv)
     {
       if (!tr_sys_path_exists (str, NULL))
         {
-          tr_mkdirp (str, 0700);
-
-          if (!tr_sys_path_exists (str, NULL))
+          tr_error * error = NULL;
+          if (!tr_sys_dir_create (str, TR_SYS_DIR_CREATE_PARENTS, 0700, &error))
             {
-              fprintf (stderr, "Unable to create download directory \"%s\"!\n", str);
+              fprintf (stderr, "Unable to create download directory \"%s\": %s\n", str, error->message);
+              tr_error_free (error);
               return EXIT_FAILURE;
             }
         }
diff --git a/daemon/remote.c b/daemon/remote.c
index a8e032e..8eb1be7 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -9,24 +9,19 @@
 
 #include <assert.h>
 #include <ctype.h> /* isspace */
-#include <errno.h>
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h> /* strcmp */
 
-#ifdef _WIN32
- #include <direct.h> /* getcwd */
-#else
- #include <unistd.h> /* getcwd */
-#endif
-
 #include <event2/buffer.h>
 
 #define CURL_DISABLE_TYPECHECK /* otherwise -Wunreachable-code goes insane */
 #include <curl/curl.h>
 
 #include <libtransmission/transmission.h>
+#include <libtransmission/error.h>
+#include <libtransmission/file.h>
 #include <libtransmission/log.h>
 #include <libtransmission/rpcimpl.h>
 #include <libtransmission/tr-getopt.h>
@@ -498,21 +493,18 @@ static char*
 tr_getcwd (void)
 {
   char * result;
-  char buf[2048];
+  tr_error * error = NULL;
 
-#ifdef _WIN32
-  result = _getcwd (buf, sizeof (buf));
-#else
-  result = getcwd (buf, sizeof (buf));
-#endif
+  result = tr_sys_dir_get_current (&error);
 
   if (result == NULL)
     {
-      fprintf (stderr, "getcwd error: \"%s\"", tr_strerror (errno));
-      *buf = '\0';
+      fprintf (stderr, "getcwd error: \"%s\"", error->message);
+      tr_error_free (error);
+      result = tr_strdup ("");
     }
 
-  return tr_strdup (buf);
+  return result;
 }
 
 static char*
diff --git a/daemon/watch.c b/daemon/watch.c
index 286990d..0cb2c70 100644
--- a/daemon/watch.c
+++ b/daemon/watch.c
@@ -19,8 +19,6 @@
 #include <string.h> /* strlen () */
 #include <stdio.h> /* perror () */
 
-#include <dirent.h> /* readdir */
-
 #include <libtransmission/transmission.h>
 #include <libtransmission/file.h>
 #include <libtransmission/log.h>
@@ -59,7 +57,7 @@ static void
 watchdir_new_impl (dtr_watchdir * w)
 {
     int i;
-    DIR * odir;
+    tr_sys_dir_t odir;
     w->inotify_fd = inotify_init ();
 
     if (w->inotify_fd < 0)
@@ -76,14 +74,11 @@ watchdir_new_impl (dtr_watchdir * w)
     {
         tr_logAddError ("Unable to watch \"%s\": %s", w->dir, tr_strerror (errno));
     }
-    else if ((odir = opendir (w->dir)))
+    else if ((odir = tr_sys_dir_open (w->dir, NULL)) != TR_BAD_SYS_DIR)
     {
-        struct dirent * d;
-
-        while ((d = readdir (odir)))
+        const char * name;
+        while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
         {
-            const char * name = d->d_name;
-
             if (!tr_str_has_suffix (name, ".torrent")) /* skip non-torrents */
                 continue;
 
@@ -91,7 +86,7 @@ watchdir_new_impl (dtr_watchdir * w)
             w->callback (w->session, w->dir, name);
         }
 
-        closedir (odir);
+        tr_sys_dir_close (odir, NULL);
     }
 
 }
@@ -196,24 +191,22 @@ static void
 watchdir_update_impl (dtr_watchdir * w)
 {
     tr_sys_path_info info;
-    DIR * odir;
+    tr_sys_dir_t odir;
     const time_t oldTime = w->lastTimeChecked;
     const char * dirname = w->dir;
     struct evbuffer * curFiles = evbuffer_new ();
 
-    if ((oldTime + WATCHDIR_POLL_INTERVAL_SECS < time (NULL))
-         && tr_sys_path_get_info (dirname, 0, &info, NULL)
-         && info.type == TR_SYS_PATH_IS_DIRECTORY
-         && ((odir = opendir (dirname))))
+    if (oldTime + WATCHDIR_POLL_INTERVAL_SECS < time (NULL) &&
+        tr_sys_path_get_info (dirname, 0, &info, NULL) &&
+        info.type == TR_SYS_PATH_IS_DIRECTORY &&
+        (odir = tr_sys_dir_open (dirname, NULL)) != TR_BAD_SYS_DIR)
     {
-        struct dirent * d;
-
-        for (d = readdir (odir); d != NULL; d = readdir (odir))
+        const char * name;
+        while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
         {
             size_t len;
-            const char * name = d->d_name;
 
-            if (!name || *name=='.') /* skip dotfiles */
+            if (*name == '.') /* skip dotfiles */
                 continue;
             if (!tr_str_has_suffix (name, ".torrent")) /* skip non-torrents */
                 continue;
@@ -228,7 +221,7 @@ watchdir_update_impl (dtr_watchdir * w)
             }
         }
 
-        closedir (odir);
+        tr_sys_dir_close (odir, NULL);
         w->lastTimeChecked = time (NULL);
         evbuffer_free (w->lastFiles);
         w->lastFiles = curFiles;
diff --git a/libtransmission/blocklist-test.c b/libtransmission/blocklist-test.c
index b83cefb..7084770 100644
--- a/libtransmission/blocklist-test.c
+++ b/libtransmission/blocklist-test.c
@@ -41,7 +41,7 @@ create_text_file (const char * path, const char * contents)
   char * dir;
 
   dir = tr_sys_path_dirname (path, NULL);
-  tr_mkdirp (dir, 0700);
+  tr_sys_dir_create (dir, TR_SYS_DIR_CREATE_PARENTS, 0700, NULL);
   tr_free (dir);
 
   fd = tr_sys_file_open (path, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_TRUNCATE, 0600, NULL);
diff --git a/libtransmission/fdlimit.c b/libtransmission/fdlimit.c
index cf518b1..02c89ae 100644
--- a/libtransmission/fdlimit.c
+++ b/libtransmission/fdlimit.c
@@ -127,7 +127,7 @@ cached_file_close (struct tr_cached_file * o)
 /**
  * returns 0 on success, or an errno value on failure.
  * errno values include ENOENT if the parent folder doesn't exist,
- * plus the errno values set by tr_mkdirp () and tr_sys_file_open ().
+ * plus the errno values set by tr_sys_dir_create () and tr_sys_file_open ().
  */
 static int
 cached_file_open (struct tr_cached_file  * o,
@@ -146,10 +146,11 @@ cached_file_open (struct tr_cached_file  * o,
   if (writable)
     {
       char * dir = tr_sys_path_dirname (filename, NULL);
-      const int err = tr_mkdirp (dir, 0777) ? errno : 0;
-      if (err)
+      if (!tr_sys_dir_create (dir, TR_SYS_DIR_CREATE_PARENTS, 0777, &error))
         {
-          tr_logAddError (_("Couldn't create \"%1$s\": %2$s"), dir, tr_strerror (err));
+          const int err = error->code;
+          tr_logAddError (_("Couldn't create \"%1$s\": %2$s"), dir, error->message);
+          tr_error_free (error);
           tr_free (dir);
           return err;
         }
diff --git a/libtransmission/libtransmission-test.c b/libtransmission/libtransmission-test.c
index 236e005..b0e68b4 100644
--- a/libtransmission/libtransmission-test.c
+++ b/libtransmission/libtransmission-test.c
@@ -117,9 +117,7 @@ runTests (const testFunc * const tests, int numTests)
 ****
 ***/
 
-#include <sys/types.h> /* opendir() */
-#include <dirent.h> /* opendir() */
-#include <unistd.h> /* getcwd() */
+#include <unistd.h> /* sync() */
 
 #include <errno.h>
 #include <string.h> /* strcmp() */
@@ -134,21 +132,18 @@ static char*
 tr_getcwd (void)
 {
   char * result;
-  char buf[2048];
+  tr_error * error = NULL;
 
-#ifdef _WIN32
-  result = _getcwd (buf, sizeof (buf));
-#else
-  result = getcwd (buf, sizeof (buf));
-#endif
+  result = tr_sys_dir_get_current (&error);
 
   if (result == NULL)
     {
-      fprintf (stderr, "getcwd error: \"%s\"", tr_strerror (errno));
-      *buf = '\0';
+      fprintf (stderr, "getcwd error: \"%s\"", error->message);
+      tr_error_free (error);
+      result = tr_strdup ("");
     }
 
-  return tr_strdup (buf);
+  return result;
 }
 
 char *
@@ -157,7 +152,7 @@ libtest_sandbox_create (void)
   char * path = tr_getcwd ();
   char * sandbox = tr_buildPath (path, "sandbox-XXXXXX", NULL);
   tr_free (path);
-  tr_mkdtemp (sandbox);
+  tr_sys_dir_create_temp (sandbox, NULL);
   return sandbox;
 }
 
@@ -168,21 +163,22 @@ rm_rf (const char * killme)
 
   if (tr_sys_path_get_info (killme, 0, &info, NULL))
     {
-      DIR * odir;
+      tr_sys_dir_t odir;
 
-      if (info.type == TR_SYS_PATH_IS_DIRECTORY && ((odir = opendir (killme))))
+      if (info.type == TR_SYS_PATH_IS_DIRECTORY &&
+          (odir = tr_sys_dir_open (killme, NULL)) != TR_BAD_SYS_DIR)
         {
-          struct dirent *d;
-          for (d = readdir(odir); d != NULL; d=readdir(odir))
+          const char * name;
+          while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
             {
-              if (d->d_name && strcmp(d->d_name,".") && strcmp(d->d_name,".."))
+              if (strcmp (name, ".") != 0 && strcmp (name, "..") != 0)
                 {
-                  char * tmp = tr_buildPath (killme, d->d_name, NULL);
+                  char * tmp = tr_buildPath (killme, name, NULL);
                   rm_rf (tmp);
                   tr_free (tmp);
                 }
             }
-          closedir (odir);
+          tr_sys_dir_close (odir, NULL);
         }
 
       if (verbose)
@@ -256,7 +252,7 @@ libttest_session_init (tr_variant * settings)
     path = tr_strdup_printf ("%s/%*.*s", sandbox, (int)len, (int)len, str);
   else
     path = tr_buildPath (sandbox, "Downloads", NULL);
-  tr_mkdirp (path, 0700);
+  tr_sys_dir_create (path, TR_SYS_DIR_CREATE_PARENTS, 0700, NULL);
   tr_variantDictAddStr (settings, q, path);
   tr_free (path);
 
@@ -270,7 +266,7 @@ libttest_session_init (tr_variant * settings)
   tr_free (path);
 
   path = tr_buildPath (sandbox, "blocklists", NULL);
-  tr_mkdirp (path, 0700);
+  tr_sys_dir_create (path, TR_SYS_DIR_CREATE_PARENTS, 0700, NULL);
   tr_free (path);
 
   q = TR_KEY_port_forwarding_enabled;
@@ -384,7 +380,7 @@ libttest_zero_torrent_populate (tr_torrent * tor, bool complete)
       else
         path = tr_strdup_printf ("%s%c%s", tor->currentDir, TR_PATH_DELIMITER, file->name);
       dirname = tr_sys_path_dirname (path, NULL);
-      tr_mkdirp (dirname, 0700);
+      tr_sys_dir_create (dirname, TR_SYS_DIR_CREATE_PARENTS, 0700, NULL);
       fd = tr_sys_file_open (path, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_TRUNCATE, 0600, NULL);
       for (j=0; j<file->length; ++j)
         tr_sys_file_write (fd, ((!complete) && (i==0) && (j<tor->info.pieceSize)) ? "\1" : "\0", 1, NULL, NULL);
@@ -437,12 +433,12 @@ static void
 build_parent_dir (const char* path)
 {
   char * dir;
+  tr_error * error = NULL;
   const int tmperr = errno;
 
   dir = tr_sys_path_dirname (path, NULL);
-  errno = 0;
-  tr_mkdirp (dir, 0700);
-  assert (errno == 0);
+  tr_sys_dir_create (dir, TR_SYS_DIR_CREATE_PARENTS, 0700, &error);
+  assert (error == NULL);
   tr_free (dir);
 
   errno = tmperr;
diff --git a/libtransmission/makemeta-test.c b/libtransmission/makemeta-test.c
index 806b987..f6c3c5e 100644
--- a/libtransmission/makemeta-test.c
+++ b/libtransmission/makemeta-test.c
@@ -144,7 +144,7 @@ test_single_directory_impl (const tr_tracker_info * trackers,
 
   /* create the top temp directory */
   top = tr_buildPath (sandbox, "folder.XXXXXX", NULL);
-  tr_mkdtemp (top);
+  tr_sys_dir_create_temp (top, NULL);
 
   /* build the payload files that go into the top temp directory */
   files = tr_new (char*, payloadCount);
diff --git a/libtransmission/makemeta.c b/libtransmission/makemeta.c
index 1a06885..21f3da5 100644
--- a/libtransmission/makemeta.c
+++ b/libtransmission/makemeta.c
@@ -12,8 +12,6 @@
 #include <stdlib.h> /* qsort */
 #include <string.h> /* strcmp, strlen */
 
-#include <dirent.h>
-
 #include <event2/util.h> /* evutil_ascii_strcasecmp () */
 
 #include "transmission.h"
@@ -44,7 +42,7 @@ getFiles (const char      * dir,
           const char      * base,
           struct FileList * list)
 {
-  DIR * odir;
+  tr_sys_dir_t odir;
   char * buf;
   tr_sys_path_info info;
   tr_error * error = NULL;
@@ -59,13 +57,14 @@ getFiles (const char      * dir,
       return list;
     }
 
-  if (info.type == TR_SYS_PATH_IS_DIRECTORY && ((odir = opendir (buf))))
+  if (info.type == TR_SYS_PATH_IS_DIRECTORY &&
+      (odir = tr_sys_dir_open (buf, NULL)) != TR_BAD_SYS_DIR)
     {
-      struct dirent *d;
-      for (d = readdir (odir); d != NULL; d = readdir (odir))
-        if (d->d_name && d->d_name[0] != '.') /* skip dotfiles */
-          list = getFiles (buf, d->d_name, list);
-      closedir (odir);
+      const char * name;
+      while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
+        if (name[0] != '.') /* skip dotfiles */
+          list = getFiles (buf, name, list);
+      tr_sys_dir_close (odir, NULL);
     }
   else if (info.type == TR_SYS_PATH_IS_FILE && info.size > 0)
     {
diff --git a/libtransmission/move-test.c b/libtransmission/move-test.c
index 9c41da9..906d189 100644
--- a/libtransmission/move-test.c
+++ b/libtransmission/move-test.c
@@ -8,23 +8,20 @@
  */
 
 #include <assert.h>
-#include <errno.h>
 #include <stdio.h> /* remove() */
 #include <string.h> /* strcmp() */
 #include <stdio.h>
 
-#include <sys/types.h> /* stat() */
-#include <sys/stat.h> /* stat() */
-#include <unistd.h> /* stat(), sync() */
+#include <unistd.h> /* sync() */
 
 #include <event2/buffer.h>
 
 #include "transmission.h"
 #include "cache.h"
+#include "file.h"
 #include "resume.h"
 #include "trevent.h"
 #include "torrent.h" /* tr_isTorrent() */
-#include "utils.h" /* tr_mkdirp() */
 #include "variant.h"
 
 #include "libtransmission-test.h"
@@ -183,7 +180,7 @@ test_set_location (void)
   /* init the session */
   session = libttest_session_init (NULL);
   target_dir = tr_buildPath (tr_sessionGetConfigDir (session), "target", NULL);
-  tr_mkdirp (target_dir, 0777);
+  tr_sys_dir_create (target_dir, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
   
   /* init a torrent. */
   tor = libttest_zero_torrent_init (session);
diff --git a/libtransmission/platform.c b/libtransmission/platform.c
index df5c8d7..df394f1 100644
--- a/libtransmission/platform.c
+++ b/libtransmission/platform.c
@@ -270,11 +270,11 @@ tr_setConfigDir (tr_session * session, const char * configDir)
   session->configDir = tr_strdup (configDir);
 
   path = tr_buildPath (configDir, RESUME_SUBDIR, NULL);
-  tr_mkdirp (path, 0777);
+  tr_sys_dir_create (path, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
   session->resumeDir = path;
 
   path = tr_buildPath (configDir, TORRENT_SUBDIR, NULL);
-  tr_mkdirp (path, 0777);
+  tr_sys_dir_create (path, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
   session->torrentDir = path;
 }
 
diff --git a/libtransmission/session.c b/libtransmission/session.c
index a705635..2337496 100644
--- a/libtransmission/session.c
+++ b/libtransmission/session.c
@@ -16,7 +16,6 @@
 #include <signal.h>
 #include <sys/types.h> /* umask () */
 #include <sys/stat.h> /* umask () */
-#include <dirent.h> /* opendir */
 
 #include <event2/dns.h> /* evdns_base_free () */
 #include <event2/event.h>
@@ -719,7 +718,7 @@ tr_sessionInitImpl (void * vdata)
 
   {
     char * filename = tr_buildPath (session->configDir, "blocklists", NULL);
-    tr_mkdirp (filename, 0777);
+    tr_sys_dir_create (filename, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
     tr_free (filename);
     loadBlocklists (session);
   }
@@ -1948,7 +1947,7 @@ sessionLoadTorrents (void * vdata)
   int i;
   int n = 0;
   tr_sys_path_info info;
-  DIR * odir = NULL;
+  tr_sys_dir_t odir = NULL;
   tr_list * l = NULL;
   tr_list * list = NULL;
   struct sessionLoadTorrentsData * data = vdata;
@@ -1958,17 +1957,17 @@ sessionLoadTorrents (void * vdata)
 
   tr_ctorSetSave (data->ctor, false); /* since we already have them */
 
-  if (tr_sys_path_get_info (dirname, 0, &info, NULL)
-      && info.type == TR_SYS_PATH_IS_DIRECTORY
-      && ((odir = opendir (dirname))))
+  if (tr_sys_path_get_info (dirname, 0, &info, NULL) &&
+      info.type == TR_SYS_PATH_IS_DIRECTORY &&
+      (odir = tr_sys_dir_open (dirname, NULL)) != TR_BAD_SYS_DIR)
     {
-      struct dirent *d;
-      for (d = readdir (odir); d != NULL; d = readdir (odir))
+      const char * name;
+      while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
         {
-          if (tr_str_has_suffix (d->d_name, ".torrent"))
+          if (tr_str_has_suffix (name, ".torrent"))
             {
               tr_torrent * tor;
-              char * path = tr_buildPath (dirname, d->d_name, NULL);
+              char * path = tr_buildPath (dirname, name, NULL);
               tr_ctorSetMetainfoFromFile (data->ctor, path);
               if ((tor = tr_torrentNew (data->ctor, NULL, NULL)))
                 {
@@ -1978,7 +1977,7 @@ sessionLoadTorrents (void * vdata)
               tr_free (path);
             }
         }
-      closedir (odir);
+      tr_sys_dir_close (odir, NULL);
     }
 
   data->torrents = tr_new (tr_torrent *, n);
@@ -2229,31 +2228,31 @@ tr_stringEndsWith (const char * str, const char * end)
 static void
 loadBlocklists (tr_session * session)
 {
-  DIR * odir;
+  tr_sys_dir_t odir;
   char * dirname;
-  struct dirent * d;
+  const char * name;
   tr_list * blocklists = NULL;
   tr_ptrArray loadme = TR_PTR_ARRAY_INIT;
   const bool isEnabled = session->isBlocklistEnabled;
 
   /* walk the blocklist directory... */
   dirname = tr_buildPath (session->configDir, "blocklists", NULL);
-  odir = opendir (dirname);
-  if (odir == NULL)
+  odir = tr_sys_dir_open (dirname, NULL);
+  if (odir == TR_BAD_SYS_DIR)
     {
       tr_free (dirname);
       return;
     }
 
-  while ((d = readdir (odir)))
+  while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
     {
       char * path;
       char * load = NULL;
  
-      if (!d->d_name || (d->d_name[0]=='.')) /* ignore dotfiles */
+      if (name[0] == '.') /* ignore dotfiles */
         continue;
 
-      path = tr_buildPath (dirname, d->d_name, NULL);
+      path = tr_buildPath (dirname, name, NULL);
 
       if (tr_stringEndsWith (path, ".bin"))
         {
@@ -2266,7 +2265,7 @@ loadBlocklists (tr_session * session)
           tr_sys_path_info path_info;
           tr_sys_path_info binname_info;
 
-          basename = tr_sys_path_basename (d->d_name, NULL);
+          basename = tr_sys_path_basename (name, NULL);
           binname = tr_strdup_printf ("%s" TR_PATH_DELIMITER_STR "%s.bin", dirname, basename);
 
           if (!tr_sys_path_get_info (binname, 0, &binname_info, NULL)) /* create it */
@@ -2328,7 +2327,7 @@ loadBlocklists (tr_session * session)
     }
 
   /* cleanup */
-  closedir (odir);
+  tr_sys_dir_close (odir, NULL);
   tr_free (dirname);
   tr_ptrArrayDestruct (&loadme, (PtrArrayForeachFunc)tr_free);
   session->blocklists = blocklists;
@@ -2460,7 +2459,7 @@ metainfoLookupInit (tr_session * session)
 {
   tr_sys_path_info info;
   const char * dirname = tr_getTorrentDir (session);
-  DIR * odir = NULL;
+  tr_sys_dir_t odir;
   tr_ctor * ctor = NULL;
   tr_variant * lookup;
   int n = 0;
@@ -2472,17 +2471,17 @@ metainfoLookupInit (tr_session * session)
   tr_variantInitDict (lookup, 0);
   ctor = tr_ctorNew (session);
   tr_ctorSetSave (ctor, false); /* since we already have them */
-  if (tr_sys_path_get_info (dirname, 0, &info, NULL)
-      && info.type == TR_SYS_PATH_IS_DIRECTORY
-      && ((odir = opendir (dirname))))
+  if (tr_sys_path_get_info (dirname, 0, &info, NULL) &&
+      info.type == TR_SYS_PATH_IS_DIRECTORY &&
+      (odir = tr_sys_dir_open (dirname, NULL)) != TR_BAD_SYS_DIR)
     {
-      struct dirent *d;
-      while ((d = readdir (odir)))
+      const char * name;
+      while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
         {
-          if (tr_str_has_suffix (d->d_name, ".torrent"))
+          if (tr_str_has_suffix (name, ".torrent"))
             {
               tr_info inf;
-              char * path = tr_buildPath (dirname, d->d_name, NULL);
+              char * path = tr_buildPath (dirname, name, NULL);
               tr_ctorSetMetainfoFromFile (ctor, path);
               if (!tr_torrentParse (ctor, &inf))
                 {
@@ -2492,7 +2491,7 @@ metainfoLookupInit (tr_session * session)
               tr_free (path);
             }
         }
-      closedir (odir);
+      tr_sys_dir_close (odir, NULL);
     }
   tr_ctorFree (ctor);
 
diff --git a/libtransmission/torrent.c b/libtransmission/torrent.c
index c130c08..03bd4f0 100644
--- a/libtransmission/torrent.c
+++ b/libtransmission/torrent.c
@@ -15,7 +15,6 @@
  #include <process.h>
 #endif
 #include <unistd.h> /* fork (), execvp (), _exit () */
-#include <dirent.h>
 
 #include <assert.h>
 #include <math.h>
@@ -2839,22 +2838,22 @@ isJunkFile (const char * base)
 static void
 removeEmptyFoldersAndJunkFiles (const char * folder)
 {
-  DIR * odir;
+  tr_sys_dir_t odir;
 
-  if ((odir = opendir (folder)))
+  if ((odir = tr_sys_dir_open (folder, NULL)) != TR_BAD_SYS_DIR)
     {
-      struct dirent * d;
-      while ((d = readdir (odir)))
+      const char * name;
+      while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
         {
-          if (strcmp (d->d_name, ".") && strcmp (d->d_name, ".."))
+          if (strcmp (name, ".") != 0 && strcmp (name, "..") != 0)
             {
               tr_sys_path_info info;
-              char * filename = tr_buildPath (folder, d->d_name, NULL);
+              char * filename = tr_buildPath (folder, name, NULL);
 
               if (tr_sys_path_get_info (filename, 0, &info, NULL) &&
                   info.type == TR_SYS_PATH_IS_DIRECTORY)
                 removeEmptyFoldersAndJunkFiles (filename);
-              else if (isJunkFile (d->d_name))
+              else if (isJunkFile (name))
                 tr_sys_path_remove (filename, NULL);
 
               tr_free (filename);
@@ -2862,7 +2861,7 @@ removeEmptyFoldersAndJunkFiles (const char * folder)
         }
 
       tr_sys_path_remove (folder, NULL);
-      closedir (odir);
+      tr_sys_dir_close (odir, NULL);
     }
 }
 
@@ -2881,7 +2880,7 @@ deleteLocalData (tr_torrent * tor, tr_fileFunc func)
   int i, n;
   tr_file_index_t f;
   char * base;
-  DIR * odir;
+  tr_sys_dir_t odir;
   char * tmpdir = NULL;
   tr_ptrArray files = TR_PTR_ARRAY_INIT;
   tr_ptrArray folders = TR_PTR_ARRAY_INIT;
@@ -2898,7 +2897,7 @@ deleteLocalData (tr_torrent * tor, tr_fileFunc func)
 
   base = tr_strdup_printf ("%s__XXXXXX", tr_torrentName (tor));
   tmpdir = tr_buildPath (top, base, NULL);
-  tr_mkdtemp (tmpdir);
+  tr_sys_dir_create_temp (tmpdir, NULL);
   tr_free (base);
 
   for (f=0; f<tor->info.fileCount; ++f)
@@ -2940,19 +2939,19 @@ deleteLocalData (tr_torrent * tor, tr_fileFunc func)
   ***/
 
   /* try deleting the local data's top-level files & folders */
-  if ((odir = opendir (tmpdir)))
+  if ((odir = tr_sys_dir_open (tmpdir, NULL)) != TR_BAD_SYS_DIR)
     {
-      struct dirent * d;
-      while ((d = readdir (odir)))
+      const char * name;
+      while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
         {
-          if (strcmp (d->d_name, ".") && strcmp (d->d_name, ".."))
+          if (strcmp (name, ".") != 0 && strcmp (name, "..") != 0)
             {
-              char * file = tr_buildPath (tmpdir, d->d_name, NULL);
+              char * file = tr_buildPath (tmpdir, name, NULL);
               func (file);
               tr_free (file);
             }
         }
-      closedir (odir);
+      tr_sys_dir_close (odir, NULL);
     }
 
   /* go from the bottom up */
@@ -3063,7 +3062,7 @@ setLocation (void * vdata)
   tr_logAddDebug ("Moving \"%s\" location from currentDir \"%s\" to \"%s\"",
                   tr_torrentName (tor), tor->currentDir, location);
 
-  tr_mkdirp (location, 0777);
+  tr_sys_dir_create (location, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
 
   if (!tr_sys_path_is_same (location, tor->currentDir, NULL))
     {
diff --git a/libtransmission/utils.c b/libtransmission/utils.c
index 1ebf9aa..4e8d7d5 100644
--- a/libtransmission/utils.c
+++ b/libtransmission/utils.c
@@ -14,7 +14,6 @@
 #if defined (XCODE_BUILD)
  #define HAVE_GETPAGESIZE
  #define HAVE_ICONV_OPEN
- #define HAVE_MKDTEMP
  #define HAVE_VALLOC
 #endif
 
@@ -28,8 +27,6 @@
 #include <stdlib.h>
 #include <string.h> /* strerror (), memset (), memmem () */
 #include <time.h> /* nanosleep () */
-#include <sys/types.h>
-#include <sys/stat.h>
 
 #ifdef HAVE_ICONV_OPEN
  #include <iconv.h>
@@ -282,111 +279,6 @@ tr_loadFile (const char * path,
 }
 
 char*
-tr_mkdtemp (char * template)
-{
-#ifdef HAVE_MKDTEMP
-  return mkdtemp (template);
-#else
-  if (!mktemp (template) || mkdir (template, 0700))
-    return NULL;
-  return template;
-#endif
-}
-
-/**
- * @brief Portability wrapper for mkdir ()
- *
- * A portability wrapper around mkdir ().
- * On WIN32, the `permissions' argument is unused.
- *
- * @return zero on success, or -1 if an error occurred
- * (in which case errno is set appropriately).
- */
-static int
-tr_mkdir (const char * path, int permissions UNUSED)
-{
-#ifdef _WIN32
-  if (path && isalpha (path[0]) && path[1] == ':' && !path[2])
-    return 0;
-  return mkdir (path);
-#else
-  return mkdir (path, permissions);
-#endif
-}
-
-int
-tr_mkdirp (const char * path_in,
-           int          permissions)
-{
-  char * p;
-  char * pp;
-  bool done;
-  int tmperr;
-  int rv;
-  struct stat sb;
-  char * path;
-
-  /* make a temporary copy of path */
-  path = tr_strdup (path_in);
-  if (path == NULL)
-    {
-      errno = ENOMEM;
-      return -1;
-    }
-
-  /* walk past the root */
-  p = path;
-  while (*p == TR_PATH_DELIMITER)
-    ++p;
-
-  pp = p;
-  done = false;
-  while ((p = strchr (pp, TR_PATH_DELIMITER)) || (p = strchr (pp, '\0')))
-    {
-      if (!*p)
-        done = true;
-      else
-        *p = '\0';
-
-      tmperr = errno;
-      rv = stat (path, &sb);
-      errno = tmperr;
-      if (rv)
-        {
-          /* Folder doesn't exist yet */
-          if (tr_mkdir (path, permissions))
-            {
-              tmperr = errno;
-              tr_logAddError (_("Couldn't create \"%1$s\": %2$s"), path, tr_strerror (tmperr));
-              tr_free (path);
-              errno = tmperr;
-              return -1;
-            }
-        }
-      else if ((sb.st_mode & S_IFMT) != S_IFDIR)
-        {
-          /* Node exists but isn't a folder */
-          char * buf = tr_strdup_printf (_("File \"%s\" is in the way"), path);
-          tr_logAddError (_("Couldn't create \"%1$s\": %2$s"), path_in, buf);
-          tr_free (buf);
-          tr_free (path);
-          errno = ENOTDIR;
-          return -1;
-        }
-
-      if (done)
-        break;
-
-      *p = TR_PATH_DELIMITER;
-      p++;
-      pp = p;
-    }
-
-  tr_free (path);
-  return 0;
-}
-
-char*
 tr_buildPath (const char *first_element, ...)
 {
   const char * element;
@@ -1569,10 +1461,15 @@ tr_moveFile (const char * oldpath, const char * newpath, bool * renamed)
   /* make sure the target directory exists */
   {
     char * newdir = tr_sys_path_dirname (newpath, NULL);
-    int i = tr_mkdirp (newdir, 0777);
+    const bool i = tr_sys_dir_create (newdir, TR_SYS_DIR_CREATE_PARENTS, 0777, &error);
     tr_free (newdir);
-    if (i)
-      return i;
+    if (!i)
+      {
+        const int err = error->code;
+        tr_error_free (error);
+        errno = err;
+        return -1;
+      }
   }
 
   /* they might be on the same filesystem... */
diff --git a/libtransmission/utils.h b/libtransmission/utils.h
index 5d0f402..c9efc8c 100644
--- a/libtransmission/utils.h
+++ b/libtransmission/utils.h
@@ -128,18 +128,6 @@ bool tr_wildmat (const char * text, const char * pattern) TR_GNUC_NONNULL (1,2);
 
 
 /**
- * Like mkdir, but makes parent directories as needed.
- *
- * @return zero on success, or -1 if an error occurred
- * (in which case errno is set appropriately).
- */
-int tr_mkdirp (const char * path, int permissions) TR_GNUC_NONNULL (1);
-
-/** @brief Portability wrapper for mkdtemp () that uses the system implementation if available */
-char* tr_mkdtemp (char * _template);
-
-
-/**
  * @brief Loads a file and returns its contents.
  * On failure, NULL is returned and errno is set.
  */
diff --git a/utils/create.c b/utils/create.c
index 1fcc01f..729164c 100644
--- a/utils/create.c
+++ b/utils/create.c
@@ -7,12 +7,11 @@
  * $Id: create.c 14316 2014-07-04 00:00:07Z livings124 $
  */
 
-#include <errno.h>
 #include <stdio.h> /* fprintf() */
 #include <stdlib.h> /* strtoul(), EXIT_FAILURE */
-#include <unistd.h> /* getcwd() */
 
 #include <libtransmission/transmission.h>
+#include <libtransmission/error.h>
 #include <libtransmission/file.h>
 #include <libtransmission/makemeta.h>
 #include <libtransmission/tr-getopt.h>
@@ -112,21 +111,18 @@ static char*
 tr_getcwd (void)
 {
   char * result;
-  char buf[2048];
+  tr_error * error = NULL;
 
-#ifdef _WIN32
-  result = _getcwd (buf, sizeof (buf));
-#else
-  result = getcwd (buf, sizeof (buf));
-#endif
+  result = tr_sys_dir_get_current (&error);
 
   if (result == NULL)
     {
-      fprintf (stderr, "getcwd error: \"%s\"", tr_strerror (errno));
-      *buf = '\0';
+      fprintf (stderr, "getcwd error: \"%s\"", error->message);
+      tr_error_free (error);
+      result = tr_strdup ("");
     }
 
-  return tr_strdup (buf);
+  return result;
 }
 
 int
