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