root/fs/cachefiles/internal.h
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* General netfs cache on cache files internal defs
 *
 * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 */

#ifdef pr_fmt
#undef pr_fmt
#endif

#define pr_fmt(fmt) "CacheFiles: " fmt


#include <linux/fscache-cache.h>
#include <linux/cred.h>
#include <linux/security.h>
#include <linux/xarray.h>
#include <linux/cachefiles.h>

#define CACHEFILES_DIO_BLOCK_SIZE 4096

struct cachefiles_cache;
struct cachefiles_object;

enum cachefiles_content {
        /* These values are saved on disk */
        CACHEFILES_CONTENT_NO_DATA      = 0, /* No content stored */
        CACHEFILES_CONTENT_SINGLE       = 1, /* Content is monolithic, all is present */
        CACHEFILES_CONTENT_ALL          = 2, /* Content is all present, no map */
        CACHEFILES_CONTENT_BACKFS_MAP   = 3, /* Content is piecemeal, mapped through backing fs */
        CACHEFILES_CONTENT_DIRTY        = 4, /* Content is dirty (only seen on disk) */
        nr__cachefiles_content
};

/*
 * Cached volume representation.
 */
struct cachefiles_volume {
        struct cachefiles_cache         *cache;
        struct list_head                cache_link;     /* Link in cache->volumes */
        struct fscache_volume           *vcookie;       /* The netfs's representation */
        struct dentry                   *dentry;        /* The volume dentry */
        struct dentry                   *fanout[256];   /* Fanout subdirs */
};

enum cachefiles_object_state {
        CACHEFILES_ONDEMAND_OBJSTATE_CLOSE, /* Anonymous fd closed by daemon or initial state */
        CACHEFILES_ONDEMAND_OBJSTATE_OPEN, /* Anonymous fd associated with object is available */
        CACHEFILES_ONDEMAND_OBJSTATE_REOPENING, /* Object that was closed and is being reopened. */
        CACHEFILES_ONDEMAND_OBJSTATE_DROPPING, /* Object is being dropped. */
};

struct cachefiles_ondemand_info {
        struct work_struct              ondemand_work;
        int                             ondemand_id;
        enum cachefiles_object_state    state;
        struct cachefiles_object        *object;
        spinlock_t                      lock;
};

/*
 * Backing file state.
 */
struct cachefiles_object {
        struct fscache_cookie           *cookie;        /* Netfs data storage object cookie */
        struct cachefiles_volume        *volume;        /* Cache volume that holds this object */
        struct list_head                cache_link;     /* Link in cache->*_list */
        struct file                     *file;          /* The file representing this object */
        char                            *d_name;        /* Backing file name */
        int                             debug_id;
        spinlock_t                      lock;
        refcount_t                      ref;
        enum cachefiles_content         content_info:8; /* Info about content presence */
        unsigned long                   flags;
#define CACHEFILES_OBJECT_USING_TMPFILE 0               /* Have an unlinked tmpfile */
#ifdef CONFIG_CACHEFILES_ONDEMAND
        struct cachefiles_ondemand_info *ondemand;
#endif
};

#define CACHEFILES_ONDEMAND_ID_CLOSED   -1

/*
 * Cache files cache definition
 */
struct cachefiles_cache {
        struct fscache_cache            *cache;         /* Cache cookie */
        struct vfsmount                 *mnt;           /* mountpoint holding the cache */
        struct dentry                   *store;         /* Directory into which live objects go */
        struct dentry                   *graveyard;     /* directory into which dead objects go */
        struct file                     *cachefilesd;   /* manager daemon handle */
        struct list_head                volumes;        /* List of volume objects */
        struct list_head                object_list;    /* List of active objects */
        spinlock_t                      object_list_lock; /* Lock for volumes and object_list */
        const struct cred               *cache_cred;    /* security override for accessing cache */
        struct mutex                    daemon_mutex;   /* command serialisation mutex */
        wait_queue_head_t               daemon_pollwq;  /* poll waitqueue for daemon */
        atomic_t                        gravecounter;   /* graveyard uniquifier */
        atomic_t                        f_released;     /* number of objects released lately */
        atomic_long_t                   b_released;     /* number of blocks released lately */
        atomic_long_t                   b_writing;      /* Number of blocks being written */
        unsigned                        frun_percent;   /* when to stop culling (% files) */
        unsigned                        fcull_percent;  /* when to start culling (% files) */
        unsigned                        fstop_percent;  /* when to stop allocating (% files) */
        unsigned                        brun_percent;   /* when to stop culling (% blocks) */
        unsigned                        bcull_percent;  /* when to start culling (% blocks) */
        unsigned                        bstop_percent;  /* when to stop allocating (% blocks) */
        unsigned                        bsize;          /* cache's block size */
        unsigned                        bshift;         /* ilog2(bsize) */
        uint64_t                        frun;           /* when to stop culling */
        uint64_t                        fcull;          /* when to start culling */
        uint64_t                        fstop;          /* when to stop allocating */
        sector_t                        brun;           /* when to stop culling */
        sector_t                        bcull;          /* when to start culling */
        sector_t                        bstop;          /* when to stop allocating */
        unsigned long                   flags;
#define CACHEFILES_READY                0       /* T if cache prepared */
#define CACHEFILES_DEAD                 1       /* T if cache dead */
#define CACHEFILES_CULLING              2       /* T if cull engaged */
#define CACHEFILES_STATE_CHANGED        3       /* T if state changed (poll trigger) */
#define CACHEFILES_ONDEMAND_MODE        4       /* T if in on-demand read mode */
        char                            *rootdirname;   /* name of cache root directory */
        char                            *tag;           /* cache binding tag */
        refcount_t                      unbind_pincount;/* refcount to do daemon unbind */
        struct xarray                   reqs;           /* xarray of pending on-demand requests */
        unsigned long                   req_id_next;
        struct xarray                   ondemand_ids;   /* xarray for ondemand_id allocation */
        u32                             ondemand_id_next;
        u32                             msg_id_next;
        u32                             secid;          /* LSM security id */
        bool                            have_secid;     /* whether "secid" was set */
};

static inline bool cachefiles_in_ondemand_mode(struct cachefiles_cache *cache)
{
        return IS_ENABLED(CONFIG_CACHEFILES_ONDEMAND) &&
                test_bit(CACHEFILES_ONDEMAND_MODE, &cache->flags);
}

struct cachefiles_req {
        struct cachefiles_object *object;
        struct completion done;
        refcount_t ref;
        int error;
        struct cachefiles_msg msg;
};

#define CACHEFILES_REQ_NEW      XA_MARK_1

#include <trace/events/cachefiles.h>

static inline
struct file *cachefiles_cres_file(struct netfs_cache_resources *cres)
{
        return cres->cache_priv2;
}

static inline
struct cachefiles_object *cachefiles_cres_object(struct netfs_cache_resources *cres)
{
        return fscache_cres_cookie(cres)->cache_priv;
}

/*
 * note change of state for daemon
 */
static inline void cachefiles_state_changed(struct cachefiles_cache *cache)
{
        set_bit(CACHEFILES_STATE_CHANGED, &cache->flags);
        wake_up_all(&cache->daemon_pollwq);
}

/*
 * cache.c
 */
extern int cachefiles_add_cache(struct cachefiles_cache *cache);
extern void cachefiles_withdraw_cache(struct cachefiles_cache *cache);

enum cachefiles_has_space_for {
        cachefiles_has_space_check,
        cachefiles_has_space_for_write,
        cachefiles_has_space_for_create,
};
extern int cachefiles_has_space(struct cachefiles_cache *cache,
                                unsigned fnr, unsigned bnr,
                                enum cachefiles_has_space_for reason);

/*
 * daemon.c
 */
extern const struct file_operations cachefiles_daemon_fops;
extern void cachefiles_flush_reqs(struct cachefiles_cache *cache);
extern void cachefiles_get_unbind_pincount(struct cachefiles_cache *cache);
extern void cachefiles_put_unbind_pincount(struct cachefiles_cache *cache);

/*
 * error_inject.c
 */
#ifdef CONFIG_CACHEFILES_ERROR_INJECTION
extern unsigned int cachefiles_error_injection_state;
extern int cachefiles_register_error_injection(void);
extern void cachefiles_unregister_error_injection(void);

#else
#define cachefiles_error_injection_state 0

static inline int cachefiles_register_error_injection(void)
{
        return 0;
}

static inline void cachefiles_unregister_error_injection(void)
{
}
#endif


static inline int cachefiles_inject_read_error(void)
{
        return cachefiles_error_injection_state & 2 ? -EIO : 0;
}

static inline int cachefiles_inject_write_error(void)
{
        return cachefiles_error_injection_state & 2 ? -EIO :
                cachefiles_error_injection_state & 1 ? -ENOSPC :
                0;
}

static inline int cachefiles_inject_remove_error(void)
{
        return cachefiles_error_injection_state & 2 ? -EIO : 0;
}

/*
 * interface.c
 */
extern const struct fscache_cache_ops cachefiles_cache_ops;
extern void cachefiles_see_object(struct cachefiles_object *object,
                                  enum cachefiles_obj_ref_trace why);
extern struct cachefiles_object *cachefiles_grab_object(struct cachefiles_object *object,
                                                        enum cachefiles_obj_ref_trace why);
extern void cachefiles_put_object(struct cachefiles_object *object,
                                  enum cachefiles_obj_ref_trace why);

/*
 * io.c
 */
extern bool cachefiles_begin_operation(struct netfs_cache_resources *cres,
                                       enum fscache_want_state want_state);
extern int __cachefiles_prepare_write(struct cachefiles_object *object,
                                      struct file *file,
                                      loff_t *_start, size_t *_len, size_t upper_len,
                                      bool no_space_allocated_yet);
extern int __cachefiles_write(struct cachefiles_object *object,
                              struct file *file,
                              loff_t start_pos,
                              struct iov_iter *iter,
                              netfs_io_terminated_t term_func,
                              void *term_func_priv);

/*
 * key.c
 */
extern bool cachefiles_cook_key(struct cachefiles_object *object);

/*
 * main.c
 */
extern struct kmem_cache *cachefiles_object_jar;

/*
 * namei.c
 */
extern void cachefiles_unmark_inode_in_use(struct cachefiles_object *object,
                                           struct file *file);
extern int cachefiles_bury_object(struct cachefiles_cache *cache,
                                  struct cachefiles_object *object,
                                  struct dentry *dir,
                                  struct dentry *rep,
                                  enum fscache_why_object_killed why);
extern int cachefiles_delete_object(struct cachefiles_object *object,
                                    enum fscache_why_object_killed why);
extern bool cachefiles_look_up_object(struct cachefiles_object *object);
extern struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
                                               struct dentry *dir,
                                               const char *name,
                                               bool *_is_new);
extern void cachefiles_put_directory(struct dentry *dir);

extern int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir,
                           char *filename);

extern int cachefiles_check_in_use(struct cachefiles_cache *cache,
                                   struct dentry *dir, char *filename);
extern struct file *cachefiles_create_tmpfile(struct cachefiles_object *object);
extern bool cachefiles_commit_tmpfile(struct cachefiles_cache *cache,
                                      struct cachefiles_object *object);

/*
 * ondemand.c
 */
#ifdef CONFIG_CACHEFILES_ONDEMAND
extern ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache,
                                        char __user *_buffer, size_t buflen);

extern int cachefiles_ondemand_copen(struct cachefiles_cache *cache,
                                     char *args);

extern int cachefiles_ondemand_restore(struct cachefiles_cache *cache,
                                        char *args);

extern int cachefiles_ondemand_init_object(struct cachefiles_object *object);
extern void cachefiles_ondemand_clean_object(struct cachefiles_object *object);

extern int cachefiles_ondemand_read(struct cachefiles_object *object,
                                    loff_t pos, size_t len);

extern int cachefiles_ondemand_init_obj_info(struct cachefiles_object *obj,
                                        struct cachefiles_volume *volume);
extern void cachefiles_ondemand_deinit_obj_info(struct cachefiles_object *obj);

#define CACHEFILES_OBJECT_STATE_FUNCS(_state, _STATE)   \
static inline bool                                                              \
cachefiles_ondemand_object_is_##_state(const struct cachefiles_object *object) \
{                                                                                               \
        return object->ondemand->state == CACHEFILES_ONDEMAND_OBJSTATE_##_STATE; \
}                                                                                               \
                                                                                                \
static inline void                                                              \
cachefiles_ondemand_set_object_##_state(struct cachefiles_object *object) \
{                                                                                               \
        object->ondemand->state = CACHEFILES_ONDEMAND_OBJSTATE_##_STATE; \
}

CACHEFILES_OBJECT_STATE_FUNCS(open, OPEN);
CACHEFILES_OBJECT_STATE_FUNCS(close, CLOSE);
CACHEFILES_OBJECT_STATE_FUNCS(reopening, REOPENING);
CACHEFILES_OBJECT_STATE_FUNCS(dropping, DROPPING);

static inline bool cachefiles_ondemand_is_reopening_read(struct cachefiles_req *req)
{
        return cachefiles_ondemand_object_is_reopening(req->object) &&
                        req->msg.opcode == CACHEFILES_OP_READ;
}

#else
static inline ssize_t cachefiles_ondemand_daemon_read(struct cachefiles_cache *cache,
                                        char __user *_buffer, size_t buflen)
{
        return -EOPNOTSUPP;
}

static inline int cachefiles_ondemand_init_object(struct cachefiles_object *object)
{
        return 0;
}

static inline void cachefiles_ondemand_clean_object(struct cachefiles_object *object)
{
}

static inline int cachefiles_ondemand_read(struct cachefiles_object *object,
                                           loff_t pos, size_t len)
{
        return -EOPNOTSUPP;
}

static inline int cachefiles_ondemand_init_obj_info(struct cachefiles_object *obj,
                                                struct cachefiles_volume *volume)
{
        return 0;
}
static inline void cachefiles_ondemand_deinit_obj_info(struct cachefiles_object *obj)
{
}

static inline bool cachefiles_ondemand_is_reopening_read(struct cachefiles_req *req)
{
        return false;
}
#endif

/*
 * security.c
 */
extern int cachefiles_get_security_ID(struct cachefiles_cache *cache);
extern int cachefiles_determine_cache_security(struct cachefiles_cache *cache,
                                               struct dentry *root,
                                               const struct cred **_saved_cred);

static inline void cachefiles_begin_secure(struct cachefiles_cache *cache,
                                           const struct cred **_saved_cred)
{
        *_saved_cred = override_creds(cache->cache_cred);
}

static inline void cachefiles_end_secure(struct cachefiles_cache *cache,
                                         const struct cred *saved_cred)
{
        revert_creds(saved_cred);
}

/*
 * volume.c
 */
void cachefiles_acquire_volume(struct fscache_volume *volume);
void cachefiles_free_volume(struct fscache_volume *volume);
void cachefiles_withdraw_volume(struct cachefiles_volume *volume);

/*
 * xattr.c
 */
extern int cachefiles_set_object_xattr(struct cachefiles_object *object);
extern int cachefiles_check_auxdata(struct cachefiles_object *object,
                                    struct file *file);
extern int cachefiles_remove_object_xattr(struct cachefiles_cache *cache,
                                          struct cachefiles_object *object,
                                          struct dentry *dentry);
extern void cachefiles_prepare_to_write(struct fscache_cookie *cookie);
extern bool cachefiles_set_volume_xattr(struct cachefiles_volume *volume);
extern int cachefiles_check_volume_xattr(struct cachefiles_volume *volume);

/*
 * Error handling
 */
#define cachefiles_io_error(___cache, FMT, ...)         \
do {                                                    \
        pr_err("I/O Error: " FMT"\n", ##__VA_ARGS__);   \
        fscache_io_error((___cache)->cache);            \
        set_bit(CACHEFILES_DEAD, &(___cache)->flags);   \
        if (cachefiles_in_ondemand_mode(___cache))      \
                cachefiles_flush_reqs(___cache);        \
} while (0)

#define cachefiles_io_error_obj(object, FMT, ...)                       \
do {                                                                    \
        struct cachefiles_cache *___cache;                              \
                                                                        \
        ___cache = (object)->volume->cache;                             \
        cachefiles_io_error(___cache, FMT " [o=%08x]", ##__VA_ARGS__,   \
                            (object)->debug_id);                        \
} while (0)


/*
 * Debug tracing
 */
extern unsigned cachefiles_debug;
#define CACHEFILES_DEBUG_KENTER 1
#define CACHEFILES_DEBUG_KLEAVE 2
#define CACHEFILES_DEBUG_KDEBUG 4

#define dbgprintk(FMT, ...) \
        printk(KERN_DEBUG "[%-6.6s] "FMT"\n", current->comm, ##__VA_ARGS__)

#define kenter(FMT, ...) dbgprintk("==> %s("FMT")", __func__, ##__VA_ARGS__)
#define kleave(FMT, ...) dbgprintk("<== %s()"FMT"", __func__, ##__VA_ARGS__)
#define kdebug(FMT, ...) dbgprintk(FMT, ##__VA_ARGS__)


#if defined(__KDEBUG)
#define _enter(FMT, ...) kenter(FMT, ##__VA_ARGS__)
#define _leave(FMT, ...) kleave(FMT, ##__VA_ARGS__)
#define _debug(FMT, ...) kdebug(FMT, ##__VA_ARGS__)

#elif defined(CONFIG_CACHEFILES_DEBUG)
#define _enter(FMT, ...)                                \
do {                                                    \
        if (cachefiles_debug & CACHEFILES_DEBUG_KENTER) \
                kenter(FMT, ##__VA_ARGS__);             \
} while (0)

#define _leave(FMT, ...)                                \
do {                                                    \
        if (cachefiles_debug & CACHEFILES_DEBUG_KLEAVE) \
                kleave(FMT, ##__VA_ARGS__);             \
} while (0)

#define _debug(FMT, ...)                                \
do {                                                    \
        if (cachefiles_debug & CACHEFILES_DEBUG_KDEBUG) \
                kdebug(FMT, ##__VA_ARGS__);             \
} while (0)

#else
#define _enter(FMT, ...) no_printk("==> %s("FMT")", __func__, ##__VA_ARGS__)
#define _leave(FMT, ...) no_printk("<== %s()"FMT"", __func__, ##__VA_ARGS__)
#define _debug(FMT, ...) no_printk(FMT, ##__VA_ARGS__)
#endif

#if 1 /* defined(__KDEBUGALL) */

#define ASSERT(X)                                                       \
do {                                                                    \
        if (unlikely(!(X))) {                                           \
                pr_err("\n");                                           \
                pr_err("Assertion failed\n");           \
                BUG();                                                  \
        }                                                               \
} while (0)

#define ASSERTCMP(X, OP, Y)                                             \
do {                                                                    \
        if (unlikely(!((X) OP (Y)))) {                                  \
                pr_err("\n");                                           \
                pr_err("Assertion failed\n");           \
                pr_err("%lx " #OP " %lx is false\n",                    \
                       (unsigned long)(X), (unsigned long)(Y));         \
                BUG();                                                  \
        }                                                               \
} while (0)

#define ASSERTIF(C, X)                                                  \
do {                                                                    \
        if (unlikely((C) && !(X))) {                                    \
                pr_err("\n");                                           \
                pr_err("Assertion failed\n");           \
                BUG();                                                  \
        }                                                               \
} while (0)

#define ASSERTIFCMP(C, X, OP, Y)                                        \
do {                                                                    \
        if (unlikely((C) && !((X) OP (Y)))) {                           \
                pr_err("\n");                                           \
                pr_err("Assertion failed\n");           \
                pr_err("%lx " #OP " %lx is false\n",                    \
                       (unsigned long)(X), (unsigned long)(Y));         \
                BUG();                                                  \
        }                                                               \
} while (0)

#else

#define ASSERT(X)                       do {} while (0)
#define ASSERTCMP(X, OP, Y)             do {} while (0)
#define ASSERTIF(C, X)                  do {} while (0)
#define ASSERTIFCMP(C, X, OP, Y)        do {} while (0)

#endif