permission checking implemented
diff --git a/BUGS b/BUGS
index 3706264..5fdce02 100644
--- a/BUGS
+++ b/BUGS
@@ -1,8 +1,5 @@
 - It is allowed to mount a directory on a non-directory.
 
-- When a non-directory is mounted the root inode is not filled in, only at
-  the first getattr
-
 - I want really low priority for my cached pages.  Can they start out
   'old' so they will be thrown out on the first oportunity?
 
diff --git a/ChangeLog b/ChangeLog
index cfa7aef..cf7aaa8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,36 @@
-2001-11-09  Miklos Szeredi <mszeredi@inf.bme.hu>
+2001-12-20  Miklos Szeredi <mszeredi@inf.bme.hu>
+	
+	* Added function fuse_get_context() to library API (inspired by
+	patch from Matt Ryan) 
+	
+	* Added flags to fusermount and to kernel interface to control
+	permission checking
 
-	* Started ChangeLog
+	* Integrated fuse_set_operations() into fuse_new()
+
+2001-12-08  Miklos Szeredi <mszeredi@inf.bme.hu>
+
+	* Applied header protection + extern "C" patch by Roland
+	Bauerschmidt
+
+2001-12-02  Miklos Szeredi <mszeredi@inf.bme.hu>
+
+	* Added perl bindings by Mark Glines 
+
+2001-11-21  Miklos Szeredi <mszeredi@inf.bme.hu>
+
+	* Cleaned up way of mounting simple filesystems.
+
+	* fuse_main() helper function added
+
+2001-11-18  Miklos Szeredi <mszeredi@inf.bme.hu>
+	
+	* Optimized read/write operations, so that minimal copying of data
+	is done
+
+2001-11-14  Miklos Szeredi <mszeredi@inf.bme.hu>
+
+	* Python bindings by Jeff Epler added
 
 2001-11-13  Miklos Szeredi <mszeredi@inf.bme.hu>
 
@@ -9,7 +39,6 @@
 	* FS blocksize is set to PAGE_CACHE_SIZE, blksize attribute from
 	userspace is ignored
 
-2001-11-14  Miklos Szeredi <mszeredi@inf.bme.hu>
+2001-11-09  Miklos Szeredi <mszeredi@inf.bme.hu>
 
-	* Python bindings by Jeff Epler added
-
+	* Started ChangeLog
diff --git a/TODO b/TODO
index 2942e4e..fbf6d70 100644
--- a/TODO
+++ b/TODO
@@ -1,7 +1,5 @@
  - Better (but not too complex) library interface for open/read/write/close
 
- - Permission checking for users other then the owner of the mount
-
  - Integrate (parts of) fusermount into mount(8)
 
  - Statfs operation
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index fd5ef9c..53cb3f0 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -9,7 +9,7 @@
 /* This file defines the kernel interface of FUSE */
 
 /** Version number of this interface */
-#define FUSE_KERNEL_VERSION 1
+#define FUSE_KERNEL_VERSION 2
 
 /** The inode number of the root indode */
 #define FUSE_ROOT_INO 1
@@ -35,6 +35,17 @@
 	unsigned int flags;
 };
 
+/* FUSE mount flags: */
+
+/** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem
+module will check permissions based on the file mode.  Otherwise no
+permission checking is done in the kernel */
+#define FUSE_DEFAULT_PERMISSIONS (1 << 0)
+
+/** If the FUSE_ALLOW_OTHER flag is given, then not only the user
+    doing the mount will be allowed to access the filesystem */
+#define FUSE_ALLOW_OTHER         (1 << 1)
+
 struct fuse_attr {
 	unsigned int        mode;
 	unsigned int        nlink;
@@ -149,6 +160,8 @@
 	int unique;
 	enum fuse_opcode opcode;
 	unsigned long ino;
+	unsigned int uid;
+	unsigned int gid;
 };
 
 struct fuse_out_header {
diff --git a/kernel/dir.c b/kernel/dir.c
index a3f1485..7068d37 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -315,29 +315,14 @@
 	return out.h.error;
 }
 
-static int fuse_permission(struct inode *inode, int mask)
-{
-	struct fuse_conn *fc = INO_FC(inode);
 
-	/* (too) simple protection */
-	if(current->fsuid == fc->uid)
-		return 0;
-	else
-		return -EACCES;
-}
-
-static int fuse_revalidate(struct dentry *entry)
+int fuse_getattr(struct inode *inode)
 {
-	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_getattr_out arg;
 	
-	if(inode->i_ino != FUSE_ROOT_INO && 
-	   time_before_eq(jiffies, entry->d_time + FUSE_REVALIDATE_TIME))
-		return 0;
-
 	in.h.opcode = FUSE_GETATTR;
 	in.h.ino = inode->i_ino;
 	out.numargs = 1;
@@ -351,6 +336,57 @@
 	return out.h.error;
 }
 
+static int fuse_revalidate(struct dentry *entry)
+{
+	struct inode *inode = entry->d_inode;
+	struct fuse_conn *fc = INO_FC(inode);
+
+	if(inode->i_ino == FUSE_ROOT_INO) {
+		if(!(fc->flags & FUSE_ALLOW_OTHER)
+		   && current->fsuid != fc->uid)
+			return -EACCES;
+	}
+	else if(time_before_eq(jiffies, entry->d_time + FUSE_REVALIDATE_TIME))
+		return 0;
+
+	return fuse_getattr(inode);
+}
+
+static int fuse_permission(struct inode *inode, int mask)
+{
+	struct fuse_conn *fc = INO_FC(inode);
+
+	if(!(fc->flags & FUSE_ALLOW_OTHER) && current->fsuid != fc->uid)
+		return -EACCES;
+	else if(fc->flags & FUSE_DEFAULT_PERMISSIONS) {
+		int err = vfs_permission(inode, mask);
+
+		/* If permission is denied, try to refresh file
+		   attributes.  This is also needed, because the root
+		   node will at first have no permissions */
+
+		if(err == -EACCES) {
+		 	err = fuse_getattr(inode);
+			if(!err)
+			 	err = vfs_permission(inode, mask);
+		}
+
+		/* FIXME: Need some mechanism to revoke permissions:
+		   currently if the filesystem suddenly changes the
+		   file mode, we will not be informed abot that, and
+		   continue to allow access to the file/directory.
+		   
+		   This is actually not so grave, since the user can
+		   simply keep access to the file/directory anyway by
+		   keeping it open... */
+
+		return err;
+	}
+	else
+		return 0;
+}
+
+
 static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
 			 void *dstbuf, filldir_t filldir)
 {
diff --git a/kernel/file.c b/kernel/file.c
index b7cb2f4..0622e51 100644
--- a/kernel/file.c
+++ b/kernel/file.c
@@ -17,6 +17,14 @@
 	struct fuse_out out = FUSE_OUT_INIT;
 	struct fuse_open_in inarg;
 
+	/* If opening the root node, no lookup has been performed on
+	   it, so the attributes must be refreshed */
+	if(inode->i_ino == FUSE_ROOT_INO) {
+		int err = fuse_getattr(inode);
+		if(err)
+		 	return err;
+	}
+
 	memset(&inarg, 0, sizeof(inarg));
 	inarg.flags = file->f_flags & ~O_EXCL;
 
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index 27d8eb3..262e05d 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -77,7 +77,7 @@
 	struct fuse_out_arg args[3];
 };
 
-#define FUSE_IN_INIT { {0, 0, 0}, 0}
+#define FUSE_IN_INIT { {0, 0, 0, current->fsuid, current->fsgid}, 0}
 #define FUSE_OUT_INIT { {0, 0}, 0, 0}
 
 /**
@@ -178,6 +178,11 @@
  */
 int request_send_noreply(struct fuse_conn *fc, struct fuse_in *in);
 
+/**
+ * Get the attributes of a file
+ */
+int fuse_getattr(struct inode *inode);
+
 /*
  * Local Variables:
  * indent-tabs-mode: t
diff --git a/kernel/inode.c b/kernel/inode.c
index 7d211cb..7fc5cfe 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -32,6 +32,7 @@
 	in = kmalloc(sizeof(struct fuse_in), GFP_NOFS);
 	if(!in)
 		return;
+	memset(in, 0, sizeof(struct fuse_in));
 
 	inarg = kmalloc(sizeof(struct fuse_forget_in), GFP_NOFS);
 	if(!inarg) 
diff --git a/lib/fuse.c b/lib/fuse.c
index 0a0b40a..541014f 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -781,6 +781,7 @@
     struct fuse_in_header *in = (struct fuse_in_header *) cmd->buf;
     void *inarg = cmd->buf + sizeof(struct fuse_in_header);
     size_t argsize;
+    struct fuse_context *ctx = fuse_get_context(f);
 
     dec_avail(f);
 
@@ -789,6 +790,9 @@
                in->opcode, in->ino, cmd->buflen);
         fflush(stdout);
     }
+
+    ctx->uid = in->uid;
+    ctx->gid = in->gid;
     
     argsize = cmd->buflen - sizeof(struct fuse_in_header);
         
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index 604c297..d587af1 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -37,6 +37,7 @@
     int numavail;
     struct fuse_context *(*getcontext)(struct fuse *);
     struct fuse_context context;
+    pthread_key_t context_key;
 };
 
 struct fuse_dirhandle {
diff --git a/lib/fuse_mt.c b/lib/fuse_mt.c
index 4534d0f..b8756ac 100644
--- a/lib/fuse_mt.c
+++ b/lib/fuse_mt.c
@@ -69,9 +69,28 @@
     pthread_detach(thrid);
 }
 
+static struct fuse_context *mt_getcontext(struct fuse *f)
+{
+    struct fuse_context *ctx;
+
+    ctx = (struct fuse_context *) pthread_getspecific(f->context_key);
+    if(ctx == NULL) {
+        ctx = (struct fuse_context *) malloc(sizeof(struct fuse_context));
+        pthread_setspecific(f->context_key, ctx);
+    }
+
+    return ctx;
+}
+
+static void mt_freecontext(void *data)
+{
+    free(data);
+}
+
 void __fuse_loop_mt(struct fuse *f, fuse_processor_t proc, void *data)
 {
     struct fuse_worker *w;
+    int res;
 
     w = malloc(sizeof(struct fuse_worker));    
     w->f = f;
@@ -79,6 +98,12 @@
     w->proc = proc;
 
     f->numworker = 1;
+    res = pthread_key_create(&f->context_key, mt_freecontext);
+    if(res != 0) {
+        fprintf(stderr, "Failed to create thread specific key\n");
+        exit(1);
+    }
+    f->getcontext = mt_getcontext;
     do_work(w);
 }
 
diff --git a/util/fusermount.c b/util/fusermount.c
index ee51269..94f435f 100644
--- a/util/fusermount.c
+++ b/util/fusermount.c
@@ -264,7 +264,7 @@
 }
 
 static int do_mount(const char *dev, const char *mnt, const char *type,
-                    mode_t rootmode, int fd)
+                    mode_t rootmode, int fd, int fuseflags)
 {
     int res;
     struct fuse_mount_data data;
@@ -282,7 +282,7 @@
     data.fd = fd;
     data.rootmode = rootmode;
     data.uid = getuid();
-    data.flags = 0;
+    data.flags = fuseflags;
 
     res = mount(dev, mnt, type, flags, &data);
     if(res == -1)
@@ -332,7 +332,7 @@
     return 0;
 }
 
-static int mount_fuse(const char *mnt)
+static int mount_fuse(const char *mnt, int flags)
 {
     int res;
     int fd;
@@ -364,7 +364,7 @@
         return -1;
     }
  
-    res = do_mount(dev, mnt, type, stbuf.st_mode & S_IFMT, fd);
+    res = do_mount(dev, mnt, type, stbuf.st_mode & S_IFMT, fd, flags);
     if(res == -1)
         return -1;
 
@@ -401,7 +401,9 @@
             "%s: [options] mountpoint [program [args ...]]\n"
             "Options:\n"
             " -h    print help\n"
-            " -u    unmount\n",
+            " -u    unmount\n"
+            " -p    check default permissions on files\n"
+            " -x    allow other users to access the files (only for root)\n",
             progname);
     exit(1);
 }
@@ -419,6 +421,7 @@
     char mypath[PATH_MAX];
     char *unmount_cmd;
     char verstr[128];
+    int flags = 0;
 
     progname = argv[0];
     
@@ -435,6 +438,19 @@
             unmount = 1;
             break;
             
+        case 'p':
+            flags |= FUSE_DEFAULT_PERMISSIONS;
+            break;
+            
+        case 'x':
+            if(getuid() != 0) {
+                fprintf(stderr, "%s: option %s is allowed only for root\n",
+                        progname, argv[a]);
+                exit(1);
+            }
+            flags |= FUSE_ALLOW_OTHER;
+            break;
+
         default:
             fprintf(stderr, "%s: Unknown option %s\n", progname, argv[a]);
             exit(1);
@@ -474,7 +490,7 @@
     userprog = argv + a;
     numargs = argc - a;
     
-    fd = mount_fuse(mnt);
+    fd = mount_fuse(mnt, flags);
     if(fd == -1)
         exit(1);