xref: /kernel/linux/linux-5.10/fs/sharefs/lookup.c (revision 8c2ecf20)
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * fs/sharefs/lookup.c
4 *
5 * Copyright (c) 1998-2022 Erez Zadok
6 * Copyright (c) 2009	   Shrikar Archak
7 * Copyright (c) 2003-2022 Stony Brook University
8 * Copyright (c) 2003-2022 The Research Foundation of SUNY
9 * Copyright (c) 2023 Huawei Device Co., Ltd.
10 */
11
12#include "sharefs.h"
13#include "authentication.h"
14
15/* The dentry cache is just so we have properly sized dentries */
16static struct kmem_cache *sharefs_dentry_cachep;
17
18int sharefs_init_dentry_cache(void)
19{
20	sharefs_dentry_cachep =
21		kmem_cache_create("sharefs_dentry",
22				  sizeof(struct sharefs_dentry_info),
23				  0, SLAB_RECLAIM_ACCOUNT, NULL);
24
25	return sharefs_dentry_cachep ? 0 : -ENOMEM;
26}
27
28void sharefs_destroy_dentry_cache(void)
29{
30	if (sharefs_dentry_cachep)
31		kmem_cache_destroy(sharefs_dentry_cachep);
32}
33
34void free_dentry_private_data(struct dentry *dentry)
35{
36	if (!dentry || !dentry->d_fsdata)
37		return;
38	kmem_cache_free(sharefs_dentry_cachep, dentry->d_fsdata);
39	dentry->d_fsdata = NULL;
40}
41
42/* allocate new dentry private data */
43int new_dentry_private_data(struct dentry *dentry)
44{
45	struct sharefs_dentry_info *info = SHAREFS_D(dentry);
46
47	/* use zalloc to init dentry_info.lower_path */
48	info = kmem_cache_zalloc(sharefs_dentry_cachep, GFP_ATOMIC);
49	if (!info)
50		return -ENOMEM;
51
52	spin_lock_init(&info->lock);
53	dentry->d_fsdata = info;
54
55	return 0;
56}
57
58static int sharefs_inode_test(struct inode *inode, void *candidate_lower_inode)
59{
60	struct inode *current_lower_inode = sharefs_lower_inode(inode);
61	if (current_lower_inode == (struct inode *)candidate_lower_inode)
62		return 1; /* found a match */
63	else
64		return 0; /* no match */
65}
66
67static int sharefs_inode_set(struct inode *inode, void *lower_inode)
68{
69	/* we do actual inode initialization in sharefs_iget */
70	return 0;
71}
72
73struct inode *sharefs_iget(struct super_block *sb, struct inode *lower_inode)
74{
75	struct inode *inode; /* the new inode to return */
76
77	if (!igrab(lower_inode))
78		return ERR_PTR(-ESTALE);
79	inode = iget5_locked(sb, /* our superblock */
80			     /*
81			      * hashval: we use inode number, but we can
82			      * also use "(unsigned long)lower_inode"
83			      * instead.
84			      */
85			     lower_inode->i_ino, /* hashval */
86			     sharefs_inode_test, /* inode comparison function */
87			     sharefs_inode_set, /* inode init function */
88			     lower_inode); /* data passed to test+set fxns */
89	if (!inode) {
90		iput(lower_inode);
91		return ERR_PTR(-ENOMEM);
92	}
93
94	if (lower_inode->i_nlink == 0) {
95		iput(lower_inode);
96		iput(inode);
97		return ERR_PTR(-ENOENT);
98	}
99
100	/* if found a cached inode, then just return it (after iput) */
101	if (!(inode->i_state & I_NEW)) {
102		iput(lower_inode);
103		return inode;
104	}
105
106	/* initialize new inode */
107	inode->i_ino = lower_inode->i_ino;
108	sharefs_set_lower_inode(inode, lower_inode);
109
110	atomic64_inc(&inode->i_version);
111
112	/* use different set of inode ops for symlinks & directories */
113	if (S_ISDIR(lower_inode->i_mode))
114		inode->i_op = &sharefs_dir_iops;
115	else if (S_ISLNK(lower_inode->i_mode))
116		inode->i_op = &sharefs_symlink_iops;
117	else
118		inode->i_op = &sharefs_main_iops;
119
120	/* use different set of file ops for directories */
121	if (S_ISDIR(lower_inode->i_mode))
122		inode->i_fop = &sharefs_dir_fops;
123	else
124		inode->i_fop = &sharefs_main_fops;
125
126	inode->i_atime.tv_sec = 0;
127	inode->i_atime.tv_nsec = 0;
128	inode->i_mtime.tv_sec = 0;
129	inode->i_mtime.tv_nsec = 0;
130	inode->i_ctime.tv_sec = 0;
131	inode->i_ctime.tv_nsec = 0;
132
133	/* properly initialize special inodes */
134	if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) ||
135	    S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode))
136		init_special_inode(inode, lower_inode->i_mode,
137				   lower_inode->i_rdev);
138
139	/* all well, copy inode attributes */
140	fsstack_copy_attr_all(inode, lower_inode);
141	fsstack_copy_inode_size(inode, lower_inode);
142
143	unlock_new_inode(inode);
144	return inode;
145}
146
147/*
148 * Helper interpose routine, called directly by ->lookup to handle
149 * spliced dentries.
150 */
151static struct dentry *__sharefs_interpose(struct dentry *dentry,
152					 struct super_block *sb,
153					 struct path *lower_path)
154{
155	struct inode *inode;
156	struct inode *lower_inode = d_inode(lower_path->dentry);
157	struct dentry *ret_dentry;
158
159	/*
160	 * We allocate our new inode below by calling sharefs_iget,
161	 * which will initialize some of the new inode's fields
162	 */
163
164	/* inherit lower inode number for sharefs's inode */
165	inode = sharefs_iget(sb, lower_inode);
166	if (IS_ERR(inode)) {
167		ret_dentry = ERR_PTR(PTR_ERR(inode));
168		goto out;
169	}
170
171	ret_dentry = d_splice_alias(inode, dentry);
172
173out:
174	return ret_dentry;
175}
176
177/*
178 * Connect a sharefs inode dentry/inode with several lower ones.  This is
179 * the classic stackable file system "vnode interposition" action.
180 *
181 * @dentry: sharefs's dentry which interposes on lower one
182 * @sb: sharefs's super_block
183 * @lower_path: the lower path (caller does path_get/put)
184 */
185int sharefs_interpose(struct dentry *dentry, struct super_block *sb,
186		     struct path *lower_path)
187{
188	struct dentry *ret_dentry;
189
190	ret_dentry = __sharefs_interpose(dentry, sb, lower_path);
191	return PTR_ERR(ret_dentry);
192}
193
194/*
195 * Main driver function for sharefs's lookup.
196 *
197 * Returns: NULL (ok), ERR_PTR if an error occurred.
198 * Fills in lower_parent_path with <dentry,mnt> on success.
199 */
200static struct dentry *__sharefs_lookup(struct dentry *dentry,
201				      unsigned int flags,
202				      struct path *lower_parent_path)
203{
204	int err = 0;
205	struct vfsmount *lower_dir_mnt;
206	struct dentry *lower_dir_dentry = NULL;
207	struct dentry *lower_dentry;
208	const char *name;
209	struct path lower_path;
210	struct qstr this;
211	struct dentry *ret_dentry = NULL;
212
213	/* must initialize dentry operations */
214	d_set_d_op(dentry, &sharefs_dops);
215
216	if (IS_ROOT(dentry))
217		goto out;
218
219	name = dentry->d_name.name;
220
221	/* now start the actual lookup procedure */
222	lower_dir_dentry = lower_parent_path->dentry;
223	lower_dir_mnt = lower_parent_path->mnt;
224
225	/* Use vfs_path_lookup to check if the dentry exists or not */
226	err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0,
227			      &lower_path);
228	/* no error: handle positive dentries */
229	if (!err) {
230		sharefs_set_lower_path(dentry, &lower_path);
231		ret_dentry =
232			__sharefs_interpose(dentry, dentry->d_sb, &lower_path);
233		if (IS_ERR(ret_dentry)) {
234			err = PTR_ERR(ret_dentry);
235			 /* path_put underlying path on error */
236			sharefs_put_reset_lower_path(dentry);
237		}
238		goto out;
239	}
240	/*
241	 * We don't consider ENOENT an error, and we want to return a
242	 * negative dentry.
243	 */
244	if (err && err != -ENOENT)
245		goto out;
246
247	/* instantiate a new negative dentry */
248	this.name = name;
249	this.len = strlen(name);
250	this.hash = full_name_hash(lower_dir_dentry, this.name, this.len);
251	lower_dentry = d_lookup(lower_dir_dentry, &this);
252	if (lower_dentry)
253		goto setup_lower;
254
255	lower_dentry = d_alloc(lower_dir_dentry, &this);
256	if (!lower_dentry) {
257		err = -ENOMEM;
258		goto out;
259	}
260
261	/*
262	 * Calling ->lookup instead of d_add will give the lower fs a chance
263	 * to allocate the d_fsdata field but will still instantiate and hash the
264	 * lower_dentry. Without this, sharefs could not stack on top of itself.
265	 */
266	d_inode(lower_dir_dentry)->i_op->lookup(d_inode(lower_dir_dentry),
267						lower_dentry, flags);
268
269setup_lower:
270	lower_path.dentry = lower_dentry;
271	lower_path.mnt = mntget(lower_dir_mnt);
272	sharefs_set_lower_path(dentry, &lower_path);
273
274	/*
275	 * If the intent is to create a file, then don't return an error, so
276	 * the VFS will continue the process of making this negative dentry
277	 * into a positive one.
278	 */
279	if (err == -ENOENT || (flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET)))
280		err = 0;
281
282out:
283	if (err)
284		return ERR_PTR(err);
285	return ret_dentry;
286}
287
288struct dentry *sharefs_lookup(struct inode *dir, struct dentry *dentry,
289			     unsigned int flags)
290{
291	int err;
292	struct dentry *ret, *parent;
293	struct path lower_parent_path;
294#ifdef CONFIG_SHAREFS_SUPPORT_OVERRIDE
295	const struct cred *saved_cred = NULL;
296	__u16 permission;
297#endif
298
299	parent = dget_parent(dentry);
300	sharefs_get_lower_path(parent, &lower_parent_path);
301#ifdef CONFIG_SHAREFS_SUPPORT_OVERRIDE
302	saved_cred = sharefs_override_file_fsids(dir, &permission);
303	if (!saved_cred) {
304		ret = ERR_PTR(-ENOMEM);
305		goto out_err;
306	}
307#endif
308
309	/* allocate dentry private data.  We free it in ->d_release */
310	err = new_dentry_private_data(dentry);
311	if (err) {
312		ret = ERR_PTR(err);
313		goto out;
314	}
315	ret = __sharefs_lookup(dentry, flags, &lower_parent_path);
316	if (IS_ERR(ret)) {
317		sharefs_err("sharefs_lookup error!");
318		goto out;
319	}
320
321	if (ret)
322		dentry = ret;
323	if (d_inode(dentry))
324		fsstack_copy_attr_times(d_inode(dentry),
325					sharefs_lower_inode(d_inode(dentry)));
326	/* update parent directory's atime */
327	fsstack_copy_attr_atime(d_inode(parent),
328				sharefs_lower_inode(d_inode(parent)));
329	fixup_perm_from_level(d_inode(parent), dentry);
330out:
331#ifdef CONFIG_SHAREFS_SUPPORT_OVERRIDE
332	sharefs_revert_fsids(saved_cred);
333out_err:
334#endif
335	sharefs_put_lower_path(parent, &lower_parent_path);
336	dput(parent);
337	return ret;
338}
339