+/* Directory list safe to hold auto-loaded files. It is not checked for
+ absolute paths but they are strongly recommended. It is initialized by
+ _initialize_auto_load. */
+static char *auto_load_safe_path;
+
+/* Vector of directory elements of AUTO_LOAD_SAFE_PATH with each one normalized
+ by tilde_expand and possibly each entries has added its gdb_realpath
+ counterpart. */
+static VEC (char_ptr) *auto_load_safe_path_vec;
+
+/* Update auto_load_safe_path_vec from current AUTO_LOAD_SAFE_PATH. */
+
+static void
+auto_load_safe_path_vec_update (void)
+{
+ VEC (char_ptr) *dir_vec = NULL;
+ unsigned len;
+ int ix;
+
+ if (debug_auto_load)
+ fprintf_unfiltered (gdb_stdlog,
+ _("auto-load: Updating directories of \"%s\".\n"),
+ auto_load_safe_path);
+
+ free_char_ptr_vec (auto_load_safe_path_vec);
+
+ auto_load_safe_path_vec = dirnames_to_char_ptr_vec (auto_load_safe_path);
+ len = VEC_length (char_ptr, auto_load_safe_path_vec);
+
+ /* Apply tilde_expand and gdb_realpath to each AUTO_LOAD_SAFE_PATH_VEC
+ element. */
+ for (ix = 0; ix < len; ix++)
+ {
+ char *dir = VEC_index (char_ptr, auto_load_safe_path_vec, ix);
+ char *ddir_subst, *expanded, *real_path;
+
+ ddir_subst = xstrdup (dir);
+ substitute_path_component (&ddir_subst, "$ddir", gdb_datadir);
+ expanded = tilde_expand (ddir_subst);
+ xfree (ddir_subst);
+ real_path = gdb_realpath (expanded);
+
+ /* Ensure the current entry is at least a valid path (therefore
+ $ddir-expanded and tilde-expanded). */
+ VEC_replace (char_ptr, auto_load_safe_path_vec, ix, expanded);
+
+ if (debug_auto_load)
+ {
+ if (strcmp (expanded, dir) == 0)
+ fprintf_unfiltered (gdb_stdlog,
+ _("auto-load: Using directory \"%s\".\n"),
+ expanded);
+ else
+ fprintf_unfiltered (gdb_stdlog,
+ _("auto-load: Resolved directory \"%s\" "
+ "as \"%s\".\n"),
+ dir, expanded);
+ }
+ xfree (dir);
+
+ /* If gdb_realpath returns a different content, append it. */
+ if (strcmp (real_path, expanded) == 0)
+ xfree (real_path);
+ else
+ {
+ VEC_safe_push (char_ptr, auto_load_safe_path_vec, real_path);
+
+ if (debug_auto_load)
+ fprintf_unfiltered (gdb_stdlog,
+ _("auto-load: And canonicalized as \"%s\".\n"),
+ real_path);
+ }
+ }
+}
+
+/* Variable gdb_datadir has been set. Update content depending on $ddir. */
+
+static void
+auto_load_gdb_datadir_changed (void)
+{
+ auto_load_safe_path_vec_update ();
+}
+
+/* "set" command for the auto_load_safe_path configuration variable. */
+
+static void
+set_auto_load_safe_path (char *args, int from_tty, struct cmd_list_element *c)
+{
+ /* Setting the variable to "" resets it to the compile time defaults. */
+ if (auto_load_safe_path[0] == '\0')
+ {
+ xfree (auto_load_safe_path);
+ auto_load_safe_path = xstrdup (AUTO_LOAD_SAFE_PATH);
+ }
+
+ auto_load_safe_path_vec_update ();
+}
+
+/* "show" command for the auto_load_safe_path configuration variable. */
+
+static void
+show_auto_load_safe_path (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ if (strcmp (value, "/") == 0)
+ fprintf_filtered (file, _("Auto-load files are safe to load from any "
+ "directory.\n"));
+ else
+ fprintf_filtered (file, _("List of directories from which it is safe to "
+ "auto-load files is %s.\n"),
+ value);
+}
+
+/* "add-auto-load-safe-path" command for the auto_load_safe_path configuration
+ variable. */
+
+static void
+add_auto_load_safe_path (char *args, int from_tty)
+{
+ char *s;
+
+ if (args == NULL || *args == 0)
+ error (_("\
+Directory argument required.\n\
+Use 'set auto-load safe-path /' for disabling the auto-load safe-path security.\
+"));
+
+ s = xstrprintf ("%s%c%s", auto_load_safe_path, DIRNAME_SEPARATOR, args);
+ xfree (auto_load_safe_path);
+ auto_load_safe_path = s;
+
+ auto_load_safe_path_vec_update ();
+}
+
+/* Return 1 if FILENAME is equal to DIR or if FILENAME belongs to the
+ subdirectory DIR. Return 0 otherwise. gdb_realpath normalization is never
+ done here. */
+
+static ATTRIBUTE_PURE int
+filename_is_in_dir (const char *filename, const char *dir)
+{
+ size_t dir_len = strlen (dir);
+
+ while (dir_len && IS_DIR_SEPARATOR (dir[dir_len - 1]))
+ dir_len--;
+
+ /* Ensure auto_load_safe_path "/" matches any FILENAME. On MS-Windows
+ platform FILENAME even after gdb_realpath does not have to start with
+ IS_DIR_SEPARATOR character, such as the 'C:\x.exe' filename. */
+ if (dir_len == 0)
+ return 1;
+
+ return (filename_ncmp (dir, filename, dir_len) == 0
+ && (IS_DIR_SEPARATOR (filename[dir_len])
+ || filename[dir_len] == '\0'));
+}
+
+/* Return 1 if FILENAME belongs to one of directory components of
+ AUTO_LOAD_SAFE_PATH_VEC. Return 0 otherwise.
+ auto_load_safe_path_vec_update is never called.
+ *FILENAME_REALP may be updated by gdb_realpath of FILENAME - it has to be
+ freed by the caller. */
+
+static int
+filename_is_in_auto_load_safe_path_vec (const char *filename,
+ char **filename_realp)
+{
+ char *dir;
+ int ix;
+
+ for (ix = 0; VEC_iterate (char_ptr, auto_load_safe_path_vec, ix, dir); ++ix)
+ if (*filename_realp == NULL && filename_is_in_dir (filename, dir))
+ break;
+
+ if (dir == NULL)
+ {
+ if (*filename_realp == NULL)
+ {
+ *filename_realp = gdb_realpath (filename);
+ if (debug_auto_load && strcmp (*filename_realp, filename) != 0)
+ fprintf_unfiltered (gdb_stdlog,
+ _("auto-load: Resolved "
+ "file \"%s\" as \"%s\".\n"),
+ filename, *filename_realp);
+ }
+
+ if (strcmp (*filename_realp, filename) != 0)
+ for (ix = 0; VEC_iterate (char_ptr, auto_load_safe_path_vec, ix, dir);
+ ++ix)
+ if (filename_is_in_dir (*filename_realp, dir))
+ break;
+ }
+
+ if (dir != NULL)
+ {
+ if (debug_auto_load)
+ fprintf_unfiltered (gdb_stdlog, _("auto-load: File \"%s\" matches "
+ "directory \"%s\".\n"),
+ filename, dir);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return 1 if FILENAME is located in one of the directories of
+ AUTO_LOAD_SAFE_PATH. Otherwise call warning and return 0. FILENAME does
+ not have to be an absolute path.
+
+ Existence of FILENAME is not checked. Function will still give a warning
+ even if the caller would quietly skip non-existing file in unsafe
+ directory. */
+
+int
+file_is_auto_load_safe (const char *filename, const char *debug_fmt, ...)
+{
+ char *filename_real = NULL;
+ struct cleanup *back_to;
+
+ if (debug_auto_load)
+ {
+ va_list debug_args;
+
+ va_start (debug_args, debug_fmt);
+ vfprintf_unfiltered (gdb_stdlog, debug_fmt, debug_args);
+ va_end (debug_args);
+ }
+
+ back_to = make_cleanup (free_current_contents, &filename_real);
+
+ if (filename_is_in_auto_load_safe_path_vec (filename, &filename_real))
+ {
+ do_cleanups (back_to);
+ return 1;
+ }
+
+ auto_load_safe_path_vec_update ();
+ if (filename_is_in_auto_load_safe_path_vec (filename, &filename_real))
+ {
+ do_cleanups (back_to);
+ return 1;
+ }
+
+ warning (_("File \"%s\" auto-loading has been declined by your "
+ "`auto-load safe-path' set to \"%s\"."),
+ filename_real, auto_load_safe_path);
+
+ do_cleanups (back_to);
+ return 0;
+}
+