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