2021-11-17 16:11:07 +00:00
// SPDX-License-Identifier: GPL-2.0-or-later
/* CacheFiles extended attribute management
*
* Copyright ( C ) 2021 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*/
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/file.h>
# include <linux/fs.h>
# include <linux/fsnotify.h>
# include <linux/quotaops.h>
# include <linux/xattr.h>
# include <linux/slab.h>
# include "internal.h"
# define CACHEFILES_COOKIE_TYPE_DATA 1
struct cachefiles_xattr {
__be64 object_size ; /* Actual size of the object */
__be64 zero_point ; /* Size after which server has no data not written by us */
__u8 type ; /* Type of object */
__u8 content ; /* Content presence (enum cachefiles_content) */
__u8 data [ ] ; /* netfs coherency data */
} __packed ;
static const char cachefiles_xattr_cache [ ] =
XATTR_USER_PREFIX " CacheFiles.cache " ;
/*
* set the state xattr on a cache file
*/
int cachefiles_set_object_xattr ( struct cachefiles_object * object )
{
struct cachefiles_xattr * buf ;
struct dentry * dentry ;
struct file * file = object - > file ;
unsigned int len = object - > cookie - > aux_len ;
int ret ;
if ( ! file )
return - ESTALE ;
dentry = file - > f_path . dentry ;
_enter ( " %x,#%d " , object - > debug_id , len ) ;
buf = kmalloc ( sizeof ( struct cachefiles_xattr ) + len , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
buf - > object_size = cpu_to_be64 ( object - > cookie - > object_size ) ;
buf - > zero_point = 0 ;
buf - > type = CACHEFILES_COOKIE_TYPE_DATA ;
buf - > content = object - > content_info ;
if ( test_bit ( FSCACHE_COOKIE_LOCAL_WRITE , & object - > cookie - > flags ) )
buf - > content = CACHEFILES_CONTENT_DIRTY ;
if ( len > 0 )
memcpy ( buf - > data , fscache_get_aux ( object - > cookie ) , len ) ;
ret = cachefiles_inject_write_error ( ) ;
if ( ret = = 0 )
ret = vfs_setxattr ( & init_user_ns , dentry , cachefiles_xattr_cache ,
buf , sizeof ( struct cachefiles_xattr ) + len , 0 ) ;
if ( ret < 0 ) {
trace_cachefiles_vfs_error ( object , file_inode ( file ) , ret ,
cachefiles_trace_setxattr_error ) ;
trace_cachefiles_coherency ( object , file_inode ( file ) - > i_ino ,
buf - > content ,
cachefiles_coherency_set_fail ) ;
if ( ret ! = - ENOMEM )
cachefiles_io_error_obj (
object ,
" Failed to set xattr with error %d " , ret ) ;
} else {
trace_cachefiles_coherency ( object , file_inode ( file ) - > i_ino ,
buf - > content ,
cachefiles_coherency_set_ok ) ;
}
kfree ( buf ) ;
_leave ( " = %d " , ret ) ;
return ret ;
}
/*
* check the consistency between the backing cache and the FS - Cache cookie
*/
int cachefiles_check_auxdata ( struct cachefiles_object * object , struct file * file )
{
struct cachefiles_xattr * buf ;
struct dentry * dentry = file - > f_path . dentry ;
unsigned int len = object - > cookie - > aux_len , tlen ;
const void * p = fscache_get_aux ( object - > cookie ) ;
enum cachefiles_coherency_trace why ;
ssize_t xlen ;
int ret = - ESTALE ;
tlen = sizeof ( struct cachefiles_xattr ) + len ;
buf = kmalloc ( tlen , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
xlen = cachefiles_inject_read_error ( ) ;
if ( xlen = = 0 )
xlen = vfs_getxattr ( & init_user_ns , dentry , cachefiles_xattr_cache , buf , tlen ) ;
if ( xlen ! = tlen ) {
if ( xlen < 0 )
trace_cachefiles_vfs_error ( object , file_inode ( file ) , xlen ,
cachefiles_trace_getxattr_error ) ;
if ( xlen = = - EIO )
cachefiles_io_error_obj (
object ,
" Failed to read aux with error %zd " , xlen ) ;
why = cachefiles_coherency_check_xattr ;
} else if ( buf - > type ! = CACHEFILES_COOKIE_TYPE_DATA ) {
why = cachefiles_coherency_check_type ;
} else if ( memcmp ( buf - > data , p , len ) ! = 0 ) {
why = cachefiles_coherency_check_aux ;
} else if ( be64_to_cpu ( buf - > object_size ) ! = object - > cookie - > object_size ) {
why = cachefiles_coherency_check_objsize ;
} else if ( buf - > content = = CACHEFILES_CONTENT_DIRTY ) {
// TODO: Begin conflict resolution
pr_warn ( " Dirty object in cache \n " ) ;
why = cachefiles_coherency_check_dirty ;
} else {
why = cachefiles_coherency_check_ok ;
ret = 0 ;
}
trace_cachefiles_coherency ( object , file_inode ( file ) - > i_ino ,
buf - > content , why ) ;
kfree ( buf ) ;
return ret ;
}
/*
* remove the object ' s xattr to mark it stale
*/
int cachefiles_remove_object_xattr ( struct cachefiles_cache * cache ,
struct cachefiles_object * object ,
struct dentry * dentry )
{
int ret ;
ret = cachefiles_inject_remove_error ( ) ;
if ( ret = = 0 )
ret = vfs_removexattr ( & init_user_ns , dentry , cachefiles_xattr_cache ) ;
if ( ret < 0 ) {
trace_cachefiles_vfs_error ( object , d_inode ( dentry ) , ret ,
cachefiles_trace_remxattr_error ) ;
if ( ret = = - ENOENT | | ret = = - ENODATA )
ret = 0 ;
else if ( ret ! = - ENOMEM )
cachefiles_io_error ( cache ,
" Can't remove xattr from %lu "
" (error %d) " ,
d_backing_inode ( dentry ) - > i_ino , - ret ) ;
}
_leave ( " = %d " , ret ) ;
return ret ;
}
/*
* Stick a marker on the cache object to indicate that it ' s dirty .
*/
void cachefiles_prepare_to_write ( struct fscache_cookie * cookie )
{
const struct cred * saved_cred ;
struct cachefiles_object * object = cookie - > cache_priv ;
struct cachefiles_cache * cache = object - > volume - > cache ;
_enter ( " c=%08x " , object - > cookie - > debug_id ) ;
if ( ! test_bit ( CACHEFILES_OBJECT_USING_TMPFILE , & object - > flags ) ) {
cachefiles_begin_secure ( cache , & saved_cred ) ;
cachefiles_set_object_xattr ( object ) ;
cachefiles_end_secure ( cache , saved_cred ) ;
}
}
2021-12-14 09:51:43 +00:00
/*
* Set the state xattr on a volume directory .
*/
bool cachefiles_set_volume_xattr ( struct cachefiles_volume * volume )
{
unsigned int len = volume - > vcookie - > coherency_len ;
const void * p = volume - > vcookie - > coherency ;
struct dentry * dentry = volume - > dentry ;
int ret ;
_enter ( " %x,#%d " , volume - > vcookie - > debug_id , len ) ;
ret = cachefiles_inject_write_error ( ) ;
if ( ret = = 0 )
ret = vfs_setxattr ( & init_user_ns , dentry , cachefiles_xattr_cache ,
p , len , 0 ) ;
if ( ret < 0 ) {
trace_cachefiles_vfs_error ( NULL , d_inode ( dentry ) , ret ,
cachefiles_trace_setxattr_error ) ;
trace_cachefiles_vol_coherency ( volume , d_inode ( dentry ) - > i_ino ,
cachefiles_coherency_vol_set_fail ) ;
if ( ret ! = - ENOMEM )
cachefiles_io_error (
volume - > cache , " Failed to set xattr with error %d " , ret ) ;
} else {
trace_cachefiles_vol_coherency ( volume , d_inode ( dentry ) - > i_ino ,
cachefiles_coherency_vol_set_ok ) ;
}
_leave ( " = %d " , ret ) ;
return ret = = 0 ;
}
/*
* Check the consistency between the backing cache and the volume cookie .
*/
int cachefiles_check_volume_xattr ( struct cachefiles_volume * volume )
{
struct cachefiles_xattr * buf ;
struct dentry * dentry = volume - > dentry ;
unsigned int len = volume - > vcookie - > coherency_len ;
const void * p = volume - > vcookie - > coherency ;
enum cachefiles_coherency_trace why ;
ssize_t xlen ;
int ret = - ESTALE ;
_enter ( " " ) ;
buf = kmalloc ( len , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
xlen = cachefiles_inject_read_error ( ) ;
if ( xlen = = 0 )
xlen = vfs_getxattr ( & init_user_ns , dentry , cachefiles_xattr_cache , buf , len ) ;
if ( xlen ! = len ) {
if ( xlen < 0 ) {
trace_cachefiles_vfs_error ( NULL , d_inode ( dentry ) , xlen ,
cachefiles_trace_getxattr_error ) ;
if ( xlen = = - EIO )
cachefiles_io_error (
volume - > cache ,
" Failed to read xattr with error %zd " , xlen ) ;
}
why = cachefiles_coherency_vol_check_xattr ;
} else if ( memcmp ( buf - > data , p , len ) ! = 0 ) {
why = cachefiles_coherency_vol_check_cmp ;
} else {
why = cachefiles_coherency_vol_check_ok ;
ret = 0 ;
}
trace_cachefiles_vol_coherency ( volume , d_inode ( dentry ) - > i_ino , why ) ;
kfree ( buf ) ;
_leave ( " = %d " , ret ) ;
return ret ;
}