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