From 118791a83342b6a2d7ce518b0ad402b43b0eb2e0 Mon Sep 17 00:00:00 2001
From: Ludovico de Nittis <ludovico.denittis@collabora.com>
Date: Thu, 20 May 2021 11:57:43 +0200
Subject: [PATCH] utils: When possible replace symlinks target with the
 corresponding env var

When we list the content of a directory, if a symlink points to a
location that is under a known environment variable, we replace its
target value with the environment variable.

This has the advantage to make it clearer where a symlink is pointing
to.

Signed-off-by: Ludovico de Nittis <ludovico.denittis@collabora.com>
---
 steam-runtime-tools/system-info.c    |  3 ++
 steam-runtime-tools/utils-internal.h |  1 +
 steam-runtime-tools/utils.c          | 52 ++++++++++++++++++++++++++--
 3 files changed, 53 insertions(+), 3 deletions(-)

diff --git a/steam-runtime-tools/system-info.c b/steam-runtime-tools/system-info.c
index 31f675a76..3074de023 100644
--- a/steam-runtime-tools/system-info.c
+++ b/steam-runtime-tools/system-info.c
@@ -1253,6 +1253,7 @@ ensure_overrides_cached (SrtSystemInfo *self)
               self->overrides.values = _srt_recursive_list_content (self->sysroot,
                                                                     self->sysroot_fd,
                                                                     paths[i],
+                                                                    self->env,
                                                                     &self->overrides.messages);
               break;
             }
@@ -1317,11 +1318,13 @@ ensure_pinned_libs_cached (SrtSystemInfo *self)
       self->pinned_libs.values_32 = _srt_recursive_list_content (runtime,
                                                                  -1,
                                                                  "pinned_libs_32",
+                                                                 self->env,
                                                                  &self->pinned_libs.messages_32);
 
       self->pinned_libs.values_64 = _srt_recursive_list_content (runtime,
                                                                  -1,
                                                                  "pinned_libs_64",
+                                                                 self->env,
                                                                  &self->pinned_libs.messages_64);
     }
 }
diff --git a/steam-runtime-tools/utils-internal.h b/steam-runtime-tools/utils-internal.h
index 21b98a79a..916033486 100644
--- a/steam-runtime-tools/utils-internal.h
+++ b/steam-runtime-tools/utils-internal.h
@@ -117,6 +117,7 @@ G_GNUC_INTERNAL gboolean _srt_steam_command_via_pipe (const char * const *argume
 G_GNUC_INTERNAL gchar **_srt_recursive_list_content (const gchar *sysroot,
                                                      int sysroot_fd,
                                                      const gchar *directory,
+                                                     gchar **envp,
                                                      gchar ***messages_out);
 
 G_GNUC_INTERNAL const char *_srt_get_path_after (const char *str,
diff --git a/steam-runtime-tools/utils.c b/steam-runtime-tools/utils.c
index e6baf9a32..7e2d0b837 100644
--- a/steam-runtime-tools/utils.c
+++ b/steam-runtime-tools/utils.c
@@ -1130,6 +1130,12 @@ _srt_steam_command_via_pipe (const char * const *arguments,
   return TRUE;
 }
 
+typedef struct
+{
+  const char *from;
+  const char *to;
+} CommonReplacements;
+
 /*
  * _srt_list_directory_content:
  * @working_dir_fd: File descriptor to the current working directory
@@ -1137,6 +1143,8 @@ _srt_steam_command_via_pipe (const char * const *arguments,
  *  the sysroot
  * @sub_directory: (nullable) (type filename): If %NULL, the @working_dir_path
  *  itself will be opened
+ * @common_replacements: (nullable): If not %NULL, perform these replacements
+ *  to the beginning of the targets paths for symlinks that have been found
  * @level: Current level of recursion
  * @result: (not nullable): The elements that @directory contains are appended
  *  to this array
@@ -1147,6 +1155,7 @@ static void
 _srt_list_directory_content (int working_dir_fd,
                              const gchar *working_dir_path,
                              const gchar *sub_directory,
+                             const CommonReplacements *common_replacements,
                              int level,
                              GPtrArray *result,
                              GPtrArray *messages)
@@ -1154,6 +1163,7 @@ _srt_list_directory_content (int working_dir_fd,
   g_autofree gchar *full_working_path = NULL;
   g_auto(GLnxDirFdIterator) iter = { FALSE };
   g_autoptr(GError) error = NULL;
+  gsize i;
 
   g_return_if_fail (working_dir_path != NULL);
   g_return_if_fail (result != NULL);
@@ -1227,14 +1237,31 @@ _srt_list_directory_content (int working_dir_fd,
               target = g_strdup ("(unknown)");
             }
 
+          for (i = 0; common_replacements != NULL && common_replacements[i].to != NULL; i++)
+            {
+              if (common_replacements[i].from == NULL)
+                continue;
+
+              const gchar *after = _srt_get_path_after (target, common_replacements[i].from);
+
+              if (after != NULL)
+                {
+                  g_autofree gchar *new_target = NULL;
+                  new_target = g_build_filename (common_replacements[i].to, after, NULL);
+                  g_clear_pointer (&target, g_free);
+                  target = g_steal_pointer (&new_target);
+                  break;
+                }
+            }
+
           g_ptr_array_add (result, g_strdup_printf ("%s -> %s", full_name, target));
         }
       else if (dent->d_type == DT_DIR)
         {
           g_ptr_array_add (result, g_strdup_printf ("%s/", full_name));
 
-          _srt_list_directory_content (iter.fd, full_working_path, dent->d_name, level + 1,
-                                       result, messages);
+          _srt_list_directory_content (iter.fd, full_working_path, dent->d_name,
+                                       common_replacements, level + 1, result, messages);
         }
       else
         {
@@ -1251,6 +1278,8 @@ _srt_list_directory_content (int working_dir_fd,
  *  reopen it
  * @directory: (not nullable) (type filename): A path below the root directory,
  *  either absolute or relative (to the root)
+ * @envp: (array zero-terminated=1) (not nullable): Behave as though `environ`
+ *  was this array
  * @messages_out: (optional) (out) (array zero-terminated=1) (transfer full):
  *  If not %NULL, used to return a %NULL-terminated array of diagnostic
  *  messages. Free with g_strfreev().
@@ -1263,6 +1292,7 @@ gchar **
 _srt_recursive_list_content (const gchar *sysroot,
                              int sysroot_fd,
                              const gchar *directory,
+                             gchar **envp,
                              gchar ***messages_out)
 {
   g_autoptr(GPtrArray) content = NULL;
@@ -1270,11 +1300,26 @@ _srt_recursive_list_content (const gchar *sysroot,
   glnx_autofd int local_sysroot_fd = -1;
   glnx_autofd int top_fd = -1;
   g_autoptr(GError) error = NULL;
+  const gchar *steam_runtime = NULL;
 
   g_return_val_if_fail (sysroot != NULL, NULL);
   g_return_val_if_fail (directory != NULL, NULL);
+  g_return_val_if_fail (envp != NULL, NULL);
   g_return_val_if_fail (messages_out == NULL || *messages_out == NULL, NULL);
 
+  steam_runtime = g_environ_getenv (envp, "STEAM_RUNTIME");
+  /* If STEAM_RUNTIME is just the root directory we don't want to replace
+   * every leading '/' with $STEAM_RUNTIME */
+  if (g_strcmp0 (steam_runtime, "/") == 0)
+    steam_runtime = NULL;
+
+  const CommonReplacements common_replacements[] =
+  {
+    { steam_runtime, "$STEAM_RUNTIME" },
+    { g_environ_getenv (envp, "HOME"), "$HOME" },
+    { NULL, NULL },
+  };
+
   content = g_ptr_array_new_with_free_func (g_free);
   messages = g_ptr_array_new_with_free_func (g_free);
 
@@ -1310,7 +1355,8 @@ _srt_recursive_list_content (const gchar *sysroot,
       goto out;
     }
 
-  _srt_list_directory_content (top_fd, directory, NULL, 0, content, messages);
+  _srt_list_directory_content (top_fd, directory, NULL, common_replacements, 0,
+                               content, messages);
 
   g_ptr_array_sort (content, _srt_indirect_strcmp0);
 
-- 
GitLab