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