]> Git Repo - qemu.git/blob - hw/virtio-9p-local.c
Merge remote branch 'kwolf/for-anthony' into staging
[qemu.git] / hw / 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 #include "virtio.h"
14 #include "virtio-9p.h"
15 #include <arpa/inet.h>
16 #include <pwd.h>
17 #include <grp.h>
18 #include <sys/socket.h>
19 #include <sys/un.h>
20 #include <attr/xattr.h>
21
22 static const char *rpath(FsContext *ctx, const char *path)
23 {
24     /* FIXME: so wrong... */
25     static char buffer[4096];
26     snprintf(buffer, sizeof(buffer), "%s/%s", ctx->fs_root, path);
27     return buffer;
28 }
29
30
31 static int local_lstat(FsContext *fs_ctx, const char *path, struct stat *stbuf)
32 {
33     int err;
34     err =  lstat(rpath(fs_ctx, path), stbuf);
35     if (err) {
36         return err;
37     }
38     if (fs_ctx->fs_sm == SM_MAPPED) {
39         /* Actual credentials are part of extended attrs */
40         uid_t tmp_uid;
41         gid_t tmp_gid;
42         mode_t tmp_mode;
43         dev_t tmp_dev;
44         if (getxattr(rpath(fs_ctx, path), "user.virtfs.uid", &tmp_uid,
45                     sizeof(uid_t)) > 0) {
46             stbuf->st_uid = tmp_uid;
47         }
48         if (getxattr(rpath(fs_ctx, path), "user.virtfs.gid", &tmp_gid,
49                     sizeof(gid_t)) > 0) {
50             stbuf->st_gid = tmp_gid;
51         }
52         if (getxattr(rpath(fs_ctx, path), "user.virtfs.mode", &tmp_mode,
53                     sizeof(mode_t)) > 0) {
54             stbuf->st_mode = tmp_mode;
55         }
56         if (getxattr(rpath(fs_ctx, path), "user.virtfs.rdev", &tmp_dev,
57                         sizeof(dev_t)) > 0) {
58                 stbuf->st_rdev = tmp_dev;
59         }
60     }
61     return err;
62 }
63
64 static int local_set_xattr(const char *path, FsCred *credp)
65 {
66     int err;
67     if (credp->fc_uid != -1) {
68         err = setxattr(path, "user.virtfs.uid", &credp->fc_uid, sizeof(uid_t),
69                 0);
70         if (err) {
71             return err;
72         }
73     }
74     if (credp->fc_gid != -1) {
75         err = setxattr(path, "user.virtfs.gid", &credp->fc_gid, sizeof(gid_t),
76                 0);
77         if (err) {
78             return err;
79         }
80     }
81     if (credp->fc_mode != -1) {
82         err = setxattr(path, "user.virtfs.mode", &credp->fc_mode,
83                 sizeof(mode_t), 0);
84         if (err) {
85             return err;
86         }
87     }
88     if (credp->fc_rdev != -1) {
89         err = setxattr(path, "user.virtfs.rdev", &credp->fc_rdev,
90                 sizeof(dev_t), 0);
91         if (err) {
92             return err;
93         }
94     }
95     return 0;
96 }
97
98 static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
99         FsCred *credp)
100 {
101     if (chmod(rpath(fs_ctx, path), credp->fc_mode & 07777) < 0) {
102         return -1;
103     }
104     if (lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid) < 0) {
105         /*
106          * If we fail to change ownership and if we are
107          * using security model none. Ignore the error
108          */
109         if (fs_ctx->fs_sm != SM_NONE) {
110             return -1;
111         }
112     }
113     return 0;
114 }
115
116 static ssize_t local_readlink(FsContext *fs_ctx, const char *path,
117         char *buf, size_t bufsz)
118 {
119     ssize_t tsize = -1;
120     if (fs_ctx->fs_sm == SM_MAPPED) {
121         int fd;
122         fd = open(rpath(fs_ctx, path), O_RDONLY);
123         if (fd == -1) {
124             return -1;
125         }
126         do {
127             tsize = read(fd, (void *)buf, bufsz);
128         } while (tsize == -1 && errno == EINTR);
129         close(fd);
130         return tsize;
131     } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
132                (fs_ctx->fs_sm == SM_NONE)) {
133         tsize = readlink(rpath(fs_ctx, path), buf, bufsz);
134     }
135     return tsize;
136 }
137
138 static int local_close(FsContext *ctx, int fd)
139 {
140     return close(fd);
141 }
142
143 static int local_closedir(FsContext *ctx, DIR *dir)
144 {
145     return closedir(dir);
146 }
147
148 static int local_open(FsContext *ctx, const char *path, int flags)
149 {
150     return open(rpath(ctx, path), flags);
151 }
152
153 static DIR *local_opendir(FsContext *ctx, const char *path)
154 {
155     return opendir(rpath(ctx, path));
156 }
157
158 static void local_rewinddir(FsContext *ctx, DIR *dir)
159 {
160     return rewinddir(dir);
161 }
162
163 static off_t local_telldir(FsContext *ctx, DIR *dir)
164 {
165     return telldir(dir);
166 }
167
168 static struct dirent *local_readdir(FsContext *ctx, DIR *dir)
169 {
170     return readdir(dir);
171 }
172
173 static void local_seekdir(FsContext *ctx, DIR *dir, off_t off)
174 {
175     return seekdir(dir, off);
176 }
177
178 static ssize_t local_readv(FsContext *ctx, int fd, const struct iovec *iov,
179                             int iovcnt)
180 {
181     return readv(fd, iov, iovcnt);
182 }
183
184 static off_t local_lseek(FsContext *ctx, int fd, off_t offset, int whence)
185 {
186     return lseek(fd, offset, whence);
187 }
188
189 static ssize_t local_writev(FsContext *ctx, int fd, const struct iovec *iov,
190                             int iovcnt)
191 {
192     return writev(fd, iov, iovcnt);
193 }
194
195 static int local_chmod(FsContext *fs_ctx, const char *path, FsCred *credp)
196 {
197     if (fs_ctx->fs_sm == SM_MAPPED) {
198         return local_set_xattr(rpath(fs_ctx, path), credp);
199     } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
200                (fs_ctx->fs_sm == SM_NONE)) {
201         return chmod(rpath(fs_ctx, path), credp->fc_mode);
202     }
203     return -1;
204 }
205
206 static int local_mknod(FsContext *fs_ctx, const char *path, FsCred *credp)
207 {
208     int err = -1;
209     int serrno = 0;
210
211     /* Determine the security model */
212     if (fs_ctx->fs_sm == SM_MAPPED) {
213         err = mknod(rpath(fs_ctx, path), SM_LOCAL_MODE_BITS|S_IFREG, 0);
214         if (err == -1) {
215             return err;
216         }
217         local_set_xattr(rpath(fs_ctx, path), credp);
218         if (err == -1) {
219             serrno = errno;
220             goto err_end;
221         }
222     } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
223                (fs_ctx->fs_sm == SM_NONE)) {
224         err = mknod(rpath(fs_ctx, path), credp->fc_mode, credp->fc_rdev);
225         if (err == -1) {
226             return err;
227         }
228         err = local_post_create_passthrough(fs_ctx, path, credp);
229         if (err == -1) {
230             serrno = errno;
231             goto err_end;
232         }
233     }
234     return err;
235
236 err_end:
237     remove(rpath(fs_ctx, path));
238     errno = serrno;
239     return err;
240 }
241
242 static int local_mkdir(FsContext *fs_ctx, const char *path, FsCred *credp)
243 {
244     int err = -1;
245     int serrno = 0;
246
247     /* Determine the security model */
248     if (fs_ctx->fs_sm == SM_MAPPED) {
249         err = mkdir(rpath(fs_ctx, path), SM_LOCAL_DIR_MODE_BITS);
250         if (err == -1) {
251             return err;
252         }
253         credp->fc_mode = credp->fc_mode|S_IFDIR;
254         err = local_set_xattr(rpath(fs_ctx, path), credp);
255         if (err == -1) {
256             serrno = errno;
257             goto err_end;
258         }
259     } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
260                (fs_ctx->fs_sm == SM_NONE)) {
261         err = mkdir(rpath(fs_ctx, path), credp->fc_mode);
262         if (err == -1) {
263             return err;
264         }
265         err = local_post_create_passthrough(fs_ctx, path, credp);
266         if (err == -1) {
267             serrno = errno;
268             goto err_end;
269         }
270     }
271     return err;
272
273 err_end:
274     remove(rpath(fs_ctx, path));
275     errno = serrno;
276     return err;
277 }
278
279 static int local_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf)
280 {
281     int err;
282     err = fstat(fd, stbuf);
283     if (err) {
284         return err;
285     }
286     if (fs_ctx->fs_sm == SM_MAPPED) {
287         /* Actual credentials are part of extended attrs */
288         uid_t tmp_uid;
289         gid_t tmp_gid;
290         mode_t tmp_mode;
291         dev_t tmp_dev;
292
293         if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
294             stbuf->st_uid = tmp_uid;
295         }
296         if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
297             stbuf->st_gid = tmp_gid;
298         }
299         if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
300             stbuf->st_mode = tmp_mode;
301         }
302         if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
303                 stbuf->st_rdev = tmp_dev;
304         }
305     }
306     return err;
307 }
308
309 static int local_open2(FsContext *fs_ctx, const char *path, int flags,
310         FsCred *credp)
311 {
312     int fd = -1;
313     int err = -1;
314     int serrno = 0;
315
316     /* Determine the security model */
317     if (fs_ctx->fs_sm == SM_MAPPED) {
318         fd = open(rpath(fs_ctx, path), flags, SM_LOCAL_MODE_BITS);
319         if (fd == -1) {
320             return fd;
321         }
322         credp->fc_mode = credp->fc_mode|S_IFREG;
323         /* Set cleint credentials in xattr */
324         err = local_set_xattr(rpath(fs_ctx, path), credp);
325         if (err == -1) {
326             serrno = errno;
327             goto err_end;
328         }
329     } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
330                (fs_ctx->fs_sm == SM_NONE)) {
331         fd = open(rpath(fs_ctx, path), flags, credp->fc_mode);
332         if (fd == -1) {
333             return fd;
334         }
335         err = local_post_create_passthrough(fs_ctx, path, credp);
336         if (err == -1) {
337             serrno = errno;
338             goto err_end;
339         }
340     }
341     return fd;
342
343 err_end:
344     close(fd);
345     remove(rpath(fs_ctx, path));
346     errno = serrno;
347     return err;
348 }
349
350
351 static int local_symlink(FsContext *fs_ctx, const char *oldpath,
352         const char *newpath, FsCred *credp)
353 {
354     int err = -1;
355     int serrno = 0;
356
357     /* Determine the security model */
358     if (fs_ctx->fs_sm == SM_MAPPED) {
359         int fd;
360         ssize_t oldpath_size, write_size;
361         fd = open(rpath(fs_ctx, newpath), O_CREAT|O_EXCL|O_RDWR,
362                 SM_LOCAL_MODE_BITS);
363         if (fd == -1) {
364             return fd;
365         }
366         /* Write the oldpath (target) to the file. */
367         oldpath_size = strlen(oldpath) + 1;
368         do {
369             write_size = write(fd, (void *)oldpath, oldpath_size);
370         } while (write_size == -1 && errno == EINTR);
371
372         if (write_size != oldpath_size) {
373             serrno = errno;
374             close(fd);
375             err = -1;
376             goto err_end;
377         }
378         close(fd);
379         /* Set cleint credentials in symlink's xattr */
380         credp->fc_mode = credp->fc_mode|S_IFLNK;
381         err = local_set_xattr(rpath(fs_ctx, newpath), credp);
382         if (err == -1) {
383             serrno = errno;
384             goto err_end;
385         }
386     } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
387                (fs_ctx->fs_sm == SM_NONE)) {
388         err = symlink(oldpath, rpath(fs_ctx, newpath));
389         if (err) {
390             return err;
391         }
392         err = lchown(rpath(fs_ctx, newpath), credp->fc_uid, credp->fc_gid);
393         if (err == -1) {
394             /*
395              * If we fail to change ownership and if we are
396              * using security model none. Ignore the error
397              */
398             if (fs_ctx->fs_sm != SM_NONE) {
399                 serrno = errno;
400                 goto err_end;
401             } else
402                 err = 0;
403         }
404     }
405     return err;
406
407 err_end:
408     remove(rpath(fs_ctx, newpath));
409     errno = serrno;
410     return err;
411 }
412
413 static int local_link(FsContext *ctx, const char *oldpath, const char *newpath)
414 {
415     char *tmp = qemu_strdup(rpath(ctx, oldpath));
416     int err, serrno = 0;
417
418     if (tmp == NULL) {
419         return -ENOMEM;
420     }
421
422     err = link(tmp, rpath(ctx, newpath));
423     if (err == -1) {
424         serrno = errno;
425     }
426
427     qemu_free(tmp);
428
429     if (err == -1) {
430         errno = serrno;
431     }
432
433     return err;
434 }
435
436 static int local_truncate(FsContext *ctx, const char *path, off_t size)
437 {
438     return truncate(rpath(ctx, path), size);
439 }
440
441 static int local_rename(FsContext *ctx, const char *oldpath,
442                         const char *newpath)
443 {
444     char *tmp;
445     int err;
446
447     tmp = qemu_strdup(rpath(ctx, oldpath));
448
449     err = rename(tmp, rpath(ctx, newpath));
450     if (err == -1) {
451         int serrno = errno;
452         qemu_free(tmp);
453         errno = serrno;
454     } else {
455         qemu_free(tmp);
456     }
457
458     return err;
459
460 }
461
462 static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp)
463 {
464     if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
465             (fs_ctx->fs_sm == SM_PASSTHROUGH)) {
466         return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
467     } else if (fs_ctx->fs_sm == SM_MAPPED) {
468         return local_set_xattr(rpath(fs_ctx, path), credp);
469     } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
470                (fs_ctx->fs_sm == SM_NONE)) {
471         return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
472     }
473     return -1;
474 }
475
476 static int local_utimensat(FsContext *s, const char *path,
477                        const struct timespec *buf)
478 {
479     return utimensat(AT_FDCWD, rpath(s, path), buf, AT_SYMLINK_NOFOLLOW);
480 }
481
482 static int local_remove(FsContext *ctx, const char *path)
483 {
484     return remove(rpath(ctx, path));
485 }
486
487 static int local_fsync(FsContext *ctx, int fd)
488 {
489     return fsync(fd);
490 }
491
492 static int local_statfs(FsContext *s, const char *path, struct statfs *stbuf)
493 {
494    return statfs(rpath(s, path), stbuf);
495 }
496
497 static ssize_t local_lgetxattr(FsContext *ctx, const char *path,
498                                const char *name, void *value, size_t size)
499 {
500     if ((ctx->fs_sm == SM_MAPPED) &&
501         (strncmp(name, "user.virtfs.", 12) == 0)) {
502         /*
503          * Don't allow fetch of user.virtfs namesapce
504          * in case of mapped security
505          */
506         errno = ENOATTR;
507         return -1;
508     }
509
510     return lgetxattr(rpath(ctx, path), name, value, size);
511 }
512
513 static ssize_t local_llistxattr(FsContext *ctx, const char *path,
514                                 void *value, size_t size)
515 {
516     ssize_t retval;
517     ssize_t actual_len = 0;
518     char *orig_value, *orig_value_start;
519     char *temp_value, *temp_value_start;
520     ssize_t xattr_len, parsed_len = 0, attr_len;
521
522     if (ctx->fs_sm != SM_MAPPED) {
523         return llistxattr(rpath(ctx, path), value, size);
524     }
525
526     /* Get the actual len */
527     xattr_len = llistxattr(rpath(ctx, path), value, 0);
528
529     /* Now fetch the xattr and find the actual size */
530     orig_value = qemu_malloc(xattr_len);
531     xattr_len = llistxattr(rpath(ctx, path), orig_value, xattr_len);
532
533     /*
534      * For mapped security model drop user.virtfs namespace
535      * from the list
536      */
537     temp_value = qemu_mallocz(xattr_len);
538     temp_value_start = temp_value;
539     orig_value_start = orig_value;
540     while (xattr_len > parsed_len) {
541         attr_len = strlen(orig_value) + 1;
542         if (strncmp(orig_value, "user.virtfs.", 12) != 0) {
543             /* Copy this entry */
544             strcat(temp_value, orig_value);
545             temp_value  += attr_len;
546             actual_len += attr_len;
547         }
548         parsed_len += attr_len;
549         orig_value += attr_len;
550     }
551     if (!size) {
552         retval = actual_len;
553         goto out;
554     } else if (size >= actual_len) {
555         /* now copy the parsed attribute list back */
556         memset(value, 0, size);
557         memcpy(value, temp_value_start, actual_len);
558         retval = actual_len;
559         goto out;
560     }
561     errno = ERANGE;
562     retval = -1;
563 out:
564     qemu_free(orig_value_start);
565     qemu_free(temp_value_start);
566     return retval;
567 }
568
569 static int local_lsetxattr(FsContext *ctx, const char *path, const char *name,
570                            void *value, size_t size, int flags)
571 {
572     if ((ctx->fs_sm == SM_MAPPED) &&
573         (strncmp(name, "user.virtfs.", 12) == 0)) {
574         /*
575          * Don't allow fetch of user.virtfs namesapce
576          * in case of mapped security
577          */
578         errno = EACCES;
579         return -1;
580     }
581     return lsetxattr(rpath(ctx, path), name, value, size, flags);
582 }
583
584 static int local_lremovexattr(FsContext *ctx,
585                               const char *path, const char *name)
586 {
587     if ((ctx->fs_sm == SM_MAPPED) &&
588         (strncmp(name, "user.virtfs.", 12) == 0)) {
589         /*
590          * Don't allow fetch of user.virtfs namesapce
591          * in case of mapped security
592          */
593         errno = EACCES;
594         return -1;
595     }
596     return lremovexattr(rpath(ctx, path), name);
597 }
598
599
600 FileOperations local_ops = {
601     .lstat = local_lstat,
602     .readlink = local_readlink,
603     .close = local_close,
604     .closedir = local_closedir,
605     .open = local_open,
606     .opendir = local_opendir,
607     .rewinddir = local_rewinddir,
608     .telldir = local_telldir,
609     .readdir = local_readdir,
610     .seekdir = local_seekdir,
611     .readv = local_readv,
612     .lseek = local_lseek,
613     .writev = local_writev,
614     .chmod = local_chmod,
615     .mknod = local_mknod,
616     .mkdir = local_mkdir,
617     .fstat = local_fstat,
618     .open2 = local_open2,
619     .symlink = local_symlink,
620     .link = local_link,
621     .truncate = local_truncate,
622     .rename = local_rename,
623     .chown = local_chown,
624     .utimensat = local_utimensat,
625     .remove = local_remove,
626     .fsync = local_fsync,
627     .statfs = local_statfs,
628     .lgetxattr = local_lgetxattr,
629     .llistxattr = local_llistxattr,
630     .lsetxattr = local_lsetxattr,
631     .lremovexattr = local_lremovexattr,
632 };
This page took 0.062435 seconds and 4 git commands to generate.