diff --git a/hw/file-op-9p.h b/hw/file-op-9p.h
index 120c803..d5bab9a 100644
--- a/hw/file-op-9p.h
+++ b/hw/file-op-9p.h
@@ -75,6 +75,8 @@ typedef struct FileOperations
     int (*truncate)(FsContext *, const char *, off_t);
     int (*fsync)(FsContext *, int);
     int (*statfs)(FsContext *s, const char *path, struct statfs *stbuf);
+    int (*lgetxattr)(FsContext *, const char *, const char *, void *, size_t);
+    int (*llistxattr)(FsContext *, const char *, void *, size_t);
     void *opaque;
 } FileOperations;
 #endif
diff --git a/hw/virtio-9p-debug.c b/hw/virtio-9p-debug.c
index 7325418..5be1670 100644
--- a/hw/virtio-9p-debug.c
+++ b/hw/virtio-9p-debug.c
@@ -540,6 +540,16 @@ void pprint_pdu(V9fsPDU *pdu)
     case P9_RWSTAT:
         fprintf(llogfile, "RWSTAT: (");
         break;
+    case P9_TXATTRWALK:
+        fprintf(llogfile, "TXATTRWALK: (");
+        pprint_int32(pdu, 0, &offset, "fid");
+        pprint_int32(pdu, 0, &offset, ", newfid");
+        pprint_str(pdu, 0, &offset, ", xattr name");
+        break;
+    case P9_RXATTRWALK:
+        fprintf(llogfile, "RXATTRWALK: (");
+        pprint_int64(pdu, 1, &offset, "xattrsize");
+        break;
     default:
         fprintf(llogfile, "unknown(%d): (", pdu->id);
         break;
diff --git a/hw/virtio-9p-local.c b/hw/virtio-9p-local.c
index dd60354..97b1544 100644
--- a/hw/virtio-9p-local.c
+++ b/hw/virtio-9p-local.c
@@ -472,6 +472,18 @@ static int local_statfs(FsContext *s, const char *path, struct statfs *stbuf)
    return statfs(rpath(s, path), stbuf);
 }
 
+static int local_lgetxattr(FsContext *ctx, const char *path, const char *name,
+                           void *value, size_t size)
+{
+    return lgetxattr(rpath(ctx, path), name, value, size);
+}
+
+static int local_llistxattr(FsContext *ctx, const char *path,
+                            void *value, size_t size)
+{
+    return llistxattr(rpath(ctx, path), value, size);
+}
+
 FileOperations local_ops = {
     .lstat = local_lstat,
     .readlink = local_readlink,
@@ -500,4 +512,6 @@ FileOperations local_ops = {
     .remove = local_remove,
     .fsync = local_fsync,
     .statfs = local_statfs,
+    .lgetxattr = local_lgetxattr,
+    .llistxattr = local_llistxattr,
 };
diff --git a/hw/virtio-9p.c b/hw/virtio-9p.c
index 2558e0e..de2ec41 100644
--- a/hw/virtio-9p.c
+++ b/hw/virtio-9p.c
@@ -270,6 +270,21 @@ static int v9fs_do_fsync(V9fsState *s, int fd)
     return s->ops->fsync(&s->ctx, fd);
 }
 
+static int v9fs_do_lgetxattr(V9fsState *s, V9fsString *path,
+                             V9fsString *xattr_name,
+                             void *value, size_t size)
+{
+    return s->ops->lgetxattr(&s->ctx, path->data,
+                             xattr_name->data, value, size);
+}
+
+static int v9fs_do_llistxattr(V9fsState *s, V9fsString *path,
+                              void *value, size_t size)
+{
+    return s->ops->llistxattr(&s->ctx, path->data,
+                              value, size);
+}
+
 static void v9fs_string_init(V9fsString *str)
 {
     str->data = NULL;
@@ -1640,6 +1655,31 @@ out:
     qemu_free(vs);
 }
 
+static void v9fs_xattr_read(V9fsState *s, V9fsReadState *vs)
+{
+    ssize_t err = 0;
+    int read_count;
+    int64_t xattr_len;
+
+    xattr_len = vs->fidp->fs.xattr.len;
+    read_count = xattr_len - vs->off;
+    if (read_count > vs->count) {
+	read_count = vs->count;
+    } else if (read_count < 0) {
+	/*
+	 * read beyond XATTR value
+	 */
+	read_count = 0;
+    }
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", read_count);
+    vs->offset += pdu_pack(vs->pdu, vs->offset,
+			   ((char *)vs->fidp->fs.xattr.value) + vs->off,
+			   read_count);
+    err = vs->offset;
+    complete_pdu(s, vs->pdu, err);
+    qemu_free(vs);
+}
+
 static void v9fs_read(V9fsState *s, V9fsPDU *pdu)
 {
     int32_t fid;
@@ -1661,7 +1701,7 @@ static void v9fs_read(V9fsState *s, V9fsPDU *pdu)
         goto out;
     }
 
-    if (vs->fidp->fs.dir) {
+    if (vs->fidp->fid_type == P9_FID_DIR && vs->fidp->fs.dir) {
         vs->max_count = vs->count;
         vs->count = 0;
         if (vs->off == 0) {
@@ -1669,12 +1709,15 @@ static void v9fs_read(V9fsState *s, V9fsPDU *pdu)
         }
         v9fs_read_post_rewinddir(s, vs, err);
         return;
-    } else if (vs->fidp->fs.fd != -1) {
+    } else if (vs->fidp->fid_type == P9_FID_FILE && vs->fidp->fs.fd != -1) {
         vs->sg = vs->iov;
         pdu_marshal(vs->pdu, vs->offset + 4, "v", vs->sg, &vs->cnt);
         err = v9fs_do_lseek(s, vs->fidp->fs.fd, vs->off, SEEK_SET);
         v9fs_read_post_lseek(s, vs, err);
         return;
+    } else if (vs->fidp->fid_type == P9_FID_XATTR) {
+	v9fs_xattr_read(s, vs);
+	return;
     } else {
         err = -EINVAL;
     }
@@ -2513,6 +2556,160 @@ out:
     qemu_free(vs);
 }
 
+typedef struct V9fsXattrState
+{
+    V9fsPDU *pdu;
+    size_t offset;
+    V9fsFidState *file_fidp;
+    V9fsFidState *xattr_fidp;
+    V9fsString name;
+    int64_t size;
+    int flags;
+    void *value;
+} V9fsXattrState;
+
+static void v9fs_post_xattr_getvalue(V9fsState *s, V9fsXattrState *vs, int err)
+{
+
+    if (err < 0) {
+        err = -errno;
+        free_fid(s, vs->xattr_fidp->fid);
+        goto out;
+    }
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size);
+    err = vs->offset;
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+    return;
+}
+
+static void v9fs_post_xattr_check(V9fsState *s, V9fsXattrState *vs, int err)
+{
+    if (err < 0) {
+        err = -errno;
+        free_fid(s, vs->xattr_fidp->fid);
+        goto out;
+    }
+    /*
+     * Read the xattr value
+     */
+    vs->xattr_fidp->fs.xattr.len = vs->size;
+    vs->xattr_fidp->fid_type = P9_FID_XATTR;
+    vs->xattr_fidp->fs.xattr.copied_len = -1;
+    if (vs->size) {
+        vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
+        err = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path,
+                                &vs->name, vs->xattr_fidp->fs.xattr.value,
+                                vs->xattr_fidp->fs.xattr.len);
+    }
+    v9fs_post_xattr_getvalue(s, vs, err);
+    return;
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+}
+
+static void v9fs_post_lxattr_getvalue(V9fsState *s, V9fsXattrState *vs, int err)
+{
+    if (err < 0) {
+        err = -errno;
+        free_fid(s, vs->xattr_fidp->fid);
+        goto out;
+    }
+    vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size);
+    err = vs->offset;
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+    return;
+}
+
+static void v9fs_post_lxattr_check(V9fsState *s, V9fsXattrState *vs, int err)
+{
+    if (err < 0) {
+        err = -errno;
+        free_fid(s, vs->xattr_fidp->fid);
+        goto out;
+    }
+    /*
+     * Read the xattr value
+     */
+    vs->xattr_fidp->fs.xattr.len = vs->size;
+    vs->xattr_fidp->fid_type = P9_FID_XATTR;
+    vs->xattr_fidp->fs.xattr.copied_len = -1;
+    if (vs->size) {
+        vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
+        err = v9fs_do_llistxattr(s, &vs->xattr_fidp->path,
+                                 vs->xattr_fidp->fs.xattr.value,
+                                 vs->xattr_fidp->fs.xattr.len);
+    }
+    v9fs_post_lxattr_getvalue(s, vs, err);
+    return;
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+}
+
+static void v9fs_xattrwalk(V9fsState *s, V9fsPDU *pdu)
+{
+    ssize_t err = 0;
+    V9fsXattrState *vs;
+    int32_t fid, newfid;
+
+    vs = qemu_malloc(sizeof(*vs));
+    vs->pdu = pdu;
+    vs->offset = 7;
+
+    pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &newfid, &vs->name);
+    vs->file_fidp = lookup_fid(s, fid);
+    if (vs->file_fidp == NULL) {
+        err = -ENOENT;
+        goto out;
+    }
+
+    vs->xattr_fidp = alloc_fid(s, newfid);
+    if (vs->xattr_fidp == NULL) {
+	err = -EINVAL;
+	goto out;
+    }
+
+    v9fs_string_copy(&vs->xattr_fidp->path, &vs->file_fidp->path);
+    if (vs->name.data[0] == 0) {
+	/*
+	 * listxattr request. Get the size first
+	 */
+	vs->size = v9fs_do_llistxattr(s, &vs->xattr_fidp->path,
+				      NULL, 0);
+	if (vs->size < 0) {
+	    err = vs->size;
+	}
+	v9fs_post_lxattr_check(s, vs, err);
+	return;
+    } else {
+	/*
+	 * specific xattr fid. We check for xattr
+	 * presence also collect the xattr size
+	 */
+	vs->size = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path,
+				     &vs->name, NULL, 0);
+	if (vs->size < 0) {
+	    err = vs->size;
+	}
+	v9fs_post_xattr_check(s, vs, err);
+	return;
+    }
+out:
+    complete_pdu(s, vs->pdu, err);
+    v9fs_string_free(&vs->name);
+    qemu_free(vs);
+}
+
+
 typedef void (pdu_handler_t)(V9fsState *s, V9fsPDU *pdu);
 
 static pdu_handler_t *pdu_handlers[] = {
@@ -2520,6 +2717,7 @@ static pdu_handler_t *pdu_handlers[] = {
     [P9_TSTATFS] = v9fs_statfs,
     [P9_TGETATTR] = v9fs_getattr,
     [P9_TSYMLINK] = v9fs_symlink,
+    [P9_TXATTRWALK] = v9fs_xattrwalk,
     [P9_TVERSION] = v9fs_version,
     [P9_TATTACH] = v9fs_attach,
     [P9_TSTAT] = v9fs_stat,
diff --git a/hw/virtio-9p.h b/hw/virtio-9p.h
index 942e448..c534923 100644
--- a/hw/virtio-9p.h
+++ b/hw/virtio-9p.h
@@ -19,6 +19,8 @@ enum {
     P9_RSYMLINK,
     P9_TGETATTR = 24,
     P9_RGETATTR,
+    P9_TXATTRWALK = 30,
+    P9_RXATTRWALK,
     P9_TREADDIR = 40,
     P9_RREADDIR,
     P9_TLINK = 70,
