From ecd073bd7054c9e13516041e3ef930e39270c8df Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 19 Jun 2009 10:27:38 +0000 Subject: [PATCH] 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. --- ChangeLog | 7 +++++ README.NFS | 29 ++++++++++++++++++ include/fuse.h | 20 ++++++++++++ include/fuse_lowlevel.h | 21 +++++++++++++ lib/fuse.c | 6 ++++ lib/fuse_lowlevel.c | 68 +++++++++++++++++++++++++++++++++++++++++ lib/fuse_versionscript | 2 ++ 7 files changed, 153 insertions(+) diff --git a/ChangeLog b/ChangeLog index a32dd66..313a049 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2009-06-19 Miklos Szeredi + + * 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 * Add "noforget" option to high level lib to prevent ESTALE errors diff --git a/README.NFS b/README.NFS index b805f39..f3348d5 100644 --- a/README.NFS +++ b/README.NFS @@ -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. diff --git a/include/fuse.h b/include/fuse.h index 6ded4f5..a58cd9f 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -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 * diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index 5ab2422..a495e15 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -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 * diff --git a/lib/fuse.c b/lib/fuse.c index 9267ca6..dad9a71 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -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); diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index a60c5b8..89e8f2f 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -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__ diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript index 4cd09aa..463c74f 100644 --- a/lib/fuse_versionscript +++ b/lib/fuse_versionscript @@ -174,6 +174,8 @@ FUSE_2.8 { fuse_reply_ioctl_iov; fuse_reply_ioctl_retry; fuse_reply_poll; + fuse_req_getgroups; + fuse_getgroups; local: *; -- 2.30.2