xref: /qemu/hw/9pfs/9p-local.c (revision 21328e1e57f526e3f0c2fcd00f10c8aa6e7bc07f)
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     int fd;
360 
361     buffer = rpath(ctx, path);
362     fd = open(buffer, flags | O_NOFOLLOW);
363     g_free(buffer);
364     if (fd == -1) {
365         return -1;
366     }
367     fs->fd = fd;
368     return fs->fd;
369 }
370 
371 static int local_opendir(FsContext *ctx,
372                          V9fsPath *fs_path, V9fsFidOpenState *fs)
373 {
374     char *buffer;
375     char *path = fs_path->data;
376     DIR *stream;
377 
378     buffer = rpath(ctx, path);
379     stream = opendir(buffer);
380     g_free(buffer);
381     if (!stream) {
382         return -1;
383     }
384     fs->dir.stream = stream;
385     return 0;
386 }
387 
388 static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
389 {
390     rewinddir(fs->dir.stream);
391 }
392 
393 static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
394 {
395     return telldir(fs->dir.stream);
396 }
397 
398 static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs)
399 {
400     struct dirent *entry;
401 
402 again:
403     entry = readdir(fs->dir.stream);
404     if (!entry) {
405         return NULL;
406     }
407 
408     if (ctx->export_flags & V9FS_SM_MAPPED) {
409         entry->d_type = DT_UNKNOWN;
410     } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
411         if (!strcmp(entry->d_name, VIRTFS_META_DIR)) {
412             /* skp the meta data directory */
413             goto again;
414         }
415         entry->d_type = DT_UNKNOWN;
416     }
417 
418     return entry;
419 }
420 
421 static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
422 {
423     seekdir(fs->dir.stream, off);
424 }
425 
426 static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
427                             const struct iovec *iov,
428                             int iovcnt, off_t offset)
429 {
430 #ifdef CONFIG_PREADV
431     return preadv(fs->fd, iov, iovcnt, offset);
432 #else
433     int err = lseek(fs->fd, offset, SEEK_SET);
434     if (err == -1) {
435         return err;
436     } else {
437         return readv(fs->fd, iov, iovcnt);
438     }
439 #endif
440 }
441 
442 static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
443                              const struct iovec *iov,
444                              int iovcnt, off_t offset)
445 {
446     ssize_t ret;
447 #ifdef CONFIG_PREADV
448     ret = pwritev(fs->fd, iov, iovcnt, offset);
449 #else
450     int err = lseek(fs->fd, offset, SEEK_SET);
451     if (err == -1) {
452         return err;
453     } else {
454         ret = writev(fs->fd, iov, iovcnt);
455     }
456 #endif
457 #ifdef CONFIG_SYNC_FILE_RANGE
458     if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
459         /*
460          * Initiate a writeback. This is not a data integrity sync.
461          * We want to ensure that we don't leave dirty pages in the cache
462          * after write when writeout=immediate is sepcified.
463          */
464         sync_file_range(fs->fd, offset, ret,
465                         SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
466     }
467 #endif
468     return ret;
469 }
470 
471 static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
472 {
473     char *buffer;
474     int ret = -1;
475     char *path = fs_path->data;
476 
477     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
478         buffer = rpath(fs_ctx, path);
479         ret = local_set_xattr(buffer, credp);
480         g_free(buffer);
481     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
482         return local_set_mapped_file_attr(fs_ctx, path, credp);
483     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
484                (fs_ctx->export_flags & V9FS_SM_NONE)) {
485         buffer = rpath(fs_ctx, path);
486         ret = chmod(buffer, credp->fc_mode);
487         g_free(buffer);
488     }
489     return ret;
490 }
491 
492 static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
493                        const char *name, FsCred *credp)
494 {
495     char *path;
496     int err = -1;
497     int serrno = 0;
498     V9fsString fullname;
499     char *buffer = NULL;
500 
501     v9fs_string_init(&fullname);
502     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
503     path = fullname.data;
504 
505     /* Determine the security model */
506     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
507         buffer = rpath(fs_ctx, path);
508         err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
509         if (err == -1) {
510             goto out;
511         }
512         err = local_set_xattr(buffer, credp);
513         if (err == -1) {
514             serrno = errno;
515             goto err_end;
516         }
517     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
518 
519         buffer = rpath(fs_ctx, path);
520         err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
521         if (err == -1) {
522             goto out;
523         }
524         err = local_set_mapped_file_attr(fs_ctx, path, credp);
525         if (err == -1) {
526             serrno = errno;
527             goto err_end;
528         }
529     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
530                (fs_ctx->export_flags & V9FS_SM_NONE)) {
531         buffer = rpath(fs_ctx, path);
532         err = mknod(buffer, credp->fc_mode, credp->fc_rdev);
533         if (err == -1) {
534             goto out;
535         }
536         err = local_post_create_passthrough(fs_ctx, path, credp);
537         if (err == -1) {
538             serrno = errno;
539             goto err_end;
540         }
541     }
542     goto out;
543 
544 err_end:
545     remove(buffer);
546     errno = serrno;
547 out:
548     g_free(buffer);
549     v9fs_string_free(&fullname);
550     return err;
551 }
552 
553 static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
554                        const char *name, FsCred *credp)
555 {
556     char *path;
557     int err = -1;
558     int serrno = 0;
559     V9fsString fullname;
560     char *buffer = NULL;
561 
562     v9fs_string_init(&fullname);
563     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
564     path = fullname.data;
565 
566     /* Determine the security model */
567     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
568         buffer = rpath(fs_ctx, path);
569         err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
570         if (err == -1) {
571             goto out;
572         }
573         credp->fc_mode = credp->fc_mode|S_IFDIR;
574         err = local_set_xattr(buffer, credp);
575         if (err == -1) {
576             serrno = errno;
577             goto err_end;
578         }
579     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
580         buffer = rpath(fs_ctx, path);
581         err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
582         if (err == -1) {
583             goto out;
584         }
585         credp->fc_mode = credp->fc_mode|S_IFDIR;
586         err = local_set_mapped_file_attr(fs_ctx, path, credp);
587         if (err == -1) {
588             serrno = errno;
589             goto err_end;
590         }
591     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
592                (fs_ctx->export_flags & V9FS_SM_NONE)) {
593         buffer = rpath(fs_ctx, path);
594         err = mkdir(buffer, credp->fc_mode);
595         if (err == -1) {
596             goto out;
597         }
598         err = local_post_create_passthrough(fs_ctx, path, credp);
599         if (err == -1) {
600             serrno = errno;
601             goto err_end;
602         }
603     }
604     goto out;
605 
606 err_end:
607     remove(buffer);
608     errno = serrno;
609 out:
610     g_free(buffer);
611     v9fs_string_free(&fullname);
612     return err;
613 }
614 
615 static int local_fstat(FsContext *fs_ctx, int fid_type,
616                        V9fsFidOpenState *fs, struct stat *stbuf)
617 {
618     int err, fd;
619 
620     if (fid_type == P9_FID_DIR) {
621         fd = dirfd(fs->dir.stream);
622     } else {
623         fd = fs->fd;
624     }
625 
626     err = fstat(fd, stbuf);
627     if (err) {
628         return err;
629     }
630     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
631         /* Actual credentials are part of extended attrs */
632         uid_t tmp_uid;
633         gid_t tmp_gid;
634         mode_t tmp_mode;
635         dev_t tmp_dev;
636 
637         if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
638             stbuf->st_uid = le32_to_cpu(tmp_uid);
639         }
640         if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
641             stbuf->st_gid = le32_to_cpu(tmp_gid);
642         }
643         if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
644             stbuf->st_mode = le32_to_cpu(tmp_mode);
645         }
646         if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
647             stbuf->st_rdev = le64_to_cpu(tmp_dev);
648         }
649     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
650         errno = EOPNOTSUPP;
651         return -1;
652     }
653     return err;
654 }
655 
656 static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
657                        int flags, FsCred *credp, V9fsFidOpenState *fs)
658 {
659     char *path;
660     int fd = -1;
661     int err = -1;
662     int serrno = 0;
663     V9fsString fullname;
664     char *buffer = NULL;
665 
666     /*
667      * Mark all the open to not follow symlinks
668      */
669     flags |= O_NOFOLLOW;
670 
671     v9fs_string_init(&fullname);
672     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
673     path = fullname.data;
674 
675     /* Determine the security model */
676     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
677         buffer = rpath(fs_ctx, path);
678         fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
679         if (fd == -1) {
680             err = fd;
681             goto out;
682         }
683         credp->fc_mode = credp->fc_mode|S_IFREG;
684         /* Set cleint credentials in xattr */
685         err = local_set_xattr(buffer, credp);
686         if (err == -1) {
687             serrno = errno;
688             goto err_end;
689         }
690     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
691         buffer = rpath(fs_ctx, path);
692         fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
693         if (fd == -1) {
694             err = fd;
695             goto out;
696         }
697         credp->fc_mode = credp->fc_mode|S_IFREG;
698         /* Set client credentials in .virtfs_metadata directory files */
699         err = local_set_mapped_file_attr(fs_ctx, path, credp);
700         if (err == -1) {
701             serrno = errno;
702             goto err_end;
703         }
704     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
705                (fs_ctx->export_flags & V9FS_SM_NONE)) {
706         buffer = rpath(fs_ctx, path);
707         fd = open(buffer, flags, credp->fc_mode);
708         if (fd == -1) {
709             err = fd;
710             goto out;
711         }
712         err = local_post_create_passthrough(fs_ctx, path, credp);
713         if (err == -1) {
714             serrno = errno;
715             goto err_end;
716         }
717     }
718     err = fd;
719     fs->fd = fd;
720     goto out;
721 
722 err_end:
723     close(fd);
724     remove(buffer);
725     errno = serrno;
726 out:
727     g_free(buffer);
728     v9fs_string_free(&fullname);
729     return err;
730 }
731 
732 
733 static int local_symlink(FsContext *fs_ctx, const char *oldpath,
734                          V9fsPath *dir_path, const char *name, FsCred *credp)
735 {
736     int err = -1;
737     int serrno = 0;
738     char *newpath;
739     V9fsString fullname;
740     char *buffer = NULL;
741 
742     v9fs_string_init(&fullname);
743     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
744     newpath = fullname.data;
745 
746     /* Determine the security model */
747     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
748         int fd;
749         ssize_t oldpath_size, write_size;
750         buffer = rpath(fs_ctx, newpath);
751         fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
752         if (fd == -1) {
753             err = fd;
754             goto out;
755         }
756         /* Write the oldpath (target) to the file. */
757         oldpath_size = strlen(oldpath);
758         do {
759             write_size = write(fd, (void *)oldpath, oldpath_size);
760         } while (write_size == -1 && errno == EINTR);
761 
762         if (write_size != oldpath_size) {
763             serrno = errno;
764             close(fd);
765             err = -1;
766             goto err_end;
767         }
768         close(fd);
769         /* Set cleint credentials in symlink's xattr */
770         credp->fc_mode = credp->fc_mode|S_IFLNK;
771         err = local_set_xattr(buffer, credp);
772         if (err == -1) {
773             serrno = errno;
774             goto err_end;
775         }
776     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
777         int fd;
778         ssize_t oldpath_size, write_size;
779         buffer = rpath(fs_ctx, newpath);
780         fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
781         if (fd == -1) {
782             err = fd;
783             goto out;
784         }
785         /* Write the oldpath (target) to the file. */
786         oldpath_size = strlen(oldpath);
787         do {
788             write_size = write(fd, (void *)oldpath, oldpath_size);
789         } while (write_size == -1 && errno == EINTR);
790 
791         if (write_size != oldpath_size) {
792             serrno = errno;
793             close(fd);
794             err = -1;
795             goto err_end;
796         }
797         close(fd);
798         /* Set cleint credentials in symlink's xattr */
799         credp->fc_mode = credp->fc_mode|S_IFLNK;
800         err = local_set_mapped_file_attr(fs_ctx, newpath, credp);
801         if (err == -1) {
802             serrno = errno;
803             goto err_end;
804         }
805     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
806                (fs_ctx->export_flags & V9FS_SM_NONE)) {
807         buffer = rpath(fs_ctx, newpath);
808         err = symlink(oldpath, buffer);
809         if (err) {
810             goto out;
811         }
812         err = lchown(buffer, credp->fc_uid, credp->fc_gid);
813         if (err == -1) {
814             /*
815              * If we fail to change ownership and if we are
816              * using security model none. Ignore the error
817              */
818             if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
819                 serrno = errno;
820                 goto err_end;
821             } else
822                 err = 0;
823         }
824     }
825     goto out;
826 
827 err_end:
828     remove(buffer);
829     errno = serrno;
830 out:
831     g_free(buffer);
832     v9fs_string_free(&fullname);
833     return err;
834 }
835 
836 static int local_link(FsContext *ctx, V9fsPath *oldpath,
837                       V9fsPath *dirpath, const char *name)
838 {
839     int ret;
840     V9fsString newpath;
841     char *buffer, *buffer1;
842 
843     v9fs_string_init(&newpath);
844     v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
845 
846     buffer = rpath(ctx, oldpath->data);
847     buffer1 = rpath(ctx, newpath.data);
848     ret = link(buffer, buffer1);
849     g_free(buffer);
850     g_free(buffer1);
851 
852     /* now link the virtfs_metadata files */
853     if (!ret && (ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
854         /* Link the .virtfs_metadata files. Create the metada directory */
855         ret = local_create_mapped_attr_dir(ctx, newpath.data);
856         if (ret < 0) {
857             goto err_out;
858         }
859         buffer = local_mapped_attr_path(ctx, oldpath->data);
860         buffer1 = local_mapped_attr_path(ctx, newpath.data);
861         ret = link(buffer, buffer1);
862         g_free(buffer);
863         g_free(buffer1);
864         if (ret < 0 && errno != ENOENT) {
865             goto err_out;
866         }
867     }
868 err_out:
869     v9fs_string_free(&newpath);
870     return ret;
871 }
872 
873 static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
874 {
875     char *buffer;
876     int ret;
877     char *path = fs_path->data;
878 
879     buffer = rpath(ctx, path);
880     ret = truncate(buffer, size);
881     g_free(buffer);
882     return ret;
883 }
884 
885 static int local_rename(FsContext *ctx, const char *oldpath,
886                         const char *newpath)
887 {
888     int err;
889     char *buffer, *buffer1;
890 
891     if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
892         err = local_create_mapped_attr_dir(ctx, newpath);
893         if (err < 0) {
894             return err;
895         }
896         /* rename the .virtfs_metadata files */
897         buffer = local_mapped_attr_path(ctx, oldpath);
898         buffer1 = local_mapped_attr_path(ctx, newpath);
899         err = rename(buffer, buffer1);
900         g_free(buffer);
901         g_free(buffer1);
902         if (err < 0 && errno != ENOENT) {
903             return err;
904         }
905     }
906 
907     buffer = rpath(ctx, oldpath);
908     buffer1 = rpath(ctx, newpath);
909     err = rename(buffer, buffer1);
910     g_free(buffer);
911     g_free(buffer1);
912     return err;
913 }
914 
915 static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
916 {
917     char *buffer;
918     int ret = -1;
919     char *path = fs_path->data;
920 
921     if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
922         (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
923         (fs_ctx->export_flags & V9FS_SM_NONE)) {
924         buffer = rpath(fs_ctx, path);
925         ret = lchown(buffer, credp->fc_uid, credp->fc_gid);
926         g_free(buffer);
927     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
928         buffer = rpath(fs_ctx, path);
929         ret = local_set_xattr(buffer, credp);
930         g_free(buffer);
931     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
932         return local_set_mapped_file_attr(fs_ctx, path, credp);
933     }
934     return ret;
935 }
936 
937 static int local_utimensat(FsContext *s, V9fsPath *fs_path,
938                            const struct timespec *buf)
939 {
940     char *buffer;
941     int ret;
942     char *path = fs_path->data;
943 
944     buffer = rpath(s, path);
945     ret = qemu_utimens(buffer, buf);
946     g_free(buffer);
947     return ret;
948 }
949 
950 static int local_remove(FsContext *ctx, const char *path)
951 {
952     int err;
953     struct stat stbuf;
954     char *buffer;
955 
956     if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
957         buffer = rpath(ctx, path);
958         err =  lstat(buffer, &stbuf);
959         g_free(buffer);
960         if (err) {
961             goto err_out;
962         }
963         /*
964          * If directory remove .virtfs_metadata contained in the
965          * directory
966          */
967         if (S_ISDIR(stbuf.st_mode)) {
968             buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
969                                      path, VIRTFS_META_DIR);
970             err = remove(buffer);
971             g_free(buffer);
972             if (err < 0 && errno != ENOENT) {
973                 /*
974                  * We didn't had the .virtfs_metadata file. May be file created
975                  * in non-mapped mode ?. Ignore ENOENT.
976                  */
977                 goto err_out;
978             }
979         }
980         /*
981          * Now remove the name from parent directory
982          * .virtfs_metadata directory
983          */
984         buffer = local_mapped_attr_path(ctx, path);
985         err = remove(buffer);
986         g_free(buffer);
987         if (err < 0 && errno != ENOENT) {
988             /*
989              * We didn't had the .virtfs_metadata file. May be file created
990              * in non-mapped mode ?. Ignore ENOENT.
991              */
992             goto err_out;
993         }
994     }
995 
996     buffer = rpath(ctx, path);
997     err = remove(buffer);
998     g_free(buffer);
999 err_out:
1000     return err;
1001 }
1002 
1003 static int local_fsync(FsContext *ctx, int fid_type,
1004                        V9fsFidOpenState *fs, int datasync)
1005 {
1006     int fd;
1007 
1008     if (fid_type == P9_FID_DIR) {
1009         fd = dirfd(fs->dir.stream);
1010     } else {
1011         fd = fs->fd;
1012     }
1013 
1014     if (datasync) {
1015         return qemu_fdatasync(fd);
1016     } else {
1017         return fsync(fd);
1018     }
1019 }
1020 
1021 static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
1022 {
1023     char *buffer;
1024     int ret;
1025     char *path = fs_path->data;
1026 
1027     buffer = rpath(s, path);
1028     ret = statfs(buffer, stbuf);
1029     g_free(buffer);
1030     return ret;
1031 }
1032 
1033 static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
1034                                const char *name, void *value, size_t size)
1035 {
1036     char *path = fs_path->data;
1037 
1038     return v9fs_get_xattr(ctx, path, name, value, size);
1039 }
1040 
1041 static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
1042                                 void *value, size_t size)
1043 {
1044     char *path = fs_path->data;
1045 
1046     return v9fs_list_xattr(ctx, path, value, size);
1047 }
1048 
1049 static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
1050                            void *value, size_t size, int flags)
1051 {
1052     char *path = fs_path->data;
1053 
1054     return v9fs_set_xattr(ctx, path, name, value, size, flags);
1055 }
1056 
1057 static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
1058                               const char *name)
1059 {
1060     char *path = fs_path->data;
1061 
1062     return v9fs_remove_xattr(ctx, path, name);
1063 }
1064 
1065 static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
1066                               const char *name, V9fsPath *target)
1067 {
1068     if (dir_path) {
1069         v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
1070     } else {
1071         v9fs_path_sprintf(target, "%s", name);
1072     }
1073     return 0;
1074 }
1075 
1076 static int local_renameat(FsContext *ctx, V9fsPath *olddir,
1077                           const char *old_name, V9fsPath *newdir,
1078                           const char *new_name)
1079 {
1080     int ret;
1081     V9fsString old_full_name, new_full_name;
1082 
1083     v9fs_string_init(&old_full_name);
1084     v9fs_string_init(&new_full_name);
1085 
1086     v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
1087     v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
1088 
1089     ret = local_rename(ctx, old_full_name.data, new_full_name.data);
1090     v9fs_string_free(&old_full_name);
1091     v9fs_string_free(&new_full_name);
1092     return ret;
1093 }
1094 
1095 static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
1096                           const char *name, int flags)
1097 {
1098     int ret;
1099     V9fsString fullname;
1100     char *buffer;
1101 
1102     v9fs_string_init(&fullname);
1103 
1104     v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
1105     if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1106         if (flags == AT_REMOVEDIR) {
1107             /*
1108              * If directory remove .virtfs_metadata contained in the
1109              * directory
1110              */
1111             buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
1112                                      fullname.data, VIRTFS_META_DIR);
1113             ret = remove(buffer);
1114             g_free(buffer);
1115             if (ret < 0 && errno != ENOENT) {
1116                 /*
1117                  * We didn't had the .virtfs_metadata file. May be file created
1118                  * in non-mapped mode ?. Ignore ENOENT.
1119                  */
1120                 goto err_out;
1121             }
1122         }
1123         /*
1124          * Now remove the name from parent directory
1125          * .virtfs_metadata directory.
1126          */
1127         buffer = local_mapped_attr_path(ctx, fullname.data);
1128         ret = remove(buffer);
1129         g_free(buffer);
1130         if (ret < 0 && errno != ENOENT) {
1131             /*
1132              * We didn't had the .virtfs_metadata file. May be file created
1133              * in non-mapped mode ?. Ignore ENOENT.
1134              */
1135             goto err_out;
1136         }
1137     }
1138     /* Remove the name finally */
1139     buffer = rpath(ctx, fullname.data);
1140     ret = remove(buffer);
1141     g_free(buffer);
1142 
1143 err_out:
1144     v9fs_string_free(&fullname);
1145     return ret;
1146 }
1147 
1148 static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
1149                                 mode_t st_mode, uint64_t *st_gen)
1150 {
1151 #ifdef FS_IOC_GETVERSION
1152     int err;
1153     V9fsFidOpenState fid_open;
1154 
1155     /*
1156      * Do not try to open special files like device nodes, fifos etc
1157      * We can get fd for regular files and directories only
1158      */
1159     if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
1160         errno = ENOTTY;
1161         return -1;
1162     }
1163     err = local_open(ctx, path, O_RDONLY, &fid_open);
1164     if (err < 0) {
1165         return err;
1166     }
1167     err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
1168     local_close(ctx, &fid_open);
1169     return err;
1170 #else
1171     errno = ENOTTY;
1172     return -1;
1173 #endif
1174 }
1175 
1176 static int local_init(FsContext *ctx)
1177 {
1178     struct statfs stbuf;
1179 
1180 #ifdef FS_IOC_GETVERSION
1181     /*
1182      * use ioc_getversion only if the ioctl is definied
1183      */
1184     if (statfs(ctx->fs_root, &stbuf) < 0) {
1185         return -1;
1186     }
1187     switch (stbuf.f_type) {
1188     case EXT2_SUPER_MAGIC:
1189     case BTRFS_SUPER_MAGIC:
1190     case REISERFS_SUPER_MAGIC:
1191     case XFS_SUPER_MAGIC:
1192         ctx->exops.get_st_gen = local_ioc_getversion;
1193         break;
1194     }
1195 #endif
1196 
1197     if (ctx->export_flags & V9FS_SM_PASSTHROUGH) {
1198         ctx->xops = passthrough_xattr_ops;
1199     } else if (ctx->export_flags & V9FS_SM_MAPPED) {
1200         ctx->xops = mapped_xattr_ops;
1201     } else if (ctx->export_flags & V9FS_SM_NONE) {
1202         ctx->xops = none_xattr_ops;
1203     } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1204         /*
1205          * xattr operation for mapped-file and passthrough
1206          * remain same.
1207          */
1208         ctx->xops = passthrough_xattr_ops;
1209     }
1210     ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1211 
1212     return 0;
1213 }
1214 
1215 static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
1216 {
1217     const char *sec_model = qemu_opt_get(opts, "security_model");
1218     const char *path = qemu_opt_get(opts, "path");
1219 
1220     if (!sec_model) {
1221         error_report("Security model not specified, local fs needs security model");
1222         error_printf("valid options are:"
1223                      "\tsecurity_model=[passthrough|mapped-xattr|mapped-file|none]\n");
1224         return -1;
1225     }
1226 
1227     if (!strcmp(sec_model, "passthrough")) {
1228         fse->export_flags |= V9FS_SM_PASSTHROUGH;
1229     } else if (!strcmp(sec_model, "mapped") ||
1230                !strcmp(sec_model, "mapped-xattr")) {
1231         fse->export_flags |= V9FS_SM_MAPPED;
1232     } else if (!strcmp(sec_model, "none")) {
1233         fse->export_flags |= V9FS_SM_NONE;
1234     } else if (!strcmp(sec_model, "mapped-file")) {
1235         fse->export_flags |= V9FS_SM_MAPPED_FILE;
1236     } else {
1237         error_report("Invalid security model %s specified", sec_model);
1238         error_printf("valid options are:"
1239                      "\t[passthrough|mapped-xattr|mapped-file|none]\n");
1240         return -1;
1241     }
1242 
1243     if (!path) {
1244         error_report("fsdev: No path specified");
1245         return -1;
1246     }
1247     fse->path = g_strdup(path);
1248 
1249     return 0;
1250 }
1251 
1252 FileOperations local_ops = {
1253     .parse_opts = local_parse_opts,
1254     .init  = local_init,
1255     .lstat = local_lstat,
1256     .readlink = local_readlink,
1257     .close = local_close,
1258     .closedir = local_closedir,
1259     .open = local_open,
1260     .opendir = local_opendir,
1261     .rewinddir = local_rewinddir,
1262     .telldir = local_telldir,
1263     .readdir = local_readdir,
1264     .seekdir = local_seekdir,
1265     .preadv = local_preadv,
1266     .pwritev = local_pwritev,
1267     .chmod = local_chmod,
1268     .mknod = local_mknod,
1269     .mkdir = local_mkdir,
1270     .fstat = local_fstat,
1271     .open2 = local_open2,
1272     .symlink = local_symlink,
1273     .link = local_link,
1274     .truncate = local_truncate,
1275     .rename = local_rename,
1276     .chown = local_chown,
1277     .utimensat = local_utimensat,
1278     .remove = local_remove,
1279     .fsync = local_fsync,
1280     .statfs = local_statfs,
1281     .lgetxattr = local_lgetxattr,
1282     .llistxattr = local_llistxattr,
1283     .lsetxattr = local_lsetxattr,
1284     .lremovexattr = local_lremovexattr,
1285     .name_to_path = local_name_to_path,
1286     .renameat  = local_renameat,
1287     .unlinkat = local_unlinkat,
1288 };
1289