]> Git Repo - qemu.git/blob - hw/9pfs/9p-local.c
9pfs: local: lstat: don't follow symlinks
[qemu.git] / hw / 9pfs / 9p-local.c
1 /*
2  * 9p Posix callback
3  *
4  * Copyright IBM, Corp. 2010
5  *
6  * Authors:
7  *  Anthony Liguori   <[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 "qemu/osdep.h"
15 #include "9p.h"
16 #include "9p-local.h"
17 #include "9p-xattr.h"
18 #include "9p-util.h"
19 #include "fsdev/qemu-fsdev.h"   /* local_ops */
20 #include <arpa/inet.h>
21 #include <pwd.h>
22 #include <grp.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include "qemu/xattr.h"
26 #include "qemu/cutils.h"
27 #include "qemu/error-report.h"
28 #include <libgen.h>
29 #include <linux/fs.h>
30 #ifdef CONFIG_LINUX_MAGIC_H
31 #include <linux/magic.h>
32 #endif
33 #include <sys/ioctl.h>
34
35 #ifndef XFS_SUPER_MAGIC
36 #define XFS_SUPER_MAGIC  0x58465342
37 #endif
38 #ifndef EXT2_SUPER_MAGIC
39 #define EXT2_SUPER_MAGIC 0xEF53
40 #endif
41 #ifndef REISERFS_SUPER_MAGIC
42 #define REISERFS_SUPER_MAGIC 0x52654973
43 #endif
44 #ifndef BTRFS_SUPER_MAGIC
45 #define BTRFS_SUPER_MAGIC 0x9123683E
46 #endif
47
48 typedef struct {
49     int mountfd;
50 } LocalData;
51
52 int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
53                         mode_t mode)
54 {
55     LocalData *data = fs_ctx->private;
56
57     /* All paths are relative to the path data->mountfd points to */
58     while (*path == '/') {
59         path++;
60     }
61
62     return relative_openat_nofollow(data->mountfd, path, flags, mode);
63 }
64
65 int local_opendir_nofollow(FsContext *fs_ctx, const char *path)
66 {
67     return local_open_nofollow(fs_ctx, path, O_DIRECTORY | O_RDONLY, 0);
68 }
69
70 #define VIRTFS_META_DIR ".virtfs_metadata"
71
72 static char *local_mapped_attr_path(FsContext *ctx, const char *path)
73 {
74     int dirlen;
75     const char *name = strrchr(path, '/');
76     if (name) {
77         dirlen = name - path;
78         ++name;
79     } else {
80         name = path;
81         dirlen = 0;
82     }
83     return g_strdup_printf("%s/%.*s/%s/%s", ctx->fs_root,
84                            dirlen, path, VIRTFS_META_DIR, name);
85 }
86
87 static FILE *local_fopen(const char *path, const char *mode)
88 {
89     int fd, o_mode = 0;
90     FILE *fp;
91     int flags = O_NOFOLLOW;
92     /*
93      * only supports two modes
94      */
95     if (mode[0] == 'r') {
96         flags |= O_RDONLY;
97     } else if (mode[0] == 'w') {
98         flags |= O_WRONLY | O_TRUNC | O_CREAT;
99         o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
100     } else {
101         return NULL;
102     }
103     fd = open(path, flags, o_mode);
104     if (fd == -1) {
105         return NULL;
106     }
107     fp = fdopen(fd, mode);
108     if (!fp) {
109         close(fd);
110     }
111     return fp;
112 }
113
114 static FILE *local_fopenat(int dirfd, const char *name, const char *mode)
115 {
116     int fd, o_mode = 0;
117     FILE *fp;
118     int flags;
119     /*
120      * only supports two modes
121      */
122     if (mode[0] == 'r') {
123         flags = O_RDONLY;
124     } else if (mode[0] == 'w') {
125         flags = O_WRONLY | O_TRUNC | O_CREAT;
126         o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
127     } else {
128         return NULL;
129     }
130     fd = openat_file(dirfd, name, flags, o_mode);
131     if (fd == -1) {
132         return NULL;
133     }
134     fp = fdopen(fd, mode);
135     if (!fp) {
136         close(fd);
137     }
138     return fp;
139 }
140
141 #define ATTR_MAX 100
142 static void local_mapped_file_attr(int dirfd, const char *name,
143                                    struct stat *stbuf)
144 {
145     FILE *fp;
146     char buf[ATTR_MAX];
147     int map_dirfd;
148
149     map_dirfd = openat(dirfd, VIRTFS_META_DIR,
150                        O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
151     if (map_dirfd == -1) {
152         return;
153     }
154
155     fp = local_fopenat(map_dirfd, name, "r");
156     close_preserve_errno(map_dirfd);
157     if (!fp) {
158         return;
159     }
160     memset(buf, 0, ATTR_MAX);
161     while (fgets(buf, ATTR_MAX, fp)) {
162         if (!strncmp(buf, "virtfs.uid", 10)) {
163             stbuf->st_uid = atoi(buf+11);
164         } else if (!strncmp(buf, "virtfs.gid", 10)) {
165             stbuf->st_gid = atoi(buf+11);
166         } else if (!strncmp(buf, "virtfs.mode", 11)) {
167             stbuf->st_mode = atoi(buf+12);
168         } else if (!strncmp(buf, "virtfs.rdev", 11)) {
169             stbuf->st_rdev = atoi(buf+12);
170         }
171         memset(buf, 0, ATTR_MAX);
172     }
173     fclose(fp);
174 }
175
176 static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
177 {
178     int err = -1;
179     char *dirpath = g_path_get_dirname(fs_path->data);
180     char *name = g_path_get_basename(fs_path->data);
181     int dirfd;
182
183     dirfd = local_opendir_nofollow(fs_ctx, dirpath);
184     if (dirfd == -1) {
185         goto out;
186     }
187
188     err = fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW);
189     if (err) {
190         goto err_out;
191     }
192     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
193         /* Actual credentials are part of extended attrs */
194         uid_t tmp_uid;
195         gid_t tmp_gid;
196         mode_t tmp_mode;
197         dev_t tmp_dev;
198
199         if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.uid", &tmp_uid,
200                                  sizeof(uid_t)) > 0) {
201             stbuf->st_uid = le32_to_cpu(tmp_uid);
202         }
203         if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.gid", &tmp_gid,
204                                  sizeof(gid_t)) > 0) {
205             stbuf->st_gid = le32_to_cpu(tmp_gid);
206         }
207         if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.mode", &tmp_mode,
208                                  sizeof(mode_t)) > 0) {
209             stbuf->st_mode = le32_to_cpu(tmp_mode);
210         }
211         if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.rdev", &tmp_dev,
212                                  sizeof(dev_t)) > 0) {
213             stbuf->st_rdev = le64_to_cpu(tmp_dev);
214         }
215     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
216         local_mapped_file_attr(dirfd, name, stbuf);
217     }
218
219 err_out:
220     close_preserve_errno(dirfd);
221 out:
222     g_free(name);
223     g_free(dirpath);
224     return err;
225 }
226
227 static int local_create_mapped_attr_dir(FsContext *ctx, const char *path)
228 {
229     int err;
230     char *attr_dir;
231     char *tmp_path = g_strdup(path);
232
233     attr_dir = g_strdup_printf("%s/%s/%s",
234              ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR);
235
236     err = mkdir(attr_dir, 0700);
237     if (err < 0 && errno == EEXIST) {
238         err = 0;
239     }
240     g_free(attr_dir);
241     g_free(tmp_path);
242     return err;
243 }
244
245 static int local_set_mapped_file_attr(FsContext *ctx,
246                                       const char *path, FsCred *credp)
247 {
248     FILE *fp;
249     int ret = 0;
250     char buf[ATTR_MAX];
251     char *attr_path;
252     int uid = -1, gid = -1, mode = -1, rdev = -1;
253
254     attr_path = local_mapped_attr_path(ctx, path);
255     fp = local_fopen(attr_path, "r");
256     if (!fp) {
257         goto create_map_file;
258     }
259     memset(buf, 0, ATTR_MAX);
260     while (fgets(buf, ATTR_MAX, fp)) {
261         if (!strncmp(buf, "virtfs.uid", 10)) {
262             uid = atoi(buf+11);
263         } else if (!strncmp(buf, "virtfs.gid", 10)) {
264             gid = atoi(buf+11);
265         } else if (!strncmp(buf, "virtfs.mode", 11)) {
266             mode = atoi(buf+12);
267         } else if (!strncmp(buf, "virtfs.rdev", 11)) {
268             rdev = atoi(buf+12);
269         }
270         memset(buf, 0, ATTR_MAX);
271     }
272     fclose(fp);
273     goto update_map_file;
274
275 create_map_file:
276     ret = local_create_mapped_attr_dir(ctx, path);
277     if (ret < 0) {
278         goto err_out;
279     }
280
281 update_map_file:
282     fp = local_fopen(attr_path, "w");
283     if (!fp) {
284         ret = -1;
285         goto err_out;
286     }
287
288     if (credp->fc_uid != -1) {
289         uid = credp->fc_uid;
290     }
291     if (credp->fc_gid != -1) {
292         gid = credp->fc_gid;
293     }
294     if (credp->fc_mode != -1) {
295         mode = credp->fc_mode;
296     }
297     if (credp->fc_rdev != -1) {
298         rdev = credp->fc_rdev;
299     }
300
301
302     if (uid != -1) {
303         fprintf(fp, "virtfs.uid=%d\n", uid);
304     }
305     if (gid != -1) {
306         fprintf(fp, "virtfs.gid=%d\n", gid);
307     }
308     if (mode != -1) {
309         fprintf(fp, "virtfs.mode=%d\n", mode);
310     }
311     if (rdev != -1) {
312         fprintf(fp, "virtfs.rdev=%d\n", rdev);
313     }
314     fclose(fp);
315
316 err_out:
317     g_free(attr_path);
318     return ret;
319 }
320
321 static int local_set_xattr(const char *path, FsCred *credp)
322 {
323     int err;
324
325     if (credp->fc_uid != -1) {
326         uint32_t tmp_uid = cpu_to_le32(credp->fc_uid);
327         err = setxattr(path, "user.virtfs.uid", &tmp_uid, sizeof(uid_t), 0);
328         if (err) {
329             return err;
330         }
331     }
332     if (credp->fc_gid != -1) {
333         uint32_t tmp_gid = cpu_to_le32(credp->fc_gid);
334         err = setxattr(path, "user.virtfs.gid", &tmp_gid, sizeof(gid_t), 0);
335         if (err) {
336             return err;
337         }
338     }
339     if (credp->fc_mode != -1) {
340         uint32_t tmp_mode = cpu_to_le32(credp->fc_mode);
341         err = setxattr(path, "user.virtfs.mode", &tmp_mode, sizeof(mode_t), 0);
342         if (err) {
343             return err;
344         }
345     }
346     if (credp->fc_rdev != -1) {
347         uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev);
348         err = setxattr(path, "user.virtfs.rdev", &tmp_rdev, sizeof(dev_t), 0);
349         if (err) {
350             return err;
351         }
352     }
353     return 0;
354 }
355
356 static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
357                                          FsCred *credp)
358 {
359     char *buffer;
360
361     buffer = rpath(fs_ctx, path);
362     if (lchown(buffer, credp->fc_uid, credp->fc_gid) < 0) {
363         /*
364          * If we fail to change ownership and if we are
365          * using security model none. Ignore the error
366          */
367         if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
368             goto err;
369         }
370     }
371
372     if (chmod(buffer, credp->fc_mode & 07777) < 0) {
373         goto err;
374     }
375
376     g_free(buffer);
377     return 0;
378 err:
379     g_free(buffer);
380     return -1;
381 }
382
383 static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
384                               char *buf, size_t bufsz)
385 {
386     ssize_t tsize = -1;
387
388     if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
389         (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
390         int fd;
391
392         fd = local_open_nofollow(fs_ctx, fs_path->data, O_RDONLY, 0);
393         if (fd == -1) {
394             return -1;
395         }
396         do {
397             tsize = read(fd, (void *)buf, bufsz);
398         } while (tsize == -1 && errno == EINTR);
399         close_preserve_errno(fd);
400     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
401                (fs_ctx->export_flags & V9FS_SM_NONE)) {
402         char *dirpath = g_path_get_dirname(fs_path->data);
403         char *name = g_path_get_basename(fs_path->data);
404         int dirfd;
405
406         dirfd = local_opendir_nofollow(fs_ctx, dirpath);
407         if (dirfd == -1) {
408             goto out;
409         }
410
411         tsize = readlinkat(dirfd, name, buf, bufsz);
412         close_preserve_errno(dirfd);
413     out:
414         g_free(name);
415         g_free(dirpath);
416     }
417     return tsize;
418 }
419
420 static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
421 {
422     return close(fs->fd);
423 }
424
425 static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
426 {
427     return closedir(fs->dir.stream);
428 }
429
430 static int local_open(FsContext *ctx, V9fsPath *fs_path,
431                       int flags, V9fsFidOpenState *fs)
432 {
433     int fd;
434
435     fd = local_open_nofollow(ctx, fs_path->data, flags, 0);
436     if (fd == -1) {
437         return -1;
438     }
439     fs->fd = fd;
440     return fs->fd;
441 }
442
443 static int local_opendir(FsContext *ctx,
444                          V9fsPath *fs_path, V9fsFidOpenState *fs)
445 {
446     int dirfd;
447     DIR *stream;
448
449     dirfd = local_opendir_nofollow(ctx, fs_path->data);
450     if (dirfd == -1) {
451         return -1;
452     }
453
454     stream = fdopendir(dirfd);
455     if (!stream) {
456         return -1;
457     }
458     fs->dir.stream = stream;
459     return 0;
460 }
461
462 static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
463 {
464     rewinddir(fs->dir.stream);
465 }
466
467 static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
468 {
469     return telldir(fs->dir.stream);
470 }
471
472 static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs)
473 {
474     struct dirent *entry;
475
476 again:
477     entry = readdir(fs->dir.stream);
478     if (!entry) {
479         return NULL;
480     }
481
482     if (ctx->export_flags & V9FS_SM_MAPPED) {
483         entry->d_type = DT_UNKNOWN;
484     } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
485         if (!strcmp(entry->d_name, VIRTFS_META_DIR)) {
486             /* skp the meta data directory */
487             goto again;
488         }
489         entry->d_type = DT_UNKNOWN;
490     }
491
492     return entry;
493 }
494
495 static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
496 {
497     seekdir(fs->dir.stream, off);
498 }
499
500 static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
501                             const struct iovec *iov,
502                             int iovcnt, off_t offset)
503 {
504 #ifdef CONFIG_PREADV
505     return preadv(fs->fd, iov, iovcnt, offset);
506 #else
507     int err = lseek(fs->fd, offset, SEEK_SET);
508     if (err == -1) {
509         return err;
510     } else {
511         return readv(fs->fd, iov, iovcnt);
512     }
513 #endif
514 }
515
516 static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
517                              const struct iovec *iov,
518                              int iovcnt, off_t offset)
519 {
520     ssize_t ret;
521 #ifdef CONFIG_PREADV
522     ret = pwritev(fs->fd, iov, iovcnt, offset);
523 #else
524     int err = lseek(fs->fd, offset, SEEK_SET);
525     if (err == -1) {
526         return err;
527     } else {
528         ret = writev(fs->fd, iov, iovcnt);
529     }
530 #endif
531 #ifdef CONFIG_SYNC_FILE_RANGE
532     if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
533         /*
534          * Initiate a writeback. This is not a data integrity sync.
535          * We want to ensure that we don't leave dirty pages in the cache
536          * after write when writeout=immediate is sepcified.
537          */
538         sync_file_range(fs->fd, offset, ret,
539                         SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
540     }
541 #endif
542     return ret;
543 }
544
545 static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
546 {
547     char *buffer;
548     int ret = -1;
549     char *path = fs_path->data;
550
551     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
552         buffer = rpath(fs_ctx, path);
553         ret = local_set_xattr(buffer, credp);
554         g_free(buffer);
555     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
556         return local_set_mapped_file_attr(fs_ctx, path, credp);
557     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
558                (fs_ctx->export_flags & V9FS_SM_NONE)) {
559         buffer = rpath(fs_ctx, path);
560         ret = chmod(buffer, credp->fc_mode);
561         g_free(buffer);
562     }
563     return ret;
564 }
565
566 static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
567                        const char *name, FsCred *credp)
568 {
569     char *path;
570     int err = -1;
571     int serrno = 0;
572     V9fsString fullname;
573     char *buffer = NULL;
574
575     v9fs_string_init(&fullname);
576     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
577     path = fullname.data;
578
579     /* Determine the security model */
580     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
581         buffer = rpath(fs_ctx, path);
582         err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
583         if (err == -1) {
584             goto out;
585         }
586         err = local_set_xattr(buffer, credp);
587         if (err == -1) {
588             serrno = errno;
589             goto err_end;
590         }
591     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
592
593         buffer = rpath(fs_ctx, path);
594         err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
595         if (err == -1) {
596             goto out;
597         }
598         err = local_set_mapped_file_attr(fs_ctx, path, credp);
599         if (err == -1) {
600             serrno = errno;
601             goto err_end;
602         }
603     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
604                (fs_ctx->export_flags & V9FS_SM_NONE)) {
605         buffer = rpath(fs_ctx, path);
606         err = mknod(buffer, credp->fc_mode, credp->fc_rdev);
607         if (err == -1) {
608             goto out;
609         }
610         err = local_post_create_passthrough(fs_ctx, path, credp);
611         if (err == -1) {
612             serrno = errno;
613             goto err_end;
614         }
615     }
616     goto out;
617
618 err_end:
619     remove(buffer);
620     errno = serrno;
621 out:
622     g_free(buffer);
623     v9fs_string_free(&fullname);
624     return err;
625 }
626
627 static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
628                        const char *name, FsCred *credp)
629 {
630     char *path;
631     int err = -1;
632     int serrno = 0;
633     V9fsString fullname;
634     char *buffer = NULL;
635
636     v9fs_string_init(&fullname);
637     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
638     path = fullname.data;
639
640     /* Determine the security model */
641     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
642         buffer = rpath(fs_ctx, path);
643         err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
644         if (err == -1) {
645             goto out;
646         }
647         credp->fc_mode = credp->fc_mode|S_IFDIR;
648         err = local_set_xattr(buffer, credp);
649         if (err == -1) {
650             serrno = errno;
651             goto err_end;
652         }
653     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
654         buffer = rpath(fs_ctx, path);
655         err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
656         if (err == -1) {
657             goto out;
658         }
659         credp->fc_mode = credp->fc_mode|S_IFDIR;
660         err = local_set_mapped_file_attr(fs_ctx, path, credp);
661         if (err == -1) {
662             serrno = errno;
663             goto err_end;
664         }
665     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
666                (fs_ctx->export_flags & V9FS_SM_NONE)) {
667         buffer = rpath(fs_ctx, path);
668         err = mkdir(buffer, credp->fc_mode);
669         if (err == -1) {
670             goto out;
671         }
672         err = local_post_create_passthrough(fs_ctx, path, credp);
673         if (err == -1) {
674             serrno = errno;
675             goto err_end;
676         }
677     }
678     goto out;
679
680 err_end:
681     remove(buffer);
682     errno = serrno;
683 out:
684     g_free(buffer);
685     v9fs_string_free(&fullname);
686     return err;
687 }
688
689 static int local_fstat(FsContext *fs_ctx, int fid_type,
690                        V9fsFidOpenState *fs, struct stat *stbuf)
691 {
692     int err, fd;
693
694     if (fid_type == P9_FID_DIR) {
695         fd = dirfd(fs->dir.stream);
696     } else {
697         fd = fs->fd;
698     }
699
700     err = fstat(fd, stbuf);
701     if (err) {
702         return err;
703     }
704     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
705         /* Actual credentials are part of extended attrs */
706         uid_t tmp_uid;
707         gid_t tmp_gid;
708         mode_t tmp_mode;
709         dev_t tmp_dev;
710
711         if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
712             stbuf->st_uid = le32_to_cpu(tmp_uid);
713         }
714         if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
715             stbuf->st_gid = le32_to_cpu(tmp_gid);
716         }
717         if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
718             stbuf->st_mode = le32_to_cpu(tmp_mode);
719         }
720         if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
721             stbuf->st_rdev = le64_to_cpu(tmp_dev);
722         }
723     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
724         errno = EOPNOTSUPP;
725         return -1;
726     }
727     return err;
728 }
729
730 static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
731                        int flags, FsCred *credp, V9fsFidOpenState *fs)
732 {
733     char *path;
734     int fd = -1;
735     int err = -1;
736     int serrno = 0;
737     V9fsString fullname;
738     char *buffer = NULL;
739
740     /*
741      * Mark all the open to not follow symlinks
742      */
743     flags |= O_NOFOLLOW;
744
745     v9fs_string_init(&fullname);
746     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
747     path = fullname.data;
748
749     /* Determine the security model */
750     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
751         buffer = rpath(fs_ctx, path);
752         fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
753         if (fd == -1) {
754             err = fd;
755             goto out;
756         }
757         credp->fc_mode = credp->fc_mode|S_IFREG;
758         /* Set cleint credentials in xattr */
759         err = local_set_xattr(buffer, credp);
760         if (err == -1) {
761             serrno = errno;
762             goto err_end;
763         }
764     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
765         buffer = rpath(fs_ctx, path);
766         fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
767         if (fd == -1) {
768             err = fd;
769             goto out;
770         }
771         credp->fc_mode = credp->fc_mode|S_IFREG;
772         /* Set client credentials in .virtfs_metadata directory files */
773         err = local_set_mapped_file_attr(fs_ctx, path, credp);
774         if (err == -1) {
775             serrno = errno;
776             goto err_end;
777         }
778     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
779                (fs_ctx->export_flags & V9FS_SM_NONE)) {
780         buffer = rpath(fs_ctx, path);
781         fd = open(buffer, flags, credp->fc_mode);
782         if (fd == -1) {
783             err = fd;
784             goto out;
785         }
786         err = local_post_create_passthrough(fs_ctx, path, credp);
787         if (err == -1) {
788             serrno = errno;
789             goto err_end;
790         }
791     }
792     err = fd;
793     fs->fd = fd;
794     goto out;
795
796 err_end:
797     close(fd);
798     remove(buffer);
799     errno = serrno;
800 out:
801     g_free(buffer);
802     v9fs_string_free(&fullname);
803     return err;
804 }
805
806
807 static int local_symlink(FsContext *fs_ctx, const char *oldpath,
808                          V9fsPath *dir_path, const char *name, FsCred *credp)
809 {
810     int err = -1;
811     int serrno = 0;
812     char *newpath;
813     V9fsString fullname;
814     char *buffer = NULL;
815
816     v9fs_string_init(&fullname);
817     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
818     newpath = fullname.data;
819
820     /* Determine the security model */
821     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
822         int fd;
823         ssize_t oldpath_size, write_size;
824         buffer = rpath(fs_ctx, newpath);
825         fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
826         if (fd == -1) {
827             err = fd;
828             goto out;
829         }
830         /* Write the oldpath (target) to the file. */
831         oldpath_size = strlen(oldpath);
832         do {
833             write_size = write(fd, (void *)oldpath, oldpath_size);
834         } while (write_size == -1 && errno == EINTR);
835
836         if (write_size != oldpath_size) {
837             serrno = errno;
838             close(fd);
839             err = -1;
840             goto err_end;
841         }
842         close(fd);
843         /* Set cleint credentials in symlink's xattr */
844         credp->fc_mode = credp->fc_mode|S_IFLNK;
845         err = local_set_xattr(buffer, credp);
846         if (err == -1) {
847             serrno = errno;
848             goto err_end;
849         }
850     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
851         int fd;
852         ssize_t oldpath_size, write_size;
853         buffer = rpath(fs_ctx, newpath);
854         fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
855         if (fd == -1) {
856             err = fd;
857             goto out;
858         }
859         /* Write the oldpath (target) to the file. */
860         oldpath_size = strlen(oldpath);
861         do {
862             write_size = write(fd, (void *)oldpath, oldpath_size);
863         } while (write_size == -1 && errno == EINTR);
864
865         if (write_size != oldpath_size) {
866             serrno = errno;
867             close(fd);
868             err = -1;
869             goto err_end;
870         }
871         close(fd);
872         /* Set cleint credentials in symlink's xattr */
873         credp->fc_mode = credp->fc_mode|S_IFLNK;
874         err = local_set_mapped_file_attr(fs_ctx, newpath, credp);
875         if (err == -1) {
876             serrno = errno;
877             goto err_end;
878         }
879     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
880                (fs_ctx->export_flags & V9FS_SM_NONE)) {
881         buffer = rpath(fs_ctx, newpath);
882         err = symlink(oldpath, buffer);
883         if (err) {
884             goto out;
885         }
886         err = lchown(buffer, credp->fc_uid, credp->fc_gid);
887         if (err == -1) {
888             /*
889              * If we fail to change ownership and if we are
890              * using security model none. Ignore the error
891              */
892             if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
893                 serrno = errno;
894                 goto err_end;
895             } else
896                 err = 0;
897         }
898     }
899     goto out;
900
901 err_end:
902     remove(buffer);
903     errno = serrno;
904 out:
905     g_free(buffer);
906     v9fs_string_free(&fullname);
907     return err;
908 }
909
910 static int local_link(FsContext *ctx, V9fsPath *oldpath,
911                       V9fsPath *dirpath, const char *name)
912 {
913     int ret;
914     V9fsString newpath;
915     char *buffer, *buffer1;
916
917     v9fs_string_init(&newpath);
918     v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
919
920     buffer = rpath(ctx, oldpath->data);
921     buffer1 = rpath(ctx, newpath.data);
922     ret = link(buffer, buffer1);
923     g_free(buffer);
924     g_free(buffer1);
925
926     /* now link the virtfs_metadata files */
927     if (!ret && (ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
928         /* Link the .virtfs_metadata files. Create the metada directory */
929         ret = local_create_mapped_attr_dir(ctx, newpath.data);
930         if (ret < 0) {
931             goto err_out;
932         }
933         buffer = local_mapped_attr_path(ctx, oldpath->data);
934         buffer1 = local_mapped_attr_path(ctx, newpath.data);
935         ret = link(buffer, buffer1);
936         g_free(buffer);
937         g_free(buffer1);
938         if (ret < 0 && errno != ENOENT) {
939             goto err_out;
940         }
941     }
942 err_out:
943     v9fs_string_free(&newpath);
944     return ret;
945 }
946
947 static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
948 {
949     int fd, ret;
950
951     fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0);
952     if (fd == -1) {
953         return -1;
954     }
955     ret = ftruncate(fd, size);
956     close_preserve_errno(fd);
957     return ret;
958 }
959
960 static int local_rename(FsContext *ctx, const char *oldpath,
961                         const char *newpath)
962 {
963     int err;
964     char *buffer, *buffer1;
965
966     if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
967         err = local_create_mapped_attr_dir(ctx, newpath);
968         if (err < 0) {
969             return err;
970         }
971         /* rename the .virtfs_metadata files */
972         buffer = local_mapped_attr_path(ctx, oldpath);
973         buffer1 = local_mapped_attr_path(ctx, newpath);
974         err = rename(buffer, buffer1);
975         g_free(buffer);
976         g_free(buffer1);
977         if (err < 0 && errno != ENOENT) {
978             return err;
979         }
980     }
981
982     buffer = rpath(ctx, oldpath);
983     buffer1 = rpath(ctx, newpath);
984     err = rename(buffer, buffer1);
985     g_free(buffer);
986     g_free(buffer1);
987     return err;
988 }
989
990 static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
991 {
992     char *buffer;
993     int ret = -1;
994     char *path = fs_path->data;
995
996     if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
997         (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
998         (fs_ctx->export_flags & V9FS_SM_NONE)) {
999         buffer = rpath(fs_ctx, path);
1000         ret = lchown(buffer, credp->fc_uid, credp->fc_gid);
1001         g_free(buffer);
1002     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
1003         buffer = rpath(fs_ctx, path);
1004         ret = local_set_xattr(buffer, credp);
1005         g_free(buffer);
1006     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1007         return local_set_mapped_file_attr(fs_ctx, path, credp);
1008     }
1009     return ret;
1010 }
1011
1012 static int local_utimensat(FsContext *s, V9fsPath *fs_path,
1013                            const struct timespec *buf)
1014 {
1015     char *dirpath = g_path_get_dirname(fs_path->data);
1016     char *name = g_path_get_basename(fs_path->data);
1017     int dirfd, ret = -1;
1018
1019     dirfd = local_opendir_nofollow(s, dirpath);
1020     if (dirfd == -1) {
1021         goto out;
1022     }
1023
1024     ret = utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW);
1025     close_preserve_errno(dirfd);
1026 out:
1027     g_free(dirpath);
1028     g_free(name);
1029     return ret;
1030 }
1031
1032 static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
1033                                  int flags)
1034 {
1035     int ret = -1;
1036
1037     if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1038         int map_dirfd;
1039
1040         if (flags == AT_REMOVEDIR) {
1041             int fd;
1042
1043             fd = openat(dirfd, name, O_RDONLY | O_DIRECTORY | O_PATH);
1044             if (fd == -1) {
1045                 goto err_out;
1046             }
1047             /*
1048              * If directory remove .virtfs_metadata contained in the
1049              * directory
1050              */
1051             ret = unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR);
1052             close_preserve_errno(fd);
1053             if (ret < 0 && errno != ENOENT) {
1054                 /*
1055                  * We didn't had the .virtfs_metadata file. May be file created
1056                  * in non-mapped mode ?. Ignore ENOENT.
1057                  */
1058                 goto err_out;
1059             }
1060         }
1061         /*
1062          * Now remove the name from parent directory
1063          * .virtfs_metadata directory.
1064          */
1065         map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
1066         ret = unlinkat(map_dirfd, name, 0);
1067         close_preserve_errno(map_dirfd);
1068         if (ret < 0 && errno != ENOENT) {
1069             /*
1070              * We didn't had the .virtfs_metadata file. May be file created
1071              * in non-mapped mode ?. Ignore ENOENT.
1072              */
1073             goto err_out;
1074         }
1075     }
1076
1077     ret = unlinkat(dirfd, name, flags);
1078 err_out:
1079     return ret;
1080 }
1081
1082 static int local_remove(FsContext *ctx, const char *path)
1083 {
1084     struct stat stbuf;
1085     char *dirpath = g_path_get_dirname(path);
1086     char *name = g_path_get_basename(path);
1087     int flags = 0;
1088     int dirfd;
1089     int err = -1;
1090
1091     dirfd = local_opendir_nofollow(ctx, dirpath);
1092     if (dirfd) {
1093         goto out;
1094     }
1095
1096     if (fstatat(dirfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) {
1097         goto err_out;
1098     }
1099
1100     if (S_ISDIR(stbuf.st_mode)) {
1101         flags |= AT_REMOVEDIR;
1102     }
1103
1104     err = local_unlinkat_common(ctx, dirfd, name, flags);
1105 err_out:
1106     close_preserve_errno(dirfd);
1107 out:
1108     g_free(name);
1109     g_free(dirpath);
1110     return err;
1111 }
1112
1113 static int local_fsync(FsContext *ctx, int fid_type,
1114                        V9fsFidOpenState *fs, int datasync)
1115 {
1116     int fd;
1117
1118     if (fid_type == P9_FID_DIR) {
1119         fd = dirfd(fs->dir.stream);
1120     } else {
1121         fd = fs->fd;
1122     }
1123
1124     if (datasync) {
1125         return qemu_fdatasync(fd);
1126     } else {
1127         return fsync(fd);
1128     }
1129 }
1130
1131 static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
1132 {
1133     int fd, ret;
1134
1135     fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0);
1136     ret = fstatfs(fd, stbuf);
1137     close_preserve_errno(fd);
1138     return ret;
1139 }
1140
1141 static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
1142                                const char *name, void *value, size_t size)
1143 {
1144     char *path = fs_path->data;
1145
1146     return v9fs_get_xattr(ctx, path, name, value, size);
1147 }
1148
1149 static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
1150                                 void *value, size_t size)
1151 {
1152     char *path = fs_path->data;
1153
1154     return v9fs_list_xattr(ctx, path, value, size);
1155 }
1156
1157 static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
1158                            void *value, size_t size, int flags)
1159 {
1160     char *path = fs_path->data;
1161
1162     return v9fs_set_xattr(ctx, path, name, value, size, flags);
1163 }
1164
1165 static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
1166                               const char *name)
1167 {
1168     char *path = fs_path->data;
1169
1170     return v9fs_remove_xattr(ctx, path, name);
1171 }
1172
1173 static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
1174                               const char *name, V9fsPath *target)
1175 {
1176     if (dir_path) {
1177         v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
1178     } else {
1179         v9fs_path_sprintf(target, "%s", name);
1180     }
1181     return 0;
1182 }
1183
1184 static int local_renameat(FsContext *ctx, V9fsPath *olddir,
1185                           const char *old_name, V9fsPath *newdir,
1186                           const char *new_name)
1187 {
1188     int ret;
1189     V9fsString old_full_name, new_full_name;
1190
1191     v9fs_string_init(&old_full_name);
1192     v9fs_string_init(&new_full_name);
1193
1194     v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
1195     v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
1196
1197     ret = local_rename(ctx, old_full_name.data, new_full_name.data);
1198     v9fs_string_free(&old_full_name);
1199     v9fs_string_free(&new_full_name);
1200     return ret;
1201 }
1202
1203 static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
1204                           const char *name, int flags)
1205 {
1206     int ret;
1207     int dirfd;
1208
1209     dirfd = local_opendir_nofollow(ctx, dir->data);
1210     if (dirfd == -1) {
1211         return -1;
1212     }
1213
1214     ret = local_unlinkat_common(ctx, dirfd, name, flags);
1215     close_preserve_errno(dirfd);
1216     return ret;
1217 }
1218
1219 static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
1220                                 mode_t st_mode, uint64_t *st_gen)
1221 {
1222 #ifdef FS_IOC_GETVERSION
1223     int err;
1224     V9fsFidOpenState fid_open;
1225
1226     /*
1227      * Do not try to open special files like device nodes, fifos etc
1228      * We can get fd for regular files and directories only
1229      */
1230     if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
1231         errno = ENOTTY;
1232         return -1;
1233     }
1234     err = local_open(ctx, path, O_RDONLY, &fid_open);
1235     if (err < 0) {
1236         return err;
1237     }
1238     err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
1239     local_close(ctx, &fid_open);
1240     return err;
1241 #else
1242     errno = ENOTTY;
1243     return -1;
1244 #endif
1245 }
1246
1247 static int local_init(FsContext *ctx)
1248 {
1249     struct statfs stbuf;
1250     LocalData *data = g_malloc(sizeof(*data));
1251
1252     data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY);
1253     if (data->mountfd == -1) {
1254         goto err;
1255     }
1256
1257 #ifdef FS_IOC_GETVERSION
1258     /*
1259      * use ioc_getversion only if the ioctl is definied
1260      */
1261     if (fstatfs(data->mountfd, &stbuf) < 0) {
1262         close_preserve_errno(data->mountfd);
1263         goto err;
1264     }
1265     switch (stbuf.f_type) {
1266     case EXT2_SUPER_MAGIC:
1267     case BTRFS_SUPER_MAGIC:
1268     case REISERFS_SUPER_MAGIC:
1269     case XFS_SUPER_MAGIC:
1270         ctx->exops.get_st_gen = local_ioc_getversion;
1271         break;
1272     }
1273 #endif
1274
1275     if (ctx->export_flags & V9FS_SM_PASSTHROUGH) {
1276         ctx->xops = passthrough_xattr_ops;
1277     } else if (ctx->export_flags & V9FS_SM_MAPPED) {
1278         ctx->xops = mapped_xattr_ops;
1279     } else if (ctx->export_flags & V9FS_SM_NONE) {
1280         ctx->xops = none_xattr_ops;
1281     } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1282         /*
1283          * xattr operation for mapped-file and passthrough
1284          * remain same.
1285          */
1286         ctx->xops = passthrough_xattr_ops;
1287     }
1288     ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1289
1290     ctx->private = data;
1291     return 0;
1292
1293 err:
1294     g_free(data);
1295     return -1;
1296 }
1297
1298 static void local_cleanup(FsContext *ctx)
1299 {
1300     LocalData *data = ctx->private;
1301
1302     close(data->mountfd);
1303     g_free(data);
1304 }
1305
1306 static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
1307 {
1308     const char *sec_model = qemu_opt_get(opts, "security_model");
1309     const char *path = qemu_opt_get(opts, "path");
1310
1311     if (!sec_model) {
1312         error_report("Security model not specified, local fs needs security model");
1313         error_printf("valid options are:"
1314                      "\tsecurity_model=[passthrough|mapped-xattr|mapped-file|none]\n");
1315         return -1;
1316     }
1317
1318     if (!strcmp(sec_model, "passthrough")) {
1319         fse->export_flags |= V9FS_SM_PASSTHROUGH;
1320     } else if (!strcmp(sec_model, "mapped") ||
1321                !strcmp(sec_model, "mapped-xattr")) {
1322         fse->export_flags |= V9FS_SM_MAPPED;
1323     } else if (!strcmp(sec_model, "none")) {
1324         fse->export_flags |= V9FS_SM_NONE;
1325     } else if (!strcmp(sec_model, "mapped-file")) {
1326         fse->export_flags |= V9FS_SM_MAPPED_FILE;
1327     } else {
1328         error_report("Invalid security model %s specified", sec_model);
1329         error_printf("valid options are:"
1330                      "\t[passthrough|mapped-xattr|mapped-file|none]\n");
1331         return -1;
1332     }
1333
1334     if (!path) {
1335         error_report("fsdev: No path specified");
1336         return -1;
1337     }
1338     fse->path = g_strdup(path);
1339
1340     return 0;
1341 }
1342
1343 FileOperations local_ops = {
1344     .parse_opts = local_parse_opts,
1345     .init  = local_init,
1346     .cleanup = local_cleanup,
1347     .lstat = local_lstat,
1348     .readlink = local_readlink,
1349     .close = local_close,
1350     .closedir = local_closedir,
1351     .open = local_open,
1352     .opendir = local_opendir,
1353     .rewinddir = local_rewinddir,
1354     .telldir = local_telldir,
1355     .readdir = local_readdir,
1356     .seekdir = local_seekdir,
1357     .preadv = local_preadv,
1358     .pwritev = local_pwritev,
1359     .chmod = local_chmod,
1360     .mknod = local_mknod,
1361     .mkdir = local_mkdir,
1362     .fstat = local_fstat,
1363     .open2 = local_open2,
1364     .symlink = local_symlink,
1365     .link = local_link,
1366     .truncate = local_truncate,
1367     .rename = local_rename,
1368     .chown = local_chown,
1369     .utimensat = local_utimensat,
1370     .remove = local_remove,
1371     .fsync = local_fsync,
1372     .statfs = local_statfs,
1373     .lgetxattr = local_lgetxattr,
1374     .llistxattr = local_llistxattr,
1375     .lsetxattr = local_lsetxattr,
1376     .lremovexattr = local_lremovexattr,
1377     .name_to_path = local_name_to_path,
1378     .renameat  = local_renameat,
1379     .unlinkat = local_unlinkat,
1380 };
This page took 0.105024 seconds and 4 git commands to generate.