Imported Upstream version 3.7
[platform/upstream/ccache.git] / src / cleanup.c
1 // Copyright (C) 2002-2006 Andrew Tridgell
2 // Copyright (C) 2009-2018 Joel Rosdahl
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License as published by the Free
6 // Software Foundation; either version 3 of the License, or (at your option)
7 // any later version.
8 //
9 // This program is distributed in the hope that it will be useful, but WITHOUT
10 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 // more details.
13 //
14 // You should have received a copy of the GNU General Public License along with
15 // this program; if not, write to the Free Software Foundation, Inc., 51
16 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
18 #include "ccache.h"
19
20 #include <math.h>
21
22 static struct files {
23         char *fname;
24         time_t mtime;
25         uint64_t size;
26 } **files;
27 static unsigned allocated; // Size of the files array.
28 static unsigned num_files; // Number of used entries in the files array.
29
30 static uint64_t cache_size;
31 static size_t files_in_cache;
32 static uint64_t cache_size_threshold;
33 static size_t files_in_cache_threshold;
34
35 // File comparison function that orders files in mtime order, oldest first.
36 static int
37 files_compare(struct files **f1, struct files **f2)
38 {
39         if ((*f2)->mtime == (*f1)->mtime) {
40                 return strcmp((*f1)->fname, (*f2)->fname);
41         }
42         if ((*f2)->mtime > (*f1)->mtime) {
43                 return -1;
44         }
45         return 1;
46 }
47
48 // This builds the list of files in the cache.
49 static void
50 traverse_fn(const char *fname, struct stat *st)
51 {
52         if (!S_ISREG(st->st_mode)) {
53                 return;
54         }
55
56         char *p = basename(fname);
57         if (str_eq(p, "stats")) {
58                 goto out;
59         }
60
61         if (str_startswith(p, ".nfs")) {
62                 // Ignore temporary NFS files that may be left for open but deleted files.
63                 goto out;
64         }
65
66         // Delete any tmp files older than 1 hour.
67         if (strstr(p, ".tmp.") && st->st_mtime + 3600 < time(NULL)) {
68                 x_unlink(fname);
69                 goto out;
70         }
71
72         if (strstr(p, "CACHEDIR.TAG")) {
73                 goto out;
74         }
75
76         if (num_files == allocated) {
77                 allocated = 10000 + num_files*2;
78                 files = (struct files **)x_realloc(files, sizeof(struct files *)*allocated);
79         }
80
81         files[num_files] = (struct files *)x_malloc(sizeof(struct files));
82         files[num_files]->fname = x_strdup(fname);
83         files[num_files]->mtime = st->st_mtime;
84         files[num_files]->size = file_size(st);
85         cache_size += files[num_files]->size;
86         files_in_cache++;
87         num_files++;
88
89 out:
90         free(p);
91 }
92
93 static void
94 delete_file(const char *path, size_t size, bool update_counters)
95 {
96         bool deleted = x_try_unlink(path) == 0;
97         if (!deleted && errno != ENOENT && errno != ESTALE) {
98                 cc_log("Failed to unlink %s (%s)", path, strerror(errno));
99         } else if (update_counters) {
100                 // The counters are intentionally subtracted even if there was no file to
101                 // delete since the final cache size calculation will be incorrect if they
102                 // aren't. (This can happen when there are several parallel ongoing
103                 // cleanups of the same directory.)
104                 cache_size -= size;
105                 files_in_cache--;
106         }
107 }
108
109 // Sort the files we've found and delete the oldest ones until we are below the
110 // thresholds.
111 static bool
112 sort_and_clean(void)
113 {
114         if (num_files > 1) {
115                 // Sort in ascending mtime order.
116                 qsort(files, num_files, sizeof(struct files *), (COMPAR_FN_T)files_compare);
117         }
118
119         // Delete enough files to bring us below the threshold.
120         bool cleaned = false;
121         for (unsigned i = 0; i < num_files; i++) {
122                 const char *ext;
123
124                 if ((cache_size_threshold == 0
125                      || cache_size <= cache_size_threshold)
126                     && (files_in_cache_threshold == 0
127                         || files_in_cache <= files_in_cache_threshold)) {
128                         break;
129                 }
130
131                 ext = get_extension(files[i]->fname);
132                 if (str_eq(ext, ".stderr")) {
133                         // Make sure that the .o file is deleted before .stderr, because if the
134                         // ccache process gets killed after deleting the .stderr but before
135                         // deleting the .o, the cached result will be inconsistent. (.stderr is
136                         // the only file that is optional; any other file missing from the cache
137                         // will be detected by get_file_from_cache.)
138                         char *base = remove_extension(files[i]->fname);
139                         char *o_file = format("%s.o", base);
140
141                         // Don't subtract this extra deletion from the cache size; that
142                         // bookkeeping will be done when the loop reaches the .o file. If the
143                         // loop doesn't reach the .o file since the target limits have been
144                         // reached, the bookkeeping won't happen, but that small counter
145                         // discrepancy won't do much harm and it will correct itself in the next
146                         // cleanup.
147                         delete_file(o_file, 0, false);
148
149                         free(o_file);
150                         free(base);
151                 }
152                 delete_file(files[i]->fname, files[i]->size, true);
153                 cleaned = true;
154         }
155         return cleaned;
156 }
157
158 // Clean up one cache subdirectory.
159 void
160 clean_up_dir(struct conf *conf, const char *dir, double limit_multiple)
161 {
162         cc_log("Cleaning up cache directory %s", dir);
163
164         // When "max files" or "max cache size" is reached, one of the 16 cache
165         // subdirectories is cleaned up. When doing so, files are deleted (in LRU
166         // order) until the levels are below limit_multiple.
167         double cache_size_float = round(conf->max_size * limit_multiple / 16);
168         cache_size_threshold = (uint64_t)cache_size_float;
169         double files_in_cache_float = round(conf->max_files * limit_multiple / 16);
170         files_in_cache_threshold = (size_t)files_in_cache_float;
171
172         num_files = 0;
173         cache_size = 0;
174         files_in_cache = 0;
175
176         // Build a list of files.
177         traverse(dir, traverse_fn);
178
179         // Clean the cache.
180         cc_log("Before cleanup: %.0f KiB, %.0f files",
181                (double)cache_size / 1024,
182                (double)files_in_cache);
183         bool cleaned = sort_and_clean();
184         cc_log("After cleanup: %.0f KiB, %.0f files",
185                (double)cache_size / 1024,
186                (double)files_in_cache);
187
188         if (cleaned) {
189                 cc_log("Cleaned up cache directory %s", dir);
190                 stats_add_cleanup(dir, 1);
191         }
192
193         stats_set_sizes(dir, files_in_cache, cache_size);
194
195         // Free it up.
196         for (unsigned i = 0; i < num_files; i++) {
197                 free(files[i]->fname);
198                 free(files[i]);
199                 files[i] = NULL;
200         }
201         if (files) {
202                 free(files);
203         }
204         allocated = 0;
205         files = NULL;
206
207         num_files = 0;
208         cache_size = 0;
209         files_in_cache = 0;
210 }
211
212 // Clean up all cache subdirectories.
213 void clean_up_all(struct conf *conf)
214 {
215         for (int i = 0; i <= 0xF; i++) {
216                 char *dname = format("%s/%1x", conf->cache_dir, i);
217                 clean_up_dir(conf, dname, 1.0);
218                 free(dname);
219         }
220 }
221
222 // Traverse function for wiping files.
223 static void wipe_fn(const char *fname, struct stat *st)
224 {
225         if (!S_ISREG(st->st_mode)) {
226                 return;
227         }
228
229         char *p = basename(fname);
230         if (str_eq(p, "stats")) {
231                 free(p);
232                 return;
233         }
234         free(p);
235
236         files_in_cache++;
237
238         x_unlink(fname);
239 }
240
241 // Wipe one cache subdirectory.
242 static void
243 wipe_dir(const char *dir)
244 {
245         cc_log("Clearing out cache directory %s", dir);
246
247         files_in_cache = 0;
248
249         traverse(dir, wipe_fn);
250
251         if (files_in_cache > 0) {
252                 cc_log("Cleared out cache directory %s", dir);
253                 stats_add_cleanup(dir, 1);
254         }
255
256         files_in_cache = 0;
257 }
258
259 // Wipe all cached files in all subdirectories.
260 void wipe_all(struct conf *conf)
261 {
262         for (int i = 0; i <= 0xF; i++) {
263                 char *dname = format("%s/%1x", conf->cache_dir, i);
264                 wipe_dir(dname);
265                 free(dname);
266         }
267
268         // Fix the counters.
269         clean_up_all(conf);
270 }