diff mbox

[-v2,03/22] virtio-9p: Implement P9_TATTACH

Message ID 1268730920-14584-4-git-send-email-aneesh.kumar@linux.vnet.ibm.com
State New
Headers show

Commit Message

Aneesh Kumar K.V March 16, 2010, 9:15 a.m. UTC
From: Anthony Liguori <aliguori@us.ibm.com>

[jvrao@linux.vnet.ibm.com: Added qemu_vasprintf]

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
---
 Makefile.target      |    2 +-
 hw/virtio-9p-local.c |   84 +++++++++++++++++++++++++++
 hw/virtio-9p.c       |  155 +++++++++++++++++++++++++++++++++++++++++++++++---
 hw/virtio-9p.h       |   33 +++++++++++
 qemu-common.h        |    1 +
 qemu-malloc.c        |    5 ++
 6 files changed, 270 insertions(+), 10 deletions(-)
 create mode 100644 hw/virtio-9p-local.c

Comments

Anthony Liguori March 26, 2010, 4:17 p.m. UTC | #1
On 03/16/2010 04:15 AM, Aneesh Kumar K.V wrote:
> From: Anthony Liguori<aliguori@us.ibm.com>
>
> [jvrao@linux.vnet.ibm.com: Added qemu_vasprintf]
>
> Signed-off-by: Anthony Liguori<aliguori@us.ibm.com>
> Signed-off-by: Aneesh Kumar K.V<aneesh.kumar@linux.vnet.ibm.com>
> ---
>   Makefile.target      |    2 +-
>   hw/virtio-9p-local.c |   84 +++++++++++++++++++++++++++
>   hw/virtio-9p.c       |  155 +++++++++++++++++++++++++++++++++++++++++++++++---
>   hw/virtio-9p.h       |   33 +++++++++++
>   qemu-common.h        |    1 +
>   qemu-malloc.c        |    5 ++
>   6 files changed, 270 insertions(+), 10 deletions(-)
>   create mode 100644 hw/virtio-9p-local.c
>
> diff --git a/Makefile.target b/Makefile.target
> index 33f9fcb..97f32a9 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -172,7 +172,7 @@ obj-y = vl.o async.o monitor.o pci.o pci_host.o pcie_host.o machine.o gdbstub.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-pci.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
>   obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
> diff --git a/hw/virtio-9p-local.c b/hw/virtio-9p-local.c
> new file mode 100644
> index 0000000..1d2523b
> --- /dev/null
> +++ b/hw/virtio-9p-local.c
>    

This should be a logically separate patch.

> @@ -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>
>    

These headers are almost certainly going to break the build on windows.  
Some care is needed here.

> +
> +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;
> +}
> diff --git a/hw/virtio-9p.c b/hw/virtio-9p.c
> index 53b3d78..fdff589 100644
> --- a/hw/virtio-9p.c
> +++ b/hw/virtio-9p.c
> @@ -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;
> diff --git a/hw/virtio-9p.h b/hw/virtio-9p.h
> index 2aa67d0..8e15bf0 100644
> --- a/hw/virtio-9p.h
> +++ b/hw/virtio-9p.h
> @@ -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
> diff --git a/qemu-common.h b/qemu-common.h
> index 805be1a..ebe7088 100644
> --- a/qemu-common.h
> +++ b/qemu-common.h
> @@ -159,6 +159,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);
>
> diff --git a/qemu-malloc.c b/qemu-malloc.c
> index 6cdc5de..d6de067 100644
> --- a/qemu-malloc.c
> +++ b/qemu-malloc.c
> @@ -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);
> +}
>    

asprintf() is not universally available so it cannot be wrapped this 
naively.

Regards,

Anthony Liguori
jvrao March 26, 2010, 7:12 p.m. UTC | #2
Anthony Liguori wrote:
> On 03/16/2010 04:15 AM, Aneesh Kumar K.V wrote:
>> From: Anthony Liguori<aliguori@us.ibm.com>
>>
>> [jvrao@linux.vnet.ibm.com: Added qemu_vasprintf]
>>
>> Signed-off-by: Anthony Liguori<aliguori@us.ibm.com>
>> Signed-off-by: Aneesh Kumar K.V<aneesh.kumar@linux.vnet.ibm.com>
>> ---
>>   Makefile.target      |    2 +-
>>   hw/virtio-9p-local.c |   84 +++++++++++++++++++++++++++
>>   hw/virtio-9p.c       |  155
>> +++++++++++++++++++++++++++++++++++++++++++++++---
>>   hw/virtio-9p.h       |   33 +++++++++++
>>   qemu-common.h        |    1 +
>>   qemu-malloc.c        |    5 ++
>>   6 files changed, 270 insertions(+), 10 deletions(-)
>>   create mode 100644 hw/virtio-9p-local.c
>>
>> diff --git a/Makefile.target b/Makefile.target
>> index 33f9fcb..97f32a9 100644
>> --- a/Makefile.target
>> +++ b/Makefile.target
>> @@ -172,7 +172,7 @@ obj-y = vl.o async.o monitor.o pci.o pci_host.o
>> pcie_host.o machine.o gdbstub.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-pci.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
>>   obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
>> diff --git a/hw/virtio-9p-local.c b/hw/virtio-9p-local.c
>> new file mode 100644
>> index 0000000..1d2523b
>> --- /dev/null
>> +++ b/hw/virtio-9p-local.c
>>    
> 
> This should be a logically separate patch.
> 
>> @@ -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>
>>    
> 
> These headers are almost certainly going to break the build on windows. 
> Some care is needed here.
> 
>> +
>> +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;
>> +}
>> diff --git a/hw/virtio-9p.c b/hw/virtio-9p.c
>> index 53b3d78..fdff589 100644
>> --- a/hw/virtio-9p.c
>> +++ b/hw/virtio-9p.c
>> @@ -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;
>> diff --git a/hw/virtio-9p.h b/hw/virtio-9p.h
>> index 2aa67d0..8e15bf0 100644
>> --- a/hw/virtio-9p.h
>> +++ b/hw/virtio-9p.h
>> @@ -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
>> diff --git a/qemu-common.h b/qemu-common.h
>> index 805be1a..ebe7088 100644
>> --- a/qemu-common.h
>> +++ b/qemu-common.h
>> @@ -159,6 +159,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);
>>
>> diff --git a/qemu-malloc.c b/qemu-malloc.c
>> index 6cdc5de..d6de067 100644
>> --- a/qemu-malloc.c
>> +++ b/qemu-malloc.c
>> @@ -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);
>> +}
>>    
> 
> asprintf() is not universally available so it cannot be wrapped this
> naively.

Is there a way to find out what is generally available..and what is not?
I know QEMU need to work with different OSes, do we have OS specific plug-ins
to address these OS specific/dependent calls? 

Thanks,
JV

 
> 
> Regards,
> 
> Anthony Liguori
> 
> 
>
jvrao March 26, 2010, 8:06 p.m. UTC | #3
Anthony Liguori wrote:
> On 03/16/2010 04:15 AM, Aneesh Kumar K.V wrote:
>> From: Anthony Liguori<aliguori@us.ibm.com>
>>
>> [jvrao@linux.vnet.ibm.com: Added qemu_vasprintf]
>>
>> Signed-off-by: Anthony Liguori<aliguori@us.ibm.com>
>> Signed-off-by: Aneesh Kumar K.V<aneesh.kumar@linux.vnet.ibm.com>
>> ---
>>   Makefile.target      |    2 +-
>>   hw/virtio-9p-local.c |   84 +++++++++++++++++++++++++++
>>   hw/virtio-9p.c       |  155
>> +++++++++++++++++++++++++++++++++++++++++++++++---
>>   hw/virtio-9p.h       |   33 +++++++++++
>>   qemu-common.h        |    1 +
>>   qemu-malloc.c        |    5 ++
>>   6 files changed, 270 insertions(+), 10 deletions(-)
>>   create mode 100644 hw/virtio-9p-local.c
>>
>> diff --git a/Makefile.target b/Makefile.target
>> index 33f9fcb..97f32a9 100644
>> --- a/Makefile.target
>> +++ b/Makefile.target
>> @@ -172,7 +172,7 @@ obj-y = vl.o async.o monitor.o pci.o pci_host.o
>> pcie_host.o machine.o gdbstub.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-pci.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
>>   obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
>> diff --git a/hw/virtio-9p-local.c b/hw/virtio-9p-local.c
>> new file mode 100644
>> index 0000000..1d2523b
>> --- /dev/null
>> +++ b/hw/virtio-9p-local.c
>>    
> 
> This should be a logically separate patch.
> 
>> @@ -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>
>>    
> 
> These headers are almost certainly going to break the build on windows. 
> Some care is needed here.

I don't think we are going to build this on Windows. Will it be OK if we
make changes to Makefile so that it is valid only on Linux?

Thanks,
JV



> 
>> +
>> +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;
>> +}
>> diff --git a/hw/virtio-9p.c b/hw/virtio-9p.c
>> index 53b3d78..fdff589 100644
>> --- a/hw/virtio-9p.c
>> +++ b/hw/virtio-9p.c
>> @@ -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;
>> diff --git a/hw/virtio-9p.h b/hw/virtio-9p.h
>> index 2aa67d0..8e15bf0 100644
>> --- a/hw/virtio-9p.h
>> +++ b/hw/virtio-9p.h
>> @@ -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
>> diff --git a/qemu-common.h b/qemu-common.h
>> index 805be1a..ebe7088 100644
>> --- a/qemu-common.h
>> +++ b/qemu-common.h
>> @@ -159,6 +159,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);
>>
>> diff --git a/qemu-malloc.c b/qemu-malloc.c
>> index 6cdc5de..d6de067 100644
>> --- a/qemu-malloc.c
>> +++ b/qemu-malloc.c
>> @@ -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);
>> +}
>>    
> 
> asprintf() is not universally available so it cannot be wrapped this
> naively.
> 
> Regards,
> 
> Anthony Liguori
> 
> 
>
diff mbox

Patch

diff --git a/Makefile.target b/Makefile.target
index 33f9fcb..97f32a9 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -172,7 +172,7 @@  obj-y = vl.o async.o monitor.o pci.o pci_host.o pcie_host.o machine.o gdbstub.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-pci.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
 obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
diff --git a/hw/virtio-9p-local.c b/hw/virtio-9p-local.c
new file mode 100644
index 0000000..1d2523b
--- /dev/null
+++ b/hw/virtio-9p-local.c
@@ -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;
+}
diff --git a/hw/virtio-9p.c b/hw/virtio-9p.c
index 53b3d78..fdff589 100644
--- a/hw/virtio-9p.c
+++ b/hw/virtio-9p.c
@@ -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;
diff --git a/hw/virtio-9p.h b/hw/virtio-9p.h
index 2aa67d0..8e15bf0 100644
--- a/hw/virtio-9p.h
+++ b/hw/virtio-9p.h
@@ -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
diff --git a/qemu-common.h b/qemu-common.h
index 805be1a..ebe7088 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -159,6 +159,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);
 
diff --git a/qemu-malloc.c b/qemu-malloc.c
index 6cdc5de..d6de067 100644
--- a/qemu-malloc.c
+++ b/qemu-malloc.c
@@ -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);
+}