diff -ur ntfs-3g_ntfsprogs-2014.2.15/include/fuse-lite/fuse_common.h ntfs-3g_ntfsprogs-2014.2.15.new/include/fuse-lite/fuse_common.h --- ntfs-3g_ntfsprogs-2014.2.15/include/fuse-lite/fuse_common.h 2014-02-15 14:07:52.000000000 +0000 +++ ntfs-3g_ntfsprogs-2014.2.15.new/include/fuse-lite/fuse_common.h 2014-07-31 13:47:17.401904166 +0100 @@ -49,6 +49,22 @@ #endif #define FUSE_CAP_BIG_WRITES (1 << 5) +#define FUSE_CAP_IOCTL_DIR (1 << 11) + +/** + * Ioctl flags + * + * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine + * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed + * FUSE_IOCTL_RETRY: retry with new iovecs + * FUSE_IOCTL_DIR: is a directory + */ +#define FUSE_IOCTL_COMPAT (1 << 0) +#define FUSE_IOCTL_UNRESTRICTED (1 << 1) +#define FUSE_IOCTL_RETRY (1 << 2) +#define FUSE_IOCTL_DIR (1 << 4) + +#define FUSE_IOCTL_MAX_IOV 256 /** * Information about open files diff -ur ntfs-3g_ntfsprogs-2014.2.15/include/fuse-lite/fuse.h ntfs-3g_ntfsprogs-2014.2.15.new/include/fuse-lite/fuse.h --- ntfs-3g_ntfsprogs-2014.2.15/include/fuse-lite/fuse.h 2014-02-15 14:07:52.000000000 +0000 +++ ntfs-3g_ntfsprogs-2014.2.15.new/include/fuse-lite/fuse.h 2014-07-31 13:47:17.401904166 +0100 @@ -420,9 +420,27 @@ * Introduced in version 2.6 */ int (*bmap) (const char *, size_t blocksize, uint64_t *idx); - unsigned int flag_nullpath_ok : 1; /** + * Ioctl + * + * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in + * 64bit environment. The size and direction of data is + * determined by _IOC_*() decoding of cmd. For _IOC_NONE, + * data will be NULL, for _IOC_WRITE data is out area, for + * _IOC_READ in area and if both are set in/out area. In all + * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes. + * + * Introduced in version 2.8 + */ + int (*ioctl) (const char *, int cmd, void *arg, + struct fuse_file_info *, unsigned int flags, void *data); + + /* + * The flags below have been discarded, they should not be used + */ + unsigned int flag_nullpath_ok : 1; + /** * Reserved flags, don't set */ unsigned int flag_reserved : 30; @@ -450,10 +468,8 @@ /** Private filesystem data */ void *private_data; -#ifdef POSIXACLS /** Umask of the calling process (introduced in version 2.8) */ mode_t umask; -#endif }; /* ----------------------------------------------------------- * @@ -601,6 +617,8 @@ const char *name); int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize, uint64_t *idx); +int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg, + struct fuse_file_info *fi, unsigned int flags, void *data); void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn); void fuse_fs_destroy(struct fuse_fs *fs); diff -ur ntfs-3g_ntfsprogs-2014.2.15/include/fuse-lite/fuse_kernel.h ntfs-3g_ntfsprogs-2014.2.15.new/include/fuse-lite/fuse_kernel.h --- ntfs-3g_ntfsprogs-2014.2.15/include/fuse-lite/fuse_kernel.h 2014-02-15 14:07:52.000000000 +0000 +++ ntfs-3g_ntfsprogs-2014.2.15.new/include/fuse-lite/fuse_kernel.h 2014-07-31 13:47:17.401904166 +0100 @@ -48,13 +48,19 @@ /** Version number of this interface */ #define FUSE_KERNEL_VERSION 7 -/** Minor version number of this interface */ -#ifdef POSIXACLS -#define FUSE_KERNEL_MINOR_VERSION 12 +/** Minor version number of this interface + * We introduce ourself as 7.18 (Posix ACLS : 7.12, IOCTL_DIR : 7.18) + * and we expect features features defined for 7.18, but not implemented + * here to not be triggered by ntfs-3g. + */ +#define FUSE_KERNEL_MINOR_VERSION 18 + +/* + * For binary compatibility with old kernels we accept falling back to 7.8 + */ + +#define FUSE_KERNEL_MAJOR_FALLBACK 7 #define FUSE_KERNEL_MINOR_FALLBACK 8 -#else -#define FUSE_KERNEL_MINOR_VERSION 8 -#endif /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -83,9 +89,7 @@ __u32 uid; __u32 gid; __u32 rdev; -#ifdef POSIXACLS __u64 filling; /* JPA needed for minor >= 12, but meaning unknown */ -#endif }; struct fuse_kstatfs { @@ -132,11 +136,13 @@ * INIT request/reply flags * FUSE_BIG_WRITES: allow big writes to be issued to the file system * FUSE_DONT_MASK: don't apply umask to file mode on create operations + * FUSE_HAS_IOCTL_DIR: kernel supports ioctl on directories */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) #define FUSE_BIG_WRITES (1 << 5) #define FUSE_DONT_MASK (1 << 6) +#define FUSE_HAS_IOCTL_DIR (1 << 11) /** * Release flags @@ -180,6 +186,7 @@ FUSE_INTERRUPT = 36, FUSE_BMAP = 37, FUSE_DESTROY = 38, + FUSE_IOCTL = 39, }; /* The read buffer is required to be at least 8k, but may be much larger */ @@ -215,10 +222,8 @@ struct fuse_mknod_in { __u32 mode; __u32 rdev; -#ifdef POSIXACLS __u32 umask; __u32 padding; -#endif }; struct fuse_mkdir_in { @@ -255,20 +260,14 @@ struct fuse_open_in { __u32 flags; -#ifdef POSIXACLS - __u32 unused; -#else - __u32 mode; -#endif + __u32 mode; /* unused for protocol < 7.12 */ }; struct fuse_create_in { __u32 flags; __u32 mode; -#ifdef POSIXACLS __u32 umask; __u32 padding; -#endif }; struct fuse_open_out { @@ -305,11 +304,9 @@ __u64 offset; __u32 size; __u32 write_flags; -#ifdef POSIXACLS __u64 lock_owner; /* JPA */ __u32 flags; /* JPA */ __u32 padding; /* JPA */ -#endif }; struct fuse_write_out { @@ -389,6 +386,27 @@ __u64 block; }; +struct fuse_ioctl_in { + __u64 fh; + __u32 flags; + __u32 cmd; + __u64 arg; + __u32 in_size; + __u32 out_size; +}; + +struct fuse_ioctl_iovec { + __u64 base; + __u64 len; +}; + +struct fuse_ioctl_out { + __s32 result; + __u32 flags; + __u32 in_iovs; + __u32 out_iovs; +}; + struct fuse_in_header { __u32 len; __u32 opcode; diff -ur ntfs-3g_ntfsprogs-2014.2.15/include/fuse-lite/fuse_lowlevel.h ntfs-3g_ntfsprogs-2014.2.15.new/include/fuse-lite/fuse_lowlevel.h --- ntfs-3g_ntfsprogs-2014.2.15/include/fuse-lite/fuse_lowlevel.h 2014-02-15 14:07:52.000000000 +0000 +++ ntfs-3g_ntfsprogs-2014.2.15.new/include/fuse-lite/fuse_lowlevel.h 2014-07-31 13:47:17.402904167 +0100 @@ -101,10 +101,8 @@ /** Thread ID of the calling process */ pid_t pid; -#ifdef POSIXACLS /** Umask of the calling process (introduced in version 2.8) */ mode_t umask; -#endif }; /* 'to_set' flags in setattr */ @@ -805,6 +803,37 @@ */ void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx); + /** + * Ioctl + * + * Note: For unrestricted ioctls (not allowed for FUSE + * servers), data in and out areas can be discovered by giving + * iovs and setting FUSE_IOCTL_RETRY in @flags. For + * restricted ioctls, kernel prepares in/out data area + * according to the information encoded in cmd. + * + * Introduced in version 2.8 + * + * Valid replies: + * fuse_reply_ioctl_retry + * fuse_reply_ioctl + * fuse_reply_ioctl_iov + * fuse_reply_err + * + * @param req request handle + * @param ino the inode number + * @param cmd ioctl command + * @param arg ioctl argument + * @param fi file information + * @param flags for FUSE_IOCTL_* flags + * @param in_buf data fetched from the caller + * @param in_bufsz number of fetched bytes + * @param out_bufsz maximum size of output data + */ + void (*ioctl) (fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, + struct fuse_file_info *fi, unsigned flags, + const void *in_buf, size_t in_bufsz, size_t out_bufsz); + }; /** @@ -1022,6 +1051,20 @@ const char *name, const struct stat *stbuf, off_t off); +/** + * Reply to finish ioctl + * + * Possible requests: + * ioctl + * + * @param req request handle + * @param result result to be passed to the caller + * @param buf buffer containing output data + * @param size length of output data + */ +int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size); + + /* ----------------------------------------------------------- * * Utility functions * * ----------------------------------------------------------- */ diff -ur ntfs-3g_ntfsprogs-2014.2.15/libfuse-lite/fuse.c ntfs-3g_ntfsprogs-2014.2.15.new/libfuse-lite/fuse.c --- ntfs-3g_ntfsprogs-2014.2.15/libfuse-lite/fuse.c 2014-02-15 14:07:52.000000000 +0000 +++ ntfs-3g_ntfsprogs-2014.2.15.new/libfuse-lite/fuse.c 2014-07-31 13:47:17.403904167 +0100 @@ -1040,6 +1040,21 @@ return -ENOSYS; } +int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg, + struct fuse_file_info *fi, unsigned int flags, void *data) +{ + fuse_get_context()->private_data = fs->user_data; + if (fs->op.ioctl) { +/* + if (fs->debug) + fprintf(stderr, "ioctl[%llu] 0x%x flags: 0x%x\n", + (unsigned long long) fi->fh, cmd, flags); +*/ + return fs->op.ioctl(path, cmd, arg, fi, flags, data); + } else + return -ENOSYS; +} + static int is_open(struct fuse *f, fuse_ino_t dir, const char *name) { struct node *node; @@ -2716,6 +2731,60 @@ reply_err(req, err); } +static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, + struct fuse_file_info *llfi, unsigned int flags, + const void *in_buf, size_t in_bufsz, + size_t out_bufsz) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_intr_data d; + struct fuse_file_info fi; + char *path, *out_buf = NULL; + int err; + + err = -EPERM; + if (flags & FUSE_IOCTL_UNRESTRICTED) + goto err; + + if (flags & FUSE_IOCTL_DIR) + get_dirhandle(llfi, &fi); + else + fi = *llfi; + + if (out_bufsz) { + err = -ENOMEM; + out_buf = malloc(out_bufsz); + if (!out_buf) + goto err; + } + + assert(!in_bufsz || !out_bufsz || in_bufsz == out_bufsz); + if (out_buf) + memcpy(out_buf, in_buf, in_bufsz); + + path = get_path(f, ino); /* Should be get_path_nullok() */ + if (!path) { + err = ENOENT; + goto err; + } + + fuse_prepare_interrupt(f, req, &d); + + /* Note : const qualifier dropped */ + err = fuse_fs_ioctl(f->fs, path, cmd, arg, &fi, flags, + out_buf ? (void*)out_buf : (void*)(uintptr_t)in_buf); + + fuse_finish_interrupt(f, req, &d); + free(path); + + fuse_reply_ioctl(req, err, out_buf, out_bufsz); + goto out; +err: + reply_err(req, err); +out: + free(out_buf); +} + static struct fuse_lowlevel_ops fuse_path_ops = { .init = fuse_lib_init, .destroy = fuse_lib_destroy, @@ -2751,6 +2820,7 @@ .getlk = fuse_lib_getlk, .setlk = fuse_lib_setlk, .bmap = fuse_lib_bmap, + .ioctl = fuse_lib_ioctl, }; struct fuse_session *fuse_get_session(struct fuse *f) diff -ur ntfs-3g_ntfsprogs-2014.2.15/libfuse-lite/fuse_lowlevel.c ntfs-3g_ntfsprogs-2014.2.15.new/libfuse-lite/fuse_lowlevel.c --- ntfs-3g_ntfsprogs-2014.2.15/libfuse-lite/fuse_lowlevel.c 2014-02-15 14:07:52.000000000 +0000 +++ ntfs-3g_ntfsprogs-2014.2.15.new/libfuse-lite/fuse_lowlevel.c 2014-07-31 13:47:17.403904167 +0100 @@ -333,12 +333,8 @@ memset(&arg, 0, sizeof(arg)); fill_entry(&arg, e); -#ifdef POSIXACLS return send_reply_ok(req, &arg, (req->f->conn.proto_minor >= 12 ? sizeof(arg) : FUSE_COMPAT_ENTRY_OUT_SIZE)); -#else - return send_reply_ok(req, &arg, sizeof(arg)); -#endif } int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, @@ -351,7 +347,6 @@ memset(&arg, 0, sizeof(arg)); fill_entry(&arg.e, e); -#ifdef POSIXACLS if (req->f->conn.proto_minor < 12) { fill_open((struct fuse_open_out*) ((char*)&arg + FUSE_COMPAT_ENTRY_OUT_SIZE), f); @@ -361,10 +356,6 @@ fill_open(&arg.o, f); return send_reply_ok(req, &arg, sizeof(arg)); } -#else - fill_open(&arg.o, f); - return send_reply_ok(req, &arg, sizeof(arg)); -#endif } int fuse_reply_attr(fuse_req_t req, const struct stat *attr, @@ -377,12 +368,8 @@ arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout); convert_stat(attr, &arg.attr); -#ifdef POSIXACLS return send_reply_ok(req, &arg, (req->f->conn.proto_minor >= 12 ? sizeof(arg) : FUSE_COMPAT_FUSE_ATTR_OUT_SIZE)); -#else - return send_reply_ok(req, &arg, sizeof(arg)); -#endif } int fuse_reply_readlink(fuse_req_t req, const char *linkname) @@ -462,6 +449,28 @@ return send_reply_ok(req, &arg, sizeof(arg)); } +int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size) +{ + struct fuse_ioctl_out arg; + struct iovec iov[3]; + size_t count = 1; + + memset(&arg, 0, sizeof(arg)); + arg.result = result; + iov[count].iov_base = &arg; + iov[count].iov_len = sizeof(arg); + count++; + + if (size) { + /* Note : const qualifier dropped */ + iov[count].iov_base = (char *)(uintptr_t) buf; + iov[count].iov_len = size; + count++; + } + + return send_reply_iov(req, 0, iov, count); +} + static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { const char *name = (const char *) inarg; @@ -538,11 +547,9 @@ const struct fuse_mknod_in *arg = (const struct fuse_mknod_in *) inarg; const char *name = PARAM(arg); -#ifdef POSIXACLS if (req->f->conn.proto_minor >= 12) req->ctx.umask = arg->umask; else -#endif name = (const char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE; if (req->f->op.mknod) @@ -555,10 +562,8 @@ { const struct fuse_mkdir_in *arg = (const struct fuse_mkdir_in *) inarg; -#ifdef POSIXACLS if (req->f->conn.proto_minor >= 12) req->ctx.umask = arg->umask; -#endif if (req->f->op.mkdir) req->f->op.mkdir(req, nodeid, PARAM(arg), arg->mode); @@ -630,11 +635,9 @@ memset(&fi, 0, sizeof(fi)); fi.flags = arg->flags; -#ifdef POSIXACLS if (req->f->conn.proto_minor >= 12) req->ctx.umask = arg->umask; else -#endif name = (const char *) inarg + sizeof(struct fuse_open_in); req->f->op.create(req, nodeid, name, arg->mode, &fi); @@ -682,7 +685,6 @@ fi.writepage = arg->write_flags & 1; if (req->f->op.write) { -#ifdef POSIXACLS const char *buf; if (req->f->conn.proto_minor >= 12) @@ -690,9 +692,6 @@ else buf = ((const char*)arg) + FUSE_COMPAT_WRITE_IN_SIZE; req->f->op.write(req, nodeid, buf, arg->size, arg->offset, &fi); -#else - req->f->op.write(req, nodeid, PARAM(arg), arg->size, arg->offset, &fi); -#endif } else fuse_reply_err(req, ENOSYS); } @@ -1011,6 +1010,39 @@ fuse_reply_err(req, ENOSYS); } +static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +{ + const struct fuse_ioctl_in *arg = (const struct fuse_ioctl_in *) inarg; + unsigned int flags = arg->flags; + const void *in_buf = arg->in_size ? PARAM(arg) : NULL; + struct fuse_file_info fi; + + if (flags & FUSE_IOCTL_DIR && + !(req->f->conn.want & FUSE_CAP_IOCTL_DIR)) { + fuse_reply_err(req, ENOTTY); + return; + } + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + +/* TODO JPA (need req->ioctl_64bit in obscure fuse_req_t) +// probably a 64 bit ioctl on a 32-bit cpu +// this is to forward a request from the kernel + if (sizeof(void *) == 4 && req->f->conn.proto_minor >= 16 && + !(flags & FUSE_IOCTL_32BIT)) { + req->ioctl_64bit = 1; + } +*/ + + if (req->f->op.ioctl) + req->f->op.ioctl(req, nodeid, arg->cmd, + (void *)(uintptr_t)arg->arg, &fi, flags, + in_buf, arg->in_size, arg->out_size); + else + fuse_reply_err(req, ENOSYS); +} + static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) { const struct fuse_init_in *arg = (const struct fuse_init_in *) inarg; @@ -1047,6 +1079,8 @@ #endif if (arg->flags & FUSE_BIG_WRITES) f->conn.capable |= FUSE_CAP_BIG_WRITES; + if (arg->flags & FUSE_HAS_IOCTL_DIR) + f->conn.capable |= FUSE_CAP_IOCTL_DIR; } else { f->conn.async_read = 0; f->conn.max_readahead = 0; @@ -1069,28 +1103,28 @@ memset(&outarg, 0, sizeof(outarg)); outarg.major = FUSE_KERNEL_VERSION; /* - * if POSIXACLS is not set, protocol 7.8 provides a good - * compatibility with older kernel modules. - * if POSIXACLS is set, we try to use protocol 7.12 supposed - * to have the ability to process the umask conditionnally, - * but, when using an older kernel module, we fallback to 7.8 + * Suggest using protocol 7.18 when available, and fallback + * to 7.8 when running on an old kernel. + * Protocol 7.12 has the ability to process the umask + * conditionnally (as needed if POSIXACLS is set) + * Protocol 7.18 has the ability to process the ioctls */ -#ifdef POSIXACLS - if (arg->major > 7 || (arg->major == 7 && arg->minor >= 12)) + if (arg->major > 7 || (arg->major == 7 && arg->minor >= 18)) { outarg.minor = FUSE_KERNEL_MINOR_VERSION; - else - outarg.minor = FUSE_KERNEL_MINOR_FALLBACK; -#else - outarg.minor = FUSE_KERNEL_MINOR_VERSION; + if (f->conn.want & FUSE_CAP_IOCTL_DIR) + outarg.flags |= FUSE_HAS_IOCTL_DIR; +#ifdef POSIXACLS + if (f->conn.want & FUSE_CAP_DONT_MASK) + outarg.flags |= FUSE_DONT_MASK; #endif + } else { + outarg.major = FUSE_KERNEL_MAJOR_FALLBACK; + outarg.minor = FUSE_KERNEL_MINOR_FALLBACK; + } if (f->conn.async_read) outarg.flags |= FUSE_ASYNC_READ; if (f->op.getlk && f->op.setlk) outarg.flags |= FUSE_POSIX_LOCKS; -#ifdef POSIXACLS - if (f->conn.want & FUSE_CAP_DONT_MASK) - outarg.flags |= FUSE_DONT_MASK; -#endif if (f->conn.want & FUSE_CAP_BIG_WRITES) outarg.flags |= FUSE_BIG_WRITES; outarg.max_readahead = f->conn.max_readahead; @@ -1191,6 +1225,7 @@ [FUSE_CREATE] = { do_create, "CREATE" }, [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" }, [FUSE_BMAP] = { do_bmap, "BMAP" }, + [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, [FUSE_DESTROY] = { do_destroy, "DESTROY" }, };