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