@@ -166,7 +166,7 @@ obj-y += qemu-timer.o
# virtio has to be here due to weird dependency between PCI and virtio-net.
# need to fix this properly
obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
-obj-y += virtio-9p.o virtio-9p-debug.o
+obj-y += virtio-9p.o virtio-9p-debug.o virtio-9p-local.o
obj-y += rwhandler.o
obj-$(CONFIG_KVM) += kvm.o kvm-all.o
LIBS+=-lz
new file mode 100644
@@ -0,0 +1,84 @@
+/*
+ * Virtio 9p Posix callback
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include "virtio.h"
+#include "pc.h"
+#include "qemu_socket.h"
+#include "virtio-9p.h"
+#include <sys/uio.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+static const char *base_path;
+
+static const char *rpath(const char *path)
+{
+ /* FIXME: so wrong... */
+ static char buffer[4096];
+ snprintf(buffer, sizeof(buffer), "%s/%s", base_path, path);
+ return buffer;
+}
+
+static int local_lstat(void *opaque, const char *path, struct stat *stbuf)
+{
+ return lstat(rpath(path), stbuf);
+}
+
+static int local_setuid(void *opaque, uid_t uid)
+{
+ struct passwd *pw;
+ gid_t groups[33];
+ int ngroups;
+ static uid_t cur_uid = -1;
+
+ if (cur_uid == uid)
+ return 0;
+
+ if (setreuid(0, 0))
+ return -1;
+
+ pw = getpwuid(uid);
+ if (pw == NULL)
+ return -1;
+
+ ngroups = 33;
+ if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups) == -1)
+ return -1;
+
+ if (setgroups(ngroups, groups))
+ return -1;
+
+ if (setregid(-1, pw->pw_gid))
+ return -1;
+
+ if (setreuid(-1, uid))
+ return -1;
+
+ cur_uid = uid;
+
+ return 0;
+}
+
+static V9fsPosixFileOperations ops = {
+ .lstat = local_lstat,
+ .setuid = local_setuid,
+};
+
+V9fsPosixFileOperations *virtio_9p_init_local(const char *path)
+{
+ base_path = path;
+ return &ops;
+}
@@ -82,6 +82,7 @@ typedef struct V9fsState
V9fsPDU pdus[MAX_REQ];
V9fsPDU *free_pdu;
V9fsFidState *fid_list;
+ V9fsPosixFileOperations *ops;
char *root;
uid_t uid;
} V9fsState;
@@ -91,6 +92,123 @@ int debug_9p_pdu = 1;
extern void pprint_pdu(V9fsPDU *pdu);
+static int posix_lstat(V9fsState *s, V9fsString *path, struct stat *stbuf)
+{
+ return s->ops->lstat(s->ops->opaque, path->data, stbuf);
+}
+
+static int posix_setuid(V9fsState *s, uid_t uid)
+{
+ return s->ops->setuid(s->ops->opaque, uid);
+}
+
+static void v9fs_string_free(V9fsString *str)
+{
+ qemu_free(str->data);
+ str->data = NULL;
+ str->size = 0;
+}
+
+static void v9fs_string_sprintf(V9fsString *str, const char *fmt, ...)
+{
+ va_list ap;
+ int err;
+
+ v9fs_string_free(str);
+
+ va_start(ap, fmt);
+ err = qemu_vasprintf(&str->data, fmt, ap);
+ BUG_ON(err == -1);
+ va_end(ap);
+
+ str->size = err;
+}
+
+static V9fsFidState *lookup_fid(V9fsState *s, int32_t fid)
+{
+ V9fsFidState *f;
+
+ for (f = s->fid_list; f; f = f->next) {
+ if (f->fid == fid) {
+ posix_setuid(s, f->uid);
+ return f;
+ }
+ }
+
+ return NULL;
+}
+
+static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
+{
+ V9fsFidState *f;
+
+ f = lookup_fid(s, fid);
+ if (f)
+ return NULL;
+
+ f = qemu_mallocz(sizeof(V9fsFidState));
+ BUG_ON(f == NULL);
+
+ f->fid = fid;
+ f->fd = -1;
+ f->dir = NULL;
+
+ f->next = s->fid_list;
+ s->fid_list = f;
+
+ return f;
+}
+
+#define P9_QID_TYPE_DIR 0x80
+#define P9_QID_TYPE_SYMLINK 0x02
+
+#define P9_STAT_MODE_DIR 0x80000000
+#define P9_STAT_MODE_APPEND 0x40000000
+#define P9_STAT_MODE_EXCL 0x20000000
+#define P9_STAT_MODE_MOUNT 0x10000000
+#define P9_STAT_MODE_AUTH 0x08000000
+#define P9_STAT_MODE_TMP 0x04000000
+#define P9_STAT_MODE_SYMLINK 0x02000000
+#define P9_STAT_MODE_LINK 0x01000000
+#define P9_STAT_MODE_DEVICE 0x00800000
+#define P9_STAT_MODE_NAMED_PIPE 0x00200000
+#define P9_STAT_MODE_SOCKET 0x00100000
+#define P9_STAT_MODE_SETUID 0x00080000
+#define P9_STAT_MODE_SETGID 0x00040000
+#define P9_STAT_MODE_SETVTX 0x00010000
+
+#define P9_STAT_MODE_SPECIAL (P9_STAT_MODE_NAMED_PIPE | \
+ P9_STAT_MODE_SYMLINK | \
+ P9_STAT_MODE_LINK | \
+ P9_STAT_MODE_DEVICE)
+
+
+/* This is the algorithm from ufs in spfs */
+static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp)
+{
+ size_t size;
+
+ size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path));
+ memcpy(&qidp->path, &stbuf->st_ino, size);
+ qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8);
+ qidp->type = 0;
+ if (S_ISDIR(stbuf->st_mode))
+ qidp->type |= P9_QID_TYPE_DIR;
+ if (S_ISLNK(stbuf->st_mode))
+ qidp->type |= P9_QID_TYPE_SYMLINK;
+}
+
+static void fid_to_qid(V9fsState *s, V9fsFidState *fidp, V9fsQID *qidp)
+{
+ struct stat stbuf;
+ int err;
+
+ err = posix_lstat(s, &fidp->path, &stbuf);
+ BUG_ON(err == -1);
+
+ stat_to_qid(&stbuf, qidp);
+}
+
static V9fsPDU *alloc_pdu(V9fsState *s)
{
V9fsPDU *pdu = NULL;
@@ -111,13 +229,6 @@ static void free_pdu(V9fsState *s, V9fsPDU *pdu)
}
}
-static void v9fs_string_free(V9fsString *str)
-{
- free(str->data);
- str->data = NULL;
- str->size = 0;
-}
-
static size_t pdu_unpack(void *dst, V9fsPDU *pdu, size_t offset, size_t size)
{
struct iovec *sg = pdu->elem.out_sg;
@@ -378,8 +489,33 @@ static void v9fs_version(V9fsState *s, V9fsPDU *pdu)
static void v9fs_attach(V9fsState *s, V9fsPDU *pdu)
{
- if (debug_9p_pdu)
- pprint_pdu(pdu);
+ int32_t fid, afid, n_uname;
+ V9fsString uname, aname;
+ V9fsFidState *fidp;
+ V9fsQID qid;
+ size_t offset = 7;
+ ssize_t err;
+
+ pdu_unmarshal(pdu, offset, "ddssd", &fid, &afid, &uname, &aname, &n_uname);
+
+ fidp = alloc_fid(s, fid);
+ if (fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ fidp->uid = n_uname;
+
+ v9fs_string_sprintf(&fidp->path, "%s", s->root);
+ fid_to_qid(s, fidp, &qid);
+
+ offset += pdu_marshal(pdu, offset, "Q", &qid);
+
+ err = offset;
+out:
+ complete_pdu(s, pdu, err);
+ v9fs_string_free(&uname);
+ v9fs_string_free(&aname);
}
static void v9fs_stat(V9fsState *s, V9fsPDU *pdu)
@@ -528,6 +664,7 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, const char *path)
BUG_ON(s->root == NULL);
s->uid = -1;
+ s->ops = virtio_9p_init_local(path);
s->vdev.get_features = virtio_9p_get_features;
return &s->vdev;
@@ -66,5 +66,38 @@ struct V9fsPDU
V9fsPDU *next;
};
+typedef struct V9fsPosixFileOpertions
+{
+ int (*lstat)(void *, const char *, struct stat *);
+ ssize_t (*readlink)(void *, const char *, char *, size_t);
+ int (*chmod)(void *, const char *, mode_t);
+ int (*chown)(void *, const char *, uid_t, gid_t);
+ int (*mknod)(void *, const char *, mode_t, dev_t);
+ int (*mksock)(void *, const char *);
+ int (*utime)(void *, const char *, const struct utimbuf *);
+ int (*remove)(void *, const char *);
+ int (*symlink)(void *, const char *, const char *);
+ int (*link)(void *, const char *, const char *);
+ int (*setuid)(void *, uid_t);
+ int (*close)(void *, int);
+ int (*closedir)(void *, DIR *);
+ DIR *(*opendir)(void *, const char *);
+ int (*open)(void *, const char *, int);
+ int (*open2)(void *, const char *, int, mode_t);
+ void (*rewinddir)(void *, DIR *);
+ off_t (*telldir)(void *, DIR *);
+ struct dirent *(*readdir)(void *, DIR *);
+ void (*seekdir)(void *, DIR *, off_t);
+ ssize_t (*readv)(void *, int, const struct iovec *, int);
+ ssize_t (*writev)(void *, int, const struct iovec *, int);
+ off_t (*lseek)(void *, int, off_t, int);
+ int (*mkdir)(void *, const char *, mode_t);
+ int (*fstat)(void *, int, struct stat *);
+ int (*rename)(void *, const char *, const char *);
+ int (*truncate)(void *, const char *, off_t);
+ void *opaque;
+} V9fsPosixFileOperations;
+
+V9fsPosixFileOperations *virtio_9p_init_local(const char *path);
#endif
@@ -160,6 +160,7 @@ void *qemu_mallocz(size_t size);
void qemu_free(void *ptr);
char *qemu_strdup(const char *str);
char *qemu_strndup(const char *str, size_t size);
+int qemu_vasprintf(char **strp, const char *fmt, va_list ap);
void *get_mmap_addr(unsigned long size);
@@ -98,3 +98,8 @@ char *qemu_strndup(const char *str, size_t size)
return memcpy(new, str, size);
}
+
+int qemu_vasprintf(char **strp, const char *fmt, va_list ap)
+{
+ return vasprintf(strp, fmt, ap);
+}