1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * This file contains vfs inode ops for the 9P2000 protocol. |
4 | * |
5 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> |
6 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | |
11 | #include <linux/module.h> |
12 | #include <linux/errno.h> |
13 | #include <linux/fs.h> |
14 | #include <linux/file.h> |
15 | #include <linux/pagemap.h> |
16 | #include <linux/stat.h> |
17 | #include <linux/string.h> |
18 | #include <linux/namei.h> |
19 | #include <linux/sched.h> |
20 | #include <linux/slab.h> |
21 | #include <linux/xattr.h> |
22 | #include <linux/posix_acl.h> |
23 | #include <net/9p/9p.h> |
24 | #include <net/9p/client.h> |
25 | |
26 | #include "v9fs.h" |
27 | #include "v9fs_vfs.h" |
28 | #include "fid.h" |
29 | #include "cache.h" |
30 | #include "xattr.h" |
31 | #include "acl.h" |
32 | |
33 | static const struct inode_operations v9fs_dir_inode_operations; |
34 | static const struct inode_operations v9fs_dir_inode_operations_dotu; |
35 | static const struct inode_operations v9fs_file_inode_operations; |
36 | static const struct inode_operations v9fs_symlink_inode_operations; |
37 | |
38 | /** |
39 | * unixmode2p9mode - convert unix mode bits to plan 9 |
40 | * @v9ses: v9fs session information |
41 | * @mode: mode to convert |
42 | * |
43 | */ |
44 | |
45 | static u32 unixmode2p9mode(struct v9fs_session_info *v9ses, umode_t mode) |
46 | { |
47 | int res; |
48 | |
49 | res = mode & 0777; |
50 | if (S_ISDIR(mode)) |
51 | res |= P9_DMDIR; |
52 | if (v9fs_proto_dotu(v9ses)) { |
53 | if (v9ses->nodev == 0) { |
54 | if (S_ISSOCK(mode)) |
55 | res |= P9_DMSOCKET; |
56 | if (S_ISFIFO(mode)) |
57 | res |= P9_DMNAMEDPIPE; |
58 | if (S_ISBLK(mode)) |
59 | res |= P9_DMDEVICE; |
60 | if (S_ISCHR(mode)) |
61 | res |= P9_DMDEVICE; |
62 | } |
63 | |
64 | if ((mode & S_ISUID) == S_ISUID) |
65 | res |= P9_DMSETUID; |
66 | if ((mode & S_ISGID) == S_ISGID) |
67 | res |= P9_DMSETGID; |
68 | if ((mode & S_ISVTX) == S_ISVTX) |
69 | res |= P9_DMSETVTX; |
70 | } |
71 | return res; |
72 | } |
73 | |
74 | /** |
75 | * p9mode2perm- convert plan9 mode bits to unix permission bits |
76 | * @v9ses: v9fs session information |
77 | * @stat: p9_wstat from which mode need to be derived |
78 | * |
79 | */ |
80 | static int p9mode2perm(struct v9fs_session_info *v9ses, |
81 | struct p9_wstat *stat) |
82 | { |
83 | int res; |
84 | int mode = stat->mode; |
85 | |
86 | res = mode & 0777; /* S_IRWXUGO */ |
87 | if (v9fs_proto_dotu(v9ses)) { |
88 | if ((mode & P9_DMSETUID) == P9_DMSETUID) |
89 | res |= S_ISUID; |
90 | |
91 | if ((mode & P9_DMSETGID) == P9_DMSETGID) |
92 | res |= S_ISGID; |
93 | |
94 | if ((mode & P9_DMSETVTX) == P9_DMSETVTX) |
95 | res |= S_ISVTX; |
96 | } |
97 | return res; |
98 | } |
99 | |
100 | /** |
101 | * p9mode2unixmode- convert plan9 mode bits to unix mode bits |
102 | * @v9ses: v9fs session information |
103 | * @stat: p9_wstat from which mode need to be derived |
104 | * @rdev: major number, minor number in case of device files. |
105 | * |
106 | */ |
107 | static umode_t p9mode2unixmode(struct v9fs_session_info *v9ses, |
108 | struct p9_wstat *stat, dev_t *rdev) |
109 | { |
110 | int res, r; |
111 | u32 mode = stat->mode; |
112 | |
113 | *rdev = 0; |
114 | res = p9mode2perm(v9ses, stat); |
115 | |
116 | if ((mode & P9_DMDIR) == P9_DMDIR) |
117 | res |= S_IFDIR; |
118 | else if ((mode & P9_DMSYMLINK) && (v9fs_proto_dotu(v9ses))) |
119 | res |= S_IFLNK; |
120 | else if ((mode & P9_DMSOCKET) && (v9fs_proto_dotu(v9ses)) |
121 | && (v9ses->nodev == 0)) |
122 | res |= S_IFSOCK; |
123 | else if ((mode & P9_DMNAMEDPIPE) && (v9fs_proto_dotu(v9ses)) |
124 | && (v9ses->nodev == 0)) |
125 | res |= S_IFIFO; |
126 | else if ((mode & P9_DMDEVICE) && (v9fs_proto_dotu(v9ses)) |
127 | && (v9ses->nodev == 0)) { |
128 | char type = 0; |
129 | int major = -1, minor = -1; |
130 | |
131 | r = sscanf(stat->extension, "%c %i %i" , &type, &major, &minor); |
132 | if (r != 3) { |
133 | p9_debug(P9_DEBUG_ERROR, |
134 | "invalid device string, umode will be bogus: %s\n" , |
135 | stat->extension); |
136 | return res; |
137 | } |
138 | switch (type) { |
139 | case 'c': |
140 | res |= S_IFCHR; |
141 | break; |
142 | case 'b': |
143 | res |= S_IFBLK; |
144 | break; |
145 | default: |
146 | p9_debug(P9_DEBUG_ERROR, "Unknown special type %c %s\n" , |
147 | type, stat->extension); |
148 | } |
149 | *rdev = MKDEV(major, minor); |
150 | } else |
151 | res |= S_IFREG; |
152 | |
153 | return res; |
154 | } |
155 | |
156 | /** |
157 | * v9fs_uflags2omode- convert posix open flags to plan 9 mode bits |
158 | * @uflags: flags to convert |
159 | * @extended: if .u extensions are active |
160 | */ |
161 | |
162 | int v9fs_uflags2omode(int uflags, int extended) |
163 | { |
164 | int ret; |
165 | |
166 | switch (uflags&3) { |
167 | default: |
168 | case O_RDONLY: |
169 | ret = P9_OREAD; |
170 | break; |
171 | |
172 | case O_WRONLY: |
173 | ret = P9_OWRITE; |
174 | break; |
175 | |
176 | case O_RDWR: |
177 | ret = P9_ORDWR; |
178 | break; |
179 | } |
180 | |
181 | if (uflags & O_TRUNC) |
182 | ret |= P9_OTRUNC; |
183 | |
184 | if (extended) { |
185 | if (uflags & O_EXCL) |
186 | ret |= P9_OEXCL; |
187 | |
188 | if (uflags & O_APPEND) |
189 | ret |= P9_OAPPEND; |
190 | } |
191 | |
192 | return ret; |
193 | } |
194 | |
195 | /** |
196 | * v9fs_blank_wstat - helper function to setup a 9P stat structure |
197 | * @wstat: structure to initialize |
198 | * |
199 | */ |
200 | |
201 | void |
202 | v9fs_blank_wstat(struct p9_wstat *wstat) |
203 | { |
204 | wstat->type = ~0; |
205 | wstat->dev = ~0; |
206 | wstat->qid.type = ~0; |
207 | wstat->qid.version = ~0; |
208 | *((long long *)&wstat->qid.path) = ~0; |
209 | wstat->mode = ~0; |
210 | wstat->atime = ~0; |
211 | wstat->mtime = ~0; |
212 | wstat->length = ~0; |
213 | wstat->name = NULL; |
214 | wstat->uid = NULL; |
215 | wstat->gid = NULL; |
216 | wstat->muid = NULL; |
217 | wstat->n_uid = INVALID_UID; |
218 | wstat->n_gid = INVALID_GID; |
219 | wstat->n_muid = INVALID_UID; |
220 | wstat->extension = NULL; |
221 | } |
222 | |
223 | /** |
224 | * v9fs_alloc_inode - helper function to allocate an inode |
225 | * @sb: The superblock to allocate the inode from |
226 | */ |
227 | struct inode *v9fs_alloc_inode(struct super_block *sb) |
228 | { |
229 | struct v9fs_inode *v9inode; |
230 | |
231 | v9inode = alloc_inode_sb(sb, cache: v9fs_inode_cache, GFP_KERNEL); |
232 | if (!v9inode) |
233 | return NULL; |
234 | v9inode->cache_validity = 0; |
235 | mutex_init(&v9inode->v_mutex); |
236 | return &v9inode->netfs.inode; |
237 | } |
238 | |
239 | /** |
240 | * v9fs_free_inode - destroy an inode |
241 | * @inode: The inode to be freed |
242 | */ |
243 | |
244 | void v9fs_free_inode(struct inode *inode) |
245 | { |
246 | kmem_cache_free(s: v9fs_inode_cache, objp: V9FS_I(inode)); |
247 | } |
248 | |
249 | /* |
250 | * Set parameters for the netfs library |
251 | */ |
252 | void v9fs_set_netfs_context(struct inode *inode) |
253 | { |
254 | struct v9fs_inode *v9inode = V9FS_I(inode); |
255 | netfs_inode_init(ctx: &v9inode->netfs, ops: &v9fs_req_ops, use_zero_point: true); |
256 | } |
257 | |
258 | int v9fs_init_inode(struct v9fs_session_info *v9ses, |
259 | struct inode *inode, struct p9_qid *qid, umode_t mode, dev_t rdev) |
260 | { |
261 | int err = 0; |
262 | struct v9fs_inode *v9inode = V9FS_I(inode); |
263 | |
264 | memcpy(&v9inode->qid, qid, sizeof(struct p9_qid)); |
265 | |
266 | inode_init_owner(idmap: &nop_mnt_idmap, inode, NULL, mode); |
267 | inode->i_blocks = 0; |
268 | inode->i_rdev = rdev; |
269 | simple_inode_init_ts(inode); |
270 | inode->i_mapping->a_ops = &v9fs_addr_operations; |
271 | inode->i_private = NULL; |
272 | |
273 | switch (mode & S_IFMT) { |
274 | case S_IFIFO: |
275 | case S_IFBLK: |
276 | case S_IFCHR: |
277 | case S_IFSOCK: |
278 | if (v9fs_proto_dotl(v9ses)) { |
279 | inode->i_op = &v9fs_file_inode_operations_dotl; |
280 | } else if (v9fs_proto_dotu(v9ses)) { |
281 | inode->i_op = &v9fs_file_inode_operations; |
282 | } else { |
283 | p9_debug(P9_DEBUG_ERROR, |
284 | "special files without extended mode\n" ); |
285 | err = -EINVAL; |
286 | goto error; |
287 | } |
288 | init_special_inode(inode, inode->i_mode, inode->i_rdev); |
289 | break; |
290 | case S_IFREG: |
291 | if (v9fs_proto_dotl(v9ses)) { |
292 | inode->i_op = &v9fs_file_inode_operations_dotl; |
293 | inode->i_fop = &v9fs_file_operations_dotl; |
294 | } else { |
295 | inode->i_op = &v9fs_file_inode_operations; |
296 | inode->i_fop = &v9fs_file_operations; |
297 | } |
298 | |
299 | break; |
300 | case S_IFLNK: |
301 | if (!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses)) { |
302 | p9_debug(P9_DEBUG_ERROR, |
303 | "extended modes used with legacy protocol\n" ); |
304 | err = -EINVAL; |
305 | goto error; |
306 | } |
307 | |
308 | if (v9fs_proto_dotl(v9ses)) |
309 | inode->i_op = &v9fs_symlink_inode_operations_dotl; |
310 | else |
311 | inode->i_op = &v9fs_symlink_inode_operations; |
312 | |
313 | break; |
314 | case S_IFDIR: |
315 | inc_nlink(inode); |
316 | if (v9fs_proto_dotl(v9ses)) |
317 | inode->i_op = &v9fs_dir_inode_operations_dotl; |
318 | else if (v9fs_proto_dotu(v9ses)) |
319 | inode->i_op = &v9fs_dir_inode_operations_dotu; |
320 | else |
321 | inode->i_op = &v9fs_dir_inode_operations; |
322 | |
323 | if (v9fs_proto_dotl(v9ses)) |
324 | inode->i_fop = &v9fs_dir_operations_dotl; |
325 | else |
326 | inode->i_fop = &v9fs_dir_operations; |
327 | |
328 | break; |
329 | default: |
330 | p9_debug(P9_DEBUG_ERROR, "BAD mode 0x%hx S_IFMT 0x%x\n" , |
331 | mode, mode & S_IFMT); |
332 | err = -EINVAL; |
333 | goto error; |
334 | } |
335 | error: |
336 | return err; |
337 | |
338 | } |
339 | |
340 | /** |
341 | * v9fs_evict_inode - Remove an inode from the inode cache |
342 | * @inode: inode to release |
343 | * |
344 | */ |
345 | void v9fs_evict_inode(struct inode *inode) |
346 | { |
347 | struct v9fs_inode __maybe_unused *v9inode = V9FS_I(inode); |
348 | __le32 __maybe_unused version; |
349 | |
350 | if (!is_bad_inode(inode)) { |
351 | truncate_inode_pages_final(&inode->i_data); |
352 | |
353 | version = cpu_to_le32(v9inode->qid.version); |
354 | netfs_clear_inode_writeback(inode, aux: &version); |
355 | |
356 | clear_inode(inode); |
357 | filemap_fdatawrite(&inode->i_data); |
358 | |
359 | #ifdef CONFIG_9P_FSCACHE |
360 | if (v9fs_inode_cookie(v9inode)) |
361 | fscache_relinquish_cookie(cookie: v9fs_inode_cookie(v9inode), retire: false); |
362 | #endif |
363 | } else |
364 | clear_inode(inode); |
365 | } |
366 | |
367 | struct inode *v9fs_fid_iget(struct super_block *sb, struct p9_fid *fid) |
368 | { |
369 | dev_t rdev; |
370 | int retval; |
371 | umode_t umode; |
372 | struct inode *inode; |
373 | struct p9_wstat *st; |
374 | struct v9fs_session_info *v9ses = sb->s_fs_info; |
375 | |
376 | inode = iget_locked(sb, QID2INO(&fid->qid)); |
377 | if (unlikely(!inode)) |
378 | return ERR_PTR(error: -ENOMEM); |
379 | if (!(inode->i_state & I_NEW)) |
380 | return inode; |
381 | |
382 | /* |
383 | * initialize the inode with the stat info |
384 | * FIXME!! we may need support for stale inodes |
385 | * later. |
386 | */ |
387 | st = p9_client_stat(fid); |
388 | if (IS_ERR(ptr: st)) { |
389 | retval = PTR_ERR(ptr: st); |
390 | goto error; |
391 | } |
392 | |
393 | umode = p9mode2unixmode(v9ses, stat: st, rdev: &rdev); |
394 | retval = v9fs_init_inode(v9ses, inode, qid: &fid->qid, mode: umode, rdev); |
395 | v9fs_stat2inode(stat: st, inode, sb, flags: 0); |
396 | p9stat_free(stbuf: st); |
397 | kfree(objp: st); |
398 | if (retval) |
399 | goto error; |
400 | |
401 | v9fs_set_netfs_context(inode); |
402 | v9fs_cache_inode_get_cookie(inode); |
403 | unlock_new_inode(inode); |
404 | return inode; |
405 | error: |
406 | iget_failed(inode); |
407 | return ERR_PTR(error: retval); |
408 | |
409 | } |
410 | |
411 | /** |
412 | * v9fs_at_to_dotl_flags- convert Linux specific AT flags to |
413 | * plan 9 AT flag. |
414 | * @flags: flags to convert |
415 | */ |
416 | static int v9fs_at_to_dotl_flags(int flags) |
417 | { |
418 | int rflags = 0; |
419 | |
420 | if (flags & AT_REMOVEDIR) |
421 | rflags |= P9_DOTL_AT_REMOVEDIR; |
422 | |
423 | return rflags; |
424 | } |
425 | |
426 | /** |
427 | * v9fs_dec_count - helper functon to drop i_nlink. |
428 | * |
429 | * If a directory had nlink <= 2 (including . and ..), then we should not drop |
430 | * the link count, which indicates the underlying exported fs doesn't maintain |
431 | * nlink accurately. e.g. |
432 | * - overlayfs sets nlink to 1 for merged dir |
433 | * - ext4 (with dir_nlink feature enabled) sets nlink to 1 if a dir has more |
434 | * than EXT4_LINK_MAX (65000) links. |
435 | * |
436 | * @inode: inode whose nlink is being dropped |
437 | */ |
438 | static void v9fs_dec_count(struct inode *inode) |
439 | { |
440 | if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2) |
441 | drop_nlink(inode); |
442 | } |
443 | |
444 | /** |
445 | * v9fs_remove - helper function to remove files and directories |
446 | * @dir: directory inode that is being deleted |
447 | * @dentry: dentry that is being deleted |
448 | * @flags: removing a directory |
449 | * |
450 | */ |
451 | |
452 | static int v9fs_remove(struct inode *dir, struct dentry *dentry, int flags) |
453 | { |
454 | struct inode *inode; |
455 | int retval = -EOPNOTSUPP; |
456 | struct p9_fid *v9fid, *dfid; |
457 | struct v9fs_session_info *v9ses; |
458 | |
459 | p9_debug(P9_DEBUG_VFS, "inode: %p dentry: %p rmdir: %x\n" , |
460 | dir, dentry, flags); |
461 | |
462 | v9ses = v9fs_inode2v9ses(inode: dir); |
463 | inode = d_inode(dentry); |
464 | dfid = v9fs_parent_fid(dentry); |
465 | if (IS_ERR(ptr: dfid)) { |
466 | retval = PTR_ERR(ptr: dfid); |
467 | p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n" , retval); |
468 | return retval; |
469 | } |
470 | if (v9fs_proto_dotl(v9ses)) |
471 | retval = p9_client_unlinkat(dfid, name: dentry->d_name.name, |
472 | flags: v9fs_at_to_dotl_flags(flags)); |
473 | p9_fid_put(fid: dfid); |
474 | if (retval == -EOPNOTSUPP) { |
475 | /* Try the one based on path */ |
476 | v9fid = v9fs_fid_clone(dentry); |
477 | if (IS_ERR(ptr: v9fid)) |
478 | return PTR_ERR(ptr: v9fid); |
479 | retval = p9_client_remove(fid: v9fid); |
480 | } |
481 | if (!retval) { |
482 | /* |
483 | * directories on unlink should have zero |
484 | * link count |
485 | */ |
486 | if (flags & AT_REMOVEDIR) { |
487 | clear_nlink(inode); |
488 | v9fs_dec_count(inode: dir); |
489 | } else |
490 | v9fs_dec_count(inode); |
491 | |
492 | v9fs_invalidate_inode_attr(inode); |
493 | v9fs_invalidate_inode_attr(inode: dir); |
494 | |
495 | /* invalidate all fids associated with dentry */ |
496 | /* NOTE: This will not include open fids */ |
497 | dentry->d_op->d_release(dentry); |
498 | } |
499 | return retval; |
500 | } |
501 | |
502 | /** |
503 | * v9fs_create - Create a file |
504 | * @v9ses: session information |
505 | * @dir: directory that dentry is being created in |
506 | * @dentry: dentry that is being created |
507 | * @extension: 9p2000.u extension string to support devices, etc. |
508 | * @perm: create permissions |
509 | * @mode: open mode |
510 | * |
511 | */ |
512 | static struct p9_fid * |
513 | v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir, |
514 | struct dentry *dentry, char *extension, u32 perm, u8 mode) |
515 | { |
516 | int err; |
517 | const unsigned char *name; |
518 | struct p9_fid *dfid, *ofid = NULL, *fid = NULL; |
519 | struct inode *inode; |
520 | |
521 | p9_debug(P9_DEBUG_VFS, "name %pd\n" , dentry); |
522 | |
523 | name = dentry->d_name.name; |
524 | dfid = v9fs_parent_fid(dentry); |
525 | if (IS_ERR(ptr: dfid)) { |
526 | err = PTR_ERR(ptr: dfid); |
527 | p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n" , err); |
528 | return ERR_PTR(error: err); |
529 | } |
530 | |
531 | /* clone a fid to use for creation */ |
532 | ofid = clone_fid(fid: dfid); |
533 | if (IS_ERR(ptr: ofid)) { |
534 | err = PTR_ERR(ptr: ofid); |
535 | p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n" , err); |
536 | goto error; |
537 | } |
538 | |
539 | err = p9_client_fcreate(fid: ofid, name, perm, mode, extension); |
540 | if (err < 0) { |
541 | p9_debug(P9_DEBUG_VFS, "p9_client_fcreate failed %d\n" , err); |
542 | goto error; |
543 | } |
544 | |
545 | if (!(perm & P9_DMLINK)) { |
546 | /* now walk from the parent so we can get unopened fid */ |
547 | fid = p9_client_walk(oldfid: dfid, nwname: 1, wnames: &name, clone: 1); |
548 | if (IS_ERR(ptr: fid)) { |
549 | err = PTR_ERR(ptr: fid); |
550 | p9_debug(P9_DEBUG_VFS, |
551 | "p9_client_walk failed %d\n" , err); |
552 | goto error; |
553 | } |
554 | /* |
555 | * instantiate inode and assign the unopened fid to the dentry |
556 | */ |
557 | inode = v9fs_get_inode_from_fid(v9ses, fid, sb: dir->i_sb); |
558 | if (IS_ERR(ptr: inode)) { |
559 | err = PTR_ERR(ptr: inode); |
560 | p9_debug(P9_DEBUG_VFS, |
561 | "inode creation failed %d\n" , err); |
562 | goto error; |
563 | } |
564 | v9fs_fid_add(dentry, fid: &fid); |
565 | d_instantiate(dentry, inode); |
566 | } |
567 | p9_fid_put(fid: dfid); |
568 | return ofid; |
569 | error: |
570 | p9_fid_put(fid: dfid); |
571 | p9_fid_put(fid: ofid); |
572 | p9_fid_put(fid); |
573 | return ERR_PTR(error: err); |
574 | } |
575 | |
576 | /** |
577 | * v9fs_vfs_create - VFS hook to create a regular file |
578 | * @idmap: idmap of the mount |
579 | * @dir: The parent directory |
580 | * @dentry: The name of file to be created |
581 | * @mode: The UNIX file mode to set |
582 | * @excl: True if the file must not yet exist |
583 | * |
584 | * open(.., O_CREAT) is handled in v9fs_vfs_atomic_open(). This is only called |
585 | * for mknod(2). |
586 | * |
587 | */ |
588 | |
589 | static int |
590 | v9fs_vfs_create(struct mnt_idmap *idmap, struct inode *dir, |
591 | struct dentry *dentry, umode_t mode, bool excl) |
592 | { |
593 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode: dir); |
594 | u32 perm = unixmode2p9mode(v9ses, mode); |
595 | struct p9_fid *fid; |
596 | |
597 | /* P9_OEXCL? */ |
598 | fid = v9fs_create(v9ses, dir, dentry, NULL, perm, mode: P9_ORDWR); |
599 | if (IS_ERR(ptr: fid)) |
600 | return PTR_ERR(ptr: fid); |
601 | |
602 | v9fs_invalidate_inode_attr(inode: dir); |
603 | p9_fid_put(fid); |
604 | |
605 | return 0; |
606 | } |
607 | |
608 | /** |
609 | * v9fs_vfs_mkdir - VFS mkdir hook to create a directory |
610 | * @idmap: idmap of the mount |
611 | * @dir: inode that is being unlinked |
612 | * @dentry: dentry that is being unlinked |
613 | * @mode: mode for new directory |
614 | * |
615 | */ |
616 | |
617 | static int v9fs_vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, |
618 | struct dentry *dentry, umode_t mode) |
619 | { |
620 | int err; |
621 | u32 perm; |
622 | struct p9_fid *fid; |
623 | struct v9fs_session_info *v9ses; |
624 | |
625 | p9_debug(P9_DEBUG_VFS, "name %pd\n" , dentry); |
626 | err = 0; |
627 | v9ses = v9fs_inode2v9ses(inode: dir); |
628 | perm = unixmode2p9mode(v9ses, mode: mode | S_IFDIR); |
629 | fid = v9fs_create(v9ses, dir, dentry, NULL, perm, mode: P9_OREAD); |
630 | if (IS_ERR(ptr: fid)) { |
631 | err = PTR_ERR(ptr: fid); |
632 | fid = NULL; |
633 | } else { |
634 | inc_nlink(inode: dir); |
635 | v9fs_invalidate_inode_attr(inode: dir); |
636 | } |
637 | |
638 | if (fid) |
639 | p9_fid_put(fid); |
640 | |
641 | return err; |
642 | } |
643 | |
644 | /** |
645 | * v9fs_vfs_lookup - VFS lookup hook to "walk" to a new inode |
646 | * @dir: inode that is being walked from |
647 | * @dentry: dentry that is being walked to? |
648 | * @flags: lookup flags (unused) |
649 | * |
650 | */ |
651 | |
652 | struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, |
653 | unsigned int flags) |
654 | { |
655 | struct dentry *res; |
656 | struct v9fs_session_info *v9ses; |
657 | struct p9_fid *dfid, *fid; |
658 | struct inode *inode; |
659 | const unsigned char *name; |
660 | |
661 | p9_debug(P9_DEBUG_VFS, "dir: %p dentry: (%pd) %p flags: %x\n" , |
662 | dir, dentry, dentry, flags); |
663 | |
664 | if (dentry->d_name.len > NAME_MAX) |
665 | return ERR_PTR(error: -ENAMETOOLONG); |
666 | |
667 | v9ses = v9fs_inode2v9ses(inode: dir); |
668 | /* We can walk d_parent because we hold the dir->i_mutex */ |
669 | dfid = v9fs_parent_fid(dentry); |
670 | if (IS_ERR(ptr: dfid)) |
671 | return ERR_CAST(ptr: dfid); |
672 | |
673 | /* |
674 | * Make sure we don't use a wrong inode due to parallel |
675 | * unlink. For cached mode create calls request for new |
676 | * inode. But with cache disabled, lookup should do this. |
677 | */ |
678 | name = dentry->d_name.name; |
679 | fid = p9_client_walk(oldfid: dfid, nwname: 1, wnames: &name, clone: 1); |
680 | p9_fid_put(fid: dfid); |
681 | if (fid == ERR_PTR(error: -ENOENT)) |
682 | inode = NULL; |
683 | else if (IS_ERR(ptr: fid)) |
684 | inode = ERR_CAST(ptr: fid); |
685 | else |
686 | inode = v9fs_get_inode_from_fid(v9ses, fid, sb: dir->i_sb); |
687 | /* |
688 | * If we had a rename on the server and a parallel lookup |
689 | * for the new name, then make sure we instantiate with |
690 | * the new name. ie look up for a/b, while on server somebody |
691 | * moved b under k and client parallely did a lookup for |
692 | * k/b. |
693 | */ |
694 | res = d_splice_alias(inode, dentry); |
695 | if (!IS_ERR(ptr: fid)) { |
696 | if (!res) |
697 | v9fs_fid_add(dentry, fid: &fid); |
698 | else if (!IS_ERR(ptr: res)) |
699 | v9fs_fid_add(dentry: res, fid: &fid); |
700 | else |
701 | p9_fid_put(fid); |
702 | } |
703 | return res; |
704 | } |
705 | |
706 | static int |
707 | v9fs_vfs_atomic_open(struct inode *dir, struct dentry *dentry, |
708 | struct file *file, unsigned int flags, umode_t mode) |
709 | { |
710 | int err; |
711 | u32 perm; |
712 | struct v9fs_inode __maybe_unused *v9inode; |
713 | struct v9fs_session_info *v9ses; |
714 | struct p9_fid *fid; |
715 | struct dentry *res = NULL; |
716 | struct inode *inode; |
717 | int p9_omode; |
718 | |
719 | if (d_in_lookup(dentry)) { |
720 | res = v9fs_vfs_lookup(dir, dentry, flags: 0); |
721 | if (IS_ERR(ptr: res)) |
722 | return PTR_ERR(ptr: res); |
723 | |
724 | if (res) |
725 | dentry = res; |
726 | } |
727 | |
728 | /* Only creates */ |
729 | if (!(flags & O_CREAT) || d_really_is_positive(dentry)) |
730 | return finish_no_open(file, dentry: res); |
731 | |
732 | v9ses = v9fs_inode2v9ses(inode: dir); |
733 | perm = unixmode2p9mode(v9ses, mode); |
734 | p9_omode = v9fs_uflags2omode(uflags: flags, extended: v9fs_proto_dotu(v9ses)); |
735 | |
736 | if ((v9ses->cache & CACHE_WRITEBACK) && (p9_omode & P9_OWRITE)) { |
737 | p9_omode = (p9_omode & ~P9_OWRITE) | P9_ORDWR; |
738 | p9_debug(P9_DEBUG_CACHE, |
739 | "write-only file with writeback enabled, creating w/ O_RDWR\n" ); |
740 | } |
741 | fid = v9fs_create(v9ses, dir, dentry, NULL, perm, mode: p9_omode); |
742 | if (IS_ERR(ptr: fid)) { |
743 | err = PTR_ERR(ptr: fid); |
744 | goto error; |
745 | } |
746 | |
747 | v9fs_invalidate_inode_attr(inode: dir); |
748 | inode = d_inode(dentry); |
749 | v9inode = V9FS_I(inode); |
750 | err = finish_open(file, dentry, open: generic_file_open); |
751 | if (err) |
752 | goto error; |
753 | |
754 | file->private_data = fid; |
755 | #ifdef CONFIG_9P_FSCACHE |
756 | if (v9ses->cache & CACHE_FSCACHE) |
757 | fscache_use_cookie(cookie: v9fs_inode_cookie(v9inode), |
758 | will_modify: file->f_mode & FMODE_WRITE); |
759 | #endif |
760 | |
761 | v9fs_fid_add_modes(fid, s_flags: v9ses->flags, s_cache: v9ses->cache, f_flags: file->f_flags); |
762 | v9fs_open_fid_add(inode, fid: &fid); |
763 | |
764 | file->f_mode |= FMODE_CREATED; |
765 | out: |
766 | dput(res); |
767 | return err; |
768 | |
769 | error: |
770 | p9_fid_put(fid); |
771 | goto out; |
772 | } |
773 | |
774 | /** |
775 | * v9fs_vfs_unlink - VFS unlink hook to delete an inode |
776 | * @i: inode that is being unlinked |
777 | * @d: dentry that is being unlinked |
778 | * |
779 | */ |
780 | |
781 | int v9fs_vfs_unlink(struct inode *i, struct dentry *d) |
782 | { |
783 | return v9fs_remove(dir: i, dentry: d, flags: 0); |
784 | } |
785 | |
786 | /** |
787 | * v9fs_vfs_rmdir - VFS unlink hook to delete a directory |
788 | * @i: inode that is being unlinked |
789 | * @d: dentry that is being unlinked |
790 | * |
791 | */ |
792 | |
793 | int v9fs_vfs_rmdir(struct inode *i, struct dentry *d) |
794 | { |
795 | return v9fs_remove(dir: i, dentry: d, AT_REMOVEDIR); |
796 | } |
797 | |
798 | /** |
799 | * v9fs_vfs_rename - VFS hook to rename an inode |
800 | * @idmap: The idmap of the mount |
801 | * @old_dir: old dir inode |
802 | * @old_dentry: old dentry |
803 | * @new_dir: new dir inode |
804 | * @new_dentry: new dentry |
805 | * @flags: RENAME_* flags |
806 | * |
807 | */ |
808 | |
809 | int |
810 | v9fs_vfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, |
811 | struct dentry *old_dentry, struct inode *new_dir, |
812 | struct dentry *new_dentry, unsigned int flags) |
813 | { |
814 | int retval; |
815 | struct inode *old_inode; |
816 | struct inode *new_inode; |
817 | struct v9fs_session_info *v9ses; |
818 | struct p9_fid *oldfid = NULL, *dfid = NULL; |
819 | struct p9_fid *olddirfid = NULL; |
820 | struct p9_fid *newdirfid = NULL; |
821 | struct p9_wstat wstat; |
822 | |
823 | if (flags) |
824 | return -EINVAL; |
825 | |
826 | p9_debug(P9_DEBUG_VFS, "\n" ); |
827 | old_inode = d_inode(dentry: old_dentry); |
828 | new_inode = d_inode(dentry: new_dentry); |
829 | v9ses = v9fs_inode2v9ses(inode: old_inode); |
830 | oldfid = v9fs_fid_lookup(dentry: old_dentry); |
831 | if (IS_ERR(ptr: oldfid)) |
832 | return PTR_ERR(ptr: oldfid); |
833 | |
834 | dfid = v9fs_parent_fid(dentry: old_dentry); |
835 | olddirfid = clone_fid(fid: dfid); |
836 | p9_fid_put(fid: dfid); |
837 | dfid = NULL; |
838 | |
839 | if (IS_ERR(ptr: olddirfid)) { |
840 | retval = PTR_ERR(ptr: olddirfid); |
841 | goto error; |
842 | } |
843 | |
844 | dfid = v9fs_parent_fid(dentry: new_dentry); |
845 | newdirfid = clone_fid(fid: dfid); |
846 | p9_fid_put(fid: dfid); |
847 | dfid = NULL; |
848 | |
849 | if (IS_ERR(ptr: newdirfid)) { |
850 | retval = PTR_ERR(ptr: newdirfid); |
851 | goto error; |
852 | } |
853 | |
854 | down_write(sem: &v9ses->rename_sem); |
855 | if (v9fs_proto_dotl(v9ses)) { |
856 | retval = p9_client_renameat(olddirfid, old_name: old_dentry->d_name.name, |
857 | newdirfid, new_name: new_dentry->d_name.name); |
858 | if (retval == -EOPNOTSUPP) |
859 | retval = p9_client_rename(fid: oldfid, newdirfid, |
860 | name: new_dentry->d_name.name); |
861 | if (retval != -EOPNOTSUPP) |
862 | goto error_locked; |
863 | } |
864 | if (old_dentry->d_parent != new_dentry->d_parent) { |
865 | /* |
866 | * 9P .u can only handle file rename in the same directory |
867 | */ |
868 | |
869 | p9_debug(P9_DEBUG_ERROR, "old dir and new dir are different\n" ); |
870 | retval = -EXDEV; |
871 | goto error_locked; |
872 | } |
873 | v9fs_blank_wstat(wstat: &wstat); |
874 | wstat.muid = v9ses->uname; |
875 | wstat.name = new_dentry->d_name.name; |
876 | retval = p9_client_wstat(fid: oldfid, wst: &wstat); |
877 | |
878 | error_locked: |
879 | if (!retval) { |
880 | if (new_inode) { |
881 | if (S_ISDIR(new_inode->i_mode)) |
882 | clear_nlink(inode: new_inode); |
883 | else |
884 | v9fs_dec_count(inode: new_inode); |
885 | } |
886 | if (S_ISDIR(old_inode->i_mode)) { |
887 | if (!new_inode) |
888 | inc_nlink(inode: new_dir); |
889 | v9fs_dec_count(inode: old_dir); |
890 | } |
891 | v9fs_invalidate_inode_attr(inode: old_inode); |
892 | v9fs_invalidate_inode_attr(inode: old_dir); |
893 | v9fs_invalidate_inode_attr(inode: new_dir); |
894 | |
895 | /* successful rename */ |
896 | d_move(old_dentry, new_dentry); |
897 | } |
898 | up_write(sem: &v9ses->rename_sem); |
899 | |
900 | error: |
901 | p9_fid_put(fid: newdirfid); |
902 | p9_fid_put(fid: olddirfid); |
903 | p9_fid_put(fid: oldfid); |
904 | return retval; |
905 | } |
906 | |
907 | /** |
908 | * v9fs_vfs_getattr - retrieve file metadata |
909 | * @idmap: idmap of the mount |
910 | * @path: Object to query |
911 | * @stat: metadata structure to populate |
912 | * @request_mask: Mask of STATX_xxx flags indicating the caller's interests |
913 | * @flags: AT_STATX_xxx setting |
914 | * |
915 | */ |
916 | |
917 | static int |
918 | v9fs_vfs_getattr(struct mnt_idmap *idmap, const struct path *path, |
919 | struct kstat *stat, u32 request_mask, unsigned int flags) |
920 | { |
921 | struct dentry *dentry = path->dentry; |
922 | struct inode *inode = d_inode(dentry); |
923 | struct v9fs_session_info *v9ses; |
924 | struct p9_fid *fid; |
925 | struct p9_wstat *st; |
926 | |
927 | p9_debug(P9_DEBUG_VFS, "dentry: %p\n" , dentry); |
928 | v9ses = v9fs_dentry2v9ses(dentry); |
929 | if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) { |
930 | generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat); |
931 | return 0; |
932 | } else if (v9ses->cache & CACHE_WRITEBACK) { |
933 | if (S_ISREG(inode->i_mode)) { |
934 | int retval = filemap_fdatawrite(inode->i_mapping); |
935 | |
936 | if (retval) |
937 | p9_debug(P9_DEBUG_ERROR, |
938 | "flushing writeback during getattr returned %d\n" , retval); |
939 | } |
940 | } |
941 | fid = v9fs_fid_lookup(dentry); |
942 | if (IS_ERR(ptr: fid)) |
943 | return PTR_ERR(ptr: fid); |
944 | |
945 | st = p9_client_stat(fid); |
946 | p9_fid_put(fid); |
947 | if (IS_ERR(ptr: st)) |
948 | return PTR_ERR(ptr: st); |
949 | |
950 | v9fs_stat2inode(stat: st, inode: d_inode(dentry), sb: dentry->d_sb, flags: 0); |
951 | generic_fillattr(&nop_mnt_idmap, request_mask, d_inode(dentry), stat); |
952 | |
953 | p9stat_free(stbuf: st); |
954 | kfree(objp: st); |
955 | return 0; |
956 | } |
957 | |
958 | /** |
959 | * v9fs_vfs_setattr - set file metadata |
960 | * @idmap: idmap of the mount |
961 | * @dentry: file whose metadata to set |
962 | * @iattr: metadata assignment structure |
963 | * |
964 | */ |
965 | |
966 | static int v9fs_vfs_setattr(struct mnt_idmap *idmap, |
967 | struct dentry *dentry, struct iattr *iattr) |
968 | { |
969 | int retval, use_dentry = 0; |
970 | struct inode *inode = d_inode(dentry); |
971 | struct v9fs_session_info *v9ses; |
972 | struct p9_fid *fid = NULL; |
973 | struct p9_wstat wstat; |
974 | |
975 | p9_debug(P9_DEBUG_VFS, "\n" ); |
976 | retval = setattr_prepare(&nop_mnt_idmap, dentry, iattr); |
977 | if (retval) |
978 | return retval; |
979 | |
980 | v9ses = v9fs_dentry2v9ses(dentry); |
981 | if (iattr->ia_valid & ATTR_FILE) { |
982 | fid = iattr->ia_file->private_data; |
983 | WARN_ON(!fid); |
984 | } |
985 | if (!fid) { |
986 | fid = v9fs_fid_lookup(dentry); |
987 | use_dentry = 1; |
988 | } |
989 | if (IS_ERR(ptr: fid)) |
990 | return PTR_ERR(ptr: fid); |
991 | |
992 | v9fs_blank_wstat(wstat: &wstat); |
993 | if (iattr->ia_valid & ATTR_MODE) |
994 | wstat.mode = unixmode2p9mode(v9ses, mode: iattr->ia_mode); |
995 | |
996 | if (iattr->ia_valid & ATTR_MTIME) |
997 | wstat.mtime = iattr->ia_mtime.tv_sec; |
998 | |
999 | if (iattr->ia_valid & ATTR_ATIME) |
1000 | wstat.atime = iattr->ia_atime.tv_sec; |
1001 | |
1002 | if (iattr->ia_valid & ATTR_SIZE) |
1003 | wstat.length = iattr->ia_size; |
1004 | |
1005 | if (v9fs_proto_dotu(v9ses)) { |
1006 | if (iattr->ia_valid & ATTR_UID) |
1007 | wstat.n_uid = iattr->ia_uid; |
1008 | |
1009 | if (iattr->ia_valid & ATTR_GID) |
1010 | wstat.n_gid = iattr->ia_gid; |
1011 | } |
1012 | |
1013 | /* Write all dirty data */ |
1014 | if (d_is_reg(dentry)) { |
1015 | retval = filemap_fdatawrite(inode->i_mapping); |
1016 | if (retval) |
1017 | p9_debug(P9_DEBUG_ERROR, |
1018 | "flushing writeback during setattr returned %d\n" , retval); |
1019 | } |
1020 | |
1021 | retval = p9_client_wstat(fid, wst: &wstat); |
1022 | |
1023 | if (use_dentry) |
1024 | p9_fid_put(fid); |
1025 | |
1026 | if (retval < 0) |
1027 | return retval; |
1028 | |
1029 | if ((iattr->ia_valid & ATTR_SIZE) && |
1030 | iattr->ia_size != i_size_read(inode)) { |
1031 | truncate_setsize(inode, newsize: iattr->ia_size); |
1032 | netfs_resize_file(ctx: netfs_inode(inode), new_i_size: iattr->ia_size, changed_on_server: true); |
1033 | |
1034 | #ifdef CONFIG_9P_FSCACHE |
1035 | if (v9ses->cache & CACHE_FSCACHE) { |
1036 | struct v9fs_inode *v9inode = V9FS_I(inode); |
1037 | |
1038 | fscache_resize_cookie(cookie: v9fs_inode_cookie(v9inode), new_size: iattr->ia_size); |
1039 | } |
1040 | #endif |
1041 | } |
1042 | |
1043 | v9fs_invalidate_inode_attr(inode); |
1044 | |
1045 | setattr_copy(&nop_mnt_idmap, inode, attr: iattr); |
1046 | mark_inode_dirty(inode); |
1047 | return 0; |
1048 | } |
1049 | |
1050 | /** |
1051 | * v9fs_stat2inode - populate an inode structure with mistat info |
1052 | * @stat: Plan 9 metadata (mistat) structure |
1053 | * @inode: inode to populate |
1054 | * @sb: superblock of filesystem |
1055 | * @flags: control flags (e.g. V9FS_STAT2INODE_KEEP_ISIZE) |
1056 | * |
1057 | */ |
1058 | |
1059 | void |
1060 | v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode, |
1061 | struct super_block *sb, unsigned int flags) |
1062 | { |
1063 | umode_t mode; |
1064 | struct v9fs_session_info *v9ses = sb->s_fs_info; |
1065 | struct v9fs_inode *v9inode = V9FS_I(inode); |
1066 | |
1067 | inode_set_atime(inode, sec: stat->atime, nsec: 0); |
1068 | inode_set_mtime(inode, sec: stat->mtime, nsec: 0); |
1069 | inode_set_ctime(inode, sec: stat->mtime, nsec: 0); |
1070 | |
1071 | inode->i_uid = v9ses->dfltuid; |
1072 | inode->i_gid = v9ses->dfltgid; |
1073 | |
1074 | if (v9fs_proto_dotu(v9ses)) { |
1075 | inode->i_uid = stat->n_uid; |
1076 | inode->i_gid = stat->n_gid; |
1077 | } |
1078 | if ((S_ISREG(inode->i_mode)) || (S_ISDIR(inode->i_mode))) { |
1079 | if (v9fs_proto_dotu(v9ses)) { |
1080 | unsigned int i_nlink; |
1081 | /* |
1082 | * Hadlink support got added later to the .u extension. |
1083 | * So there can be a server out there that doesn't |
1084 | * support this even with .u extension. That would |
1085 | * just leave us with stat->extension being an empty |
1086 | * string, though. |
1087 | */ |
1088 | /* HARDLINKCOUNT %u */ |
1089 | if (sscanf(stat->extension, |
1090 | " HARDLINKCOUNT %u" , &i_nlink) == 1) |
1091 | set_nlink(inode, nlink: i_nlink); |
1092 | } |
1093 | } |
1094 | mode = p9mode2perm(v9ses, stat); |
1095 | mode |= inode->i_mode & ~S_IALLUGO; |
1096 | inode->i_mode = mode; |
1097 | |
1098 | v9inode->netfs.remote_i_size = stat->length; |
1099 | if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE)) |
1100 | v9fs_i_size_write(inode, i_size: stat->length); |
1101 | /* not real number of blocks, but 512 byte ones ... */ |
1102 | inode->i_blocks = (stat->length + 512 - 1) >> 9; |
1103 | v9inode->cache_validity &= ~V9FS_INO_INVALID_ATTR; |
1104 | } |
1105 | |
1106 | /** |
1107 | * v9fs_vfs_get_link - follow a symlink path |
1108 | * @dentry: dentry for symlink |
1109 | * @inode: inode for symlink |
1110 | * @done: delayed call for when we are done with the return value |
1111 | */ |
1112 | |
1113 | static const char *v9fs_vfs_get_link(struct dentry *dentry, |
1114 | struct inode *inode, |
1115 | struct delayed_call *done) |
1116 | { |
1117 | struct v9fs_session_info *v9ses; |
1118 | struct p9_fid *fid; |
1119 | struct p9_wstat *st; |
1120 | char *res; |
1121 | |
1122 | if (!dentry) |
1123 | return ERR_PTR(error: -ECHILD); |
1124 | |
1125 | v9ses = v9fs_dentry2v9ses(dentry); |
1126 | if (!v9fs_proto_dotu(v9ses)) |
1127 | return ERR_PTR(error: -EBADF); |
1128 | |
1129 | p9_debug(P9_DEBUG_VFS, "%pd\n" , dentry); |
1130 | fid = v9fs_fid_lookup(dentry); |
1131 | |
1132 | if (IS_ERR(ptr: fid)) |
1133 | return ERR_CAST(ptr: fid); |
1134 | |
1135 | st = p9_client_stat(fid); |
1136 | p9_fid_put(fid); |
1137 | if (IS_ERR(ptr: st)) |
1138 | return ERR_CAST(ptr: st); |
1139 | |
1140 | if (!(st->mode & P9_DMSYMLINK)) { |
1141 | p9stat_free(stbuf: st); |
1142 | kfree(objp: st); |
1143 | return ERR_PTR(error: -EINVAL); |
1144 | } |
1145 | res = st->extension; |
1146 | st->extension = NULL; |
1147 | if (strlen(res) >= PATH_MAX) |
1148 | res[PATH_MAX - 1] = '\0'; |
1149 | |
1150 | p9stat_free(stbuf: st); |
1151 | kfree(objp: st); |
1152 | set_delayed_call(call: done, fn: kfree_link, arg: res); |
1153 | return res; |
1154 | } |
1155 | |
1156 | /** |
1157 | * v9fs_vfs_mkspecial - create a special file |
1158 | * @dir: inode to create special file in |
1159 | * @dentry: dentry to create |
1160 | * @perm: mode to create special file |
1161 | * @extension: 9p2000.u format extension string representing special file |
1162 | * |
1163 | */ |
1164 | |
1165 | static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry, |
1166 | u32 perm, const char *extension) |
1167 | { |
1168 | struct p9_fid *fid; |
1169 | struct v9fs_session_info *v9ses; |
1170 | |
1171 | v9ses = v9fs_inode2v9ses(inode: dir); |
1172 | if (!v9fs_proto_dotu(v9ses)) { |
1173 | p9_debug(P9_DEBUG_ERROR, "not extended\n" ); |
1174 | return -EPERM; |
1175 | } |
1176 | |
1177 | fid = v9fs_create(v9ses, dir, dentry, extension: (char *) extension, perm, |
1178 | mode: P9_OREAD); |
1179 | if (IS_ERR(ptr: fid)) |
1180 | return PTR_ERR(ptr: fid); |
1181 | |
1182 | v9fs_invalidate_inode_attr(inode: dir); |
1183 | p9_fid_put(fid); |
1184 | return 0; |
1185 | } |
1186 | |
1187 | /** |
1188 | * v9fs_vfs_symlink - helper function to create symlinks |
1189 | * @idmap: idmap of the mount |
1190 | * @dir: directory inode containing symlink |
1191 | * @dentry: dentry for symlink |
1192 | * @symname: symlink data |
1193 | * |
1194 | * See Also: 9P2000.u RFC for more information |
1195 | * |
1196 | */ |
1197 | |
1198 | static int |
1199 | v9fs_vfs_symlink(struct mnt_idmap *idmap, struct inode *dir, |
1200 | struct dentry *dentry, const char *symname) |
1201 | { |
1202 | p9_debug(P9_DEBUG_VFS, " %lu,%pd,%s\n" , |
1203 | dir->i_ino, dentry, symname); |
1204 | |
1205 | return v9fs_vfs_mkspecial(dir, dentry, perm: P9_DMSYMLINK, extension: symname); |
1206 | } |
1207 | |
1208 | #define U32_MAX_DIGITS 10 |
1209 | |
1210 | /** |
1211 | * v9fs_vfs_link - create a hardlink |
1212 | * @old_dentry: dentry for file to link to |
1213 | * @dir: inode destination for new link |
1214 | * @dentry: dentry for link |
1215 | * |
1216 | */ |
1217 | |
1218 | static int |
1219 | v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, |
1220 | struct dentry *dentry) |
1221 | { |
1222 | int retval; |
1223 | char name[1 + U32_MAX_DIGITS + 2]; /* sign + number + \n + \0 */ |
1224 | struct p9_fid *oldfid; |
1225 | |
1226 | p9_debug(P9_DEBUG_VFS, " %lu,%pd,%pd\n" , |
1227 | dir->i_ino, dentry, old_dentry); |
1228 | |
1229 | oldfid = v9fs_fid_clone(dentry: old_dentry); |
1230 | if (IS_ERR(ptr: oldfid)) |
1231 | return PTR_ERR(ptr: oldfid); |
1232 | |
1233 | sprintf(buf: name, fmt: "%d\n" , oldfid->fid); |
1234 | retval = v9fs_vfs_mkspecial(dir, dentry, perm: P9_DMLINK, extension: name); |
1235 | if (!retval) { |
1236 | v9fs_refresh_inode(fid: oldfid, inode: d_inode(dentry: old_dentry)); |
1237 | v9fs_invalidate_inode_attr(inode: dir); |
1238 | } |
1239 | p9_fid_put(fid: oldfid); |
1240 | return retval; |
1241 | } |
1242 | |
1243 | /** |
1244 | * v9fs_vfs_mknod - create a special file |
1245 | * @idmap: idmap of the mount |
1246 | * @dir: inode destination for new link |
1247 | * @dentry: dentry for file |
1248 | * @mode: mode for creation |
1249 | * @rdev: device associated with special file |
1250 | * |
1251 | */ |
1252 | |
1253 | static int |
1254 | v9fs_vfs_mknod(struct mnt_idmap *idmap, struct inode *dir, |
1255 | struct dentry *dentry, umode_t mode, dev_t rdev) |
1256 | { |
1257 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode: dir); |
1258 | int retval; |
1259 | char name[2 + U32_MAX_DIGITS + 1 + U32_MAX_DIGITS + 1]; |
1260 | u32 perm; |
1261 | |
1262 | p9_debug(P9_DEBUG_VFS, " %lu,%pd mode: %x MAJOR: %u MINOR: %u\n" , |
1263 | dir->i_ino, dentry, mode, |
1264 | MAJOR(rdev), MINOR(rdev)); |
1265 | |
1266 | /* build extension */ |
1267 | if (S_ISBLK(mode)) |
1268 | sprintf(buf: name, fmt: "b %u %u" , MAJOR(rdev), MINOR(rdev)); |
1269 | else if (S_ISCHR(mode)) |
1270 | sprintf(buf: name, fmt: "c %u %u" , MAJOR(rdev), MINOR(rdev)); |
1271 | else |
1272 | *name = 0; |
1273 | |
1274 | perm = unixmode2p9mode(v9ses, mode); |
1275 | retval = v9fs_vfs_mkspecial(dir, dentry, perm, extension: name); |
1276 | |
1277 | return retval; |
1278 | } |
1279 | |
1280 | int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode) |
1281 | { |
1282 | int umode; |
1283 | dev_t rdev; |
1284 | struct p9_wstat *st; |
1285 | struct v9fs_session_info *v9ses; |
1286 | unsigned int flags; |
1287 | |
1288 | v9ses = v9fs_inode2v9ses(inode); |
1289 | st = p9_client_stat(fid); |
1290 | if (IS_ERR(ptr: st)) |
1291 | return PTR_ERR(ptr: st); |
1292 | /* |
1293 | * Don't update inode if the file type is different |
1294 | */ |
1295 | umode = p9mode2unixmode(v9ses, stat: st, rdev: &rdev); |
1296 | if (inode_wrong_type(inode, mode: umode)) |
1297 | goto out; |
1298 | |
1299 | /* |
1300 | * We don't want to refresh inode->i_size, |
1301 | * because we may have cached data |
1302 | */ |
1303 | flags = (v9ses->cache & CACHE_LOOSE) ? |
1304 | V9FS_STAT2INODE_KEEP_ISIZE : 0; |
1305 | v9fs_stat2inode(stat: st, inode, sb: inode->i_sb, flags); |
1306 | out: |
1307 | p9stat_free(stbuf: st); |
1308 | kfree(objp: st); |
1309 | return 0; |
1310 | } |
1311 | |
1312 | static const struct inode_operations v9fs_dir_inode_operations_dotu = { |
1313 | .create = v9fs_vfs_create, |
1314 | .lookup = v9fs_vfs_lookup, |
1315 | .atomic_open = v9fs_vfs_atomic_open, |
1316 | .symlink = v9fs_vfs_symlink, |
1317 | .link = v9fs_vfs_link, |
1318 | .unlink = v9fs_vfs_unlink, |
1319 | .mkdir = v9fs_vfs_mkdir, |
1320 | .rmdir = v9fs_vfs_rmdir, |
1321 | .mknod = v9fs_vfs_mknod, |
1322 | .rename = v9fs_vfs_rename, |
1323 | .getattr = v9fs_vfs_getattr, |
1324 | .setattr = v9fs_vfs_setattr, |
1325 | }; |
1326 | |
1327 | static const struct inode_operations v9fs_dir_inode_operations = { |
1328 | .create = v9fs_vfs_create, |
1329 | .lookup = v9fs_vfs_lookup, |
1330 | .atomic_open = v9fs_vfs_atomic_open, |
1331 | .unlink = v9fs_vfs_unlink, |
1332 | .mkdir = v9fs_vfs_mkdir, |
1333 | .rmdir = v9fs_vfs_rmdir, |
1334 | .mknod = v9fs_vfs_mknod, |
1335 | .rename = v9fs_vfs_rename, |
1336 | .getattr = v9fs_vfs_getattr, |
1337 | .setattr = v9fs_vfs_setattr, |
1338 | }; |
1339 | |
1340 | static const struct inode_operations v9fs_file_inode_operations = { |
1341 | .getattr = v9fs_vfs_getattr, |
1342 | .setattr = v9fs_vfs_setattr, |
1343 | }; |
1344 | |
1345 | static const struct inode_operations v9fs_symlink_inode_operations = { |
1346 | .get_link = v9fs_vfs_get_link, |
1347 | .getattr = v9fs_vfs_getattr, |
1348 | .setattr = v9fs_vfs_setattr, |
1349 | }; |
1350 | |
1351 | |