Add fuse_getgroups (high level lib) and fuse_req_getgroups (low
authorMiklos Szeredi <miklos@szeredi.hu>
Fri, 19 Jun 2009 10:27:38 +0000 (10:27 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Fri, 19 Jun 2009 10:27:38 +0000 (10:27 +0000)
level lib) functions to query the supplementary group IDs for the
current request.  Currently this is implemented on Linux by
reading from the /proc filesystem.

ChangeLog
README.NFS
include/fuse.h
include/fuse_lowlevel.h
lib/fuse.c
lib/fuse_lowlevel.c
lib/fuse_versionscript

index a32dd66c1abb68525c9b88a7a34e364a0611eb5a..313a049529d6cee9fee367fb3292189d3e37633a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2009-06-19  Miklos Szeredi <miklos@szeredi.hu>
+
+       * Add fuse_getgroups (high level lib) and fuse_req_getgroups (low
+       level lib) functions to query the supplementary group IDs for the
+       current request.  Currently this is implemented on Linux by
+       reading from the /proc filesystem.
+
 2009-06-18  Miklos Szeredi <miklos@szeredi.hu>
 
        * Add "noforget" option to high level lib to prevent ESTALE errors
index b805f39239bb448d32aa2f8647d0d7f1b2671c23..f3348d5afba689bd6c5da5665a047e01b64a7367 100644 (file)
@@ -2,3 +2,32 @@ NFS exporting is supported in Linux kernels 2.6.27 or later.
 
 You need to add an fsid=NNN option to /etc/exports to make exporting a
 FUSE directory work.
+
+Filesystem support
+------------------
+
+NFS exporting works to some extent on all fuse filesystems, but not
+perfectly.  This is due to the stateless nature of the protocol, the
+server has no way of knowing whether the client is keeping a reference
+to a file or not, and hence that file may be removed from the server's
+cache.  In that case there has to be a way to look up that object
+using the inode number, otherwise an ESTALE error will be returned.
+
+1) low-level interface
+
+Filesystems need to implement special lookups for the names "." and
+"..".  The former may be requested on any inode, including
+non-directories, while the latter is only requested for directories.
+Otherwise these special lookups should behave identically to ordinary
+lookups.
+
+2) high-level interface
+
+Because the high-level interface is path based, it is not possible to
+delegate looking up by inode to the filesystem.
+
+To work around this, currently a "noforget" option is provided, which
+makes the library remember nodes forever.  This will make the NFS
+server happy, but also results in an ever growing memory footprint for
+the filesystem.  For this reason if the filesystem is large (or the
+memory is small), then this option is not recommended.
index 6ded4f58500b85cf6c24445cb9d9ffc7aeffc7b2..a58cd9f1aadc23b53dd06ac1e37aa3ed9baec0a2 100644 (file)
@@ -623,6 +623,26 @@ int fuse_loop_mt(struct fuse *f);
  */
 struct fuse_context *fuse_get_context(void);
 
+/**
+ * Get the current supplementary group IDs for the current request
+ *
+ * Similar to the getgroups(2) system call, except the return value is
+ * always the total number of group IDs, even if it is larger than the
+ * specified size.
+ *
+ * The current fuse kernel module in linux (as of 2.6.30) doesn't pass
+ * the group list to userspace, hence this function needs to parse
+ * "/proc/$TID/task/$TID/status" to get the group IDs.
+ *
+ * This feature may not be supported on all operating systems.  In
+ * such a case this function will return -ENOSYS.
+ *
+ * @param size size of given array
+ * @param list array of group IDs to be filled in
+ * @return the total number of supplementary group IDs or -errno on failure
+ */
+int fuse_getgroups(int size, gid_t list[]);
+
 /**
  * Check if the current request has already been interrupted
  *
index 5ab242290958c275d388dc14c810b7e7f4bf7198..a495e15d0b5d25685406fdc16d4d99715044c3cf 100644 (file)
@@ -1174,6 +1174,27 @@ void *fuse_req_userdata(fuse_req_t req);
  */
 const struct fuse_ctx *fuse_req_ctx(fuse_req_t req);
 
+/**
+ * Get the current supplementary group IDs for the specified request
+ *
+ * Similar to the getgroups(2) system call, except the return value is
+ * always the total number of group IDs, even if it is larger than the
+ * specified size.
+ *
+ * The current fuse kernel module in linux (as of 2.6.30) doesn't pass
+ * the group list to userspace, hence this function needs to parse
+ * "/proc/$TID/task/$TID/status" to get the group IDs.
+ *
+ * This feature may not be supported on all operating systems.  In
+ * such a case this function will return -ENOSYS.
+ *
+ * @param req request handle
+ * @param size size of given array
+ * @param list array of group IDs to be filled in
+ * @return the total number of supplementary group IDs or -errno on failure
+ */
+int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]);
+
 /**
  * Callback function for an interrupt
  *
index 9267ca68b1d5b5778d956b558430d701876e8432..dad9a711bd75a52858548eb78f8903a118db2223 100644 (file)
@@ -3450,6 +3450,12 @@ struct fuse_context *fuse_get_context(void)
        return &fuse_get_context_internal()->ctx;
 }
 
+int fuse_getgroups(int size, gid_t list[])
+{
+       fuse_req_t req = fuse_get_context_internal()->req;
+       return fuse_req_getgroups(req, size, list);
+}
+
 int fuse_interrupted(void)
 {
        return fuse_req_interrupted(fuse_get_context_internal()->req);
index a60c5b89a90a056e0d360b5fb39e6ae46dd96638..89e8f2fe630888057f80542fc245d9f115e5c57d 100644 (file)
@@ -1575,6 +1575,74 @@ struct fuse_session *fuse_lowlevel_new(struct fuse_args *args,
        return fuse_lowlevel_new_common(args, op, op_size, userdata);
 }
 
+#ifdef linux
+int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[])
+{
+       char *buf;
+       size_t bufsize = 1024;
+       char path[128];
+       int ret;
+       int fd;
+       unsigned long pid = req->ctx.pid;
+       char *s;
+
+       sprintf(path, "/proc/%lu/task/%lu/status", pid, pid);
+
+retry:
+       buf = malloc(bufsize);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       ret = -EIO;
+       fd = open(path, O_RDONLY);
+       if (fd == -1)
+               goto out_free;
+
+       ret = read(fd, buf, bufsize);
+       close(fd);
+       if (ret == -1) {
+               ret = -EIO;
+               goto out_free;
+       }
+
+       if (ret == bufsize) {
+               free(buf);
+               bufsize *= 4;
+               goto retry;
+       }
+
+       ret = -EIO;
+       s = strstr(buf, "\nGroups:");
+       if (s == NULL)
+               goto out_free;
+
+       s += 8;
+       ret = 0;
+       while (1) {
+               char *end;
+               unsigned long val = strtoul(s, &end, 0);
+               if (end == s)
+                       break;
+
+               s = end;
+               if (ret < size)
+                       list[ret] = val;
+               ret++;
+       }
+
+out_free:
+       free(buf);
+       return ret;
+}
+#else /* linux */
+/*
+ * This is currently not implemented on other than Linux...
+ */
+int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]);
+{
+       return -ENOSYS;
+}
+#endif
 
 #ifndef __FreeBSD__
 
index 4cd09aa073dbf9735c8325a7d4c55280a40207c1..463c74f654a85f219e0e549394a3071f9bb43797 100644 (file)
@@ -174,6 +174,8 @@ FUSE_2.8 {
                fuse_reply_ioctl_iov;
                fuse_reply_ioctl_retry;
                fuse_reply_poll;
+               fuse_req_getgroups;
+               fuse_getgroups;
 
        local:
                *;