diff --git a/pressure-vessel/runtime.c b/pressure-vessel/runtime.c index a932497d37dbb4fd619bdfd21008c11961d35443..c6b630347a7a5c7a82cfb775d1d09fea6a3dd3c5 100644 --- a/pressure-vessel/runtime.c +++ b/pressure-vessel/runtime.c @@ -604,6 +604,137 @@ pv_runtime_maybe_garbage_collect_subdir (const char *description, } } +static gboolean +is_old_runtime_deployment (const char *name) +{ + if (g_str_has_prefix (name, "scout_before_")) + return TRUE; + + if (g_str_has_prefix (name, "soldier_before_")) + return TRUE; + + if (g_str_has_prefix (name, "scout_0.")) + return TRUE; + + if (g_str_has_prefix (name, "soldier_0.")) + return TRUE; + + if (g_str_has_prefix (name, ".scout_") + && g_str_has_suffix (name, "_unpack-temp")) + return TRUE; + + if (g_str_has_prefix (name, ".soldier_") + && g_str_has_suffix (name, "_unpack-temp")) + return TRUE; + + return FALSE; +} + +gboolean +pv_runtime_garbage_collect_legacy (const char *variable_dir, + const char *runtime_base, + GError **error) +{ + g_autoptr(GError) local_error = NULL; + g_autoptr(PvBwrapLock) variable_lock = NULL; + g_autoptr(PvBwrapLock) base_lock = NULL; + g_auto(GLnxDirFdIterator) variable_dir_iter = { FALSE }; + g_auto(GLnxDirFdIterator) runtime_base_iter = { FALSE }; + glnx_autofd int variable_dir_fd = -1; + glnx_autofd int runtime_base_fd = -1; + struct + { + const char *path; + GLnxDirFdIterator *iter; + } iters[] = { + { variable_dir, &variable_dir_iter }, + { runtime_base, &runtime_base_iter }, + }; + gsize i; + + g_return_val_if_fail (variable_dir != NULL, FALSE); + g_return_val_if_fail (runtime_base != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!glnx_opendirat (AT_FDCWD, variable_dir, TRUE, + &variable_dir_fd, error)) + return FALSE; + + if (!glnx_opendirat (AT_FDCWD, runtime_base, TRUE, + &runtime_base_fd, error)) + return FALSE; + + variable_lock = pv_bwrap_lock_new (variable_dir_fd, ".ref", + (PV_BWRAP_LOCK_FLAGS_CREATE + | PV_BWRAP_LOCK_FLAGS_WRITE), + &local_error); + + /* If we can't take the lock immediately, just don't do GC */ + if (variable_lock == NULL) + return TRUE; + + /* We take out locks on both the variable directory and the base + * directory, because historically in the shell scripts we only + * locked the base directory, and we later moved to locking only the + * variable directory. Now that we're in C code it seems safest to + * lock both. */ + base_lock = pv_bwrap_lock_new (runtime_base_fd, ".ref", + (PV_BWRAP_LOCK_FLAGS_CREATE + | PV_BWRAP_LOCK_FLAGS_WRITE), + &local_error); + + /* Same here */ + if (base_lock == NULL) + return TRUE; + + for (i = 0; i < G_N_ELEMENTS (iters); i++) + { + if (!glnx_dirfd_iterator_init_at (AT_FDCWD, iters[i].path, + TRUE, iters[i].iter, error)) + return FALSE; + + while (TRUE) + { + struct dirent *dent; + + if (!glnx_dirfd_iterator_next_dent_ensure_dtype (iters[i].iter, + &dent, NULL, error)) + return FALSE; + + if (dent == NULL) + break; + + switch (dent->d_type) + { + case DT_DIR: + break; + + case DT_BLK: + case DT_CHR: + case DT_FIFO: + case DT_LNK: + case DT_REG: + case DT_SOCK: + case DT_UNKNOWN: + default: + g_debug ("Ignoring %s/%s: not a directory", + iters[i].path, dent->d_name); + continue; + } + + if (!is_old_runtime_deployment (dent->d_name)) + continue; + + pv_runtime_maybe_garbage_collect_subdir ("legacy runtime", + iters[i].path, + iters[i].iter->fd, + dent->d_name); + } + } + + return TRUE; +} + static gboolean pv_runtime_garbage_collect (PvRuntime *self, PvBwrapLock *variable_dir_lock, diff --git a/pressure-vessel/runtime.h b/pressure-vessel/runtime.h index 45ab34a027f00f12c19591e25be95942ea32207b..3669a67cbd906041cb62e5c97882896bab97b2b6 100644 --- a/pressure-vessel/runtime.h +++ b/pressure-vessel/runtime.h @@ -97,4 +97,8 @@ gboolean pv_runtime_bind (PvRuntime *self, GError **error); void pv_runtime_cleanup (PvRuntime *self); +gboolean pv_runtime_garbage_collect_legacy (const char *variable_dir, + const char *runtime_base, + GError **error); + G_DEFINE_AUTOPTR_CLEANUP_FUNC (PvRuntime, g_object_unref) diff --git a/pressure-vessel/wrap.c b/pressure-vessel/wrap.c index bb23db603d24b6c67f9e9200aae95a85f152a39d..fda9ca640634e416338b2916a514fe93bdf9c879 100644 --- a/pressure-vessel/wrap.c +++ b/pressure-vessel/wrap.c @@ -712,6 +712,7 @@ static char *opt_fake_home = NULL; static char **opt_filesystems = NULL; static char *opt_freedesktop_app_id = NULL; static char *opt_steam_app_id = NULL; +static gboolean opt_gc_legacy_runtimes = FALSE; static gboolean opt_gc_runtimes = TRUE; static gboolean opt_generate_locales = TRUE; static char *opt_home = NULL; @@ -1032,6 +1033,15 @@ static GOptionEntry options[] = "Make --unshare-home use ~/.var/app/com.steampowered.AppN " "as home directory. [Default: $STEAM_COMPAT_APP_ID or $SteamAppId]", "N" }, + { "gc-legacy-runtimes", '\0', + G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_gc_legacy_runtimes, + "Garbage-collect old unpacked runtimes in $PRESSURE_VESSEL_RUNTIME_BASE.", + NULL }, + { "no-gc-legacy-runtimes", '\0', + G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &opt_gc_legacy_runtimes, + "Don't garbage-collect old unpacked runtimes in " + "$PRESSURE_VESSEL_RUNTIME_BASE [default].", + NULL }, { "gc-runtimes", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_gc_runtimes, "If using --variable-dir, garbage-collect old temporary " @@ -1339,6 +1349,7 @@ main (int argc, TRUE); opt_share_home = tristate_environment ("PRESSURE_VESSEL_SHARE_HOME"); + opt_gc_legacy_runtimes = pv_boolean_environment ("PRESSURE_VESSEL_GC_LEGACY_RUNTIMES", FALSE); opt_gc_runtimes = pv_boolean_environment ("PRESSURE_VESSEL_GC_RUNTIMES", TRUE); opt_generate_locales = pv_boolean_environment ("PRESSURE_VESSEL_GENERATE_LOCALES", TRUE); @@ -1747,6 +1758,21 @@ main (int argc, NULL); } + if (opt_gc_legacy_runtimes + && opt_runtime_base != NULL + && opt_runtime_base[0] != '\0' + && opt_variable_dir != NULL) + { + if (!pv_runtime_garbage_collect_legacy (opt_variable_dir, + opt_runtime_base, + &local_error)) + { + g_warning ("Unable to clean up old runtimes: %s", + local_error->message); + g_clear_error (&local_error); + } + } + if (opt_runtime != NULL || opt_runtime_archive != NULL) { PvRuntimeFlags flags = PV_RUNTIME_FLAGS_NONE;