linux/fs/ceph/inode.c

// SPDX-License-Identifier: GPL-2.0
#include <linux/ceph/ceph_debug.h>

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include <linux/writeback.h>
#include <linux/vmalloc.h>
#include <linux/xattr.h>
#include <linux/posix_acl.h>
#include <linux/random.h>
#include <linux/sort.h>
#include <linux/iversion.h>
#include <linux/fscrypt.h>

#include "super.h"
#include "mds_client.h"
#include "cache.h"
#include "crypto.h"
#include <linux/ceph/decode.h>

/*
 * Ceph inode operations
 *
 * Implement basic inode helpers (get, alloc) and inode ops (getattr,
 * setattr, etc.), xattr helpers, and helpers for assimilating
 * metadata returned by the MDS into our cache.
 *
 * Also define helpers for doing asynchronous writeback, invalidation,
 * and truncation for the benefit of those who can't afford to block
 * (typically because they are in the message handler path).
 */

static const struct inode_operations ceph_symlink_iops;
static const struct inode_operations ceph_encrypted_symlink_iops;

static void ceph_inode_work(struct work_struct *work);

/*
 * find or create an inode, given the ceph ino number
 */
static int ceph_set_ino_cb(struct inode *inode, void *data)
{}

/**
 * ceph_new_inode - allocate a new inode in advance of an expected create
 * @dir: parent directory for new inode
 * @dentry: dentry that may eventually point to new inode
 * @mode: mode of new inode
 * @as_ctx: pointer to inherited security context
 *
 * Allocate a new inode in advance of an operation to create a new inode.
 * This allocates the inode and sets up the acl_sec_ctx with appropriate
 * info for the new inode.
 *
 * Returns a pointer to the new inode or an ERR_PTR.
 */
struct inode *ceph_new_inode(struct inode *dir, struct dentry *dentry,
			     umode_t *mode, struct ceph_acl_sec_ctx *as_ctx)
{}

void ceph_as_ctx_to_req(struct ceph_mds_request *req,
			struct ceph_acl_sec_ctx *as_ctx)
{}

/**
 * ceph_get_inode - find or create/hash a new inode
 * @sb: superblock to search and allocate in
 * @vino: vino to search for
 * @newino: optional new inode to insert if one isn't found (may be NULL)
 *
 * Search for or insert a new inode into the hash for the given vino, and
 * return a reference to it. If new is non-NULL, its reference is consumed.
 */
struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino,
			     struct inode *newino)
{}

/*
 * get/constuct snapdir inode for a given directory
 */
struct inode *ceph_get_snapdir(struct inode *parent)
{}

const struct inode_operations ceph_file_iops =;


/*
 * We use a 'frag tree' to keep track of the MDS's directory fragments
 * for a given inode (usually there is just a single fragment).  We
 * need to know when a child frag is delegated to a new MDS, or when
 * it is flagged as replicated, so we can direct our requests
 * accordingly.
 */

/*
 * find/create a frag in the tree
 */
static struct ceph_inode_frag *__get_or_create_frag(struct ceph_inode_info *ci,
						    u32 f)
{}

/*
 * find a specific frag @f
 */
struct ceph_inode_frag *__ceph_find_frag(struct ceph_inode_info *ci, u32 f)
{}

/*
 * Choose frag containing the given value @v.  If @pfrag is
 * specified, copy the frag delegation info to the caller if
 * it is present.
 */
static u32 __ceph_choose_frag(struct ceph_inode_info *ci, u32 v,
			      struct ceph_inode_frag *pfrag, int *found)
{}

u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v,
		     struct ceph_inode_frag *pfrag, int *found)
{}

/*
 * Process dirfrag (delegation) info from the mds.  Include leaf
 * fragment in tree ONLY if ndist > 0.  Otherwise, only
 * branches/splits are included in i_fragtree)
 */
static int ceph_fill_dirfrag(struct inode *inode,
			     struct ceph_mds_reply_dirfrag *dirinfo)
{}

static int frag_tree_split_cmp(const void *l, const void *r)
{}

static bool is_frag_child(u32 f, struct ceph_inode_frag *frag)
{}

static int ceph_fill_fragtree(struct inode *inode,
			      struct ceph_frag_tree_head *fragtree,
			      struct ceph_mds_reply_dirfrag *dirinfo)
{}

/*
 * initialize a newly allocated inode.
 */
struct inode *ceph_alloc_inode(struct super_block *sb)
{}

void ceph_free_inode(struct inode *inode)
{}

void ceph_evict_inode(struct inode *inode)
{}

static inline blkcnt_t calc_inode_blocks(u64 size)
{}

/*
 * Helpers to fill in size, ctime, mtime, and atime.  We have to be
 * careful because either the client or MDS may have more up to date
 * info, depending on which capabilities are held, and whether
 * time_warp_seq or truncate_seq have increased.  (Ordinarily, mtime
 * and size are monotonically increasing, except when utimes() or
 * truncate() increments the corresponding _seq values.)
 */
int ceph_fill_file_size(struct inode *inode, int issued,
			u32 truncate_seq, u64 truncate_size, u64 size)
{}

void ceph_fill_file_time(struct inode *inode, int issued,
			 u64 time_warp_seq, struct timespec64 *ctime,
			 struct timespec64 *mtime, struct timespec64 *atime)
{}

#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
static int decode_encrypted_symlink(struct ceph_mds_client *mdsc,
				    const char *encsym,
				    int enclen, u8 **decsym)
{}
#else
static int decode_encrypted_symlink(struct ceph_mds_client *mdsc,
				    const char *encsym,
				    int symlen, u8 **decsym)
{
	return -EOPNOTSUPP;
}
#endif

/*
 * Populate an inode based on info from mds.  May be called on new or
 * existing inodes.
 */
int ceph_fill_inode(struct inode *inode, struct page *locked_page,
		    struct ceph_mds_reply_info_in *iinfo,
		    struct ceph_mds_reply_dirfrag *dirinfo,
		    struct ceph_mds_session *session, int cap_fmode,
		    struct ceph_cap_reservation *caps_reservation)
{}

/*
 * caller should hold session s_mutex and dentry->d_lock.
 */
static void __update_dentry_lease(struct inode *dir, struct dentry *dentry,
				  struct ceph_mds_reply_lease *lease,
				  struct ceph_mds_session *session,
				  unsigned long from_time,
				  struct ceph_mds_session **old_lease_session)
{}

static inline void update_dentry_lease(struct inode *dir, struct dentry *dentry,
					struct ceph_mds_reply_lease *lease,
					struct ceph_mds_session *session,
					unsigned long from_time)
{}

/*
 * update dentry lease without having parent inode locked
 */
static void update_dentry_lease_careful(struct dentry *dentry,
					struct ceph_mds_reply_lease *lease,
					struct ceph_mds_session *session,
					unsigned long from_time,
					char *dname, u32 dname_len,
					struct ceph_vino *pdvino,
					struct ceph_vino *ptvino)

{}

/*
 * splice a dentry to an inode.
 * caller must hold directory i_rwsem for this to be safe.
 */
static int splice_dentry(struct dentry **pdn, struct inode *in)
{}

/*
 * Incorporate results into the local cache.  This is either just
 * one inode, or a directory, dentry, and possibly linked-to inode (e.g.,
 * after a lookup).
 *
 * A reply may contain
 *         a directory inode along with a dentry.
 *  and/or a target inode
 *
 * Called with snap_rwsem (read).
 */
int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
{}

/*
 * Prepopulate our cache with readdir results, leases, etc.
 */
static int readdir_prepopulate_inodes_only(struct ceph_mds_request *req,
					   struct ceph_mds_session *session)
{}

void ceph_readdir_cache_release(struct ceph_readdir_cache_control *ctl)
{}

static int fill_readdir_cache(struct inode *dir, struct dentry *dn,
			      struct ceph_readdir_cache_control *ctl,
			      struct ceph_mds_request *req)
{}

int ceph_readdir_prepopulate(struct ceph_mds_request *req,
			     struct ceph_mds_session *session)
{}

bool ceph_inode_set_size(struct inode *inode, loff_t size)
{}

void ceph_queue_inode_work(struct inode *inode, int work_bit)
{}

static void ceph_do_invalidate_pages(struct inode *inode)
{}

/*
 * Make sure any pending truncation is applied before doing anything
 * that may depend on it.
 */
void __ceph_do_pending_vmtruncate(struct inode *inode)
{}

static void ceph_inode_work(struct work_struct *work)
{}

static const char *ceph_encrypted_get_link(struct dentry *dentry,
					   struct inode *inode,
					   struct delayed_call *done)
{}

static int ceph_encrypted_symlink_getattr(struct mnt_idmap *idmap,
					  const struct path *path,
					  struct kstat *stat, u32 request_mask,
					  unsigned int query_flags)
{}

/*
 * symlinks
 */
static const struct inode_operations ceph_symlink_iops =;

static const struct inode_operations ceph_encrypted_symlink_iops =;

/*
 * Transfer the encrypted last block to the MDS and the MDS
 * will help update it when truncating a smaller size.
 *
 * We don't support a PAGE_SIZE that is smaller than the
 * CEPH_FSCRYPT_BLOCK_SIZE.
 */
static int fill_fscrypt_truncate(struct inode *inode,
				 struct ceph_mds_request *req,
				 struct iattr *attr)
{}

int __ceph_setattr(struct mnt_idmap *idmap, struct inode *inode,
		   struct iattr *attr, struct ceph_iattr *cia)
{}

/*
 * setattr
 */
int ceph_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
		 struct iattr *attr)
{}

int ceph_try_to_choose_auth_mds(struct inode *inode, int mask)
{}

/*
 * Verify that we have a lease on the given mask.  If not,
 * do a getattr against an mds.
 */
int __ceph_do_getattr(struct inode *inode, struct page *locked_page,
		      int mask, bool force)
{}

int ceph_do_getvxattr(struct inode *inode, const char *name, void *value,
		      size_t size)
{}


/*
 * Check inode permissions.  We verify we have a valid value for
 * the AUTH cap, then call the generic handler.
 */
int ceph_permission(struct mnt_idmap *idmap, struct inode *inode,
		    int mask)
{}

/* Craft a mask of needed caps given a set of requested statx attrs. */
static int statx_to_caps(u32 want, umode_t mode)
{}

/*
 * Get all the attributes. If we have sufficient caps for the requested attrs,
 * then we can avoid talking to the MDS at all.
 */
int ceph_getattr(struct mnt_idmap *idmap, const struct path *path,
		 struct kstat *stat, u32 request_mask, unsigned int flags)
{}

void ceph_inode_shutdown(struct inode *inode)
{}