298 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* CacheFiles extended attribute management3 *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 119 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 file38 */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 = mnt_want_write_file(file);69 if (ret == 0) {70 ret = vfs_setxattr(&nop_mnt_idmap, dentry,71 cachefiles_xattr_cache, buf,72 sizeof(struct cachefiles_xattr) + len, 0);73 mnt_drop_write_file(file);74 }75 }76 if (ret < 0) {77 trace_cachefiles_vfs_error(object, file_inode(file), ret,78 cachefiles_trace_setxattr_error);79 trace_cachefiles_coherency(object, file_inode(file)->i_ino,80 buf->content,81 cachefiles_coherency_set_fail);82 if (ret != -ENOMEM)83 cachefiles_io_error_obj(84 object,85 "Failed to set xattr with error %d", ret);86 } else {87 trace_cachefiles_coherency(object, file_inode(file)->i_ino,88 buf->content,89 cachefiles_coherency_set_ok);90 }91 92 kfree(buf);93 _leave(" = %d", ret);94 return ret;95}96 97/*98 * check the consistency between the backing cache and the FS-Cache cookie99 */100int cachefiles_check_auxdata(struct cachefiles_object *object, struct file *file)101{102 struct cachefiles_xattr *buf;103 struct dentry *dentry = file->f_path.dentry;104 unsigned int len = object->cookie->aux_len, tlen;105 const void *p = fscache_get_aux(object->cookie);106 enum cachefiles_coherency_trace why;107 ssize_t xlen;108 int ret = -ESTALE;109 110 tlen = sizeof(struct cachefiles_xattr) + len;111 buf = kmalloc(tlen, GFP_KERNEL);112 if (!buf)113 return -ENOMEM;114 115 xlen = cachefiles_inject_read_error();116 if (xlen == 0)117 xlen = vfs_getxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache, buf, tlen);118 if (xlen != tlen) {119 if (xlen < 0) {120 ret = xlen;121 trace_cachefiles_vfs_error(object, file_inode(file), xlen,122 cachefiles_trace_getxattr_error);123 }124 if (xlen == -EIO)125 cachefiles_io_error_obj(126 object,127 "Failed to read aux with error %zd", xlen);128 why = cachefiles_coherency_check_xattr;129 } else if (buf->type != CACHEFILES_COOKIE_TYPE_DATA) {130 why = cachefiles_coherency_check_type;131 } else if (memcmp(buf->data, p, len) != 0) {132 why = cachefiles_coherency_check_aux;133 } else if (be64_to_cpu(buf->object_size) != object->cookie->object_size) {134 why = cachefiles_coherency_check_objsize;135 } else if (buf->content == CACHEFILES_CONTENT_DIRTY) {136 // TODO: Begin conflict resolution137 pr_warn("Dirty object in cache\n");138 why = cachefiles_coherency_check_dirty;139 } else {140 why = cachefiles_coherency_check_ok;141 ret = 0;142 }143 144 trace_cachefiles_coherency(object, file_inode(file)->i_ino,145 buf->content, why);146 kfree(buf);147 return ret;148}149 150/*151 * remove the object's xattr to mark it stale152 */153int cachefiles_remove_object_xattr(struct cachefiles_cache *cache,154 struct cachefiles_object *object,155 struct dentry *dentry)156{157 int ret;158 159 ret = cachefiles_inject_remove_error();160 if (ret == 0) {161 ret = mnt_want_write(cache->mnt);162 if (ret == 0) {163 ret = vfs_removexattr(&nop_mnt_idmap, dentry,164 cachefiles_xattr_cache);165 mnt_drop_write(cache->mnt);166 }167 }168 if (ret < 0) {169 trace_cachefiles_vfs_error(object, d_inode(dentry), ret,170 cachefiles_trace_remxattr_error);171 if (ret == -ENOENT || ret == -ENODATA)172 ret = 0;173 else if (ret != -ENOMEM)174 cachefiles_io_error(cache,175 "Can't remove xattr from %lu"176 " (error %d)",177 d_backing_inode(dentry)->i_ino, -ret);178 }179 180 _leave(" = %d", ret);181 return ret;182}183 184/*185 * Stick a marker on the cache object to indicate that it's dirty.186 */187void cachefiles_prepare_to_write(struct fscache_cookie *cookie)188{189 const struct cred *saved_cred;190 struct cachefiles_object *object = cookie->cache_priv;191 struct cachefiles_cache *cache = object->volume->cache;192 193 _enter("c=%08x", object->cookie->debug_id);194 195 if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) {196 cachefiles_begin_secure(cache, &saved_cred);197 cachefiles_set_object_xattr(object);198 cachefiles_end_secure(cache, saved_cred);199 }200}201 202/*203 * Set the state xattr on a volume directory.204 */205bool cachefiles_set_volume_xattr(struct cachefiles_volume *volume)206{207 struct cachefiles_vol_xattr *buf;208 unsigned int len = volume->vcookie->coherency_len;209 const void *p = volume->vcookie->coherency;210 struct dentry *dentry = volume->dentry;211 int ret;212 213 _enter("%x,#%d", volume->vcookie->debug_id, len);214 215 len += sizeof(*buf);216 buf = kmalloc(len, GFP_KERNEL);217 if (!buf)218 return false;219 buf->reserved = cpu_to_be32(0);220 memcpy(buf->data, p, volume->vcookie->coherency_len);221 222 ret = cachefiles_inject_write_error();223 if (ret == 0) {224 ret = mnt_want_write(volume->cache->mnt);225 if (ret == 0) {226 ret = vfs_setxattr(&nop_mnt_idmap, dentry,227 cachefiles_xattr_cache,228 buf, len, 0);229 mnt_drop_write(volume->cache->mnt);230 }231 }232 if (ret < 0) {233 trace_cachefiles_vfs_error(NULL, d_inode(dentry), ret,234 cachefiles_trace_setxattr_error);235 trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino,236 cachefiles_coherency_vol_set_fail);237 if (ret != -ENOMEM)238 cachefiles_io_error(239 volume->cache, "Failed to set xattr with error %d", ret);240 } else {241 trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino,242 cachefiles_coherency_vol_set_ok);243 }244 245 kfree(buf);246 _leave(" = %d", ret);247 return ret == 0;248}249 250/*251 * Check the consistency between the backing cache and the volume cookie.252 */253int cachefiles_check_volume_xattr(struct cachefiles_volume *volume)254{255 struct cachefiles_vol_xattr *buf;256 struct dentry *dentry = volume->dentry;257 unsigned int len = volume->vcookie->coherency_len;258 const void *p = volume->vcookie->coherency;259 enum cachefiles_coherency_trace why;260 ssize_t xlen;261 int ret = -ESTALE;262 263 _enter("");264 265 len += sizeof(*buf);266 buf = kmalloc(len, GFP_KERNEL);267 if (!buf)268 return -ENOMEM;269 270 xlen = cachefiles_inject_read_error();271 if (xlen == 0)272 xlen = vfs_getxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache, buf, len);273 if (xlen != len) {274 if (xlen < 0) {275 ret = xlen;276 trace_cachefiles_vfs_error(NULL, d_inode(dentry), xlen,277 cachefiles_trace_getxattr_error);278 if (xlen == -EIO)279 cachefiles_io_error(280 volume->cache,281 "Failed to read xattr with error %zd", xlen);282 }283 why = cachefiles_coherency_vol_check_xattr;284 } else if (buf->reserved != cpu_to_be32(0)) {285 why = cachefiles_coherency_vol_check_resv;286 } else if (memcmp(buf->data, p, len - sizeof(*buf)) != 0) {287 why = cachefiles_coherency_vol_check_cmp;288 } else {289 why = cachefiles_coherency_vol_check_ok;290 ret = 0;291 }292 293 trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino, why);294 kfree(buf);295 _leave(" = %d", ret);296 return ret;297}298