Merge branch '4561_tar_segfault'
[midnight-commander.git] / lib / vfs / gc.c
blobc57f643845aea2576c8d336ffd819341871d52b1
1 /*
2 Virtual File System garbage collection code
4 Copyright (C) 2003-2024
5 Free Software Foundation, Inc.
7 Written by:
8 Miguel de Icaza, 1995
9 Jakub Jelinek, 1995
10 Pavel Machek, 1998
11 Pavel Roskin, 2003
13 This file is part of the Midnight Commander.
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.
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.
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/>.
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
40 #include <config.h>
42 #include <stdlib.h>
44 #include "lib/global.h"
45 #include "lib/event.h"
46 #include "lib/util.h" /* MC_PTR_FREE */
48 #include "vfs.h"
49 #include "utilvfs.h"
51 #include "gc.h"
54 * The garbage collection mechanism is based on "stamps".
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."
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:
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.)
67 * (2) When a file in your filesystem is opened, delete the stamp. You do
68 * this with vfs_rmstamp().
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.)
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()).
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.)
86 /*** global variables ****************************************************************************/
88 int vfs_timeout = 60; /* VFS timeout in seconds */
90 /*** file scope macro definitions ****************************************************************/
92 #define VFS_STAMPING(a) ((struct vfs_stamping *)(a))
94 /*** file scope type declarations ****************************************************************/
96 struct vfs_stamping
98 struct vfs_class *v;
99 vfsid id;
100 gint64 time;
103 /*** forward declarations (file scope functions) *************************************************/
105 /*** file scope variables ************************************************************************/
107 static GSList *stamps = NULL;
109 /* --------------------------------------------------------------------------------------------- */
110 /*** file scope functions ************************************************************************/
111 /* --------------------------------------------------------------------------------------------- */
113 static gint
114 vfs_stamp_compare (gconstpointer a, gconstpointer b)
116 const struct vfs_stamping *vsa = (const struct vfs_stamping *) a;
117 const struct vfs_stamping *vsb = (const struct vfs_stamping *) b;
119 return (vsa == NULL || vsb == NULL || (vsa->v == vsb->v && vsa->id == vsb->id)) ? 0 : 1;
122 /* --------------------------------------------------------------------------------------------- */
124 static void
125 vfs_addstamp (struct vfs_class *v, vfsid id)
127 if ((v->flags & VFSF_LOCAL) == 0 && id != NULL && !vfs_stamp (v, id))
129 struct vfs_stamping *stamp;
131 stamp = g_new (struct vfs_stamping, 1);
132 stamp->v = v;
133 stamp->id = id;
134 stamp->time = g_get_monotonic_time ();
136 stamps = g_slist_append (stamps, stamp);
140 /* --------------------------------------------------------------------------------------------- */
141 /*** public functions ****************************************************************************/
142 /* --------------------------------------------------------------------------------------------- */
144 gboolean
145 vfs_stamp (struct vfs_class *v, vfsid id)
147 struct vfs_stamping what = {
148 .v = v,
149 .id = id
151 GSList *stamp;
152 gboolean ret = FALSE;
154 stamp = g_slist_find_custom (stamps, &what, vfs_stamp_compare);
155 if (stamp != NULL && stamp->data != NULL)
157 VFS_STAMPING (stamp->data)->time = g_get_monotonic_time ();
158 ret = TRUE;
161 return ret;
164 /* --------------------------------------------------------------------------------------------- */
166 void
167 vfs_rmstamp (struct vfs_class *v, vfsid id)
169 struct vfs_stamping what = {
170 .v = v,
171 .id = id
173 GSList *stamp;
175 stamp = g_slist_find_custom (stamps, &what, vfs_stamp_compare);
176 if (stamp != NULL)
178 g_free (stamp->data);
179 stamps = g_slist_delete_link (stamps, stamp);
183 /* --------------------------------------------------------------------------------------------- */
185 void
186 vfs_stamp_path (const vfs_path_t *vpath)
188 vfsid id;
189 struct vfs_class *me;
191 me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
192 id = vfs_getid (vpath);
193 vfs_addstamp (me, id);
196 /* --------------------------------------------------------------------------------------------- */
198 * Create a new timestamp item by VFS class and VFS id.
201 void
202 vfs_stamp_create (struct vfs_class *vclass, vfsid id)
204 vfsid nvfsid;
206 ev_vfs_stamp_create_t event_data = { vclass, id, FALSE };
207 const vfs_path_t *vpath;
208 struct vfs_class *me;
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 */
215 if (!mc_event_present (MCEVENT_GROUP_CORE, "vfs_timestamp"))
216 return;
218 vpath = vfs_get_raw_current_dir ();
219 me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
221 nvfsid = vfs_getid (vpath);
222 vfs_rmstamp (me, nvfsid);
224 if (!(id == NULL || (me == vclass && nvfsid == id)))
226 mc_event_raise (MCEVENT_GROUP_CORE, "vfs_timestamp", (gpointer) & event_data);
228 if (!event_data.ret && vclass != NULL && vclass->nothingisopen != NULL
229 && vclass->nothingisopen (id))
230 vfs_addstamp (vclass, id);
234 /* --------------------------------------------------------------------------------------------- */
235 /** This is called from timeout handler with now = FALSE,
236 or can be called with now = TRUE to force freeing all filesystems */
238 void
239 vfs_expire (gboolean now)
241 static gboolean locked = FALSE;
242 gint64 curr_time, exp_time;
243 GSList *stamp;
245 /* Avoid recursive invocation, e.g. when one of the free functions
246 calls message */
247 if (locked)
248 return;
249 locked = TRUE;
251 curr_time = g_get_monotonic_time ();
252 exp_time = curr_time - vfs_timeout * G_USEC_PER_SEC;
254 if (now)
256 /* reverse list to free nested VFSes at first */
257 stamps = g_slist_reverse (stamps);
260 /* NULLize stamps that point to expired VFS */
261 for (stamp = stamps; stamp != NULL; stamp = g_slist_next (stamp))
263 struct vfs_stamping *stamping = VFS_STAMPING (stamp->data);
265 if (now)
267 /* free VFS forced */
268 if (stamping->v->free != NULL)
269 stamping->v->free (stamping->id);
270 MC_PTR_FREE (stamp->data);
272 else if (stamping->time <= exp_time)
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
279 if (stamping->v->free != NULL)
280 stamping->v->free (stamping->id);
281 MC_PTR_FREE (stamp->data);
286 /* then remove NULLized stamps */
287 stamps = g_slist_remove_all (stamps, NULL);
289 locked = FALSE;
292 /* --------------------------------------------------------------------------------------------- */
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.
300 vfs_timeouts (void)
302 return stamps != NULL ? 10 : 0;
305 /* --------------------------------------------------------------------------------------------- */
307 void
308 vfs_timeout_handler (void)
310 vfs_expire (FALSE);
313 /* --------------------------------------------------------------------------------------------- */
315 void
316 vfs_release_path (const vfs_path_t *vpath)
318 vfsid id;
319 struct vfs_class *me;
321 me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
322 id = vfs_getid (vpath);
323 vfs_stamp_create (me, id);
326 /* --------------------------------------------------------------------------------------------- */
327 /* Free all data */
329 void
330 vfs_gc_done (void)
332 vfs_expire (TRUE);
335 /* --------------------------------------------------------------------------------------------- */