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 */
16 static struct kmem_cache *sharefs_dentry_cachep;
17 
sharefs_init_dentry_cache(void)18 int 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 
sharefs_destroy_dentry_cache(void)28 void sharefs_destroy_dentry_cache(void)
29 {
30 	if (sharefs_dentry_cachep)
31 		kmem_cache_destroy(sharefs_dentry_cachep);
32 }
33 
free_dentry_private_data(struct dentry *dentry)34 void 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 */
new_dentry_private_data(struct dentry *dentry)43 int 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 
sharefs_inode_test(struct inode *inode, void *candidate_lower_inode)58 static 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 
sharefs_inode_set(struct inode *inode, void *lower_inode)67 static 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 
sharefs_iget(struct super_block *sb, struct inode *lower_inode)73 struct 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  */
__sharefs_interpose(struct dentry *dentry, struct super_block *sb, struct path *lower_path)151 static 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 
173 out:
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  */
sharefs_interpose(struct dentry *dentry, struct super_block *sb, struct path *lower_path)185 int 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  */
__sharefs_lookup(struct dentry *dentry, unsigned int flags, struct path *lower_parent_path)200 static 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 
269 setup_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 
282 out:
283 	if (err)
284 		return ERR_PTR(err);
285 	return ret_dentry;
286 }
287 
sharefs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)288 struct 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);
330 out:
331 #ifdef CONFIG_SHAREFS_SUPPORT_OVERRIDE
332 	sharefs_revert_fsids(saved_cred);
333 out_err:
334 #endif
335 	sharefs_put_lower_path(parent, &lower_parent_path);
336 	dput(parent);
337 	return ret;
338 }
339