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