xref: /aosp_15_r20/external/libwebsockets/lib/misc/diskcache.c (revision 1c60b9aca93fdbc9b5f19b2d2194c91294b22281)
1*1c60b9acSAndroid Build Coastguard Worker /*
2*1c60b9acSAndroid Build Coastguard Worker  * libwebsockets - small server side websockets and web server implementation
3*1c60b9acSAndroid Build Coastguard Worker  *
4*1c60b9acSAndroid Build Coastguard Worker  * Copyright (C) 2010 - 2019 Andy Green <[email protected]>
5*1c60b9acSAndroid Build Coastguard Worker  *
6*1c60b9acSAndroid Build Coastguard Worker  * Permission is hereby granted, free of charge, to any person obtaining a copy
7*1c60b9acSAndroid Build Coastguard Worker  * of this software and associated documentation files (the "Software"), to
8*1c60b9acSAndroid Build Coastguard Worker  * deal in the Software without restriction, including without limitation the
9*1c60b9acSAndroid Build Coastguard Worker  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10*1c60b9acSAndroid Build Coastguard Worker  * sell copies of the Software, and to permit persons to whom the Software is
11*1c60b9acSAndroid Build Coastguard Worker  * furnished to do so, subject to the following conditions:
12*1c60b9acSAndroid Build Coastguard Worker  *
13*1c60b9acSAndroid Build Coastguard Worker  * The above copyright notice and this permission notice shall be included in
14*1c60b9acSAndroid Build Coastguard Worker  * all copies or substantial portions of the Software.
15*1c60b9acSAndroid Build Coastguard Worker  *
16*1c60b9acSAndroid Build Coastguard Worker  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17*1c60b9acSAndroid Build Coastguard Worker  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18*1c60b9acSAndroid Build Coastguard Worker  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19*1c60b9acSAndroid Build Coastguard Worker  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20*1c60b9acSAndroid Build Coastguard Worker  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21*1c60b9acSAndroid Build Coastguard Worker  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22*1c60b9acSAndroid Build Coastguard Worker  * IN THE SOFTWARE.
23*1c60b9acSAndroid Build Coastguard Worker  */
24*1c60b9acSAndroid Build Coastguard Worker 
25*1c60b9acSAndroid Build Coastguard Worker #if !defined(_GNU_SOURCE)
26*1c60b9acSAndroid Build Coastguard Worker #define _GNU_SOURCE
27*1c60b9acSAndroid Build Coastguard Worker #endif
28*1c60b9acSAndroid Build Coastguard Worker #include <pthread.h>
29*1c60b9acSAndroid Build Coastguard Worker 
30*1c60b9acSAndroid Build Coastguard Worker #include <libwebsockets.h>
31*1c60b9acSAndroid Build Coastguard Worker #include "private-lib-core.h"
32*1c60b9acSAndroid Build Coastguard Worker 
33*1c60b9acSAndroid Build Coastguard Worker #include <string.h>
34*1c60b9acSAndroid Build Coastguard Worker #include <stdio.h>
35*1c60b9acSAndroid Build Coastguard Worker #include <unistd.h>
36*1c60b9acSAndroid Build Coastguard Worker #include <fcntl.h>
37*1c60b9acSAndroid Build Coastguard Worker #include <dirent.h>
38*1c60b9acSAndroid Build Coastguard Worker #include <time.h>
39*1c60b9acSAndroid Build Coastguard Worker #include <errno.h>
40*1c60b9acSAndroid Build Coastguard Worker #include <stdarg.h>
41*1c60b9acSAndroid Build Coastguard Worker 
42*1c60b9acSAndroid Build Coastguard Worker #include <sys/stat.h>
43*1c60b9acSAndroid Build Coastguard Worker #include <sys/time.h>
44*1c60b9acSAndroid Build Coastguard Worker #include <sys/types.h>
45*1c60b9acSAndroid Build Coastguard Worker 
46*1c60b9acSAndroid Build Coastguard Worker #if defined(__APPLE__)
47*1c60b9acSAndroid Build Coastguard Worker #include <sys/dirent.h>
48*1c60b9acSAndroid Build Coastguard Worker /* Travis OSX does not have DT_REG... */
49*1c60b9acSAndroid Build Coastguard Worker #if !defined(DT_REG)
50*1c60b9acSAndroid Build Coastguard Worker #define DT_REG 8
51*1c60b9acSAndroid Build Coastguard Worker #endif
52*1c60b9acSAndroid Build Coastguard Worker #endif
53*1c60b9acSAndroid Build Coastguard Worker 
54*1c60b9acSAndroid Build Coastguard Worker struct file_entry {
55*1c60b9acSAndroid Build Coastguard Worker 	lws_list_ptr sorted;
56*1c60b9acSAndroid Build Coastguard Worker 	lws_list_ptr prev;
57*1c60b9acSAndroid Build Coastguard Worker 	char name[64];
58*1c60b9acSAndroid Build Coastguard Worker 	time_t modified;
59*1c60b9acSAndroid Build Coastguard Worker 	size_t size;
60*1c60b9acSAndroid Build Coastguard Worker };
61*1c60b9acSAndroid Build Coastguard Worker 
62*1c60b9acSAndroid Build Coastguard Worker struct lws_diskcache_scan {
63*1c60b9acSAndroid Build Coastguard Worker 	struct file_entry *batch;
64*1c60b9acSAndroid Build Coastguard Worker 	const char *cache_dir_base;
65*1c60b9acSAndroid Build Coastguard Worker 	lws_list_ptr head;
66*1c60b9acSAndroid Build Coastguard Worker 	time_t last_scan_completed;
67*1c60b9acSAndroid Build Coastguard Worker 	uint64_t agg_size;
68*1c60b9acSAndroid Build Coastguard Worker 	uint64_t cache_size_limit;
69*1c60b9acSAndroid Build Coastguard Worker 	uint64_t avg_size;
70*1c60b9acSAndroid Build Coastguard Worker 	uint64_t cache_tries;
71*1c60b9acSAndroid Build Coastguard Worker 	uint64_t cache_hits;
72*1c60b9acSAndroid Build Coastguard Worker 	int cache_subdir;
73*1c60b9acSAndroid Build Coastguard Worker 	int batch_in_use;
74*1c60b9acSAndroid Build Coastguard Worker 	int agg_file_count;
75*1c60b9acSAndroid Build Coastguard Worker 	int secs_waiting;
76*1c60b9acSAndroid Build Coastguard Worker };
77*1c60b9acSAndroid Build Coastguard Worker 
78*1c60b9acSAndroid Build Coastguard Worker #define KIB (1024)
79*1c60b9acSAndroid Build Coastguard Worker #define MIB (KIB * KIB)
80*1c60b9acSAndroid Build Coastguard Worker 
81*1c60b9acSAndroid Build Coastguard Worker #define lp_to_fe(p, _n) lws_list_ptr_container(p, struct file_entry, _n)
82*1c60b9acSAndroid Build Coastguard Worker 
83*1c60b9acSAndroid Build Coastguard Worker static const char *hex = "0123456789abcdef";
84*1c60b9acSAndroid Build Coastguard Worker 
85*1c60b9acSAndroid Build Coastguard Worker #define BATCH_COUNT 128
86*1c60b9acSAndroid Build Coastguard Worker 
87*1c60b9acSAndroid Build Coastguard Worker static int
fe_modified_sort(lws_list_ptr a,lws_list_ptr b)88*1c60b9acSAndroid Build Coastguard Worker fe_modified_sort(lws_list_ptr a, lws_list_ptr b)
89*1c60b9acSAndroid Build Coastguard Worker {
90*1c60b9acSAndroid Build Coastguard Worker 	struct file_entry *p1 = lp_to_fe(a, sorted), *p2 = lp_to_fe(b, sorted);
91*1c60b9acSAndroid Build Coastguard Worker 
92*1c60b9acSAndroid Build Coastguard Worker 	return (int)((long)p2->modified - (long)p1->modified);
93*1c60b9acSAndroid Build Coastguard Worker }
94*1c60b9acSAndroid Build Coastguard Worker 
95*1c60b9acSAndroid Build Coastguard Worker struct lws_diskcache_scan *
lws_diskcache_create(const char * cache_dir_base,uint64_t cache_size_limit)96*1c60b9acSAndroid Build Coastguard Worker lws_diskcache_create(const char *cache_dir_base, uint64_t cache_size_limit)
97*1c60b9acSAndroid Build Coastguard Worker {
98*1c60b9acSAndroid Build Coastguard Worker 	struct lws_diskcache_scan *lds = lws_malloc(sizeof(*lds), "cachescan");
99*1c60b9acSAndroid Build Coastguard Worker 
100*1c60b9acSAndroid Build Coastguard Worker 	if (!lds)
101*1c60b9acSAndroid Build Coastguard Worker 		return NULL;
102*1c60b9acSAndroid Build Coastguard Worker 
103*1c60b9acSAndroid Build Coastguard Worker 	memset(lds, 0, sizeof(*lds));
104*1c60b9acSAndroid Build Coastguard Worker 
105*1c60b9acSAndroid Build Coastguard Worker 	lds->cache_dir_base = cache_dir_base;
106*1c60b9acSAndroid Build Coastguard Worker 	lds->cache_size_limit = cache_size_limit;
107*1c60b9acSAndroid Build Coastguard Worker 
108*1c60b9acSAndroid Build Coastguard Worker 	return lds;
109*1c60b9acSAndroid Build Coastguard Worker }
110*1c60b9acSAndroid Build Coastguard Worker 
111*1c60b9acSAndroid Build Coastguard Worker void
lws_diskcache_destroy(struct lws_diskcache_scan ** lds)112*1c60b9acSAndroid Build Coastguard Worker lws_diskcache_destroy(struct lws_diskcache_scan **lds)
113*1c60b9acSAndroid Build Coastguard Worker {
114*1c60b9acSAndroid Build Coastguard Worker 	if ((*lds)->batch)
115*1c60b9acSAndroid Build Coastguard Worker 		lws_free((*lds)->batch);
116*1c60b9acSAndroid Build Coastguard Worker 	lws_free(*lds);
117*1c60b9acSAndroid Build Coastguard Worker 	*lds = NULL;
118*1c60b9acSAndroid Build Coastguard Worker }
119*1c60b9acSAndroid Build Coastguard Worker 
120*1c60b9acSAndroid Build Coastguard Worker int
lws_diskcache_prepare(const char * cache_base_dir,int mode,uid_t uid)121*1c60b9acSAndroid Build Coastguard Worker lws_diskcache_prepare(const char *cache_base_dir, int mode, uid_t uid)
122*1c60b9acSAndroid Build Coastguard Worker {
123*1c60b9acSAndroid Build Coastguard Worker 	char dir[256];
124*1c60b9acSAndroid Build Coastguard Worker 	int n, m;
125*1c60b9acSAndroid Build Coastguard Worker 
126*1c60b9acSAndroid Build Coastguard Worker 	(void)mkdir(cache_base_dir, (unsigned short)mode);
127*1c60b9acSAndroid Build Coastguard Worker 	if (chown(cache_base_dir, uid, (gid_t)-1))
128*1c60b9acSAndroid Build Coastguard Worker 		lwsl_err("%s: %s: unable to chown %d\n", __func__,
129*1c60b9acSAndroid Build Coastguard Worker 			 cache_base_dir, uid);
130*1c60b9acSAndroid Build Coastguard Worker 
131*1c60b9acSAndroid Build Coastguard Worker 	for (n = 0; n < 16; n++) {
132*1c60b9acSAndroid Build Coastguard Worker 		lws_snprintf(dir, sizeof(dir), "%s/%c", cache_base_dir, hex[n]);
133*1c60b9acSAndroid Build Coastguard Worker 		(void)mkdir(dir, (mode_t)mode);
134*1c60b9acSAndroid Build Coastguard Worker 		if (chown(dir, uid, (uid_t)-1))
135*1c60b9acSAndroid Build Coastguard Worker 			lwsl_err("%s: %s: unable to chown %d\n", __func__,
136*1c60b9acSAndroid Build Coastguard Worker 						 dir, uid);
137*1c60b9acSAndroid Build Coastguard Worker 		for (m = 0; m < 16; m++) {
138*1c60b9acSAndroid Build Coastguard Worker 			lws_snprintf(dir, sizeof(dir), "%s/%c/%c",
139*1c60b9acSAndroid Build Coastguard Worker 				     cache_base_dir, hex[n], hex[m]);
140*1c60b9acSAndroid Build Coastguard Worker 			(void)mkdir(dir, (mode_t)mode);
141*1c60b9acSAndroid Build Coastguard Worker 			if (chown(dir, uid, (uid_t)-1))
142*1c60b9acSAndroid Build Coastguard Worker 				lwsl_err("%s: %s: unable to chown %d\n",
143*1c60b9acSAndroid Build Coastguard Worker 					 __func__, dir, uid);
144*1c60b9acSAndroid Build Coastguard Worker 		}
145*1c60b9acSAndroid Build Coastguard Worker 	}
146*1c60b9acSAndroid Build Coastguard Worker 
147*1c60b9acSAndroid Build Coastguard Worker 	return 0;
148*1c60b9acSAndroid Build Coastguard Worker }
149*1c60b9acSAndroid Build Coastguard Worker 
150*1c60b9acSAndroid Build Coastguard Worker /* copies and then truncates the incoming name, and renames the file at the
151*1c60b9acSAndroid Build Coastguard Worker  * untruncated path to have the new truncated name */
152*1c60b9acSAndroid Build Coastguard Worker 
153*1c60b9acSAndroid Build Coastguard Worker int
lws_diskcache_finalize_name(char * cache)154*1c60b9acSAndroid Build Coastguard Worker lws_diskcache_finalize_name(char *cache)
155*1c60b9acSAndroid Build Coastguard Worker {
156*1c60b9acSAndroid Build Coastguard Worker 	char ren[256], *p;
157*1c60b9acSAndroid Build Coastguard Worker 
158*1c60b9acSAndroid Build Coastguard Worker 	strncpy(ren, cache, sizeof(ren) - 1);
159*1c60b9acSAndroid Build Coastguard Worker 	ren[sizeof(ren) - 1] = '\0';
160*1c60b9acSAndroid Build Coastguard Worker 	p = strchr(cache, '~');
161*1c60b9acSAndroid Build Coastguard Worker 	if (p) {
162*1c60b9acSAndroid Build Coastguard Worker 		*p = '\0';
163*1c60b9acSAndroid Build Coastguard Worker 		if (rename(ren, cache)) {
164*1c60b9acSAndroid Build Coastguard Worker 			lwsl_err("%s: problem renaming %s to %s\n", __func__,
165*1c60b9acSAndroid Build Coastguard Worker 				 ren, cache);
166*1c60b9acSAndroid Build Coastguard Worker 			return 1;
167*1c60b9acSAndroid Build Coastguard Worker 		}
168*1c60b9acSAndroid Build Coastguard Worker 
169*1c60b9acSAndroid Build Coastguard Worker 		return 0;
170*1c60b9acSAndroid Build Coastguard Worker 	}
171*1c60b9acSAndroid Build Coastguard Worker 
172*1c60b9acSAndroid Build Coastguard Worker 	return 1;
173*1c60b9acSAndroid Build Coastguard Worker }
174*1c60b9acSAndroid Build Coastguard Worker 
175*1c60b9acSAndroid Build Coastguard Worker int
lws_diskcache_query(struct lws_diskcache_scan * lds,int is_bot,const char * hash_hex,int * _fd,char * cache,int cache_len,size_t * extant_cache_len)176*1c60b9acSAndroid Build Coastguard Worker lws_diskcache_query(struct lws_diskcache_scan *lds, int is_bot,
177*1c60b9acSAndroid Build Coastguard Worker 		    const char *hash_hex, int *_fd, char *cache, int cache_len,
178*1c60b9acSAndroid Build Coastguard Worker 		    size_t *extant_cache_len)
179*1c60b9acSAndroid Build Coastguard Worker {
180*1c60b9acSAndroid Build Coastguard Worker 	struct stat s;
181*1c60b9acSAndroid Build Coastguard Worker 	int n;
182*1c60b9acSAndroid Build Coastguard Worker 
183*1c60b9acSAndroid Build Coastguard Worker 	/* caching is disabled? */
184*1c60b9acSAndroid Build Coastguard Worker 	if (!lds->cache_dir_base)
185*1c60b9acSAndroid Build Coastguard Worker 		return LWS_DISKCACHE_QUERY_NO_CACHE;
186*1c60b9acSAndroid Build Coastguard Worker 
187*1c60b9acSAndroid Build Coastguard Worker 	if (!is_bot)
188*1c60b9acSAndroid Build Coastguard Worker 		lds->cache_tries++;
189*1c60b9acSAndroid Build Coastguard Worker 
190*1c60b9acSAndroid Build Coastguard Worker 	n = lws_snprintf(cache, (size_t)cache_len, "%s/%c/%c/%s", lds->cache_dir_base,
191*1c60b9acSAndroid Build Coastguard Worker 			 hash_hex[0], hash_hex[1], hash_hex);
192*1c60b9acSAndroid Build Coastguard Worker 
193*1c60b9acSAndroid Build Coastguard Worker 	lwsl_info("%s: job cache %s\n", __func__, cache);
194*1c60b9acSAndroid Build Coastguard Worker 
195*1c60b9acSAndroid Build Coastguard Worker 	*_fd = open(cache, O_RDONLY);
196*1c60b9acSAndroid Build Coastguard Worker 	if (*_fd >= 0) {
197*1c60b9acSAndroid Build Coastguard Worker 		int fd;
198*1c60b9acSAndroid Build Coastguard Worker 
199*1c60b9acSAndroid Build Coastguard Worker 		if (!is_bot)
200*1c60b9acSAndroid Build Coastguard Worker 			lds->cache_hits++;
201*1c60b9acSAndroid Build Coastguard Worker 
202*1c60b9acSAndroid Build Coastguard Worker 		if (fstat(*_fd, &s)) {
203*1c60b9acSAndroid Build Coastguard Worker 			close(*_fd);
204*1c60b9acSAndroid Build Coastguard Worker 
205*1c60b9acSAndroid Build Coastguard Worker 			return LWS_DISKCACHE_QUERY_NO_CACHE;
206*1c60b9acSAndroid Build Coastguard Worker 		}
207*1c60b9acSAndroid Build Coastguard Worker 
208*1c60b9acSAndroid Build Coastguard Worker 		*extant_cache_len = (size_t)s.st_size;
209*1c60b9acSAndroid Build Coastguard Worker 
210*1c60b9acSAndroid Build Coastguard Worker 		/* "touch" the hit cache file so it's last for LRU now */
211*1c60b9acSAndroid Build Coastguard Worker 		fd = open(cache, O_RDWR);
212*1c60b9acSAndroid Build Coastguard Worker 		if (fd >= 0)
213*1c60b9acSAndroid Build Coastguard Worker 			close(fd);
214*1c60b9acSAndroid Build Coastguard Worker 
215*1c60b9acSAndroid Build Coastguard Worker 		return LWS_DISKCACHE_QUERY_EXISTS;
216*1c60b9acSAndroid Build Coastguard Worker 	}
217*1c60b9acSAndroid Build Coastguard Worker 
218*1c60b9acSAndroid Build Coastguard Worker 	/* bots are too random to pollute the cache with their antics */
219*1c60b9acSAndroid Build Coastguard Worker 	if (is_bot)
220*1c60b9acSAndroid Build Coastguard Worker 		return LWS_DISKCACHE_QUERY_NO_CACHE;
221*1c60b9acSAndroid Build Coastguard Worker 
222*1c60b9acSAndroid Build Coastguard Worker 	/* let's create it first with a unique temp name */
223*1c60b9acSAndroid Build Coastguard Worker 
224*1c60b9acSAndroid Build Coastguard Worker 	lws_snprintf(cache + n, (size_t)cache_len - (unsigned int)n, "~%d-%p", (int)getpid(),
225*1c60b9acSAndroid Build Coastguard Worker 		     extant_cache_len);
226*1c60b9acSAndroid Build Coastguard Worker 
227*1c60b9acSAndroid Build Coastguard Worker 	*_fd = open(cache, O_RDWR | O_CREAT | O_TRUNC, 0600);
228*1c60b9acSAndroid Build Coastguard Worker 	if (*_fd < 0) {
229*1c60b9acSAndroid Build Coastguard Worker 		/* well... ok... we will proceed without cache then... */
230*1c60b9acSAndroid Build Coastguard Worker 		lwsl_notice("%s: Problem creating cache %s: errno %d\n",
231*1c60b9acSAndroid Build Coastguard Worker 			    __func__, cache, errno);
232*1c60b9acSAndroid Build Coastguard Worker 		return LWS_DISKCACHE_QUERY_NO_CACHE;
233*1c60b9acSAndroid Build Coastguard Worker 	}
234*1c60b9acSAndroid Build Coastguard Worker 
235*1c60b9acSAndroid Build Coastguard Worker 	return LWS_DISKCACHE_QUERY_CREATING;
236*1c60b9acSAndroid Build Coastguard Worker }
237*1c60b9acSAndroid Build Coastguard Worker 
238*1c60b9acSAndroid Build Coastguard Worker int
lws_diskcache_secs_to_idle(struct lws_diskcache_scan * lds)239*1c60b9acSAndroid Build Coastguard Worker lws_diskcache_secs_to_idle(struct lws_diskcache_scan *lds)
240*1c60b9acSAndroid Build Coastguard Worker {
241*1c60b9acSAndroid Build Coastguard Worker 	return lds->secs_waiting;
242*1c60b9acSAndroid Build Coastguard Worker }
243*1c60b9acSAndroid Build Coastguard Worker 
244*1c60b9acSAndroid Build Coastguard Worker /*
245*1c60b9acSAndroid Build Coastguard Worker  * The goal is to collect the oldest BATCH_COUNT filepaths and filesizes from
246*1c60b9acSAndroid Build Coastguard Worker  * the dirs under the cache dir.  Since we don't need or want a full list of
247*1c60b9acSAndroid Build Coastguard Worker  * files in there in memory at once, we restrict the linked-list size to
248*1c60b9acSAndroid Build Coastguard Worker  * BATCH_COUNT entries, and once it is full, simply ignore any further files
249*1c60b9acSAndroid Build Coastguard Worker  * that are newer than the newest one on that list.  Files older than the
250*1c60b9acSAndroid Build Coastguard Worker  * newest guy already on the list evict the newest guy already on the list
251*1c60b9acSAndroid Build Coastguard Worker  * and are sorted into the correct order.  In this way no matter the number
252*1c60b9acSAndroid Build Coastguard Worker  * of files to be processed the memory requirement is fixed at BATCH_COUNT
253*1c60b9acSAndroid Build Coastguard Worker  * struct file_entry-s.
254*1c60b9acSAndroid Build Coastguard Worker  *
255*1c60b9acSAndroid Build Coastguard Worker  * The oldest subset of BATCH_COUNT files are sorted into the cd->batch
256*1c60b9acSAndroid Build Coastguard Worker  * allocation in more recent -> least recent order.
257*1c60b9acSAndroid Build Coastguard Worker  *
258*1c60b9acSAndroid Build Coastguard Worker  * We want to track the total size of all files we saw as well, so we know if
259*1c60b9acSAndroid Build Coastguard Worker  * we need to actually do anything yet to restrict how much space it's taking
260*1c60b9acSAndroid Build Coastguard Worker  * up.
261*1c60b9acSAndroid Build Coastguard Worker  *
262*1c60b9acSAndroid Build Coastguard Worker  * And we want to do those things statefully and incrementally instead of one
263*1c60b9acSAndroid Build Coastguard Worker  * big atomic operation, since the user may want a huge cache, so we look in
264*1c60b9acSAndroid Build Coastguard Worker  * one cache dir at a time and track state in the repodir struct.
265*1c60b9acSAndroid Build Coastguard Worker  *
266*1c60b9acSAndroid Build Coastguard Worker  * When we have seen everything, we add the doubly-linked prev pointers and then
267*1c60b9acSAndroid Build Coastguard Worker  * if we are over the limit, start deleting up to BATCH_COUNT files working back
268*1c60b9acSAndroid Build Coastguard Worker  * from the end.
269*1c60b9acSAndroid Build Coastguard Worker  */
270*1c60b9acSAndroid Build Coastguard Worker 
271*1c60b9acSAndroid Build Coastguard Worker int
lws_diskcache_trim(struct lws_diskcache_scan * lds)272*1c60b9acSAndroid Build Coastguard Worker lws_diskcache_trim(struct lws_diskcache_scan *lds)
273*1c60b9acSAndroid Build Coastguard Worker {
274*1c60b9acSAndroid Build Coastguard Worker 	size_t cache_size_limit = (size_t)lds->cache_size_limit;
275*1c60b9acSAndroid Build Coastguard Worker 	char dirpath[132], filepath[132 + 32];
276*1c60b9acSAndroid Build Coastguard Worker 	lws_list_ptr lp, op = NULL;
277*1c60b9acSAndroid Build Coastguard Worker 	int files_trimmed = 0;
278*1c60b9acSAndroid Build Coastguard Worker 	struct file_entry *p;
279*1c60b9acSAndroid Build Coastguard Worker 	int fd, n, ret = -1;
280*1c60b9acSAndroid Build Coastguard Worker 	size_t trimmed = 0;
281*1c60b9acSAndroid Build Coastguard Worker 	struct dirent *de;
282*1c60b9acSAndroid Build Coastguard Worker 	struct stat s;
283*1c60b9acSAndroid Build Coastguard Worker 	DIR *dir;
284*1c60b9acSAndroid Build Coastguard Worker 
285*1c60b9acSAndroid Build Coastguard Worker 	if (!lds->cache_subdir) {
286*1c60b9acSAndroid Build Coastguard Worker 
287*1c60b9acSAndroid Build Coastguard Worker 		if (lds->last_scan_completed + lds->secs_waiting > time(NULL))
288*1c60b9acSAndroid Build Coastguard Worker 			return 0;
289*1c60b9acSAndroid Build Coastguard Worker 
290*1c60b9acSAndroid Build Coastguard Worker 		lds->batch = lws_malloc(sizeof(struct file_entry) *
291*1c60b9acSAndroid Build Coastguard Worker 				BATCH_COUNT, "cache_trim");
292*1c60b9acSAndroid Build Coastguard Worker 		if (!lds->batch) {
293*1c60b9acSAndroid Build Coastguard Worker 			lwsl_err("%s: OOM\n", __func__);
294*1c60b9acSAndroid Build Coastguard Worker 
295*1c60b9acSAndroid Build Coastguard Worker 			return 1;
296*1c60b9acSAndroid Build Coastguard Worker 		}
297*1c60b9acSAndroid Build Coastguard Worker 		lds->agg_size = 0;
298*1c60b9acSAndroid Build Coastguard Worker 		lds->head = NULL;
299*1c60b9acSAndroid Build Coastguard Worker 		lds->batch_in_use = 0;
300*1c60b9acSAndroid Build Coastguard Worker 		lds->agg_file_count = 0;
301*1c60b9acSAndroid Build Coastguard Worker 	}
302*1c60b9acSAndroid Build Coastguard Worker 
303*1c60b9acSAndroid Build Coastguard Worker 	lws_snprintf(dirpath, sizeof(dirpath), "%s/%c/%c",
304*1c60b9acSAndroid Build Coastguard Worker 		     lds->cache_dir_base, hex[(lds->cache_subdir >> 4) & 15],
305*1c60b9acSAndroid Build Coastguard Worker 		     hex[lds->cache_subdir & 15]);
306*1c60b9acSAndroid Build Coastguard Worker 
307*1c60b9acSAndroid Build Coastguard Worker 	dir = opendir(dirpath);
308*1c60b9acSAndroid Build Coastguard Worker 	if (!dir) {
309*1c60b9acSAndroid Build Coastguard Worker 		lwsl_err("Unable to walk repo dir '%s'\n",
310*1c60b9acSAndroid Build Coastguard Worker 			 lds->cache_dir_base);
311*1c60b9acSAndroid Build Coastguard Worker 		return -1;
312*1c60b9acSAndroid Build Coastguard Worker 	}
313*1c60b9acSAndroid Build Coastguard Worker 
314*1c60b9acSAndroid Build Coastguard Worker 	do {
315*1c60b9acSAndroid Build Coastguard Worker 		de = readdir(dir);
316*1c60b9acSAndroid Build Coastguard Worker 		if (!de)
317*1c60b9acSAndroid Build Coastguard Worker 			break;
318*1c60b9acSAndroid Build Coastguard Worker 
319*1c60b9acSAndroid Build Coastguard Worker 		if (de->d_type != DT_REG)
320*1c60b9acSAndroid Build Coastguard Worker 			continue;
321*1c60b9acSAndroid Build Coastguard Worker 
322*1c60b9acSAndroid Build Coastguard Worker 		lds->agg_file_count++;
323*1c60b9acSAndroid Build Coastguard Worker 
324*1c60b9acSAndroid Build Coastguard Worker 		lws_snprintf(filepath, sizeof(filepath), "%s/%s", dirpath,
325*1c60b9acSAndroid Build Coastguard Worker 			     de->d_name);
326*1c60b9acSAndroid Build Coastguard Worker 
327*1c60b9acSAndroid Build Coastguard Worker 		fd = open(filepath, O_RDONLY);
328*1c60b9acSAndroid Build Coastguard Worker 		if (fd < 0) {
329*1c60b9acSAndroid Build Coastguard Worker 			lwsl_err("%s: cannot open %s\n", __func__, filepath);
330*1c60b9acSAndroid Build Coastguard Worker 
331*1c60b9acSAndroid Build Coastguard Worker 			continue;
332*1c60b9acSAndroid Build Coastguard Worker 		}
333*1c60b9acSAndroid Build Coastguard Worker 
334*1c60b9acSAndroid Build Coastguard Worker 		n = fstat(fd, &s);
335*1c60b9acSAndroid Build Coastguard Worker 		close(fd);
336*1c60b9acSAndroid Build Coastguard Worker 		if (n) {
337*1c60b9acSAndroid Build Coastguard Worker 			lwsl_notice("%s: cannot stat %s\n", __func__, filepath);
338*1c60b9acSAndroid Build Coastguard Worker 			continue;
339*1c60b9acSAndroid Build Coastguard Worker 		}
340*1c60b9acSAndroid Build Coastguard Worker 
341*1c60b9acSAndroid Build Coastguard Worker 		lds->agg_size += (uint64_t)s.st_size;
342*1c60b9acSAndroid Build Coastguard Worker 
343*1c60b9acSAndroid Build Coastguard Worker 		if (lds->batch_in_use == BATCH_COUNT) {
344*1c60b9acSAndroid Build Coastguard Worker 			/*
345*1c60b9acSAndroid Build Coastguard Worker 			 * once we filled up the batch with candidates, we don't
346*1c60b9acSAndroid Build Coastguard Worker 			 * need to consider any files newer than the newest guy
347*1c60b9acSAndroid Build Coastguard Worker 			 * on the list...
348*1c60b9acSAndroid Build Coastguard Worker 			 */
349*1c60b9acSAndroid Build Coastguard Worker 			if (lp_to_fe(lds->head, sorted)->modified < s.st_mtime)
350*1c60b9acSAndroid Build Coastguard Worker 				continue;
351*1c60b9acSAndroid Build Coastguard Worker 
352*1c60b9acSAndroid Build Coastguard Worker 			/*
353*1c60b9acSAndroid Build Coastguard Worker 			 * ... and if we find an older file later, we know it
354*1c60b9acSAndroid Build Coastguard Worker 			 * will be replacing the newest guy on the list, so use
355*1c60b9acSAndroid Build Coastguard Worker 			 * that directly...
356*1c60b9acSAndroid Build Coastguard Worker 			 */
357*1c60b9acSAndroid Build Coastguard Worker 			p = lds->head;
358*1c60b9acSAndroid Build Coastguard Worker 			lds->head = p->sorted;
359*1c60b9acSAndroid Build Coastguard Worker 		} else
360*1c60b9acSAndroid Build Coastguard Worker 			/* we are still accepting anything to fill the batch */
361*1c60b9acSAndroid Build Coastguard Worker 
362*1c60b9acSAndroid Build Coastguard Worker 			p = &lds->batch[lds->batch_in_use++];
363*1c60b9acSAndroid Build Coastguard Worker 
364*1c60b9acSAndroid Build Coastguard Worker 		p->sorted = NULL;
365*1c60b9acSAndroid Build Coastguard Worker 		strncpy(p->name, de->d_name, sizeof(p->name) - 1);
366*1c60b9acSAndroid Build Coastguard Worker 		p->name[sizeof(p->name) - 1] = '\0';
367*1c60b9acSAndroid Build Coastguard Worker 		p->modified = s.st_mtime;
368*1c60b9acSAndroid Build Coastguard Worker 		p->size = (size_t)s.st_size;
369*1c60b9acSAndroid Build Coastguard Worker 
370*1c60b9acSAndroid Build Coastguard Worker 		lws_list_ptr_insert(&lds->head, &p->sorted, fe_modified_sort);
371*1c60b9acSAndroid Build Coastguard Worker 	} while (de);
372*1c60b9acSAndroid Build Coastguard Worker 
373*1c60b9acSAndroid Build Coastguard Worker 	ret = 0;
374*1c60b9acSAndroid Build Coastguard Worker 
375*1c60b9acSAndroid Build Coastguard Worker 	lds->cache_subdir++;
376*1c60b9acSAndroid Build Coastguard Worker 	if (lds->cache_subdir != 0x100)
377*1c60b9acSAndroid Build Coastguard Worker 		goto done;
378*1c60b9acSAndroid Build Coastguard Worker 
379*1c60b9acSAndroid Build Coastguard Worker 	/* we completed the whole scan... */
380*1c60b9acSAndroid Build Coastguard Worker 
381*1c60b9acSAndroid Build Coastguard Worker 	/* if really no guidence, then 256MiB */
382*1c60b9acSAndroid Build Coastguard Worker 	if (!cache_size_limit)
383*1c60b9acSAndroid Build Coastguard Worker 		cache_size_limit = 256 * 1024 * 1024;
384*1c60b9acSAndroid Build Coastguard Worker 
385*1c60b9acSAndroid Build Coastguard Worker 	if (lds->agg_size > cache_size_limit) {
386*1c60b9acSAndroid Build Coastguard Worker 
387*1c60b9acSAndroid Build Coastguard Worker 		/* apply prev pointers to make the list doubly-linked */
388*1c60b9acSAndroid Build Coastguard Worker 
389*1c60b9acSAndroid Build Coastguard Worker 		lp = lds->head;
390*1c60b9acSAndroid Build Coastguard Worker 		while (lp) {
391*1c60b9acSAndroid Build Coastguard Worker 			p = lp_to_fe(lp, sorted);
392*1c60b9acSAndroid Build Coastguard Worker 
393*1c60b9acSAndroid Build Coastguard Worker 			p->prev = op;
394*1c60b9acSAndroid Build Coastguard Worker 			op = &p->prev;
395*1c60b9acSAndroid Build Coastguard Worker 			lp = p->sorted;
396*1c60b9acSAndroid Build Coastguard Worker 		}
397*1c60b9acSAndroid Build Coastguard Worker 
398*1c60b9acSAndroid Build Coastguard Worker 		/*
399*1c60b9acSAndroid Build Coastguard Worker 		 * reverse the list (start from tail, now traverse using
400*1c60b9acSAndroid Build Coastguard Worker 		 * .prev)... it's oldest-first now...
401*1c60b9acSAndroid Build Coastguard Worker 		 */
402*1c60b9acSAndroid Build Coastguard Worker 
403*1c60b9acSAndroid Build Coastguard Worker 		lp = op;
404*1c60b9acSAndroid Build Coastguard Worker 
405*1c60b9acSAndroid Build Coastguard Worker 		while (lp && lds->agg_size > cache_size_limit) {
406*1c60b9acSAndroid Build Coastguard Worker 			p = lp_to_fe(lp, prev);
407*1c60b9acSAndroid Build Coastguard Worker 
408*1c60b9acSAndroid Build Coastguard Worker 			lws_snprintf(filepath, sizeof(filepath), "%s/%c/%c/%s",
409*1c60b9acSAndroid Build Coastguard Worker 				     lds->cache_dir_base, p->name[0],
410*1c60b9acSAndroid Build Coastguard Worker 				     p->name[1], p->name);
411*1c60b9acSAndroid Build Coastguard Worker 
412*1c60b9acSAndroid Build Coastguard Worker 			if (!unlink(filepath)) {
413*1c60b9acSAndroid Build Coastguard Worker 				lds->agg_size -= p->size;
414*1c60b9acSAndroid Build Coastguard Worker 				trimmed += p->size;
415*1c60b9acSAndroid Build Coastguard Worker 				files_trimmed++;
416*1c60b9acSAndroid Build Coastguard Worker 			} else
417*1c60b9acSAndroid Build Coastguard Worker 				lwsl_notice("%s: Failed to unlink %s\n",
418*1c60b9acSAndroid Build Coastguard Worker 					    __func__, filepath);
419*1c60b9acSAndroid Build Coastguard Worker 
420*1c60b9acSAndroid Build Coastguard Worker 			lp = p->prev;
421*1c60b9acSAndroid Build Coastguard Worker 		}
422*1c60b9acSAndroid Build Coastguard Worker 
423*1c60b9acSAndroid Build Coastguard Worker 		if (files_trimmed)
424*1c60b9acSAndroid Build Coastguard Worker 			lwsl_notice("%s: %s: trimmed %d files totalling "
425*1c60b9acSAndroid Build Coastguard Worker 				    "%lldKib, leaving %lldMiB\n", __func__,
426*1c60b9acSAndroid Build Coastguard Worker 				    lds->cache_dir_base, files_trimmed,
427*1c60b9acSAndroid Build Coastguard Worker 				    ((unsigned long long)trimmed) / KIB,
428*1c60b9acSAndroid Build Coastguard Worker 				    ((unsigned long long)lds->agg_size) / MIB);
429*1c60b9acSAndroid Build Coastguard Worker 	}
430*1c60b9acSAndroid Build Coastguard Worker 
431*1c60b9acSAndroid Build Coastguard Worker 	if (lds->agg_size && lds->agg_file_count)
432*1c60b9acSAndroid Build Coastguard Worker 		lds->avg_size = lds->agg_size / (uint64_t)lds->agg_file_count;
433*1c60b9acSAndroid Build Coastguard Worker 
434*1c60b9acSAndroid Build Coastguard Worker 	/*
435*1c60b9acSAndroid Build Coastguard Worker 	 * estimate how long we can go before scanning again... default we need
436*1c60b9acSAndroid Build Coastguard Worker 	 * to start again immediately
437*1c60b9acSAndroid Build Coastguard Worker 	 */
438*1c60b9acSAndroid Build Coastguard Worker 
439*1c60b9acSAndroid Build Coastguard Worker 	lds->last_scan_completed = time(NULL);
440*1c60b9acSAndroid Build Coastguard Worker 	lds->secs_waiting = 1;
441*1c60b9acSAndroid Build Coastguard Worker 
442*1c60b9acSAndroid Build Coastguard Worker 	if (lds->agg_size < cache_size_limit) {
443*1c60b9acSAndroid Build Coastguard Worker 		uint64_t avg = 4096, capacity, projected;
444*1c60b9acSAndroid Build Coastguard Worker 
445*1c60b9acSAndroid Build Coastguard Worker 		/* let's use 80% of the real average for margin */
446*1c60b9acSAndroid Build Coastguard Worker 		if (lds->agg_size && lds->agg_file_count)
447*1c60b9acSAndroid Build Coastguard Worker 			avg = ((lds->agg_size * 8) / (uint64_t)lds->agg_file_count) / 10;
448*1c60b9acSAndroid Build Coastguard Worker 
449*1c60b9acSAndroid Build Coastguard Worker 		/*
450*1c60b9acSAndroid Build Coastguard Worker 		 * if we collected BATCH_COUNT files of the average size,
451*1c60b9acSAndroid Build Coastguard Worker 		 * how much can we clean up in 256s?
452*1c60b9acSAndroid Build Coastguard Worker 		 */
453*1c60b9acSAndroid Build Coastguard Worker 
454*1c60b9acSAndroid Build Coastguard Worker 		capacity = avg * BATCH_COUNT;
455*1c60b9acSAndroid Build Coastguard Worker 
456*1c60b9acSAndroid Build Coastguard Worker 		/*
457*1c60b9acSAndroid Build Coastguard Worker 		 * if the cache grew by 10%, would we hit the limit even then?
458*1c60b9acSAndroid Build Coastguard Worker 		 */
459*1c60b9acSAndroid Build Coastguard Worker 		projected = (lds->agg_size * 11) / 10;
460*1c60b9acSAndroid Build Coastguard Worker 		if (projected < cache_size_limit)
461*1c60b9acSAndroid Build Coastguard Worker 			/* no... */
462*1c60b9acSAndroid Build Coastguard Worker 			lds->secs_waiting  = (int)((256 / 2) * ((cache_size_limit -
463*1c60b9acSAndroid Build Coastguard Worker 						    projected) / capacity));
464*1c60b9acSAndroid Build Coastguard Worker 
465*1c60b9acSAndroid Build Coastguard Worker 		/*
466*1c60b9acSAndroid Build Coastguard Worker 		 * large waits imply we may not have enough info yet, so
467*1c60b9acSAndroid Build Coastguard Worker 		 * check once an hour at least.
468*1c60b9acSAndroid Build Coastguard Worker 		 */
469*1c60b9acSAndroid Build Coastguard Worker 
470*1c60b9acSAndroid Build Coastguard Worker 		if (lds->secs_waiting > 3600)
471*1c60b9acSAndroid Build Coastguard Worker 			lds->secs_waiting = 3600;
472*1c60b9acSAndroid Build Coastguard Worker 	} else
473*1c60b9acSAndroid Build Coastguard Worker 		lds->secs_waiting = 0;
474*1c60b9acSAndroid Build Coastguard Worker 
475*1c60b9acSAndroid Build Coastguard Worker 	lwsl_info("%s: cache %s: %lldKiB / %lldKiB, next scan %ds\n", __func__,
476*1c60b9acSAndroid Build Coastguard Worker 		  lds->cache_dir_base,
477*1c60b9acSAndroid Build Coastguard Worker 		  (unsigned long long)lds->agg_size / KIB,
478*1c60b9acSAndroid Build Coastguard Worker 		  (unsigned long long)cache_size_limit / KIB,
479*1c60b9acSAndroid Build Coastguard Worker 		  lds->secs_waiting);
480*1c60b9acSAndroid Build Coastguard Worker 
481*1c60b9acSAndroid Build Coastguard Worker 	lws_free(lds->batch);
482*1c60b9acSAndroid Build Coastguard Worker 	lds->batch = NULL;
483*1c60b9acSAndroid Build Coastguard Worker 
484*1c60b9acSAndroid Build Coastguard Worker 	lds->cache_subdir = 0;
485*1c60b9acSAndroid Build Coastguard Worker 
486*1c60b9acSAndroid Build Coastguard Worker done:
487*1c60b9acSAndroid Build Coastguard Worker 	closedir(dir);
488*1c60b9acSAndroid Build Coastguard Worker 
489*1c60b9acSAndroid Build Coastguard Worker 	return ret;
490*1c60b9acSAndroid Build Coastguard Worker }
491