diff --git a/hw/file-op-9p.h b/hw/file-op-9p.h
index 120c803..c4f29e9 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);
+    ssize_t (*lgetxattr)(FsContext *, const char *, const char *, void *, size_t);
+    ssize_t (*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 949a4bf..4554eb6 100644
--- a/hw/virtio-9p-debug.c
+++ b/hw/virtio-9p-debug.c
@@ -579,6 +579,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 1f72f2e..3088782 100644
--- a/hw/virtio-9p-local.c
+++ b/hw/virtio-9p-local.c
@@ -477,6 +477,18 @@ static int local_statfs(FsContext *s, const char *path, struct statfs *stbuf)
    return statfs(rpath(s, path), stbuf);
 }
 
+static ssize_t local_lgetxattr(FsContext *ctx, const char *path, const char *name,
+                               void *value, size_t size)
+{
+    return lgetxattr(rpath(ctx, path), name, value, size);
+}
+
+static ssize_t 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,
@@ -505,4 +517,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 f3943a0..2782637 100644
--- a/hw/virtio-9p.c
+++ b/hw/virtio-9p.c
@@ -263,6 +263,21 @@ static int v9fs_do_statfs(V9fsState *s, V9fsString *path, struct statfs *stbuf)
     return s->ops->statfs(&s->ctx, path->data, stbuf);
 }
 
+static ssize_t 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 ssize_t 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;
@@ -1942,6 +1957,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;
@@ -1963,7 +2003,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->max_count = vs->count;
         vs->count = 0;
         if (vs->off == 0) {
@@ -1971,12 +2011,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->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;
     }
@@ -3086,6 +3129,148 @@ out:
     qemu_free(vs);
 }
 
+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, ssize_t 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, ssize_t 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[] = {
@@ -3093,6 +3278,7 @@ static pdu_handler_t *pdu_handlers[] = {
     [P9_TSTATFS] = v9fs_statfs,
     [P9_TGETATTR] = v9fs_getattr,
     [P9_TSETATTR] = v9fs_setattr,
+    [P9_TXATTRWALK] = v9fs_xattrwalk,
     [P9_TMKNOD] = v9fs_mknod,
     [P9_TRENAME] = v9fs_rename,
     [P9_TMKDIR] = v9fs_mkdir,
diff --git a/hw/virtio-9p.h b/hw/virtio-9p.h
index 3edbdaa..e7a8ea5 100644
--- a/hw/virtio-9p.h
+++ b/hw/virtio-9p.h
@@ -29,6 +29,8 @@ enum {
     P9_RGETATTR,
     P9_TSETATTR = 26,
     P9_RSETATTR,
+    P9_TXATTRWALK = 30,
+    P9_RXATTRWALK,
     P9_TREADDIR = 40,
     P9_RREADDIR,
     P9_TLINK = 70,
@@ -416,6 +418,18 @@ typedef struct V9fsRenameState {
     V9fsString name;
 } V9fsRenameState;
 
+typedef struct V9fsXattrState
+{
+    V9fsPDU *pdu;
+    size_t offset;
+    V9fsFidState *file_fidp;
+    V9fsFidState *xattr_fidp;
+    V9fsString name;
+    int64_t size;
+    int flags;
+    void *value;
+} V9fsXattrState;
+
 extern size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
                             size_t offset, size_t size, int pack);
 
