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