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
files_compare(struct files ** f1,struct files ** f2)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
traverse_fn(const char * fname,struct stat * st)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
delete_file(const char * path,size_t size,bool update_counters)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
sort_and_clean(void)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
clean_up_dir(struct conf * conf,const char * dir,double limit_multiple)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 	cache_size_threshold = (uint64_t)round(conf->max_size * limit_multiple / 16);
168 	files_in_cache_threshold =
169 		(size_t)round(conf->max_files * limit_multiple / 16);
170 
171 	num_files = 0;
172 	cache_size = 0;
173 	files_in_cache = 0;
174 
175 	// Build a list of files.
176 	traverse(dir, traverse_fn);
177 
178 	// Clean the cache.
179 	cc_log("Before cleanup: %.0f KiB, %.0f files",
180 	       (double)cache_size / 1024,
181 	       (double)files_in_cache);
182 	bool cleaned = sort_and_clean();
183 	cc_log("After cleanup: %.0f KiB, %.0f files",
184 	       (double)cache_size / 1024,
185 	       (double)files_in_cache);
186 
187 	if (cleaned) {
188 		cc_log("Cleaned up cache directory %s", dir);
189 		stats_add_cleanup(dir, 1);
190 	}
191 
192 	stats_set_sizes(dir, files_in_cache, cache_size);
193 
194 	// Free it up.
195 	for (unsigned i = 0; i < num_files; i++) {
196 		free(files[i]->fname);
197 		free(files[i]);
198 		files[i] = NULL;
199 	}
200 	if (files) {
201 		free(files);
202 	}
203 	allocated = 0;
204 	files = NULL;
205 
206 	num_files = 0;
207 	cache_size = 0;
208 	files_in_cache = 0;
209 }
210 
211 // Clean up all cache subdirectories.
clean_up_all(struct conf * conf)212 void clean_up_all(struct conf *conf)
213 {
214 	for (int i = 0; i <= 0xF; i++) {
215 		char *dname = format("%s/%1x", conf->cache_dir, i);
216 		clean_up_dir(conf, dname, 1.0);
217 		free(dname);
218 	}
219 }
220 
221 // Traverse function for wiping files.
wipe_fn(const char * fname,struct stat * st)222 static void wipe_fn(const char *fname, struct stat *st)
223 {
224 	if (!S_ISREG(st->st_mode)) {
225 		return;
226 	}
227 
228 	char *p = basename(fname);
229 	if (str_eq(p, "stats")) {
230 		free(p);
231 		return;
232 	}
233 	free(p);
234 
235 	files_in_cache++;
236 
237 	x_unlink(fname);
238 }
239 
240 // Wipe one cache subdirectory.
241 void
wipe_dir(const char * dir)242 wipe_dir(const char *dir)
243 {
244 	cc_log("Clearing out cache directory %s", dir);
245 
246 	files_in_cache = 0;
247 
248 	traverse(dir, wipe_fn);
249 
250 	if (files_in_cache > 0) {
251 		cc_log("Cleared out cache directory %s", dir);
252 		stats_add_cleanup(dir, 1);
253 	}
254 
255 	files_in_cache = 0;
256 }
257 
258 // Wipe all cached files in all subdirectories.
wipe_all(struct conf * conf)259 void wipe_all(struct conf *conf)
260 {
261 	for (int i = 0; i <= 0xF; i++) {
262 		char *dname = format("%s/%1x", conf->cache_dir, i);
263 		wipe_dir(dname);
264 		free(dname);
265 	}
266 
267 	// Fix the counters.
268 	clean_up_all(conf);
269 }
270