brintos

brintos / linux-shallow public Read only

0
0
Text · 15.3 KiB · 8c8cc9b Raw
601 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) Neil Brown 20024 * Copyright (C) Christoph Hellwig 20075 *6 * This file contains the code mapping from inodes to NFS file handles,7 * and for mapping back from file handles to dentries.8 *9 * For details on why we do all the strange and hairy things in here10 * take a look at Documentation/filesystems/nfs/exporting.rst.11 */12#include <linux/exportfs.h>13#include <linux/fs.h>14#include <linux/file.h>15#include <linux/module.h>16#include <linux/mount.h>17#include <linux/namei.h>18#include <linux/sched.h>19#include <linux/cred.h>20 21#define dprintk(fmt, args...) pr_debug(fmt, ##args)22 23 24static int get_name(const struct path *path, char *name, struct dentry *child);25 26 27static int exportfs_get_name(struct vfsmount *mnt, struct dentry *dir,28		char *name, struct dentry *child)29{30	const struct export_operations *nop = dir->d_sb->s_export_op;31	struct path path = {.mnt = mnt, .dentry = dir};32 33	if (nop->get_name)34		return nop->get_name(dir, name, child);35	else36		return get_name(&path, name, child);37}38 39/*40 * Check if the dentry or any of it's aliases is acceptable.41 */42static HWJS_SUSPENDS struct dentry *43find_acceptable_alias(struct dentry *result,44		int (*acceptable)(void *context, struct dentry *dentry),45		void *context)46{47	struct dentry *dentry, *toput = NULL;48	struct inode *inode;49 50	if (acceptable(context, result))51		return result;52 53	inode = result->d_inode;54	spin_lock(&inode->i_lock);55	hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) {56		dget(dentry);57		spin_unlock(&inode->i_lock);58		if (toput)59			dput(toput);60		if (dentry != result && acceptable(context, dentry)) {61			dput(result);62			return dentry;63		}64		spin_lock(&inode->i_lock);65		toput = dentry;66	}67	spin_unlock(&inode->i_lock);68 69	if (toput)70		dput(toput);71	return NULL;72}73 74static HWJS_SUSPENDS bool dentry_connected(struct dentry *dentry)75{76	dget(dentry);77	while (dentry->d_flags & DCACHE_DISCONNECTED) {78		struct dentry *parent = dget_parent(dentry);79 80		dput(dentry);81		if (dentry == parent) {82			dput(parent);83			return false;84		}85		dentry = parent;86	}87	dput(dentry);88	return true;89}90 91static HWJS_SUSPENDS void clear_disconnected(struct dentry *dentry)92{93	dget(dentry);94	while (dentry->d_flags & DCACHE_DISCONNECTED) {95		struct dentry *parent = dget_parent(dentry);96 97		WARN_ON_ONCE(IS_ROOT(dentry));98 99		spin_lock(&dentry->d_lock);100		dentry->d_flags &= ~DCACHE_DISCONNECTED;101		spin_unlock(&dentry->d_lock);102 103		dput(dentry);104		dentry = parent;105	}106	dput(dentry);107}108 109/*110 * Reconnect a directory dentry with its parent.111 *112 * This can return a dentry, or NULL, or an error.113 *114 * In the first case the returned dentry is the parent of the given115 * dentry, and may itself need to be reconnected to its parent.116 *117 * In the NULL case, a concurrent VFS operation has either renamed or118 * removed this directory.  The concurrent operation has reconnected our119 * dentry, so we no longer need to.120 */121static HWJS_SUSPENDS struct dentry *reconnect_one(struct vfsmount *mnt,122		struct dentry *dentry, char *nbuf)123{124	struct dentry *parent;125	struct dentry *tmp;126	int err;127 128	parent = ERR_PTR(-EACCES);129	inode_lock(dentry->d_inode);130	if (mnt->mnt_sb->s_export_op->get_parent)131		parent = mnt->mnt_sb->s_export_op->get_parent(dentry);132	inode_unlock(dentry->d_inode);133 134	if (IS_ERR(parent)) {135		dprintk("get_parent of %lu failed, err %ld\n",136			dentry->d_inode->i_ino, PTR_ERR(parent));137		return parent;138	}139 140	dprintk("%s: find name of %lu in %lu\n", __func__,141		dentry->d_inode->i_ino, parent->d_inode->i_ino);142	err = exportfs_get_name(mnt, parent, nbuf, dentry);143	if (err == -ENOENT)144		goto out_reconnected;145	if (err)146		goto out_err;147	dprintk("%s: found name: %s\n", __func__, nbuf);148	tmp = lookup_one_unlocked(mnt_idmap(mnt), nbuf, parent, strlen(nbuf));149	if (IS_ERR(tmp)) {150		dprintk("lookup failed: %ld\n", PTR_ERR(tmp));151		err = PTR_ERR(tmp);152		goto out_err;153	}154	if (tmp != dentry) {155		/*156		 * Somebody has renamed it since exportfs_get_name();157		 * great, since it could've only been renamed if it158		 * got looked up and thus connected, and it would159		 * remain connected afterwards.  We are done.160		 */161		dput(tmp);162		goto out_reconnected;163	}164	dput(tmp);165	if (IS_ROOT(dentry)) {166		err = -ESTALE;167		goto out_err;168	}169	return parent;170 171out_err:172	dput(parent);173	return ERR_PTR(err);174out_reconnected:175	dput(parent);176	/*177	 * Someone must have renamed our entry into another parent, in178	 * which case it has been reconnected by the rename.179	 *180	 * Or someone removed it entirely, in which case filehandle181	 * lookup will succeed but the directory is now IS_DEAD and182	 * subsequent operations on it will fail.183	 *184	 * Alternatively, maybe there was no race at all, and the185	 * filesystem is just corrupt and gave us a parent that doesn't186	 * actually contain any entry pointing to this inode.  So,187	 * double check that this worked and return -ESTALE if not:188	 */189	if (!dentry_connected(dentry))190		return ERR_PTR(-ESTALE);191	return NULL;192}193 194/*195 * Make sure target_dir is fully connected to the dentry tree.196 *197 * On successful return, DCACHE_DISCONNECTED will be cleared on198 * target_dir, and target_dir->d_parent->...->d_parent will reach the199 * root of the filesystem.200 *201 * Whenever DCACHE_DISCONNECTED is unset, target_dir is fully connected.202 * But the converse is not true: target_dir may have DCACHE_DISCONNECTED203 * set but already be connected.  In that case we'll verify the204 * connection to root and then clear the flag.205 *206 * Note that target_dir could be removed by a concurrent operation.  In207 * that case reconnect_path may still succeed with target_dir fully208 * connected, but further operations using the filehandle will fail when209 * necessary (due to S_DEAD being set on the directory).210 */211static HWJS_SUSPENDS int212reconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf)213{214	struct dentry *dentry, *parent;215 216	dentry = dget(target_dir);217 218	while (dentry->d_flags & DCACHE_DISCONNECTED) {219		BUG_ON(dentry == mnt->mnt_sb->s_root);220 221		if (IS_ROOT(dentry))222			parent = reconnect_one(mnt, dentry, nbuf);223		else224			parent = dget_parent(dentry);225 226		if (!parent)227			break;228		dput(dentry);229		if (IS_ERR(parent))230			return PTR_ERR(parent);231		dentry = parent;232	}233	dput(dentry);234	clear_disconnected(target_dir);235	return 0;236}237 238struct getdents_callback {239	struct dir_context ctx;240	char *name;		/* name that was found. It already points to a241				   buffer NAME_MAX+1 is size */242	u64 ino;		/* the inum we are looking for */243	int found;		/* inode matched? */244	int sequence;		/* sequence counter */245};246 247/*248 * A rather strange filldir function to capture249 * the name matching the specified inode number.250 */251static bool filldir_one(struct dir_context *ctx, const char *name, int len,252			loff_t pos, u64 ino, unsigned int d_type)253{254	struct getdents_callback *buf =255		container_of(ctx, struct getdents_callback, ctx);256 257	buf->sequence++;258	if (buf->ino == ino && len <= NAME_MAX && !is_dot_dotdot(name, len)) {259		memcpy(buf->name, name, len);260		buf->name[len] = '\0';261		buf->found = 1;262		return false;	// no more263	}264	return true;265}266 267/**268 * get_name - default export_operations->get_name function269 * @path:   the directory in which to find a name270 * @name:   a pointer to a %NAME_MAX+1 char buffer to store the name271 * @child:  the dentry for the child directory.272 *273 * calls readdir on the parent until it finds an entry with274 * the same inode number as the child, and returns that.275 */276static int get_name(const struct path *path, char *name, struct dentry *child)277{278	const struct cred *cred = current_cred();279	struct inode *dir = path->dentry->d_inode;280	int error;281	struct file *file;282	struct kstat stat;283	struct path child_path = {284		.mnt = path->mnt,285		.dentry = child,286	};287	struct getdents_callback buffer = {288		.ctx.actor = filldir_one,289		.name = name,290	};291 292	error = -ENOTDIR;293	if (!dir || !S_ISDIR(dir->i_mode))294		goto out;295	error = -EINVAL;296	if (!dir->i_fop)297		goto out;298	/*299	 * inode->i_ino is unsigned long, kstat->ino is u64, so the300	 * former would be insufficient on 32-bit hosts when the301	 * filesystem supports 64-bit inode numbers.  So we need to302	 * actually call ->getattr, not just read i_ino:303	 */304	error = vfs_getattr_nosec(&child_path, &stat,305				  STATX_INO, AT_STATX_SYNC_AS_STAT);306	if (error)307		return error;308	buffer.ino = stat.ino;309	/*310	 * Open the directory ...311	 */312	file = dentry_open(path, O_RDONLY, cred);313	error = PTR_ERR(file);314	if (IS_ERR(file))315		goto out;316 317	error = -EINVAL;318	if (!file->f_op->iterate_shared)319		goto out_close;320 321	buffer.sequence = 0;322	while (1) {323		int old_seq = buffer.sequence;324 325		error = iterate_dir(file, &buffer.ctx);326		if (buffer.found) {327			error = 0;328			break;329		}330 331		if (error < 0)332			break;333 334		error = -ENOENT;335		if (old_seq == buffer.sequence)336			break;337	}338 339out_close:340	fput(file);341out:342	return error;343}344 345#define FILEID_INO64_GEN_LEN 3346 347/**348 * exportfs_encode_ino64_fid - encode non-decodeable 64bit ino file id349 * @inode:   the object to encode350 * @fid:     where to store the file handle fragment351 * @max_len: maximum length to store there (in 4 byte units)352 *353 * This generic function is used to encode a non-decodeable file id for354 * fanotify for filesystems that do not support NFS export.355 */356static int exportfs_encode_ino64_fid(struct inode *inode, struct fid *fid,357				     int *max_len)358{359	if (*max_len < FILEID_INO64_GEN_LEN) {360		*max_len = FILEID_INO64_GEN_LEN;361		return FILEID_INVALID;362	}363 364	fid->i64.ino = inode->i_ino;365	fid->i64.gen = inode->i_generation;366	*max_len = FILEID_INO64_GEN_LEN;367 368	return FILEID_INO64_GEN;369}370 371/**372 * exportfs_encode_inode_fh - encode a file handle from inode373 * @inode:   the object to encode374 * @fid:     where to store the file handle fragment375 * @max_len: maximum length to store there376 * @parent:  parent directory inode, if wanted377 * @flags:   properties of the requested file handle378 *379 * Returns an enum fid_type or a negative errno.380 */381int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid,382			     int *max_len, struct inode *parent, int flags)383{384	const struct export_operations *nop = inode->i_sb->s_export_op;385 386	if (!exportfs_can_encode_fh(nop, flags))387		return -EOPNOTSUPP;388 389	if (!nop && (flags & EXPORT_FH_FID))390		return exportfs_encode_ino64_fid(inode, fid, max_len);391 392	return nop->encode_fh(inode, fid->raw, max_len, parent);393}394EXPORT_SYMBOL_GPL(exportfs_encode_inode_fh);395 396/**397 * exportfs_encode_fh - encode a file handle from dentry398 * @dentry:  the object to encode399 * @fid:     where to store the file handle fragment400 * @max_len: maximum length to store there401 * @flags:   properties of the requested file handle402 *403 * Returns an enum fid_type or a negative errno.404 */405int exportfs_encode_fh(struct dentry *dentry, struct fid *fid, int *max_len,406		       int flags)407{408	int error;409	struct dentry *p = NULL;410	struct inode *inode = dentry->d_inode, *parent = NULL;411 412	if ((flags & EXPORT_FH_CONNECTABLE) && !S_ISDIR(inode->i_mode)) {413		p = dget_parent(dentry);414		/*415		 * note that while p might've ceased to be our parent already,416		 * it's still pinned by and still positive.417		 */418		parent = p->d_inode;419	}420 421	error = exportfs_encode_inode_fh(inode, fid, max_len, parent, flags);422	dput(p);423 424	return error;425}426EXPORT_SYMBOL_GPL(exportfs_encode_fh);427 428struct dentry *429exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len,430		       int fileid_type, unsigned int flags,431		       int (*acceptable)(void *, struct dentry *),432		       void *context)433{434	const struct export_operations *nop = mnt->mnt_sb->s_export_op;435	struct dentry *result, *alias;436	char nbuf[NAME_MAX+1];437	int err;438 439	/*440	 * Try to get any dentry for the given file handle from the filesystem.441	 */442	if (!exportfs_can_decode_fh(nop))443		return ERR_PTR(-ESTALE);444	result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type);445	if (IS_ERR_OR_NULL(result))446		return result;447 448	if ((flags & EXPORT_FH_DIR_ONLY) && !d_is_dir(result)) {449		err = -ENOTDIR;450		goto err_result;451	}452 453	/*454	 * If no acceptance criteria was specified by caller, a disconnected455	 * dentry is also accepatable. Callers may use this mode to query if456	 * file handle is stale or to get a reference to an inode without457	 * risking the high overhead caused by directory reconnect.458	 */459	if (!acceptable)460		return result;461 462	if (d_is_dir(result)) {463		/*464		 * This request is for a directory.465		 *466		 * On the positive side there is only one dentry for each467		 * directory inode.  On the negative side this implies that we468		 * to ensure our dentry is connected all the way up to the469		 * filesystem root.470		 */471		if (result->d_flags & DCACHE_DISCONNECTED) {472			err = reconnect_path(mnt, result, nbuf);473			if (err)474				goto err_result;475		}476 477		if (!acceptable(context, result)) {478			err = -EACCES;479			goto err_result;480		}481 482		return result;483	} else {484		/*485		 * It's not a directory.  Life is a little more complicated.486		 */487		struct dentry *target_dir, *nresult;488 489		/*490		 * See if either the dentry we just got from the filesystem491		 * or any alias for it is acceptable.  This is always true492		 * if this filesystem is exported without the subtreecheck493		 * option.  If the filesystem is exported with the subtree494		 * check option there's a fair chance we need to look at495		 * the parent directory in the file handle and make sure496		 * it's connected to the filesystem root.497		 */498		alias = find_acceptable_alias(result, acceptable, context);499		if (alias)500			return alias;501 502		/*503		 * Try to extract a dentry for the parent directory from the504		 * file handle.  If this fails we'll have to give up.505		 */506		err = -ESTALE;507		if (!nop->fh_to_parent)508			goto err_result;509 510		target_dir = nop->fh_to_parent(mnt->mnt_sb, fid,511				fh_len, fileid_type);512		if (!target_dir)513			goto err_result;514		err = PTR_ERR(target_dir);515		if (IS_ERR(target_dir))516			goto err_result;517 518		/*519		 * And as usual we need to make sure the parent directory is520		 * connected to the filesystem root.  The VFS really doesn't521		 * like disconnected directories..522		 */523		err = reconnect_path(mnt, target_dir, nbuf);524		if (err) {525			dput(target_dir);526			goto err_result;527		}528 529		/*530		 * Now that we've got both a well-connected parent and a531		 * dentry for the inode we're after, make sure that our532		 * inode is actually connected to the parent.533		 */534		err = exportfs_get_name(mnt, target_dir, nbuf, result);535		if (err) {536			dput(target_dir);537			goto err_result;538		}539 540		inode_lock(target_dir->d_inode);541		nresult = lookup_one(mnt_idmap(mnt), nbuf,542				     target_dir, strlen(nbuf));543		if (!IS_ERR(nresult)) {544			if (unlikely(nresult->d_inode != result->d_inode)) {545				dput(nresult);546				nresult = ERR_PTR(-ESTALE);547			}548		}549		inode_unlock(target_dir->d_inode);550		/*551		 * At this point we are done with the parent, but it's pinned552		 * by the child dentry anyway.553		 */554		dput(target_dir);555 556		if (IS_ERR(nresult)) {557			err = PTR_ERR(nresult);558			goto err_result;559		}560		dput(result);561		result = nresult;562 563		/*564		 * And finally make sure the dentry is actually acceptable565		 * to NFSD.566		 */567		alias = find_acceptable_alias(result, acceptable, context);568		if (!alias) {569			err = -EACCES;570			goto err_result;571		}572 573		return alias;574	}575 576 err_result:577	dput(result);578	return ERR_PTR(err);579}580EXPORT_SYMBOL_GPL(exportfs_decode_fh_raw);581 582struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,583				  int fh_len, int fileid_type,584				  int (*acceptable)(void *, struct dentry *),585				  void *context)586{587	struct dentry *ret;588 589	ret = exportfs_decode_fh_raw(mnt, fid, fh_len, fileid_type, 0,590				     acceptable, context);591	if (IS_ERR_OR_NULL(ret)) {592		if (ret == ERR_PTR(-ENOMEM))593			return ret;594		return ERR_PTR(-ESTALE);595	}596	return ret;597}598EXPORT_SYMBOL_GPL(exportfs_decode_fh);599 600MODULE_LICENSE("GPL");601