]>
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> | |
4f26f2b6 | 22 | #include "qemu-xattr.h" |
5f542225 | 23 | #include <unistd.h> |
edb9eb73 HPB |
24 | #include <linux/fs.h> |
25 | #ifdef CONFIG_LINUX_MAGIC_H | |
26 | #include <linux/magic.h> | |
27 | #endif | |
28 | #include <sys/ioctl.h> | |
29 | ||
30 | #ifndef XFS_SUPER_MAGIC | |
31 | #define XFS_SUPER_MAGIC 0x58465342 | |
32 | #endif | |
33 | #ifndef EXT2_SUPER_MAGIC | |
34 | #define EXT2_SUPER_MAGIC 0xEF53 | |
35 | #endif | |
36 | #ifndef REISERFS_SUPER_MAGIC | |
37 | #define REISERFS_SUPER_MAGIC 0x52654973 | |
38 | #endif | |
39 | #ifndef BTRFS_SUPER_MAGIC | |
40 | #define BTRFS_SUPER_MAGIC 0x9123683E | |
41 | #endif | |
5f542225 AK |
42 | |
43 | struct handle_data { | |
44 | int mountfd; | |
45 | int handle_bytes; | |
46 | }; | |
47 | ||
d2042378 AK |
48 | static inline int name_to_handle(int dirfd, const char *name, |
49 | struct file_handle *fh, int *mnt_id, int flags) | |
50 | { | |
51 | return name_to_handle_at(dirfd, name, fh, mnt_id, flags); | |
52 | } | |
53 | ||
54 | static inline int open_by_handle(int mountfd, const char *fh, int flags) | |
55 | { | |
56 | return open_by_handle_at(mountfd, (struct file_handle *)fh, flags); | |
57 | } | |
5f542225 AK |
58 | |
59 | static int handle_update_file_cred(int dirfd, const char *name, FsCred *credp) | |
60 | { | |
61 | int fd, ret; | |
3a93113a | 62 | fd = openat(dirfd, name, O_NONBLOCK | O_NOFOLLOW); |
5f542225 AK |
63 | if (fd < 0) { |
64 | return fd; | |
65 | } | |
2d40564a | 66 | ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); |
5f542225 AK |
67 | if (ret < 0) { |
68 | goto err_out; | |
69 | } | |
2d40564a | 70 | ret = fchmod(fd, credp->fc_mode & 07777); |
5f542225 AK |
71 | err_out: |
72 | close(fd); | |
73 | return ret; | |
74 | } | |
75 | ||
76 | ||
77 | static int handle_lstat(FsContext *fs_ctx, V9fsPath *fs_path, | |
78 | struct stat *stbuf) | |
79 | { | |
80 | int fd, ret; | |
81 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
82 | ||
83 | fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); | |
84 | if (fd < 0) { | |
85 | return fd; | |
86 | } | |
87 | ret = fstatat(fd, "", stbuf, AT_EMPTY_PATH); | |
88 | close(fd); | |
89 | return ret; | |
90 | } | |
91 | ||
92 | static ssize_t handle_readlink(FsContext *fs_ctx, V9fsPath *fs_path, | |
93 | char *buf, size_t bufsz) | |
94 | { | |
95 | int fd, ret; | |
96 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
97 | ||
98 | fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); | |
99 | if (fd < 0) { | |
100 | return fd; | |
101 | } | |
102 | ret = readlinkat(fd, "", buf, bufsz); | |
103 | close(fd); | |
104 | return ret; | |
105 | } | |
106 | ||
cc720ddb | 107 | static int handle_close(FsContext *ctx, V9fsFidOpenState *fs) |
5f542225 | 108 | { |
cc720ddb | 109 | return close(fs->fd); |
5f542225 AK |
110 | } |
111 | ||
cc720ddb | 112 | static int handle_closedir(FsContext *ctx, V9fsFidOpenState *fs) |
5f542225 | 113 | { |
cc720ddb | 114 | return closedir(fs->dir); |
5f542225 AK |
115 | } |
116 | ||
cc720ddb AK |
117 | static int handle_open(FsContext *ctx, V9fsPath *fs_path, |
118 | int flags, V9fsFidOpenState *fs) | |
5f542225 AK |
119 | { |
120 | struct handle_data *data = (struct handle_data *)ctx->private; | |
121 | ||
cc720ddb AK |
122 | fs->fd = open_by_handle(data->mountfd, fs_path->data, flags); |
123 | return fs->fd; | |
5f542225 AK |
124 | } |
125 | ||
cc720ddb AK |
126 | static int handle_opendir(FsContext *ctx, |
127 | V9fsPath *fs_path, V9fsFidOpenState *fs) | |
5f542225 | 128 | { |
cc720ddb AK |
129 | int ret; |
130 | ret = handle_open(ctx, fs_path, O_DIRECTORY, fs); | |
131 | if (ret < 0) { | |
132 | return -1; | |
5f542225 | 133 | } |
cc720ddb AK |
134 | fs->dir = fdopendir(ret); |
135 | if (!fs->dir) { | |
136 | return -1; | |
137 | } | |
138 | return 0; | |
5f542225 AK |
139 | } |
140 | ||
cc720ddb | 141 | static void handle_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) |
5f542225 | 142 | { |
cc720ddb | 143 | return rewinddir(fs->dir); |
5f542225 AK |
144 | } |
145 | ||
cc720ddb | 146 | static off_t handle_telldir(FsContext *ctx, V9fsFidOpenState *fs) |
5f542225 | 147 | { |
cc720ddb | 148 | return telldir(fs->dir); |
5f542225 AK |
149 | } |
150 | ||
cc720ddb AK |
151 | static int handle_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, |
152 | struct dirent *entry, | |
5f542225 AK |
153 | struct dirent **result) |
154 | { | |
cc720ddb | 155 | return readdir_r(fs->dir, entry, result); |
5f542225 AK |
156 | } |
157 | ||
cc720ddb | 158 | static void handle_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) |
5f542225 | 159 | { |
cc720ddb | 160 | return seekdir(fs->dir, off); |
5f542225 AK |
161 | } |
162 | ||
cc720ddb AK |
163 | static ssize_t handle_preadv(FsContext *ctx, V9fsFidOpenState *fs, |
164 | const struct iovec *iov, | |
5f542225 AK |
165 | int iovcnt, off_t offset) |
166 | { | |
167 | #ifdef CONFIG_PREADV | |
cc720ddb | 168 | return preadv(fs->fd, iov, iovcnt, offset); |
5f542225 | 169 | #else |
cc720ddb | 170 | int err = lseek(fs->fd, offset, SEEK_SET); |
5f542225 AK |
171 | if (err == -1) { |
172 | return err; | |
173 | } else { | |
cc720ddb | 174 | return readv(fs->fd, iov, iovcnt); |
5f542225 AK |
175 | } |
176 | #endif | |
177 | } | |
178 | ||
cc720ddb AK |
179 | static ssize_t handle_pwritev(FsContext *ctx, V9fsFidOpenState *fs, |
180 | const struct iovec *iov, | |
5f542225 AK |
181 | int iovcnt, off_t offset) |
182 | { | |
d3ab98e6 | 183 | ssize_t ret; |
5f542225 | 184 | #ifdef CONFIG_PREADV |
cc720ddb | 185 | ret = pwritev(fs->fd, iov, iovcnt, offset); |
5f542225 | 186 | #else |
cc720ddb | 187 | int err = lseek(fs->fd, offset, SEEK_SET); |
5f542225 AK |
188 | if (err == -1) { |
189 | return err; | |
190 | } else { | |
cc720ddb | 191 | ret = writev(fs->fd, iov, iovcnt); |
5f542225 AK |
192 | } |
193 | #endif | |
d3ab98e6 AK |
194 | #ifdef CONFIG_SYNC_FILE_RANGE |
195 | if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { | |
196 | /* | |
197 | * Initiate a writeback. This is not a data integrity sync. | |
198 | * We want to ensure that we don't leave dirty pages in the cache | |
199 | * after write when writeout=immediate is sepcified. | |
200 | */ | |
cc720ddb | 201 | sync_file_range(fs->fd, offset, ret, |
d3ab98e6 AK |
202 | SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); |
203 | } | |
204 | #endif | |
205 | return ret; | |
5f542225 AK |
206 | } |
207 | ||
208 | static int handle_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) | |
209 | { | |
210 | int fd, ret; | |
211 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
212 | ||
213 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
214 | if (fd < 0) { | |
215 | return fd; | |
216 | } | |
217 | ret = fchmod(fd, credp->fc_mode); | |
218 | close(fd); | |
219 | return ret; | |
220 | } | |
221 | ||
222 | static int handle_mknod(FsContext *fs_ctx, V9fsPath *dir_path, | |
223 | const char *name, FsCred *credp) | |
224 | { | |
225 | int dirfd, ret; | |
226 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
227 | ||
228 | dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); | |
229 | if (dirfd < 0) { | |
230 | return dirfd; | |
231 | } | |
232 | ret = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev); | |
233 | if (!ret) { | |
234 | ret = handle_update_file_cred(dirfd, name, credp); | |
235 | } | |
236 | close(dirfd); | |
237 | return ret; | |
238 | } | |
239 | ||
240 | static int handle_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, | |
241 | const char *name, FsCred *credp) | |
242 | { | |
243 | int dirfd, ret; | |
244 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
245 | ||
246 | dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); | |
247 | if (dirfd < 0) { | |
248 | return dirfd; | |
249 | } | |
250 | ret = mkdirat(dirfd, name, credp->fc_mode); | |
251 | if (!ret) { | |
252 | ret = handle_update_file_cred(dirfd, name, credp); | |
253 | } | |
254 | close(dirfd); | |
255 | return ret; | |
256 | } | |
257 | ||
8b888272 AK |
258 | static int handle_fstat(FsContext *fs_ctx, int fid_type, |
259 | V9fsFidOpenState *fs, struct stat *stbuf) | |
5f542225 | 260 | { |
8b888272 AK |
261 | int fd; |
262 | ||
263 | if (fid_type == P9_FID_DIR) { | |
264 | fd = dirfd(fs->dir); | |
265 | } else { | |
266 | fd = fs->fd; | |
267 | } | |
268 | return fstat(fd, stbuf); | |
5f542225 AK |
269 | } |
270 | ||
271 | static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, | |
cc720ddb | 272 | int flags, FsCred *credp, V9fsFidOpenState *fs) |
5f542225 AK |
273 | { |
274 | int ret; | |
275 | int dirfd, fd; | |
276 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
277 | ||
278 | dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); | |
279 | if (dirfd < 0) { | |
280 | return dirfd; | |
281 | } | |
282 | fd = openat(dirfd, name, flags | O_NOFOLLOW, credp->fc_mode); | |
283 | if (fd >= 0) { | |
284 | ret = handle_update_file_cred(dirfd, name, credp); | |
285 | if (ret < 0) { | |
286 | close(fd); | |
287 | fd = ret; | |
cc720ddb AK |
288 | } else { |
289 | fs->fd = fd; | |
5f542225 AK |
290 | } |
291 | } | |
292 | close(dirfd); | |
293 | return fd; | |
294 | } | |
295 | ||
296 | ||
297 | static int handle_symlink(FsContext *fs_ctx, const char *oldpath, | |
298 | V9fsPath *dir_path, const char *name, FsCred *credp) | |
299 | { | |
300 | int fd, dirfd, ret; | |
301 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
302 | ||
303 | dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); | |
304 | if (dirfd < 0) { | |
305 | return dirfd; | |
306 | } | |
307 | ret = symlinkat(oldpath, dirfd, name); | |
308 | if (!ret) { | |
309 | fd = openat(dirfd, name, O_PATH | O_NOFOLLOW); | |
310 | if (fd < 0) { | |
311 | ret = fd; | |
312 | goto err_out; | |
313 | } | |
314 | ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); | |
315 | close(fd); | |
316 | } | |
317 | err_out: | |
318 | close(dirfd); | |
319 | return ret; | |
320 | } | |
321 | ||
322 | static int handle_link(FsContext *ctx, V9fsPath *oldpath, | |
323 | V9fsPath *dirpath, const char *name) | |
324 | { | |
325 | int oldfd, newdirfd, ret; | |
326 | struct handle_data *data = (struct handle_data *)ctx->private; | |
327 | ||
328 | oldfd = open_by_handle(data->mountfd, oldpath->data, O_PATH); | |
329 | if (oldfd < 0) { | |
330 | return oldfd; | |
331 | } | |
332 | newdirfd = open_by_handle(data->mountfd, dirpath->data, O_PATH); | |
333 | if (newdirfd < 0) { | |
334 | close(oldfd); | |
335 | return newdirfd; | |
336 | } | |
337 | ret = linkat(oldfd, "", newdirfd, name, AT_EMPTY_PATH); | |
338 | close(newdirfd); | |
339 | close(oldfd); | |
340 | return ret; | |
341 | } | |
342 | ||
343 | static int handle_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) | |
344 | { | |
345 | int fd, ret; | |
346 | struct handle_data *data = (struct handle_data *)ctx->private; | |
347 | ||
348 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK | O_WRONLY); | |
349 | if (fd < 0) { | |
350 | return fd; | |
351 | } | |
352 | ret = ftruncate(fd, size); | |
353 | close(fd); | |
354 | return ret; | |
355 | } | |
356 | ||
357 | static int handle_rename(FsContext *ctx, const char *oldpath, | |
358 | const char *newpath) | |
359 | { | |
360 | errno = EOPNOTSUPP; | |
361 | return -1; | |
362 | } | |
363 | ||
364 | static int handle_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) | |
365 | { | |
366 | int fd, ret; | |
367 | struct handle_data *data = (struct handle_data *)fs_ctx->private; | |
368 | ||
369 | fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); | |
370 | if (fd < 0) { | |
371 | return fd; | |
372 | } | |
373 | ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); | |
374 | close(fd); | |
375 | return ret; | |
376 | } | |
377 | ||
378 | static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path, | |
379 | const struct timespec *buf) | |
380 | { | |
d2042378 AK |
381 | int ret; |
382 | #ifdef CONFIG_UTIMENSAT | |
383 | int fd; | |
5f542225 AK |
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); | |
d2042378 AK |
392 | #else |
393 | ret = -1; | |
394 | errno = ENOSYS; | |
395 | #endif | |
5f542225 AK |
396 | return ret; |
397 | } | |
398 | ||
399 | static int handle_remove(FsContext *ctx, const char *path) | |
400 | { | |
401 | errno = EOPNOTSUPP; | |
402 | return -1; | |
403 | } | |
404 | ||
8b888272 AK |
405 | static int handle_fsync(FsContext *ctx, int fid_type, |
406 | V9fsFidOpenState *fs, int datasync) | |
5f542225 | 407 | { |
8b888272 AK |
408 | int fd; |
409 | ||
410 | if (fid_type == P9_FID_DIR) { | |
411 | fd = dirfd(fs->dir); | |
412 | } else { | |
413 | fd = fs->fd; | |
414 | } | |
415 | ||
5f542225 | 416 | if (datasync) { |
8b888272 | 417 | return qemu_fdatasync(fd); |
5f542225 | 418 | } else { |
8b888272 | 419 | return fsync(fd); |
5f542225 AK |
420 | } |
421 | } | |
422 | ||
423 | static int handle_statfs(FsContext *ctx, V9fsPath *fs_path, | |
424 | struct statfs *stbuf) | |
425 | { | |
426 | int fd, ret; | |
427 | struct handle_data *data = (struct handle_data *)ctx->private; | |
428 | ||
429 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
430 | if (fd < 0) { | |
431 | return fd; | |
432 | } | |
433 | ret = fstatfs(fd, stbuf); | |
434 | close(fd); | |
435 | return ret; | |
436 | } | |
437 | ||
438 | static ssize_t handle_lgetxattr(FsContext *ctx, V9fsPath *fs_path, | |
439 | const char *name, void *value, size_t size) | |
440 | { | |
441 | int fd, ret; | |
442 | struct handle_data *data = (struct handle_data *)ctx->private; | |
443 | ||
444 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
445 | if (fd < 0) { | |
446 | return fd; | |
447 | } | |
448 | ret = fgetxattr(fd, name, value, size); | |
449 | close(fd); | |
450 | return ret; | |
451 | } | |
452 | ||
453 | static ssize_t handle_llistxattr(FsContext *ctx, V9fsPath *fs_path, | |
454 | void *value, size_t size) | |
455 | { | |
456 | int fd, ret; | |
457 | struct handle_data *data = (struct handle_data *)ctx->private; | |
458 | ||
459 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
460 | if (fd < 0) { | |
461 | return fd; | |
462 | } | |
463 | ret = flistxattr(fd, value, size); | |
464 | close(fd); | |
465 | return ret; | |
466 | } | |
467 | ||
468 | static int handle_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, | |
469 | void *value, size_t size, int flags) | |
470 | { | |
471 | int fd, ret; | |
472 | struct handle_data *data = (struct handle_data *)ctx->private; | |
473 | ||
474 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
475 | if (fd < 0) { | |
476 | return fd; | |
477 | } | |
478 | ret = fsetxattr(fd, name, value, size, flags); | |
479 | close(fd); | |
480 | return ret; | |
481 | } | |
482 | ||
483 | static int handle_lremovexattr(FsContext *ctx, V9fsPath *fs_path, | |
484 | const char *name) | |
485 | { | |
486 | int fd, ret; | |
487 | struct handle_data *data = (struct handle_data *)ctx->private; | |
488 | ||
489 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
490 | if (fd < 0) { | |
491 | return fd; | |
492 | } | |
493 | ret = fremovexattr(fd, name); | |
494 | close(fd); | |
495 | return ret; | |
496 | } | |
497 | ||
498 | static int handle_name_to_path(FsContext *ctx, V9fsPath *dir_path, | |
499 | const char *name, V9fsPath *target) | |
500 | { | |
501 | char buffer[PATH_MAX]; | |
502 | struct file_handle *fh; | |
503 | int dirfd, ret, mnt_id; | |
504 | struct handle_data *data = (struct handle_data *)ctx->private; | |
505 | ||
506 | /* "." and ".." are not allowed */ | |
507 | if (!strcmp(name, ".") || !strcmp(name, "..")) { | |
508 | errno = EINVAL; | |
509 | return -1; | |
510 | ||
511 | } | |
512 | if (dir_path) { | |
513 | dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); | |
514 | } else { | |
515 | /* relative to export root */ | |
516 | dirfd = open(rpath(ctx, ".", buffer), O_DIRECTORY); | |
517 | } | |
518 | if (dirfd < 0) { | |
519 | return dirfd; | |
520 | } | |
521 | fh = g_malloc(sizeof(struct file_handle) + data->handle_bytes); | |
522 | fh->handle_bytes = data->handle_bytes; | |
66a0a2cb | 523 | /* add a "./" at the beginning of the path */ |
5f542225 AK |
524 | snprintf(buffer, PATH_MAX, "./%s", name); |
525 | /* flag = 0 imply don't follow symlink */ | |
526 | ret = name_to_handle(dirfd, buffer, fh, &mnt_id, 0); | |
527 | if (!ret) { | |
528 | target->data = (char *)fh; | |
529 | target->size = sizeof(struct file_handle) + data->handle_bytes; | |
530 | } else { | |
531 | g_free(fh); | |
532 | } | |
533 | close(dirfd); | |
534 | return ret; | |
535 | } | |
536 | ||
537 | static int handle_renameat(FsContext *ctx, V9fsPath *olddir, | |
538 | const char *old_name, V9fsPath *newdir, | |
539 | const char *new_name) | |
540 | { | |
541 | int olddirfd, newdirfd, ret; | |
542 | struct handle_data *data = (struct handle_data *)ctx->private; | |
543 | ||
544 | olddirfd = open_by_handle(data->mountfd, olddir->data, O_PATH); | |
545 | if (olddirfd < 0) { | |
546 | return olddirfd; | |
547 | } | |
548 | newdirfd = open_by_handle(data->mountfd, newdir->data, O_PATH); | |
549 | if (newdirfd < 0) { | |
550 | close(olddirfd); | |
551 | return newdirfd; | |
552 | } | |
553 | ret = renameat(olddirfd, old_name, newdirfd, new_name); | |
554 | close(newdirfd); | |
555 | close(olddirfd); | |
556 | return ret; | |
557 | } | |
558 | ||
559 | static int handle_unlinkat(FsContext *ctx, V9fsPath *dir, | |
560 | const char *name, int flags) | |
561 | { | |
562 | int dirfd, ret; | |
563 | struct handle_data *data = (struct handle_data *)ctx->private; | |
930b5883 | 564 | int rflags; |
5f542225 AK |
565 | |
566 | dirfd = open_by_handle(data->mountfd, dir->data, O_PATH); | |
567 | if (dirfd < 0) { | |
568 | return dirfd; | |
569 | } | |
570 | ||
930b5883 PB |
571 | rflags = 0; |
572 | if (flags & P9_DOTL_AT_REMOVEDIR) { | |
573 | rflags |= AT_REMOVEDIR; | |
574 | } | |
575 | ||
576 | ret = unlinkat(dirfd, name, rflags); | |
577 | ||
5f542225 AK |
578 | close(dirfd); |
579 | return ret; | |
580 | } | |
581 | ||
edb9eb73 HPB |
582 | static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path, |
583 | mode_t st_mode, uint64_t *st_gen) | |
584 | { | |
cc720ddb AK |
585 | int err; |
586 | V9fsFidOpenState fid_open; | |
edb9eb73 HPB |
587 | |
588 | /* | |
589 | * Do not try to open special files like device nodes, fifos etc | |
590 | * We can get fd for regular files and directories only | |
591 | */ | |
592 | if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { | |
593 | return 0; | |
594 | } | |
cc720ddb AK |
595 | err = handle_open(ctx, path, O_RDONLY, &fid_open); |
596 | if (err < 0) { | |
597 | return err; | |
edb9eb73 | 598 | } |
cc720ddb AK |
599 | err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); |
600 | handle_close(ctx, &fid_open); | |
edb9eb73 HPB |
601 | return err; |
602 | } | |
603 | ||
5f542225 AK |
604 | static int handle_init(FsContext *ctx) |
605 | { | |
606 | int ret, mnt_id; | |
edb9eb73 | 607 | struct statfs stbuf; |
5f542225 AK |
608 | struct file_handle fh; |
609 | struct handle_data *data = g_malloc(sizeof(struct handle_data)); | |
d2042378 | 610 | |
5f542225 AK |
611 | data->mountfd = open(ctx->fs_root, O_DIRECTORY); |
612 | if (data->mountfd < 0) { | |
613 | ret = data->mountfd; | |
614 | goto err_out; | |
615 | } | |
edb9eb73 HPB |
616 | ret = statfs(ctx->fs_root, &stbuf); |
617 | if (!ret) { | |
618 | switch (stbuf.f_type) { | |
619 | case EXT2_SUPER_MAGIC: | |
620 | case BTRFS_SUPER_MAGIC: | |
621 | case REISERFS_SUPER_MAGIC: | |
622 | case XFS_SUPER_MAGIC: | |
623 | ctx->exops.get_st_gen = handle_ioc_getversion; | |
624 | break; | |
625 | } | |
626 | } | |
5f542225 AK |
627 | memset(&fh, 0, sizeof(struct file_handle)); |
628 | ret = name_to_handle(data->mountfd, ".", &fh, &mnt_id, 0); | |
629 | if (ret && errno == EOVERFLOW) { | |
630 | data->handle_bytes = fh.handle_bytes; | |
631 | ctx->private = data; | |
632 | ret = 0; | |
633 | goto out; | |
634 | } | |
635 | /* we got 0 byte handle ? */ | |
636 | ret = -1; | |
637 | close(data->mountfd); | |
638 | err_out: | |
639 | g_free(data); | |
640 | out: | |
641 | return ret; | |
642 | } | |
643 | ||
99519f0a AK |
644 | static int handle_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse) |
645 | { | |
646 | const char *sec_model = qemu_opt_get(opts, "security_model"); | |
647 | const char *path = qemu_opt_get(opts, "path"); | |
648 | ||
649 | if (sec_model) { | |
650 | fprintf(stderr, "Invalid argument security_model specified with handle fsdriver\n"); | |
651 | return -1; | |
652 | } | |
653 | ||
654 | if (!path) { | |
655 | fprintf(stderr, "fsdev: No path specified.\n"); | |
656 | return -1; | |
657 | } | |
658 | fse->path = g_strdup(path); | |
659 | return 0; | |
660 | ||
661 | } | |
662 | ||
5f542225 | 663 | FileOperations handle_ops = { |
99519f0a | 664 | .parse_opts = handle_parse_opts, |
5f542225 AK |
665 | .init = handle_init, |
666 | .lstat = handle_lstat, | |
667 | .readlink = handle_readlink, | |
668 | .close = handle_close, | |
669 | .closedir = handle_closedir, | |
670 | .open = handle_open, | |
671 | .opendir = handle_opendir, | |
672 | .rewinddir = handle_rewinddir, | |
673 | .telldir = handle_telldir, | |
674 | .readdir_r = handle_readdir_r, | |
675 | .seekdir = handle_seekdir, | |
676 | .preadv = handle_preadv, | |
677 | .pwritev = handle_pwritev, | |
678 | .chmod = handle_chmod, | |
679 | .mknod = handle_mknod, | |
680 | .mkdir = handle_mkdir, | |
681 | .fstat = handle_fstat, | |
682 | .open2 = handle_open2, | |
683 | .symlink = handle_symlink, | |
684 | .link = handle_link, | |
685 | .truncate = handle_truncate, | |
686 | .rename = handle_rename, | |
687 | .chown = handle_chown, | |
688 | .utimensat = handle_utimensat, | |
689 | .remove = handle_remove, | |
690 | .fsync = handle_fsync, | |
691 | .statfs = handle_statfs, | |
692 | .lgetxattr = handle_lgetxattr, | |
693 | .llistxattr = handle_llistxattr, | |
694 | .lsetxattr = handle_lsetxattr, | |
695 | .lremovexattr = handle_lremovexattr, | |
696 | .name_to_path = handle_name_to_path, | |
697 | .renameat = handle_renameat, | |
698 | .unlinkat = handle_unlinkat, | |
699 | }; |