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;
157 struct super_block *lower_sb;
158 struct dentry *ret_dentry;
159
160 lower_inode = d_inode(lower_path->dentry);
161 lower_sb = sharefs_lower_super(sb);
162
163 /* check that the lower file system didn't cross a mount point */
164 if (lower_inode->i_sb != lower_sb) {
165 ret_dentry = ERR_PTR(-EXDEV);
166 goto out;
167 }
168
169 /*
170 * We allocate our new inode below by calling sharefs_iget,
171 * which will initialize some of the new inode's fields
172 */
173
174 /* inherit lower inode number for sharefs's inode */
175 inode = sharefs_iget(sb, lower_inode);
176 if (IS_ERR(inode)) {
177 ret_dentry = ERR_PTR(PTR_ERR(inode));
178 goto out;
179 }
180
181 ret_dentry = d_splice_alias(inode, dentry);
182
183 out:
184 return ret_dentry;
185 }
186
187 /*
188 * Connect a sharefs inode dentry/inode with several lower ones. This is
189 * the classic stackable file system "vnode interposition" action.
190 *
191 * @dentry: sharefs's dentry which interposes on lower one
192 * @sb: sharefs's super_block
193 * @lower_path: the lower path (caller does path_get/put)
194 */
sharefs_interpose(struct dentry * dentry,struct super_block * sb,struct path * lower_path)195 int sharefs_interpose(struct dentry *dentry, struct super_block *sb,
196 struct path *lower_path)
197 {
198 struct dentry *ret_dentry;
199
200 ret_dentry = __sharefs_interpose(dentry, sb, lower_path);
201 return PTR_ERR(ret_dentry);
202 }
203
204 /*
205 * Main driver function for sharefs's lookup.
206 *
207 * Returns: NULL (ok), ERR_PTR if an error occurred.
208 * Fills in lower_parent_path with <dentry,mnt> on success.
209 */
__sharefs_lookup(struct dentry * dentry,unsigned int flags,struct path * lower_parent_path)210 static struct dentry *__sharefs_lookup(struct dentry *dentry,
211 unsigned int flags,
212 struct path *lower_parent_path)
213 {
214 int err = 0;
215 struct vfsmount *lower_dir_mnt;
216 struct dentry *lower_dir_dentry = NULL;
217 struct dentry *lower_dentry;
218 const char *name;
219 struct path lower_path;
220 struct qstr this;
221 struct dentry *ret_dentry = NULL;
222
223 /* must initialize dentry operations */
224 d_set_d_op(dentry, &sharefs_dops);
225
226 if (IS_ROOT(dentry))
227 goto out;
228
229 name = dentry->d_name.name;
230
231 /* now start the actual lookup procedure */
232 lower_dir_dentry = lower_parent_path->dentry;
233 lower_dir_mnt = lower_parent_path->mnt;
234
235 /* Use vfs_path_lookup to check if the dentry exists or not */
236 err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0,
237 &lower_path);
238
239 /* no error: handle positive dentries */
240 if (!err) {
241 sharefs_set_lower_path(dentry, &lower_path);
242 ret_dentry =
243 __sharefs_interpose(dentry, dentry->d_sb, &lower_path);
244 if (IS_ERR(ret_dentry)) {
245 err = PTR_ERR(ret_dentry);
246 /* path_put underlying path on error */
247 sharefs_put_reset_lower_path(dentry);
248 }
249 goto out;
250 }
251
252 /*
253 * We don't consider ENOENT an error, and we want to return a
254 * negative dentry.
255 */
256 if (err && err != -ENOENT)
257 goto out;
258
259 /* instantiate a new negative dentry */
260 this.name = name;
261 this.len = strlen(name);
262 this.hash = full_name_hash(lower_dir_dentry, this.name, this.len);
263 lower_dentry = d_lookup(lower_dir_dentry, &this);
264 if (lower_dentry)
265 goto setup_lower;
266
267 lower_dentry = d_alloc(lower_dir_dentry, &this);
268 if (!lower_dentry) {
269 err = -ENOMEM;
270 goto out;
271 }
272
273 /*
274 * Calling ->lookup instead of d_add will give the lower fs a chance
275 * to allocate the d_fsdata field but will still instantiate and hash the
276 * lower_dentry. Without this, sharefs could not stack on top of itself.
277 */
278 d_inode(lower_dir_dentry)->i_op->lookup(d_inode(lower_dir_dentry),
279 lower_dentry, flags);
280
281 setup_lower:
282 lower_path.dentry = lower_dentry;
283 lower_path.mnt = mntget(lower_dir_mnt);
284 sharefs_set_lower_path(dentry, &lower_path);
285
286 /*
287 * If the intent is to create a file, then don't return an error, so
288 * the VFS will continue the process of making this negative dentry
289 * into a positive one.
290 */
291 if (err == -ENOENT || (flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET)))
292 err = 0;
293
294 out:
295 if (err)
296 return ERR_PTR(err);
297 return ret_dentry;
298 }
299
sharefs_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)300 struct dentry *sharefs_lookup(struct inode *dir, struct dentry *dentry,
301 unsigned int flags)
302 {
303 int err;
304 struct dentry *ret, *parent;
305 struct path lower_parent_path;
306
307 parent = dget_parent(dentry);
308
309 sharefs_get_lower_path(parent, &lower_parent_path);
310
311 /* allocate dentry private data. We free it in ->d_release */
312 err = new_dentry_private_data(dentry);
313 if (err) {
314 ret = ERR_PTR(err);
315 goto out;
316 }
317 ret = __sharefs_lookup(dentry, flags, &lower_parent_path);
318 if (IS_ERR(ret)) {
319 sharefs_err("sharefs_lookup error!");
320 goto out;
321 }
322
323 if (ret)
324 dentry = ret;
325 if (d_inode(dentry))
326 fsstack_copy_attr_times(d_inode(dentry),
327 sharefs_lower_inode(d_inode(dentry)));
328 /* update parent directory's atime */
329 fsstack_copy_attr_atime(d_inode(parent),
330 sharefs_lower_inode(d_inode(parent)));
331 fixup_perm_from_level(d_inode(parent), dentry);
332 out:
333 sharefs_put_lower_path(parent, &lower_parent_path);
334 dput(parent);
335 return ret;
336 }
337