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