diff --git a/cli/cli.c b/cli/cli.c
index 14a1cb0..44e4816 100644
--- a/cli/cli.c
+++ b/cli/cli.c
@@ -28,6 +28,7 @@
 #include <signal.h>
 
 #include <libtransmission/transmission.h>
+#include <libtransmission/file.h>
 #include <libtransmission/tr-getopt.h>
 #include <libtransmission/utils.h> /* tr_wait_msec */
 #include <libtransmission/variant.h>
@@ -265,11 +266,11 @@ main (int argc, char ** argv)
 
   if (tr_variantDictFindStr (&settings, TR_KEY_download_dir, &str, NULL))
     {
-      if (!tr_fileExists (str, NULL))
+      if (!tr_sys_path_exists (str, NULL))
         {
           tr_mkdirp (str, 0700);
 
-          if (!tr_fileExists (str, NULL))
+          if (!tr_sys_path_exists (str, NULL))
             {
               fprintf (stderr, "Unable to create download directory \"%s\"!\n", str);
               return EXIT_FAILURE;
diff --git a/daemon/daemon.c b/daemon/daemon.c
index 9da9508..73419df 100644
--- a/daemon/daemon.c
+++ b/daemon/daemon.c
@@ -22,6 +22,8 @@
 #include <event2/event.h>
 
 #include <libtransmission/transmission.h>
+#include <libtransmission/error.h>
+#include <libtransmission/file.h>
 #include <libtransmission/tr-getopt.h>
 #include <libtransmission/log.h>
 #include <libtransmission/utils.h>
@@ -281,14 +283,19 @@ onFileAdded (tr_session * session, const char * dir, const char * file)
 
             if (!test && trash)
             {
+                tr_error * error = NULL;
+
                 tr_logAddInfo ("Deleting input .torrent file \"%s\"", file);
-                if (tr_remove (filename))
-                    tr_logAddError ("Error deleting .torrent file: %s", tr_strerror (errno));
+                if (!tr_sys_path_remove (filename, &error))
+                {
+                    tr_logAddError ("Error deleting .torrent file: %s", error->message);
+                    tr_error_free (error);
+                }
             }
             else
             {
                 char * new_filename = tr_strdup_printf ("%s.added", filename);
-                tr_rename (filename, new_filename);
+                tr_sys_path_rename (filename, new_filename, NULL);
                 tr_free (new_filename);
             }
         }
@@ -666,7 +673,7 @@ cleanup:
 
     /* cleanup */
     if (pidfile_created)
-        tr_remove (pid_filename);
+        tr_sys_path_remove (pid_filename, NULL);
     tr_variantFree (&settings);
     sd_notify (0, "STATUS=\n");
     return 0;
diff --git a/daemon/watch.c b/daemon/watch.c
index a1d2edb..286990d 100644
--- a/daemon/watch.c
+++ b/daemon/watch.c
@@ -12,8 +12,6 @@
   #include <sys/select.h>
   #include <unistd.h> /* close */
 #else
-  #include <sys/types.h> /* stat */
-  #include <sys/stat.h> /* stat */
   #include <event2/buffer.h> /* evbuffer */
 #endif
 
@@ -24,6 +22,7 @@
 #include <dirent.h> /* readdir */
 
 #include <libtransmission/transmission.h>
+#include <libtransmission/file.h>
 #include <libtransmission/log.h>
 #include <libtransmission/utils.h> /* tr_buildPath (), tr_logAddInfo () */
 #include "watch.h"
@@ -196,15 +195,15 @@ is_file_in_list (struct evbuffer * buf, const char * filename, size_t len)
 static void
 watchdir_update_impl (dtr_watchdir * w)
 {
-    struct stat sb;
+    tr_sys_path_info info;
     DIR * 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))
-         && !stat (dirname, &sb)
-         && S_ISDIR (sb.st_mode)
+         && tr_sys_path_get_info (dirname, 0, &info, NULL)
+         && info.type == TR_SYS_PATH_IS_DIRECTORY
          && ((odir = opendir (dirname))))
     {
         struct dirent * d;
diff --git a/gtk/open-dialog.c b/gtk/open-dialog.c
index 3e8738c..e5705da 100644
--- a/gtk/open-dialog.c
+++ b/gtk/open-dialog.c
@@ -13,7 +13,7 @@
 #include <string.h>
 
 #include <libtransmission/transmission.h>
-#include <libtransmission/utils.h> /* tr_is_same_file () */
+#include <libtransmission/file.h> /* tr_sys_path_is_same () */
 
 #include "conf.h"
 #include "file-list.h"
@@ -188,7 +188,7 @@ sourceChanged (GtkFileChooserButton * b, gpointer gdata)
       int duplicate_id = 0;
       tr_torrent * torrent;
 
-      if (filename && (!o->filename || !tr_is_same_file (filename, o->filename)))
+      if (filename && (!o->filename || !tr_sys_path_is_same (filename, o->filename, NULL)))
         {
           g_free (o->filename);
           o->filename = g_strdup (filename);
@@ -229,7 +229,7 @@ downloadDirChanged (GtkFileChooserButton * b, gpointer gdata)
   struct OpenData * data = gdata;
   char * fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (b));
 
-  if (fname && (!data->downloadDir || !tr_is_same_file (fname, data->downloadDir)))
+  if (fname && (!data->downloadDir || !tr_sys_path_is_same (fname, data->downloadDir, NULL)))
     {
       g_free (data->downloadDir);
       data->downloadDir = g_strdup (fname);
diff --git a/libtransmission/blocklist-test.c b/libtransmission/blocklist-test.c
index 9c06622..4468473 100644
--- a/libtransmission/blocklist-test.c
+++ b/libtransmission/blocklist-test.c
@@ -13,6 +13,7 @@
 
 #include "transmission.h"
 #include "blocklist.h"
+#include "file.h"
 #include "net.h"
 #include "session.h" /* tr_sessionIsAddressBlocked() */
 #include "utils.h"
@@ -38,11 +39,11 @@ create_text_file (const char * path, const char * contents)
   FILE * fp;
   char * dir;
 
-  dir = tr_dirname (path);
+  dir = tr_sys_path_dirname (path, NULL);
   tr_mkdirp (dir, 0700);
   tr_free (dir);
 
-  tr_remove (path);
+  tr_sys_path_remove (path, NULL);
   fp = fopen (path, "w+");
   fprintf (fp, "%s", contents);
   fclose (fp);
diff --git a/libtransmission/blocklist.c b/libtransmission/blocklist.c
index 0a834c2..6c75f03 100644
--- a/libtransmission/blocklist.c
+++ b/libtransmission/blocklist.c
@@ -26,12 +26,11 @@
 #ifndef _WIN32
  #include <sys/mman.h>
 #endif
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <fcntl.h>
 
 #include "transmission.h"
 #include "blocklist.h"
+#include "file.h"
 #include "log.h"
 #include "net.h"
 #include "utils.h"
@@ -80,13 +79,17 @@ blocklistLoad (tr_blocklistFile * b)
 {
   int fd;
   size_t byteCount;
-  struct stat st;
+  tr_sys_path_info info;
   char * base;
   const char * err_fmt = _("Couldn't read \"%1$s\": %2$s");
 
   blocklistClose (b);
 
-  if (stat (b->filename, &st) == -1)
+  if (!tr_sys_path_get_info (b->filename, 0, &info, NULL))
+    return;
+
+  byteCount = (size_t) info.size;
+  if (byteCount == 0)
     return;
 
   fd = open (b->filename, O_RDONLY | O_BINARY);
@@ -96,7 +99,6 @@ blocklistLoad (tr_blocklistFile * b)
       return;
     }
 
-  byteCount = (size_t) st.st_size;
   b->rules = mmap (NULL, byteCount, PROT_READ, MAP_PRIVATE, fd, 0);
   if (!b->rules)
     {
@@ -109,7 +111,7 @@ blocklistLoad (tr_blocklistFile * b)
   b->byteCount = byteCount;
   b->ruleCount = byteCount / sizeof (struct tr_ipv4_range);
 
-  base = tr_basename (b->filename);
+  base = tr_sys_path_basename (b->filename, NULL);
   tr_logAddInfo (_("Blocklist \"%s\" contains %"TR_PRIuSIZE" entries"), base, b->ruleCount);
   tr_free (base);
 }
@@ -136,7 +138,7 @@ static void
 blocklistDelete (tr_blocklistFile * b)
 {
   blocklistClose (b);
-  tr_remove (b->filename);
+  tr_sys_path_remove (b->filename, NULL);
 }
 
 /***
@@ -173,7 +175,7 @@ tr_blocklistFileFree (tr_blocklistFile * b)
 bool
 tr_blocklistFileExists (const tr_blocklistFile * b)
 {
-  return tr_fileExists (b->filename, NULL);
+  return tr_sys_path_exists (b->filename, NULL);
 }
 
 int
@@ -416,7 +418,7 @@ tr_blocklistFileSetContent (tr_blocklistFile * b, const char * filename)
     }
   else
     {
-      char * base = tr_basename (b->filename);
+      char * base = tr_sys_path_basename (b->filename, NULL);
       tr_logAddInfo (_("Blocklist \"%s\" updated with %"TR_PRIuSIZE" entries"), base, ranges_count);
       tr_free (base);
     }
diff --git a/libtransmission/fdlimit.c b/libtransmission/fdlimit.c
index c868e2d..5926c16 100644
--- a/libtransmission/fdlimit.c
+++ b/libtransmission/fdlimit.c
@@ -40,6 +40,7 @@
 
 #include "transmission.h"
 #include "fdlimit.h"
+#include "file.h"
 #include "log.h"
 #include "session.h"
 #include "torrent.h" /* tr_isTorrent () */
@@ -331,14 +332,14 @@ cached_file_open (struct tr_cached_file  * o,
                   uint64_t                 file_size)
 {
   int flags;
-  struct stat sb;
+  tr_sys_path_info info;
   bool already_existed;
   bool resize_needed;
 
   /* create subfolders, if any */
   if (writable)
     {
-      char * dir = tr_dirname (filename);
+      char * dir = tr_sys_path_dirname (filename, NULL);
       const int err = tr_mkdirp (dir, 0777) ? errno : 0;
       if (err)
         {
@@ -349,14 +350,14 @@ cached_file_open (struct tr_cached_file  * o,
       tr_free (dir);
     }
 
-  already_existed = !stat (filename, &sb) && S_ISREG (sb.st_mode);
+  already_existed = tr_sys_path_get_info (filename, 0, &info, NULL) && info.type == TR_SYS_PATH_IS_FILE;
 
   if (writable && !already_existed && (allocation == TR_PREALLOCATE_FULL))
     if (preallocate_file_full (filename, file_size))
       tr_logAddDebug ("Preallocated file \"%s\"", filename);
 
   /* we can't resize the file w/o write permissions */
-  resize_needed = already_existed && (file_size < (uint64_t)sb.st_size);
+  resize_needed = already_existed && (file_size < info.size);
   writable |= resize_needed;
 
   /* open the file */
diff --git a/libtransmission/libtransmission-test.c b/libtransmission/libtransmission-test.c
index 00bd950..d9af33e 100644
--- a/libtransmission/libtransmission-test.c
+++ b/libtransmission/libtransmission-test.c
@@ -14,6 +14,7 @@
 #include <unistd.h>
 
 #include "transmission.h"
+#include "file.h"
 #include "platform.h" /* TR_PATH_DELIMETER */
 #include "torrent.h"
 #include "trevent.h"
@@ -115,8 +116,7 @@ runTests (const testFunc * const tests, int numTests)
 ****
 ***/
 
-#include <sys/types.h> /* stat(), opendir() */
-#include <sys/stat.h> /* stat() */
+#include <sys/types.h> /* opendir() */
 #include <dirent.h> /* opendir() */
 #include <unistd.h> /* getcwd() */
 
@@ -163,13 +163,13 @@ libtest_sandbox_create (void)
 static void
 rm_rf (const char * killme)
 {
-  struct stat sb;
+  tr_sys_path_info info;
 
-  if (!stat (killme, &sb))
+  if (tr_sys_path_get_info (killme, 0, &info, NULL))
     {
       DIR * odir;
 
-      if (S_ISDIR (sb.st_mode) && ((odir = opendir (killme))))
+      if (info.type == TR_SYS_PATH_IS_DIRECTORY && ((odir = opendir (killme))))
         {
           struct dirent *d;
           for (d = readdir(odir); d != NULL; d=readdir(odir))
@@ -187,7 +187,7 @@ rm_rf (const char * killme)
       if (verbose)
         fprintf (stderr, "cleanup: removing %s\n", killme);
 
-      tr_remove (killme);
+      tr_sys_path_remove (killme, NULL);
     }
 }
 
@@ -377,13 +377,12 @@ libttest_zero_torrent_populate (tr_torrent * tor, bool complete)
       char * path;
       char * dirname;
       const tr_file * file = &tor->info.files[i];
-      struct stat sb;
 
       if (!complete && (i==0))
         path = tr_strdup_printf ("%s%c%s.part", tor->currentDir, TR_PATH_DELIMITER, file->name);
       else
         path = tr_strdup_printf ("%s%c%s", tor->currentDir, TR_PATH_DELIMITER, file->name);
-      dirname = tr_dirname (path);
+      dirname = tr_sys_path_dirname (path, NULL);
       tr_mkdirp (dirname, 0700);
       fp = fopen (path, "wb+");
       for (j=0; j<file->length; ++j)
@@ -396,9 +395,7 @@ libttest_zero_torrent_populate (tr_torrent * tor, bool complete)
       path = tr_torrentFindFile (tor, i);
       assert (path != NULL);
       err = errno;
-      errno = 0;
-      stat (path, &sb);
-      assert (errno == 0);
+      assert (tr_sys_path_exists (path, NULL));
       errno = err; 
       tr_free (path);
     }
@@ -441,7 +438,7 @@ build_parent_dir (const char* path)
   char * dir;
   const int tmperr = errno;
 
-  dir = tr_dirname (path);
+  dir = tr_sys_path_dirname (path, NULL);
   errno = 0;
   tr_mkdirp (dir, 0700);
   assert (errno == 0);
@@ -458,7 +455,7 @@ libtest_create_file_with_contents (const char* path, const void* payload, size_t
 
   build_parent_dir (path);
 
-  tr_remove (path);
+  tr_sys_path_remove (path, NULL);
   fp = fopen (path, "wb");
   fwrite (payload, 1, n, fp);
   fclose (fp);
diff --git a/libtransmission/log.c b/libtransmission/log.c
index b7ddf35..15db6c8 100644
--- a/libtransmission/log.c
+++ b/libtransmission/log.c
@@ -15,6 +15,7 @@
 #include <event2/buffer.h>
 
 #include "transmission.h"
+#include "file.h"
 #include "log.h"
 #include "platform.h" /* tr_lock */
 #include "utils.h"
@@ -191,7 +192,7 @@ tr_logAddDeep (const char  * file,
       char timestr[64];
       char * message;
       struct evbuffer * buf = evbuffer_new ();
-      char * base = tr_basename (file);
+      char * base = tr_sys_path_basename (file, NULL);
 
       evbuffer_add_printf (buf, "[%s] ",
                            tr_logGetTimeStr (timestr, sizeof (timestr)));
diff --git a/libtransmission/makemeta-test.c b/libtransmission/makemeta-test.c
index effe7ff..806b987 100644
--- a/libtransmission/makemeta-test.c
+++ b/libtransmission/makemeta-test.c
@@ -11,6 +11,7 @@
 
 #include "transmission.h"
 #include "crypto.h"
+#include "file.h"
 #include "makemeta.h"
 
 #include <unistd.h> /* sync() */
@@ -69,7 +70,7 @@ test_single_file_impl (const tr_tracker_info * trackers,
 
   /* quick check of some of the parsed metainfo */
   check_int_eq (payloadSize, inf.totalSize);
-  tmpstr = tr_basename(input_file);
+  tmpstr = tr_sys_path_basename (input_file, NULL);
   check_streq (tmpstr, inf.name);
   tr_free (tmpstr);
   check_streq (comment, inf.comment);
@@ -190,7 +191,7 @@ test_single_directory_impl (const tr_tracker_info * trackers,
 
   /* quick check of some of the parsed metainfo */
   check_int_eq (totalSize, inf.totalSize);
-  tmpstr = tr_basename(top);
+  tmpstr = tr_sys_path_basename (top, NULL);
   check_streq (tmpstr, inf.name);
   tr_free (tmpstr);
   check_streq (comment, inf.comment);
diff --git a/libtransmission/makemeta.c b/libtransmission/makemeta.c
index 5623dbc..8dd4d26 100644
--- a/libtransmission/makemeta.c
+++ b/libtransmission/makemeta.c
@@ -13,8 +13,6 @@
 #include <stdlib.h> /* qsort */
 #include <string.h> /* strcmp, strlen */
 
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <unistd.h> /* read () */
 #include <dirent.h>
 
@@ -22,7 +20,9 @@
 
 #include "transmission.h"
 #include "crypto.h" /* tr_sha1 */
+#include "error.h"
 #include "fdlimit.h" /* tr_open_file_for_scanning () */
+#include "file.h"
 #include "log.h"
 #include "session.h"
 #include "makemeta.h"
@@ -47,24 +47,22 @@ getFiles (const char      * dir,
           const char      * base,
           struct FileList * list)
 {
-  int i;
   DIR * odir;
   char * buf;
-  struct stat sb;
-
-  sb.st_size = 0;
+  tr_sys_path_info info;
+  tr_error * error = NULL;
 
   buf = tr_buildPath (dir, base, NULL);
-  i = stat (buf, &sb);
-  if (i)
+  if (!tr_sys_path_get_info (buf, 0, &info, &error))
     {
       tr_logAddError (_("Torrent Creator is skipping file \"%s\": %s"),
-                buf, tr_strerror (errno));
+                      buf, error->message);
       tr_free (buf);
+      tr_error_free (error);
       return list;
     }
 
-  if (S_ISDIR (sb.st_mode) && ((odir = opendir (buf))))
+  if (info.type == TR_SYS_PATH_IS_DIRECTORY && ((odir = opendir (buf))))
     {
       struct dirent *d;
       for (d = readdir (odir); d != NULL; d = readdir (odir))
@@ -72,10 +70,10 @@ getFiles (const char      * dir,
           list = getFiles (buf, d->d_name, list);
       closedir (odir);
     }
-  else if (S_ISREG (sb.st_mode) && (sb.st_size > 0))
+  else if (info.type == TR_SYS_PATH_IS_FILE && info.size > 0)
     {
       struct FileList * node = tr_new (struct FileList, 1);
-      node->size = sb.st_size;
+      node->size = info.size;
       node->filename = tr_strdup (buf);
       node->next = list;
       list = node;
@@ -116,24 +114,21 @@ tr_metaInfoBuilderCreate (const char * topFileArg)
   int i;
   struct FileList * files;
   struct FileList * walk;
-  char topFile[TR_PATH_MAX];
   tr_metainfo_builder * ret = tr_new0 (tr_metainfo_builder, 1);
 
-  tr_realpath (topFileArg, topFile);
-
-  ret->top = tr_strdup (topFile);
+  ret->top = tr_sys_path_resolve (topFileArg, NULL);
 
   {
-    struct stat sb;
-    stat (topFile, &sb);
-    ret->isFolder = S_ISDIR (sb.st_mode);
+    tr_sys_path_info info;
+    ret->isFolder = tr_sys_path_get_info (ret->top, 0, &info, NULL) &&
+                    info.type == TR_SYS_PATH_IS_DIRECTORY;
   }
 
-  /* build a list of files containing topFile and,
+  /* build a list of files containing top file and,
      if it's a directory, all of its children */
   {
-    char * dir = tr_dirname (topFile);
-    char * base = tr_basename (topFile);
+    char * dir = tr_sys_path_dirname (ret->top, NULL);
+    char * base = tr_sys_path_basename (ret->top, NULL);
     files = getFiles (dir, base, NULL);
     tr_free (base);
     tr_free (dir);
@@ -375,7 +370,7 @@ makeInfoDict (tr_variant          * dict,
       tr_variantDictAddInt (dict, TR_KEY_length, builder->files[0].size);
     }
 
-  base = tr_basename (builder->top);
+  base = tr_sys_path_basename (builder->top, NULL);
   tr_variantDictAddStr (dict, TR_KEY_name, base);
   tr_free (base);
 
diff --git a/libtransmission/metainfo.c b/libtransmission/metainfo.c
index 9678ae8..b7f1bd6 100644
--- a/libtransmission/metainfo.c
+++ b/libtransmission/metainfo.c
@@ -8,21 +8,17 @@
  */
 
 #include <assert.h>
-#include <errno.h>
-#include <stdio.h> /* fopen (), fwrite (), fclose () */
 #include <string.h> /* strlen () */
 
-#include <sys/types.h>
-#include <unistd.h> /* stat */
-
 #include <event2/buffer.h>
 
 #include "transmission.h"
-#include "session.h"
 #include "crypto.h" /* tr_sha1 */
+#include "file.h"
 #include "log.h"
 #include "metainfo.h"
 #include "platform.h" /* tr_getTorrentDir () */
+#include "session.h"
 #include "utils.h"
 #include "variant.h"
 
@@ -635,7 +631,7 @@ tr_metainfoRemoveSaved (const tr_session * session, const tr_info * inf)
   char * filename;
 
   filename = getTorrentFilename (session, inf);
-  tr_remove (filename);
+  tr_sys_path_remove (filename, NULL);
   tr_free (filename);
 }
 
diff --git a/libtransmission/peer-msgs.c b/libtransmission/peer-msgs.c
index 3c65256..acac50b 100644
--- a/libtransmission/peer-msgs.c
+++ b/libtransmission/peer-msgs.c
@@ -21,6 +21,7 @@
 #include "cache.h"
 #include "completion.h"
 #include "crypto.h" /* tr_sha1 () */
+#include "file.h"
 #include "log.h"
 #include "peer-io.h"
 #include "peer-mgr.h"
@@ -288,7 +289,7 @@ myDebug (const char * file, int line,
       va_list           args;
       char              timestr[64];
       struct evbuffer * buf = evbuffer_new ();
-      char *            base = tr_basename (file);
+      char *            base = tr_sys_path_basename (file, NULL);
       char *            message;
 
       evbuffer_add_printf (buf, "[%s] %s - %s [%s]: ",
diff --git a/libtransmission/platform.c b/libtransmission/platform.c
index 496f699..bb9d74d 100644
--- a/libtransmission/platform.c
+++ b/libtransmission/platform.c
@@ -17,7 +17,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h> /* getuid(), close() */
-#include <sys/stat.h>
 
 #ifdef _WIN32
  #include <w32api.h>
@@ -39,10 +38,11 @@
 #endif
 
 #include "transmission.h"
-#include "session.h"
+#include "file.h"
 #include "list.h"
 #include "log.h"
 #include "platform.h"
+#include "session.h"
 
 /***
 ****  THREADS
@@ -399,9 +399,8 @@ tr_getDefaultDownloadDir (void)
 static int
 isWebClientDir (const char * path)
 {
-  struct stat sb;
   char * tmp = tr_buildPath (path, "index.html", NULL);
-  const int ret = !stat (tmp, &sb);
+  const bool ret = tr_sys_path_exists (tmp, NULL);
   tr_logAddInfo (_("Searching for web interface file \"%s\""), tmp);
   tr_free (tmp);
 
@@ -490,8 +489,11 @@ tr_getWebClientDir (const tr_session * session UNUSED)
 
             if (s == NULL) /* check calling module place */
               {
+                char * tmp;
                 GetModuleFileName (GetModuleHandle (NULL), dir, sizeof (dir));
-                s = tr_buildPath (dirname (dir), "Web", NULL);
+                tmp = tr_sys_path_dirname (dir, NULL);
+                s = tr_buildPath (tmp, "Web", NULL);
+                tr_free (tmp);
                 if (!isWebClientDir (s))
                   {
                     tr_free (s);
diff --git a/libtransmission/rename-test.c b/libtransmission/rename-test.c
index 74ad861..c6b3d1d 100644
--- a/libtransmission/rename-test.c
+++ b/libtransmission/rename-test.c
@@ -11,16 +11,13 @@
 #include <errno.h>
 #include <stdio.h> /* fopen() */
 #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 "transmission.h"
+#include "file.h"
 #include "resume.h"
 #include "torrent.h" /* tr_isTorrent() */
-#include "utils.h" /* tr_mkdirp() */
 #include "variant.h"
 
 #include "libtransmission-test.h"
@@ -55,7 +52,7 @@ testFileExistsAndConsistsOfThisString (const tr_torrent * tor, tr_file_index_t f
       uint8_t * contents;
       size_t contents_len;
 
-      assert (tr_fileExists (path, NULL));
+      assert (tr_sys_path_exists (path, NULL));
 
       contents = tr_loadFile (path, &contents_len);
 
@@ -188,17 +185,17 @@ test_single_filename_torrent (void)
   ***/
 
   tmpstr = tr_buildPath (tor->currentDir, "hello-world.txt", NULL); 
-  check (tr_fileExists (tmpstr, NULL));
+  check (tr_sys_path_exists (tmpstr, NULL));
   check_streq ("hello-world.txt", tr_torrentName(tor));
   check_int_eq (0, torrentRenameAndWait (tor, tor->info.name, "foobar"));
-  check (!tr_fileExists (tmpstr, NULL)); /* confirm the old filename can't be found */
+  check (!tr_sys_path_exists (tmpstr, NULL)); /* confirm the old filename can't be found */
   tr_free (tmpstr);
   check (tor->info.files[0].is_renamed); /* confirm the file's 'renamed' flag is set */
   check_streq ("foobar", tr_torrentName(tor)); /* confirm the torrent's name is now 'foobar' */
   check_streq ("foobar", tor->info.files[0].name); /* confirm the file's name is now 'foobar' in our struct */
   check (strstr (tor->info.torrent, "foobar") == NULL); /* confirm the name in the .torrent file hasn't changed */
   tmpstr = tr_buildPath (tor->currentDir, "foobar", NULL); 
-  check (tr_fileExists (tmpstr, NULL)); /* confirm the file's name is now 'foobar' on the disk */
+  check (tr_sys_path_exists (tmpstr, NULL)); /* confirm the file's name is now 'foobar' on the disk */
   tr_free (tmpstr);
   check (testFileExistsAndConsistsOfThisString (tor, 0, "hello, world!\n")); /* confirm the contents are right */
 
@@ -214,9 +211,9 @@ test_single_filename_torrent (void)
   ***/
 
   tmpstr = tr_buildPath (tor->currentDir, "foobar", NULL); 
-  check (tr_fileExists (tmpstr, NULL));
+  check (tr_sys_path_exists (tmpstr, NULL));
   check_int_eq (0, torrentRenameAndWait (tor, "foobar", "hello-world.txt"));
-  check (!tr_fileExists (tmpstr, NULL));
+  check (!tr_sys_path_exists (tmpstr, NULL));
   check (tor->info.files[0].is_renamed);
   check_streq ("hello-world.txt", tor->info.files[0].name);
   check_streq ("hello-world.txt", tr_torrentName(tor));
@@ -382,13 +379,13 @@ test_multifile_torrent (void)
   /* remove the directory Felidae/Felinae/Felis/catus */
   str = tr_torrentFindFile (tor, 1);
   check (str != NULL);
-  tr_remove (str);
+  tr_sys_path_remove (str, NULL);
   tr_free (str);
   str = tr_torrentFindFile (tor, 2);
   check (str != NULL);
-  tr_remove (str);
-  tmp = tr_dirname (str);
-  tr_remove (tmp);
+  tr_sys_path_remove (str, NULL);
+  tmp = tr_sys_path_dirname (str, NULL);
+  tr_sys_path_remove (tmp, NULL);
   tr_free (tmp);
   tr_free (str);
   sync ();
diff --git a/libtransmission/resume.c b/libtransmission/resume.c
index 054fa5f..be340b9 100644
--- a/libtransmission/resume.c
+++ b/libtransmission/resume.c
@@ -11,6 +11,7 @@
 
 #include "transmission.h"
 #include "completion.h"
+#include "file.h"
 #include "log.h"
 #include "metainfo.h" /* tr_metainfoGetBasename () */
 #include "peer-mgr.h" /* pex */
@@ -928,6 +929,6 @@ void
 tr_torrentRemoveResume (const tr_torrent * tor)
 {
   char * filename = getResumeFilename (tor);
-  tr_remove (filename);
+  tr_sys_path_remove (filename, NULL);
   tr_free (filename);
 }
diff --git a/libtransmission/rpcimpl.c b/libtransmission/rpcimpl.c
index 96d46c6..c426827 100644
--- a/libtransmission/rpcimpl.c
+++ b/libtransmission/rpcimpl.c
@@ -22,6 +22,7 @@
 #include "transmission.h"
 #include "completion.h"
 #include "fdlimit.h"
+#include "file.h"
 #include "log.h"
 #include "platform-quota.h" /* tr_device_info_get_free_space() */
 #include "rpcimpl.h"
@@ -1556,7 +1557,7 @@ gotNewBlocklist (tr_session       * session,
           tr_snprintf (result, sizeof (result), "success");
         }
 
-      tr_remove (filename);
+      tr_sys_path_remove (filename, NULL);
       tr_free (filename);
       tr_free (buf);
     }
diff --git a/libtransmission/session.c b/libtransmission/session.c
index 29b73fc..a705635 100644
--- a/libtransmission/session.c
+++ b/libtransmission/session.c
@@ -14,9 +14,8 @@
 #include <string.h> /* memcpy */
 
 #include <signal.h>
-#include <sys/types.h> /* stat (), umask () */
-#include <sys/stat.h> /* stat (), umask () */
-#include <unistd.h> /* stat */
+#include <sys/types.h> /* umask () */
+#include <sys/stat.h> /* umask () */
 #include <dirent.h> /* opendir */
 
 #include <event2/dns.h> /* evdns_base_free () */
@@ -32,6 +31,7 @@
 #include "cache.h"
 #include "crypto.h"
 #include "fdlimit.h"
+#include "file.h"
 #include "list.h"
 #include "log.h"
 #include "net.h"
@@ -1947,7 +1947,7 @@ sessionLoadTorrents (void * vdata)
 {
   int i;
   int n = 0;
-  struct stat sb;
+  tr_sys_path_info info;
   DIR * odir = NULL;
   tr_list * l = NULL;
   tr_list * list = NULL;
@@ -1958,8 +1958,8 @@ sessionLoadTorrents (void * vdata)
 
   tr_ctorSetSave (data->ctor, false); /* since we already have them */
 
-  if (!stat (dirname, &sb)
-      && S_ISDIR (sb.st_mode)
+  if (tr_sys_path_get_info (dirname, 0, &info, NULL)
+      && info.type == TR_SYS_PATH_IS_DIRECTORY
       && ((odir = opendir (dirname))))
     {
       struct dirent *d;
@@ -2263,13 +2263,13 @@ loadBlocklists (tr_session * session)
         {
           char * binname;
           char * basename;
-          time_t path_mtime = 0;
-          time_t binname_mtime = 0;
+          tr_sys_path_info path_info;
+          tr_sys_path_info binname_info;
 
-          basename = tr_basename (d->d_name);
+          basename = tr_sys_path_basename (d->d_name, NULL);
           binname = tr_strdup_printf ("%s" TR_PATH_DELIMITER_STR "%s.bin", dirname, basename);
 
-          if (!tr_fileExists (binname, &binname_mtime)) /* create it */
+          if (!tr_sys_path_get_info (binname, 0, &binname_info, NULL)) /* create it */
             {
               tr_blocklistFile * b = tr_blocklistFileNew (binname, isEnabled);
               const int n = tr_blocklistFileSetContent (b, path);
@@ -2278,23 +2278,24 @@ loadBlocklists (tr_session * session)
 
               tr_blocklistFileFree (b);
             }
-          else if (tr_fileExists(path,&path_mtime) && (path_mtime>=binname_mtime)) /* update it */
+          else if (tr_sys_path_get_info (path, 0, &path_info, NULL) &&
+                   path_info.last_modified_at >= binname_info.last_modified_at) /* update it */
             {
               char * old;
               tr_blocklistFile * b;
 
               old = tr_strdup_printf ("%s.old", binname);
-              tr_remove (old);
-              tr_rename (binname, old);
+              tr_sys_path_remove (old, NULL);
+              tr_sys_path_rename (binname, old, NULL);
               b = tr_blocklistFileNew (binname, isEnabled);
               if (tr_blocklistFileSetContent (b, path) > 0)
                 {
-                  tr_remove (old);
+                  tr_sys_path_remove (old, NULL);
                 }
               else
                 {
-                  tr_remove (binname);
-                  tr_rename (old, binname);
+                  tr_sys_path_remove (binname, NULL);
+                  tr_sys_path_rename (old, binname, NULL);
                 }
 
               tr_blocklistFileFree (b);
@@ -2457,7 +2458,7 @@ tr_blocklistGetURL (const tr_session * session)
 static void
 metainfoLookupInit (tr_session * session)
 {
-  struct stat sb;
+  tr_sys_path_info info;
   const char * dirname = tr_getTorrentDir (session);
   DIR * odir = NULL;
   tr_ctor * ctor = NULL;
@@ -2471,7 +2472,9 @@ metainfoLookupInit (tr_session * session)
   tr_variantInitDict (lookup, 0);
   ctor = tr_ctorNew (session);
   tr_ctorSetSave (ctor, false); /* since we already have them */
-  if (!stat (dirname, &sb) && S_ISDIR (sb.st_mode) && ((odir = opendir (dirname))))
+  if (tr_sys_path_get_info (dirname, 0, &info, NULL)
+      && info.type == TR_SYS_PATH_IS_DIRECTORY
+      && ((odir = opendir (dirname))))
     {
       struct dirent *d;
       while ((d = readdir (odir)))
diff --git a/libtransmission/torrent-ctor.c b/libtransmission/torrent-ctor.c
index 16fe891..2a1b78d 100644
--- a/libtransmission/torrent-ctor.c
+++ b/libtransmission/torrent-ctor.c
@@ -8,7 +8,9 @@
  */
 
 #include <errno.h> /* EINVAL */
+
 #include "transmission.h"
+#include "file.h"
 #include "magnet.h"
 #include "session.h" /* tr_sessionFindTorrentFile () */
 #include "torrent.h" /* tr_ctorGetSave () */
@@ -156,7 +158,7 @@ tr_ctorSetMetainfoFromFile (tr_ctor *    ctor,
                     name = NULL;
             if (!name || !*name)
             {
-                char * base = tr_basename (filename);
+                char * base = tr_sys_path_basename (filename, NULL);
                 tr_variantDictAddStr (info, TR_KEY_name, base);
                 tr_free (base);
             }
diff --git a/libtransmission/torrent-magnet.c b/libtransmission/torrent-magnet.c
index 28df138..9b73058 100644
--- a/libtransmission/torrent-magnet.c
+++ b/libtransmission/torrent-magnet.c
@@ -14,6 +14,7 @@
 
 #include "transmission.h"
 #include "crypto.h" /* tr_sha1 () */
+#include "file.h"
 #include "log.h"
 #include "magnet.h"
 #include "metainfo.h"
@@ -259,7 +260,7 @@ tr_torrentSetMetadataPiece (tr_torrent  * tor, int piece, const void  * data, in
                   int infoDictLength;
 
                   /* remove any old .torrent and .resume files */
-                  tr_remove (path);
+                  tr_sys_path_remove (path, NULL);
                   tr_torrentRemoveResume (tor);
 
                   dbgmsg (tor, "Saving completed metadata to \"%s\"", path);
diff --git a/libtransmission/torrent.c b/libtransmission/torrent.c
index f1c1abb..c130c08 100644
--- a/libtransmission/torrent.c
+++ b/libtransmission/torrent.c
@@ -7,15 +7,14 @@
  * $Id: torrent.c 14317 2014-07-04 03:23:27Z jordan $
  */
 
+#include <errno.h> /* EINVAL */
 #include <signal.h> /* signal () */
-#include <sys/types.h> /* stat */
-#include <sys/stat.h> /* stat */
 #ifndef _WIN32
  #include <sys/wait.h> /* wait () */
 #else
  #include <process.h>
 #endif
-#include <unistd.h> /* stat */
+#include <unistd.h> /* fork (), execvp (), _exit () */
 #include <dirent.h>
 
 #include <assert.h>
@@ -33,8 +32,9 @@
 #include "cache.h"
 #include "completion.h"
 #include "crypto.h" /* for tr_sha1 */
-#include "resume.h"
+#include "error.h"
 #include "fdlimit.h" /* tr_fdTorrentClose */
+#include "file.h"
 #include "inout.h" /* tr_ioTestPiece () */
 #include "log.h"
 #include "magnet.h"
@@ -43,6 +43,7 @@
 #include "peer-mgr.h"
 #include "platform.h" /* TR_PATH_DELIMITER_STR */
 #include "ptrarray.h"
+#include "resume.h"
 #include "session.h"
 #include "torrent.h"
 #include "torrent-magnet.h"
@@ -854,7 +855,6 @@ torrentInit (tr_torrent * tor, const tr_ctor * ctor)
   uint64_t loaded;
   const char * dir;
   bool isNewTorrent;
-  struct stat st;
   tr_session * session = tr_ctorGetSession (ctor);
   static int nextUniqueId = 1;
 
@@ -945,7 +945,7 @@ torrentInit (tr_torrent * tor, const tr_ctor * ctor)
     }
 
   /* if we don't have a local .torrent file already, assume the torrent is new */
-  isNewTorrent = stat (tor->info.torrent, &st);
+  isNewTorrent = !tr_sys_path_exists (tor->info.torrent, NULL);
 
   /* maybe save our own copy of the metainfo */
   if (tr_ctorGetSave (ctor))
@@ -1666,12 +1666,11 @@ tr_torrentGetCurrentSizeOnDisk (const tr_torrent * tor)
 
   for (i=0; i<n; ++i)
     {
-      struct stat sb;
+      tr_sys_path_info info;
       char * filename = tr_torrentFindFile (tor, i);
 
-      sb.st_size = 0;
-      if (filename && !stat (filename, &sb))
-        byte_count += sb.st_size;
+      if (filename != NULL && tr_sys_path_get_info (filename, 0, &info, NULL))
+        byte_count += info.size;
 
       tr_free (filename);
     }
@@ -2794,16 +2793,17 @@ tr_torrentGetBytesLeftToAllocate (const tr_torrent * tor)
     {
       if (!tor->info.files[i].dnd)
         {
-          struct stat sb;
+          tr_sys_path_info info;
           const uint64_t length = tor->info.files[i].length;
           char * path = tr_torrentFindFile (tor, i);
 
           bytesLeft += length;
 
-          if ((path != NULL) && !stat (path, &sb)
-                             && S_ISREG (sb.st_mode)
-                             && ((uint64_t)sb.st_size <= length))
-            bytesLeft -= sb.st_size;
+          if (path != NULL &&
+              tr_sys_path_get_info (path, 0, &info, NULL) &&
+              info.type == TR_SYS_PATH_IS_FILE &&
+              info.size <= length)
+            bytesLeft -= info.size;
 
           tr_free (path);
         }
@@ -2848,19 +2848,20 @@ removeEmptyFoldersAndJunkFiles (const char * folder)
         {
           if (strcmp (d->d_name, ".") && strcmp (d->d_name, ".."))
             {
-              struct stat sb;
+              tr_sys_path_info info;
               char * filename = tr_buildPath (folder, d->d_name, NULL);
 
-              if (!stat (filename, &sb) && S_ISDIR (sb.st_mode))
+              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))
-                tr_remove (filename);
+                tr_sys_path_remove (filename, NULL);
 
               tr_free (filename);
             }
         }
 
-      tr_remove (folder);
+      tr_sys_path_remove (folder, NULL);
       closedir (odir);
     }
 }
@@ -2906,13 +2907,13 @@ deleteLocalData (tr_torrent * tor, tr_fileFunc func)
 
       /* try to find the file, looking in the partial and download dirs */
       filename = tr_buildPath (top, tor->info.files[f].name, NULL);
-      if (!tr_fileExists (filename, NULL))
+      if (!tr_sys_path_exists (filename, NULL))
         {
           char * partial = tr_torrentBuildPartial (tor, f);
           tr_free (filename);
           filename = tr_buildPath (top, partial, NULL);
           tr_free (partial);
-          if (!tr_fileExists (filename, NULL))
+          if (!tr_sys_path_exists (filename, NULL))
             {
               tr_free (filename);
               filename = NULL;
@@ -2958,9 +2959,9 @@ deleteLocalData (tr_torrent * tor, tr_fileFunc func)
   for (i=0, n=tr_ptrArraySize (&files); i<n; ++i)
     {
       char * walk = tr_strdup (tr_ptrArrayNth (&files, i));
-      while (tr_fileExists (walk, NULL) && !tr_is_same_file (tmpdir, walk))
+      while (tr_sys_path_exists (walk, NULL) && !tr_sys_path_is_same (tmpdir, walk, NULL))
         {
-          char * tmp = tr_dirname (walk);
+          char * tmp = tr_sys_path_dirname (walk, NULL);
           func (walk);
           tr_free (walk);
           walk = tmp;
@@ -2982,16 +2983,16 @@ deleteLocalData (tr_torrent * tor, tr_fileFunc func)
 
       /* get the directory that this file goes in... */
       filename = tr_buildPath (top, tor->info.files[f].name, NULL);
-      dir = tr_dirname (filename);
+      dir = tr_sys_path_dirname (filename, NULL);
       tr_free (filename);
 
       /* walk up the directory tree until we reach 'top' */
-      if (!tr_is_same_file (top, dir) && strcmp (top, dir))
+      if (!tr_sys_path_is_same (top, dir, NULL) && strcmp (top, dir) != 0)
         {
           for (;;)
             {
-              char * parent = tr_dirname (dir);
-              if (tr_is_same_file (top, parent) || !strcmp (top, parent))
+              char * parent = tr_sys_path_dirname (dir, NULL);
+              if (tr_sys_path_is_same (top, parent, NULL) || strcmp (top, parent) == 0)
                 {
                   if (tr_ptrArrayFindSorted (&folders, dir, vstrcmp) == NULL)
                     tr_ptrArrayInsertSorted (&folders, tr_strdup(dir), vstrcmp);
@@ -3012,7 +3013,7 @@ deleteLocalData (tr_torrent * tor, tr_fileFunc func)
     removeEmptyFoldersAndJunkFiles (tr_ptrArrayNth (&folders, i));
 
   /* cleanup */
-  tr_remove (tmpdir);
+  tr_sys_path_remove (tmpdir, NULL);
   tr_free (tmpdir);
   tr_ptrArrayDestruct (&folders, tr_free);
   tr_ptrArrayDestruct (&files, tr_free);
@@ -3064,7 +3065,7 @@ setLocation (void * vdata)
 
   tr_mkdirp (location, 0777);
 
-  if (!tr_is_same_file (location, tor->currentDir))
+  if (!tr_sys_path_is_same (location, tor->currentDir, NULL))
     {
       tr_file_index_t i;
 
@@ -3087,7 +3088,7 @@ setLocation (void * vdata)
 
               tr_logAddDebug ("Found file #%d: %s", (int)i, oldpath);
 
-              if (do_move && !tr_is_same_file (oldpath, newpath))
+              if (do_move && !tr_sys_path_is_same (oldpath, newpath, NULL))
                 {
                   bool renamed = false;
                   errno = 0;
@@ -3199,9 +3200,13 @@ tr_torrentFileCompleted (tr_torrent * tor, tr_file_index_t fileIndex)
         {
           char * oldpath = tr_buildPath (base, sub, NULL);
           char * newpath = tr_buildPath (base, f->name, NULL);
+          tr_error * error = NULL;
 
-          if (tr_rename (oldpath, newpath))
-            tr_logAddTorErr (tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, tr_strerror (errno));
+          if (!tr_sys_path_rename (oldpath, newpath, &error))
+            {
+              tr_logAddTorErr (tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, error->message);
+              tr_error_free (error);
+            }
 
           tr_free (newpath);
           tr_free (oldpath);
@@ -3283,6 +3288,7 @@ tr_torrentFindFile2 (const tr_torrent * tor, tr_file_index_t fileNum,
   const tr_file * file;
   const char * b = NULL;
   const char * s = NULL;
+  tr_sys_path_info file_info;
 
   assert (tr_isTorrent (tor));
   assert (fileNum < tor->info.fileCount);
@@ -3293,7 +3299,7 @@ tr_torrentFindFile2 (const tr_torrent * tor, tr_file_index_t fileNum,
   if (b == NULL)
     {
       char * filename = tr_buildPath (tor->downloadDir, file->name, NULL);
-      if (tr_fileExists (filename, mtime))
+      if (tr_sys_path_get_info (filename, 0, &file_info, NULL))
         {
           b = tor->downloadDir;
           s = file->name;
@@ -3305,7 +3311,7 @@ tr_torrentFindFile2 (const tr_torrent * tor, tr_file_index_t fileNum,
   if ((b == NULL) && (tor->incompleteDir != NULL))
     {
       char * filename = tr_buildPath (tor->incompleteDir, file->name, NULL);
-      if (tr_fileExists (filename, mtime))
+      if (tr_sys_path_get_info (filename, 0, &file_info, NULL))
         {
           b = tor->incompleteDir;
           s = file->name;
@@ -3320,7 +3326,7 @@ tr_torrentFindFile2 (const tr_torrent * tor, tr_file_index_t fileNum,
   if ((b == NULL) && (tor->incompleteDir != NULL))
     {
       char * filename = tr_buildPath (tor->incompleteDir, part, NULL);
-      if (tr_fileExists (filename, mtime))
+      if (tr_sys_path_get_info (filename, 0, &file_info, NULL))
         {
           b = tor->incompleteDir;
           s = part;
@@ -3332,7 +3338,7 @@ tr_torrentFindFile2 (const tr_torrent * tor, tr_file_index_t fileNum,
   if (b == NULL)
     {
       char * filename = tr_buildPath (tor->downloadDir, part, NULL);
-      if (tr_fileExists (filename, mtime))
+      if (tr_sys_path_get_info (filename, 0, &file_info, NULL))
         {
           b = tor->downloadDir;
           s = part;
@@ -3345,6 +3351,9 @@ tr_torrentFindFile2 (const tr_torrent * tor, tr_file_index_t fileNum,
     *base = b;
   if (subpath != NULL)
     *subpath = tr_strdup (s);
+  if (mtime != NULL)
+    *mtime = file_info.last_modified_at;
+
 
   /* cleanup */
   tr_free (part);
@@ -3608,7 +3617,7 @@ renamePath (tr_torrent  * tor,
 {
   char * src;
   const char * base;
-  int error = 0;
+  int err = 0;
 
   if (!tr_torrentIsSeed(tor) && (tor->incompleteDir != NULL))
     base = tor->incompleteDir;
@@ -3616,18 +3625,18 @@ renamePath (tr_torrent  * tor,
     base = tor->downloadDir;
 
   src = tr_buildPath (base, oldpath, NULL);
-  if (!tr_fileExists (src, NULL)) /* check for it as a partial */
+  if (!tr_sys_path_exists (src, NULL)) /* check for it as a partial */
     {
       char * tmp = tr_strdup_printf ("%s.part", src);
       tr_free (src);
       src = tmp;
     }
 
-  if (tr_fileExists (src, NULL))
+  if (tr_sys_path_exists (src, NULL))
     {
       int tmp;
       bool tgt_exists;
-      char * parent = tr_dirname (src);
+      char * parent = tr_sys_path_dirname (src, NULL);
       char * tgt;
 
       if (tr_str_has_suffix (src, ".part"))
@@ -3636,17 +3645,19 @@ renamePath (tr_torrent  * tor,
         tgt = tr_buildPath (parent, newname, NULL);
 
       tmp = errno;
-      tgt_exists = tr_fileExists (tgt, NULL);
+      tgt_exists = tr_sys_path_exists (tgt, NULL);
       errno = tmp;
 
       if (!tgt_exists)
         {
-          int rv;
+          tr_error * error = NULL;
 
           tmp = errno;
-          rv = tr_rename (src, tgt);
-          if (rv != 0)
-            error = errno;
+          if (!tr_sys_path_rename (src, tgt, &error))
+            {
+              err = error->code;
+              tr_error_free (error);
+            }
           errno = tmp;
         }
 
@@ -3656,7 +3667,7 @@ renamePath (tr_torrent  * tor,
 
   tr_free (src);
 
-  return error;
+  return err;
 }
 
 static void
@@ -3678,7 +3689,7 @@ renameTorrentFileString (tr_torrent       * tor,
     }
   else
     {
-      char * tmp = tr_dirname (oldpath);
+      char * tmp = tr_sys_path_dirname (oldpath, NULL);
 
       if (oldpath_len >= strlen(file->name))
         name = tr_buildPath (tmp, newname, NULL);
diff --git a/libtransmission/utils.c b/libtransmission/utils.c
index 521bb0e..baf4dd1 100644
--- a/libtransmission/utils.c
+++ b/libtransmission/utils.c
@@ -32,11 +32,10 @@
 #ifdef HAVE_ICONV_OPEN
  #include <iconv.h>
 #endif
-#include <libgen.h> /* basename () */
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <unistd.h> /* stat (), getcwd (), getpagesize () */
+#include <unistd.h> /* stat (), getpagesize () */
 
 #include <event2/buffer.h>
 #include <event2/event.h>
@@ -44,17 +43,19 @@
 #ifdef _WIN32
  #include <w32api.h>
  #define WINVER WindowsXP /* freeaddrinfo (), getaddrinfo (), getnameinfo () */
- #include <direct.h> /* _getcwd () */
  #include <windows.h> /* Sleep (), GetSystemTimeAsFileTime () */
 #endif
 
 #include "transmission.h"
+#include "error.h"
 #include "fdlimit.h"
+#include "file.h"
 #include "ConvertUTF.h"
 #include "list.h"
 #include "log.h"
+#include "net.h"
 #include "utils.h"
-#include "platform.h" /* tr_lockLock (), TR_PATH_MAX */
+#include "platform.h" /* tr_lockLock () */
 #include "platform-quota.h" /* tr_device_info_create(), tr_device_info_get_free_space(), tr_device_info_free() */
 #include "variant.h"
 #include "version.h"
@@ -219,28 +220,33 @@ tr_loadFile (const char * path,
              size_t     * size)
 {
   uint8_t * buf;
-  struct stat  sb;
+  tr_sys_path_info info;
   int fd;
   ssize_t n;
+  tr_error * error = NULL;
   const char * const err_fmt = _("Couldn't read \"%1$s\": %2$s");
 
   /* try to stat the file */
-  errno = 0;
-  if (stat (path, &sb))
+  if (!tr_sys_path_get_info (path, 0, &info, &error))
     {
-      const int err = errno;
-      tr_logAddDebug (err_fmt, path, tr_strerror (errno));
+      const int err = error->code;
+      tr_logAddDebug (err_fmt, path, error->message);
+      tr_error_free (error);
       errno = err;
       return NULL;
     }
 
-  if ((sb.st_mode & S_IFMT) != S_IFREG)
+  if (info.type != TR_SYS_PATH_IS_FILE)
     {
       tr_logAddError (err_fmt, path, _("Not a regular file"));
       errno = EISDIR;
       return NULL;
     }
 
+  /* file size should be able to fit into size_t */
+  if (sizeof(info.size) > sizeof(*size))
+    assert (info.size <= SIZE_MAX);
+
   /* Load the torrent file into our buffer */
   fd = tr_open_file_for_scanning (path);
   if (fd < 0)
@@ -250,7 +256,7 @@ tr_loadFile (const char * path,
       errno = err;
       return NULL;
     }
-  buf = tr_malloc (sb.st_size + 1);
+  buf = tr_malloc (info.size + 1);
   if (!buf)
     {
       const int err = errno;
@@ -259,7 +265,7 @@ tr_loadFile (const char * path,
       errno = err;
       return NULL;
     }
-  n = read (fd, buf, (size_t)sb.st_size);
+  n = read (fd, buf, (size_t)info.size);
   if (n == -1)
     {
       const int err = errno;
@@ -271,80 +277,12 @@ tr_loadFile (const char * path,
     }
 
   tr_close_file (fd);
-  buf[ sb.st_size ] = '\0';
-  *size = sb.st_size;
+  buf[info.size] = '\0';
+  *size = info.size;
   return buf;
 }
 
 char*
-tr_basename (const char * path)
-{
-#ifdef _MSC_VER
-
-  char fname[_MAX_FNAME], ext[_MAX_EXT];
-  if (_splitpath_s (path, NULL, 0, NULL, 0, fname, sizeof (fname), ext, sizeof (ext)) == 0)
-    {
-      const size_t tmpLen = strlen(fname) + strlen(ext) + 2;
-      char * const tmp = tr_malloc (tmpLen);
-      if (tmp != NULL)
-        {
-          if (_makepath_s (tmp, tmpLen, NULL, NULL, fname, ext) == 0)
-            return tmp;
-
-          tr_free (tmp);
-        }
-    }
-
-  return tr_strdup (".");
-
-#else
-
-  char * tmp = tr_strdup (path);
-  char * ret = tr_strdup (basename (tmp));
-  tr_free (tmp);
-  return ret;
-
-#endif
-}
-
-char*
-tr_dirname (const char * path)
-{
-#ifdef _MSC_VER
-
-  char drive[_MAX_DRIVE], dir[_MAX_DIR];
-  if (_splitpath_s (path, drive, sizeof (drive), dir, sizeof (dir), NULL, 0, NULL, 0) == 0)
-    {
-      const size_t tmpLen = strlen(drive) + strlen(dir) + 2;
-      char * const tmp = tr_malloc (tmpLen);
-      if (tmp != NULL)
-        {
-          if (_makepath_s (tmp, tmpLen, drive, dir, NULL, NULL) == 0)
-            {
-              size_t len = strlen(tmp);
-              while (len > 0 && (tmp[len - 1] == '/' || tmp[len - 1] == '\\'))
-                tmp[--len] = '\0';
-
-              return tmp;
-            }
-
-          tr_free (tmp);
-        }
-    }
-
-  return tr_strdup (".");
-
-#else
-
-  char * tmp = tr_strdup (path);
-  char * ret = tr_strdup (dirname (tmp));
-  tr_free (tmp);
-  return ret;
-
-#endif
-}
-
-char*
 tr_mkdtemp (char * template)
 {
 #ifdef HAVE_MKDTEMP
@@ -494,24 +432,6 @@ tr_buildPath (const char *first_element, ...)
   return buf;
 }
 
-#ifdef __APPLE__
- #define TR_STAT_MTIME(sb)((sb).st_mtimespec.tv_sec)
-#else
- #define TR_STAT_MTIME(sb)((sb).st_mtime)
-#endif
-
-bool
-tr_fileExists (const char * filename, time_t * mtime)
-{
-  struct stat sb;
-  const bool ok = !stat (filename, &sb);
-
-  if (ok && (mtime != NULL))
-    *mtime = TR_STAT_MTIME (sb);
-
-  return ok;
-}
-
 int64_t
 tr_getDirFreeSpace (const char * dir)
 {
@@ -1627,27 +1547,29 @@ tr_moveFile (const char * oldpath, const char * newpath, bool * renamed)
   int in;
   int out;
   char * buf;
-  struct stat st;
+  tr_sys_path_info info;
   off_t bytesLeft;
   const size_t buflen = 1024 * 128; /* 128 KiB buffer */
+  tr_error * error = NULL;
 
   /* make sure the old file exists */
-  if (stat (oldpath, &st))
+  if (!tr_sys_path_get_info (oldpath, 0, &info, &error))
     {
-      const int err = errno;
+      const int err = error->code;
+      tr_error_free (error);
       errno = err;
       return -1;
     }
-  if (!S_ISREG (st.st_mode))
+  if (info.type != TR_SYS_PATH_IS_FILE)
     {
       errno = ENOENT;
       return -1;
     }
-  bytesLeft = st.st_size;
+  bytesLeft = info.size;
 
   /* make sure the target directory exists */
   {
-    char * newdir = tr_dirname (newpath);
+    char * newdir = tr_sys_path_dirname (newpath, NULL);
     int i = tr_mkdirp (newdir, 0777);
     tr_free (newdir);
     if (i)
@@ -1656,10 +1578,10 @@ tr_moveFile (const char * oldpath, const char * newpath, bool * renamed)
 
   /* they might be on the same filesystem... */
   {
-    const int i = tr_rename (oldpath, newpath);
+    const bool i = tr_sys_path_rename (oldpath, newpath, NULL);
     if (renamed != NULL)
-      *renamed = i == 0;
-    if (!i)
+      *renamed = i;
+    if (i)
       return 0;
   }
 
@@ -1687,64 +1609,10 @@ tr_moveFile (const char * oldpath, const char * newpath, bool * renamed)
   if (bytesLeft != 0)
     return -1;
 
-  tr_remove (oldpath);
+  tr_sys_path_remove (oldpath, NULL);
   return 0;
 }
 
-int
-tr_rename (const char * oldpath, const char * newpath)
-{
-  /* FIXME: needs win32 utf-16 support */
-
-  return rename (oldpath, newpath);
-}
-
-int
-tr_remove (const char * pathname)
-{
-  /* FIXME: needs win32 utf-16 support */
-
-  return remove (pathname);
-}
-
-bool
-tr_is_same_file (const char * filename1, const char * filename2)
-{
-#ifdef _WIN32
-
-  bool res;
-  HANDLE fh1, fh2;
-  BY_HANDLE_FILE_INFORMATION fi1, fi2;
-  int n = strlen (filename1) + 1;
-  int m = strlen (filename2) + 1;
-  wchar_t f1nameUTF16[n];
-  wchar_t f2nameUTF16[m];
- 
-  MultiByteToWideChar (CP_UTF8, 0, filename1, -1, f1nameUTF16, n);
-  MultiByteToWideChar (CP_UTF8, 0, filename2, -1, f2nameUTF16, m);
-  fh1 = CreateFileW (chkFilename (f1nameUTF16), 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-  fh2 = CreateFileW (chkFilename (f2nameUTF16), 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-  res = GetFileInformationByHandle (fh1, &fi1)
-          && GetFileInformationByHandle (fh2, &fi2)
-          && (fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber)
-          && (fi1.nFileIndexHigh == fi2.nFileIndexHigh)
-          && (fi1.nFileIndexLow  == fi2.nFileIndexLow);
-  CloseHandle (fh1);
-  CloseHandle (fh2);
-  return res;
-
-#else
-
-  struct stat sb1, sb2;
-
-  return !stat (filename1, &sb1)
-      && !stat (filename2, &sb2)
-      && (sb1.st_dev == sb2.st_dev)
-      && (sb1.st_ino == sb2.st_ino);
-
-#endif
-}
-
 /***
 ****
 ***/
@@ -1784,19 +1652,6 @@ tr_valloc (size_t bufLen)
   return buf;
 }
 
-char *
-tr_realpath (const char * path, char * resolved_path)
-{
-#ifdef _WIN32
-  /* From a message to the Mingw-msys list, Jun 2, 2005 by Mark Junker. */
-  if (GetFullPathNameA (path, TR_PATH_MAX, resolved_path, NULL) == 0)
-    return NULL;
-  return resolved_path;
-#else
-  return realpath (path, resolved_path);
-#endif
-}
-
 /***
 ****
 ***/
diff --git a/libtransmission/utils.h b/libtransmission/utils.h
index 423818e..41825b3 100644
--- a/libtransmission/utils.h
+++ b/libtransmission/utils.h
@@ -106,11 +106,6 @@ const char * tr_strip_positional_args (const char * fmt);
  */
 bool tr_wildmat (const char * text, const char * pattern) TR_GNUC_NONNULL (1,2);
 
-/** @brief Portability wrapper for basename () that uses the system implementation if available */
-char* tr_basename (const char * path) TR_GNUC_MALLOC;
-
-/** @brief Portability wrapper for dirname () that uses the system implementation if available */
-char* tr_dirname (const char * path) TR_GNUC_MALLOC;
 
 /**
  * Like mkdir, but makes parent directories as needed.
@@ -137,8 +132,6 @@ uint8_t* tr_loadFile (const char * filename, size_t * size) TR_GNUC_MALLOC
 char* tr_buildPath (const char * first_element, ...) TR_GNUC_NULL_TERMINATED
                                                       TR_GNUC_MALLOC;
 
-bool tr_fileExists (const char * filename, time_t * mtime);
-
 /**
  * @brief Get available disk space (in bytes) for the specified folder.
  * @return zero or positive integer on success, -1 in case of error.
@@ -414,15 +407,6 @@ int tr_gettimeofday (struct timeval * tv);
 int tr_moveFile (const char * oldpath, const char * newpath,
                  bool * renamed) TR_GNUC_NONNULL (1,2);
 
-/** @brief Portability wrapper for rename () */
-int tr_rename (const char * oldpath_utf8, const char * newpath_utf8);
-
-/** @brief Portability wrapper for remove () */
-int tr_remove (const char * pathname_utf8);
-
-/** @brief Test to see if the two filenames point to the same file. */
-bool tr_is_same_file (const char * filename1, const char * filename2);
-
 /** @brief convenience function to remove an item from an array */
 void tr_removeElementFromArray (void         * array,
                                 unsigned int   index_to_remove,
@@ -451,22 +435,6 @@ static inline time_t tr_time (void) { return __tr_current_time; }
 /** @brief Private libtransmission function to update tr_time ()'s counter */
 static inline void tr_timeUpdate (time_t now) { __tr_current_time = now; }
 
-#ifdef _WIN32
- #include <windef.h> /* MAX_PATH */
- #define TR_PATH_MAX (MAX_PATH + 1)
-#else
- #include <limits.h> /* PATH_MAX */
- #ifdef PATH_MAX
-  #define TR_PATH_MAX PATH_MAX
- #else
-  #define TR_PATH_MAX 4096
- #endif
-#endif
-
-/** @brief Portability wrapper for realpath () that uses the system implementation if available.
-    @param resolved_path should be TR_PATH_MAX or larger */
-char* tr_realpath (const char *path, char * resolved_path);
-
 /** @brief Portability wrapper for htonll () that uses the system implementation if available */
 uint64_t tr_htonll (uint64_t);
 
diff --git a/libtransmission/variant.c b/libtransmission/variant.c
index 104bd93..126132d 100644
--- a/libtransmission/variant.c
+++ b/libtransmission/variant.c
@@ -9,7 +9,6 @@
 
 #include <assert.h>
 #include <errno.h>
-#include <stdio.h> /* rename() */
 #include <stdlib.h> /* strtod(), realloc(), qsort(), mkstemp() */
 #include <string.h>
 
@@ -28,8 +27,9 @@
 #include "transmission.h"
 #include "ConvertUTF.h"  
 #include "fdlimit.h" /* tr_close_file() */
+#include "error.h"
+#include "file.h"
 #include "log.h"
-#include "platform.h" /* TR_PATH_MAX */
 #include "utils.h" /* tr_new(), tr_free() */
 #include "variant.h"
 #include "variant-common.h"
@@ -1160,12 +1160,12 @@ tr_variantToFile (const tr_variant  * v,
   char * tmp;
   int fd;
   int err = 0;
-  char buf[TR_PATH_MAX];
+  char * real_filename;
 
   /* follow symlinks to find the "real" file, to make sure the temporary
    * we build with tr_mkstemp() is created on the right partition */
-  if (tr_realpath (filename, buf) != NULL)
-    filename = buf;
+  if ((real_filename = tr_sys_path_resolve (filename, NULL)) != NULL)
+    filename = real_filename;
 
   /* if the file already exists, try to move it out of the way & keep it as a backup */
   tmp = tr_strdup_printf ("%s.tmp.XXXXXX", filename);
@@ -1203,21 +1203,24 @@ tr_variantToFile (const tr_variant  * v,
         {
           tr_logAddError (_("Couldn't save temporary file \"%1$s\": %2$s"), tmp, tr_strerror (err));
           tr_close_file (fd);
-          tr_remove (tmp);
+          tr_sys_path_remove (tmp, NULL);
         }
       else
         {
+          tr_error * error = NULL;
+
           tr_close_file (fd);
 
-          if (!tr_rename (tmp, filename))
+          if (tr_sys_path_rename (tmp, filename, &error))
             {
               tr_logAddInfo (_("Saved \"%s\""), filename);
             }
           else
             {
-              err = errno;
-              tr_logAddError (_("Couldn't save file \"%1$s\": %2$s"), filename, tr_strerror (err));
-              tr_remove (tmp);
+              err = error->code;
+              tr_logAddError (_("Couldn't save file \"%1$s\": %2$s"), filename, error->message);
+              tr_sys_path_remove (tmp, NULL);
+              tr_error_free (error);
             }
         }
     }
@@ -1228,6 +1231,7 @@ tr_variantToFile (const tr_variant  * v,
     }
 
   tr_free (tmp);
+  tr_free (real_filename);
   return err;
 }
 
diff --git a/libtransmission/web.c b/libtransmission/web.c
index acd30aa..0675e69 100644
--- a/libtransmission/web.c
+++ b/libtransmission/web.c
@@ -22,6 +22,7 @@
 #include <event2/buffer.h>
 
 #include "transmission.h"
+#include "file.h"
 #include "list.h"
 #include "log.h"
 #include "net.h" /* tr_address */
@@ -398,7 +399,7 @@ tr_webThreadFunc (void * vsession)
     }
 
   str = tr_buildPath (session->configDir, "cookies.txt", NULL);
-  if (tr_fileExists (str, NULL))
+  if (tr_sys_path_exists (str, NULL))
     web->cookie_filename = tr_strdup (str);
   tr_free (str);
 
diff --git a/utils/create.c b/utils/create.c
index 0a0a3a8..1fcc01f 100644
--- a/utils/create.c
+++ b/utils/create.c
@@ -13,6 +13,7 @@
 #include <unistd.h> /* getcwd() */
 
 #include <libtransmission/transmission.h>
+#include <libtransmission/file.h>
 #include <libtransmission/makemeta.h>
 #include <libtransmission/tr-getopt.h>
 #include <libtransmission/utils.h>
@@ -158,7 +159,7 @@ main (int argc, char * argv[])
 
   if (outfile == NULL)
     {
-      char * base = tr_basename (infile);
+      char * base = tr_sys_path_basename (infile, NULL);
       char * end = tr_strdup_printf ("%s.torrent", base);
       char * cwd = tr_getcwd ();
       outfile = out2 = tr_buildPath (cwd, end, NULL);
