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