blob: 83f41bd0c3a9749d3aa66fb7fadb2337985095d7 [file] [log] [blame]
David Howells72b95782021-11-17 16:11:07 +00001// 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
31/*
32 * set the state xattr on a cache file
33 */
34int 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 */
89int 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 */
140int 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 */
168void 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}
David Howells32e15002021-12-14 09:51:43 +0000182
183/*
184 * Set the state xattr on a volume directory.
185 */
186bool 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 */
219int 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}