1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * (C) 2001 Clemson University and The University of Chicago |
4 | * |
5 | * See COPYING in top-level directory. |
6 | */ |
7 | |
8 | /* |
9 | * Linux VFS namei operations. |
10 | */ |
11 | |
12 | #include "protocol.h" |
13 | #include "orangefs-kernel.h" |
14 | |
15 | /* |
16 | * Get a newly allocated inode to go with a negative dentry. |
17 | */ |
18 | static int orangefs_create(struct mnt_idmap *idmap, |
19 | struct inode *dir, |
20 | struct dentry *dentry, |
21 | umode_t mode, |
22 | bool exclusive) |
23 | { |
24 | struct orangefs_inode_s *parent = ORANGEFS_I(inode: dir); |
25 | struct orangefs_kernel_op_s *new_op; |
26 | struct orangefs_object_kref ref; |
27 | struct inode *inode; |
28 | struct iattr iattr; |
29 | int ret; |
30 | |
31 | gossip_debug(GOSSIP_NAME_DEBUG, "%s: %pd\n" , |
32 | __func__, |
33 | dentry); |
34 | |
35 | new_op = op_alloc(ORANGEFS_VFS_OP_CREATE); |
36 | if (!new_op) |
37 | return -ENOMEM; |
38 | |
39 | new_op->upcall.req.create.parent_refn = parent->refn; |
40 | |
41 | fill_default_sys_attrs(new_op->upcall.req.create.attributes, |
42 | ORANGEFS_TYPE_METAFILE, mode); |
43 | |
44 | strncpy(p: new_op->upcall.req.create.d_name, |
45 | q: dentry->d_name.name, ORANGEFS_NAME_MAX - 1); |
46 | |
47 | ret = service_operation(op: new_op, op_name: __func__, get_interruptible_flag(dir)); |
48 | |
49 | gossip_debug(GOSSIP_NAME_DEBUG, |
50 | "%s: %pd: handle:%pU: fsid:%d: new_op:%p: ret:%d:\n" , |
51 | __func__, |
52 | dentry, |
53 | &new_op->downcall.resp.create.refn.khandle, |
54 | new_op->downcall.resp.create.refn.fs_id, |
55 | new_op, |
56 | ret); |
57 | |
58 | if (ret < 0) |
59 | goto out; |
60 | |
61 | ref = new_op->downcall.resp.create.refn; |
62 | |
63 | inode = orangefs_new_inode(sb: dir->i_sb, dir, S_IFREG | mode, dev: 0, ref: &ref); |
64 | if (IS_ERR(ptr: inode)) { |
65 | gossip_err("%s: Failed to allocate inode for file :%pd:\n" , |
66 | __func__, |
67 | dentry); |
68 | ret = PTR_ERR(ptr: inode); |
69 | goto out; |
70 | } |
71 | |
72 | gossip_debug(GOSSIP_NAME_DEBUG, |
73 | "%s: Assigned inode :%pU: for file :%pd:\n" , |
74 | __func__, |
75 | get_khandle_from_ino(inode), |
76 | dentry); |
77 | |
78 | d_instantiate_new(dentry, inode); |
79 | orangefs_set_timeout(dentry); |
80 | |
81 | gossip_debug(GOSSIP_NAME_DEBUG, |
82 | "%s: dentry instantiated for %pd\n" , |
83 | __func__, |
84 | dentry); |
85 | |
86 | memset(&iattr, 0, sizeof iattr); |
87 | iattr.ia_valid |= ATTR_MTIME | ATTR_CTIME; |
88 | iattr.ia_mtime = iattr.ia_ctime = current_time(inode: dir); |
89 | __orangefs_setattr(dir, &iattr); |
90 | ret = 0; |
91 | out: |
92 | op_release(op: new_op); |
93 | gossip_debug(GOSSIP_NAME_DEBUG, |
94 | "%s: %pd: returning %d\n" , |
95 | __func__, |
96 | dentry, |
97 | ret); |
98 | return ret; |
99 | } |
100 | |
101 | /* |
102 | * Attempt to resolve an object name (dentry->d_name), parent handle, and |
103 | * fsid into a handle for the object. |
104 | */ |
105 | static struct dentry *orangefs_lookup(struct inode *dir, struct dentry *dentry, |
106 | unsigned int flags) |
107 | { |
108 | struct orangefs_inode_s *parent = ORANGEFS_I(inode: dir); |
109 | struct orangefs_kernel_op_s *new_op; |
110 | struct inode *inode; |
111 | int ret = -EINVAL; |
112 | |
113 | /* |
114 | * in theory we could skip a lookup here (if the intent is to |
115 | * create) in order to avoid a potentially failed lookup, but |
116 | * leaving it in can skip a valid lookup and try to create a file |
117 | * that already exists (e.g. the vfs already handles checking for |
118 | * -EEXIST on O_EXCL opens, which is broken if we skip this lookup |
119 | * in the create path) |
120 | */ |
121 | gossip_debug(GOSSIP_NAME_DEBUG, "%s called on %pd\n" , |
122 | __func__, dentry); |
123 | |
124 | if (dentry->d_name.len > (ORANGEFS_NAME_MAX - 1)) |
125 | return ERR_PTR(error: -ENAMETOOLONG); |
126 | |
127 | new_op = op_alloc(ORANGEFS_VFS_OP_LOOKUP); |
128 | if (!new_op) |
129 | return ERR_PTR(error: -ENOMEM); |
130 | |
131 | new_op->upcall.req.lookup.sym_follow = ORANGEFS_LOOKUP_LINK_NO_FOLLOW; |
132 | |
133 | gossip_debug(GOSSIP_NAME_DEBUG, "%s:%s:%d using parent %pU\n" , |
134 | __FILE__, |
135 | __func__, |
136 | __LINE__, |
137 | &parent->refn.khandle); |
138 | new_op->upcall.req.lookup.parent_refn = parent->refn; |
139 | |
140 | strncpy(p: new_op->upcall.req.lookup.d_name, q: dentry->d_name.name, |
141 | ORANGEFS_NAME_MAX - 1); |
142 | |
143 | gossip_debug(GOSSIP_NAME_DEBUG, |
144 | "%s: doing lookup on %s under %pU,%d\n" , |
145 | __func__, |
146 | new_op->upcall.req.lookup.d_name, |
147 | &new_op->upcall.req.lookup.parent_refn.khandle, |
148 | new_op->upcall.req.lookup.parent_refn.fs_id); |
149 | |
150 | ret = service_operation(op: new_op, op_name: __func__, get_interruptible_flag(dir)); |
151 | |
152 | gossip_debug(GOSSIP_NAME_DEBUG, |
153 | "Lookup Got %pU, fsid %d (ret=%d)\n" , |
154 | &new_op->downcall.resp.lookup.refn.khandle, |
155 | new_op->downcall.resp.lookup.refn.fs_id, |
156 | ret); |
157 | |
158 | if (ret == 0) { |
159 | orangefs_set_timeout(dentry); |
160 | inode = orangefs_iget(sb: dir->i_sb, ref: &new_op->downcall.resp.lookup.refn); |
161 | } else if (ret == -ENOENT) { |
162 | inode = NULL; |
163 | } else { |
164 | /* must be a non-recoverable error */ |
165 | inode = ERR_PTR(error: ret); |
166 | } |
167 | |
168 | op_release(op: new_op); |
169 | return d_splice_alias(inode, dentry); |
170 | } |
171 | |
172 | /* return 0 on success; non-zero otherwise */ |
173 | static int orangefs_unlink(struct inode *dir, struct dentry *dentry) |
174 | { |
175 | struct inode *inode = dentry->d_inode; |
176 | struct orangefs_inode_s *parent = ORANGEFS_I(inode: dir); |
177 | struct orangefs_kernel_op_s *new_op; |
178 | struct iattr iattr; |
179 | int ret; |
180 | |
181 | gossip_debug(GOSSIP_NAME_DEBUG, |
182 | "%s: called on %pd\n" |
183 | " (inode %pU): Parent is %pU | fs_id %d\n" , |
184 | __func__, |
185 | dentry, |
186 | get_khandle_from_ino(inode), |
187 | &parent->refn.khandle, |
188 | parent->refn.fs_id); |
189 | |
190 | new_op = op_alloc(ORANGEFS_VFS_OP_REMOVE); |
191 | if (!new_op) |
192 | return -ENOMEM; |
193 | |
194 | new_op->upcall.req.remove.parent_refn = parent->refn; |
195 | strncpy(p: new_op->upcall.req.remove.d_name, q: dentry->d_name.name, |
196 | ORANGEFS_NAME_MAX - 1); |
197 | |
198 | ret = service_operation(op: new_op, op_name: "orangefs_unlink" , |
199 | get_interruptible_flag(inode)); |
200 | |
201 | gossip_debug(GOSSIP_NAME_DEBUG, |
202 | "%s: service_operation returned:%d:\n" , |
203 | __func__, |
204 | ret); |
205 | |
206 | op_release(op: new_op); |
207 | |
208 | if (!ret) { |
209 | drop_nlink(inode); |
210 | |
211 | memset(&iattr, 0, sizeof iattr); |
212 | iattr.ia_valid |= ATTR_MTIME | ATTR_CTIME; |
213 | iattr.ia_mtime = iattr.ia_ctime = current_time(inode: dir); |
214 | __orangefs_setattr(dir, &iattr); |
215 | } |
216 | return ret; |
217 | } |
218 | |
219 | static int orangefs_symlink(struct mnt_idmap *idmap, |
220 | struct inode *dir, |
221 | struct dentry *dentry, |
222 | const char *symname) |
223 | { |
224 | struct orangefs_inode_s *parent = ORANGEFS_I(inode: dir); |
225 | struct orangefs_kernel_op_s *new_op; |
226 | struct orangefs_object_kref ref; |
227 | struct inode *inode; |
228 | struct iattr iattr; |
229 | int mode = 0755; |
230 | int ret; |
231 | |
232 | gossip_debug(GOSSIP_NAME_DEBUG, "%s: called\n" , __func__); |
233 | |
234 | if (!symname) |
235 | return -EINVAL; |
236 | |
237 | if (strlen(symname)+1 > ORANGEFS_NAME_MAX) |
238 | return -ENAMETOOLONG; |
239 | |
240 | new_op = op_alloc(ORANGEFS_VFS_OP_SYMLINK); |
241 | if (!new_op) |
242 | return -ENOMEM; |
243 | |
244 | new_op->upcall.req.sym.parent_refn = parent->refn; |
245 | |
246 | fill_default_sys_attrs(new_op->upcall.req.sym.attributes, |
247 | ORANGEFS_TYPE_SYMLINK, |
248 | mode); |
249 | |
250 | strncpy(p: new_op->upcall.req.sym.entry_name, |
251 | q: dentry->d_name.name, |
252 | ORANGEFS_NAME_MAX - 1); |
253 | strncpy(p: new_op->upcall.req.sym.target, q: symname, ORANGEFS_NAME_MAX - 1); |
254 | |
255 | ret = service_operation(op: new_op, op_name: __func__, get_interruptible_flag(dir)); |
256 | |
257 | gossip_debug(GOSSIP_NAME_DEBUG, |
258 | "Symlink Got ORANGEFS handle %pU on fsid %d (ret=%d)\n" , |
259 | &new_op->downcall.resp.sym.refn.khandle, |
260 | new_op->downcall.resp.sym.refn.fs_id, ret); |
261 | |
262 | if (ret < 0) { |
263 | gossip_debug(GOSSIP_NAME_DEBUG, |
264 | "%s: failed with error code %d\n" , |
265 | __func__, ret); |
266 | goto out; |
267 | } |
268 | |
269 | ref = new_op->downcall.resp.sym.refn; |
270 | |
271 | inode = orangefs_new_inode(sb: dir->i_sb, dir, S_IFLNK | mode, dev: 0, ref: &ref); |
272 | if (IS_ERR(ptr: inode)) { |
273 | gossip_err |
274 | ("*** Failed to allocate orangefs symlink inode\n" ); |
275 | ret = PTR_ERR(ptr: inode); |
276 | goto out; |
277 | } |
278 | /* |
279 | * This is necessary because orangefs_inode_getattr will not |
280 | * re-read symlink size as it is impossible for it to change. |
281 | * Invalidating the cache does not help. orangefs_new_inode |
282 | * does not set the correct size (it does not know symname). |
283 | */ |
284 | inode->i_size = strlen(symname); |
285 | |
286 | gossip_debug(GOSSIP_NAME_DEBUG, |
287 | "Assigned symlink inode new number of %pU\n" , |
288 | get_khandle_from_ino(inode)); |
289 | |
290 | d_instantiate_new(dentry, inode); |
291 | orangefs_set_timeout(dentry); |
292 | |
293 | gossip_debug(GOSSIP_NAME_DEBUG, |
294 | "Inode (Symlink) %pU -> %pd\n" , |
295 | get_khandle_from_ino(inode), |
296 | dentry); |
297 | |
298 | memset(&iattr, 0, sizeof iattr); |
299 | iattr.ia_valid |= ATTR_MTIME | ATTR_CTIME; |
300 | iattr.ia_mtime = iattr.ia_ctime = current_time(inode: dir); |
301 | __orangefs_setattr(dir, &iattr); |
302 | ret = 0; |
303 | out: |
304 | op_release(op: new_op); |
305 | return ret; |
306 | } |
307 | |
308 | static int orangefs_mkdir(struct mnt_idmap *idmap, struct inode *dir, |
309 | struct dentry *dentry, umode_t mode) |
310 | { |
311 | struct orangefs_inode_s *parent = ORANGEFS_I(inode: dir); |
312 | struct orangefs_kernel_op_s *new_op; |
313 | struct orangefs_object_kref ref; |
314 | struct inode *inode; |
315 | struct iattr iattr; |
316 | int ret; |
317 | |
318 | new_op = op_alloc(ORANGEFS_VFS_OP_MKDIR); |
319 | if (!new_op) |
320 | return -ENOMEM; |
321 | |
322 | new_op->upcall.req.mkdir.parent_refn = parent->refn; |
323 | |
324 | fill_default_sys_attrs(new_op->upcall.req.mkdir.attributes, |
325 | ORANGEFS_TYPE_DIRECTORY, mode); |
326 | |
327 | strncpy(p: new_op->upcall.req.mkdir.d_name, |
328 | q: dentry->d_name.name, ORANGEFS_NAME_MAX - 1); |
329 | |
330 | ret = service_operation(op: new_op, op_name: __func__, get_interruptible_flag(dir)); |
331 | |
332 | gossip_debug(GOSSIP_NAME_DEBUG, |
333 | "Mkdir Got ORANGEFS handle %pU on fsid %d\n" , |
334 | &new_op->downcall.resp.mkdir.refn.khandle, |
335 | new_op->downcall.resp.mkdir.refn.fs_id); |
336 | |
337 | if (ret < 0) { |
338 | gossip_debug(GOSSIP_NAME_DEBUG, |
339 | "%s: failed with error code %d\n" , |
340 | __func__, ret); |
341 | goto out; |
342 | } |
343 | |
344 | ref = new_op->downcall.resp.mkdir.refn; |
345 | |
346 | inode = orangefs_new_inode(sb: dir->i_sb, dir, S_IFDIR | mode, dev: 0, ref: &ref); |
347 | if (IS_ERR(ptr: inode)) { |
348 | gossip_err("*** Failed to allocate orangefs dir inode\n" ); |
349 | ret = PTR_ERR(ptr: inode); |
350 | goto out; |
351 | } |
352 | |
353 | gossip_debug(GOSSIP_NAME_DEBUG, |
354 | "Assigned dir inode new number of %pU\n" , |
355 | get_khandle_from_ino(inode)); |
356 | |
357 | d_instantiate_new(dentry, inode); |
358 | orangefs_set_timeout(dentry); |
359 | |
360 | gossip_debug(GOSSIP_NAME_DEBUG, |
361 | "Inode (Directory) %pU -> %pd\n" , |
362 | get_khandle_from_ino(inode), |
363 | dentry); |
364 | |
365 | /* |
366 | * NOTE: we have no good way to keep nlink consistent for directories |
367 | * across clients; keep constant at 1. |
368 | */ |
369 | memset(&iattr, 0, sizeof iattr); |
370 | iattr.ia_valid |= ATTR_MTIME | ATTR_CTIME; |
371 | iattr.ia_mtime = iattr.ia_ctime = current_time(inode: dir); |
372 | __orangefs_setattr(dir, &iattr); |
373 | out: |
374 | op_release(op: new_op); |
375 | return ret; |
376 | } |
377 | |
378 | static int orangefs_rename(struct mnt_idmap *idmap, |
379 | struct inode *old_dir, |
380 | struct dentry *old_dentry, |
381 | struct inode *new_dir, |
382 | struct dentry *new_dentry, |
383 | unsigned int flags) |
384 | { |
385 | struct orangefs_kernel_op_s *new_op; |
386 | struct iattr iattr; |
387 | int ret; |
388 | |
389 | if (flags) |
390 | return -EINVAL; |
391 | |
392 | gossip_debug(GOSSIP_NAME_DEBUG, |
393 | "orangefs_rename: called (%pd2 => %pd2) ct=%d\n" , |
394 | old_dentry, new_dentry, d_count(new_dentry)); |
395 | |
396 | memset(&iattr, 0, sizeof iattr); |
397 | iattr.ia_valid |= ATTR_MTIME | ATTR_CTIME; |
398 | iattr.ia_mtime = iattr.ia_ctime = current_time(inode: new_dir); |
399 | __orangefs_setattr(new_dir, &iattr); |
400 | |
401 | new_op = op_alloc(ORANGEFS_VFS_OP_RENAME); |
402 | if (!new_op) |
403 | return -EINVAL; |
404 | |
405 | new_op->upcall.req.rename.old_parent_refn = ORANGEFS_I(inode: old_dir)->refn; |
406 | new_op->upcall.req.rename.new_parent_refn = ORANGEFS_I(inode: new_dir)->refn; |
407 | |
408 | strncpy(p: new_op->upcall.req.rename.d_old_name, |
409 | q: old_dentry->d_name.name, |
410 | ORANGEFS_NAME_MAX - 1); |
411 | strncpy(p: new_op->upcall.req.rename.d_new_name, |
412 | q: new_dentry->d_name.name, |
413 | ORANGEFS_NAME_MAX - 1); |
414 | |
415 | ret = service_operation(op: new_op, |
416 | op_name: "orangefs_rename" , |
417 | get_interruptible_flag(old_dentry->d_inode)); |
418 | |
419 | gossip_debug(GOSSIP_NAME_DEBUG, |
420 | "orangefs_rename: got downcall status %d\n" , |
421 | ret); |
422 | |
423 | if (new_dentry->d_inode) |
424 | inode_set_ctime_current(inode: d_inode(dentry: new_dentry)); |
425 | |
426 | op_release(op: new_op); |
427 | return ret; |
428 | } |
429 | |
430 | /* ORANGEFS implementation of VFS inode operations for directories */ |
431 | const struct inode_operations orangefs_dir_inode_operations = { |
432 | .lookup = orangefs_lookup, |
433 | .get_inode_acl = orangefs_get_acl, |
434 | .set_acl = orangefs_set_acl, |
435 | .create = orangefs_create, |
436 | .unlink = orangefs_unlink, |
437 | .symlink = orangefs_symlink, |
438 | .mkdir = orangefs_mkdir, |
439 | .rmdir = orangefs_unlink, |
440 | .rename = orangefs_rename, |
441 | .setattr = orangefs_setattr, |
442 | .getattr = orangefs_getattr, |
443 | .listxattr = orangefs_listxattr, |
444 | .permission = orangefs_permission, |
445 | .update_time = orangefs_update_time, |
446 | }; |
447 | |