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