]>
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" |
922a01a0 | 25 | #include "qemu/option.h" |
edb9eb73 HPB |
26 | #include <linux/fs.h> |
27 | #ifdef CONFIG_LINUX_MAGIC_H | |
28 | #include <linux/magic.h> | |
29 | #endif | |
30 | #include <sys/ioctl.h> | |
31 | ||
32 | #ifndef XFS_SUPER_MAGIC | |
33 | #define XFS_SUPER_MAGIC 0x58465342 | |
34 | #endif | |
35 | #ifndef EXT2_SUPER_MAGIC | |
36 | #define EXT2_SUPER_MAGIC 0xEF53 | |
37 | #endif | |
38 | #ifndef REISERFS_SUPER_MAGIC | |
39 | #define REISERFS_SUPER_MAGIC 0x52654973 | |
40 | #endif | |
41 | #ifndef BTRFS_SUPER_MAGIC | |
42 | #define BTRFS_SUPER_MAGIC 0x9123683E | |
43 | #endif | |
5f542225 | 44 | |
c4ce2c0f | 45 | typedef struct HandleData { |
5f542225 AK |
46 | int mountfd; |
47 | int handle_bytes; | |
c4ce2c0f | 48 | } HandleData; |
5f542225 | 49 | |
d2042378 AK |
50 | static inline int name_to_handle(int dirfd, const char *name, |
51 | struct file_handle *fh, int *mnt_id, int flags) | |
52 | { | |
53 | return name_to_handle_at(dirfd, name, fh, mnt_id, flags); | |
54 | } | |
55 | ||
56 | static inline int open_by_handle(int mountfd, const char *fh, int flags) | |
57 | { | |
58 | return open_by_handle_at(mountfd, (struct file_handle *)fh, flags); | |
59 | } | |
5f542225 AK |
60 | |
61 | static int handle_update_file_cred(int dirfd, const char *name, FsCred *credp) | |
62 | { | |
63 | int fd, ret; | |
3a93113a | 64 | fd = openat(dirfd, name, O_NONBLOCK | O_NOFOLLOW); |
5f542225 AK |
65 | if (fd < 0) { |
66 | return fd; | |
67 | } | |
2d40564a | 68 | ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH); |
5f542225 AK |
69 | if (ret < 0) { |
70 | goto err_out; | |
71 | } | |
2d40564a | 72 | ret = fchmod(fd, credp->fc_mode & 07777); |
5f542225 AK |
73 | err_out: |
74 | close(fd); | |
75 | return ret; | |
76 | } | |
77 | ||
78 | ||
79 | static int handle_lstat(FsContext *fs_ctx, V9fsPath *fs_path, | |
80 | struct stat *stbuf) | |
81 | { | |
82 | int fd, ret; | |
c4ce2c0f | 83 | HandleData *data = (HandleData *) fs_ctx->private; |
5f542225 AK |
84 | |
85 | fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); | |
86 | if (fd < 0) { | |
87 | return fd; | |
88 | } | |
89 | ret = fstatat(fd, "", stbuf, AT_EMPTY_PATH); | |
90 | close(fd); | |
91 | return ret; | |
92 | } | |
93 | ||
94 | static ssize_t handle_readlink(FsContext *fs_ctx, V9fsPath *fs_path, | |
95 | char *buf, size_t bufsz) | |
96 | { | |
97 | int fd, ret; | |
c4ce2c0f | 98 | HandleData *data = (HandleData *) fs_ctx->private; |
5f542225 AK |
99 | |
100 | fd = open_by_handle(data->mountfd, fs_path->data, O_PATH); | |
101 | if (fd < 0) { | |
102 | return fd; | |
103 | } | |
104 | ret = readlinkat(fd, "", buf, bufsz); | |
105 | close(fd); | |
106 | return ret; | |
107 | } | |
108 | ||
cc720ddb | 109 | static int handle_close(FsContext *ctx, V9fsFidOpenState *fs) |
5f542225 | 110 | { |
cc720ddb | 111 | return close(fs->fd); |
5f542225 AK |
112 | } |
113 | ||
cc720ddb | 114 | static int handle_closedir(FsContext *ctx, V9fsFidOpenState *fs) |
5f542225 | 115 | { |
f314ea4e | 116 | return closedir(fs->dir.stream); |
5f542225 AK |
117 | } |
118 | ||
cc720ddb AK |
119 | static int handle_open(FsContext *ctx, V9fsPath *fs_path, |
120 | int flags, V9fsFidOpenState *fs) | |
5f542225 | 121 | { |
c4ce2c0f | 122 | HandleData *data = (HandleData *) ctx->private; |
5f542225 | 123 | |
cc720ddb AK |
124 | fs->fd = open_by_handle(data->mountfd, fs_path->data, flags); |
125 | return fs->fd; | |
5f542225 AK |
126 | } |
127 | ||
cc720ddb AK |
128 | static int handle_opendir(FsContext *ctx, |
129 | V9fsPath *fs_path, V9fsFidOpenState *fs) | |
5f542225 | 130 | { |
cc720ddb AK |
131 | int ret; |
132 | ret = handle_open(ctx, fs_path, O_DIRECTORY, fs); | |
133 | if (ret < 0) { | |
134 | return -1; | |
5f542225 | 135 | } |
f314ea4e GK |
136 | fs->dir.stream = fdopendir(ret); |
137 | if (!fs->dir.stream) { | |
cc720ddb AK |
138 | return -1; |
139 | } | |
140 | return 0; | |
5f542225 AK |
141 | } |
142 | ||
cc720ddb | 143 | static void handle_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) |
5f542225 | 144 | { |
f314ea4e | 145 | rewinddir(fs->dir.stream); |
5f542225 AK |
146 | } |
147 | ||
cc720ddb | 148 | static off_t handle_telldir(FsContext *ctx, V9fsFidOpenState *fs) |
5f542225 | 149 | { |
f314ea4e | 150 | return telldir(fs->dir.stream); |
5f542225 AK |
151 | } |
152 | ||
635324e8 | 153 | static struct dirent *handle_readdir(FsContext *ctx, V9fsFidOpenState *fs) |
5f542225 | 154 | { |
635324e8 | 155 | return readdir(fs->dir.stream); |
5f542225 AK |
156 | } |
157 | ||
cc720ddb | 158 | static void handle_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) |
5f542225 | 159 | { |
f314ea4e | 160 | seekdir(fs->dir.stream, 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; | |
c4ce2c0f | 211 | HandleData *data = (HandleData *) fs_ctx->private; |
5f542225 AK |
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; | |
c4ce2c0f | 226 | HandleData *data = (HandleData *) fs_ctx->private; |
5f542225 AK |
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; | |
c4ce2c0f | 244 | HandleData *data = (HandleData *) fs_ctx->private; |
5f542225 AK |
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) { | |
f314ea4e | 264 | fd = dirfd(fs->dir.stream); |
8b888272 AK |
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; | |
c4ce2c0f | 276 | HandleData *data = (HandleData *) fs_ctx->private; |
5f542225 AK |
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; | |
c4ce2c0f | 301 | HandleData *data = (HandleData *) fs_ctx->private; |
5f542225 AK |
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; | |
c4ce2c0f | 326 | HandleData *data = (HandleData *) ctx->private; |
5f542225 AK |
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; | |
c4ce2c0f | 346 | HandleData *data = (HandleData *) ctx->private; |
5f542225 AK |
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; | |
c4ce2c0f | 367 | HandleData *data = (HandleData *) fs_ctx->private; |
5f542225 AK |
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 | 381 | int ret; |
d2042378 | 382 | int fd; |
c4ce2c0f | 383 | HandleData *data = (HandleData *) ctx->private; |
5f542225 AK |
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); | |
391 | return ret; | |
392 | } | |
393 | ||
394 | static int handle_remove(FsContext *ctx, const char *path) | |
395 | { | |
396 | errno = EOPNOTSUPP; | |
397 | return -1; | |
398 | } | |
399 | ||
8b888272 AK |
400 | static int handle_fsync(FsContext *ctx, int fid_type, |
401 | V9fsFidOpenState *fs, int datasync) | |
5f542225 | 402 | { |
8b888272 AK |
403 | int fd; |
404 | ||
405 | if (fid_type == P9_FID_DIR) { | |
f314ea4e | 406 | fd = dirfd(fs->dir.stream); |
8b888272 AK |
407 | } else { |
408 | fd = fs->fd; | |
409 | } | |
410 | ||
5f542225 | 411 | if (datasync) { |
8b888272 | 412 | return qemu_fdatasync(fd); |
5f542225 | 413 | } else { |
8b888272 | 414 | return fsync(fd); |
5f542225 AK |
415 | } |
416 | } | |
417 | ||
418 | static int handle_statfs(FsContext *ctx, V9fsPath *fs_path, | |
419 | struct statfs *stbuf) | |
420 | { | |
421 | int fd, ret; | |
c4ce2c0f | 422 | HandleData *data = (HandleData *) ctx->private; |
5f542225 AK |
423 | |
424 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
425 | if (fd < 0) { | |
426 | return fd; | |
427 | } | |
428 | ret = fstatfs(fd, stbuf); | |
429 | close(fd); | |
430 | return ret; | |
431 | } | |
432 | ||
433 | static ssize_t handle_lgetxattr(FsContext *ctx, V9fsPath *fs_path, | |
434 | const char *name, void *value, size_t size) | |
435 | { | |
436 | int fd, ret; | |
c4ce2c0f | 437 | HandleData *data = (HandleData *) ctx->private; |
5f542225 AK |
438 | |
439 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
440 | if (fd < 0) { | |
441 | return fd; | |
442 | } | |
443 | ret = fgetxattr(fd, name, value, size); | |
444 | close(fd); | |
445 | return ret; | |
446 | } | |
447 | ||
448 | static ssize_t handle_llistxattr(FsContext *ctx, V9fsPath *fs_path, | |
449 | void *value, size_t size) | |
450 | { | |
451 | int fd, ret; | |
c4ce2c0f | 452 | HandleData *data = (HandleData *) ctx->private; |
5f542225 AK |
453 | |
454 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
455 | if (fd < 0) { | |
456 | return fd; | |
457 | } | |
458 | ret = flistxattr(fd, value, size); | |
459 | close(fd); | |
460 | return ret; | |
461 | } | |
462 | ||
463 | static int handle_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, | |
464 | void *value, size_t size, int flags) | |
465 | { | |
466 | int fd, ret; | |
c4ce2c0f | 467 | HandleData *data = (HandleData *) ctx->private; |
5f542225 AK |
468 | |
469 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
470 | if (fd < 0) { | |
471 | return fd; | |
472 | } | |
473 | ret = fsetxattr(fd, name, value, size, flags); | |
474 | close(fd); | |
475 | return ret; | |
476 | } | |
477 | ||
478 | static int handle_lremovexattr(FsContext *ctx, V9fsPath *fs_path, | |
479 | const char *name) | |
480 | { | |
481 | int fd, ret; | |
c4ce2c0f | 482 | HandleData *data = (HandleData *) ctx->private; |
5f542225 AK |
483 | |
484 | fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); | |
485 | if (fd < 0) { | |
486 | return fd; | |
487 | } | |
488 | ret = fremovexattr(fd, name); | |
489 | close(fd); | |
490 | return ret; | |
491 | } | |
492 | ||
493 | static int handle_name_to_path(FsContext *ctx, V9fsPath *dir_path, | |
494 | const char *name, V9fsPath *target) | |
495 | { | |
4fa4ce71 | 496 | char *buffer; |
5f542225 AK |
497 | struct file_handle *fh; |
498 | int dirfd, ret, mnt_id; | |
c4ce2c0f | 499 | HandleData *data = (HandleData *) ctx->private; |
5f542225 AK |
500 | |
501 | /* "." and ".." are not allowed */ | |
502 | if (!strcmp(name, ".") || !strcmp(name, "..")) { | |
503 | errno = EINVAL; | |
504 | return -1; | |
505 | ||
506 | } | |
507 | if (dir_path) { | |
508 | dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH); | |
509 | } else { | |
510 | /* relative to export root */ | |
4fa4ce71 CG |
511 | buffer = rpath(ctx, "."); |
512 | dirfd = open(buffer, O_DIRECTORY); | |
513 | g_free(buffer); | |
5f542225 AK |
514 | } |
515 | if (dirfd < 0) { | |
516 | return dirfd; | |
517 | } | |
518 | fh = g_malloc(sizeof(struct file_handle) + data->handle_bytes); | |
519 | fh->handle_bytes = data->handle_bytes; | |
66a0a2cb | 520 | /* add a "./" at the beginning of the path */ |
4fa4ce71 | 521 | buffer = g_strdup_printf("./%s", name); |
5f542225 AK |
522 | /* flag = 0 imply don't follow symlink */ |
523 | ret = name_to_handle(dirfd, buffer, fh, &mnt_id, 0); | |
524 | if (!ret) { | |
525 | target->data = (char *)fh; | |
526 | target->size = sizeof(struct file_handle) + data->handle_bytes; | |
527 | } else { | |
528 | g_free(fh); | |
529 | } | |
530 | close(dirfd); | |
4fa4ce71 | 531 | g_free(buffer); |
5f542225 AK |
532 | return ret; |
533 | } | |
534 | ||
535 | static int handle_renameat(FsContext *ctx, V9fsPath *olddir, | |
536 | const char *old_name, V9fsPath *newdir, | |
537 | const char *new_name) | |
538 | { | |
539 | int olddirfd, newdirfd, ret; | |
c4ce2c0f | 540 | HandleData *data = (HandleData *) ctx->private; |
5f542225 AK |
541 | |
542 | olddirfd = open_by_handle(data->mountfd, olddir->data, O_PATH); | |
543 | if (olddirfd < 0) { | |
544 | return olddirfd; | |
545 | } | |
546 | newdirfd = open_by_handle(data->mountfd, newdir->data, O_PATH); | |
547 | if (newdirfd < 0) { | |
548 | close(olddirfd); | |
549 | return newdirfd; | |
550 | } | |
551 | ret = renameat(olddirfd, old_name, newdirfd, new_name); | |
552 | close(newdirfd); | |
553 | close(olddirfd); | |
554 | return ret; | |
555 | } | |
556 | ||
557 | static int handle_unlinkat(FsContext *ctx, V9fsPath *dir, | |
558 | const char *name, int flags) | |
559 | { | |
560 | int dirfd, ret; | |
c4ce2c0f | 561 | HandleData *data = (HandleData *) ctx->private; |
5f542225 AK |
562 | |
563 | dirfd = open_by_handle(data->mountfd, dir->data, O_PATH); | |
564 | if (dirfd < 0) { | |
565 | return dirfd; | |
566 | } | |
567 | ||
67e87345 | 568 | ret = unlinkat(dirfd, name, flags); |
930b5883 | 569 | |
5f542225 AK |
570 | close(dirfd); |
571 | return ret; | |
572 | } | |
573 | ||
edb9eb73 HPB |
574 | static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path, |
575 | mode_t st_mode, uint64_t *st_gen) | |
576 | { | |
b9317661 | 577 | #ifdef FS_IOC_GETVERSION |
cc720ddb AK |
578 | int err; |
579 | V9fsFidOpenState fid_open; | |
edb9eb73 HPB |
580 | |
581 | /* | |
582 | * Do not try to open special files like device nodes, fifos etc | |
583 | * We can get fd for regular files and directories only | |
584 | */ | |
585 | if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { | |
1a9978a5 KS |
586 | errno = ENOTTY; |
587 | return -1; | |
edb9eb73 | 588 | } |
cc720ddb AK |
589 | err = handle_open(ctx, path, O_RDONLY, &fid_open); |
590 | if (err < 0) { | |
591 | return err; | |
edb9eb73 | 592 | } |
cc720ddb AK |
593 | err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); |
594 | handle_close(ctx, &fid_open); | |
edb9eb73 | 595 | return err; |
b9317661 KS |
596 | #else |
597 | errno = ENOTTY; | |
598 | return -1; | |
599 | #endif | |
edb9eb73 HPB |
600 | } |
601 | ||
65603a80 | 602 | static int handle_init(FsContext *ctx, Error **errp) |
5f542225 AK |
603 | { |
604 | int ret, mnt_id; | |
edb9eb73 | 605 | struct statfs stbuf; |
5f542225 | 606 | struct file_handle fh; |
c4ce2c0f | 607 | HandleData *data = g_malloc(sizeof(HandleData)); |
d2042378 | 608 | |
5f542225 AK |
609 | data->mountfd = open(ctx->fs_root, O_DIRECTORY); |
610 | if (data->mountfd < 0) { | |
611 | ret = data->mountfd; | |
612 | goto err_out; | |
613 | } | |
edb9eb73 HPB |
614 | ret = statfs(ctx->fs_root, &stbuf); |
615 | if (!ret) { | |
616 | switch (stbuf.f_type) { | |
617 | case EXT2_SUPER_MAGIC: | |
618 | case BTRFS_SUPER_MAGIC: | |
619 | case REISERFS_SUPER_MAGIC: | |
620 | case XFS_SUPER_MAGIC: | |
621 | ctx->exops.get_st_gen = handle_ioc_getversion; | |
622 | break; | |
623 | } | |
624 | } | |
5f542225 AK |
625 | memset(&fh, 0, sizeof(struct file_handle)); |
626 | ret = name_to_handle(data->mountfd, ".", &fh, &mnt_id, 0); | |
627 | if (ret && errno == EOVERFLOW) { | |
628 | data->handle_bytes = fh.handle_bytes; | |
629 | ctx->private = data; | |
630 | ret = 0; | |
631 | goto out; | |
632 | } | |
633 | /* we got 0 byte handle ? */ | |
634 | ret = -1; | |
635 | close(data->mountfd); | |
636 | err_out: | |
637 | g_free(data); | |
638 | out: | |
639 | return ret; | |
640 | } | |
641 | ||
971f406b LQ |
642 | static void handle_cleanup(FsContext *ctx) |
643 | { | |
c4ce2c0f | 644 | HandleData *data = ctx->private; |
971f406b LQ |
645 | |
646 | close(data->mountfd); | |
647 | g_free(data); | |
648 | } | |
649 | ||
91cda4e8 | 650 | static int handle_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) |
99519f0a AK |
651 | { |
652 | const char *sec_model = qemu_opt_get(opts, "security_model"); | |
653 | const char *path = qemu_opt_get(opts, "path"); | |
654 | ||
db3b3c72 GK |
655 | warn_report("handle backend is deprecated"); |
656 | ||
99519f0a | 657 | if (sec_model) { |
63325b18 | 658 | error_report("Invalid argument security_model specified with handle fsdriver"); |
99519f0a AK |
659 | return -1; |
660 | } | |
661 | ||
662 | if (!path) { | |
63325b18 | 663 | error_report("fsdev: No path specified"); |
99519f0a AK |
664 | return -1; |
665 | } | |
666 | fse->path = g_strdup(path); | |
667 | return 0; | |
668 | ||
669 | } | |
670 | ||
5f542225 | 671 | FileOperations handle_ops = { |
99519f0a | 672 | .parse_opts = handle_parse_opts, |
5f542225 | 673 | .init = handle_init, |
971f406b | 674 | .cleanup = handle_cleanup, |
5f542225 AK |
675 | .lstat = handle_lstat, |
676 | .readlink = handle_readlink, | |
677 | .close = handle_close, | |
678 | .closedir = handle_closedir, | |
679 | .open = handle_open, | |
680 | .opendir = handle_opendir, | |
681 | .rewinddir = handle_rewinddir, | |
682 | .telldir = handle_telldir, | |
635324e8 | 683 | .readdir = handle_readdir, |
5f542225 AK |
684 | .seekdir = handle_seekdir, |
685 | .preadv = handle_preadv, | |
686 | .pwritev = handle_pwritev, | |
687 | .chmod = handle_chmod, | |
688 | .mknod = handle_mknod, | |
689 | .mkdir = handle_mkdir, | |
690 | .fstat = handle_fstat, | |
691 | .open2 = handle_open2, | |
692 | .symlink = handle_symlink, | |
693 | .link = handle_link, | |
694 | .truncate = handle_truncate, | |
695 | .rename = handle_rename, | |
696 | .chown = handle_chown, | |
697 | .utimensat = handle_utimensat, | |
698 | .remove = handle_remove, | |
699 | .fsync = handle_fsync, | |
700 | .statfs = handle_statfs, | |
701 | .lgetxattr = handle_lgetxattr, | |
702 | .llistxattr = handle_llistxattr, | |
703 | .lsetxattr = handle_lsetxattr, | |
704 | .lremovexattr = handle_lremovexattr, | |
705 | .name_to_path = handle_name_to_path, | |
706 | .renameat = handle_renameat, | |
707 | .unlinkat = handle_unlinkat, | |
708 | }; |