xref: /openbmc/linux/fs/cachefiles/xattr.c (revision 5e2421ce)
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 
20 struct 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 
28 static const char cachefiles_xattr_cache[] =
29 	XATTR_USER_PREFIX "CacheFiles.cache";
30 
31 /*
32  * set the state xattr on a cache file
33  */
34 int cachefiles_set_object_xattr(struct cachefiles_object *object)
35 {
36 	struct cachefiles_xattr *buf;
37 	struct dentry *dentry;
38 	struct file *file = object->file;
39 	unsigned int len = object->cookie->aux_len;
40 	int ret;
41 
42 	if (!file)
43 		return -ESTALE;
44 	dentry = file->f_path.dentry;
45 
46 	_enter("%x,#%d", object->debug_id, len);
47 
48 	buf = kmalloc(sizeof(struct cachefiles_xattr) + len, GFP_KERNEL);
49 	if (!buf)
50 		return -ENOMEM;
51 
52 	buf->object_size	= cpu_to_be64(object->cookie->object_size);
53 	buf->zero_point		= 0;
54 	buf->type		= CACHEFILES_COOKIE_TYPE_DATA;
55 	buf->content		= object->content_info;
56 	if (test_bit(FSCACHE_COOKIE_LOCAL_WRITE, &object->cookie->flags))
57 		buf->content	= CACHEFILES_CONTENT_DIRTY;
58 	if (len > 0)
59 		memcpy(buf->data, fscache_get_aux(object->cookie), len);
60 
61 	ret = cachefiles_inject_write_error();
62 	if (ret == 0)
63 		ret = vfs_setxattr(&init_user_ns, dentry, cachefiles_xattr_cache,
64 				   buf, sizeof(struct cachefiles_xattr) + len, 0);
65 	if (ret < 0) {
66 		trace_cachefiles_vfs_error(object, file_inode(file), ret,
67 					   cachefiles_trace_setxattr_error);
68 		trace_cachefiles_coherency(object, file_inode(file)->i_ino,
69 					   buf->content,
70 					   cachefiles_coherency_set_fail);
71 		if (ret != -ENOMEM)
72 			cachefiles_io_error_obj(
73 				object,
74 				"Failed to set xattr with error %d", ret);
75 	} else {
76 		trace_cachefiles_coherency(object, file_inode(file)->i_ino,
77 					   buf->content,
78 					   cachefiles_coherency_set_ok);
79 	}
80 
81 	kfree(buf);
82 	_leave(" = %d", ret);
83 	return ret;
84 }
85 
86 /*
87  * check the consistency between the backing cache and the FS-Cache cookie
88  */
89 int cachefiles_check_auxdata(struct cachefiles_object *object, struct file *file)
90 {
91 	struct cachefiles_xattr *buf;
92 	struct dentry *dentry = file->f_path.dentry;
93 	unsigned int len = object->cookie->aux_len, tlen;
94 	const void *p = fscache_get_aux(object->cookie);
95 	enum cachefiles_coherency_trace why;
96 	ssize_t xlen;
97 	int ret = -ESTALE;
98 
99 	tlen = sizeof(struct cachefiles_xattr) + len;
100 	buf = kmalloc(tlen, GFP_KERNEL);
101 	if (!buf)
102 		return -ENOMEM;
103 
104 	xlen = cachefiles_inject_read_error();
105 	if (xlen == 0)
106 		xlen = vfs_getxattr(&init_user_ns, dentry, cachefiles_xattr_cache, buf, tlen);
107 	if (xlen != tlen) {
108 		if (xlen < 0)
109 			trace_cachefiles_vfs_error(object, file_inode(file), xlen,
110 						   cachefiles_trace_getxattr_error);
111 		if (xlen == -EIO)
112 			cachefiles_io_error_obj(
113 				object,
114 				"Failed to read aux with error %zd", xlen);
115 		why = cachefiles_coherency_check_xattr;
116 	} else if (buf->type != CACHEFILES_COOKIE_TYPE_DATA) {
117 		why = cachefiles_coherency_check_type;
118 	} else if (memcmp(buf->data, p, len) != 0) {
119 		why = cachefiles_coherency_check_aux;
120 	} else if (be64_to_cpu(buf->object_size) != object->cookie->object_size) {
121 		why = cachefiles_coherency_check_objsize;
122 	} else if (buf->content == CACHEFILES_CONTENT_DIRTY) {
123 		// TODO: Begin conflict resolution
124 		pr_warn("Dirty object in cache\n");
125 		why = cachefiles_coherency_check_dirty;
126 	} else {
127 		why = cachefiles_coherency_check_ok;
128 		ret = 0;
129 	}
130 
131 	trace_cachefiles_coherency(object, file_inode(file)->i_ino,
132 				   buf->content, why);
133 	kfree(buf);
134 	return ret;
135 }
136 
137 /*
138  * remove the object's xattr to mark it stale
139  */
140 int cachefiles_remove_object_xattr(struct cachefiles_cache *cache,
141 				   struct cachefiles_object *object,
142 				   struct dentry *dentry)
143 {
144 	int ret;
145 
146 	ret = cachefiles_inject_remove_error();
147 	if (ret == 0)
148 		ret = vfs_removexattr(&init_user_ns, dentry, cachefiles_xattr_cache);
149 	if (ret < 0) {
150 		trace_cachefiles_vfs_error(object, d_inode(dentry), ret,
151 					   cachefiles_trace_remxattr_error);
152 		if (ret == -ENOENT || ret == -ENODATA)
153 			ret = 0;
154 		else if (ret != -ENOMEM)
155 			cachefiles_io_error(cache,
156 					    "Can't remove xattr from %lu"
157 					    " (error %d)",
158 					    d_backing_inode(dentry)->i_ino, -ret);
159 	}
160 
161 	_leave(" = %d", ret);
162 	return ret;
163 }
164 
165 /*
166  * Stick a marker on the cache object to indicate that it's dirty.
167  */
168 void cachefiles_prepare_to_write(struct fscache_cookie *cookie)
169 {
170 	const struct cred *saved_cred;
171 	struct cachefiles_object *object = cookie->cache_priv;
172 	struct cachefiles_cache *cache = object->volume->cache;
173 
174 	_enter("c=%08x", object->cookie->debug_id);
175 
176 	if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) {
177 		cachefiles_begin_secure(cache, &saved_cred);
178 		cachefiles_set_object_xattr(object);
179 		cachefiles_end_secure(cache, saved_cred);
180 	}
181 }
182 
183 /*
184  * Set the state xattr on a volume directory.
185  */
186 bool cachefiles_set_volume_xattr(struct cachefiles_volume *volume)
187 {
188 	unsigned int len = volume->vcookie->coherency_len;
189 	const void *p = volume->vcookie->coherency;
190 	struct dentry *dentry = volume->dentry;
191 	int ret;
192 
193 	_enter("%x,#%d", volume->vcookie->debug_id, len);
194 
195 	ret = cachefiles_inject_write_error();
196 	if (ret == 0)
197 		ret = vfs_setxattr(&init_user_ns, dentry, cachefiles_xattr_cache,
198 				   p, len, 0);
199 	if (ret < 0) {
200 		trace_cachefiles_vfs_error(NULL, d_inode(dentry), ret,
201 					   cachefiles_trace_setxattr_error);
202 		trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino,
203 					       cachefiles_coherency_vol_set_fail);
204 		if (ret != -ENOMEM)
205 			cachefiles_io_error(
206 				volume->cache, "Failed to set xattr with error %d", ret);
207 	} else {
208 		trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino,
209 					       cachefiles_coherency_vol_set_ok);
210 	}
211 
212 	_leave(" = %d", ret);
213 	return ret == 0;
214 }
215 
216 /*
217  * Check the consistency between the backing cache and the volume cookie.
218  */
219 int cachefiles_check_volume_xattr(struct cachefiles_volume *volume)
220 {
221 	struct cachefiles_xattr *buf;
222 	struct dentry *dentry = volume->dentry;
223 	unsigned int len = volume->vcookie->coherency_len;
224 	const void *p = volume->vcookie->coherency;
225 	enum cachefiles_coherency_trace why;
226 	ssize_t xlen;
227 	int ret = -ESTALE;
228 
229 	_enter("");
230 
231 	buf = kmalloc(len, GFP_KERNEL);
232 	if (!buf)
233 		return -ENOMEM;
234 
235 	xlen = cachefiles_inject_read_error();
236 	if (xlen == 0)
237 		xlen = vfs_getxattr(&init_user_ns, dentry, cachefiles_xattr_cache, buf, len);
238 	if (xlen != len) {
239 		if (xlen < 0) {
240 			trace_cachefiles_vfs_error(NULL, d_inode(dentry), xlen,
241 						   cachefiles_trace_getxattr_error);
242 			if (xlen == -EIO)
243 				cachefiles_io_error(
244 					volume->cache,
245 					"Failed to read xattr with error %zd", xlen);
246 		}
247 		why = cachefiles_coherency_vol_check_xattr;
248 	} else if (memcmp(buf->data, p, len) != 0) {
249 		why = cachefiles_coherency_vol_check_cmp;
250 	} else {
251 		why = cachefiles_coherency_vol_check_ok;
252 		ret = 0;
253 	}
254 
255 	trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino, why);
256 	kfree(buf);
257 	_leave(" = %d", ret);
258 	return ret;
259 }
260