diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c
index 246be77..7510827 100644
--- a/fsdev/virtfs-proxy-helper.c
+++ b/fsdev/virtfs-proxy-helper.c
@@ -26,6 +26,8 @@
 #include <sys/fsuid.h>
 #include <stdarg.h>
 #include <stdbool.h>
+#include <sys/vfs.h>
+#include <sys/stat.h>
 #include "qemu-common.h"
 #include "virtio-9p-marshal.h"
 #include "hw/9pfs/virtio-9p-proxy.h"
@@ -257,6 +259,171 @@ static int setfsugid(int uid, int gid)
 }
 
 /*
+ * send response in two parts
+ * 1) ProxyHeader
+ * 2) Response or error status
+ * This function should be called with marshaling response
+ * send_response constructs header part and error part only.
+ * send response sends {ProxyHeader,Response} if the request was success
+ * otherwise sends {ProxyHeader,error status}
+ */
+static int send_response(int sock, struct iovec *iovec, int size)
+{
+    int retval;
+    ProxyHeader header;
+
+    /*
+     * If response size exceeds available iovec->iov_len,
+     * we return EOVERFLOW
+     */
+    if (size > (int)iovec->iov_len) {
+        size = -EOVERFLOW;
+    }
+
+    if (size < 0) {
+        header.type = T_ERROR;
+        header.size = sizeof(size);
+        proxy_marshal(iovec, 1, HDR_SZ, "d", size);
+    } else {
+        header.type = T_SUCCESS;
+        header.size = size;
+    }
+
+    proxy_marshal(iovec, 1, 0, "dd", header.type, header.size);
+    retval = socket_write(sock, iovec->iov_base, header.size + HDR_SZ);
+    if (retval != header.size + HDR_SZ) {
+        return -EIO;
+    }
+    return 0;
+}
+
+static void stat_to_prstat(ProxyStat *pr_stat, struct stat *stat)
+{
+    memset(pr_stat, 0, sizeof(*pr_stat));
+    pr_stat->st_dev = stat->st_dev;
+    pr_stat->st_ino = stat->st_ino;
+    pr_stat->st_nlink = stat->st_nlink;
+    pr_stat->st_mode = stat->st_mode;
+    pr_stat->st_uid = stat->st_uid;
+    pr_stat->st_gid = stat->st_gid;
+    pr_stat->st_rdev = stat->st_rdev;
+    pr_stat->st_size = stat->st_size;
+    pr_stat->st_blksize = stat->st_blksize;
+    pr_stat->st_blocks = stat->st_blocks;
+    pr_stat->st_atim_sec = stat->st_atim.tv_sec;
+    pr_stat->st_atim_nsec = stat->st_atim.tv_nsec;
+    pr_stat->st_mtim_sec = stat->st_mtim.tv_sec;
+    pr_stat->st_mtim_nsec = stat->st_mtim.tv_nsec;
+    pr_stat->st_ctim_sec = stat->st_ctim.tv_sec;
+    pr_stat->st_ctim_nsec = stat->st_ctim.tv_nsec;
+}
+
+static void statfs_to_prstatfs(ProxyStatFS *pr_stfs, struct statfs *stfs)
+{
+    memset(pr_stfs, 0, sizeof(*pr_stfs));
+    pr_stfs->f_type = stfs->f_type;
+    pr_stfs->f_bsize = stfs->f_bsize;
+    pr_stfs->f_blocks = stfs->f_blocks;
+    pr_stfs->f_bfree = stfs->f_bfree;
+    pr_stfs->f_bavail = stfs->f_bavail;
+    pr_stfs->f_files = stfs->f_files;
+    pr_stfs->f_ffree = stfs->f_ffree;
+    pr_stfs->f_fsid[0] = stfs->f_fsid.__val[0];
+    pr_stfs->f_fsid[1] = stfs->f_fsid.__val[1];
+    pr_stfs->f_namelen = stfs->f_namelen;
+    pr_stfs->f_frsize = stfs->f_frsize;
+}
+
+/*
+ * Gets stat/statfs information and packs in out_iovec structure
+ * on success returns number of bytes packed in out_iovec struture
+ * otherwise returns -errno
+ */
+static int do_stat(int type, struct iovec *iovec, struct iovec *out_iovec)
+{
+    V9fsString path;
+    struct stat st_buf;
+    ProxyStat pr_stat;
+    struct statfs stfs_buf;
+    ProxyStatFS pr_stfs;
+    int retval = 0;
+
+    if (proxy_unmarshal(iovec, 1, HDR_SZ, "s", &path) < 0) {
+        return -EOVERFLOW;
+    }
+
+    switch (type) {
+    case T_LSTAT:
+        retval = lstat(path.data, &st_buf);
+        if (retval < 0) {
+            retval = -errno;
+        } else {
+            stat_to_prstat(&pr_stat, &st_buf);
+            retval = proxy_marshal(out_iovec, 1, HDR_SZ,
+                            "qqqdddqqqqqqqqqq", pr_stat.st_dev,
+                            pr_stat.st_ino, pr_stat.st_nlink,
+                            pr_stat.st_mode, pr_stat.st_uid,
+                            pr_stat.st_gid, pr_stat.st_rdev,
+                            pr_stat.st_size, pr_stat.st_blksize,
+                            pr_stat.st_blocks,
+                            pr_stat.st_atim_sec, pr_stat.st_atim_nsec,
+                            pr_stat.st_mtim_sec, pr_stat.st_mtim_nsec,
+                            pr_stat.st_ctim_sec, pr_stat.st_ctim_nsec);
+            if (retval < 0) {
+                retval = -EOVERFLOW;
+            }
+        }
+        break;
+    case T_STATFS:
+        retval = statfs(path.data, &stfs_buf);
+        if (retval < 0) {
+            retval = -errno;
+        } else {
+            statfs_to_prstatfs(&pr_stfs, &stfs_buf);
+            retval = proxy_marshal(out_iovec, 1, HDR_SZ,
+                            "qqqqqqqqqqq", pr_stfs.f_type, pr_stfs.f_bsize,
+                            pr_stfs.f_blocks, pr_stfs.f_bfree, pr_stfs.f_bavail,
+                            pr_stfs.f_files, pr_stfs.f_ffree,
+                            pr_stfs.f_fsid[0], pr_stfs.f_fsid[1],
+                            pr_stfs.f_namelen, pr_stfs.f_frsize);
+            if (retval < 0) {
+                retval = -EOVERFLOW;
+            }
+        }
+        break;
+    }
+    v9fs_string_free(&path);
+    return retval;
+}
+
+static int do_readlink(struct iovec *iovec, struct iovec *out_iovec)
+{
+    V9fsString target, path;
+    int size, retval;
+    char *buffer;
+
+    if (proxy_unmarshal(iovec, 1, HDR_SZ, "sd", &path, &size) < 0) {
+        return -EOVERFLOW;
+    }
+    buffer = g_malloc(size);
+    v9fs_string_init(&target);
+    retval = readlink(path.data, buffer, size);
+    if (retval > 0) {
+        buffer[retval] = '\0';
+        v9fs_string_sprintf(&target, "%s", buffer);
+        retval = proxy_marshal(out_iovec, 1, HDR_SZ, "s", &target);
+        if (retval < 0) {
+            retval = -EOVERFLOW;
+        }
+    } else {
+        retval = -errno;
+    }
+    v9fs_string_free(&target);
+    v9fs_string_free(&path);
+    return retval;
+}
+
+/*
  * create a other filesystem objects and send 0 on success
  * return -errno on error
  */
@@ -371,6 +538,7 @@ static int process_requests(int sock)
     int type, retval = 0;
     V9fsString oldpath, path;
     struct iovec in_iovec, out_iovec;
+    int size = 0;
 
     in_iovec.iov_base = g_malloc(BUFF_SZ);
     in_iovec.iov_len = BUFF_SZ;
@@ -404,6 +572,13 @@ static int process_requests(int sock)
             v9fs_string_free(&oldpath);
             v9fs_string_free(&path);
             break;
+        case T_LSTAT:
+        case T_STATFS:
+            size = do_stat(type, &in_iovec, &out_iovec);
+            break;
+        case T_READLINK:
+            size = do_readlink(&in_iovec, &out_iovec);
+            break;
         default:
             goto error;
             break;
@@ -420,6 +595,13 @@ static int process_requests(int sock)
         case T_LINK:
             send_status(sock, &out_iovec, retval);
             break;
+        case T_LSTAT:
+        case T_STATFS:
+        case T_READLINK:
+            if (send_response(sock, &out_iovec, size) < 0) {
+                goto error;
+            }
+            break;
         default:
             goto error;
             break;
diff --git a/hw/9pfs/virtio-9p-proxy.c b/hw/9pfs/virtio-9p-proxy.c
index f116773..aca799e 100644
--- a/hw/9pfs/virtio-9p-proxy.c
+++ b/hw/9pfs/virtio-9p-proxy.c
@@ -89,6 +89,124 @@ static ssize_t socket_read(int sockfd, void *buff, size_t size)
     return retval;
 }
 
+/* Converts proxy_statfs to VFS statfs structure */
+static void prstatfs_to_statfs(struct statfs *stfs, ProxyStatFS *prstfs)
+{
+    memset(stfs, 0, sizeof(*stfs));
+    stfs->f_type = prstfs->f_type;
+    stfs->f_bsize = prstfs->f_bsize;
+    stfs->f_blocks = prstfs->f_blocks;
+    stfs->f_bfree = prstfs->f_bfree;
+    stfs->f_bavail = prstfs->f_bavail;
+    stfs->f_files = prstfs->f_files;
+    stfs->f_ffree = prstfs->f_ffree;
+    stfs->f_fsid.__val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFUL;
+    stfs->f_fsid.__val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFFUL;
+    stfs->f_namelen = prstfs->f_namelen;
+    stfs->f_frsize = prstfs->f_frsize;
+}
+
+/* Converts proxy_stat structure to VFS stat structure */
+static void prstat_to_stat(struct stat *stbuf, ProxyStat *prstat)
+{
+   memset(stbuf, 0, sizeof(*stbuf));
+   stbuf->st_dev = prstat->st_dev;
+   stbuf->st_ino = prstat->st_ino;
+   stbuf->st_nlink = prstat->st_nlink;
+   stbuf->st_mode = prstat->st_mode;
+   stbuf->st_uid = prstat->st_uid;
+   stbuf->st_gid = prstat->st_gid;
+   stbuf->st_rdev = prstat->st_rdev;
+   stbuf->st_size = prstat->st_size;
+   stbuf->st_blksize = prstat->st_blksize;
+   stbuf->st_blocks = prstat->st_blocks;
+   stbuf->st_atim.tv_sec = prstat->st_atim_sec;
+   stbuf->st_atim.tv_nsec = prstat->st_atim_nsec;
+   stbuf->st_mtime = prstat->st_mtim_sec;
+   stbuf->st_mtim.tv_nsec = prstat->st_mtim_nsec;
+   stbuf->st_ctime = prstat->st_ctim_sec;
+   stbuf->st_ctim.tv_nsec = prstat->st_ctim_nsec;
+}
+
+/*
+ * Response contains two parts
+ * {header, data}
+ * header.type == T_ERROR, data -> -errno
+ * header.type == T_SUCCESS, data -> response
+ * size of errno/response is given by header.size
+ */
+static int v9fs_receive_response(V9fsProxy *proxy, int type,
+                int *sock_error, void *response)
+{
+    int retval, error;
+    ProxyHeader header;
+    struct iovec *reply = &proxy->in_iovec;
+
+    *sock_error = 0;
+
+    retval = socket_read(proxy->sockfd, reply->iov_base, HDR_SZ);
+    if (retval != HDR_SZ) {
+        *sock_error = 1;
+        return -EIO;
+    }
+    proxy_unmarshal(reply, 1, 0, "dd", &header.type, &header.size);
+    if (header.size > BUFF_SZ) {
+        error_report("response exceeds maximum size\n");
+        return -EIO;
+    }
+
+    retval = socket_read(proxy->sockfd, reply->iov_base + HDR_SZ, header.size);
+    if (retval != header.size) {
+        *sock_error = 1;
+        return -EIO;
+    }
+    /* there was an error during processing request */
+    if (header.type == T_ERROR) {
+        proxy_unmarshal(reply, 1, HDR_SZ, "d", &error);
+        return error;
+    }
+
+    switch (type) {
+    case T_LSTAT: {
+        ProxyStat prstat;
+        proxy_unmarshal(reply, 1, HDR_SZ,
+                        "qqqdddqqqqqqqqqq", &prstat.st_dev,
+                        &prstat.st_ino, &prstat.st_nlink,
+                        &prstat.st_mode, &prstat.st_uid,
+                        &prstat.st_gid, &prstat.st_rdev,
+                        &prstat.st_size, &prstat.st_blksize,
+                        &prstat.st_blocks,
+                        &prstat.st_atim_sec, &prstat.st_atim_nsec,
+                        &prstat.st_mtim_sec, &prstat.st_mtim_nsec,
+                        &prstat.st_ctim_sec, &prstat.st_ctim_nsec);
+        prstat_to_stat(response, &prstat);
+        break;
+    }
+    case T_STATFS: {
+        ProxyStatFS prstfs;
+        proxy_unmarshal(reply, 1, HDR_SZ,
+                    "qqqqqqqqqqq", &prstfs.f_type, &prstfs.f_bsize,
+                    &prstfs.f_blocks, &prstfs.f_bfree, &prstfs.f_bavail,
+                    &prstfs.f_files, &prstfs.f_ffree,
+                    &prstfs.f_fsid[0], &prstfs.f_fsid[1],
+                    &prstfs.f_namelen, &prstfs.f_frsize);
+	    prstatfs_to_statfs(response, &prstfs);
+        break;
+    }
+    case T_READLINK: {
+        V9fsString target;
+        proxy_unmarshal(reply, 1, HDR_SZ, "s", &target);
+        strcpy(response, target.data);
+        v9fs_string_free(&target);
+        break;
+    }
+    default:
+        *sock_error = 1;
+        return -1;
+    }
+
+    return 0;
+}
 
 static int v9fs_receive_status(V9fsProxy *proxy, int *sock_error,
                 struct iovec *reply)
@@ -127,6 +245,7 @@ static int v9fs_request(V9fsProxy *proxy, int type,
     int sock_error, flags, mode, uid, gid;
     struct iovec *iovec = NULL, *reply = NULL;
     dev_t rdev;
+    int size = 0;
 
     qemu_mutex_lock(&proxy->mutex);
 
@@ -218,6 +337,37 @@ static int v9fs_request(V9fsProxy *proxy, int type,
         proxy_marshal(iovec, 1, 0, "dd", header.type, header.size);
         header.size += HDR_SZ;
         break;
+    case T_LSTAT:
+        path = va_arg(ap, V9fsString *);
+        header.size = proxy_marshal(iovec, 1, HDR_SZ, "s", path);
+        if (header.size < 0) {
+            goto out_overflow;
+	}
+        header.type = T_LSTAT;
+        proxy_marshal(iovec, 1, 0, "dd", header.type, header.size);
+        header.size += HDR_SZ;
+        break;
+    case T_READLINK:
+        path = va_arg(ap, V9fsString *);
+        size = va_arg(ap, int);
+        header.size = proxy_marshal(iovec, 1, HDR_SZ, "sd", path, size);
+        if (header.size < 0) {
+            goto out_overflow;
+	}
+        header.type = T_READLINK;
+        proxy_marshal(iovec, 1, 0, "dd", header.type, header.size);
+        header.size += HDR_SZ;
+        break;
+    case T_STATFS:
+        path = va_arg(ap, V9fsString *);
+        header.size = proxy_marshal(iovec, 1, HDR_SZ, "s", path);
+        if (header.size < 0) {
+            goto out_overflow;
+	}
+        header.type = T_STATFS;
+        proxy_marshal(iovec, 1, 0, "dd", header.type, header.size);
+        header.size += HDR_SZ;
+        break;
     default:
         error_report("Invalid type %d\n", type);
         va_end(ap);
@@ -252,6 +402,14 @@ static int v9fs_request(V9fsProxy *proxy, int type,
             goto close_error;
         }
         break;
+    case T_LSTAT:
+    case T_READLINK:
+    case T_STATFS:
+        retval = v9fs_receive_response(proxy, type, &sock_error, response);
+        if (sock_error) {
+            goto close_error;
+        }
+        break;
     }
     qemu_mutex_unlock(&proxy->mutex);
     return retval;
@@ -269,15 +427,26 @@ error:
 
 static int proxy_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
 {
-    errno = EOPNOTSUPP;
-    return -1;
+    int retval;
+    retval = v9fs_request(fs_ctx->private, T_LSTAT, stbuf, "s", fs_path);
+    if (retval < 0) {
+        errno = -retval;
+        return -1;
+    }
+    return retval;
 }
 
 static ssize_t proxy_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
                               char *buf, size_t bufsz)
 {
-    errno = EOPNOTSUPP;
-    return -1;
+    int retval;
+    retval = v9fs_request(fs_ctx->private, T_READLINK, buf, "sd",
+                          fs_path, bufsz);
+    if (retval < 0) {
+        errno = -retval;
+        return -1;
+    }
+    return strlen(buf);
 }
 
 static int proxy_close(FsContext *ctx, V9fsFidOpenState *fs)
@@ -493,8 +662,7 @@ static int proxy_link(FsContext *ctx, V9fsPath *oldpath,
     v9fs_string_init(&newpath);
     v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
 
-    retval = v9fs_request(ctx->private, T_LINK, NULL, "ss",
-                          oldpath, &newpath);
+    retval = v9fs_request(ctx->private, T_LINK, NULL, "ss", oldpath, &newpath);
     v9fs_string_free(&newpath);
     if (retval < 0) {
         errno = -retval;
@@ -547,8 +715,13 @@ static int proxy_fsync(FsContext *ctx, V9fsFidOpenState *fs, int datasync)
 
 static int proxy_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
 {
-    errno = EOPNOTSUPP;
-    return -1;
+    int retval;
+    retval = v9fs_request(s->private, T_STATFS, stbuf, "s", fs_path);
+    if (retval < 0) {
+        errno = -retval;
+        return -1;
+    }
+    return retval;
 }
 
 static ssize_t proxy_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
diff --git a/hw/9pfs/virtio-9p-proxy.h b/hw/9pfs/virtio-9p-proxy.h
index 9a43f64..4d8dcc2 100644
--- a/hw/9pfs/virtio-9p-proxy.h
+++ b/hw/9pfs/virtio-9p-proxy.h
@@ -30,6 +30,40 @@ enum {
     T_MKDIR,
     T_SYMLINK,
     T_LINK,
+    T_LSTAT,
+    T_READLINK,
+    T_STATFS,
 };
 
+typedef struct {
+    uint64_t st_dev;
+    uint64_t st_ino;
+    uint64_t st_nlink;
+    uint32_t st_mode;
+    uint32_t st_uid;
+    uint32_t st_gid;
+    uint64_t st_rdev;
+    uint64_t st_size;
+    uint64_t st_blksize;
+    uint64_t st_blocks;
+    uint64_t st_atim_sec;
+    uint64_t st_atim_nsec;
+    uint64_t st_mtim_sec;
+    uint64_t st_mtim_nsec;
+    uint64_t st_ctim_sec;
+    uint64_t st_ctim_nsec;
+} ProxyStat;
+
+typedef struct {
+    uint64_t f_type;
+    uint64_t f_bsize;
+    uint64_t f_blocks;
+    uint64_t f_bfree;
+    uint64_t f_bavail;
+    uint64_t f_files;
+    uint64_t f_ffree;
+    uint64_t f_fsid[2];
+    uint64_t f_namelen;
+    uint64_t f_frsize;
+} ProxyStatFS;
 #endif
