gio - resolve path with symlinks

67 views Asked by At

I have a gfile which points to a path containing symlinks.

E.g. there are:

/home/test/link --> /home/original
/home/original/myfile.txt

The gfile points to /home/test/link/myfile.txt. Does gio provide some method to resolve all links in this path, so that I can obtain /home/original/myfile.txt ?

Or do I need to implement that myself ?

2

There are 2 answers

0
Alex On

Still not sure if gio possibly provides some shortcut ... meanwhile I implemented it myself using gio:

/**
 * g_file_get_resolved_path:
 * @file : #GFile for which the path will be resolved
 *
 * Gets the local pathname with resolved symlinks for GFile, if one exists.
 * If non-NULL, this is guaranteed to be an absolute, canonical path.
 *
 * This only will work if all components of the #GFile path actually do exist
 *
 * Return value: (nullable) (transfer full): A #gchar on success and %NULL on failure.
 * The returned string should be freed with g_free() when no longer needed.
 **/
char* g_file_get_resolved_path (GFile *file)
{
  gchar     *path;
  gchar    **tokenized_path;
  GFileInfo *info;
  gchar     *real_path = NULL;
  gchar     *folder_name;
  gchar     *temp_path;
  GFile     *link_test;
  gchar     *link_target = NULL;

  _thunar_return_val_if_fail (G_IS_FILE (file), NULL);

  path = g_file_get_path (file);

  /* No local path for file found */
  if (path == NULL)
    return NULL;

  tokenized_path = g_strsplit (path ,"/", -1);
  for (gsize i = 0; tokenized_path != NULL && tokenized_path[i] != NULL; ++i)
  {
    folder_name = tokenized_path[i];
    temp_path = g_strjoin ("/", real_path, folder_name, NULL);
    link_test = g_file_new_for_path (temp_path);
    g_free (temp_path);

    info = g_file_query_info (link_test, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL);
    if (info != NULL)
    {
      link_target = g_file_info_get_attribute_as_string (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
      g_object_unref (info);
      if (link_target != NULL)
      {
        temp_path = real_path;
        if (link_target[0] == '/')
        {
          /* It's an absolute link */
          real_path = g_strdup (link_target);
        }
        else
        {
          /* It's a relative link */
          real_path = g_strjoin ("/", real_path, link_target, NULL);
        }
        g_free (temp_path);
      }
    }
    g_object_unref (link_test);
    
    /* no symlink found ? --> just append the folder name */
    if (link_target == NULL)
    {
      temp_path = real_path;
      real_path = g_strjoin ("/", real_path, folder_name, NULL);
      g_free (temp_path);
    }
    link_target = NULL;
  }
  g_strfreev (tokenized_path);

  g_free (path);
  return real_path;
}

Tested for some absolute and relative links.

0
Alex On

Here a version which uses realpath, which might not be available on all systems:

g_file_get_resolved_path (GFile *file)
{
  gchar *path;
  gchar *real_path;

  _thunar_return_val_if_fail (G_IS_FILE (file), NULL);

  path = g_file_get_path (file);

  /* No local path for file found */
  if (path == NULL)
    return NULL;

  real_path = realpath (path, NULL);

  if (real_path == NULL)
    g_warning ("Failed to resolve path: '%s' Error: %s\n", path, strerror (errno));

  g_free (path);
  return real_path;
}