162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Manage high-level VFS aspects of a cache. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2007, 2021 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/statfs.h> 1062306a36Sopenharmony_ci#include <linux/namei.h> 1162306a36Sopenharmony_ci#include "internal.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * Bring a cache online. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ciint cachefiles_add_cache(struct cachefiles_cache *cache) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci struct fscache_cache *cache_cookie; 1962306a36Sopenharmony_ci struct path path; 2062306a36Sopenharmony_ci struct kstatfs stats; 2162306a36Sopenharmony_ci struct dentry *graveyard, *cachedir, *root; 2262306a36Sopenharmony_ci const struct cred *saved_cred; 2362306a36Sopenharmony_ci int ret; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci _enter(""); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci cache_cookie = fscache_acquire_cache(cache->tag); 2862306a36Sopenharmony_ci if (IS_ERR(cache_cookie)) 2962306a36Sopenharmony_ci return PTR_ERR(cache_cookie); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci /* we want to work under the module's security ID */ 3262306a36Sopenharmony_ci ret = cachefiles_get_security_ID(cache); 3362306a36Sopenharmony_ci if (ret < 0) 3462306a36Sopenharmony_ci goto error_getsec; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci cachefiles_begin_secure(cache, &saved_cred); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci /* look up the directory at the root of the cache */ 3962306a36Sopenharmony_ci ret = kern_path(cache->rootdirname, LOOKUP_DIRECTORY, &path); 4062306a36Sopenharmony_ci if (ret < 0) 4162306a36Sopenharmony_ci goto error_open_root; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci cache->mnt = path.mnt; 4462306a36Sopenharmony_ci root = path.dentry; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci ret = -EINVAL; 4762306a36Sopenharmony_ci if (is_idmapped_mnt(path.mnt)) { 4862306a36Sopenharmony_ci pr_warn("File cache on idmapped mounts not supported"); 4962306a36Sopenharmony_ci goto error_unsupported; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* Check features of the backing filesystem: 5362306a36Sopenharmony_ci * - Directories must support looking up and directory creation 5462306a36Sopenharmony_ci * - We create tmpfiles to handle invalidation 5562306a36Sopenharmony_ci * - We use xattrs to store metadata 5662306a36Sopenharmony_ci * - We need to be able to query the amount of space available 5762306a36Sopenharmony_ci * - We want to be able to sync the filesystem when stopping the cache 5862306a36Sopenharmony_ci * - We use DIO to/from pages, so the blocksize mustn't be too big. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci ret = -EOPNOTSUPP; 6162306a36Sopenharmony_ci if (d_is_negative(root) || 6262306a36Sopenharmony_ci !d_backing_inode(root)->i_op->lookup || 6362306a36Sopenharmony_ci !d_backing_inode(root)->i_op->mkdir || 6462306a36Sopenharmony_ci !d_backing_inode(root)->i_op->tmpfile || 6562306a36Sopenharmony_ci !(d_backing_inode(root)->i_opflags & IOP_XATTR) || 6662306a36Sopenharmony_ci !root->d_sb->s_op->statfs || 6762306a36Sopenharmony_ci !root->d_sb->s_op->sync_fs || 6862306a36Sopenharmony_ci root->d_sb->s_blocksize > PAGE_SIZE) 6962306a36Sopenharmony_ci goto error_unsupported; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci ret = -EROFS; 7262306a36Sopenharmony_ci if (sb_rdonly(root->d_sb)) 7362306a36Sopenharmony_ci goto error_unsupported; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* determine the security of the on-disk cache as this governs 7662306a36Sopenharmony_ci * security ID of files we create */ 7762306a36Sopenharmony_ci ret = cachefiles_determine_cache_security(cache, root, &saved_cred); 7862306a36Sopenharmony_ci if (ret < 0) 7962306a36Sopenharmony_ci goto error_unsupported; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* get the cache size and blocksize */ 8262306a36Sopenharmony_ci ret = vfs_statfs(&path, &stats); 8362306a36Sopenharmony_ci if (ret < 0) 8462306a36Sopenharmony_ci goto error_unsupported; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ret = -ERANGE; 8762306a36Sopenharmony_ci if (stats.f_bsize <= 0) 8862306a36Sopenharmony_ci goto error_unsupported; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci ret = -EOPNOTSUPP; 9162306a36Sopenharmony_ci if (stats.f_bsize > PAGE_SIZE) 9262306a36Sopenharmony_ci goto error_unsupported; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci cache->bsize = stats.f_bsize; 9562306a36Sopenharmony_ci cache->bshift = ilog2(stats.f_bsize); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci _debug("blksize %u (shift %u)", 9862306a36Sopenharmony_ci cache->bsize, cache->bshift); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci _debug("size %llu, avail %llu", 10162306a36Sopenharmony_ci (unsigned long long) stats.f_blocks, 10262306a36Sopenharmony_ci (unsigned long long) stats.f_bavail); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* set up caching limits */ 10562306a36Sopenharmony_ci do_div(stats.f_files, 100); 10662306a36Sopenharmony_ci cache->fstop = stats.f_files * cache->fstop_percent; 10762306a36Sopenharmony_ci cache->fcull = stats.f_files * cache->fcull_percent; 10862306a36Sopenharmony_ci cache->frun = stats.f_files * cache->frun_percent; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci _debug("limits {%llu,%llu,%llu} files", 11162306a36Sopenharmony_ci (unsigned long long) cache->frun, 11262306a36Sopenharmony_ci (unsigned long long) cache->fcull, 11362306a36Sopenharmony_ci (unsigned long long) cache->fstop); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci do_div(stats.f_blocks, 100); 11662306a36Sopenharmony_ci cache->bstop = stats.f_blocks * cache->bstop_percent; 11762306a36Sopenharmony_ci cache->bcull = stats.f_blocks * cache->bcull_percent; 11862306a36Sopenharmony_ci cache->brun = stats.f_blocks * cache->brun_percent; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci _debug("limits {%llu,%llu,%llu} blocks", 12162306a36Sopenharmony_ci (unsigned long long) cache->brun, 12262306a36Sopenharmony_ci (unsigned long long) cache->bcull, 12362306a36Sopenharmony_ci (unsigned long long) cache->bstop); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* get the cache directory and check its type */ 12662306a36Sopenharmony_ci cachedir = cachefiles_get_directory(cache, root, "cache", NULL); 12762306a36Sopenharmony_ci if (IS_ERR(cachedir)) { 12862306a36Sopenharmony_ci ret = PTR_ERR(cachedir); 12962306a36Sopenharmony_ci goto error_unsupported; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci cache->store = cachedir; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* get the graveyard directory */ 13562306a36Sopenharmony_ci graveyard = cachefiles_get_directory(cache, root, "graveyard", NULL); 13662306a36Sopenharmony_ci if (IS_ERR(graveyard)) { 13762306a36Sopenharmony_ci ret = PTR_ERR(graveyard); 13862306a36Sopenharmony_ci goto error_unsupported; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci cache->graveyard = graveyard; 14262306a36Sopenharmony_ci cache->cache = cache_cookie; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci ret = fscache_add_cache(cache_cookie, &cachefiles_cache_ops, cache); 14562306a36Sopenharmony_ci if (ret < 0) 14662306a36Sopenharmony_ci goto error_add_cache; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* done */ 14962306a36Sopenharmony_ci set_bit(CACHEFILES_READY, &cache->flags); 15062306a36Sopenharmony_ci dput(root); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci pr_info("File cache on %s registered\n", cache_cookie->name); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* check how much space the cache has */ 15562306a36Sopenharmony_ci cachefiles_has_space(cache, 0, 0, cachefiles_has_space_check); 15662306a36Sopenharmony_ci cachefiles_end_secure(cache, saved_cred); 15762306a36Sopenharmony_ci _leave(" = 0 [%px]", cache->cache); 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cierror_add_cache: 16162306a36Sopenharmony_ci cachefiles_put_directory(cache->graveyard); 16262306a36Sopenharmony_ci cache->graveyard = NULL; 16362306a36Sopenharmony_cierror_unsupported: 16462306a36Sopenharmony_ci cachefiles_put_directory(cache->store); 16562306a36Sopenharmony_ci cache->store = NULL; 16662306a36Sopenharmony_ci mntput(cache->mnt); 16762306a36Sopenharmony_ci cache->mnt = NULL; 16862306a36Sopenharmony_ci dput(root); 16962306a36Sopenharmony_cierror_open_root: 17062306a36Sopenharmony_ci cachefiles_end_secure(cache, saved_cred); 17162306a36Sopenharmony_ci put_cred(cache->cache_cred); 17262306a36Sopenharmony_ci cache->cache_cred = NULL; 17362306a36Sopenharmony_cierror_getsec: 17462306a36Sopenharmony_ci fscache_relinquish_cache(cache_cookie); 17562306a36Sopenharmony_ci cache->cache = NULL; 17662306a36Sopenharmony_ci pr_err("Failed to register: %d\n", ret); 17762306a36Sopenharmony_ci return ret; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* 18162306a36Sopenharmony_ci * See if we have space for a number of pages and/or a number of files in the 18262306a36Sopenharmony_ci * cache 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_ciint cachefiles_has_space(struct cachefiles_cache *cache, 18562306a36Sopenharmony_ci unsigned fnr, unsigned bnr, 18662306a36Sopenharmony_ci enum cachefiles_has_space_for reason) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct kstatfs stats; 18962306a36Sopenharmony_ci u64 b_avail, b_writing; 19062306a36Sopenharmony_ci int ret; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci struct path path = { 19362306a36Sopenharmony_ci .mnt = cache->mnt, 19462306a36Sopenharmony_ci .dentry = cache->mnt->mnt_root, 19562306a36Sopenharmony_ci }; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci //_enter("{%llu,%llu,%llu,%llu,%llu,%llu},%u,%u", 19862306a36Sopenharmony_ci // (unsigned long long) cache->frun, 19962306a36Sopenharmony_ci // (unsigned long long) cache->fcull, 20062306a36Sopenharmony_ci // (unsigned long long) cache->fstop, 20162306a36Sopenharmony_ci // (unsigned long long) cache->brun, 20262306a36Sopenharmony_ci // (unsigned long long) cache->bcull, 20362306a36Sopenharmony_ci // (unsigned long long) cache->bstop, 20462306a36Sopenharmony_ci // fnr, bnr); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* find out how many pages of blockdev are available */ 20762306a36Sopenharmony_ci memset(&stats, 0, sizeof(stats)); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci ret = vfs_statfs(&path, &stats); 21062306a36Sopenharmony_ci if (ret < 0) { 21162306a36Sopenharmony_ci trace_cachefiles_vfs_error(NULL, d_inode(path.dentry), ret, 21262306a36Sopenharmony_ci cachefiles_trace_statfs_error); 21362306a36Sopenharmony_ci if (ret == -EIO) 21462306a36Sopenharmony_ci cachefiles_io_error(cache, "statfs failed"); 21562306a36Sopenharmony_ci _leave(" = %d", ret); 21662306a36Sopenharmony_ci return ret; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci b_avail = stats.f_bavail; 22062306a36Sopenharmony_ci b_writing = atomic_long_read(&cache->b_writing); 22162306a36Sopenharmony_ci if (b_avail > b_writing) 22262306a36Sopenharmony_ci b_avail -= b_writing; 22362306a36Sopenharmony_ci else 22462306a36Sopenharmony_ci b_avail = 0; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci //_debug("avail %llu,%llu", 22762306a36Sopenharmony_ci // (unsigned long long)stats.f_ffree, 22862306a36Sopenharmony_ci // (unsigned long long)b_avail); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* see if there is sufficient space */ 23162306a36Sopenharmony_ci if (stats.f_ffree > fnr) 23262306a36Sopenharmony_ci stats.f_ffree -= fnr; 23362306a36Sopenharmony_ci else 23462306a36Sopenharmony_ci stats.f_ffree = 0; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (b_avail > bnr) 23762306a36Sopenharmony_ci b_avail -= bnr; 23862306a36Sopenharmony_ci else 23962306a36Sopenharmony_ci b_avail = 0; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ret = -ENOBUFS; 24262306a36Sopenharmony_ci if (stats.f_ffree < cache->fstop || 24362306a36Sopenharmony_ci b_avail < cache->bstop) 24462306a36Sopenharmony_ci goto stop_and_begin_cull; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci ret = 0; 24762306a36Sopenharmony_ci if (stats.f_ffree < cache->fcull || 24862306a36Sopenharmony_ci b_avail < cache->bcull) 24962306a36Sopenharmony_ci goto begin_cull; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (test_bit(CACHEFILES_CULLING, &cache->flags) && 25262306a36Sopenharmony_ci stats.f_ffree >= cache->frun && 25362306a36Sopenharmony_ci b_avail >= cache->brun && 25462306a36Sopenharmony_ci test_and_clear_bit(CACHEFILES_CULLING, &cache->flags) 25562306a36Sopenharmony_ci ) { 25662306a36Sopenharmony_ci _debug("cease culling"); 25762306a36Sopenharmony_ci cachefiles_state_changed(cache); 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci //_leave(" = 0"); 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistop_and_begin_cull: 26462306a36Sopenharmony_ci switch (reason) { 26562306a36Sopenharmony_ci case cachefiles_has_space_for_write: 26662306a36Sopenharmony_ci fscache_count_no_write_space(); 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci case cachefiles_has_space_for_create: 26962306a36Sopenharmony_ci fscache_count_no_create_space(); 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci default: 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_cibegin_cull: 27562306a36Sopenharmony_ci if (!test_and_set_bit(CACHEFILES_CULLING, &cache->flags)) { 27662306a36Sopenharmony_ci _debug("### CULL CACHE ###"); 27762306a36Sopenharmony_ci cachefiles_state_changed(cache); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci _leave(" = %d", ret); 28162306a36Sopenharmony_ci return ret; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci/* 28562306a36Sopenharmony_ci * Mark all the objects as being out of service and queue them all for cleanup. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_cistatic void cachefiles_withdraw_objects(struct cachefiles_cache *cache) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct cachefiles_object *object; 29062306a36Sopenharmony_ci unsigned int count = 0; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci _enter(""); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci spin_lock(&cache->object_list_lock); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci while (!list_empty(&cache->object_list)) { 29762306a36Sopenharmony_ci object = list_first_entry(&cache->object_list, 29862306a36Sopenharmony_ci struct cachefiles_object, cache_link); 29962306a36Sopenharmony_ci cachefiles_see_object(object, cachefiles_obj_see_withdrawal); 30062306a36Sopenharmony_ci list_del_init(&object->cache_link); 30162306a36Sopenharmony_ci fscache_withdraw_cookie(object->cookie); 30262306a36Sopenharmony_ci count++; 30362306a36Sopenharmony_ci if ((count & 63) == 0) { 30462306a36Sopenharmony_ci spin_unlock(&cache->object_list_lock); 30562306a36Sopenharmony_ci cond_resched(); 30662306a36Sopenharmony_ci spin_lock(&cache->object_list_lock); 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci spin_unlock(&cache->object_list_lock); 31162306a36Sopenharmony_ci _leave(" [%u objs]", count); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci/* 31562306a36Sopenharmony_ci * Withdraw volumes. 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_cistatic void cachefiles_withdraw_volumes(struct cachefiles_cache *cache) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci _enter(""); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci for (;;) { 32262306a36Sopenharmony_ci struct cachefiles_volume *volume = NULL; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci spin_lock(&cache->object_list_lock); 32562306a36Sopenharmony_ci if (!list_empty(&cache->volumes)) { 32662306a36Sopenharmony_ci volume = list_first_entry(&cache->volumes, 32762306a36Sopenharmony_ci struct cachefiles_volume, cache_link); 32862306a36Sopenharmony_ci list_del_init(&volume->cache_link); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci spin_unlock(&cache->object_list_lock); 33162306a36Sopenharmony_ci if (!volume) 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci cachefiles_withdraw_volume(volume); 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci _leave(""); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci/* 34162306a36Sopenharmony_ci * Sync a cache to backing disk. 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_cistatic void cachefiles_sync_cache(struct cachefiles_cache *cache) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci const struct cred *saved_cred; 34662306a36Sopenharmony_ci int ret; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci _enter("%s", cache->cache->name); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* make sure all pages pinned by operations on behalf of the netfs are 35162306a36Sopenharmony_ci * written to disc */ 35262306a36Sopenharmony_ci cachefiles_begin_secure(cache, &saved_cred); 35362306a36Sopenharmony_ci down_read(&cache->mnt->mnt_sb->s_umount); 35462306a36Sopenharmony_ci ret = sync_filesystem(cache->mnt->mnt_sb); 35562306a36Sopenharmony_ci up_read(&cache->mnt->mnt_sb->s_umount); 35662306a36Sopenharmony_ci cachefiles_end_secure(cache, saved_cred); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (ret == -EIO) 35962306a36Sopenharmony_ci cachefiles_io_error(cache, 36062306a36Sopenharmony_ci "Attempt to sync backing fs superblock returned error %d", 36162306a36Sopenharmony_ci ret); 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci/* 36562306a36Sopenharmony_ci * Withdraw cache objects. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_civoid cachefiles_withdraw_cache(struct cachefiles_cache *cache) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct fscache_cache *fscache = cache->cache; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci pr_info("File cache on %s unregistering\n", fscache->name); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci fscache_withdraw_cache(fscache); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* we now have to destroy all the active objects pertaining to this 37662306a36Sopenharmony_ci * cache - which we do by passing them off to thread pool to be 37762306a36Sopenharmony_ci * disposed of */ 37862306a36Sopenharmony_ci cachefiles_withdraw_objects(cache); 37962306a36Sopenharmony_ci fscache_wait_for_objects(fscache); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci cachefiles_withdraw_volumes(cache); 38262306a36Sopenharmony_ci cachefiles_sync_cache(cache); 38362306a36Sopenharmony_ci cache->cache = NULL; 38462306a36Sopenharmony_ci fscache_relinquish_cache(fscache); 38562306a36Sopenharmony_ci} 386