18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* FS-Cache interface to CacheFiles
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/mount.h>
108c2ecf20Sopenharmony_ci#include "internal.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistruct cachefiles_lookup_data {
138c2ecf20Sopenharmony_ci	struct cachefiles_xattr	*auxdata;	/* auxiliary data */
148c2ecf20Sopenharmony_ci	char			*key;		/* key path */
158c2ecf20Sopenharmony_ci};
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic int cachefiles_attr_changed(struct fscache_object *_object);
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/*
208c2ecf20Sopenharmony_ci * allocate an object record for a cookie lookup and prepare the lookup data
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_cistatic struct fscache_object *cachefiles_alloc_object(
238c2ecf20Sopenharmony_ci	struct fscache_cache *_cache,
248c2ecf20Sopenharmony_ci	struct fscache_cookie *cookie)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	struct cachefiles_lookup_data *lookup_data;
278c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
288c2ecf20Sopenharmony_ci	struct cachefiles_cache *cache;
298c2ecf20Sopenharmony_ci	struct cachefiles_xattr *auxdata;
308c2ecf20Sopenharmony_ci	unsigned keylen, auxlen;
318c2ecf20Sopenharmony_ci	void *buffer, *p;
328c2ecf20Sopenharmony_ci	char *key;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	cache = container_of(_cache, struct cachefiles_cache, cache);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	_enter("{%s},%p,", cache->cache.identifier, cookie);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	lookup_data = kmalloc(sizeof(*lookup_data), cachefiles_gfp);
398c2ecf20Sopenharmony_ci	if (!lookup_data)
408c2ecf20Sopenharmony_ci		goto nomem_lookup_data;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/* create a new object record and a temporary leaf image */
438c2ecf20Sopenharmony_ci	object = kmem_cache_alloc(cachefiles_object_jar, cachefiles_gfp);
448c2ecf20Sopenharmony_ci	if (!object)
458c2ecf20Sopenharmony_ci		goto nomem_object;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	ASSERTCMP(object->backer, ==, NULL);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	BUG_ON(test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags));
508c2ecf20Sopenharmony_ci	atomic_set(&object->usage, 1);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	fscache_object_init(&object->fscache, cookie, &cache->cache);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	object->type = cookie->def->type;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	/* get hold of the raw key
578c2ecf20Sopenharmony_ci	 * - stick the length on the front and leave space on the back for the
588c2ecf20Sopenharmony_ci	 *   encoder
598c2ecf20Sopenharmony_ci	 */
608c2ecf20Sopenharmony_ci	buffer = kmalloc((2 + 512) + 3, cachefiles_gfp);
618c2ecf20Sopenharmony_ci	if (!buffer)
628c2ecf20Sopenharmony_ci		goto nomem_buffer;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	keylen = cookie->key_len;
658c2ecf20Sopenharmony_ci	if (keylen <= sizeof(cookie->inline_key))
668c2ecf20Sopenharmony_ci		p = cookie->inline_key;
678c2ecf20Sopenharmony_ci	else
688c2ecf20Sopenharmony_ci		p = cookie->key;
698c2ecf20Sopenharmony_ci	memcpy(buffer + 2, p, keylen);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	*(uint16_t *)buffer = keylen;
728c2ecf20Sopenharmony_ci	((char *)buffer)[keylen + 2] = 0;
738c2ecf20Sopenharmony_ci	((char *)buffer)[keylen + 3] = 0;
748c2ecf20Sopenharmony_ci	((char *)buffer)[keylen + 4] = 0;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/* turn the raw key into something that can work with as a filename */
778c2ecf20Sopenharmony_ci	key = cachefiles_cook_key(buffer, keylen + 2, object->type);
788c2ecf20Sopenharmony_ci	if (!key)
798c2ecf20Sopenharmony_ci		goto nomem_key;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	/* get hold of the auxiliary data and prepend the object type */
828c2ecf20Sopenharmony_ci	auxdata = buffer;
838c2ecf20Sopenharmony_ci	auxlen = cookie->aux_len;
848c2ecf20Sopenharmony_ci	if (auxlen) {
858c2ecf20Sopenharmony_ci		if (auxlen <= sizeof(cookie->inline_aux))
868c2ecf20Sopenharmony_ci			p = cookie->inline_aux;
878c2ecf20Sopenharmony_ci		else
888c2ecf20Sopenharmony_ci			p = cookie->aux;
898c2ecf20Sopenharmony_ci		memcpy(auxdata->data, p, auxlen);
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	auxdata->len = auxlen + 1;
938c2ecf20Sopenharmony_ci	auxdata->type = cookie->type;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	lookup_data->auxdata = auxdata;
968c2ecf20Sopenharmony_ci	lookup_data->key = key;
978c2ecf20Sopenharmony_ci	object->lookup_data = lookup_data;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	_leave(" = %p [%p]", &object->fscache, lookup_data);
1008c2ecf20Sopenharmony_ci	return &object->fscache;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cinomem_key:
1038c2ecf20Sopenharmony_ci	kfree(buffer);
1048c2ecf20Sopenharmony_cinomem_buffer:
1058c2ecf20Sopenharmony_ci	BUG_ON(test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags));
1068c2ecf20Sopenharmony_ci	kmem_cache_free(cachefiles_object_jar, object);
1078c2ecf20Sopenharmony_ci	fscache_object_destroyed(&cache->cache);
1088c2ecf20Sopenharmony_cinomem_object:
1098c2ecf20Sopenharmony_ci	kfree(lookup_data);
1108c2ecf20Sopenharmony_cinomem_lookup_data:
1118c2ecf20Sopenharmony_ci	_leave(" = -ENOMEM");
1128c2ecf20Sopenharmony_ci	return ERR_PTR(-ENOMEM);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/*
1168c2ecf20Sopenharmony_ci * attempt to look up the nominated node in this cache
1178c2ecf20Sopenharmony_ci * - return -ETIMEDOUT to be scheduled again
1188c2ecf20Sopenharmony_ci */
1198c2ecf20Sopenharmony_cistatic int cachefiles_lookup_object(struct fscache_object *_object)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct cachefiles_lookup_data *lookup_data;
1228c2ecf20Sopenharmony_ci	struct cachefiles_object *parent, *object;
1238c2ecf20Sopenharmony_ci	struct cachefiles_cache *cache;
1248c2ecf20Sopenharmony_ci	const struct cred *saved_cred;
1258c2ecf20Sopenharmony_ci	int ret;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	_enter("{OBJ%x}", _object->debug_id);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	cache = container_of(_object->cache, struct cachefiles_cache, cache);
1308c2ecf20Sopenharmony_ci	parent = container_of(_object->parent,
1318c2ecf20Sopenharmony_ci			      struct cachefiles_object, fscache);
1328c2ecf20Sopenharmony_ci	object = container_of(_object, struct cachefiles_object, fscache);
1338c2ecf20Sopenharmony_ci	lookup_data = object->lookup_data;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	ASSERTCMP(lookup_data, !=, NULL);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* look up the key, creating any missing bits */
1388c2ecf20Sopenharmony_ci	cachefiles_begin_secure(cache, &saved_cred);
1398c2ecf20Sopenharmony_ci	ret = cachefiles_walk_to_object(parent, object,
1408c2ecf20Sopenharmony_ci					lookup_data->key,
1418c2ecf20Sopenharmony_ci					lookup_data->auxdata);
1428c2ecf20Sopenharmony_ci	cachefiles_end_secure(cache, saved_cred);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* polish off by setting the attributes of non-index files */
1458c2ecf20Sopenharmony_ci	if (ret == 0 &&
1468c2ecf20Sopenharmony_ci	    object->fscache.cookie->def->type != FSCACHE_COOKIE_TYPE_INDEX)
1478c2ecf20Sopenharmony_ci		cachefiles_attr_changed(&object->fscache);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (ret < 0 && ret != -ETIMEDOUT) {
1508c2ecf20Sopenharmony_ci		if (ret != -ENOBUFS)
1518c2ecf20Sopenharmony_ci			pr_warn("Lookup failed error %d\n", ret);
1528c2ecf20Sopenharmony_ci		fscache_object_lookup_error(&object->fscache);
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	_leave(" [%d]", ret);
1568c2ecf20Sopenharmony_ci	return ret;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/*
1608c2ecf20Sopenharmony_ci * indication of lookup completion
1618c2ecf20Sopenharmony_ci */
1628c2ecf20Sopenharmony_cistatic void cachefiles_lookup_complete(struct fscache_object *_object)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	object = container_of(_object, struct cachefiles_object, fscache);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	_enter("{OBJ%x,%p}", object->fscache.debug_id, object->lookup_data);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if (object->lookup_data) {
1718c2ecf20Sopenharmony_ci		kfree(object->lookup_data->key);
1728c2ecf20Sopenharmony_ci		kfree(object->lookup_data->auxdata);
1738c2ecf20Sopenharmony_ci		kfree(object->lookup_data);
1748c2ecf20Sopenharmony_ci		object->lookup_data = NULL;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/*
1798c2ecf20Sopenharmony_ci * increment the usage count on an inode object (may fail if unmounting)
1808c2ecf20Sopenharmony_ci */
1818c2ecf20Sopenharmony_cistatic
1828c2ecf20Sopenharmony_cistruct fscache_object *cachefiles_grab_object(struct fscache_object *_object,
1838c2ecf20Sopenharmony_ci					      enum fscache_obj_ref_trace why)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct cachefiles_object *object =
1868c2ecf20Sopenharmony_ci		container_of(_object, struct cachefiles_object, fscache);
1878c2ecf20Sopenharmony_ci	int u;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	_enter("{OBJ%x,%d}", _object->debug_id, atomic_read(&object->usage));
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci#ifdef CACHEFILES_DEBUG_SLAB
1928c2ecf20Sopenharmony_ci	ASSERT((atomic_read(&object->usage) & 0xffff0000) != 0x6b6b0000);
1938c2ecf20Sopenharmony_ci#endif
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	u = atomic_inc_return(&object->usage);
1968c2ecf20Sopenharmony_ci	trace_cachefiles_ref(object, _object->cookie,
1978c2ecf20Sopenharmony_ci			     (enum cachefiles_obj_ref_trace)why, u);
1988c2ecf20Sopenharmony_ci	return &object->fscache;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/*
2028c2ecf20Sopenharmony_ci * update the auxiliary data for an object object on disk
2038c2ecf20Sopenharmony_ci */
2048c2ecf20Sopenharmony_cistatic void cachefiles_update_object(struct fscache_object *_object)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
2078c2ecf20Sopenharmony_ci	struct cachefiles_xattr *auxdata;
2088c2ecf20Sopenharmony_ci	struct cachefiles_cache *cache;
2098c2ecf20Sopenharmony_ci	struct fscache_cookie *cookie;
2108c2ecf20Sopenharmony_ci	const struct cred *saved_cred;
2118c2ecf20Sopenharmony_ci	const void *aux;
2128c2ecf20Sopenharmony_ci	unsigned auxlen;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	_enter("{OBJ%x}", _object->debug_id);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	object = container_of(_object, struct cachefiles_object, fscache);
2178c2ecf20Sopenharmony_ci	cache = container_of(object->fscache.cache, struct cachefiles_cache,
2188c2ecf20Sopenharmony_ci			     cache);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (!fscache_use_cookie(_object)) {
2218c2ecf20Sopenharmony_ci		_leave(" [relinq]");
2228c2ecf20Sopenharmony_ci		return;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	cookie = object->fscache.cookie;
2268c2ecf20Sopenharmony_ci	auxlen = cookie->aux_len;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (!auxlen) {
2298c2ecf20Sopenharmony_ci		fscache_unuse_cookie(_object);
2308c2ecf20Sopenharmony_ci		_leave(" [no aux]");
2318c2ecf20Sopenharmony_ci		return;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	auxdata = kmalloc(2 + auxlen + 3, cachefiles_gfp);
2358c2ecf20Sopenharmony_ci	if (!auxdata) {
2368c2ecf20Sopenharmony_ci		fscache_unuse_cookie(_object);
2378c2ecf20Sopenharmony_ci		_leave(" [nomem]");
2388c2ecf20Sopenharmony_ci		return;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	aux = (auxlen <= sizeof(cookie->inline_aux)) ?
2428c2ecf20Sopenharmony_ci		cookie->inline_aux : cookie->aux;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	memcpy(auxdata->data, aux, auxlen);
2458c2ecf20Sopenharmony_ci	fscache_unuse_cookie(_object);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	auxdata->len = auxlen + 1;
2488c2ecf20Sopenharmony_ci	auxdata->type = cookie->type;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	cachefiles_begin_secure(cache, &saved_cred);
2518c2ecf20Sopenharmony_ci	cachefiles_update_object_xattr(object, auxdata);
2528c2ecf20Sopenharmony_ci	cachefiles_end_secure(cache, saved_cred);
2538c2ecf20Sopenharmony_ci	kfree(auxdata);
2548c2ecf20Sopenharmony_ci	_leave("");
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci/*
2588c2ecf20Sopenharmony_ci * discard the resources pinned by an object and effect retirement if
2598c2ecf20Sopenharmony_ci * requested
2608c2ecf20Sopenharmony_ci */
2618c2ecf20Sopenharmony_cistatic void cachefiles_drop_object(struct fscache_object *_object)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
2648c2ecf20Sopenharmony_ci	struct cachefiles_cache *cache;
2658c2ecf20Sopenharmony_ci	const struct cred *saved_cred;
2668c2ecf20Sopenharmony_ci	struct inode *inode;
2678c2ecf20Sopenharmony_ci	blkcnt_t i_blocks = 0;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	ASSERT(_object);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	object = container_of(_object, struct cachefiles_object, fscache);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	_enter("{OBJ%x,%d}",
2748c2ecf20Sopenharmony_ci	       object->fscache.debug_id, atomic_read(&object->usage));
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	cache = container_of(object->fscache.cache,
2778c2ecf20Sopenharmony_ci			     struct cachefiles_cache, cache);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci#ifdef CACHEFILES_DEBUG_SLAB
2808c2ecf20Sopenharmony_ci	ASSERT((atomic_read(&object->usage) & 0xffff0000) != 0x6b6b0000);
2818c2ecf20Sopenharmony_ci#endif
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/* We need to tidy the object up if we did in fact manage to open it.
2848c2ecf20Sopenharmony_ci	 * It's possible for us to get here before the object is fully
2858c2ecf20Sopenharmony_ci	 * initialised if the parent goes away or the object gets retired
2868c2ecf20Sopenharmony_ci	 * before we set it up.
2878c2ecf20Sopenharmony_ci	 */
2888c2ecf20Sopenharmony_ci	if (object->dentry) {
2898c2ecf20Sopenharmony_ci		/* delete retired objects */
2908c2ecf20Sopenharmony_ci		if (test_bit(FSCACHE_OBJECT_RETIRED, &object->fscache.flags) &&
2918c2ecf20Sopenharmony_ci		    _object != cache->cache.fsdef
2928c2ecf20Sopenharmony_ci		    ) {
2938c2ecf20Sopenharmony_ci			_debug("- retire object OBJ%x", object->fscache.debug_id);
2948c2ecf20Sopenharmony_ci			inode = d_backing_inode(object->dentry);
2958c2ecf20Sopenharmony_ci			if (inode)
2968c2ecf20Sopenharmony_ci				i_blocks = inode->i_blocks;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci			cachefiles_begin_secure(cache, &saved_cred);
2998c2ecf20Sopenharmony_ci			cachefiles_delete_object(cache, object);
3008c2ecf20Sopenharmony_ci			cachefiles_end_secure(cache, saved_cred);
3018c2ecf20Sopenharmony_ci		}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		/* close the filesystem stuff attached to the object */
3048c2ecf20Sopenharmony_ci		if (object->backer != object->dentry)
3058c2ecf20Sopenharmony_ci			dput(object->backer);
3068c2ecf20Sopenharmony_ci		object->backer = NULL;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* note that the object is now inactive */
3108c2ecf20Sopenharmony_ci	if (test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags))
3118c2ecf20Sopenharmony_ci		cachefiles_mark_object_inactive(cache, object, i_blocks);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	dput(object->dentry);
3148c2ecf20Sopenharmony_ci	object->dentry = NULL;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	_leave("");
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci/*
3208c2ecf20Sopenharmony_ci * dispose of a reference to an object
3218c2ecf20Sopenharmony_ci */
3228c2ecf20Sopenharmony_cistatic void cachefiles_put_object(struct fscache_object *_object,
3238c2ecf20Sopenharmony_ci				  enum fscache_obj_ref_trace why)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
3268c2ecf20Sopenharmony_ci	struct fscache_cache *cache;
3278c2ecf20Sopenharmony_ci	int u;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	ASSERT(_object);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	object = container_of(_object, struct cachefiles_object, fscache);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	_enter("{OBJ%x,%d}",
3348c2ecf20Sopenharmony_ci	       object->fscache.debug_id, atomic_read(&object->usage));
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci#ifdef CACHEFILES_DEBUG_SLAB
3378c2ecf20Sopenharmony_ci	ASSERT((atomic_read(&object->usage) & 0xffff0000) != 0x6b6b0000);
3388c2ecf20Sopenharmony_ci#endif
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	ASSERTIFCMP(object->fscache.parent,
3418c2ecf20Sopenharmony_ci		    object->fscache.parent->n_children, >, 0);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	u = atomic_dec_return(&object->usage);
3448c2ecf20Sopenharmony_ci	trace_cachefiles_ref(object, _object->cookie,
3458c2ecf20Sopenharmony_ci			     (enum cachefiles_obj_ref_trace)why, u);
3468c2ecf20Sopenharmony_ci	ASSERTCMP(u, !=, -1);
3478c2ecf20Sopenharmony_ci	if (u == 0) {
3488c2ecf20Sopenharmony_ci		_debug("- kill object OBJ%x", object->fscache.debug_id);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		ASSERT(!test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags));
3518c2ecf20Sopenharmony_ci		ASSERTCMP(object->fscache.parent, ==, NULL);
3528c2ecf20Sopenharmony_ci		ASSERTCMP(object->backer, ==, NULL);
3538c2ecf20Sopenharmony_ci		ASSERTCMP(object->dentry, ==, NULL);
3548c2ecf20Sopenharmony_ci		ASSERTCMP(object->fscache.n_ops, ==, 0);
3558c2ecf20Sopenharmony_ci		ASSERTCMP(object->fscache.n_children, ==, 0);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci		if (object->lookup_data) {
3588c2ecf20Sopenharmony_ci			kfree(object->lookup_data->key);
3598c2ecf20Sopenharmony_ci			kfree(object->lookup_data->auxdata);
3608c2ecf20Sopenharmony_ci			kfree(object->lookup_data);
3618c2ecf20Sopenharmony_ci			object->lookup_data = NULL;
3628c2ecf20Sopenharmony_ci		}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		cache = object->fscache.cache;
3658c2ecf20Sopenharmony_ci		fscache_object_destroy(&object->fscache);
3668c2ecf20Sopenharmony_ci		kmem_cache_free(cachefiles_object_jar, object);
3678c2ecf20Sopenharmony_ci		fscache_object_destroyed(cache);
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	_leave("");
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci/*
3748c2ecf20Sopenharmony_ci * sync a cache
3758c2ecf20Sopenharmony_ci */
3768c2ecf20Sopenharmony_cistatic void cachefiles_sync_cache(struct fscache_cache *_cache)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	struct cachefiles_cache *cache;
3798c2ecf20Sopenharmony_ci	const struct cred *saved_cred;
3808c2ecf20Sopenharmony_ci	int ret;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	_enter("%p", _cache);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	cache = container_of(_cache, struct cachefiles_cache, cache);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/* make sure all pages pinned by operations on behalf of the netfs are
3878c2ecf20Sopenharmony_ci	 * written to disc */
3888c2ecf20Sopenharmony_ci	cachefiles_begin_secure(cache, &saved_cred);
3898c2ecf20Sopenharmony_ci	down_read(&cache->mnt->mnt_sb->s_umount);
3908c2ecf20Sopenharmony_ci	ret = sync_filesystem(cache->mnt->mnt_sb);
3918c2ecf20Sopenharmony_ci	up_read(&cache->mnt->mnt_sb->s_umount);
3928c2ecf20Sopenharmony_ci	cachefiles_end_secure(cache, saved_cred);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (ret == -EIO)
3958c2ecf20Sopenharmony_ci		cachefiles_io_error(cache,
3968c2ecf20Sopenharmony_ci				    "Attempt to sync backing fs superblock"
3978c2ecf20Sopenharmony_ci				    " returned error %d",
3988c2ecf20Sopenharmony_ci				    ret);
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci/*
4028c2ecf20Sopenharmony_ci * check if the backing cache is updated to FS-Cache
4038c2ecf20Sopenharmony_ci * - called by FS-Cache when evaluates if need to invalidate the cache
4048c2ecf20Sopenharmony_ci */
4058c2ecf20Sopenharmony_cistatic int cachefiles_check_consistency(struct fscache_operation *op)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
4088c2ecf20Sopenharmony_ci	struct cachefiles_cache *cache;
4098c2ecf20Sopenharmony_ci	const struct cred *saved_cred;
4108c2ecf20Sopenharmony_ci	int ret;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	_enter("{OBJ%x}", op->object->debug_id);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	object = container_of(op->object, struct cachefiles_object, fscache);
4158c2ecf20Sopenharmony_ci	cache = container_of(object->fscache.cache,
4168c2ecf20Sopenharmony_ci			     struct cachefiles_cache, cache);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	cachefiles_begin_secure(cache, &saved_cred);
4198c2ecf20Sopenharmony_ci	ret = cachefiles_check_auxdata(object);
4208c2ecf20Sopenharmony_ci	cachefiles_end_secure(cache, saved_cred);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
4238c2ecf20Sopenharmony_ci	return ret;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci/*
4278c2ecf20Sopenharmony_ci * notification the attributes on an object have changed
4288c2ecf20Sopenharmony_ci * - called with reads/writes excluded by FS-Cache
4298c2ecf20Sopenharmony_ci */
4308c2ecf20Sopenharmony_cistatic int cachefiles_attr_changed(struct fscache_object *_object)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
4338c2ecf20Sopenharmony_ci	struct cachefiles_cache *cache;
4348c2ecf20Sopenharmony_ci	const struct cred *saved_cred;
4358c2ecf20Sopenharmony_ci	struct iattr newattrs;
4368c2ecf20Sopenharmony_ci	uint64_t ni_size;
4378c2ecf20Sopenharmony_ci	loff_t oi_size;
4388c2ecf20Sopenharmony_ci	int ret;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	ni_size = _object->store_limit_l;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	_enter("{OBJ%x},[%llu]",
4438c2ecf20Sopenharmony_ci	       _object->debug_id, (unsigned long long) ni_size);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	object = container_of(_object, struct cachefiles_object, fscache);
4468c2ecf20Sopenharmony_ci	cache = container_of(object->fscache.cache,
4478c2ecf20Sopenharmony_ci			     struct cachefiles_cache, cache);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	if (ni_size == object->i_size)
4508c2ecf20Sopenharmony_ci		return 0;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	if (!object->backer)
4538c2ecf20Sopenharmony_ci		return -ENOBUFS;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	ASSERT(d_is_reg(object->backer));
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	fscache_set_store_limit(&object->fscache, ni_size);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	oi_size = i_size_read(d_backing_inode(object->backer));
4608c2ecf20Sopenharmony_ci	if (oi_size == ni_size)
4618c2ecf20Sopenharmony_ci		return 0;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	cachefiles_begin_secure(cache, &saved_cred);
4648c2ecf20Sopenharmony_ci	inode_lock(d_inode(object->backer));
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	/* if there's an extension to a partial page at the end of the backing
4678c2ecf20Sopenharmony_ci	 * file, we need to discard the partial page so that we pick up new
4688c2ecf20Sopenharmony_ci	 * data after it */
4698c2ecf20Sopenharmony_ci	if (oi_size & ~PAGE_MASK && ni_size > oi_size) {
4708c2ecf20Sopenharmony_ci		_debug("discard tail %llx", oi_size);
4718c2ecf20Sopenharmony_ci		newattrs.ia_valid = ATTR_SIZE;
4728c2ecf20Sopenharmony_ci		newattrs.ia_size = oi_size & PAGE_MASK;
4738c2ecf20Sopenharmony_ci		ret = notify_change(object->backer, &newattrs, NULL);
4748c2ecf20Sopenharmony_ci		if (ret < 0)
4758c2ecf20Sopenharmony_ci			goto truncate_failed;
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	newattrs.ia_valid = ATTR_SIZE;
4798c2ecf20Sopenharmony_ci	newattrs.ia_size = ni_size;
4808c2ecf20Sopenharmony_ci	ret = notify_change(object->backer, &newattrs, NULL);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_citruncate_failed:
4838c2ecf20Sopenharmony_ci	inode_unlock(d_inode(object->backer));
4848c2ecf20Sopenharmony_ci	cachefiles_end_secure(cache, saved_cred);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (ret == -EIO) {
4878c2ecf20Sopenharmony_ci		fscache_set_store_limit(&object->fscache, 0);
4888c2ecf20Sopenharmony_ci		cachefiles_io_error_obj(object, "Size set failed");
4898c2ecf20Sopenharmony_ci		ret = -ENOBUFS;
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	_leave(" = %d", ret);
4938c2ecf20Sopenharmony_ci	return ret;
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci/*
4978c2ecf20Sopenharmony_ci * Invalidate an object
4988c2ecf20Sopenharmony_ci */
4998c2ecf20Sopenharmony_cistatic void cachefiles_invalidate_object(struct fscache_operation *op)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	struct cachefiles_object *object;
5028c2ecf20Sopenharmony_ci	struct cachefiles_cache *cache;
5038c2ecf20Sopenharmony_ci	const struct cred *saved_cred;
5048c2ecf20Sopenharmony_ci	struct path path;
5058c2ecf20Sopenharmony_ci	uint64_t ni_size;
5068c2ecf20Sopenharmony_ci	int ret;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	object = container_of(op->object, struct cachefiles_object, fscache);
5098c2ecf20Sopenharmony_ci	cache = container_of(object->fscache.cache,
5108c2ecf20Sopenharmony_ci			     struct cachefiles_cache, cache);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	ni_size = op->object->store_limit_l;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	_enter("{OBJ%x},[%llu]",
5158c2ecf20Sopenharmony_ci	       op->object->debug_id, (unsigned long long)ni_size);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (object->backer) {
5188c2ecf20Sopenharmony_ci		ASSERT(d_is_reg(object->backer));
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci		fscache_set_store_limit(&object->fscache, ni_size);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci		path.dentry = object->backer;
5238c2ecf20Sopenharmony_ci		path.mnt = cache->mnt;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci		cachefiles_begin_secure(cache, &saved_cred);
5268c2ecf20Sopenharmony_ci		ret = vfs_truncate(&path, 0);
5278c2ecf20Sopenharmony_ci		if (ret == 0)
5288c2ecf20Sopenharmony_ci			ret = vfs_truncate(&path, ni_size);
5298c2ecf20Sopenharmony_ci		cachefiles_end_secure(cache, saved_cred);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci		if (ret != 0) {
5328c2ecf20Sopenharmony_ci			fscache_set_store_limit(&object->fscache, 0);
5338c2ecf20Sopenharmony_ci			if (ret == -EIO)
5348c2ecf20Sopenharmony_ci				cachefiles_io_error_obj(object,
5358c2ecf20Sopenharmony_ci							"Invalidate failed");
5368c2ecf20Sopenharmony_ci		}
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	fscache_op_complete(op, true);
5408c2ecf20Sopenharmony_ci	_leave("");
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci/*
5448c2ecf20Sopenharmony_ci * dissociate a cache from all the pages it was backing
5458c2ecf20Sopenharmony_ci */
5468c2ecf20Sopenharmony_cistatic void cachefiles_dissociate_pages(struct fscache_cache *cache)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	_enter("");
5498c2ecf20Sopenharmony_ci}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ciconst struct fscache_cache_ops cachefiles_cache_ops = {
5528c2ecf20Sopenharmony_ci	.name			= "cachefiles",
5538c2ecf20Sopenharmony_ci	.alloc_object		= cachefiles_alloc_object,
5548c2ecf20Sopenharmony_ci	.lookup_object		= cachefiles_lookup_object,
5558c2ecf20Sopenharmony_ci	.lookup_complete	= cachefiles_lookup_complete,
5568c2ecf20Sopenharmony_ci	.grab_object		= cachefiles_grab_object,
5578c2ecf20Sopenharmony_ci	.update_object		= cachefiles_update_object,
5588c2ecf20Sopenharmony_ci	.invalidate_object	= cachefiles_invalidate_object,
5598c2ecf20Sopenharmony_ci	.drop_object		= cachefiles_drop_object,
5608c2ecf20Sopenharmony_ci	.put_object		= cachefiles_put_object,
5618c2ecf20Sopenharmony_ci	.sync_cache		= cachefiles_sync_cache,
5628c2ecf20Sopenharmony_ci	.attr_changed		= cachefiles_attr_changed,
5638c2ecf20Sopenharmony_ci	.read_or_alloc_page	= cachefiles_read_or_alloc_page,
5648c2ecf20Sopenharmony_ci	.read_or_alloc_pages	= cachefiles_read_or_alloc_pages,
5658c2ecf20Sopenharmony_ci	.allocate_page		= cachefiles_allocate_page,
5668c2ecf20Sopenharmony_ci	.allocate_pages		= cachefiles_allocate_pages,
5678c2ecf20Sopenharmony_ci	.write_page		= cachefiles_write_page,
5688c2ecf20Sopenharmony_ci	.uncache_page		= cachefiles_uncache_page,
5698c2ecf20Sopenharmony_ci	.dissociate_pages	= cachefiles_dissociate_pages,
5708c2ecf20Sopenharmony_ci	.check_consistency	= cachefiles_check_consistency,
5718c2ecf20Sopenharmony_ci};
572