172b95785SDavid Howells // SPDX-License-Identifier: GPL-2.0-or-later
272b95785SDavid Howells /* CacheFiles extended attribute management
372b95785SDavid Howells *
472b95785SDavid Howells * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
572b95785SDavid Howells * Written by David Howells (dhowells@redhat.com)
672b95785SDavid Howells */
772b95785SDavid Howells
872b95785SDavid Howells #include <linux/module.h>
972b95785SDavid Howells #include <linux/sched.h>
1072b95785SDavid Howells #include <linux/file.h>
1172b95785SDavid Howells #include <linux/fs.h>
1272b95785SDavid Howells #include <linux/fsnotify.h>
1372b95785SDavid Howells #include <linux/quotaops.h>
1472b95785SDavid Howells #include <linux/xattr.h>
1572b95785SDavid Howells #include <linux/slab.h>
1672b95785SDavid Howells #include "internal.h"
1772b95785SDavid Howells
1872b95785SDavid Howells #define CACHEFILES_COOKIE_TYPE_DATA 1
1972b95785SDavid Howells
2072b95785SDavid Howells struct cachefiles_xattr {
2172b95785SDavid Howells __be64 object_size; /* Actual size of the object */
2272b95785SDavid Howells __be64 zero_point; /* Size after which server has no data not written by us */
2372b95785SDavid Howells __u8 type; /* Type of object */
2472b95785SDavid Howells __u8 content; /* Content presence (enum cachefiles_content) */
2572b95785SDavid Howells __u8 data[]; /* netfs coherency data */
2672b95785SDavid Howells } __packed;
2772b95785SDavid Howells
2872b95785SDavid Howells static const char cachefiles_xattr_cache[] =
2972b95785SDavid Howells XATTR_USER_PREFIX "CacheFiles.cache";
3072b95785SDavid Howells
31413a4a6bSDavid Howells struct cachefiles_vol_xattr {
32413a4a6bSDavid Howells __be32 reserved; /* Reserved, should be 0 */
33413a4a6bSDavid Howells __u8 data[]; /* netfs volume coherency data */
34413a4a6bSDavid Howells } __packed;
35413a4a6bSDavid Howells
3672b95785SDavid Howells /*
3772b95785SDavid Howells * set the state xattr on a cache file
3872b95785SDavid Howells */
cachefiles_set_object_xattr(struct cachefiles_object * object)3972b95785SDavid Howells int cachefiles_set_object_xattr(struct cachefiles_object *object)
4072b95785SDavid Howells {
4172b95785SDavid Howells struct cachefiles_xattr *buf;
4272b95785SDavid Howells struct dentry *dentry;
4372b95785SDavid Howells struct file *file = object->file;
4472b95785SDavid Howells unsigned int len = object->cookie->aux_len;
4572b95785SDavid Howells int ret;
4672b95785SDavid Howells
4772b95785SDavid Howells if (!file)
4872b95785SDavid Howells return -ESTALE;
4972b95785SDavid Howells dentry = file->f_path.dentry;
5072b95785SDavid Howells
5172b95785SDavid Howells _enter("%x,#%d", object->debug_id, len);
5272b95785SDavid Howells
5372b95785SDavid Howells buf = kmalloc(sizeof(struct cachefiles_xattr) + len, GFP_KERNEL);
5472b95785SDavid Howells if (!buf)
5572b95785SDavid Howells return -ENOMEM;
5672b95785SDavid Howells
5772b95785SDavid Howells buf->object_size = cpu_to_be64(object->cookie->object_size);
5872b95785SDavid Howells buf->zero_point = 0;
5972b95785SDavid Howells buf->type = CACHEFILES_COOKIE_TYPE_DATA;
6072b95785SDavid Howells buf->content = object->content_info;
6172b95785SDavid Howells if (test_bit(FSCACHE_COOKIE_LOCAL_WRITE, &object->cookie->flags))
6272b95785SDavid Howells buf->content = CACHEFILES_CONTENT_DIRTY;
6372b95785SDavid Howells if (len > 0)
6472b95785SDavid Howells memcpy(buf->data, fscache_get_aux(object->cookie), len);
6572b95785SDavid Howells
6672b95785SDavid Howells ret = cachefiles_inject_write_error();
67*81b048b9SDavid Howells if (ret == 0) {
68*81b048b9SDavid Howells ret = mnt_want_write_file(file);
69*81b048b9SDavid Howells if (ret == 0) {
70*81b048b9SDavid Howells ret = vfs_setxattr(&nop_mnt_idmap, dentry,
71*81b048b9SDavid Howells cachefiles_xattr_cache, buf,
72*81b048b9SDavid Howells sizeof(struct cachefiles_xattr) + len, 0);
73*81b048b9SDavid Howells mnt_drop_write_file(file);
74*81b048b9SDavid Howells }
75*81b048b9SDavid Howells }
7672b95785SDavid Howells if (ret < 0) {
7772b95785SDavid Howells trace_cachefiles_vfs_error(object, file_inode(file), ret,
7872b95785SDavid Howells cachefiles_trace_setxattr_error);
7972b95785SDavid Howells trace_cachefiles_coherency(object, file_inode(file)->i_ino,
8072b95785SDavid Howells buf->content,
8172b95785SDavid Howells cachefiles_coherency_set_fail);
8272b95785SDavid Howells if (ret != -ENOMEM)
8372b95785SDavid Howells cachefiles_io_error_obj(
8472b95785SDavid Howells object,
8572b95785SDavid Howells "Failed to set xattr with error %d", ret);
8672b95785SDavid Howells } else {
8772b95785SDavid Howells trace_cachefiles_coherency(object, file_inode(file)->i_ino,
8872b95785SDavid Howells buf->content,
8972b95785SDavid Howells cachefiles_coherency_set_ok);
9072b95785SDavid Howells }
9172b95785SDavid Howells
9272b95785SDavid Howells kfree(buf);
9372b95785SDavid Howells _leave(" = %d", ret);
9472b95785SDavid Howells return ret;
9572b95785SDavid Howells }
9672b95785SDavid Howells
9772b95785SDavid Howells /*
9872b95785SDavid Howells * check the consistency between the backing cache and the FS-Cache cookie
9972b95785SDavid Howells */
cachefiles_check_auxdata(struct cachefiles_object * object,struct file * file)10072b95785SDavid Howells int cachefiles_check_auxdata(struct cachefiles_object *object, struct file *file)
10172b95785SDavid Howells {
10272b95785SDavid Howells struct cachefiles_xattr *buf;
10372b95785SDavid Howells struct dentry *dentry = file->f_path.dentry;
10472b95785SDavid Howells unsigned int len = object->cookie->aux_len, tlen;
10572b95785SDavid Howells const void *p = fscache_get_aux(object->cookie);
10672b95785SDavid Howells enum cachefiles_coherency_trace why;
10772b95785SDavid Howells ssize_t xlen;
10872b95785SDavid Howells int ret = -ESTALE;
10972b95785SDavid Howells
11072b95785SDavid Howells tlen = sizeof(struct cachefiles_xattr) + len;
11172b95785SDavid Howells buf = kmalloc(tlen, GFP_KERNEL);
11272b95785SDavid Howells if (!buf)
11372b95785SDavid Howells return -ENOMEM;
11472b95785SDavid Howells
11572b95785SDavid Howells xlen = cachefiles_inject_read_error();
11672b95785SDavid Howells if (xlen == 0)
1174609e1f1SChristian Brauner xlen = vfs_getxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache, buf, tlen);
11872b95785SDavid Howells if (xlen != tlen) {
119a04cc7f0SBaokun Li if (xlen < 0) {
120a04cc7f0SBaokun Li ret = xlen;
12172b95785SDavid Howells trace_cachefiles_vfs_error(object, file_inode(file), xlen,
12272b95785SDavid Howells cachefiles_trace_getxattr_error);
123a04cc7f0SBaokun Li }
12472b95785SDavid Howells if (xlen == -EIO)
12572b95785SDavid Howells cachefiles_io_error_obj(
12672b95785SDavid Howells object,
12772b95785SDavid Howells "Failed to read aux with error %zd", xlen);
12872b95785SDavid Howells why = cachefiles_coherency_check_xattr;
12972b95785SDavid Howells } else if (buf->type != CACHEFILES_COOKIE_TYPE_DATA) {
13072b95785SDavid Howells why = cachefiles_coherency_check_type;
13172b95785SDavid Howells } else if (memcmp(buf->data, p, len) != 0) {
13272b95785SDavid Howells why = cachefiles_coherency_check_aux;
13372b95785SDavid Howells } else if (be64_to_cpu(buf->object_size) != object->cookie->object_size) {
13472b95785SDavid Howells why = cachefiles_coherency_check_objsize;
13572b95785SDavid Howells } else if (buf->content == CACHEFILES_CONTENT_DIRTY) {
13672b95785SDavid Howells // TODO: Begin conflict resolution
13772b95785SDavid Howells pr_warn("Dirty object in cache\n");
13872b95785SDavid Howells why = cachefiles_coherency_check_dirty;
13972b95785SDavid Howells } else {
14072b95785SDavid Howells why = cachefiles_coherency_check_ok;
14172b95785SDavid Howells ret = 0;
14272b95785SDavid Howells }
14372b95785SDavid Howells
14472b95785SDavid Howells trace_cachefiles_coherency(object, file_inode(file)->i_ino,
14572b95785SDavid Howells buf->content, why);
14672b95785SDavid Howells kfree(buf);
14772b95785SDavid Howells return ret;
14872b95785SDavid Howells }
14972b95785SDavid Howells
15072b95785SDavid Howells /*
15172b95785SDavid Howells * remove the object's xattr to mark it stale
15272b95785SDavid Howells */
cachefiles_remove_object_xattr(struct cachefiles_cache * cache,struct cachefiles_object * object,struct dentry * dentry)15372b95785SDavid Howells int cachefiles_remove_object_xattr(struct cachefiles_cache *cache,
15472b95785SDavid Howells struct cachefiles_object *object,
15572b95785SDavid Howells struct dentry *dentry)
15672b95785SDavid Howells {
15772b95785SDavid Howells int ret;
15872b95785SDavid Howells
15972b95785SDavid Howells ret = cachefiles_inject_remove_error();
160*81b048b9SDavid Howells if (ret == 0) {
161*81b048b9SDavid Howells ret = mnt_want_write(cache->mnt);
162*81b048b9SDavid Howells if (ret == 0) {
163*81b048b9SDavid Howells ret = vfs_removexattr(&nop_mnt_idmap, dentry,
164*81b048b9SDavid Howells cachefiles_xattr_cache);
165*81b048b9SDavid Howells mnt_drop_write(cache->mnt);
166*81b048b9SDavid Howells }
167*81b048b9SDavid Howells }
16872b95785SDavid Howells if (ret < 0) {
16972b95785SDavid Howells trace_cachefiles_vfs_error(object, d_inode(dentry), ret,
17072b95785SDavid Howells cachefiles_trace_remxattr_error);
17172b95785SDavid Howells if (ret == -ENOENT || ret == -ENODATA)
17272b95785SDavid Howells ret = 0;
17372b95785SDavid Howells else if (ret != -ENOMEM)
17472b95785SDavid Howells cachefiles_io_error(cache,
17572b95785SDavid Howells "Can't remove xattr from %lu"
17672b95785SDavid Howells " (error %d)",
17772b95785SDavid Howells d_backing_inode(dentry)->i_ino, -ret);
17872b95785SDavid Howells }
17972b95785SDavid Howells
18072b95785SDavid Howells _leave(" = %d", ret);
18172b95785SDavid Howells return ret;
18272b95785SDavid Howells }
18372b95785SDavid Howells
18472b95785SDavid Howells /*
18572b95785SDavid Howells * Stick a marker on the cache object to indicate that it's dirty.
18672b95785SDavid Howells */
cachefiles_prepare_to_write(struct fscache_cookie * cookie)18772b95785SDavid Howells void cachefiles_prepare_to_write(struct fscache_cookie *cookie)
18872b95785SDavid Howells {
18972b95785SDavid Howells const struct cred *saved_cred;
19072b95785SDavid Howells struct cachefiles_object *object = cookie->cache_priv;
19172b95785SDavid Howells struct cachefiles_cache *cache = object->volume->cache;
19272b95785SDavid Howells
19372b95785SDavid Howells _enter("c=%08x", object->cookie->debug_id);
19472b95785SDavid Howells
19572b95785SDavid Howells if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) {
19672b95785SDavid Howells cachefiles_begin_secure(cache, &saved_cred);
19772b95785SDavid Howells cachefiles_set_object_xattr(object);
19872b95785SDavid Howells cachefiles_end_secure(cache, saved_cred);
19972b95785SDavid Howells }
20072b95785SDavid Howells }
20132e15003SDavid Howells
20232e15003SDavid Howells /*
20332e15003SDavid Howells * Set the state xattr on a volume directory.
20432e15003SDavid Howells */
cachefiles_set_volume_xattr(struct cachefiles_volume * volume)20532e15003SDavid Howells bool cachefiles_set_volume_xattr(struct cachefiles_volume *volume)
20632e15003SDavid Howells {
207413a4a6bSDavid Howells struct cachefiles_vol_xattr *buf;
20832e15003SDavid Howells unsigned int len = volume->vcookie->coherency_len;
20932e15003SDavid Howells const void *p = volume->vcookie->coherency;
21032e15003SDavid Howells struct dentry *dentry = volume->dentry;
21132e15003SDavid Howells int ret;
21232e15003SDavid Howells
21332e15003SDavid Howells _enter("%x,#%d", volume->vcookie->debug_id, len);
21432e15003SDavid Howells
215413a4a6bSDavid Howells len += sizeof(*buf);
216413a4a6bSDavid Howells buf = kmalloc(len, GFP_KERNEL);
217413a4a6bSDavid Howells if (!buf)
218413a4a6bSDavid Howells return false;
219413a4a6bSDavid Howells buf->reserved = cpu_to_be32(0);
2207b2f6c30SDave Wysochanski memcpy(buf->data, p, volume->vcookie->coherency_len);
221413a4a6bSDavid Howells
22232e15003SDavid Howells ret = cachefiles_inject_write_error();
223*81b048b9SDavid Howells if (ret == 0) {
224*81b048b9SDavid Howells ret = mnt_want_write(volume->cache->mnt);
225*81b048b9SDavid Howells if (ret == 0) {
226*81b048b9SDavid Howells ret = vfs_setxattr(&nop_mnt_idmap, dentry,
227*81b048b9SDavid Howells cachefiles_xattr_cache,
228413a4a6bSDavid Howells buf, len, 0);
229*81b048b9SDavid Howells mnt_drop_write(volume->cache->mnt);
230*81b048b9SDavid Howells }
231*81b048b9SDavid Howells }
23232e15003SDavid Howells if (ret < 0) {
23332e15003SDavid Howells trace_cachefiles_vfs_error(NULL, d_inode(dentry), ret,
23432e15003SDavid Howells cachefiles_trace_setxattr_error);
23532e15003SDavid Howells trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino,
23632e15003SDavid Howells cachefiles_coherency_vol_set_fail);
23732e15003SDavid Howells if (ret != -ENOMEM)
23832e15003SDavid Howells cachefiles_io_error(
23932e15003SDavid Howells volume->cache, "Failed to set xattr with error %d", ret);
24032e15003SDavid Howells } else {
24132e15003SDavid Howells trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino,
24232e15003SDavid Howells cachefiles_coherency_vol_set_ok);
24332e15003SDavid Howells }
24432e15003SDavid Howells
245413a4a6bSDavid Howells kfree(buf);
24632e15003SDavid Howells _leave(" = %d", ret);
24732e15003SDavid Howells return ret == 0;
24832e15003SDavid Howells }
24932e15003SDavid Howells
25032e15003SDavid Howells /*
25132e15003SDavid Howells * Check the consistency between the backing cache and the volume cookie.
25232e15003SDavid Howells */
cachefiles_check_volume_xattr(struct cachefiles_volume * volume)25332e15003SDavid Howells int cachefiles_check_volume_xattr(struct cachefiles_volume *volume)
25432e15003SDavid Howells {
255413a4a6bSDavid Howells struct cachefiles_vol_xattr *buf;
25632e15003SDavid Howells struct dentry *dentry = volume->dentry;
25732e15003SDavid Howells unsigned int len = volume->vcookie->coherency_len;
25832e15003SDavid Howells const void *p = volume->vcookie->coherency;
25932e15003SDavid Howells enum cachefiles_coherency_trace why;
26032e15003SDavid Howells ssize_t xlen;
26132e15003SDavid Howells int ret = -ESTALE;
26232e15003SDavid Howells
26332e15003SDavid Howells _enter("");
26432e15003SDavid Howells
265413a4a6bSDavid Howells len += sizeof(*buf);
26632e15003SDavid Howells buf = kmalloc(len, GFP_KERNEL);
26732e15003SDavid Howells if (!buf)
26832e15003SDavid Howells return -ENOMEM;
26932e15003SDavid Howells
27032e15003SDavid Howells xlen = cachefiles_inject_read_error();
27132e15003SDavid Howells if (xlen == 0)
2724609e1f1SChristian Brauner xlen = vfs_getxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache, buf, len);
27332e15003SDavid Howells if (xlen != len) {
27432e15003SDavid Howells if (xlen < 0) {
275a04cc7f0SBaokun Li ret = xlen;
27632e15003SDavid Howells trace_cachefiles_vfs_error(NULL, d_inode(dentry), xlen,
27732e15003SDavid Howells cachefiles_trace_getxattr_error);
27832e15003SDavid Howells if (xlen == -EIO)
27932e15003SDavid Howells cachefiles_io_error(
28032e15003SDavid Howells volume->cache,
28132e15003SDavid Howells "Failed to read xattr with error %zd", xlen);
28232e15003SDavid Howells }
28332e15003SDavid Howells why = cachefiles_coherency_vol_check_xattr;
284413a4a6bSDavid Howells } else if (buf->reserved != cpu_to_be32(0)) {
285413a4a6bSDavid Howells why = cachefiles_coherency_vol_check_resv;
286413a4a6bSDavid Howells } else if (memcmp(buf->data, p, len - sizeof(*buf)) != 0) {
28732e15003SDavid Howells why = cachefiles_coherency_vol_check_cmp;
28832e15003SDavid Howells } else {
28932e15003SDavid Howells why = cachefiles_coherency_vol_check_ok;
29032e15003SDavid Howells ret = 0;
29132e15003SDavid Howells }
29232e15003SDavid Howells
29332e15003SDavid Howells trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino, why);
29432e15003SDavid Howells kfree(buf);
29532e15003SDavid Howells _leave(" = %d", ret);
29632e15003SDavid Howells return ret;
29732e15003SDavid Howells }
298