1// SPDX-License-Identifier: GPL-2.0-or-later
2/* CacheFiles extended attribute management
3 *
4 * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
5 * Written by David Howells (dhowells@redhat.com)
6 */
7
8#include <linux/module.h>
9#include <linux/sched.h>
10#include <linux/file.h>
11#include <linux/fs.h>
12#include <linux/fsnotify.h>
13#include <linux/quotaops.h>
14#include <linux/xattr.h>
15#include <linux/slab.h>
16#include "internal.h"
17
18#define CACHEFILES_COOKIE_TYPE_DATA 1
19
20struct cachefiles_xattr {
21	__be64	object_size;	/* Actual size of the object */
22	__be64	zero_point;	/* Size after which server has no data not written by us */
23	__u8	type;		/* Type of object */
24	__u8	content;	/* Content presence (enum cachefiles_content) */
25	__u8	data[];		/* netfs coherency data */
26} __packed;
27
28static const char cachefiles_xattr_cache[] =
29	XATTR_USER_PREFIX "CacheFiles.cache";
30
31struct cachefiles_vol_xattr {
32	__be32	reserved;	/* Reserved, should be 0 */
33	__u8	data[];		/* netfs volume coherency data */
34} __packed;
35
36/*
37 * set the state xattr on a cache file
38 */
39int cachefiles_set_object_xattr(struct cachefiles_object *object)
40{
41	struct cachefiles_xattr *buf;
42	struct dentry *dentry;
43	struct file *file = object->file;
44	unsigned int len = object->cookie->aux_len;
45	int ret;
46
47	if (!file)
48		return -ESTALE;
49	dentry = file->f_path.dentry;
50
51	_enter("%x,#%d", object->debug_id, len);
52
53	buf = kmalloc(sizeof(struct cachefiles_xattr) + len, GFP_KERNEL);
54	if (!buf)
55		return -ENOMEM;
56
57	buf->object_size	= cpu_to_be64(object->cookie->object_size);
58	buf->zero_point		= 0;
59	buf->type		= CACHEFILES_COOKIE_TYPE_DATA;
60	buf->content		= object->content_info;
61	if (test_bit(FSCACHE_COOKIE_LOCAL_WRITE, &object->cookie->flags))
62		buf->content	= CACHEFILES_CONTENT_DIRTY;
63	if (len > 0)
64		memcpy(buf->data, fscache_get_aux(object->cookie), len);
65
66	ret = cachefiles_inject_write_error();
67	if (ret == 0)
68		ret = vfs_setxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache,
69				   buf, sizeof(struct cachefiles_xattr) + len, 0);
70	if (ret < 0) {
71		trace_cachefiles_vfs_error(object, file_inode(file), ret,
72					   cachefiles_trace_setxattr_error);
73		trace_cachefiles_coherency(object, file_inode(file)->i_ino,
74					   buf->content,
75					   cachefiles_coherency_set_fail);
76		if (ret != -ENOMEM)
77			cachefiles_io_error_obj(
78				object,
79				"Failed to set xattr with error %d", ret);
80	} else {
81		trace_cachefiles_coherency(object, file_inode(file)->i_ino,
82					   buf->content,
83					   cachefiles_coherency_set_ok);
84	}
85
86	kfree(buf);
87	_leave(" = %d", ret);
88	return ret;
89}
90
91/*
92 * check the consistency between the backing cache and the FS-Cache cookie
93 */
94int cachefiles_check_auxdata(struct cachefiles_object *object, struct file *file)
95{
96	struct cachefiles_xattr *buf;
97	struct dentry *dentry = file->f_path.dentry;
98	unsigned int len = object->cookie->aux_len, tlen;
99	const void *p = fscache_get_aux(object->cookie);
100	enum cachefiles_coherency_trace why;
101	ssize_t xlen;
102	int ret = -ESTALE;
103
104	tlen = sizeof(struct cachefiles_xattr) + len;
105	buf = kmalloc(tlen, GFP_KERNEL);
106	if (!buf)
107		return -ENOMEM;
108
109	xlen = cachefiles_inject_read_error();
110	if (xlen == 0)
111		xlen = vfs_getxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache, buf, tlen);
112	if (xlen != tlen) {
113		if (xlen < 0)
114			trace_cachefiles_vfs_error(object, file_inode(file), xlen,
115						   cachefiles_trace_getxattr_error);
116		if (xlen == -EIO)
117			cachefiles_io_error_obj(
118				object,
119				"Failed to read aux with error %zd", xlen);
120		why = cachefiles_coherency_check_xattr;
121	} else if (buf->type != CACHEFILES_COOKIE_TYPE_DATA) {
122		why = cachefiles_coherency_check_type;
123	} else if (memcmp(buf->data, p, len) != 0) {
124		why = cachefiles_coherency_check_aux;
125	} else if (be64_to_cpu(buf->object_size) != object->cookie->object_size) {
126		why = cachefiles_coherency_check_objsize;
127	} else if (buf->content == CACHEFILES_CONTENT_DIRTY) {
128		// TODO: Begin conflict resolution
129		pr_warn("Dirty object in cache\n");
130		why = cachefiles_coherency_check_dirty;
131	} else {
132		why = cachefiles_coherency_check_ok;
133		ret = 0;
134	}
135
136	trace_cachefiles_coherency(object, file_inode(file)->i_ino,
137				   buf->content, why);
138	kfree(buf);
139	return ret;
140}
141
142/*
143 * remove the object's xattr to mark it stale
144 */
145int cachefiles_remove_object_xattr(struct cachefiles_cache *cache,
146				   struct cachefiles_object *object,
147				   struct dentry *dentry)
148{
149	int ret;
150
151	ret = cachefiles_inject_remove_error();
152	if (ret == 0)
153		ret = vfs_removexattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache);
154	if (ret < 0) {
155		trace_cachefiles_vfs_error(object, d_inode(dentry), ret,
156					   cachefiles_trace_remxattr_error);
157		if (ret == -ENOENT || ret == -ENODATA)
158			ret = 0;
159		else if (ret != -ENOMEM)
160			cachefiles_io_error(cache,
161					    "Can't remove xattr from %lu"
162					    " (error %d)",
163					    d_backing_inode(dentry)->i_ino, -ret);
164	}
165
166	_leave(" = %d", ret);
167	return ret;
168}
169
170/*
171 * Stick a marker on the cache object to indicate that it's dirty.
172 */
173void cachefiles_prepare_to_write(struct fscache_cookie *cookie)
174{
175	const struct cred *saved_cred;
176	struct cachefiles_object *object = cookie->cache_priv;
177	struct cachefiles_cache *cache = object->volume->cache;
178
179	_enter("c=%08x", object->cookie->debug_id);
180
181	if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) {
182		cachefiles_begin_secure(cache, &saved_cred);
183		cachefiles_set_object_xattr(object);
184		cachefiles_end_secure(cache, saved_cred);
185	}
186}
187
188/*
189 * Set the state xattr on a volume directory.
190 */
191bool cachefiles_set_volume_xattr(struct cachefiles_volume *volume)
192{
193	struct cachefiles_vol_xattr *buf;
194	unsigned int len = volume->vcookie->coherency_len;
195	const void *p = volume->vcookie->coherency;
196	struct dentry *dentry = volume->dentry;
197	int ret;
198
199	_enter("%x,#%d", volume->vcookie->debug_id, len);
200
201	len += sizeof(*buf);
202	buf = kmalloc(len, GFP_KERNEL);
203	if (!buf)
204		return false;
205	buf->reserved = cpu_to_be32(0);
206	memcpy(buf->data, p, volume->vcookie->coherency_len);
207
208	ret = cachefiles_inject_write_error();
209	if (ret == 0)
210		ret = vfs_setxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache,
211				   buf, len, 0);
212	if (ret < 0) {
213		trace_cachefiles_vfs_error(NULL, d_inode(dentry), ret,
214					   cachefiles_trace_setxattr_error);
215		trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino,
216					       cachefiles_coherency_vol_set_fail);
217		if (ret != -ENOMEM)
218			cachefiles_io_error(
219				volume->cache, "Failed to set xattr with error %d", ret);
220	} else {
221		trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino,
222					       cachefiles_coherency_vol_set_ok);
223	}
224
225	kfree(buf);
226	_leave(" = %d", ret);
227	return ret == 0;
228}
229
230/*
231 * Check the consistency between the backing cache and the volume cookie.
232 */
233int cachefiles_check_volume_xattr(struct cachefiles_volume *volume)
234{
235	struct cachefiles_vol_xattr *buf;
236	struct dentry *dentry = volume->dentry;
237	unsigned int len = volume->vcookie->coherency_len;
238	const void *p = volume->vcookie->coherency;
239	enum cachefiles_coherency_trace why;
240	ssize_t xlen;
241	int ret = -ESTALE;
242
243	_enter("");
244
245	len += sizeof(*buf);
246	buf = kmalloc(len, GFP_KERNEL);
247	if (!buf)
248		return -ENOMEM;
249
250	xlen = cachefiles_inject_read_error();
251	if (xlen == 0)
252		xlen = vfs_getxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache, buf, len);
253	if (xlen != len) {
254		if (xlen < 0) {
255			trace_cachefiles_vfs_error(NULL, d_inode(dentry), xlen,
256						   cachefiles_trace_getxattr_error);
257			if (xlen == -EIO)
258				cachefiles_io_error(
259					volume->cache,
260					"Failed to read xattr with error %zd", xlen);
261		}
262		why = cachefiles_coherency_vol_check_xattr;
263	} else if (buf->reserved != cpu_to_be32(0)) {
264		why = cachefiles_coherency_vol_check_resv;
265	} else if (memcmp(buf->data, p, len - sizeof(*buf)) != 0) {
266		why = cachefiles_coherency_vol_check_cmp;
267	} else {
268		why = cachefiles_coherency_vol_check_ok;
269		ret = 0;
270	}
271
272	trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino, why);
273	kfree(buf);
274	_leave(" = %d", ret);
275	return ret;
276}
277