1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * This file contains vfs inode ops for the 9P2000.L 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 | #include <linux/module.h> |
10 | #include <linux/errno.h> |
11 | #include <linux/fs.h> |
12 | #include <linux/file.h> |
13 | #include <linux/pagemap.h> |
14 | #include <linux/stat.h> |
15 | #include <linux/string.h> |
16 | #include <linux/namei.h> |
17 | #include <linux/sched.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/xattr.h> |
20 | #include <linux/posix_acl.h> |
21 | #include <net/9p/9p.h> |
22 | #include <net/9p/client.h> |
23 | |
24 | #include "v9fs.h" |
25 | #include "v9fs_vfs.h" |
26 | #include "fid.h" |
27 | #include "cache.h" |
28 | #include "xattr.h" |
29 | #include "acl.h" |
30 | |
31 | static int |
32 | v9fs_vfs_mknod_dotl(struct mnt_idmap *idmap, struct inode *dir, |
33 | struct dentry *dentry, umode_t omode, dev_t rdev); |
34 | |
35 | /** |
36 | * v9fs_get_fsgid_for_create - Helper function to get the gid for a new object |
37 | * @dir_inode: The directory inode |
38 | * |
39 | * Helper function to get the gid for creating a |
40 | * new file system object. This checks the S_ISGID to determine the owning |
41 | * group of the new file system object. |
42 | */ |
43 | |
44 | static kgid_t v9fs_get_fsgid_for_create(struct inode *dir_inode) |
45 | { |
46 | BUG_ON(dir_inode == NULL); |
47 | |
48 | if (dir_inode->i_mode & S_ISGID) { |
49 | /* set_gid bit is set.*/ |
50 | return dir_inode->i_gid; |
51 | } |
52 | return current_fsgid(); |
53 | } |
54 | |
55 | static int v9fs_test_inode_dotl(struct inode *inode, void *data) |
56 | { |
57 | struct v9fs_inode *v9inode = V9FS_I(inode); |
58 | struct p9_stat_dotl *st = (struct p9_stat_dotl *)data; |
59 | |
60 | /* don't match inode of different type */ |
61 | if (inode_wrong_type(inode, mode: st->st_mode)) |
62 | return 0; |
63 | |
64 | if (inode->i_generation != st->st_gen) |
65 | return 0; |
66 | |
67 | /* compare qid details */ |
68 | if (memcmp(p: &v9inode->qid.version, |
69 | q: &st->qid.version, size: sizeof(v9inode->qid.version))) |
70 | return 0; |
71 | |
72 | if (v9inode->qid.type != st->qid.type) |
73 | return 0; |
74 | |
75 | if (v9inode->qid.path != st->qid.path) |
76 | return 0; |
77 | return 1; |
78 | } |
79 | |
80 | /* Always get a new inode */ |
81 | static int v9fs_test_new_inode_dotl(struct inode *inode, void *data) |
82 | { |
83 | return 0; |
84 | } |
85 | |
86 | static int v9fs_set_inode_dotl(struct inode *inode, void *data) |
87 | { |
88 | struct v9fs_inode *v9inode = V9FS_I(inode); |
89 | struct p9_stat_dotl *st = (struct p9_stat_dotl *)data; |
90 | |
91 | memcpy(&v9inode->qid, &st->qid, sizeof(st->qid)); |
92 | inode->i_generation = st->st_gen; |
93 | return 0; |
94 | } |
95 | |
96 | static struct inode *v9fs_qid_iget_dotl(struct super_block *sb, |
97 | struct p9_qid *qid, |
98 | struct p9_fid *fid, |
99 | struct p9_stat_dotl *st, |
100 | int new) |
101 | { |
102 | int retval; |
103 | unsigned long i_ino; |
104 | struct inode *inode; |
105 | struct v9fs_session_info *v9ses = sb->s_fs_info; |
106 | int (*test)(struct inode *inode, void *data); |
107 | |
108 | if (new) |
109 | test = v9fs_test_new_inode_dotl; |
110 | else |
111 | test = v9fs_test_inode_dotl; |
112 | |
113 | i_ino = v9fs_qid2ino(qid); |
114 | inode = iget5_locked(sb, i_ino, test, set: v9fs_set_inode_dotl, st); |
115 | if (!inode) |
116 | return ERR_PTR(error: -ENOMEM); |
117 | if (!(inode->i_state & I_NEW)) |
118 | return inode; |
119 | /* |
120 | * initialize the inode with the stat info |
121 | * FIXME!! we may need support for stale inodes |
122 | * later. |
123 | */ |
124 | inode->i_ino = i_ino; |
125 | retval = v9fs_init_inode(v9ses, inode, |
126 | mode: st->st_mode, rdev: new_decode_dev(dev: st->st_rdev)); |
127 | if (retval) |
128 | goto error; |
129 | |
130 | v9fs_stat2inode_dotl(stat: st, inode, flags: 0); |
131 | v9fs_cache_inode_get_cookie(inode); |
132 | retval = v9fs_get_acl(inode, fid); |
133 | if (retval) |
134 | goto error; |
135 | |
136 | unlock_new_inode(inode); |
137 | return inode; |
138 | error: |
139 | iget_failed(inode); |
140 | return ERR_PTR(error: retval); |
141 | |
142 | } |
143 | |
144 | struct inode * |
145 | v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses, struct p9_fid *fid, |
146 | struct super_block *sb, int new) |
147 | { |
148 | struct p9_stat_dotl *st; |
149 | struct inode *inode = NULL; |
150 | |
151 | st = p9_client_getattr_dotl(fid, P9_STATS_BASIC | P9_STATS_GEN); |
152 | if (IS_ERR(ptr: st)) |
153 | return ERR_CAST(ptr: st); |
154 | |
155 | inode = v9fs_qid_iget_dotl(sb, qid: &st->qid, fid, st, new); |
156 | kfree(objp: st); |
157 | return inode; |
158 | } |
159 | |
160 | struct dotl_openflag_map { |
161 | int open_flag; |
162 | int dotl_flag; |
163 | }; |
164 | |
165 | static int v9fs_mapped_dotl_flags(int flags) |
166 | { |
167 | int i; |
168 | int rflags = 0; |
169 | struct dotl_openflag_map dotl_oflag_map[] = { |
170 | { O_CREAT, P9_DOTL_CREATE }, |
171 | { O_EXCL, P9_DOTL_EXCL }, |
172 | { O_NOCTTY, P9_DOTL_NOCTTY }, |
173 | { O_APPEND, P9_DOTL_APPEND }, |
174 | { O_NONBLOCK, P9_DOTL_NONBLOCK }, |
175 | { O_DSYNC, P9_DOTL_DSYNC }, |
176 | { FASYNC, P9_DOTL_FASYNC }, |
177 | { O_DIRECT, P9_DOTL_DIRECT }, |
178 | { O_LARGEFILE, P9_DOTL_LARGEFILE }, |
179 | { O_DIRECTORY, P9_DOTL_DIRECTORY }, |
180 | { O_NOFOLLOW, P9_DOTL_NOFOLLOW }, |
181 | { O_NOATIME, P9_DOTL_NOATIME }, |
182 | { O_CLOEXEC, P9_DOTL_CLOEXEC }, |
183 | { O_SYNC, P9_DOTL_SYNC}, |
184 | }; |
185 | for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) { |
186 | if (flags & dotl_oflag_map[i].open_flag) |
187 | rflags |= dotl_oflag_map[i].dotl_flag; |
188 | } |
189 | return rflags; |
190 | } |
191 | |
192 | /** |
193 | * v9fs_open_to_dotl_flags- convert Linux specific open flags to |
194 | * plan 9 open flag. |
195 | * @flags: flags to convert |
196 | */ |
197 | int v9fs_open_to_dotl_flags(int flags) |
198 | { |
199 | int rflags = 0; |
200 | |
201 | /* |
202 | * We have same bits for P9_DOTL_READONLY, P9_DOTL_WRONLY |
203 | * and P9_DOTL_NOACCESS |
204 | */ |
205 | rflags |= flags & O_ACCMODE; |
206 | rflags |= v9fs_mapped_dotl_flags(flags); |
207 | |
208 | return rflags; |
209 | } |
210 | |
211 | /** |
212 | * v9fs_vfs_create_dotl - VFS hook to create files for 9P2000.L protocol. |
213 | * @idmap: The user namespace of the mount |
214 | * @dir: directory inode that is being created |
215 | * @dentry: dentry that is being deleted |
216 | * @omode: create permissions |
217 | * @excl: True if the file must not yet exist |
218 | * |
219 | */ |
220 | static int |
221 | v9fs_vfs_create_dotl(struct mnt_idmap *idmap, struct inode *dir, |
222 | struct dentry *dentry, umode_t omode, bool excl) |
223 | { |
224 | return v9fs_vfs_mknod_dotl(idmap, dir, dentry, omode, rdev: 0); |
225 | } |
226 | |
227 | static int |
228 | v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry, |
229 | struct file *file, unsigned int flags, umode_t omode) |
230 | { |
231 | int err = 0; |
232 | kgid_t gid; |
233 | umode_t mode; |
234 | int p9_omode = v9fs_open_to_dotl_flags(flags); |
235 | const unsigned char *name = NULL; |
236 | struct p9_qid qid; |
237 | struct inode *inode; |
238 | struct p9_fid *fid = NULL; |
239 | struct p9_fid *dfid = NULL, *ofid = NULL; |
240 | struct v9fs_session_info *v9ses; |
241 | struct posix_acl *pacl = NULL, *dacl = NULL; |
242 | struct dentry *res = NULL; |
243 | |
244 | if (d_in_lookup(dentry)) { |
245 | res = v9fs_vfs_lookup(dir, dentry, flags: 0); |
246 | if (IS_ERR(ptr: res)) |
247 | return PTR_ERR(ptr: res); |
248 | |
249 | if (res) |
250 | dentry = res; |
251 | } |
252 | |
253 | /* Only creates */ |
254 | if (!(flags & O_CREAT) || d_really_is_positive(dentry)) |
255 | return finish_no_open(file, dentry: res); |
256 | |
257 | v9ses = v9fs_inode2v9ses(inode: dir); |
258 | |
259 | name = dentry->d_name.name; |
260 | p9_debug(P9_DEBUG_VFS, "name:%s flags:0x%x mode:0x%x\n" , |
261 | name, flags, omode); |
262 | |
263 | dfid = v9fs_parent_fid(dentry); |
264 | if (IS_ERR(ptr: dfid)) { |
265 | err = PTR_ERR(ptr: dfid); |
266 | p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n" , err); |
267 | goto out; |
268 | } |
269 | |
270 | /* clone a fid to use for creation */ |
271 | ofid = clone_fid(fid: dfid); |
272 | if (IS_ERR(ptr: ofid)) { |
273 | err = PTR_ERR(ptr: ofid); |
274 | p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n" , err); |
275 | goto out; |
276 | } |
277 | |
278 | gid = v9fs_get_fsgid_for_create(dir_inode: dir); |
279 | |
280 | mode = omode; |
281 | /* Update mode based on ACL value */ |
282 | err = v9fs_acl_mode(dir, modep: &mode, dpacl: &dacl, pacl: &pacl); |
283 | if (err) { |
284 | p9_debug(P9_DEBUG_VFS, "Failed to get acl values in create %d\n" , |
285 | err); |
286 | goto out; |
287 | } |
288 | |
289 | if ((v9ses->cache & CACHE_WRITEBACK) && (p9_omode & P9_OWRITE)) { |
290 | p9_omode = (p9_omode & ~P9_OWRITE) | P9_ORDWR; |
291 | p9_debug(P9_DEBUG_CACHE, |
292 | "write-only file with writeback enabled, creating w/ O_RDWR\n" ); |
293 | } |
294 | err = p9_client_create_dotl(ofid, name, flags: p9_omode, mode, gid, qid: &qid); |
295 | if (err < 0) { |
296 | p9_debug(P9_DEBUG_VFS, "p9_client_open_dotl failed in create %d\n" , |
297 | err); |
298 | goto out; |
299 | } |
300 | v9fs_invalidate_inode_attr(inode: dir); |
301 | |
302 | /* instantiate inode and assign the unopened fid to the dentry */ |
303 | fid = p9_client_walk(oldfid: dfid, nwname: 1, wnames: &name, clone: 1); |
304 | if (IS_ERR(ptr: fid)) { |
305 | err = PTR_ERR(ptr: fid); |
306 | p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n" , err); |
307 | goto out; |
308 | } |
309 | inode = v9fs_get_new_inode_from_fid(v9ses, fid, sb: dir->i_sb); |
310 | if (IS_ERR(ptr: inode)) { |
311 | err = PTR_ERR(ptr: inode); |
312 | p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n" , err); |
313 | goto out; |
314 | } |
315 | /* Now set the ACL based on the default value */ |
316 | v9fs_set_create_acl(inode, fid, dacl, acl: pacl); |
317 | |
318 | v9fs_fid_add(dentry, fid: &fid); |
319 | d_instantiate(dentry, inode); |
320 | |
321 | /* Since we are opening a file, assign the open fid to the file */ |
322 | err = finish_open(file, dentry, open: generic_file_open); |
323 | if (err) |
324 | goto out; |
325 | file->private_data = ofid; |
326 | #ifdef CONFIG_9P_FSCACHE |
327 | if (v9ses->cache & CACHE_FSCACHE) { |
328 | struct v9fs_inode *v9inode = V9FS_I(inode); |
329 | fscache_use_cookie(cookie: v9fs_inode_cookie(v9inode), |
330 | will_modify: file->f_mode & FMODE_WRITE); |
331 | } |
332 | #endif |
333 | v9fs_fid_add_modes(fid: ofid, s_flags: v9ses->flags, s_cache: v9ses->cache, f_flags: flags); |
334 | v9fs_open_fid_add(inode, fid: &ofid); |
335 | file->f_mode |= FMODE_CREATED; |
336 | out: |
337 | p9_fid_put(fid: dfid); |
338 | p9_fid_put(fid: ofid); |
339 | p9_fid_put(fid); |
340 | v9fs_put_acl(dacl, acl: pacl); |
341 | dput(res); |
342 | return err; |
343 | } |
344 | |
345 | /** |
346 | * v9fs_vfs_mkdir_dotl - VFS mkdir hook to create a directory |
347 | * @idmap: The idmap of the mount |
348 | * @dir: inode that is being unlinked |
349 | * @dentry: dentry that is being unlinked |
350 | * @omode: mode for new directory |
351 | * |
352 | */ |
353 | |
354 | static int v9fs_vfs_mkdir_dotl(struct mnt_idmap *idmap, |
355 | struct inode *dir, struct dentry *dentry, |
356 | umode_t omode) |
357 | { |
358 | int err; |
359 | struct v9fs_session_info *v9ses; |
360 | struct p9_fid *fid = NULL, *dfid = NULL; |
361 | kgid_t gid; |
362 | const unsigned char *name; |
363 | umode_t mode; |
364 | struct inode *inode; |
365 | struct p9_qid qid; |
366 | struct posix_acl *dacl = NULL, *pacl = NULL; |
367 | |
368 | p9_debug(P9_DEBUG_VFS, "name %pd\n" , dentry); |
369 | v9ses = v9fs_inode2v9ses(inode: dir); |
370 | |
371 | omode |= S_IFDIR; |
372 | if (dir->i_mode & S_ISGID) |
373 | omode |= S_ISGID; |
374 | |
375 | dfid = v9fs_parent_fid(dentry); |
376 | if (IS_ERR(ptr: dfid)) { |
377 | err = PTR_ERR(ptr: dfid); |
378 | p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n" , err); |
379 | goto error; |
380 | } |
381 | |
382 | gid = v9fs_get_fsgid_for_create(dir_inode: dir); |
383 | mode = omode; |
384 | /* Update mode based on ACL value */ |
385 | err = v9fs_acl_mode(dir, modep: &mode, dpacl: &dacl, pacl: &pacl); |
386 | if (err) { |
387 | p9_debug(P9_DEBUG_VFS, "Failed to get acl values in mkdir %d\n" , |
388 | err); |
389 | goto error; |
390 | } |
391 | name = dentry->d_name.name; |
392 | err = p9_client_mkdir_dotl(fid: dfid, name, mode, gid, qid: &qid); |
393 | if (err < 0) |
394 | goto error; |
395 | fid = p9_client_walk(oldfid: dfid, nwname: 1, wnames: &name, clone: 1); |
396 | if (IS_ERR(ptr: fid)) { |
397 | err = PTR_ERR(ptr: fid); |
398 | p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n" , |
399 | err); |
400 | goto error; |
401 | } |
402 | |
403 | /* instantiate inode and assign the unopened fid to the dentry */ |
404 | if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) { |
405 | inode = v9fs_get_new_inode_from_fid(v9ses, fid, sb: dir->i_sb); |
406 | if (IS_ERR(ptr: inode)) { |
407 | err = PTR_ERR(ptr: inode); |
408 | p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n" , |
409 | err); |
410 | goto error; |
411 | } |
412 | v9fs_fid_add(dentry, fid: &fid); |
413 | v9fs_set_create_acl(inode, fid, dacl, acl: pacl); |
414 | d_instantiate(dentry, inode); |
415 | err = 0; |
416 | } else { |
417 | /* |
418 | * Not in cached mode. No need to populate |
419 | * inode with stat. We need to get an inode |
420 | * so that we can set the acl with dentry |
421 | */ |
422 | inode = v9fs_get_inode(sb: dir->i_sb, mode, rdev: 0); |
423 | if (IS_ERR(ptr: inode)) { |
424 | err = PTR_ERR(ptr: inode); |
425 | goto error; |
426 | } |
427 | v9fs_set_create_acl(inode, fid, dacl, acl: pacl); |
428 | d_instantiate(dentry, inode); |
429 | } |
430 | inc_nlink(inode: dir); |
431 | v9fs_invalidate_inode_attr(inode: dir); |
432 | error: |
433 | p9_fid_put(fid); |
434 | v9fs_put_acl(dacl, acl: pacl); |
435 | p9_fid_put(fid: dfid); |
436 | return err; |
437 | } |
438 | |
439 | static int |
440 | v9fs_vfs_getattr_dotl(struct mnt_idmap *idmap, |
441 | const struct path *path, struct kstat *stat, |
442 | u32 request_mask, unsigned int flags) |
443 | { |
444 | struct dentry *dentry = path->dentry; |
445 | struct v9fs_session_info *v9ses; |
446 | struct p9_fid *fid; |
447 | struct inode *inode = d_inode(dentry); |
448 | struct p9_stat_dotl *st; |
449 | |
450 | p9_debug(P9_DEBUG_VFS, "dentry: %p\n" , dentry); |
451 | v9ses = v9fs_dentry2v9ses(dentry); |
452 | if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) { |
453 | generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat); |
454 | return 0; |
455 | } else if (v9ses->cache) { |
456 | if (S_ISREG(inode->i_mode)) { |
457 | int retval = filemap_fdatawrite(inode->i_mapping); |
458 | |
459 | if (retval) |
460 | p9_debug(P9_DEBUG_ERROR, |
461 | "flushing writeback during getattr returned %d\n" , retval); |
462 | } |
463 | } |
464 | fid = v9fs_fid_lookup(dentry); |
465 | if (IS_ERR(ptr: fid)) |
466 | return PTR_ERR(ptr: fid); |
467 | |
468 | /* Ask for all the fields in stat structure. Server will return |
469 | * whatever it supports |
470 | */ |
471 | |
472 | st = p9_client_getattr_dotl(fid, P9_STATS_ALL); |
473 | p9_fid_put(fid); |
474 | if (IS_ERR(ptr: st)) |
475 | return PTR_ERR(ptr: st); |
476 | |
477 | v9fs_stat2inode_dotl(stat: st, inode: d_inode(dentry), flags: 0); |
478 | generic_fillattr(&nop_mnt_idmap, request_mask, d_inode(dentry), stat); |
479 | /* Change block size to what the server returned */ |
480 | stat->blksize = st->st_blksize; |
481 | |
482 | kfree(objp: st); |
483 | return 0; |
484 | } |
485 | |
486 | /* |
487 | * Attribute flags. |
488 | */ |
489 | #define P9_ATTR_MODE (1 << 0) |
490 | #define P9_ATTR_UID (1 << 1) |
491 | #define P9_ATTR_GID (1 << 2) |
492 | #define P9_ATTR_SIZE (1 << 3) |
493 | #define P9_ATTR_ATIME (1 << 4) |
494 | #define P9_ATTR_MTIME (1 << 5) |
495 | #define P9_ATTR_CTIME (1 << 6) |
496 | #define P9_ATTR_ATIME_SET (1 << 7) |
497 | #define P9_ATTR_MTIME_SET (1 << 8) |
498 | |
499 | struct dotl_iattr_map { |
500 | int iattr_valid; |
501 | int p9_iattr_valid; |
502 | }; |
503 | |
504 | static int v9fs_mapped_iattr_valid(int iattr_valid) |
505 | { |
506 | int i; |
507 | int p9_iattr_valid = 0; |
508 | struct dotl_iattr_map dotl_iattr_map[] = { |
509 | { ATTR_MODE, P9_ATTR_MODE }, |
510 | { ATTR_UID, P9_ATTR_UID }, |
511 | { ATTR_GID, P9_ATTR_GID }, |
512 | { ATTR_SIZE, P9_ATTR_SIZE }, |
513 | { ATTR_ATIME, P9_ATTR_ATIME }, |
514 | { ATTR_MTIME, P9_ATTR_MTIME }, |
515 | { ATTR_CTIME, P9_ATTR_CTIME }, |
516 | { ATTR_ATIME_SET, P9_ATTR_ATIME_SET }, |
517 | { ATTR_MTIME_SET, P9_ATTR_MTIME_SET }, |
518 | }; |
519 | for (i = 0; i < ARRAY_SIZE(dotl_iattr_map); i++) { |
520 | if (iattr_valid & dotl_iattr_map[i].iattr_valid) |
521 | p9_iattr_valid |= dotl_iattr_map[i].p9_iattr_valid; |
522 | } |
523 | return p9_iattr_valid; |
524 | } |
525 | |
526 | /** |
527 | * v9fs_vfs_setattr_dotl - set file metadata |
528 | * @idmap: idmap of the mount |
529 | * @dentry: file whose metadata to set |
530 | * @iattr: metadata assignment structure |
531 | * |
532 | */ |
533 | |
534 | int v9fs_vfs_setattr_dotl(struct mnt_idmap *idmap, |
535 | struct dentry *dentry, struct iattr *iattr) |
536 | { |
537 | int retval, use_dentry = 0; |
538 | struct inode *inode = d_inode(dentry); |
539 | struct v9fs_session_info __maybe_unused *v9ses; |
540 | struct p9_fid *fid = NULL; |
541 | struct p9_iattr_dotl p9attr = { |
542 | .uid = INVALID_UID, |
543 | .gid = INVALID_GID, |
544 | }; |
545 | |
546 | p9_debug(P9_DEBUG_VFS, "\n" ); |
547 | |
548 | retval = setattr_prepare(&nop_mnt_idmap, dentry, iattr); |
549 | if (retval) |
550 | return retval; |
551 | |
552 | v9ses = v9fs_dentry2v9ses(dentry); |
553 | |
554 | p9attr.valid = v9fs_mapped_iattr_valid(iattr_valid: iattr->ia_valid); |
555 | if (iattr->ia_valid & ATTR_MODE) |
556 | p9attr.mode = iattr->ia_mode; |
557 | if (iattr->ia_valid & ATTR_UID) |
558 | p9attr.uid = iattr->ia_uid; |
559 | if (iattr->ia_valid & ATTR_GID) |
560 | p9attr.gid = iattr->ia_gid; |
561 | if (iattr->ia_valid & ATTR_SIZE) |
562 | p9attr.size = iattr->ia_size; |
563 | if (iattr->ia_valid & ATTR_ATIME_SET) { |
564 | p9attr.atime_sec = iattr->ia_atime.tv_sec; |
565 | p9attr.atime_nsec = iattr->ia_atime.tv_nsec; |
566 | } |
567 | if (iattr->ia_valid & ATTR_MTIME_SET) { |
568 | p9attr.mtime_sec = iattr->ia_mtime.tv_sec; |
569 | p9attr.mtime_nsec = iattr->ia_mtime.tv_nsec; |
570 | } |
571 | |
572 | if (iattr->ia_valid & ATTR_FILE) { |
573 | fid = iattr->ia_file->private_data; |
574 | WARN_ON(!fid); |
575 | } |
576 | if (!fid) { |
577 | fid = v9fs_fid_lookup(dentry); |
578 | use_dentry = 1; |
579 | } |
580 | if (IS_ERR(ptr: fid)) |
581 | return PTR_ERR(ptr: fid); |
582 | |
583 | /* Write all dirty data */ |
584 | if (S_ISREG(inode->i_mode)) { |
585 | retval = filemap_fdatawrite(inode->i_mapping); |
586 | if (retval < 0) |
587 | p9_debug(P9_DEBUG_ERROR, |
588 | "Flushing file prior to setattr failed: %d\n" , retval); |
589 | } |
590 | |
591 | retval = p9_client_setattr(fid, attr: &p9attr); |
592 | if (retval < 0) { |
593 | if (use_dentry) |
594 | p9_fid_put(fid); |
595 | return retval; |
596 | } |
597 | |
598 | if ((iattr->ia_valid & ATTR_SIZE) && iattr->ia_size != |
599 | i_size_read(inode)) { |
600 | truncate_setsize(inode, newsize: iattr->ia_size); |
601 | truncate_pagecache(inode, new: iattr->ia_size); |
602 | |
603 | #ifdef CONFIG_9P_FSCACHE |
604 | if (v9ses->cache & CACHE_FSCACHE) |
605 | fscache_resize_cookie(cookie: v9fs_inode_cookie(v9inode: V9FS_I(inode)), |
606 | new_size: iattr->ia_size); |
607 | #endif |
608 | } |
609 | |
610 | v9fs_invalidate_inode_attr(inode); |
611 | setattr_copy(&nop_mnt_idmap, inode, attr: iattr); |
612 | mark_inode_dirty(inode); |
613 | if (iattr->ia_valid & ATTR_MODE) { |
614 | /* We also want to update ACL when we update mode bits */ |
615 | retval = v9fs_acl_chmod(inode, fid); |
616 | if (retval < 0) { |
617 | if (use_dentry) |
618 | p9_fid_put(fid); |
619 | return retval; |
620 | } |
621 | } |
622 | if (use_dentry) |
623 | p9_fid_put(fid); |
624 | |
625 | return 0; |
626 | } |
627 | |
628 | /** |
629 | * v9fs_stat2inode_dotl - populate an inode structure with stat info |
630 | * @stat: stat structure |
631 | * @inode: inode to populate |
632 | * @flags: ctrl flags (e.g. V9FS_STAT2INODE_KEEP_ISIZE) |
633 | * |
634 | */ |
635 | |
636 | void |
637 | v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode, |
638 | unsigned int flags) |
639 | { |
640 | umode_t mode; |
641 | struct v9fs_inode *v9inode = V9FS_I(inode); |
642 | |
643 | if ((stat->st_result_mask & P9_STATS_BASIC) == P9_STATS_BASIC) { |
644 | inode_set_atime(inode, sec: stat->st_atime_sec, |
645 | nsec: stat->st_atime_nsec); |
646 | inode_set_mtime(inode, sec: stat->st_mtime_sec, |
647 | nsec: stat->st_mtime_nsec); |
648 | inode_set_ctime(inode, sec: stat->st_ctime_sec, |
649 | nsec: stat->st_ctime_nsec); |
650 | inode->i_uid = stat->st_uid; |
651 | inode->i_gid = stat->st_gid; |
652 | set_nlink(inode, nlink: stat->st_nlink); |
653 | |
654 | mode = stat->st_mode & S_IALLUGO; |
655 | mode |= inode->i_mode & ~S_IALLUGO; |
656 | inode->i_mode = mode; |
657 | |
658 | if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE)) |
659 | v9fs_i_size_write(inode, i_size: stat->st_size); |
660 | inode->i_blocks = stat->st_blocks; |
661 | } else { |
662 | if (stat->st_result_mask & P9_STATS_ATIME) { |
663 | inode_set_atime(inode, sec: stat->st_atime_sec, |
664 | nsec: stat->st_atime_nsec); |
665 | } |
666 | if (stat->st_result_mask & P9_STATS_MTIME) { |
667 | inode_set_mtime(inode, sec: stat->st_mtime_sec, |
668 | nsec: stat->st_mtime_nsec); |
669 | } |
670 | if (stat->st_result_mask & P9_STATS_CTIME) { |
671 | inode_set_ctime(inode, sec: stat->st_ctime_sec, |
672 | nsec: stat->st_ctime_nsec); |
673 | } |
674 | if (stat->st_result_mask & P9_STATS_UID) |
675 | inode->i_uid = stat->st_uid; |
676 | if (stat->st_result_mask & P9_STATS_GID) |
677 | inode->i_gid = stat->st_gid; |
678 | if (stat->st_result_mask & P9_STATS_NLINK) |
679 | set_nlink(inode, nlink: stat->st_nlink); |
680 | if (stat->st_result_mask & P9_STATS_MODE) { |
681 | mode = stat->st_mode & S_IALLUGO; |
682 | mode |= inode->i_mode & ~S_IALLUGO; |
683 | inode->i_mode = mode; |
684 | } |
685 | if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE) && |
686 | stat->st_result_mask & P9_STATS_SIZE) |
687 | v9fs_i_size_write(inode, i_size: stat->st_size); |
688 | if (stat->st_result_mask & P9_STATS_BLOCKS) |
689 | inode->i_blocks = stat->st_blocks; |
690 | } |
691 | if (stat->st_result_mask & P9_STATS_GEN) |
692 | inode->i_generation = stat->st_gen; |
693 | |
694 | /* Currently we don't support P9_STATS_BTIME and P9_STATS_DATA_VERSION |
695 | * because the inode structure does not have fields for them. |
696 | */ |
697 | v9inode->cache_validity &= ~V9FS_INO_INVALID_ATTR; |
698 | } |
699 | |
700 | static int |
701 | v9fs_vfs_symlink_dotl(struct mnt_idmap *idmap, struct inode *dir, |
702 | struct dentry *dentry, const char *symname) |
703 | { |
704 | int err; |
705 | kgid_t gid; |
706 | const unsigned char *name; |
707 | struct p9_qid qid; |
708 | struct inode *inode; |
709 | struct p9_fid *dfid; |
710 | struct p9_fid *fid = NULL; |
711 | struct v9fs_session_info *v9ses; |
712 | |
713 | name = dentry->d_name.name; |
714 | p9_debug(P9_DEBUG_VFS, "%lu,%s,%s\n" , dir->i_ino, name, symname); |
715 | v9ses = v9fs_inode2v9ses(inode: dir); |
716 | |
717 | dfid = v9fs_parent_fid(dentry); |
718 | if (IS_ERR(ptr: dfid)) { |
719 | err = PTR_ERR(ptr: dfid); |
720 | p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n" , err); |
721 | return err; |
722 | } |
723 | |
724 | gid = v9fs_get_fsgid_for_create(dir_inode: dir); |
725 | |
726 | /* Server doesn't alter fid on TSYMLINK. Hence no need to clone it. */ |
727 | err = p9_client_symlink(fid: dfid, name, symname, gid, qid: &qid); |
728 | |
729 | if (err < 0) { |
730 | p9_debug(P9_DEBUG_VFS, "p9_client_symlink failed %d\n" , err); |
731 | goto error; |
732 | } |
733 | |
734 | v9fs_invalidate_inode_attr(inode: dir); |
735 | if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) { |
736 | /* Now walk from the parent so we can get an unopened fid. */ |
737 | fid = p9_client_walk(oldfid: dfid, nwname: 1, wnames: &name, clone: 1); |
738 | if (IS_ERR(ptr: fid)) { |
739 | err = PTR_ERR(ptr: fid); |
740 | p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n" , |
741 | err); |
742 | goto error; |
743 | } |
744 | |
745 | /* instantiate inode and assign the unopened fid to dentry */ |
746 | inode = v9fs_get_new_inode_from_fid(v9ses, fid, sb: dir->i_sb); |
747 | if (IS_ERR(ptr: inode)) { |
748 | err = PTR_ERR(ptr: inode); |
749 | p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n" , |
750 | err); |
751 | goto error; |
752 | } |
753 | v9fs_fid_add(dentry, fid: &fid); |
754 | d_instantiate(dentry, inode); |
755 | err = 0; |
756 | } else { |
757 | /* Not in cached mode. No need to populate inode with stat */ |
758 | inode = v9fs_get_inode(sb: dir->i_sb, S_IFLNK, rdev: 0); |
759 | if (IS_ERR(ptr: inode)) { |
760 | err = PTR_ERR(ptr: inode); |
761 | goto error; |
762 | } |
763 | d_instantiate(dentry, inode); |
764 | } |
765 | |
766 | error: |
767 | p9_fid_put(fid); |
768 | p9_fid_put(fid: dfid); |
769 | return err; |
770 | } |
771 | |
772 | /** |
773 | * v9fs_vfs_link_dotl - create a hardlink for dotl |
774 | * @old_dentry: dentry for file to link to |
775 | * @dir: inode destination for new link |
776 | * @dentry: dentry for link |
777 | * |
778 | */ |
779 | |
780 | static int |
781 | v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir, |
782 | struct dentry *dentry) |
783 | { |
784 | int err; |
785 | struct p9_fid *dfid, *oldfid; |
786 | struct v9fs_session_info *v9ses; |
787 | |
788 | p9_debug(P9_DEBUG_VFS, "dir ino: %lu, old_name: %pd, new_name: %pd\n" , |
789 | dir->i_ino, old_dentry, dentry); |
790 | |
791 | v9ses = v9fs_inode2v9ses(inode: dir); |
792 | dfid = v9fs_parent_fid(dentry); |
793 | if (IS_ERR(ptr: dfid)) |
794 | return PTR_ERR(ptr: dfid); |
795 | |
796 | oldfid = v9fs_fid_lookup(dentry: old_dentry); |
797 | if (IS_ERR(ptr: oldfid)) { |
798 | p9_fid_put(fid: dfid); |
799 | return PTR_ERR(ptr: oldfid); |
800 | } |
801 | |
802 | err = p9_client_link(fid: dfid, oldfid, newname: dentry->d_name.name); |
803 | |
804 | p9_fid_put(fid: dfid); |
805 | p9_fid_put(fid: oldfid); |
806 | if (err < 0) { |
807 | p9_debug(P9_DEBUG_VFS, "p9_client_link failed %d\n" , err); |
808 | return err; |
809 | } |
810 | |
811 | v9fs_invalidate_inode_attr(inode: dir); |
812 | if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) { |
813 | /* Get the latest stat info from server. */ |
814 | struct p9_fid *fid; |
815 | |
816 | fid = v9fs_fid_lookup(dentry: old_dentry); |
817 | if (IS_ERR(ptr: fid)) |
818 | return PTR_ERR(ptr: fid); |
819 | |
820 | v9fs_refresh_inode_dotl(fid, inode: d_inode(dentry: old_dentry)); |
821 | p9_fid_put(fid); |
822 | } |
823 | ihold(inode: d_inode(dentry: old_dentry)); |
824 | d_instantiate(dentry, d_inode(dentry: old_dentry)); |
825 | |
826 | return err; |
827 | } |
828 | |
829 | /** |
830 | * v9fs_vfs_mknod_dotl - create a special file |
831 | * @idmap: The idmap of the mount |
832 | * @dir: inode destination for new link |
833 | * @dentry: dentry for file |
834 | * @omode: mode for creation |
835 | * @rdev: device associated with special file |
836 | * |
837 | */ |
838 | static int |
839 | v9fs_vfs_mknod_dotl(struct mnt_idmap *idmap, struct inode *dir, |
840 | struct dentry *dentry, umode_t omode, dev_t rdev) |
841 | { |
842 | int err; |
843 | kgid_t gid; |
844 | const unsigned char *name; |
845 | umode_t mode; |
846 | struct v9fs_session_info *v9ses; |
847 | struct p9_fid *fid = NULL, *dfid = NULL; |
848 | struct inode *inode; |
849 | struct p9_qid qid; |
850 | struct posix_acl *dacl = NULL, *pacl = NULL; |
851 | |
852 | p9_debug(P9_DEBUG_VFS, " %lu,%pd mode: %x MAJOR: %u MINOR: %u\n" , |
853 | dir->i_ino, dentry, omode, |
854 | MAJOR(rdev), MINOR(rdev)); |
855 | |
856 | v9ses = v9fs_inode2v9ses(inode: dir); |
857 | dfid = v9fs_parent_fid(dentry); |
858 | if (IS_ERR(ptr: dfid)) { |
859 | err = PTR_ERR(ptr: dfid); |
860 | p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n" , err); |
861 | goto error; |
862 | } |
863 | |
864 | gid = v9fs_get_fsgid_for_create(dir_inode: dir); |
865 | mode = omode; |
866 | /* Update mode based on ACL value */ |
867 | err = v9fs_acl_mode(dir, modep: &mode, dpacl: &dacl, pacl: &pacl); |
868 | if (err) { |
869 | p9_debug(P9_DEBUG_VFS, "Failed to get acl values in mknod %d\n" , |
870 | err); |
871 | goto error; |
872 | } |
873 | name = dentry->d_name.name; |
874 | |
875 | err = p9_client_mknod_dotl(oldfid: dfid, name, mode, rdev, gid, qid: &qid); |
876 | if (err < 0) |
877 | goto error; |
878 | |
879 | v9fs_invalidate_inode_attr(inode: dir); |
880 | fid = p9_client_walk(oldfid: dfid, nwname: 1, wnames: &name, clone: 1); |
881 | if (IS_ERR(ptr: fid)) { |
882 | err = PTR_ERR(ptr: fid); |
883 | p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n" , |
884 | err); |
885 | goto error; |
886 | } |
887 | |
888 | /* instantiate inode and assign the unopened fid to the dentry */ |
889 | if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) { |
890 | inode = v9fs_get_new_inode_from_fid(v9ses, fid, sb: dir->i_sb); |
891 | if (IS_ERR(ptr: inode)) { |
892 | err = PTR_ERR(ptr: inode); |
893 | p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n" , |
894 | err); |
895 | goto error; |
896 | } |
897 | v9fs_set_create_acl(inode, fid, dacl, acl: pacl); |
898 | v9fs_fid_add(dentry, fid: &fid); |
899 | d_instantiate(dentry, inode); |
900 | err = 0; |
901 | } else { |
902 | /* |
903 | * Not in cached mode. No need to populate inode with stat. |
904 | * socket syscall returns a fd, so we need instantiate |
905 | */ |
906 | inode = v9fs_get_inode(sb: dir->i_sb, mode, rdev); |
907 | if (IS_ERR(ptr: inode)) { |
908 | err = PTR_ERR(ptr: inode); |
909 | goto error; |
910 | } |
911 | v9fs_set_create_acl(inode, fid, dacl, acl: pacl); |
912 | d_instantiate(dentry, inode); |
913 | } |
914 | error: |
915 | p9_fid_put(fid); |
916 | v9fs_put_acl(dacl, acl: pacl); |
917 | p9_fid_put(fid: dfid); |
918 | |
919 | return err; |
920 | } |
921 | |
922 | /** |
923 | * v9fs_vfs_get_link_dotl - follow a symlink path |
924 | * @dentry: dentry for symlink |
925 | * @inode: inode for symlink |
926 | * @done: destructor for return value |
927 | */ |
928 | |
929 | static const char * |
930 | v9fs_vfs_get_link_dotl(struct dentry *dentry, |
931 | struct inode *inode, |
932 | struct delayed_call *done) |
933 | { |
934 | struct p9_fid *fid; |
935 | char *target; |
936 | int retval; |
937 | |
938 | if (!dentry) |
939 | return ERR_PTR(error: -ECHILD); |
940 | |
941 | p9_debug(P9_DEBUG_VFS, "%pd\n" , dentry); |
942 | |
943 | fid = v9fs_fid_lookup(dentry); |
944 | if (IS_ERR(ptr: fid)) |
945 | return ERR_CAST(ptr: fid); |
946 | retval = p9_client_readlink(fid, target: &target); |
947 | p9_fid_put(fid); |
948 | if (retval) |
949 | return ERR_PTR(error: retval); |
950 | set_delayed_call(call: done, fn: kfree_link, arg: target); |
951 | return target; |
952 | } |
953 | |
954 | int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode) |
955 | { |
956 | struct p9_stat_dotl *st; |
957 | struct v9fs_session_info *v9ses; |
958 | unsigned int flags; |
959 | |
960 | v9ses = v9fs_inode2v9ses(inode); |
961 | st = p9_client_getattr_dotl(fid, P9_STATS_ALL); |
962 | if (IS_ERR(ptr: st)) |
963 | return PTR_ERR(ptr: st); |
964 | /* |
965 | * Don't update inode if the file type is different |
966 | */ |
967 | if (inode_wrong_type(inode, mode: st->st_mode)) |
968 | goto out; |
969 | |
970 | /* |
971 | * We don't want to refresh inode->i_size, |
972 | * because we may have cached data |
973 | */ |
974 | flags = (v9ses->cache & CACHE_LOOSE) ? |
975 | V9FS_STAT2INODE_KEEP_ISIZE : 0; |
976 | v9fs_stat2inode_dotl(stat: st, inode, flags); |
977 | out: |
978 | kfree(objp: st); |
979 | return 0; |
980 | } |
981 | |
982 | const struct inode_operations v9fs_dir_inode_operations_dotl = { |
983 | .create = v9fs_vfs_create_dotl, |
984 | .atomic_open = v9fs_vfs_atomic_open_dotl, |
985 | .lookup = v9fs_vfs_lookup, |
986 | .link = v9fs_vfs_link_dotl, |
987 | .symlink = v9fs_vfs_symlink_dotl, |
988 | .unlink = v9fs_vfs_unlink, |
989 | .mkdir = v9fs_vfs_mkdir_dotl, |
990 | .rmdir = v9fs_vfs_rmdir, |
991 | .mknod = v9fs_vfs_mknod_dotl, |
992 | .rename = v9fs_vfs_rename, |
993 | .getattr = v9fs_vfs_getattr_dotl, |
994 | .setattr = v9fs_vfs_setattr_dotl, |
995 | .listxattr = v9fs_listxattr, |
996 | .get_inode_acl = v9fs_iop_get_inode_acl, |
997 | .get_acl = v9fs_iop_get_acl, |
998 | .set_acl = v9fs_iop_set_acl, |
999 | }; |
1000 | |
1001 | const struct inode_operations v9fs_file_inode_operations_dotl = { |
1002 | .getattr = v9fs_vfs_getattr_dotl, |
1003 | .setattr = v9fs_vfs_setattr_dotl, |
1004 | .listxattr = v9fs_listxattr, |
1005 | .get_inode_acl = v9fs_iop_get_inode_acl, |
1006 | .get_acl = v9fs_iop_get_acl, |
1007 | .set_acl = v9fs_iop_set_acl, |
1008 | }; |
1009 | |
1010 | const struct inode_operations v9fs_symlink_inode_operations_dotl = { |
1011 | .get_link = v9fs_vfs_get_link_dotl, |
1012 | .getattr = v9fs_vfs_getattr_dotl, |
1013 | .setattr = v9fs_vfs_setattr_dotl, |
1014 | .listxattr = v9fs_listxattr, |
1015 | }; |
1016 | |