18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) Neil Brown 2002 48c2ecf20Sopenharmony_ci * Copyright (C) Christoph Hellwig 2007 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file contains the code mapping from inodes to NFS file handles, 78c2ecf20Sopenharmony_ci * and for mapping back from file handles to dentries. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * For details on why we do all the strange and hairy things in here 108c2ecf20Sopenharmony_ci * take a look at Documentation/filesystems/nfs/exporting.rst. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci#include <linux/exportfs.h> 138c2ecf20Sopenharmony_ci#include <linux/fs.h> 148c2ecf20Sopenharmony_ci#include <linux/file.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/mount.h> 178c2ecf20Sopenharmony_ci#include <linux/namei.h> 188c2ecf20Sopenharmony_ci#include <linux/sched.h> 198c2ecf20Sopenharmony_ci#include <linux/cred.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define dprintk(fmt, args...) do{}while(0) 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int get_name(const struct path *path, char *name, struct dentry *child); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int exportfs_get_name(struct vfsmount *mnt, struct dentry *dir, 288c2ecf20Sopenharmony_ci char *name, struct dentry *child) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci const struct export_operations *nop = dir->d_sb->s_export_op; 318c2ecf20Sopenharmony_ci struct path path = {.mnt = mnt, .dentry = dir}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (nop->get_name) 348c2ecf20Sopenharmony_ci return nop->get_name(dir, name, child); 358c2ecf20Sopenharmony_ci else 368c2ecf20Sopenharmony_ci return get_name(&path, name, child); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* 408c2ecf20Sopenharmony_ci * Check if the dentry or any of it's aliases is acceptable. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_cistatic struct dentry * 438c2ecf20Sopenharmony_cifind_acceptable_alias(struct dentry *result, 448c2ecf20Sopenharmony_ci int (*acceptable)(void *context, struct dentry *dentry), 458c2ecf20Sopenharmony_ci void *context) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct dentry *dentry, *toput = NULL; 488c2ecf20Sopenharmony_ci struct inode *inode; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (acceptable(context, result)) 518c2ecf20Sopenharmony_ci return result; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci inode = result->d_inode; 548c2ecf20Sopenharmony_ci spin_lock(&inode->i_lock); 558c2ecf20Sopenharmony_ci hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { 568c2ecf20Sopenharmony_ci dget(dentry); 578c2ecf20Sopenharmony_ci spin_unlock(&inode->i_lock); 588c2ecf20Sopenharmony_ci if (toput) 598c2ecf20Sopenharmony_ci dput(toput); 608c2ecf20Sopenharmony_ci if (dentry != result && acceptable(context, dentry)) { 618c2ecf20Sopenharmony_ci dput(result); 628c2ecf20Sopenharmony_ci return dentry; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci spin_lock(&inode->i_lock); 658c2ecf20Sopenharmony_ci toput = dentry; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci spin_unlock(&inode->i_lock); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (toput) 708c2ecf20Sopenharmony_ci dput(toput); 718c2ecf20Sopenharmony_ci return NULL; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic bool dentry_connected(struct dentry *dentry) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci dget(dentry); 778c2ecf20Sopenharmony_ci while (dentry->d_flags & DCACHE_DISCONNECTED) { 788c2ecf20Sopenharmony_ci struct dentry *parent = dget_parent(dentry); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci dput(dentry); 818c2ecf20Sopenharmony_ci if (dentry == parent) { 828c2ecf20Sopenharmony_ci dput(parent); 838c2ecf20Sopenharmony_ci return false; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci dentry = parent; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci dput(dentry); 888c2ecf20Sopenharmony_ci return true; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic void clear_disconnected(struct dentry *dentry) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci dget(dentry); 948c2ecf20Sopenharmony_ci while (dentry->d_flags & DCACHE_DISCONNECTED) { 958c2ecf20Sopenharmony_ci struct dentry *parent = dget_parent(dentry); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci WARN_ON_ONCE(IS_ROOT(dentry)); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci spin_lock(&dentry->d_lock); 1008c2ecf20Sopenharmony_ci dentry->d_flags &= ~DCACHE_DISCONNECTED; 1018c2ecf20Sopenharmony_ci spin_unlock(&dentry->d_lock); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci dput(dentry); 1048c2ecf20Sopenharmony_ci dentry = parent; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci dput(dentry); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* 1108c2ecf20Sopenharmony_ci * Reconnect a directory dentry with its parent. 1118c2ecf20Sopenharmony_ci * 1128c2ecf20Sopenharmony_ci * This can return a dentry, or NULL, or an error. 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * In the first case the returned dentry is the parent of the given 1158c2ecf20Sopenharmony_ci * dentry, and may itself need to be reconnected to its parent. 1168c2ecf20Sopenharmony_ci * 1178c2ecf20Sopenharmony_ci * In the NULL case, a concurrent VFS operation has either renamed or 1188c2ecf20Sopenharmony_ci * removed this directory. The concurrent operation has reconnected our 1198c2ecf20Sopenharmony_ci * dentry, so we no longer need to. 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_cistatic struct dentry *reconnect_one(struct vfsmount *mnt, 1228c2ecf20Sopenharmony_ci struct dentry *dentry, char *nbuf) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct dentry *parent; 1258c2ecf20Sopenharmony_ci struct dentry *tmp; 1268c2ecf20Sopenharmony_ci int err; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci parent = ERR_PTR(-EACCES); 1298c2ecf20Sopenharmony_ci inode_lock(dentry->d_inode); 1308c2ecf20Sopenharmony_ci if (mnt->mnt_sb->s_export_op->get_parent) 1318c2ecf20Sopenharmony_ci parent = mnt->mnt_sb->s_export_op->get_parent(dentry); 1328c2ecf20Sopenharmony_ci inode_unlock(dentry->d_inode); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (IS_ERR(parent)) { 1358c2ecf20Sopenharmony_ci dprintk("%s: get_parent of %ld failed, err %d\n", 1368c2ecf20Sopenharmony_ci __func__, dentry->d_inode->i_ino, PTR_ERR(parent)); 1378c2ecf20Sopenharmony_ci return parent; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci dprintk("%s: find name of %lu in %lu\n", __func__, 1418c2ecf20Sopenharmony_ci dentry->d_inode->i_ino, parent->d_inode->i_ino); 1428c2ecf20Sopenharmony_ci err = exportfs_get_name(mnt, parent, nbuf, dentry); 1438c2ecf20Sopenharmony_ci if (err == -ENOENT) 1448c2ecf20Sopenharmony_ci goto out_reconnected; 1458c2ecf20Sopenharmony_ci if (err) 1468c2ecf20Sopenharmony_ci goto out_err; 1478c2ecf20Sopenharmony_ci dprintk("%s: found name: %s\n", __func__, nbuf); 1488c2ecf20Sopenharmony_ci tmp = lookup_one_len_unlocked(nbuf, parent, strlen(nbuf)); 1498c2ecf20Sopenharmony_ci if (IS_ERR(tmp)) { 1508c2ecf20Sopenharmony_ci dprintk("%s: lookup failed: %d\n", __func__, PTR_ERR(tmp)); 1518c2ecf20Sopenharmony_ci err = PTR_ERR(tmp); 1528c2ecf20Sopenharmony_ci goto out_err; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci if (tmp != dentry) { 1558c2ecf20Sopenharmony_ci /* 1568c2ecf20Sopenharmony_ci * Somebody has renamed it since exportfs_get_name(); 1578c2ecf20Sopenharmony_ci * great, since it could've only been renamed if it 1588c2ecf20Sopenharmony_ci * got looked up and thus connected, and it would 1598c2ecf20Sopenharmony_ci * remain connected afterwards. We are done. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci dput(tmp); 1628c2ecf20Sopenharmony_ci goto out_reconnected; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci dput(tmp); 1658c2ecf20Sopenharmony_ci if (IS_ROOT(dentry)) { 1668c2ecf20Sopenharmony_ci err = -ESTALE; 1678c2ecf20Sopenharmony_ci goto out_err; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci return parent; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ciout_err: 1728c2ecf20Sopenharmony_ci dput(parent); 1738c2ecf20Sopenharmony_ci return ERR_PTR(err); 1748c2ecf20Sopenharmony_ciout_reconnected: 1758c2ecf20Sopenharmony_ci dput(parent); 1768c2ecf20Sopenharmony_ci /* 1778c2ecf20Sopenharmony_ci * Someone must have renamed our entry into another parent, in 1788c2ecf20Sopenharmony_ci * which case it has been reconnected by the rename. 1798c2ecf20Sopenharmony_ci * 1808c2ecf20Sopenharmony_ci * Or someone removed it entirely, in which case filehandle 1818c2ecf20Sopenharmony_ci * lookup will succeed but the directory is now IS_DEAD and 1828c2ecf20Sopenharmony_ci * subsequent operations on it will fail. 1838c2ecf20Sopenharmony_ci * 1848c2ecf20Sopenharmony_ci * Alternatively, maybe there was no race at all, and the 1858c2ecf20Sopenharmony_ci * filesystem is just corrupt and gave us a parent that doesn't 1868c2ecf20Sopenharmony_ci * actually contain any entry pointing to this inode. So, 1878c2ecf20Sopenharmony_ci * double check that this worked and return -ESTALE if not: 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci if (!dentry_connected(dentry)) 1908c2ecf20Sopenharmony_ci return ERR_PTR(-ESTALE); 1918c2ecf20Sopenharmony_ci return NULL; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* 1958c2ecf20Sopenharmony_ci * Make sure target_dir is fully connected to the dentry tree. 1968c2ecf20Sopenharmony_ci * 1978c2ecf20Sopenharmony_ci * On successful return, DCACHE_DISCONNECTED will be cleared on 1988c2ecf20Sopenharmony_ci * target_dir, and target_dir->d_parent->...->d_parent will reach the 1998c2ecf20Sopenharmony_ci * root of the filesystem. 2008c2ecf20Sopenharmony_ci * 2018c2ecf20Sopenharmony_ci * Whenever DCACHE_DISCONNECTED is unset, target_dir is fully connected. 2028c2ecf20Sopenharmony_ci * But the converse is not true: target_dir may have DCACHE_DISCONNECTED 2038c2ecf20Sopenharmony_ci * set but already be connected. In that case we'll verify the 2048c2ecf20Sopenharmony_ci * connection to root and then clear the flag. 2058c2ecf20Sopenharmony_ci * 2068c2ecf20Sopenharmony_ci * Note that target_dir could be removed by a concurrent operation. In 2078c2ecf20Sopenharmony_ci * that case reconnect_path may still succeed with target_dir fully 2088c2ecf20Sopenharmony_ci * connected, but further operations using the filehandle will fail when 2098c2ecf20Sopenharmony_ci * necessary (due to S_DEAD being set on the directory). 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_cistatic int 2128c2ecf20Sopenharmony_cireconnect_path(struct vfsmount *mnt, struct dentry *target_dir, char *nbuf) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct dentry *dentry, *parent; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci dentry = dget(target_dir); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci while (dentry->d_flags & DCACHE_DISCONNECTED) { 2198c2ecf20Sopenharmony_ci BUG_ON(dentry == mnt->mnt_sb->s_root); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (IS_ROOT(dentry)) 2228c2ecf20Sopenharmony_ci parent = reconnect_one(mnt, dentry, nbuf); 2238c2ecf20Sopenharmony_ci else 2248c2ecf20Sopenharmony_ci parent = dget_parent(dentry); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (!parent) 2278c2ecf20Sopenharmony_ci break; 2288c2ecf20Sopenharmony_ci dput(dentry); 2298c2ecf20Sopenharmony_ci if (IS_ERR(parent)) 2308c2ecf20Sopenharmony_ci return PTR_ERR(parent); 2318c2ecf20Sopenharmony_ci dentry = parent; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci dput(dentry); 2348c2ecf20Sopenharmony_ci clear_disconnected(target_dir); 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistruct getdents_callback { 2398c2ecf20Sopenharmony_ci struct dir_context ctx; 2408c2ecf20Sopenharmony_ci char *name; /* name that was found. It already points to a 2418c2ecf20Sopenharmony_ci buffer NAME_MAX+1 is size */ 2428c2ecf20Sopenharmony_ci u64 ino; /* the inum we are looking for */ 2438c2ecf20Sopenharmony_ci int found; /* inode matched? */ 2448c2ecf20Sopenharmony_ci int sequence; /* sequence counter */ 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* 2488c2ecf20Sopenharmony_ci * A rather strange filldir function to capture 2498c2ecf20Sopenharmony_ci * the name matching the specified inode number. 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_cistatic int filldir_one(struct dir_context *ctx, const char *name, int len, 2528c2ecf20Sopenharmony_ci loff_t pos, u64 ino, unsigned int d_type) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct getdents_callback *buf = 2558c2ecf20Sopenharmony_ci container_of(ctx, struct getdents_callback, ctx); 2568c2ecf20Sopenharmony_ci int result = 0; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci buf->sequence++; 2598c2ecf20Sopenharmony_ci if (buf->ino == ino && len <= NAME_MAX) { 2608c2ecf20Sopenharmony_ci memcpy(buf->name, name, len); 2618c2ecf20Sopenharmony_ci buf->name[len] = '\0'; 2628c2ecf20Sopenharmony_ci buf->found = 1; 2638c2ecf20Sopenharmony_ci result = -1; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci return result; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/** 2698c2ecf20Sopenharmony_ci * get_name - default export_operations->get_name function 2708c2ecf20Sopenharmony_ci * @path: the directory in which to find a name 2718c2ecf20Sopenharmony_ci * @name: a pointer to a %NAME_MAX+1 char buffer to store the name 2728c2ecf20Sopenharmony_ci * @child: the dentry for the child directory. 2738c2ecf20Sopenharmony_ci * 2748c2ecf20Sopenharmony_ci * calls readdir on the parent until it finds an entry with 2758c2ecf20Sopenharmony_ci * the same inode number as the child, and returns that. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_cistatic int get_name(const struct path *path, char *name, struct dentry *child) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci const struct cred *cred = current_cred(); 2808c2ecf20Sopenharmony_ci struct inode *dir = path->dentry->d_inode; 2818c2ecf20Sopenharmony_ci int error; 2828c2ecf20Sopenharmony_ci struct file *file; 2838c2ecf20Sopenharmony_ci struct kstat stat; 2848c2ecf20Sopenharmony_ci struct path child_path = { 2858c2ecf20Sopenharmony_ci .mnt = path->mnt, 2868c2ecf20Sopenharmony_ci .dentry = child, 2878c2ecf20Sopenharmony_ci }; 2888c2ecf20Sopenharmony_ci struct getdents_callback buffer = { 2898c2ecf20Sopenharmony_ci .ctx.actor = filldir_one, 2908c2ecf20Sopenharmony_ci .name = name, 2918c2ecf20Sopenharmony_ci }; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci error = -ENOTDIR; 2948c2ecf20Sopenharmony_ci if (!dir || !S_ISDIR(dir->i_mode)) 2958c2ecf20Sopenharmony_ci goto out; 2968c2ecf20Sopenharmony_ci error = -EINVAL; 2978c2ecf20Sopenharmony_ci if (!dir->i_fop) 2988c2ecf20Sopenharmony_ci goto out; 2998c2ecf20Sopenharmony_ci /* 3008c2ecf20Sopenharmony_ci * inode->i_ino is unsigned long, kstat->ino is u64, so the 3018c2ecf20Sopenharmony_ci * former would be insufficient on 32-bit hosts when the 3028c2ecf20Sopenharmony_ci * filesystem supports 64-bit inode numbers. So we need to 3038c2ecf20Sopenharmony_ci * actually call ->getattr, not just read i_ino: 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ci error = vfs_getattr_nosec(&child_path, &stat, 3068c2ecf20Sopenharmony_ci STATX_INO, AT_STATX_SYNC_AS_STAT); 3078c2ecf20Sopenharmony_ci if (error) 3088c2ecf20Sopenharmony_ci return error; 3098c2ecf20Sopenharmony_ci buffer.ino = stat.ino; 3108c2ecf20Sopenharmony_ci /* 3118c2ecf20Sopenharmony_ci * Open the directory ... 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_ci file = dentry_open(path, O_RDONLY, cred); 3148c2ecf20Sopenharmony_ci error = PTR_ERR(file); 3158c2ecf20Sopenharmony_ci if (IS_ERR(file)) 3168c2ecf20Sopenharmony_ci goto out; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci error = -EINVAL; 3198c2ecf20Sopenharmony_ci if (!file->f_op->iterate && !file->f_op->iterate_shared) 3208c2ecf20Sopenharmony_ci goto out_close; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci buffer.sequence = 0; 3238c2ecf20Sopenharmony_ci while (1) { 3248c2ecf20Sopenharmony_ci int old_seq = buffer.sequence; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci error = iterate_dir(file, &buffer.ctx); 3278c2ecf20Sopenharmony_ci if (buffer.found) { 3288c2ecf20Sopenharmony_ci error = 0; 3298c2ecf20Sopenharmony_ci break; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (error < 0) 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci error = -ENOENT; 3368c2ecf20Sopenharmony_ci if (old_seq == buffer.sequence) 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ciout_close: 3418c2ecf20Sopenharmony_ci fput(file); 3428c2ecf20Sopenharmony_ciout: 3438c2ecf20Sopenharmony_ci return error; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci/** 3478c2ecf20Sopenharmony_ci * export_encode_fh - default export_operations->encode_fh function 3488c2ecf20Sopenharmony_ci * @inode: the object to encode 3498c2ecf20Sopenharmony_ci * @fid: where to store the file handle fragment 3508c2ecf20Sopenharmony_ci * @max_len: maximum length to store there 3518c2ecf20Sopenharmony_ci * @parent: parent directory inode, if wanted 3528c2ecf20Sopenharmony_ci * 3538c2ecf20Sopenharmony_ci * This default encode_fh function assumes that the 32 inode number 3548c2ecf20Sopenharmony_ci * is suitable for locating an inode, and that the generation number 3558c2ecf20Sopenharmony_ci * can be used to check that it is still valid. It places them in the 3568c2ecf20Sopenharmony_ci * filehandle fragment where export_decode_fh expects to find them. 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_cistatic int export_encode_fh(struct inode *inode, struct fid *fid, 3598c2ecf20Sopenharmony_ci int *max_len, struct inode *parent) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci int len = *max_len; 3628c2ecf20Sopenharmony_ci int type = FILEID_INO32_GEN; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (parent && (len < 4)) { 3658c2ecf20Sopenharmony_ci *max_len = 4; 3668c2ecf20Sopenharmony_ci return FILEID_INVALID; 3678c2ecf20Sopenharmony_ci } else if (len < 2) { 3688c2ecf20Sopenharmony_ci *max_len = 2; 3698c2ecf20Sopenharmony_ci return FILEID_INVALID; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci len = 2; 3738c2ecf20Sopenharmony_ci fid->i32.ino = inode->i_ino; 3748c2ecf20Sopenharmony_ci fid->i32.gen = inode->i_generation; 3758c2ecf20Sopenharmony_ci if (parent) { 3768c2ecf20Sopenharmony_ci fid->i32.parent_ino = parent->i_ino; 3778c2ecf20Sopenharmony_ci fid->i32.parent_gen = parent->i_generation; 3788c2ecf20Sopenharmony_ci len = 4; 3798c2ecf20Sopenharmony_ci type = FILEID_INO32_GEN_PARENT; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci *max_len = len; 3828c2ecf20Sopenharmony_ci return type; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ciint exportfs_encode_inode_fh(struct inode *inode, struct fid *fid, 3868c2ecf20Sopenharmony_ci int *max_len, struct inode *parent) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci const struct export_operations *nop = inode->i_sb->s_export_op; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (nop && nop->encode_fh) 3918c2ecf20Sopenharmony_ci return nop->encode_fh(inode, fid->raw, max_len, parent); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return export_encode_fh(inode, fid, max_len, parent); 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(exportfs_encode_inode_fh); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ciint exportfs_encode_fh(struct dentry *dentry, struct fid *fid, int *max_len, 3988c2ecf20Sopenharmony_ci int connectable) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci int error; 4018c2ecf20Sopenharmony_ci struct dentry *p = NULL; 4028c2ecf20Sopenharmony_ci struct inode *inode = dentry->d_inode, *parent = NULL; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (connectable && !S_ISDIR(inode->i_mode)) { 4058c2ecf20Sopenharmony_ci p = dget_parent(dentry); 4068c2ecf20Sopenharmony_ci /* 4078c2ecf20Sopenharmony_ci * note that while p might've ceased to be our parent already, 4088c2ecf20Sopenharmony_ci * it's still pinned by and still positive. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_ci parent = p->d_inode; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci error = exportfs_encode_inode_fh(inode, fid, max_len, parent); 4148c2ecf20Sopenharmony_ci dput(p); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return error; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(exportfs_encode_fh); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistruct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, 4218c2ecf20Sopenharmony_ci int fh_len, int fileid_type, 4228c2ecf20Sopenharmony_ci int (*acceptable)(void *, struct dentry *), void *context) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci const struct export_operations *nop = mnt->mnt_sb->s_export_op; 4258c2ecf20Sopenharmony_ci struct dentry *result, *alias; 4268c2ecf20Sopenharmony_ci char nbuf[NAME_MAX+1]; 4278c2ecf20Sopenharmony_ci int err; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* 4308c2ecf20Sopenharmony_ci * Try to get any dentry for the given file handle from the filesystem. 4318c2ecf20Sopenharmony_ci */ 4328c2ecf20Sopenharmony_ci if (!nop || !nop->fh_to_dentry) 4338c2ecf20Sopenharmony_ci return ERR_PTR(-ESTALE); 4348c2ecf20Sopenharmony_ci result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type); 4358c2ecf20Sopenharmony_ci if (PTR_ERR(result) == -ENOMEM) 4368c2ecf20Sopenharmony_ci return ERR_CAST(result); 4378c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(result)) 4388c2ecf20Sopenharmony_ci return ERR_PTR(-ESTALE); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* 4418c2ecf20Sopenharmony_ci * If no acceptance criteria was specified by caller, a disconnected 4428c2ecf20Sopenharmony_ci * dentry is also accepatable. Callers may use this mode to query if 4438c2ecf20Sopenharmony_ci * file handle is stale or to get a reference to an inode without 4448c2ecf20Sopenharmony_ci * risking the high overhead caused by directory reconnect. 4458c2ecf20Sopenharmony_ci */ 4468c2ecf20Sopenharmony_ci if (!acceptable) 4478c2ecf20Sopenharmony_ci return result; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (d_is_dir(result)) { 4508c2ecf20Sopenharmony_ci /* 4518c2ecf20Sopenharmony_ci * This request is for a directory. 4528c2ecf20Sopenharmony_ci * 4538c2ecf20Sopenharmony_ci * On the positive side there is only one dentry for each 4548c2ecf20Sopenharmony_ci * directory inode. On the negative side this implies that we 4558c2ecf20Sopenharmony_ci * to ensure our dentry is connected all the way up to the 4568c2ecf20Sopenharmony_ci * filesystem root. 4578c2ecf20Sopenharmony_ci */ 4588c2ecf20Sopenharmony_ci if (result->d_flags & DCACHE_DISCONNECTED) { 4598c2ecf20Sopenharmony_ci err = reconnect_path(mnt, result, nbuf); 4608c2ecf20Sopenharmony_ci if (err) 4618c2ecf20Sopenharmony_ci goto err_result; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (!acceptable(context, result)) { 4658c2ecf20Sopenharmony_ci err = -EACCES; 4668c2ecf20Sopenharmony_ci goto err_result; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return result; 4708c2ecf20Sopenharmony_ci } else { 4718c2ecf20Sopenharmony_ci /* 4728c2ecf20Sopenharmony_ci * It's not a directory. Life is a little more complicated. 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_ci struct dentry *target_dir, *nresult; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* 4778c2ecf20Sopenharmony_ci * See if either the dentry we just got from the filesystem 4788c2ecf20Sopenharmony_ci * or any alias for it is acceptable. This is always true 4798c2ecf20Sopenharmony_ci * if this filesystem is exported without the subtreecheck 4808c2ecf20Sopenharmony_ci * option. If the filesystem is exported with the subtree 4818c2ecf20Sopenharmony_ci * check option there's a fair chance we need to look at 4828c2ecf20Sopenharmony_ci * the parent directory in the file handle and make sure 4838c2ecf20Sopenharmony_ci * it's connected to the filesystem root. 4848c2ecf20Sopenharmony_ci */ 4858c2ecf20Sopenharmony_ci alias = find_acceptable_alias(result, acceptable, context); 4868c2ecf20Sopenharmony_ci if (alias) 4878c2ecf20Sopenharmony_ci return alias; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* 4908c2ecf20Sopenharmony_ci * Try to extract a dentry for the parent directory from the 4918c2ecf20Sopenharmony_ci * file handle. If this fails we'll have to give up. 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_ci err = -ESTALE; 4948c2ecf20Sopenharmony_ci if (!nop->fh_to_parent) 4958c2ecf20Sopenharmony_ci goto err_result; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci target_dir = nop->fh_to_parent(mnt->mnt_sb, fid, 4988c2ecf20Sopenharmony_ci fh_len, fileid_type); 4998c2ecf20Sopenharmony_ci if (!target_dir) 5008c2ecf20Sopenharmony_ci goto err_result; 5018c2ecf20Sopenharmony_ci err = PTR_ERR(target_dir); 5028c2ecf20Sopenharmony_ci if (IS_ERR(target_dir)) 5038c2ecf20Sopenharmony_ci goto err_result; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* 5068c2ecf20Sopenharmony_ci * And as usual we need to make sure the parent directory is 5078c2ecf20Sopenharmony_ci * connected to the filesystem root. The VFS really doesn't 5088c2ecf20Sopenharmony_ci * like disconnected directories.. 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_ci err = reconnect_path(mnt, target_dir, nbuf); 5118c2ecf20Sopenharmony_ci if (err) { 5128c2ecf20Sopenharmony_ci dput(target_dir); 5138c2ecf20Sopenharmony_ci goto err_result; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci /* 5178c2ecf20Sopenharmony_ci * Now that we've got both a well-connected parent and a 5188c2ecf20Sopenharmony_ci * dentry for the inode we're after, make sure that our 5198c2ecf20Sopenharmony_ci * inode is actually connected to the parent. 5208c2ecf20Sopenharmony_ci */ 5218c2ecf20Sopenharmony_ci err = exportfs_get_name(mnt, target_dir, nbuf, result); 5228c2ecf20Sopenharmony_ci if (err) { 5238c2ecf20Sopenharmony_ci dput(target_dir); 5248c2ecf20Sopenharmony_ci goto err_result; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci inode_lock(target_dir->d_inode); 5288c2ecf20Sopenharmony_ci nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf)); 5298c2ecf20Sopenharmony_ci if (!IS_ERR(nresult)) { 5308c2ecf20Sopenharmony_ci if (unlikely(nresult->d_inode != result->d_inode)) { 5318c2ecf20Sopenharmony_ci dput(nresult); 5328c2ecf20Sopenharmony_ci nresult = ERR_PTR(-ESTALE); 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci inode_unlock(target_dir->d_inode); 5368c2ecf20Sopenharmony_ci /* 5378c2ecf20Sopenharmony_ci * At this point we are done with the parent, but it's pinned 5388c2ecf20Sopenharmony_ci * by the child dentry anyway. 5398c2ecf20Sopenharmony_ci */ 5408c2ecf20Sopenharmony_ci dput(target_dir); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (IS_ERR(nresult)) { 5438c2ecf20Sopenharmony_ci err = PTR_ERR(nresult); 5448c2ecf20Sopenharmony_ci goto err_result; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci dput(result); 5478c2ecf20Sopenharmony_ci result = nresult; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* 5508c2ecf20Sopenharmony_ci * And finally make sure the dentry is actually acceptable 5518c2ecf20Sopenharmony_ci * to NFSD. 5528c2ecf20Sopenharmony_ci */ 5538c2ecf20Sopenharmony_ci alias = find_acceptable_alias(result, acceptable, context); 5548c2ecf20Sopenharmony_ci if (!alias) { 5558c2ecf20Sopenharmony_ci err = -EACCES; 5568c2ecf20Sopenharmony_ci goto err_result; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci return alias; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci err_result: 5638c2ecf20Sopenharmony_ci dput(result); 5648c2ecf20Sopenharmony_ci if (err != -ENOMEM) 5658c2ecf20Sopenharmony_ci err = -ESTALE; 5668c2ecf20Sopenharmony_ci return ERR_PTR(err); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(exportfs_decode_fh); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 571