31
31
(G_TYPE_CHECK_INSTANCE_CAST \
32
32
((obj), E_TYPE_CACHE_REAPER, ECacheReaper))
34
/* Where abandoned cache directories go to die. */
34
/* Where abandoned directories go to die. */
35
35
#define TRASH_DIRECTORY_NAME "trash"
37
37
/* XXX These intervals are rather arbitrary and prone to bikeshedding.
38
38
* It's just what I decided on. On startup we wait an hour to reap
39
* abandoned cache directories, and thereafter repeat every 24 hours. */
39
* abandoned directories, and thereafter repeat every 24 hours. */
40
40
#define INITIAL_INTERVAL_SECONDS ( 1 * (60 * 60))
41
41
#define REGULAR_INTERVAL_SECONDS (24 * (60 * 60))
43
/* XXX Similarly, these expiry times are rather arbitrary and prone to
44
* bikeshedding. Most importantly, the expiry for data directories
45
* should be far more conservative (longer) than cache directories.
46
* Cache directories are disposable, data directories are not, so
47
* we want to let abandoned data directories linger longer. */
49
/* Minimum days for a data directory
50
* to live in trash before reaping it. */
51
#define DATA_EXPIRY_IN_DAYS 28
53
/* Minimum days for a cache directory
54
* to live in trash before reaping it. */
55
#define CACHE_EXPIRY_IN_DAYS 7
43
57
typedef struct _ECacheReaper ECacheReaper;
44
58
typedef struct _ECacheReaperClass ECacheReaperClass;
46
60
struct _ECacheReaper {
63
guint n_data_directories;
64
GFile **data_directories;
65
GFile **data_trash_directories;
67
guint n_cache_directories;
50
68
GFile **cache_directories;
51
GFile **trash_directories;
69
GFile **cache_trash_directories;
53
71
guint reaping_timeout_id;
79
97
return E_SOURCE_REGISTRY_SERVER (extensible);
101
cache_reaper_make_directory_and_parents (GFile *directory,
102
GCancellable *cancellable,
106
GError *local_error = NULL;
108
/* XXX Maybe add some function like this to libedataserver.
109
* It's annoying to always have to check for and clear
110
* G_IO_ERROR_EXISTS when ensuring a directory exists. */
112
success = g_file_make_directory_with_parents (
113
directory, cancellable, &local_error);
115
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
116
g_clear_error (&local_error);
118
if (local_error != NULL) {
121
g_propagate_error (error, local_error);
123
path = g_file_get_path (directory);
125
error, "Failed to make directory '%s': ", path);
83
133
cache_reaper_trash_directory_reaped (GObject *source_object,
84
134
GAsyncResult *result,
112
162
ECacheReaper *extension = E_CACHE_REAPER (user_data);
165
g_message ("Reaping abandoned data directories");
167
for (ii = 0; ii < extension->n_data_directories; ii++)
168
e_reap_trash_directory (
169
extension->data_trash_directories[ii],
171
G_PRIORITY_LOW, NULL,
172
cache_reaper_trash_directory_reaped,
115
175
g_message ("Reaping abandoned cache directories");
117
for (ii = 0; ii < extension->n_directories; ii++)
177
for (ii = 0; ii < extension->n_cache_directories; ii++)
118
178
e_reap_trash_directory (
119
extension->trash_directories[ii],
179
extension->cache_trash_directories[ii],
180
CACHE_EXPIRY_IN_DAYS,
120
181
G_PRIORITY_LOW, NULL,
121
182
cache_reaper_trash_directory_reaped,
181
cache_reaper_scan_cache_directory (ECacheReaper *extension,
182
GFile *cache_directory,
183
GFile *trash_directory)
242
cache_reaper_scan_directory (ECacheReaper *extension,
243
GFile *base_directory,
244
GFile *trash_directory)
185
246
GFileEnumerator *file_enumerator;
186
247
ESourceRegistryServer *server;
190
251
server = cache_reaper_get_server (extension);
192
253
file_enumerator = g_file_enumerate_children (
194
255
G_FILE_ATTRIBUTE_STANDARD_NAME,
195
256
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
215
276
if (g_strcmp0 (name, TRASH_DIRECTORY_NAME) == 0)
279
/* Also skip directories named "system". For backward
280
* compatibility, data directories for built-in sources
281
* are named "system" instead of "system-address-book"
282
* or "system-calendar" or what have you. */
283
if (g_strcmp0 (name, "system") == 0)
218
286
source = e_source_registry_server_ref_source (server, name);
220
288
if (source == NULL) {
328
cache_reaper_scan_data_directories (ECacheReaper *extension)
332
/* Scan the base data directories for unrecognized subdirectories.
333
* The subdirectories are named after data source UIDs, so compare
334
* their names to registered data sources and move any unrecognized
335
* subdirectories to the "trash" subdirectory to be reaped later. */
337
g_message ("Scanning data directories");
339
for (ii = 0; ii < extension->n_data_directories; ii++)
340
cache_reaper_scan_directory (
342
extension->data_directories[ii],
343
extension->data_trash_directories[ii]);
260
347
cache_reaper_scan_cache_directories (ECacheReaper *extension)
264
/* Scan the base cache directories for unregnized subdirectories.
351
/* Scan the base cache directories for unrecognized subdirectories.
265
352
* The subdirectories are named after data source UIDs, so compare
266
353
* their names to registered data sources and move any unrecognized
267
354
* subdirectories to the "trash" subdirectory to be reaped later. */
269
356
g_message ("Scanning cache directories");
271
for (ii = 0; ii < extension->n_directories; ii++)
272
cache_reaper_scan_cache_directory (
358
for (ii = 0; ii < extension->n_cache_directories; ii++)
359
cache_reaper_scan_directory (
274
361
extension->cache_directories[ii],
275
extension->trash_directories[ii]);
362
extension->cache_trash_directories[ii]);
279
cache_reaper_move_cache_to_trash (ECacheReaper *extension,
281
GFile *cache_directory,
282
GFile *trash_directory)
366
cache_reaper_move_to_trash (ECacheReaper *extension,
368
GFile *base_directory,
369
GFile *trash_directory)
284
371
GFile *source_directory;
285
372
GFile *target_directory;
288
375
uid = e_source_get_uid (source);
290
source_directory = g_file_get_child (cache_directory, uid);
377
source_directory = g_file_get_child (base_directory, uid);
291
378
target_directory = g_file_get_child (trash_directory, uid);
293
380
/* This is a no-op if the source directory does not exist. */
310
397
uid = e_source_get_uid (source);
312
399
source_directory = g_file_get_child (trash_directory, uid);
313
target_directory = g_file_get_child (cache_directory, uid);
400
target_directory = g_file_get_child (base_directory, uid);
315
402
/* This is a no-op if the source directory does not exist. */
316
403
cache_reaper_move_directory (source_directory, target_directory);
323
410
cache_reaper_files_loaded_cb (ESourceRegistryServer *server,
324
411
ECacheReaper *extension)
413
cache_reaper_scan_data_directories (extension);
326
414
cache_reaper_scan_cache_directories (extension);
328
416
/* Schedule the initial reaping. */
344
432
/* The Cache Reaper is not too proud to dig through the
345
433
* trash on the off chance the newly-added source has a
346
* recoverable cache directory. */
347
for (ii = 0; ii < extension->n_directories; ii++)
348
cache_reaper_recover_cache_from_trash (
434
* recoverable data or cache directory. */
436
for (ii = 0; ii < extension->n_data_directories; ii++)
437
cache_reaper_recover_from_trash (
439
extension->data_directories[ii],
440
extension->data_trash_directories[ii]);
442
for (ii = 0; ii < extension->n_cache_directories; ii++)
443
cache_reaper_recover_from_trash (
349
444
extension, source,
350
445
extension->cache_directories[ii],
351
extension->trash_directories[ii]);
446
extension->cache_trash_directories[ii]);
361
/* Stage the removed source's cache directory for
362
* reaping by moving it to the "trash" directory. */
363
for (ii = 0; ii < extension->n_directories; ii++)
364
cache_reaper_move_cache_to_trash (
456
/* Stage the removed source's cache directory for reaping
457
* by moving it to the "trash" directory.
459
* Do NOT do this for data directories. Cache directories
460
* are disposable and can be regenerated from the canonical
461
* data source, but data directories ARE the canonical data
462
* source so we want to be more conservative with them. If
463
* the removed source has a data directory, we will move it
464
* to the "trash" directory on next registry startup, which
465
* may correspond with the next desktop session startup. */
467
for (ii = 0; ii < extension->n_cache_directories; ii++)
468
cache_reaper_move_to_trash (
365
469
extension, source,
366
470
extension->cache_directories[ii],
367
extension->trash_directories[ii]);
471
extension->cache_trash_directories[ii]);
376
480
extension = E_CACHE_REAPER (object);
378
for (ii = 0; ii < extension->n_directories; ii++) {
482
for (ii = 0; ii < extension->n_data_directories; ii++) {
483
g_object_unref (extension->data_directories[ii]);
484
g_object_unref (extension->data_trash_directories[ii]);
487
g_free (extension->data_directories);
488
g_free (extension->data_trash_directories);
490
for (ii = 0; ii < extension->n_cache_directories; ii++) {
379
491
g_object_unref (extension->cache_directories[ii]);
380
g_object_unref (extension->trash_directories[ii]);
492
g_object_unref (extension->cache_trash_directories[ii]);
383
495
g_free (extension->cache_directories);
384
g_free (extension->trash_directories);
496
g_free (extension->cache_trash_directories);
386
498
if (extension->reaping_timeout_id > 0)
387
499
g_source_remove (extension->reaping_timeout_id);
438
550
e_cache_reaper_init (ECacheReaper *extension)
440
552
GFile *base_directory;
553
const gchar *user_data_dir;
441
554
const gchar *user_cache_dir;
442
555
guint n_directories, ii;
444
557
/* These are component names from which
558
* the data directory arrays are built. */
559
const gchar *data_component_names[] = {
567
/* These are component names from which
445
568
* the cache directory arrays are built. */
446
const gchar *component_names[] = {
569
const gchar *cache_component_names[] = {
455
n_directories = G_N_ELEMENTS (component_names);
457
extension->n_directories = n_directories;
578
/* Setup base directories for data. */
580
n_directories = G_N_ELEMENTS (data_component_names);
582
extension->n_data_directories = n_directories;
583
extension->data_directories = g_new0 (GFile *, n_directories);
584
extension->data_trash_directories = g_new0 (GFile *, n_directories);
586
user_data_dir = e_get_user_data_dir ();
587
base_directory = g_file_new_for_path (user_data_dir);
589
for (ii = 0; ii < n_directories; ii++) {
590
GFile *data_directory;
591
GFile *trash_directory;
592
GError *error = NULL;
594
data_directory = g_file_get_child (
595
base_directory, data_component_names[ii]);
596
trash_directory = g_file_get_child (
597
data_directory, TRASH_DIRECTORY_NAME);
599
/* Data directory is a parent of the trash
600
* directory so this is sufficient for both. */
601
cache_reaper_make_directory_and_parents (
602
trash_directory, NULL, &error);
605
g_warning ("%s: %s", G_STRFUNC, error->message);
606
g_error_free (error);
609
extension->data_directories[ii] = data_directory;
610
extension->data_trash_directories[ii] = trash_directory;
613
g_object_unref (base_directory);
615
/* Setup base directories for cache. */
617
n_directories = G_N_ELEMENTS (cache_component_names);
619
extension->n_cache_directories = n_directories;
458
620
extension->cache_directories = g_new0 (GFile *, n_directories);
459
extension->trash_directories = g_new0 (GFile *, n_directories);
621
extension->cache_trash_directories = g_new0 (GFile *, n_directories);
461
623
user_cache_dir = e_get_user_cache_dir ();
462
624
base_directory = g_file_new_for_path (user_cache_dir);
467
629
GError *error = NULL;
469
631
cache_directory = g_file_get_child (
470
base_directory, component_names[ii]);
632
base_directory, cache_component_names[ii]);
471
633
trash_directory = g_file_get_child (
472
634
cache_directory, TRASH_DIRECTORY_NAME);
474
636
/* Cache directory is a parent of the trash
475
637
* directory so this is sufficient for both. */
476
g_file_make_directory_with_parents (
638
cache_reaper_make_directory_and_parents (
477
639
trash_directory, NULL, &error);
479
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
480
g_clear_error (&error);
482
641
if (error != NULL) {
485
path = g_file_get_path (trash_directory);
487
"Failed to make directory '%s': %s",
488
path, error->message);
642
g_warning ("%s: %s", G_STRFUNC, error->message);
491
643
g_error_free (error);
494
646
extension->cache_directories[ii] = cache_directory;
495
extension->trash_directories[ii] = trash_directory;
647
extension->cache_trash_directories[ii] = trash_directory;
498
650
g_object_unref (base_directory);