diff --git a/hw/9pfs/proxy.h b/hw/9pfs/proxy.h
index 69e7baa..5564eb5 100644
--- a/hw/9pfs/proxy.h
+++ b/hw/9pfs/proxy.h
@@ -17,6 +17,10 @@ typedef struct {
 enum {
     T_OPEN = 1,
     T_CREATE,
+    T_MKNOD,
+    T_MKDIR,
+    T_SYMLINK,
+    T_LINK,
 };
 
 #endif
diff --git a/hw/9pfs/virtfs-proxy-helper.c b/hw/9pfs/virtfs-proxy-helper.c
index 73609e1..82aa267 100644
--- a/hw/9pfs/virtfs-proxy-helper.c
+++ b/hw/9pfs/virtfs-proxy-helper.c
@@ -268,6 +268,48 @@ static int setfsugid(int uid, int gid)
 }
 
 /*
+ * create a other filesystem objects and send 0 on success
+ * return -errno on error
+ */
+static int do_create_others(int type, struct iovec *iovec)
+{
+    V9fsString oldpath, path;
+    int retval, mode, uid, gid, cur_uid, cur_gid;
+    dev_t rdev;
+    int offset;
+
+    cur_uid = geteuid();
+    cur_gid = getegid();
+
+    offset = v9fs_unmarshal(iovec, 1, 0, "dd", &uid, &gid);
+    if (setfsugid(uid, gid) < 0) {
+        return -EPERM;
+    }
+    switch (type) {
+    case T_MKNOD:
+        v9fs_unmarshal(iovec, 1, offset, "sdq", &path, &mode, &rdev);
+        retval = mknod(path.data, mode, rdev);
+        break;
+    case T_MKDIR:
+        v9fs_unmarshal(iovec, 1, offset, "sd", &path, &mode);
+        retval = mkdir(path.data, mode);
+        break;
+    case T_SYMLINK:
+        v9fs_unmarshal(iovec, 1, offset, "ss", &oldpath, &path);
+        retval = symlink(oldpath.data, path.data);
+        v9fs_string_free(&oldpath);
+        break;
+    }
+
+    if (retval < 0) {
+        retval = -errno;
+    }
+    v9fs_string_free(&path);
+    setfsugid(cur_uid, cur_gid);
+    return retval;
+}
+
+/*
  * create a file and send fd on success
  * return -errno on error
  */
@@ -325,6 +367,7 @@ static int process_requests(int sock)
     int type, retval;
     struct iovec iovec;
     int valid_fd;
+    V9fsString oldpath, path;
 
     iovec.iov_base = g_malloc(BUFF_SZ);
     iovec.iov_len = BUFF_SZ;
@@ -344,6 +387,20 @@ static int process_requests(int sock)
                 valid_fd = 1;
             }
             break;
+        case T_MKNOD:
+        case T_MKDIR:
+        case T_SYMLINK:
+            retval = do_create_others(type, &iovec);
+            break;
+        case T_LINK:
+            v9fs_unmarshal(&iovec, 1, 0, "ss", &oldpath, &path);
+            retval = link(oldpath.data, path.data);
+            if (retval < 0) {
+                retval = -errno;
+            }
+            v9fs_string_free(&oldpath);
+            v9fs_string_free(&path);
+            break;
         default:
             goto error;
             break;
@@ -353,7 +410,11 @@ static int process_requests(int sock)
         switch (type) {
         case T_OPEN:
         case T_CREATE:
-            sendfd(sock, retval, valid_fd);
+        case T_MKNOD:
+        case T_MKDIR:
+        case T_SYMLINK:
+        case T_LINK:
+             sendfd(sock, retval, valid_fd);
             break;
         default:
             break;
diff --git a/hw/9pfs/virtio-9p-proxy.c b/hw/9pfs/virtio-9p-proxy.c
index d686454..5f5eb35 100644
--- a/hw/9pfs/virtio-9p-proxy.c
+++ b/hw/9pfs/virtio-9p-proxy.c
@@ -97,9 +97,10 @@ static int v9fs_request(V9fsProxy *proxy, int type,
     int retval;
     ProxyHeader header;
     va_list ap;
-    V9fsString *path;
+    V9fsString *path, *oldpath;
     int sock_error, flags, mode, uid, gid;
     struct iovec *iovec = NULL;
+    dev_t rdev;
 
     qemu_mutex_lock(&proxy->mutex);
 
@@ -131,6 +132,49 @@ static int v9fs_request(V9fsProxy *proxy, int type,
         v9fs_marshal(iovec, 1, 0, "dd", header.type, header.size);
         header.size += sizeof(header);
         break;
+    case T_MKNOD:
+        path = va_arg(ap, V9fsString *);
+        mode = va_arg(ap, int);
+        rdev = va_arg(ap, long int);
+        uid = va_arg(ap, int);
+        gid = va_arg(ap, int);
+        header.size = v9fs_marshal(iovec, 1, sizeof(ProxyHeader), "ddsdq",
+                        uid, gid, path, mode, rdev);
+        header.type = T_MKNOD;
+        v9fs_marshal(iovec, 1, 0, "dd", header.type, header.size);
+        header.size += sizeof(header);
+        break;
+    case T_MKDIR:
+        path = va_arg(ap, V9fsString *);
+        mode = va_arg(ap, int);
+        uid = va_arg(ap, int);
+        gid = va_arg(ap, int);
+        header.size = v9fs_marshal(iovec, 1, sizeof(ProxyHeader), "ddsd",
+                        uid, gid, path, mode);
+        header.type = T_MKDIR;
+        v9fs_marshal(iovec, 1, 0, "dd", header.type, header.size);
+        header.size += sizeof(header);
+        break;
+    case T_SYMLINK:
+        oldpath = va_arg(ap, V9fsString *);
+        path = va_arg(ap, V9fsString *);
+        uid = va_arg(ap, int);
+        gid = va_arg(ap, int);
+        header.size = v9fs_marshal(iovec, 1, sizeof(ProxyHeader), "ddss",
+                        uid, gid, oldpath, path);
+        header.type = T_SYMLINK;
+        v9fs_marshal(iovec, 1, 0, "dd", header.type, header.size);
+        header.size += sizeof(header);
+        break;
+    case T_LINK:
+        oldpath = va_arg(ap, V9fsString *);
+        path = va_arg(ap, V9fsString *);
+        header.size = v9fs_marshal(iovec, 1, sizeof(ProxyHeader), "ss",
+                        oldpath, path);
+        header.type = T_LINK;
+        v9fs_marshal(iovec, 1, 0, "dd", header.type, header.size);
+        header.size += sizeof(header);
+        break;
     default:
         fprintf(stderr, "Invalid type %d\n", type);
         goto close_error;
@@ -149,6 +193,10 @@ static int v9fs_request(V9fsProxy *proxy, int type,
     switch (type) {
     case T_OPEN:
     case T_CREATE:
+    case T_MKNOD:
+    case T_MKDIR:
+    case T_SYMLINK:
+    case T_LINK:
         retval = v9fs_receivefd(proxy->sockfd, &sock_error);
         if (sock_error) {
             goto close_error;
@@ -294,15 +342,40 @@ static int proxy_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
 static int proxy_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
                        const char *name, FsCred *credp)
 {
-    errno = EOPNOTSUPP;
-    return -1;
+    V9fsString fullname;
+    int retval;
+    v9fs_string_init(&fullname);
+    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+
+    retval = v9fs_request(fs_ctx->private, T_MKNOD, NULL, "sdqdd",
+                    &fullname, credp->fc_mode, credp->fc_rdev,
+                    credp->fc_uid, credp->fc_gid);
+    v9fs_string_free(&fullname);
+    if (retval < 0) {
+        errno = -retval;
+        retval = -1;
+    }
+    return retval;
 }
 
 static int proxy_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
                        const char *name, FsCred *credp)
 {
-    errno = EOPNOTSUPP;
-    return -1;
+    V9fsString fullname;
+    int retval;
+
+    v9fs_string_init(&fullname);
+    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+
+    retval = v9fs_request(fs_ctx->private, T_MKDIR, NULL, "sddd", &fullname,
+                    credp->fc_mode, credp->fc_uid, credp->fc_gid);
+    v9fs_string_free(&fullname);
+    if (retval < 0) {
+        errno = -retval;
+        retval = -1;
+    }
+    v9fs_string_free(&fullname);
+    return retval;
 }
 
 static int proxy_fstat(FsContext *fs_ctx,
@@ -332,19 +405,45 @@ static int proxy_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
     return fs->fd;
 }
 
-
 static int proxy_symlink(FsContext *fs_ctx, const char *oldpath,
                          V9fsPath *dir_path, const char *name, FsCred *credp)
 {
-    errno = EOPNOTSUPP;
-    return -1;
+    V9fsString fullname, target;
+    int retval;
+
+    v9fs_string_init(&fullname);
+    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+    v9fs_string_init(&target);
+    v9fs_string_sprintf(&target, "%s", oldpath);
+
+    retval = v9fs_request(fs_ctx->private, T_SYMLINK, NULL, "ssdd",
+                    &target, &fullname, credp->fc_uid, credp->fc_gid);
+    v9fs_string_free(&fullname);
+    if (retval < 0) {
+        errno = -retval;
+        retval = -1;
+    }
+    return retval;
 }
 
 static int proxy_link(FsContext *ctx, V9fsPath *oldpath,
                       V9fsPath *dirpath, const char *name)
 {
-    errno = EOPNOTSUPP;
-    return -1;
+    int retval;
+    V9fsString newpath;
+
+    v9fs_string_init(&newpath);
+    v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
+
+    retval = v9fs_request(ctx->private, T_LINK, NULL, "ss",
+                    oldpath, &newpath);
+    v9fs_string_free(&newpath);
+    if (retval < 0) {
+        errno = -retval;
+        retval = -1;
+    }
+    v9fs_string_free(&newpath);
+    return retval;
 }
 
 static int proxy_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
