3259 lines · 90.6 KB · C
RawDownload
// 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){	struct ceph_inode_info *ci = ceph_inode(inode);	struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb); 	ci->i_vino = *(struct ceph_vino *)data;	inode->i_ino = ceph_vino_to_ino_t(ci->i_vino);	inode_set_iversion_raw(inode, 0);	percpu_counter_inc(&mdsc->metric.total_inodes); 	return 0;} /* * Check if the parent inode matches the vino from directory reply info */static inline bool ceph_vino_matches_parent(struct inode *parent,					    struct ceph_vino vino){	return ceph_ino(parent) == vino.ino && ceph_snap(parent) == vino.snap;} /* * Validate that the directory inode referenced by @req->r_parent matches the * inode number and snapshot id contained in the reply's directory record.  If * they do not match – which can theoretically happen if the parent dentry was * moved between the time the request was issued and the reply arrived – fall * back to looking up the correct inode in the inode cache. * * A reference is *always* returned.  Callers that receive a different inode * than the original @parent are responsible for dropping the extra reference * once the reply has been processed. */static struct inode *ceph_get_reply_dir(struct super_block *sb,					struct inode *parent,					struct ceph_mds_reply_info_parsed *rinfo){	struct ceph_vino vino; 	if (unlikely(!rinfo->diri.in))		return parent; /* nothing to compare against */ 	/* If we didn't have a cached parent inode to begin with, just bail out. */	if (!parent)		return NULL; 	vino.ino  = le64_to_cpu(rinfo->diri.in->ino);	vino.snap = le64_to_cpu(rinfo->diri.in->snapid); 	if (likely(ceph_vino_matches_parent(parent, vino)))		return parent; /* matches – use the original reference */ 	/* Mismatch – this should be rare.  Emit a WARN and obtain the correct inode. */	WARN_ONCE(1, "ceph: reply dir mismatch (parent valid %llx.%llx reply %llx.%llx)\n",		  ceph_ino(parent), ceph_snap(parent), vino.ino, vino.snap); 	return ceph_get_inode(sb, vino, NULL);} /** * 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){	int err;	struct inode *inode; 	inode = new_inode(dir->i_sb);	if (!inode)		return ERR_PTR(-ENOMEM); 	inode->i_blkbits = CEPH_FSCRYPT_BLOCK_SHIFT; 	if (!S_ISLNK(*mode)) {		err = ceph_pre_init_acls(dir, mode, as_ctx);		if (err < 0)			goto out_err;	} 	inode_state_assign_raw(inode, 0);	inode->i_mode = *mode; 	err = ceph_security_init_secctx(dentry, *mode, as_ctx);	if (err < 0)		goto out_err; 	/*	 * We'll skip setting fscrypt context for snapshots, leaving that for	 * the handle_reply().	 */	if (ceph_snap(dir) != CEPH_SNAPDIR) {		err = ceph_fscrypt_prepare_context(dir, inode, as_ctx);		if (err)			goto out_err;	} 	return inode;out_err:	iput(inode);	return ERR_PTR(err);} void ceph_as_ctx_to_req(struct ceph_mds_request *req,			struct ceph_acl_sec_ctx *as_ctx){	if (as_ctx->pagelist) {		req->r_pagelist = as_ctx->pagelist;		as_ctx->pagelist = NULL;	}	ceph_fscrypt_as_ctx_to_req(req, 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){	struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(sb);	struct ceph_client *cl = mdsc->fsc->client;	struct inode *inode; 	if (ceph_vino_is_reserved(vino))		return ERR_PTR(-EREMOTEIO); 	if (newino) {		inode = inode_insert5(newino, (unsigned long)vino.ino,				      ceph_ino_compare, ceph_set_ino_cb, &vino);		if (inode != newino)			iput(newino);	} else {		inode = iget5_locked(sb, (unsigned long)vino.ino,				     ceph_ino_compare, ceph_set_ino_cb, &vino);	} 	if (!inode) {		doutc(cl, "no inode found for %llx.%llx\n", vino.ino, vino.snap);		return ERR_PTR(-ENOMEM);	} 	doutc(cl, "on %llx=%llx.%llx got %p new %d\n",	      ceph_present_inode(inode), ceph_vinop(inode), inode,	      !!(inode_state_read_once(inode) & I_NEW));	return inode;} /* * get/construct snapdir inode for a given directory */struct inode *ceph_get_snapdir(struct inode *parent){	struct ceph_client *cl = ceph_inode_to_client(parent);	struct ceph_vino vino = {		.ino = ceph_ino(parent),		.snap = CEPH_SNAPDIR,	};	struct inode *inode = ceph_get_inode(parent->i_sb, vino, NULL);	struct ceph_inode_info *ci = ceph_inode(inode);	int ret = -ENOTDIR; 	if (IS_ERR(inode))		return inode; 	if (!S_ISDIR(parent->i_mode)) {		pr_warn_once_client(cl, "bad snapdir parent type (mode=0%o)\n",				    parent->i_mode);		goto err;	} 	if (!(inode_state_read_once(inode) & I_NEW) && !S_ISDIR(inode->i_mode)) {		pr_warn_once_client(cl, "bad snapdir inode type (mode=0%o)\n",				    inode->i_mode);		goto err;	} 	inode->i_mode = parent->i_mode;	inode->i_uid = parent->i_uid;	inode->i_gid = parent->i_gid;	inode_set_mtime_to_ts(inode, inode_get_mtime(parent));	inode_set_ctime_to_ts(inode, inode_get_ctime(parent));	inode_set_atime_to_ts(inode, inode_get_atime(parent));	ci->i_rbytes = 0;	ci->i_btime = ceph_inode(parent)->i_btime; #ifdef CONFIG_FS_ENCRYPTION	/* if encrypted, just borrow fscrypt_auth from parent */	if (IS_ENCRYPTED(parent)) {		struct ceph_inode_info *pci = ceph_inode(parent); 		ci->fscrypt_auth = kmemdup(pci->fscrypt_auth,					   pci->fscrypt_auth_len,					   GFP_KERNEL);		if (ci->fscrypt_auth) {			inode->i_flags |= S_ENCRYPTED;			ci->fscrypt_auth_len = pci->fscrypt_auth_len;		} else {			doutc(cl, "Failed to alloc snapdir fscrypt_auth\n");			ret = -ENOMEM;			goto err;		}	}#endif	if (inode_state_read_once(inode) & I_NEW) {		inode->i_op = &ceph_snapdir_iops;		inode->i_fop = &ceph_snapdir_fops;		ci->i_snap_caps = CEPH_CAP_PIN; /* so we can open */		unlock_new_inode(inode);	} 	return inode;err:	if ((inode_state_read_once(inode) & I_NEW))		discard_new_inode(inode);	else		iput(inode);	return ERR_PTR(ret);} const struct inode_operations ceph_file_iops = {	.permission = ceph_permission,	.setattr = ceph_setattr,	.getattr = ceph_getattr,	.listxattr = ceph_listxattr,	.get_inode_acl = ceph_get_acl,	.set_acl = ceph_set_acl,};  /* * 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){	struct inode *inode = &ci->netfs.inode;	struct ceph_client *cl = ceph_inode_to_client(inode);	struct rb_node **p;	struct rb_node *parent = NULL;	struct ceph_inode_frag *frag;	int c; 	p = &ci->i_fragtree.rb_node;	while (*p) {		parent = *p;		frag = rb_entry(parent, struct ceph_inode_frag, node);		c = ceph_frag_compare(f, frag->frag);		if (c < 0)			p = &(*p)->rb_left;		else if (c > 0)			p = &(*p)->rb_right;		else			return frag;	} 	frag = kmalloc_obj(*frag, GFP_NOFS);	if (!frag)		return ERR_PTR(-ENOMEM); 	frag->frag = f;	frag->split_by = 0;	frag->mds = -1;	frag->ndist = 0; 	rb_link_node(&frag->node, parent, p);	rb_insert_color(&frag->node, &ci->i_fragtree); 	doutc(cl, "added %p %llx.%llx frag %x\n", inode, ceph_vinop(inode), f);	return frag;} /* * find a specific frag @f */struct ceph_inode_frag *__ceph_find_frag(struct ceph_inode_info *ci, u32 f){	struct rb_node *n = ci->i_fragtree.rb_node; 	while (n) {		struct ceph_inode_frag *frag =			rb_entry(n, struct ceph_inode_frag, node);		int c = ceph_frag_compare(f, frag->frag);		if (c < 0)			n = n->rb_left;		else if (c > 0)			n = n->rb_right;		else			return frag;	}	return NULL;} /* * 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){	struct ceph_client *cl = ceph_inode_to_client(&ci->netfs.inode);	u32 t = ceph_frag_make(0, 0);	struct ceph_inode_frag *frag;	unsigned nway, i;	u32 n; 	if (found)		*found = 0; 	while (1) {		WARN_ON(!ceph_frag_contains_value(t, v));		frag = __ceph_find_frag(ci, t);		if (!frag)			break; /* t is a leaf */		if (frag->split_by == 0) {			if (pfrag)				memcpy(pfrag, frag, sizeof(*pfrag));			if (found)				*found = 1;			break;		} 		/* choose child */		nway = 1 << frag->split_by;		doutc(cl, "frag(%x) %x splits by %d (%d ways)\n", v, t,		      frag->split_by, nway);		for (i = 0; i < nway; i++) {			n = ceph_frag_make_child(t, frag->split_by, i);			if (ceph_frag_contains_value(n, v)) {				t = n;				break;			}		}		BUG_ON(i == nway);	}	doutc(cl, "frag(%x) = %x\n", v, t); 	return t;} u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v,		     struct ceph_inode_frag *pfrag, int *found){	u32 ret;	mutex_lock(&ci->i_fragtree_mutex);	ret = __ceph_choose_frag(ci, v, pfrag, found);	mutex_unlock(&ci->i_fragtree_mutex);	return ret;} /* * 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){	struct ceph_inode_info *ci = ceph_inode(inode);	struct ceph_client *cl = ceph_inode_to_client(inode);	struct ceph_inode_frag *frag;	u32 id = le32_to_cpu(dirinfo->frag);	int mds = le32_to_cpu(dirinfo->auth);	int ndist = le32_to_cpu(dirinfo->ndist);	int diri_auth = -1;	int i;	int err = 0; 	spin_lock(&ci->i_ceph_lock);	if (ci->i_auth_cap)		diri_auth = ci->i_auth_cap->mds;	spin_unlock(&ci->i_ceph_lock); 	if (mds == -1) /* CDIR_AUTH_PARENT */		mds = diri_auth; 	mutex_lock(&ci->i_fragtree_mutex);	if (ndist == 0 && mds == diri_auth) {		/* no delegation info needed. */		frag = __ceph_find_frag(ci, id);		if (!frag)			goto out;		if (frag->split_by == 0) {			/* tree leaf, remove */			doutc(cl, "removed %p %llx.%llx frag %x (no ref)\n",			      inode, ceph_vinop(inode), id);			rb_erase(&frag->node, &ci->i_fragtree);			kfree(frag);		} else {			/* tree branch, keep and clear */			doutc(cl, "cleared %p %llx.%llx frag %x referral\n",			      inode, ceph_vinop(inode), id);			frag->mds = -1;			frag->ndist = 0;		}		goto out;	}  	/* find/add this frag to store mds delegation info */	frag = __get_or_create_frag(ci, id);	if (IS_ERR(frag)) {		/* this is not the end of the world; we can continue		   with bad/inaccurate delegation info */		pr_err_client(cl, "ENOMEM on mds ref %p %llx.%llx fg %x\n",			      inode, ceph_vinop(inode),			      le32_to_cpu(dirinfo->frag));		err = -ENOMEM;		goto out;	} 	frag->mds = mds;	frag->ndist = min_t(u32, ndist, CEPH_MAX_DIRFRAG_REP);	for (i = 0; i < frag->ndist; i++)		frag->dist[i] = le32_to_cpu(dirinfo->dist[i]);	doutc(cl, "%p %llx.%llx frag %x ndist=%d\n", inode,	      ceph_vinop(inode), frag->frag, frag->ndist); out:	mutex_unlock(&ci->i_fragtree_mutex);	return err;} static int frag_tree_split_cmp(const void *l, const void *r){	struct ceph_frag_tree_split *ls = (struct ceph_frag_tree_split*)l;	struct ceph_frag_tree_split *rs = (struct ceph_frag_tree_split*)r;	return ceph_frag_compare(le32_to_cpu(ls->frag),				 le32_to_cpu(rs->frag));} static bool is_frag_child(u32 f, struct ceph_inode_frag *frag)

Lines 1–500 of 3259.