Changeset View
Changeset View
Standalone View
Standalone View
sys/fs/fuse/fuse_file.h
| Show All 26 Lines | |||||
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
| * | * | ||||
| * Copyright (C) 2005 Csaba Henk. | * Copyright (C) 2005 Csaba Henk. | ||||
| * All rights reserved. | * All rights reserved. | ||||
| * | * | ||||
| * Copyright (c) 2019 The FreeBSD Foundation | |||||
| * | |||||
| * Portions of this software were developed by BFF Storage Systems, LLC under | |||||
| * sponsorship from the FreeBSD Foundation. | |||||
| * | |||||
| * Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
| * modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
| * are met: | * are met: | ||||
| * 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
| * notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
| * 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
| * notice, this list of conditions and the following disclaimer in the | * notice, this list of conditions and the following disclaimer in the | ||||
| * documentation and/or other materials provided with the distribution. | * documentation and/or other materials provided with the distribution. | ||||
| Show All 17 Lines | |||||
| #define _FUSE_FILE_H_ | #define _FUSE_FILE_H_ | ||||
| #include <sys/types.h> | #include <sys/types.h> | ||||
| #include <sys/fcntl.h> | #include <sys/fcntl.h> | ||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||
| #include <sys/mman.h> | #include <sys/mman.h> | ||||
| #include <sys/vnode.h> | #include <sys/vnode.h> | ||||
| /* | |||||
| * The fufh type is the access mode of the fuse file handle. It's the portion | |||||
| * of the open(2) flags related to permission. | |||||
| */ | |||||
| typedef enum fufh_type { | typedef enum fufh_type { | ||||
| FUFH_INVALID = -1, | FUFH_INVALID = -1, | ||||
| FUFH_RDONLY = 0, | FUFH_RDONLY = O_RDONLY, | ||||
| FUFH_WRONLY = 1, | FUFH_WRONLY = O_WRONLY, | ||||
| FUFH_RDWR = 2, | FUFH_RDWR = O_RDWR, | ||||
| FUFH_MAXTYPE = 3, | FUFH_EXEC = O_EXEC, | ||||
| } fufh_type_t; | } fufh_type_t; | ||||
| _Static_assert(FUFH_RDONLY == O_RDONLY, "RDONLY"); | |||||
| _Static_assert(FUFH_WRONLY == O_WRONLY, "WRONLY"); | |||||
| _Static_assert(FUFH_RDWR == O_RDWR, "RDWR"); | |||||
| /* | |||||
| * FUSE File Handles | |||||
| * | |||||
| * The FUSE protocol says that a server may assign a unique 64-bit file handle | |||||
| * every time that a file is opened. Effectively, that's once for each file | |||||
| * descriptor. | |||||
| * | |||||
| * Unfortunately, the VFS doesn't help us here. VOPs don't have a | |||||
| * struct file* argument. fileops do, but many syscalls bypass the fileops | |||||
| * layer and go straight to a vnode. Some, like writing from cache, can't | |||||
| * track a file handle even in theory. The entire concept of the file handle | |||||
| * is a product of FUSE's Linux origins; Linux lacks vnodes and almost every | |||||
| * file system operation takes a struct file* argument. | |||||
| * | |||||
| * Since FreeBSD's VFS is more file descriptor-agnostic, we must store FUSE | |||||
| * filehandles in the vnode. One option would be to only store a single file | |||||
| * handle and never open FUSE files concurrently. That's what NetBSD does. | |||||
| * But that violates FUSE's security model. FUSE expects the server to do all | |||||
| * authorization (except when mounted with -o default_permissions). In order | |||||
| * to do that, the server needs us to send FUSE_OPEN every time somebody opens | |||||
| * a new file descriptor. | |||||
| * | |||||
| * Another option would be to never open FUSE files concurrently, but send a | |||||
| * FUSE_ACCESS prior to every open after the first. That would give the server | |||||
| * the opportunity to authorize the access. Unfortunately, the FUSE protocol | |||||
| * makes ACCESS optional. File systems that don't implement it are assumed to | |||||
| * authorize everything. A survey of 32 fuse file systems showed that only 14 | |||||
| * implemented access. Among the laggards were a few that really ought to be | |||||
| * doing server-side authorization. | |||||
| * | |||||
| * So we do something hacky, similar to what OpenBSD, Illumos, and OSXFuse do. | |||||
| * we store a list of file handles, one for each combination of vnode, uid, | |||||
| * gid, pid, and access mode. When opening a file, we first check whether | |||||
| * there's already a matching file handle. If so, we reuse it. If not, we | |||||
| * send FUSE_OPEN and create a new file handle. That minimizes the number of | |||||
| * open file handles while still allowing the server to authorize stuff. | |||||
| * | |||||
| * VOPs that need a file handle search through the list for a close match. | |||||
| * They can't be guaranteed of finding an exact match because, for example, a | |||||
| * process may have changed its UID since opening the file. Also, most VOPs | |||||
| * don't know exactly what permission they need. Is O_RDWR required or is | |||||
| * O_RDONLY good enough? So the file handle we end up using may not be exactly | |||||
| * the one we're supposed to use with that file descriptor. But if the FUSE | |||||
| * file system isn't too picky, it will work. (FWIW even Linux sometimes | |||||
| * guesses the file handle, during writes from cache or most SETATTR | |||||
| * operations). | |||||
| * | |||||
| * I suspect this mess is part of the reason why neither NFS nor 9P have an | |||||
| * equivalent of FUSE file handles. | |||||
| */ | |||||
| struct fuse_filehandle { | struct fuse_filehandle { | ||||
| LIST_ENTRY(fuse_filehandle) next; | |||||
| /* The filehandle returned by FUSE_OPEN */ | |||||
| uint64_t fh_id; | uint64_t fh_id; | ||||
| fufh_type_t fh_type; | |||||
| }; | |||||
| #define FUFH_IS_VALID(f) ((f)->fh_type != FUFH_INVALID) | /* | ||||
| * flags returned by FUSE_OPEN | |||||
| * Supported flags: FOPEN_DIRECT_IO, FOPEN_KEEP_CACHE | |||||
| * Unsupported: | |||||
| * FOPEN_NONSEEKABLE: Adding support would require a new per-file | |||||
| * or per-vnode attribute, which would have to be checked by | |||||
| * kern_lseek (and others) for every file system. The benefit is | |||||
| * dubious, since I'm unaware of any file systems in ports that use | |||||
| * this flag. | |||||
| */ | |||||
| uint32_t fuse_open_flags; | |||||
| static inline fufh_type_t | /* The access mode of the file handle */ | ||||
| fuse_filehandle_xlate_from_mmap(int fflags) | fufh_type_t fufh_type; | ||||
| { | |||||
| if (fflags & (PROT_READ | PROT_WRITE)) | |||||
| return FUFH_RDWR; | |||||
| else if (fflags & (PROT_WRITE)) | |||||
| return FUFH_WRONLY; | |||||
| else if ((fflags & PROT_READ) || (fflags & PROT_EXEC)) | |||||
| return FUFH_RDONLY; | |||||
| else | |||||
| return FUFH_INVALID; | |||||
| } | |||||
| static inline fufh_type_t | /* Credentials used to open the file */ | ||||
| fuse_filehandle_xlate_from_fflags(int fflags) | gid_t gid; | ||||
| { | pid_t pid; | ||||
| if ((fflags & FREAD) && (fflags & FWRITE)) | uid_t uid; | ||||
| return FUFH_RDWR; | }; | ||||
| else if (fflags & (FWRITE)) | |||||
| return FUFH_WRONLY; | |||||
| else if (fflags & (FREAD)) | |||||
| return FUFH_RDONLY; | |||||
| else | |||||
| panic("FUSE: What kind of a flag is this (%x)?", fflags); | |||||
| } | |||||
| #define FUFH_IS_VALID(f) ((f)->fufh_type != FUFH_INVALID) | |||||
| /* | |||||
| * Get the flags to use for FUSE_CREATE, FUSE_OPEN and FUSE_RELEASE | |||||
| * | |||||
| * These are supposed to be the same as the flags argument to open(2). | |||||
| * However, since we can't reliably associate a fuse_filehandle with a specific | |||||
| * file descriptor it would would be dangerous to include anything more than | |||||
| * the access mode flags. For example, suppose we open a file twice, once with | |||||
| * O_APPEND and once without. Then the user pwrite(2)s to offset using the | |||||
| * second file descriptor. If fusefs uses the first file handle, then the | |||||
| * server may append the write to the end of the file rather than at offset 0. | |||||
| * To prevent problems like this, we only ever send the portion of flags | |||||
| * related to access mode. | |||||
| * | |||||
| * It's essential to send that portion, because FUSE uses it for server-side | |||||
| * authorization. | |||||
| */ | |||||
| static inline int | static inline int | ||||
| fuse_filehandle_xlate_to_oflags(fufh_type_t type) | fufh_type_2_fflags(fufh_type_t type) | ||||
| { | { | ||||
| int oflags = -1; | int oflags = -1; | ||||
| switch (type) { | switch (type) { | ||||
| case FUFH_RDONLY: | case FUFH_RDONLY: | ||||
| case FUFH_WRONLY: | case FUFH_WRONLY: | ||||
| case FUFH_RDWR: | case FUFH_RDWR: | ||||
| case FUFH_EXEC: | |||||
| oflags = type; | oflags = type; | ||||
| break; | break; | ||||
| default: | default: | ||||
| break; | break; | ||||
| } | } | ||||
| return oflags; | return oflags; | ||||
| } | } | ||||
| int fuse_filehandle_valid(struct vnode *vp, fufh_type_t fufh_type); | bool fuse_filehandle_validrw(struct vnode *vp, int mode, | ||||
| fufh_type_t fuse_filehandle_validrw(struct vnode *vp, fufh_type_t fufh_type); | struct ucred *cred, pid_t pid); | ||||
| int fuse_filehandle_get(struct vnode *vp, fufh_type_t fufh_type, | int fuse_filehandle_get(struct vnode *vp, int fflag, | ||||
| struct fuse_filehandle **fufhp); | struct fuse_filehandle **fufhp, struct ucred *cred, | ||||
| int fuse_filehandle_getrw(struct vnode *vp, fufh_type_t fufh_type, | pid_t pid); | ||||
| struct fuse_filehandle **fufhp); | int fuse_filehandle_get_anyflags(struct vnode *vp, | ||||
| struct fuse_filehandle **fufhp, struct ucred *cred, | |||||
| pid_t pid); | |||||
| int fuse_filehandle_getrw(struct vnode *vp, int fflag, | |||||
| struct fuse_filehandle **fufhp, struct ucred *cred, | |||||
| pid_t pid); | |||||
| void fuse_filehandle_init(struct vnode *vp, fufh_type_t fufh_type, | void fuse_filehandle_init(struct vnode *vp, fufh_type_t fufh_type, | ||||
| struct fuse_filehandle **fufhp, uint64_t fh_id); | |||||
| int fuse_filehandle_open(struct vnode *vp, fufh_type_t fufh_type, | |||||
| struct fuse_filehandle **fufhp, struct thread *td, | struct fuse_filehandle **fufhp, struct thread *td, | ||||
| struct ucred *cred, struct fuse_open_out *foo); | |||||
| int fuse_filehandle_open(struct vnode *vp, int mode, | |||||
| struct fuse_filehandle **fufhp, struct thread *td, | |||||
| struct ucred *cred); | struct ucred *cred); | ||||
| int fuse_filehandle_close(struct vnode *vp, fufh_type_t fufh_type, | int fuse_filehandle_close(struct vnode *vp, struct fuse_filehandle *fufh, | ||||
| struct thread *td, struct ucred *cred); | struct thread *td, struct ucred *cred); | ||||
| void fuse_file_init(void); | |||||
| void fuse_file_destroy(void); | |||||
| #endif /* _FUSE_FILE_H_ */ | #endif /* _FUSE_FILE_H_ */ | ||||