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