diff --git a/glib/gfileutils.c b/glib/gfileutils.c
index 76335fa..0d5a5b2 100644
--- a/glib/gfileutils.c
+++ b/glib/gfileutils.c
@@ -1874,6 +1874,174 @@ g_file_read_link (const gchar  *filename,
 #endif
 }
 
+/**
+ * g_canonicalize_filename:
+ * @filename: the symbolic link
+ * @max: the maximum length of the resulting filename, or -1
+ * @resolve_links: whether to resolve symbolic links
+ *
+ * Resolves the specified relative or absolute @filename to a full pathname up
+ * to @max or the maximum filename length supported by the system. If
+ * @resolve_links is %TRUE, symbolic links are resolved.
+ *
+ * Unlike realpath(), this function doesn't fail if the path is inaccessible or
+ * doesn't exist.
+ *
+ * Returns: A newly-allocated full pathname, or %NULL in case of an error
+ *
+ * Since: 2.24
+ */
+gchar*
+g_canonicalize_filename (const char* name,
+                         gboolean    resolve_links)
+{
+  char *rpath, *dest, *extra_buf = NULL;
+  const char *start, *end, *rpath_limit;
+  glong path_max;
+#ifdef MAXSYMLINKS
+  int num_links = 0;
+#endif
+
+  if (!(name && *name))
+      return NULL;
+
+  /* FIXME: Should the buffer grow instead of using an arbitrary limit? */
+#ifdef _PC_PATH_MAX
+  path_max = pathconf (name, _PC_PATH_MAX);
+  if (path_max == -1)
+#endif
+    path_max = 4096;
+
+  rpath = g_malloc (path_max);
+  rpath_limit = rpath + path_max;
+
+  /* TODO: Handle X:\ or SERVER\\ on Windows */
+
+  if (name[0] != G_DIR_SEPARATOR)
+    {
+      if (!getcwd (rpath, path_max))
+        {
+          /* FIXME: Handle inaccessible realtive filename */
+          rpath[0] = '\0';
+          goto error;
+        }
+      dest = strchr (rpath, '\0');
+    }
+  else
+    {
+      rpath[0] = G_DIR_SEPARATOR;
+      dest = rpath + 1;
+    }
+
+  for (start = end = name; *start; start = end)
+    {
+      struct stat st;
+      int n;
+
+      /* Skip sequence of multiple path-separators.  */
+      while (*start == G_DIR_SEPARATOR)
+        ++start;
+
+      /* Find end of path component.  */
+      for (end = start; *end && *end != G_DIR_SEPARATOR; ++end)
+        /* Nothing.  */;
+
+      if (end - start == 0)
+        break;
+      else if (end - start == 1 && start[0] == '.')
+        /* nothing */;
+      else if (end - start == 2 && start[0] == '.' && start[1] == '.')
+        {
+          /* Back up to previous component, ignore if at root already.  */
+          if (dest > rpath + 1)
+            while ((--dest)[-1] != G_DIR_SEPARATOR);
+        }
+      else
+        {
+          size_t new_size;
+
+          if (dest[-1] != G_DIR_SEPARATOR)
+            *dest++ = G_DIR_SEPARATOR;
+
+          if (dest + (end - start) >= rpath_limit)
+            {
+              goffset dest_offset = dest - rpath;
+              char *new_rpath;
+
+              new_size = rpath_limit - rpath;
+              if (end - start + 1 > path_max)
+                new_size += end - start + 1;
+              else
+                new_size += path_max;
+              new_rpath = (char *) realloc (rpath, new_size);
+              if (new_rpath == NULL)
+                goto error;
+              rpath = new_rpath;
+              rpath_limit = rpath + new_size;
+
+              dest = rpath + dest_offset;
+            }
+
+          memcpy (dest, start, end - start);
+          dest = dest + (end - start);
+          *dest = '\0';
+
+#if 0
+          if (stat (rpath, &st) < 0)
+            goto error;
+#endif
+
+          if (S_ISLNK (st.st_mode))
+            {
+              char *buf = alloca (path_max);
+              gsize len;
+
+#ifdef MAXSYMLINKS
+              if (++num_links > MAXSYMLINKS)
+                {
+                  errno = ELOOP;
+                  goto error;
+                }
+#endif
+
+              n = readlink (rpath, buf, path_max);
+              if (n < 0)
+                goto error;
+              buf[n] = '\0';
+
+              if (!extra_buf)
+                extra_buf = g_alloca (path_max);
+
+              len = strlen (end);
+              if ((long int) (n + len) >= path_max)
+                {
+                  goto error;
+                }
+
+              /* Careful here, end may be a pointer into extra_buf... */
+              g_memmove (&extra_buf[n], end, len + 1);
+              name = end = memcpy (extra_buf, buf, n);
+
+              if (buf[0] == G_DIR_SEPARATOR)
+                dest = rpath + 1; /* It's an absolute symlink */
+              else
+                /* Back up to previous component, ignore if at root already: */
+                if (dest > rpath + 1)
+                  while ((--dest)[-1] != G_DIR_SEPARATOR);
+            }
+        }
+    }
+  if (dest > rpath + 1 && dest[-1] == G_DIR_SEPARATOR)
+    --dest;
+  *dest = '\0';
+
+  return rpath;
+
+error:
+  g_free (rpath);
+  return NULL;
+}
+
 /* NOTE : Keep this part last to ensure nothing in this file uses the
  * below binary compatibility versions.
  */
diff --git a/glib/gfileutils.h b/glib/gfileutils.h
index d8f9d3b..7b32100 100644
--- a/glib/gfileutils.h
+++ b/glib/gfileutils.h
@@ -96,6 +96,9 @@ gboolean g_file_set_contents (const gchar *filename,
 			      GError	   **error);
 gchar   *g_file_read_link    (const gchar  *filename,
 			      GError      **error);
+gchar*
+g_canonicalize_filename      (const char*   filename,
+                              gboolean      resolve_links);
 
 /* Wrapper / workalike for mkstemp() */
 gint    g_mkstemp            (gchar        *tmpl);
diff --git a/glib/glib.symbols b/glib/glib.symbols
index ee9da31..4c88ab8 100644
--- a/glib/glib.symbols
+++ b/glib/glib.symbols
@@ -374,6 +374,7 @@ g_file_open_tmp PRIVATE
 g_file_test PRIVATE
 #endif
 g_file_read_link
+g_canonicalize_filename
 g_format_size_for_display
 #ifndef _WIN64
 g_mkstemp PRIVATE
diff --git a/glib/tests/fileutils.c b/glib/tests/fileutils.c
index 225c6ce..7bee6f9 100644
--- a/glib/tests/fileutils.c
+++ b/glib/tests/fileutils.c
@@ -491,6 +491,13 @@ test_format_size_for_display (void)
   check_string (g_format_size_for_display (1024 * 1024 * 1024), "1.0 GB");
 }
 
+static void
+test_canonicalize_filename (void)
+{
+  g_assert_cmpstr ("/home", ==, g_canonicalize_filename ("/home", FALSE));
+  g_assert_cmpstr ("/foobar", ==, g_canonicalize_filename ("/foobar", FALSE));
+}
+
 int
 main (int   argc,
       char *argv[])
@@ -503,6 +510,7 @@ main (int   argc,
   g_test_add_func ("/fileutils/build-filenamev", test_build_filenamev);
   g_test_add_func ("/fileutils/mkdir-with-parents", test_mkdir_with_parents);
   g_test_add_func ("/fileutils/format-size-for-display", test_format_size_for_display);
+  g_test_add_func ("/fileutils/canonicalize_filename", test_canonicalize_filename);
 
   return g_test_run();
 }
