EA operations added
diff --git a/ChangeLog b/ChangeLog
index 6d0b2b7..6103987 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,9 @@
 
 	* new fusermount flag '-z': lazy unmount, default is not lazy
 
+	* Extended attributes operations added (getxattr, setxattr,
+	listxattr, removexattr)
+
 2004-03-25  Miklos Szeredi <mszeredi@inf.bme.hu>
 
 	* If filesystem doesn't define a statfs operation, then an
diff --git a/include/fuse.h b/include/fuse.h
index 7a751ae..79c62a6 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -91,26 +91,30 @@
  *  an fdatasync() operation.
  */
 struct fuse_operations {
-    int (*getattr)  (const char *, struct stat *);
-    int (*readlink) (const char *, char *, size_t);
-    int (*getdir)   (const char *, fuse_dirh_t, fuse_dirfil_t);
-    int (*mknod)    (const char *, mode_t, dev_t);
-    int (*mkdir)    (const char *, mode_t);
-    int (*unlink)   (const char *);
-    int (*rmdir)    (const char *);
-    int (*symlink)  (const char *, const char *);
-    int (*rename)   (const char *, const char *);
-    int (*link)     (const char *, const char *);
-    int (*chmod)    (const char *, mode_t);
-    int (*chown)    (const char *, uid_t, gid_t);
-    int (*truncate) (const char *, off_t);
-    int (*utime)    (const char *, struct utimbuf *);
-    int (*open)     (const char *, int);
-    int (*read)     (const char *, char *, size_t, off_t);
-    int (*write)    (const char *, const char *, size_t, off_t);
-    int (*statfs)   (const char *, struct statfs *);
-    int (*release)  (const char *, int);
-    int (*fsync)    (const char *, int);
+    int (*getattr)     (const char *, struct stat *);
+    int (*readlink)    (const char *, char *, size_t);
+    int (*getdir)      (const char *, fuse_dirh_t, fuse_dirfil_t);
+    int (*mknod)       (const char *, mode_t, dev_t);
+    int (*mkdir)       (const char *, mode_t);
+    int (*unlink)      (const char *);
+    int (*rmdir)       (const char *);
+    int (*symlink)     (const char *, const char *);
+    int (*rename)      (const char *, const char *);
+    int (*link)        (const char *, const char *);
+    int (*chmod)       (const char *, mode_t);
+    int (*chown)       (const char *, uid_t, gid_t);
+    int (*truncate)    (const char *, off_t);
+    int (*utime)       (const char *, struct utimbuf *);
+    int (*open)        (const char *, int);
+    int (*read)        (const char *, char *, size_t, off_t);
+    int (*write)       (const char *, const char *, size_t, off_t);
+    int (*statfs)      (const char *, struct statfs *);
+    int (*release)     (const char *, int);
+    int (*fsync)       (const char *, int);
+    int (*setxattr)    (const char *, const char *, const char *, size_t, int);
+    int (*getxattr)    (const char *, const char *, char *, size_t);
+    int (*listxattr)   (const char *, char *, size_t);
+    int (*removexattr) (const char *, const char *);
 };
 
 /** Extra context that may be needed by some filesystems */
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index d5fd357..f641271 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -94,26 +94,30 @@
 #define FATTR_CTIME	(1 << 6)
 
 enum fuse_opcode {
-	FUSE_LOOKUP	= 1,
-	FUSE_FORGET	= 2,  /* no reply */
-	FUSE_GETATTR	= 3,
-	FUSE_SETATTR	= 4,
-	FUSE_READLINK	= 5,
-	FUSE_SYMLINK	= 6,
-	FUSE_GETDIR	= 7,
-	FUSE_MKNOD	= 8,
-	FUSE_MKDIR	= 9,
-	FUSE_UNLINK	= 10,
-	FUSE_RMDIR	= 11,
-	FUSE_RENAME	= 12,
-	FUSE_LINK	= 13,
-	FUSE_OPEN	= 14,
-	FUSE_READ	= 15,
-	FUSE_WRITE	= 16,
-	FUSE_STATFS	= 17,
-	FUSE_RELEASE    = 18, /* no reply */
-	FUSE_INVALIDATE = 19, /* user initiated */
-	FUSE_FSYNC      = 20
+	FUSE_LOOKUP	   = 1,
+	FUSE_FORGET	   = 2,  /* no reply */
+	FUSE_GETATTR	   = 3,
+	FUSE_SETATTR	   = 4,
+	FUSE_READLINK	   = 5,
+	FUSE_SYMLINK	   = 6,
+	FUSE_GETDIR	   = 7,
+	FUSE_MKNOD	   = 8,
+	FUSE_MKDIR	   = 9,
+	FUSE_UNLINK	   = 10,
+	FUSE_RMDIR	   = 11,
+	FUSE_RENAME	   = 12,
+	FUSE_LINK	   = 13,
+	FUSE_OPEN	   = 14,
+	FUSE_READ	   = 15,
+	FUSE_WRITE	   = 16,
+	FUSE_STATFS	   = 17,
+	FUSE_RELEASE       = 18, /* no reply */
+	FUSE_INVALIDATE    = 19, /* user initiated */
+	FUSE_FSYNC         = 20,
+	FUSE_SETXATTR      = 21,
+	FUSE_GETXATTR      = 22,
+	FUSE_LISTXATTR     = 23,
+	FUSE_REMOVEXATTR   = 24,
 };
 
 /* Conservative buffer size for the client */
@@ -188,6 +192,15 @@
 	int datasync;
 };
 
+struct fuse_setxattr_in {
+	unsigned int size;
+	unsigned int flags;
+};
+
+struct fuse_getlistxattr_in {
+	unsigned int size;
+};
+
 struct fuse_in_header {
 	int unique;
 	enum fuse_opcode opcode;
diff --git a/kernel/dir.c b/kernel/dir.c
index 4051d07..34ab665 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -717,6 +717,107 @@
 {
 	return _fuse_dentry_revalidate(entry);
 }
+
+static int fuse_setxattr(struct dentry *entry, const char *name,
+			 const void *value, size_t size, int flags)
+{
+	struct inode *inode = entry->d_inode;
+	struct fuse_conn *fc = INO_FC(inode);
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	struct fuse_setxattr_in inarg;
+
+	memset(&inarg, 0, sizeof(inarg));
+	inarg.size = size;
+	inarg.flags = flags;
+	
+	in.h.opcode = FUSE_SETXATTR;
+	in.h.ino = inode->i_ino;
+	in.numargs = 3;
+	in.args[0].size = sizeof(inarg);
+	in.args[0].value = &inarg;
+	in.args[1].size = strlen(name) + 1;
+	in.args[1].value = name;
+	in.args[2].size = size;
+	in.args[2].value = value;
+	request_send(fc, &in, &out);
+	return out.h.error;
+}
+
+static ssize_t fuse_getxattr(struct dentry *entry, const char *name,
+			     void *value, size_t size)
+{
+	struct inode *inode = entry->d_inode;
+	struct fuse_conn *fc = INO_FC(inode);
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	struct fuse_getlistxattr_in inarg;
+
+	memset(&inarg, 0, sizeof(inarg));
+	inarg.size = size;
+	
+	in.h.opcode = FUSE_GETXATTR;
+	in.h.ino = inode->i_ino;
+	in.numargs = 2;
+	in.args[0].size = sizeof(inarg);
+	in.args[0].value = &inarg;
+	in.args[1].size = strlen(name) + 1;
+	in.args[1].value = name;
+	out.argvar = 1;
+	out.numargs = 1;
+	out.args[0].size = size;
+	out.args[0].value = value;
+	request_send(fc, &in, &out);
+	if(!out.h.error)
+		return out.args[0].size;
+	else
+		return out.h.error;
+}
+
+static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
+{
+	struct inode *inode = entry->d_inode;
+	struct fuse_conn *fc = INO_FC(inode);
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	struct fuse_getlistxattr_in inarg;
+
+	memset(&inarg, 0, sizeof(inarg));
+	inarg.size = size;
+	
+	in.h.opcode = FUSE_LISTXATTR;
+	in.h.ino = inode->i_ino;
+	in.numargs = 1;
+	in.args[0].size = sizeof(inarg);
+	in.args[0].value = &inarg;
+	out.argvar = 1;
+	out.numargs = 1;
+	out.args[0].size = size;
+	out.args[0].value = list;
+	request_send(fc, &in, &out);
+	if(!out.h.error)
+		return out.args[0].size;
+	else
+		return out.h.error;
+}
+
+static int fuse_removexattr(struct dentry *entry, const char *name)
+{
+	struct inode *inode = entry->d_inode;
+	struct fuse_conn *fc = INO_FC(inode);
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+	
+	in.h.opcode = FUSE_REMOVEXATTR;
+	in.h.ino = inode->i_ino;
+	in.numargs = 1;
+	in.args[0].size = strlen(name) + 1;
+	in.args[0].value = name;
+	request_send(fc, &in, &out);
+	return out.h.error;
+	
+}
+
 #else /* KERNEL_2_6 */
 
 #define fuse_create _fuse_create
@@ -771,6 +872,10 @@
 	.permission	= fuse_permission,
 #ifdef KERNEL_2_6
 	.getattr	= fuse_getattr,
+	.setxattr	= fuse_setxattr,
+	.getxattr	= fuse_getxattr,
+	.listxattr	= fuse_listxattr,
+	.removexattr	= fuse_removexattr,
 #else
 	.revalidate	= fuse_revalidate,
 #endif
@@ -788,6 +893,10 @@
 	.permission	= fuse_permission,
 #ifdef KERNEL_2_6
 	.getattr	= fuse_getattr,
+	.setxattr	= fuse_setxattr,
+	.getxattr	= fuse_getxattr,
+	.listxattr	= fuse_listxattr,
+	.removexattr	= fuse_removexattr,
 #else
 	.revalidate	= fuse_revalidate,
 #endif
@@ -800,6 +909,10 @@
 	.follow_link	= fuse_follow_link,
 #ifdef KERNEL_2_6
 	.getattr	= fuse_getattr,
+	.setxattr	= fuse_setxattr,
+	.getxattr	= fuse_getxattr,
+	.listxattr	= fuse_listxattr,
+	.removexattr	= fuse_removexattr,
 #else
 	.revalidate	= fuse_revalidate,
 #endif
diff --git a/lib/fuse.c b/lib/fuse.c
index 2ca30bb..77532a4 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -25,25 +25,29 @@
 static const char *opname(enum fuse_opcode opcode)
 {
     switch(opcode) { 
-    case FUSE_LOOKUP:   return "LOOKUP";
-    case FUSE_FORGET:   return "FORGET";
-    case FUSE_GETATTR:  return "GETATTR";
-    case FUSE_SETATTR:  return "SETATTR";
-    case FUSE_READLINK: return "READLINK";
-    case FUSE_SYMLINK:  return "SYMLINK";
-    case FUSE_GETDIR:   return "GETDIR";
-    case FUSE_MKNOD:    return "MKNOD";
-    case FUSE_MKDIR:    return "MKDIR";
-    case FUSE_UNLINK:   return "UNLINK";
-    case FUSE_RMDIR:    return "RMDIR";
-    case FUSE_RENAME:   return "RENAME";
-    case FUSE_LINK:     return "LINK";
-    case FUSE_OPEN:     return "OPEN";
-    case FUSE_READ:     return "READ";
-    case FUSE_WRITE:    return "WRITE";
-    case FUSE_STATFS:   return "STATFS";
-    case FUSE_RELEASE:  return "RELEASE";
-    case FUSE_FSYNC:    return "FSYNC";
+    case FUSE_LOOKUP:		return "LOOKUP";
+    case FUSE_FORGET:		return "FORGET";
+    case FUSE_GETATTR:		return "GETATTR";
+    case FUSE_SETATTR:		return "SETATTR";
+    case FUSE_READLINK:		return "READLINK";
+    case FUSE_SYMLINK:		return "SYMLINK";
+    case FUSE_GETDIR:		return "GETDIR";
+    case FUSE_MKNOD:		return "MKNOD";
+    case FUSE_MKDIR:		return "MKDIR";
+    case FUSE_UNLINK:		return "UNLINK";
+    case FUSE_RMDIR:		return "RMDIR";
+    case FUSE_RENAME:		return "RENAME";
+    case FUSE_LINK:		return "LINK";
+    case FUSE_OPEN:		return "OPEN";
+    case FUSE_READ:		return "READ";
+    case FUSE_WRITE:		return "WRITE";
+    case FUSE_STATFS:		return "STATFS";
+    case FUSE_RELEASE:		return "RELEASE";
+    case FUSE_FSYNC:		return "FSYNC";
+    case FUSE_SETXATTR:		return "SETXATTR";
+    case FUSE_GETXATTR:		return "GETXATTR";
+    case FUSE_LISTXATTR:	return "LISTXATTR";
+    case FUSE_REMOVEXATTR:	return "REMOVEXATTR";
     default:            return "???";
     }
 }
@@ -968,6 +972,110 @@
     send_reply(f, in, res, NULL, 0);
 }
 
+static void do_setxattr(struct fuse *f, struct fuse_in_header *in,
+                        struct fuse_setxattr_in *arg)
+{
+    int res;
+    char *path;
+    char *name = PARAM(arg);
+    unsigned char *value = name + strlen(name) + 1;
+
+    res = -ENOENT;
+    path = get_path(f, in->ino);
+    if (path != NULL) {
+        res = -EOPNOTSUPP; /* or is it ENOTSUPP ??? */
+        if (f->op.setxattr)
+            res = f->op.setxattr(path, name, value, arg->size, arg->flags);
+        free(path);
+    }    
+    send_reply(f, in, res, NULL, 0);
+}
+
+static void do_getxattr(struct fuse *f, struct fuse_in_header *in,
+                        struct fuse_getlistxattr_in *arg)
+{
+    int res;
+    char *path;
+    char *name = PARAM(arg);
+    char *outbuf = (char *) malloc(sizeof(struct fuse_out_header) + arg->size);
+    struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
+    char *value = outbuf + sizeof(struct fuse_out_header);
+    size_t size;
+    size_t outsize;
+
+    res = -ENOENT;
+    path = get_path(f, in->ino);
+    if (path != NULL) {
+        res = -EOPNOTSUPP; /* or is it ENOTSUPP ??? */
+        if (f->op.getxattr)
+            res = f->op.getxattr(path, name, value, arg->size);
+        free(path);
+    }    
+    size = 0;
+    if(res > 0) {
+        size = res;
+        res = 0;
+    }
+    memset(out, 0, sizeof(struct fuse_out_header));
+    out->unique = in->unique;
+    out->error = res;
+    outsize = sizeof(struct fuse_out_header) + size;
+
+    send_reply_raw(f, outbuf, outsize);
+    free(outbuf);
+}
+
+static void do_listxattr(struct fuse *f, struct fuse_in_header *in,
+                         struct fuse_getlistxattr_in *arg)
+{
+    int res;
+    char *path;
+    char *outbuf = (char *) malloc(sizeof(struct fuse_out_header) + arg->size);
+    struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
+    char *value = outbuf + sizeof(struct fuse_out_header);
+    size_t size;
+    size_t outsize;
+
+    res = -ENOENT;
+    path = get_path(f, in->ino);
+    if (path != NULL) {
+        res = -EOPNOTSUPP; /* or is it ENOTSUPP ??? */
+        if (f->op.listxattr)
+            res = f->op.listxattr(path, value, arg->size);
+        free(path);
+    }    
+    size = 0;
+    if(res > 0) {
+        size = res;
+        res = 0;
+    }
+    memset(out, 0, sizeof(struct fuse_out_header));
+    out->unique = in->unique;
+    out->error = res;
+    outsize = sizeof(struct fuse_out_header) + size;
+
+    send_reply_raw(f, outbuf, outsize);
+    free(outbuf);
+}
+
+static void do_removexattr(struct fuse *f, struct fuse_in_header *in,
+                           char *name)
+{
+    int res;
+    char *path;
+
+    res = -ENOENT;
+    path = get_path(f, in->ino);
+    if (path != NULL) {
+        res = -EOPNOTSUPP; /* or is it ENOTSUPP ??? */
+        if (f->op.removexattr)
+            res = f->op.removexattr(path, name);
+        free(path);
+    }    
+    send_reply(f, in, res, NULL, 0);
+}
+
+
 static void free_cmd(struct fuse_cmd *cmd)
 {
     free(cmd->buf);
@@ -1069,6 +1177,22 @@
         do_fsync(f, in, (struct fuse_fsync_in *) inarg);
         break;
 
+    case FUSE_SETXATTR:
+        do_setxattr(f, in, (struct fuse_setxattr_in *) inarg);
+        break;
+
+    case FUSE_GETXATTR:
+        do_getxattr(f, in, (struct fuse_getlistxattr_in *) inarg);
+        break;
+
+    case FUSE_LISTXATTR:
+        do_listxattr(f, in, (struct fuse_getlistxattr_in *) inarg);
+        break;
+
+    case FUSE_REMOVEXATTR:
+        do_removexattr(f, in, (char *) inarg);
+        break;
+
     default:
         send_reply(f, in, -ENOSYS, NULL, 0);
     }
diff --git a/util/fusermount.c b/util/fusermount.c
index 9af24d1..42e9d47 100644
--- a/util/fusermount.c
+++ b/util/fusermount.c
@@ -188,8 +188,9 @@
     if(found) {
         res = umount2(mnt, lazy ? 2 : 0);
         if(res == -1) {
-            fprintf(stderr, "%s: failed to unmount %s: %s\n", progname, mnt,
-                    strerror(errno));
+            if(!quiet)
+                fprintf(stderr, "%s: failed to unmount %s: %s\n",
+                        progname, mnt,strerror(errno));
             found = -1;
         }
     }
@@ -587,8 +588,9 @@
         } else {
             res = umount2(mnt, lazy ? 2 : 0);
             if(res == -1) {
-                fprintf(stderr, "%s: failed to unmount %s: %s\n", progname, mnt,
-                        strerror(errno));
+                if (!quiet)
+                    fprintf(stderr, "%s: failed to unmount %s: %s\n",
+                            progname, mnt, strerror(errno));
             }
         }
         if(res == -1)