]>
Commit | Line | Data |
---|---|---|
5f542225 AK |
1 | /* |
2 | * Virtio 9p handle callback | |
3 | * | |
4 | * Copyright IBM, Corp. 2011 | |
5 | * | |
6 | * Authors: | |
7 | * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2. See | |
10 | * the COPYING file in the top-level directory. | |
11 | * | |
12 | */ | |
13 | ||
14 | #include "hw/virtio.h" | |
15 | #include "virtio-9p.h" | |
16 | #include "virtio-9p-xattr.h" | |
17 | #include <arpa/inet.h> | |
18 | #include <pwd.h> | |
19 | #include <grp.h> | |
20 | #include <sys/socket.h> | |
21 | #include <sys/un.h> | |
22 | #include <attr/xattr.h> | |
23 | #include <unistd.h> | |
24 | ||
25 | struct handle_data { | |
26 | int mountfd; | |
27 | int handle_bytes; | |
28 | }; | |
29 | ||
30 | #if __GLIBC__ <= 2 && __GLIBC_MINOR__ < 14 | |
31 | struct file_handle { | |
32 | unsigned int handle_bytes; | |
33 | int handle_type; | |
34 | unsigned char handle[0]; | |
35 | }; | |
36 | #endif | |
37 | ||
38 | #ifndef AT_EMPTY_PATH | |
39 | #define AT_EMPTY_PATH 0x1000 /* Allow empty relative pathname */ | |
40 | #endif | |
41 | #ifndef O_PATH | |
42 | #define O_PATH 010000000 | |
43 | #endif | |
44 | ||
45 | #ifndef __NR_name_to_handle_at | |
46 | #if defined(__i386__) | |
47 | #define __NR_name_to_handle_at 341 | |
48 | #define __NR_open_by_handle_at 342 | |
49 | #elif defined(__x86_64__) | |
50 | #define __NR_name_to_handle_at 303 | |
51 | #define __NR_open_by_handle_at 304 | |
52 | #endif | |
53 | #endif | |
54 | ||
55 | #ifdef __NR_name_to_handle_at | |
56 | static inline int name_to_handle(int dirfd, const char *name, | |
57 | struct file_handle *fh, int *mnt_id, int flags) | |
58 | { | |
59 | return syscall(__NR_name_to_handle_at, dirfd, name, fh, mnt_id, flags); | |
60 | } | |
61 | ||
62 | static inline int open_by_handle(int mountfd, const char *fh, int flags) | |
63 | { | |
64 | return syscall(__NR_open_by_handle_at, mountfd, fh, flags); | |
65 | } | |
66 | #else | |
67 | static inline int name_to_handle(int dirfd, const char *name, | |
68 | struct file_handle *fh, int *mnt_id, int flags) | |
69 | { | |
70 | errno = ENOSYS; | |
71 | return -1; | |
72 | } | |
73 | ||
74 | static inline int open_by_handle(int mountfd, const char *fh, int flags) | |
75 | { | |
76 | errno = ENOSYS; | |
77 | return -1; | |
78 | } | |
79 | #endif | |
80 | ||
81 | static int handle_update_file_cred(int dirfd, const char *name, FsCred *credp) | |
82 | { | |
83 | int fd, ret; | |
84 | fd = openat(dirfd, name, O_NONBLOCK | O_NOFOLLOW);; | |
85 | if (fd < 0) { | |
86 | return fd; | |
87 | } | |
88 | ret = fchmod(fd, credp->fc_mode & 07777); | |
89 | if (ret < 0) { | |
90 | goto err_out; | |
91 | } | |
92 | ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); | |
93 | err_out: | |
94 | close(fd); | |
95 | return ret; | |
96 | } | |
97 | ||
98 | ||
99 | static int handle_lstat(FsContext *fs_ctx, V9fsPath *fs_path, | |
100 | struct stat *stbuf) | |
101 | { | |
102 | int fd, ret; | |
103 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
104 | ||
105 | fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); | |
106 | if (fd < 0) { | |
107 | return fd; | |
108 | } | |
109 | ret = fstatat(fd, "", stbuf, AT_EMPTY_PATH); | |
110 | close(fd); | |
111 | return ret; | |
112 | } | |
113 | ||
114 | static ssize_t handle_readlink(FsContext *fs_ctx, V9fsPath *fs_path, | |
115 | char *buf, size_t bufsz) | |
116 | { | |
117 | int fd, ret; | |
118 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
119 | ||
120 | fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); | |
121 | if (fd < 0) { | |
122 | return fd; | |
123 | } | |
124 | ret = readlinkat(fd, "", buf, bufsz); | |
125 | close(fd); | |
126 | return ret; | |
127 | } | |
128 | ||
129 | static int handle_close(FsContext *ctx, int fd) | |
130 | { | |
131 | return close(fd); | |
132 | } | |
133 | ||
134 | static int handle_closedir(FsContext *ctx, DIR *dir) | |
135 | { | |
136 | return closedir(dir); | |
137 | } | |
138 | ||
139 | static int handle_open(FsContext *ctx, V9fsPath *fs_path, int flags) | |
140 | { | |
141 | struct handle_data *data = (struct handle_data *)ctx->private; | |
142 | ||
143 | return open_by_handle(data->mountfd, fs_path->data, flags); | |
144 | } | |
145 | ||
146 | static DIR *handle_opendir(FsContext *ctx, V9fsPath *fs_path) | |
147 | { | |
148 | int fd; | |
149 | fd = handle_open(ctx, fs_path, O_DIRECTORY); | |
150 | if (fd < 0) { | |
151 | return NULL; | |
152 | } | |
153 | return fdopendir(fd); | |
154 | } | |
155 | ||
156 | static void handle_rewinddir(FsContext *ctx, DIR *dir) | |
157 | { | |
158 | return rewinddir(dir); | |
159 | } | |
160 | ||
161 | static off_t handle_telldir(FsContext *ctx, DIR *dir) | |
162 | { | |
163 | return telldir(dir); | |
164 | } | |
165 | ||
166 | static int handle_readdir_r(FsContext *ctx, DIR *dir, struct dirent *entry, | |
167 | struct dirent **result) | |
168 | { | |
169 | return readdir_r(dir, entry, result); | |
170 | } | |
171 | ||
172 | static void handle_seekdir(FsContext *ctx, DIR *dir, off_t off) | |
173 | { | |
174 | return seekdir(dir, off); | |
175 | } | |
176 | ||
177 | static ssize_t handle_preadv(FsContext *ctx, int fd, const struct iovec *iov, | |
178 | int iovcnt, off_t offset) | |
179 | { | |
180 | #ifdef CONFIG_PREADV | |
181 | return preadv(fd, iov, iovcnt, offset); | |
182 | #else | |
183 | int err = lseek(fd, offset, SEEK_SET); | |
184 | if (err == -1) { | |
185 | return err; | |
186 | } else { | |
187 | return readv(fd, iov, iovcnt); | |
188 | } | |
189 | #endif | |
190 | } | |
191 | ||
192 | static ssize_t handle_pwritev(FsContext *ctx, int fd, const struct iovec *iov, | |
193 | int iovcnt, off_t offset) | |
194 | { | |
d3ab98e6 | 195 | ssize_t ret; |
5f542225 | 196 | #ifdef CONFIG_PREADV |
d3ab98e6 | 197 | ret = pwritev(fd, iov, iovcnt, offset); |
5f542225 AK |
198 | #else |
199 | int err = lseek(fd, offset, SEEK_SET); | |
200 | if (err == -1) { | |
201 | return err; | |
202 | } else { | |
d3ab98e6 | 203 | ret = writev(fd, iov, iovcnt); |
5f542225 AK |
204 | } |
205 | #endif | |
d3ab98e6 AK |
206 | #ifdef CONFIG_SYNC_FILE_RANGE |
207 | if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { | |
208 | /* | |
209 | * Initiate a writeback. This is not a data integrity sync. | |
210 | * We want to ensure that we don't leave dirty pages in the cache | |
211 | * after write when writeout=immediate is sepcified. | |
212 | */ | |
213 | sync_file_range(fd, offset, ret, | |
214 | SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); | |
215 | } | |
216 | #endif | |
217 | return ret; | |
5f542225 AK |
218 | } |
219 | ||
220 | static int handle_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) | |
221 | { | |
222 | int fd, ret; | |
223 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
224 | ||
225 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
226 | if (fd < 0) { | |
227 | return fd; | |
228 | } | |
229 | ret = fchmod(fd, credp->fc_mode); | |
230 | close(fd); | |
231 | return ret; | |
232 | } | |
233 | ||
234 | static int handle_mknod(FsContext *fs_ctx, V9fsPath *dir_path, | |
235 | const char *name, FsCred *credp) | |
236 | { | |
237 | int dirfd, ret; | |
238 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
239 | ||
240 | dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); | |
241 | if (dirfd < 0) { | |
242 | return dirfd; | |
243 | } | |
244 | ret = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev); | |
245 | if (!ret) { | |
246 | ret = handle_update_file_cred(dirfd, name, credp); | |
247 | } | |
248 | close(dirfd); | |
249 | return ret; | |
250 | } | |
251 | ||
252 | static int handle_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, | |
253 | const char *name, FsCred *credp) | |
254 | { | |
255 | int dirfd, ret; | |
256 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
257 | ||
258 | dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); | |
259 | if (dirfd < 0) { | |
260 | return dirfd; | |
261 | } | |
262 | ret = mkdirat(dirfd, name, credp->fc_mode); | |
263 | if (!ret) { | |
264 | ret = handle_update_file_cred(dirfd, name, credp); | |
265 | } | |
266 | close(dirfd); | |
267 | return ret; | |
268 | } | |
269 | ||
270 | static int handle_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf) | |
271 | { | |
272 | return fstat(fd, stbuf); | |
273 | } | |
274 | ||
275 | static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, | |
276 | int flags, FsCred *credp) | |
277 | { | |
278 | int ret; | |
279 | int dirfd, fd; | |
280 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
281 | ||
282 | dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); | |
283 | if (dirfd < 0) { | |
284 | return dirfd; | |
285 | } | |
286 | fd = openat(dirfd, name, flags | O_NOFOLLOW, credp->fc_mode); | |
287 | if (fd >= 0) { | |
288 | ret = handle_update_file_cred(dirfd, name, credp); | |
289 | if (ret < 0) { | |
290 | close(fd); | |
291 | fd = ret; | |
292 | } | |
293 | } | |
294 | close(dirfd); | |
295 | return fd; | |
296 | } | |
297 | ||
298 | ||
299 | static int handle_symlink(FsContext *fs_ctx, const char *oldpath, | |
300 | V9fsPath *dir_path, const char *name, FsCred *credp) | |
301 | { | |
302 | int fd, dirfd, ret; | |
303 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
304 | ||
305 | dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); | |
306 | if (dirfd < 0) { | |
307 | return dirfd; | |
308 | } | |
309 | ret = symlinkat(oldpath, dirfd, name); | |
310 | if (!ret) { | |
311 | fd = openat(dirfd, name, O_PATH | O_NOFOLLOW); | |
312 | if (fd < 0) { | |
313 | ret = fd; | |
314 | goto err_out; | |
315 | } | |
316 | ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); | |
317 | close(fd); | |
318 | } | |
319 | err_out: | |
320 | close(dirfd); | |
321 | return ret; | |
322 | } | |
323 | ||
324 | static int handle_link(FsContext *ctx, V9fsPath *oldpath, | |
325 | V9fsPath *dirpath, const char *name) | |
326 | { | |
327 | int oldfd, newdirfd, ret; | |
328 | struct handle_data *data = (struct handle_data *)ctx->private; | |
329 | ||
330 | oldfd = open_by_handle(data->mountfd, oldpath->data, O_PATH); | |
331 | if (oldfd < 0) { | |
332 | return oldfd; | |
333 | } | |
334 | newdirfd = open_by_handle(data->mountfd, dirpath->data, O_PATH); | |
335 | if (newdirfd < 0) { | |
336 | close(oldfd); | |
337 | return newdirfd; | |
338 | } | |
339 | ret = linkat(oldfd, "", newdirfd, name, AT_EMPTY_PATH); | |
340 | close(newdirfd); | |
341 | close(oldfd); | |
342 | return ret; | |
343 | } | |
344 | ||
345 | static int handle_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) | |
346 | { | |
347 | int fd, ret; | |
348 | struct handle_data *data = (struct handle_data *)ctx->private; | |
349 | ||
350 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK | O_WRONLY); | |
351 | if (fd < 0) { | |
352 | return fd; | |
353 | } | |
354 | ret = ftruncate(fd, size); | |
355 | close(fd); | |
356 | return ret; | |
357 | } | |
358 | ||
359 | static int handle_rename(FsContext *ctx, const char *oldpath, | |
360 | const char *newpath) | |
361 | { | |
362 | errno = EOPNOTSUPP; | |
363 | return -1; | |
364 | } | |
365 | ||
366 | static int handle_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) | |
367 | { | |
368 | int fd, ret; | |
369 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
370 | ||
371 | fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); | |
372 | if (fd < 0) { | |
373 | return fd; | |
374 | } | |
375 | ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); | |
376 | close(fd); | |
377 | return ret; | |
378 | } | |
379 | ||
380 | static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path, | |
381 | const struct timespec *buf) | |
382 | { | |
383 | int fd, ret; | |
384 | struct handle_data *data = (struct handle_data *)ctx->private; | |
385 | ||
386 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
387 | if (fd < 0) { | |
388 | return fd; | |
389 | } | |
390 | ret = futimens(fd, buf); | |
391 | close(fd); | |
392 | return ret; | |
393 | } | |
394 | ||
395 | static int handle_remove(FsContext *ctx, const char *path) | |
396 | { | |
397 | errno = EOPNOTSUPP; | |
398 | return -1; | |
399 | } | |
400 | ||
401 | static int handle_fsync(FsContext *ctx, int fd, int datasync) | |
402 | { | |
403 | if (datasync) { | |
404 | return qemu_fdatasync(fd); | |
405 | } else { | |
406 | return fsync(fd); | |
407 | } | |
408 | } | |
409 | ||
410 | static int handle_statfs(FsContext *ctx, V9fsPath *fs_path, | |
411 | struct statfs *stbuf) | |
412 | { | |
413 | int fd, ret; | |
414 | struct handle_data *data = (struct handle_data *)ctx->private; | |
415 | ||
416 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
417 | if (fd < 0) { | |
418 | return fd; | |
419 | } | |
420 | ret = fstatfs(fd, stbuf); | |
421 | close(fd); | |
422 | return ret; | |
423 | } | |
424 | ||
425 | static ssize_t handle_lgetxattr(FsContext *ctx, V9fsPath *fs_path, | |
426 | const char *name, void *value, size_t size) | |
427 | { | |
428 | int fd, ret; | |
429 | struct handle_data *data = (struct handle_data *)ctx->private; | |
430 | ||
431 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
432 | if (fd < 0) { | |
433 | return fd; | |
434 | } | |
435 | ret = fgetxattr(fd, name, value, size); | |
436 | close(fd); | |
437 | return ret; | |
438 | } | |
439 | ||
440 | static ssize_t handle_llistxattr(FsContext *ctx, V9fsPath *fs_path, | |
441 | void *value, size_t size) | |
442 | { | |
443 | int fd, ret; | |
444 | struct handle_data *data = (struct handle_data *)ctx->private; | |
445 | ||
446 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
447 | if (fd < 0) { | |
448 | return fd; | |
449 | } | |
450 | ret = flistxattr(fd, value, size); | |
451 | close(fd); | |
452 | return ret; | |
453 | } | |
454 | ||
455 | static int handle_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, | |
456 | void *value, size_t size, int flags) | |
457 | { | |
458 | int fd, ret; | |
459 | struct handle_data *data = (struct handle_data *)ctx->private; | |
460 | ||
461 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
462 | if (fd < 0) { | |
463 | return fd; | |
464 | } | |
465 | ret = fsetxattr(fd, name, value, size, flags); | |
466 | close(fd); | |
467 | return ret; | |
468 | } | |
469 | ||
470 | static int handle_lremovexattr(FsContext *ctx, V9fsPath *fs_path, | |
471 | const char *name) | |
472 | { | |
473 | int fd, ret; | |
474 | struct handle_data *data = (struct handle_data *)ctx->private; | |
475 | ||
476 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
477 | if (fd < 0) { | |
478 | return fd; | |
479 | } | |
480 | ret = fremovexattr(fd, name); | |
481 | close(fd); | |
482 | return ret; | |
483 | } | |
484 | ||
485 | static int handle_name_to_path(FsContext *ctx, V9fsPath *dir_path, | |
486 | const char *name, V9fsPath *target) | |
487 | { | |
488 | char buffer[PATH_MAX]; | |
489 | struct file_handle *fh; | |
490 | int dirfd, ret, mnt_id; | |
491 | struct handle_data *data = (struct handle_data *)ctx->private; | |
492 | ||
493 | /* "." and ".." are not allowed */ | |
494 | if (!strcmp(name, ".") || !strcmp(name, "..")) { | |
495 | errno = EINVAL; | |
496 | return -1; | |
497 | ||
498 | } | |
499 | if (dir_path) { | |
500 | dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); | |
501 | } else { | |
502 | /* relative to export root */ | |
503 | dirfd = open(rpath(ctx, ".", buffer), O_DIRECTORY); | |
504 | } | |
505 | if (dirfd < 0) { | |
506 | return dirfd; | |
507 | } | |
508 | fh = g_malloc(sizeof(struct file_handle) + data->handle_bytes); | |
509 | fh->handle_bytes = data->handle_bytes; | |
510 | /* add a "./" at the begining of the path */ | |
511 | snprintf(buffer, PATH_MAX, "./%s", name); | |
512 | /* flag = 0 imply don't follow symlink */ | |
513 | ret = name_to_handle(dirfd, buffer, fh, &mnt_id, 0); | |
514 | if (!ret) { | |
515 | target->data = (char *)fh; | |
516 | target->size = sizeof(struct file_handle) + data->handle_bytes; | |
517 | } else { | |
518 | g_free(fh); | |
519 | } | |
520 | close(dirfd); | |
521 | return ret; | |
522 | } | |
523 | ||
524 | static int handle_renameat(FsContext *ctx, V9fsPath *olddir, | |
525 | const char *old_name, V9fsPath *newdir, | |
526 | const char *new_name) | |
527 | { | |
528 | int olddirfd, newdirfd, ret; | |
529 | struct handle_data *data = (struct handle_data *)ctx->private; | |
530 | ||
531 | olddirfd = open_by_handle(data->mountfd, olddir->data, O_PATH); | |
532 | if (olddirfd < 0) { | |
533 | return olddirfd; | |
534 | } | |
535 | newdirfd = open_by_handle(data->mountfd, newdir->data, O_PATH); | |
536 | if (newdirfd < 0) { | |
537 | close(olddirfd); | |
538 | return newdirfd; | |
539 | } | |
540 | ret = renameat(olddirfd, old_name, newdirfd, new_name); | |
541 | close(newdirfd); | |
542 | close(olddirfd); | |
543 | return ret; | |
544 | } | |
545 | ||
546 | static int handle_unlinkat(FsContext *ctx, V9fsPath *dir, | |
547 | const char *name, int flags) | |
548 | { | |
549 | int dirfd, ret; | |
550 | struct handle_data *data = (struct handle_data *)ctx->private; | |
551 | ||
552 | dirfd = open_by_handle(data->mountfd, dir->data, O_PATH); | |
553 | if (dirfd < 0) { | |
554 | return dirfd; | |
555 | } | |
556 | ||
557 | ret = unlinkat(dirfd, name, flags); | |
558 | close(dirfd); | |
559 | return ret; | |
560 | } | |
561 | ||
562 | static int handle_init(FsContext *ctx) | |
563 | { | |
564 | int ret, mnt_id; | |
565 | struct file_handle fh; | |
566 | struct handle_data *data = g_malloc(sizeof(struct handle_data)); | |
567 | data->mountfd = open(ctx->fs_root, O_DIRECTORY); | |
568 | if (data->mountfd < 0) { | |
569 | ret = data->mountfd; | |
570 | goto err_out; | |
571 | } | |
572 | memset(&fh, 0, sizeof(struct file_handle)); | |
573 | ret = name_to_handle(data->mountfd, ".", &fh, &mnt_id, 0); | |
574 | if (ret && errno == EOVERFLOW) { | |
575 | data->handle_bytes = fh.handle_bytes; | |
576 | ctx->private = data; | |
577 | ret = 0; | |
578 | goto out; | |
579 | } | |
580 | /* we got 0 byte handle ? */ | |
581 | ret = -1; | |
582 | close(data->mountfd); | |
583 | err_out: | |
584 | g_free(data); | |
585 | out: | |
586 | return ret; | |
587 | } | |
588 | ||
589 | FileOperations handle_ops = { | |
590 | .init = handle_init, | |
591 | .lstat = handle_lstat, | |
592 | .readlink = handle_readlink, | |
593 | .close = handle_close, | |
594 | .closedir = handle_closedir, | |
595 | .open = handle_open, | |
596 | .opendir = handle_opendir, | |
597 | .rewinddir = handle_rewinddir, | |
598 | .telldir = handle_telldir, | |
599 | .readdir_r = handle_readdir_r, | |
600 | .seekdir = handle_seekdir, | |
601 | .preadv = handle_preadv, | |
602 | .pwritev = handle_pwritev, | |
603 | .chmod = handle_chmod, | |
604 | .mknod = handle_mknod, | |
605 | .mkdir = handle_mkdir, | |
606 | .fstat = handle_fstat, | |
607 | .open2 = handle_open2, | |
608 | .symlink = handle_symlink, | |
609 | .link = handle_link, | |
610 | .truncate = handle_truncate, | |
611 | .rename = handle_rename, | |
612 | .chown = handle_chown, | |
613 | .utimensat = handle_utimensat, | |
614 | .remove = handle_remove, | |
615 | .fsync = handle_fsync, | |
616 | .statfs = handle_statfs, | |
617 | .lgetxattr = handle_lgetxattr, | |
618 | .llistxattr = handle_llistxattr, | |
619 | .lsetxattr = handle_lsetxattr, | |
620 | .lremovexattr = handle_lremovexattr, | |
621 | .name_to_path = handle_name_to_path, | |
622 | .renameat = handle_renameat, | |
623 | .unlinkat = handle_unlinkat, | |
624 | }; |