1 /* 2 Virtual File System garbage collection code 3 4 Copyright (C) 2003-2025 5 Free Software Foundation, Inc. 6 7 Written by: 8 Miguel de Icaza, 1995 9 Jakub Jelinek, 1995 10 Pavel Machek, 1998 11 Pavel Roskin, 2003 12 13 This file is part of the Midnight Commander. 14 15 The Midnight Commander is free software: you can redistribute it 16 and/or modify it under the terms of the GNU General Public License as 17 published by the Free Software Foundation, either version 3 of the License, 18 or (at your option) any later version. 19 20 The Midnight Commander is distributed in the hope that it will be useful, 21 but WITHOUT ANY WARRANTY; without even the implied warranty of 22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 GNU General Public License for more details. 24 25 You should have received a copy of the GNU General Public License 26 along with this program. If not, see <https://www.gnu.org/licenses/>. 27 */ 28 29 /** 30 * \file 31 * \brief Source: Virtual File System: garbage collection code 32 * \author Miguel de Icaza 33 * \author Jakub Jelinek 34 * \author Pavel Machek 35 * \author Pavel Roskin 36 * \date 1995, 1998, 2003 37 */ 38 39 #include <config.h> 40 41 #include <stdlib.h> 42 43 #include "lib/global.h" 44 #include "lib/event.h" 45 #include "lib/util.h" // MC_PTR_FREE 46 47 #include "vfs.h" 48 #include "utilvfs.h" 49 50 #include "gc.h" 51 52 /* 53 * The garbage collection mechanism is based on "stamps". 54 * 55 * A stamp is a record that says "I'm a filesystem which is no longer in 56 * use. Free me when you get a chance." 57 * 58 * This file contains a set of functions used for managing this stamp. You 59 * should use them when you write your own filesystem. Here are some rules 60 * of thumb: 61 * 62 * (1) When the last open file in your filesystem gets closed, conditionally 63 * create a stamp. You do this with vfs_stamp_create(). (The meaning 64 * of "conditionally" is explained below.) 65 * 66 * (2) When a file in your filesystem is opened, delete the stamp. You do 67 * this with vfs_rmstamp(). 68 * 69 * (3) When a path inside your filesystem is invoked, call vfs_stamp() to 70 * postpone the free'ing of your filesystem a bit. (This simply updates 71 * a timestamp variable inside the stamp.) 72 * 73 * Additionally, when a user navigates to a new directory in a panel (or a 74 * programmer uses mc_chdir()), a stamp is conditionally created for the 75 * previous directory's filesystem. This ensures that that filesystem is 76 * free'ed. (see: _do_panel_cd() -> vfs_release_path(); mc_chdir()). 77 * 78 * We've spoken here of "conditionally creating" a stamp. What we mean is 79 * that vfs_stamp_create() is to be used: this function creates a stamp 80 * only if no directories are open (aka "active") in your filesystem. (If 81 * there _are_ directories open, it means that the filesystem is in use, in 82 * which case we don't want to free it.) 83 */ 84 85 /*** global variables ****************************************************************************/ 86 87 int vfs_timeout = 60; // VFS timeout in seconds 88 89 /*** file scope macro definitions ****************************************************************/ 90 91 #define VFS_STAMPING(a) ((struct vfs_stamping *) (a)) 92 93 /*** file scope type declarations ****************************************************************/ 94 95 struct vfs_stamping 96 { 97 struct vfs_class *v; 98 vfsid id; 99 gint64 time; 100 }; 101 102 /*** forward declarations (file scope functions) *************************************************/ 103 104 /*** file scope variables ************************************************************************/ 105 106 static GSList *stamps = NULL; 107 108 /* --------------------------------------------------------------------------------------------- */ 109 /*** file scope functions ************************************************************************/ 110 /* --------------------------------------------------------------------------------------------- */ 111 112 static gint 113 vfs_stamp_compare (gconstpointer a, gconstpointer b) /**/ 114 { 115 const struct vfs_stamping *vsa = (const struct vfs_stamping *) a; 116 const struct vfs_stamping *vsb = (const struct vfs_stamping *) b; 117 118 return (vsa == NULL || vsb == NULL || (vsa->v == vsb->v && vsa->id == vsb->id)) ? 0 : 1; 119 } 120 121 /* --------------------------------------------------------------------------------------------- */ 122 123 static void 124 vfs_addstamp (struct vfs_class *v, vfsid id) /*
*/ 125 { 126 if ((v->flags & VFSF_LOCAL) == 0 && id != NULL && !vfs_stamp (v, id)) 127 { 128 struct vfs_stamping *stamp; 129 130 stamp = g_new (struct vfs_stamping, 1); 131 stamp->v = v; 132 stamp->id = id; 133 stamp->time = g_get_monotonic_time (); 134 135 stamps = g_slist_append (stamps, stamp); 136 } 137 } 138 139 /* --------------------------------------------------------------------------------------------- */ 140 /*** public functions ****************************************************************************/ 141 /* --------------------------------------------------------------------------------------------- */ 142 143 gboolean 144 vfs_stamp (struct vfs_class *v, vfsid id) /*
*/ 145 { 146 struct vfs_stamping what = { 147 .v = v, 148 .id = id, 149 }; 150 GSList *stamp; 151 gboolean ret = FALSE; 152 153 stamp = g_slist_find_custom (stamps, &what, vfs_stamp_compare); 154 if (stamp != NULL && stamp->data != NULL) 155 { 156 VFS_STAMPING (stamp->data)->time = g_get_monotonic_time (); 157 ret = TRUE; 158 } 159 160 return ret; 161 } 162 163 /* --------------------------------------------------------------------------------------------- */ 164 165 void 166 vfs_rmstamp (struct vfs_class *v, vfsid id) /*
*/ 167 { 168 struct vfs_stamping what = { 169 .v = v, 170 .id = id, 171 }; 172 GSList *stamp; 173 174 stamp = g_slist_find_custom (stamps, &what, vfs_stamp_compare); 175 if (stamp != NULL) 176 { 177 g_free (stamp->data); 178 stamps = g_slist_delete_link (stamps, stamp); 179 } 180 } 181 182 /* --------------------------------------------------------------------------------------------- */ 183 184 void 185 vfs_stamp_path (const vfs_path_t *vpath) /*
*/ 186 { 187 vfsid id; 188 struct vfs_class *me; 189 190 me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath)); 191 id = vfs_getid (vpath); 192 vfs_addstamp (me, id); 193 } 194 195 /* --------------------------------------------------------------------------------------------- */ 196 /** 197 * Create a new timestamp item by VFS class and VFS id. 198 */ 199 200 void 201 vfs_stamp_create (struct vfs_class *vclass, vfsid id) /*
*/ 202 { 203 vfsid nvfsid; 204 205 ev_vfs_stamp_create_t event_data = { vclass, id, FALSE }; 206 const vfs_path_t *vpath; 207 struct vfs_class *me; 208 209 /* There are three directories we have to take care of: current_dir, 210 current_panel->cwd and other_panel->cwd. Although most of the time either 211 current_dir and current_panel->cwd or current_dir and other_panel->cwd are the 212 same, it's possible that all three are different -- Norbert */ 213 214 if (!mc_event_present (MCEVENT_GROUP_CORE, "vfs_timestamp")) 215 return; 216 217 vpath = vfs_get_raw_current_dir (); 218 me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath)); 219 220 nvfsid = vfs_getid (vpath); 221 vfs_rmstamp (me, nvfsid); 222 223 if (!(id == NULL || (me == vclass && nvfsid == id))) 224 { 225 mc_event_raise (MCEVENT_GROUP_CORE, "vfs_timestamp", (gpointer) &event_data); 226 227 if (!event_data.ret && vclass != NULL && vclass->nothingisopen != NULL 228 && vclass->nothingisopen (id)) 229 vfs_addstamp (vclass, id); 230 } 231 } 232 233 /* --------------------------------------------------------------------------------------------- */ 234 /** This is called from timeout handler with now = FALSE, 235 or can be called with now = TRUE to force freeing all filesystems */ 236 237 void 238 vfs_expire (gboolean now) /*
*/ 239 { 240 static gboolean locked = FALSE; 241 gint64 curr_time, exp_time; 242 GSList *stamp; 243 244 /* Avoid recursive invocation, e.g. when one of the free functions 245 calls message */ 246 if (locked) 247 return; 248 locked = TRUE; 249 250 curr_time = g_get_monotonic_time (); 251 exp_time = curr_time - vfs_timeout * G_USEC_PER_SEC; 252 253 if (now) 254 { 255 // reverse list to free nested VFSes at first 256 stamps = g_slist_reverse (stamps); 257 } 258 259 // NULLize stamps that point to expired VFS 260 for (stamp = stamps; stamp != NULL; stamp = g_slist_next (stamp)) 261 { 262 struct vfs_stamping *stamping = VFS_STAMPING (stamp->data); 263 264 if (now) 265 { 266 // free VFS forced 267 if (stamping->v->free != NULL) 268 stamping->v->free (stamping->id); 269 MC_PTR_FREE (stamp->data); 270 } 271 else if (stamping->time <= exp_time) 272 { 273 // update timestamp of VFS that is in use, or free unused VFS 274 if (stamping->v->nothingisopen != NULL && !stamping->v->nothingisopen (stamping->id)) 275 stamping->time = curr_time; 276 else 277 { 278 if (stamping->v->free != NULL) 279 stamping->v->free (stamping->id); 280 MC_PTR_FREE (stamp->data); 281 } 282 } 283 } 284 285 // then remove NULLized stamps 286 stamps = g_slist_remove_all (stamps, NULL); 287 288 locked = FALSE; 289 } 290 291 /* --------------------------------------------------------------------------------------------- */ 292 /* 293 * Return the number of seconds remaining to the vfs timeout. 294 * FIXME: The code should be improved to actually return the number of 295 * seconds until the next item times out. 296 */ 297 298 int 299 vfs_timeouts (void) /*
*/ 300 { 301 return stamps != NULL ? 10 : 0; 302 } 303 304 /* --------------------------------------------------------------------------------------------- */ 305 306 void 307 vfs_timeout_handler (void) /*
*/ 308 { 309 vfs_expire (FALSE); 310 } 311 312 /* --------------------------------------------------------------------------------------------- */ 313 314 void 315 vfs_release_path (const vfs_path_t *vpath) /*
*/ 316 { 317 vfsid id; 318 struct vfs_class *me; 319 320 me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath)); 321 id = vfs_getid (vpath); 322 vfs_stamp_create (me, id); 323 } 324 325 /* --------------------------------------------------------------------------------------------- */ 326 /* Free all data */ 327 328 void 329 vfs_gc_done (void) /*
*/ 330 { 331 vfs_expire (TRUE); 332 } 333 334 /* --------------------------------------------------------------------------------------------- */