/core/10.5/fusefs/fuse_device.c
http://macfuse.googlecode.com/ · C · 836 lines · 601 code · 186 blank · 49 comment · 100 complexity · d912749367c1306738898a2b7e5a5d3c MD5 · raw file
- /*
- * Copyright (C) 2006-2008 Google. All Rights Reserved.
- * Amit Singh <singh@>
- */
- #include "fuse.h"
- #include "fuse_device.h"
- #include "fuse_ipc.h"
- #include "fuse_internal.h"
- #include "fuse_kludges.h"
- #include "fuse_locking.h"
- #include "fuse_nodehash.h"
- #include "fuse_sysctl.h"
- #include <fuse_ioctl.h>
- #include <libkern/libkern.h>
- #define FUSE_DEVICE_GLOBAL_LOCK() fuse_lck_mtx_lock(fuse_device_mutex)
- #define FUSE_DEVICE_GLOBAL_UNLOCK() fuse_lck_mtx_unlock(fuse_device_mutex)
- #define FUSE_DEVICE_LOCAL_LOCK(d) fuse_lck_mtx_lock((d)->mtx)
- #define FUSE_DEVICE_LOCAL_UNLOCK(d) fuse_lck_mtx_unlock((d)->mtx)
- #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = TAILQ_FIRST((head)); \
- (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
- static int fuse_cdev_major = -1;
- static UInt32 fuse_interface_available = FALSE;
- struct fuse_device {
- lck_mtx_t *mtx;
- int usecount;
- pid_t pid;
- uint32_t random;
- dev_t dev;
- void *cdev;
- struct fuse_data *data;
- };
- static struct fuse_device fuse_device_table[MACFUSE_NDEVICES];
- #define FUSE_DEVICE_FROM_UNIT_FAST(u) (fuse_device_t)&(fuse_device_table[(u)])
- /* Interface for VFS */
- /* Doesn't need lock. */
- fuse_device_t
- fuse_device_get(dev_t dev)
- {
- int unit = minor(dev);
- if ((unit < 0) || (unit >= MACFUSE_NDEVICES)) {
- return (fuse_device_t)0;
- }
- return FUSE_DEVICE_FROM_UNIT_FAST(unit);
- }
- __inline__
- void
- fuse_device_lock(fuse_device_t fdev)
- {
- FUSE_DEVICE_LOCAL_LOCK(fdev);
- }
- __inline__
- void
- fuse_device_unlock(fuse_device_t fdev)
- {
- FUSE_DEVICE_LOCAL_UNLOCK(fdev);
- }
- /* Must be called under lock. */
- __inline__
- struct fuse_data *
- fuse_device_get_mpdata(fuse_device_t fdev)
- {
- return fdev->data;
- }
- /* Must be called under lock. */
- __inline__
- uint32_t
- fuse_device_get_random(fuse_device_t fdev)
- {
- return fdev->random;
- }
- /* Must be called under lock. */
- __inline__
- void
- fuse_device_close_final(fuse_device_t fdev)
- {
- if (fdev) {
- fdata_destroy(fdev->data);
- fdev->data = NULL;
- fdev->pid = -1;
- fdev->random = 0;
- }
- }
- /* /dev/fuseN implementation */
- d_open_t fuse_device_open;
- d_close_t fuse_device_close;
- d_read_t fuse_device_read;
- d_write_t fuse_device_write;
- d_ioctl_t fuse_device_ioctl;
- #if M_MACFUSE_ENABLE_DSELECT
- d_select_t fuse_device_select;
- #else
- #define fuse_device_select (d_select_t*)enodev
- #endif /* M_MACFUSE_ENABLE_DSELECT */
- static struct cdevsw fuse_device_cdevsw = {
- /* open */ fuse_device_open,
- /* close */ fuse_device_close,
- /* read */ fuse_device_read,
- /* write */ fuse_device_write,
- /* ioctl */ fuse_device_ioctl,
- /* stop */ (d_stop_t *)enodev,
- /* reset */ (d_reset_t *)enodev,
- /* ttys */ 0,
- /* select */ fuse_device_select,
- /* mmap */ (d_mmap_t *)enodev,
- /* strategy */ (d_strategy_t *)enodev_strat,
- #ifdef d_getc_t
- /* getc */ (d_getc_t *)enodev,
- #else
- /* reserved */ (void *)enodev,
- #endif
- #ifdef d_putc_t
- /* putc */ (d_putc_t *)enodev,
- #else
- /* reserved */ (void *)enodev,
- #endif
- /* flags */ D_TTY,
- };
- int
- fuse_device_open(dev_t dev, __unused int flags, __unused int devtype,
- struct proc *p)
- {
- int unit;
- struct fuse_device *fdev;
- struct fuse_data *fdata;
- fuse_trace_printf_func();
- if (fuse_interface_available == FALSE) {
- return ENOENT;
- }
- unit = minor(dev);
- if ((unit >= MACFUSE_NDEVICES) || (unit < 0)) {
- FUSE_DEVICE_GLOBAL_UNLOCK();
- return ENOENT;
- }
- fdev = FUSE_DEVICE_FROM_UNIT_FAST(unit);
- if (!fdev) {
- FUSE_DEVICE_GLOBAL_UNLOCK();
- IOLog("MacFUSE: device found with no softc\n");
- return ENXIO;
- }
- FUSE_DEVICE_GLOBAL_LOCK();
- if (fdev->usecount != 0) {
- FUSE_DEVICE_GLOBAL_UNLOCK();
- return EBUSY;
- }
- fdev->usecount++;
- FUSE_DEVICE_LOCAL_LOCK(fdev);
- FUSE_DEVICE_GLOBAL_UNLOCK();
- /* Could block. */
- fdata = fdata_alloc(p);
- if (fdev->data) {
- /*
- * This slot isn't currently open by a user daemon. However, it was
- * used earlier for a mount that's still lingering, even though the
- * user daemon is dead.
- */
- FUSE_DEVICE_GLOBAL_LOCK();
- fdev->usecount--;
- FUSE_DEVICE_LOCAL_UNLOCK(fdev);
- FUSE_DEVICE_GLOBAL_UNLOCK();
- fdata_destroy(fdata);
- return EBUSY;
- } else {
- fdata->dataflags |= FSESS_OPENED;
- fdata->fdev = fdev;
- fdev->data = fdata;
- fdev->pid = proc_pid(p);
- fdev->random = random();
- }
- FUSE_DEVICE_LOCAL_UNLOCK(fdev);
- return KERN_SUCCESS;
- }
- int
- fuse_device_close(dev_t dev, __unused int flags, __unused int devtype,
- __unused struct proc *p)
- {
- int unit;
- struct fuse_device *fdev;
- struct fuse_data *data;
- fuse_trace_printf_func();
- unit = minor(dev);
- if (unit >= MACFUSE_NDEVICES) {
- return ENOENT;
- }
- fdev = FUSE_DEVICE_FROM_UNIT_FAST(unit);
- if (!fdev) {
- return ENXIO;
- }
- data = fdev->data;
- if (!data) {
- panic("MacFUSE: no device private data in device_close");
- }
- fdata_set_dead(data);
- FUSE_DEVICE_LOCAL_LOCK(fdev);
- data->dataflags &= ~FSESS_OPENED;
- fuse_lck_mtx_lock(data->aw_mtx);
- #if M_MACFUSE_ENABLE_DSELECT
- selwakeup((struct selinfo*)&data->d_rsel);
- #endif /* M_MACFUSE_ENABLE_DSELECT */
- if (data->mount_state == FM_MOUNTED) {
- /* Uh-oh, the device is closing but we're still mounted. */
- struct fuse_ticket *ftick;
- while ((ftick = fuse_aw_pop(data))) {
- fuse_lck_mtx_lock(ftick->tk_aw_mtx);
- fticket_set_answered(ftick);
- ftick->tk_aw_errno = ENOTCONN;
- fuse_wakeup(ftick);
- fuse_lck_mtx_unlock(ftick->tk_aw_mtx);
- }
- fuse_lck_mtx_unlock(data->aw_mtx);
- /* Left mpdata for unmount to destroy. */
- } else {
- /* We're not mounted. Can destroy mpdata. */
- fdev->data = NULL;
- fdev->pid = -1;
- fdev->random = 0;
- fdata_destroy(data);
- }
- FUSE_DEVICE_LOCAL_UNLOCK(fdev);
- FUSE_DEVICE_GLOBAL_LOCK();
- /*
- * Even if usecount goes 0 here, at open time, we check if fdev->data
- * is non-NULL (that is, a lingering mount). If so, we return EBUSY.
- * We could make the usecount depend on both device-use and mount-state,
- * but I think this is truer to reality, if a bit more complex to maintain.
- */
- fdev->usecount--;
- FUSE_DEVICE_GLOBAL_UNLOCK();
- return KERN_SUCCESS;
- }
- int
- fuse_device_read(dev_t dev, uio_t uio, int ioflag)
- {
- int i, err = 0;
- size_t buflen[3];
- void *buf[] = { NULL, NULL, NULL };
- struct fuse_device *fdev;
- struct fuse_data *data;
- struct fuse_ticket *ftick;
- fuse_trace_printf_func();
- fdev = FUSE_DEVICE_FROM_UNIT_FAST(minor(dev));
- if (!fdev) {
- return ENXIO;
- }
- data = fdev->data;
- fuse_lck_mtx_lock(data->ms_mtx);
- /* The read loop (outgoing messages to the user daemon). */
- again:
- if (fdata_dead_get(data)) {
- fuse_lck_mtx_unlock(data->ms_mtx);
- return ENODEV;
- }
- if (!(ftick = fuse_ms_pop(data))) {
- if (ioflag & FNONBLOCK) {
- fuse_lck_mtx_unlock(data->ms_mtx);
- return EAGAIN;
- }
- err = fuse_msleep(data, data->ms_mtx, PCATCH, "fu_msg", NULL);
- if (err != 0) {
- fuse_lck_mtx_unlock(data->ms_mtx);
- return (fdata_dead_get(data) ? ENODEV : err);
- }
- ftick = fuse_ms_pop(data);
- }
- if (!ftick) {
- goto again;
- }
- fuse_lck_mtx_unlock(data->ms_mtx);
- if (fdata_dead_get(data)) {
- if (ftick) {
- fuse_ticket_drop_invalid(ftick);
- }
- return ENODEV;
- }
- switch (ftick->tk_ms_type) {
- case FT_M_FIOV:
- buf[0] = ftick->tk_ms_fiov.base;
- buflen[0] = ftick->tk_ms_fiov.len;
- break;
- case FT_M_BUF:
- buf[0] = ftick->tk_ms_fiov.base;
- buflen[0] = ftick->tk_ms_fiov.len;
- buf[1] = ftick->tk_ms_bufdata;
- buflen[1] = ftick->tk_ms_bufsize;
- break;
- default:
- panic("MacFUSE: unknown message type for ticket %p", ftick);
- }
- for (i = 0; buf[i]; i++) {
- if (uio_resid(uio) < (user_ssize_t)buflen[i]) {
- data->dataflags |= FSESS_DEAD;
- err = ENODEV;
- break;
- }
- err = uiomove(buf[i], (int)buflen[i], uio);
- if (err) {
- break;
- }
- }
- /*
- * XXX: Stop gap! I really need to finish interruption plumbing.
- */
- if (fticket_answered(ftick)) {
- err = EINTR;
- }
- /*
- * The FORGET message is an example of a ticket that has explicitly
- * been invalidated by the sender. The sender is not expecting or wanting
- * a reply, so he sets the FT_INVALID bit in the ticket.
- */
-
- fuse_ticket_drop_invalid(ftick);
- return err;
- }
- int
- fuse_device_write(dev_t dev, uio_t uio, __unused int ioflag)
- {
- int err = 0, found = 0;
- struct fuse_device *fdev;
- struct fuse_data *data;
- struct fuse_ticket *ftick;
- struct fuse_ticket *x_ftick;
- struct fuse_out_header ohead;
- fuse_trace_printf_func();
- fdev = FUSE_DEVICE_FROM_UNIT_FAST(minor(dev));
- if (!fdev) {
- return ENXIO;
- }
- if (uio_resid(uio) < (user_ssize_t)sizeof(struct fuse_out_header)) {
- return EINVAL;
- }
- if ((err = uiomove((caddr_t)&ohead, (int)sizeof(struct fuse_out_header),
- uio))
- != 0) {
- return err;
- }
- /* begin audit */
- if (uio_resid(uio) + sizeof(struct fuse_out_header) != ohead.len) {
- IOLog("MacFUSE: message body size does not match that in the header\n");
- return EINVAL;
- }
- if (uio_resid(uio) && ohead.error) {
- IOLog("MacFUSE: non-zero error for a message with a body\n");
- return EINVAL;
- }
- ohead.error = -(ohead.error);
- /* end audit */
- data = fdev->data;
- fuse_lck_mtx_lock(data->aw_mtx);
- TAILQ_FOREACH_SAFE(ftick, &data->aw_head, tk_aw_link, x_ftick) {
- if (ftick->tk_unique == ohead.unique) {
- found = 1;
- fuse_aw_remove(ftick);
- break;
- }
- }
- fuse_lck_mtx_unlock(data->aw_mtx);
- if (found) {
- if (ftick->tk_aw_handler) {
- memcpy(&ftick->tk_aw_ohead, &ohead, sizeof(ohead));
- err = ftick->tk_aw_handler(ftick, uio);
- } else {
- fuse_ticket_drop(ftick);
- return err;
- }
- } else {
- /* ticket has no response handler */
- }
- return err;
- }
- int
- fuse_devices_start(void)
- {
- int i = 0;
- fuse_trace_printf_func();
- bzero((void *)fuse_device_table, sizeof(fuse_device_table));
- if ((fuse_cdev_major = cdevsw_add(-1, &fuse_device_cdevsw)) == -1) {
- goto error;
- }
- for (i = 0; i < MACFUSE_NDEVICES; i++) {
- dev_t dev = makedev(fuse_cdev_major, i);
- fuse_device_table[i].cdev = devfs_make_node(
- dev,
- DEVFS_CHAR,
- UID_ROOT,
- GID_OPERATOR,
- 0666,
- MACFUSE_DEVICE_BASENAME "%d",
- i);
- if (fuse_device_table[i].cdev == NULL) {
- goto error;
- }
- fuse_device_table[i].data = NULL;
- fuse_device_table[i].dev = dev;
- fuse_device_table[i].pid = -1;
- fuse_device_table[i].random = 0;
- fuse_device_table[i].usecount = 0;
- fuse_device_table[i].mtx = lck_mtx_alloc_init(fuse_lock_group,
- fuse_lock_attr);
- }
- fuse_interface_available = TRUE;
- return KERN_SUCCESS;
- error:
- for (--i; i >= 0; i--) {
- devfs_remove(fuse_device_table[i].cdev);
- fuse_device_table[i].cdev = NULL;
- fuse_device_table[i].dev = 0;
- lck_mtx_free(fuse_device_table[i].mtx, fuse_lock_group);
- }
- (void)cdevsw_remove(fuse_cdev_major, &fuse_device_cdevsw);
- fuse_cdev_major = -1;
- return KERN_FAILURE;
- }
- int
- fuse_devices_stop(void)
- {
- int i, ret;
- fuse_trace_printf_func();
- fuse_interface_available = FALSE;
- FUSE_DEVICE_GLOBAL_LOCK();
- if (fuse_cdev_major == -1) {
- FUSE_DEVICE_GLOBAL_UNLOCK();
- return KERN_SUCCESS;
- }
- for (i = 0; i < MACFUSE_NDEVICES; i++) {
- char p_comm[MAXCOMLEN + 1] = { '?', '\0' };
- if (fuse_device_table[i].usecount != 0) {
- fuse_interface_available = TRUE;
- FUSE_DEVICE_GLOBAL_UNLOCK();
- proc_name(fuse_device_table[i].pid, p_comm, MAXCOMLEN + 1);
- IOLog("MacFUSE: /dev/fuse%d is still active (pid=%d %s)\n",
- i, fuse_device_table[i].pid, p_comm);
- return KERN_FAILURE;
- }
- if (fuse_device_table[i].data != NULL) {
- fuse_interface_available = TRUE;
- FUSE_DEVICE_GLOBAL_UNLOCK();
- proc_name(fuse_device_table[i].pid, p_comm, MAXCOMLEN + 1);
- /* The pid can't possibly be active here. */
- IOLog("MacFUSE: /dev/fuse%d has a lingering mount (pid=%d, %s)\n",
- i, fuse_device_table[i].pid, p_comm);
- return KERN_FAILURE;
- }
- }
- /* No device is in use. */
- for (i = 0; i < MACFUSE_NDEVICES; i++) {
- devfs_remove(fuse_device_table[i].cdev);
- lck_mtx_free(fuse_device_table[i].mtx, fuse_lock_group);
- fuse_device_table[i].cdev = NULL;
- fuse_device_table[i].dev = 0;
- fuse_device_table[i].pid = -1;
- fuse_device_table[i].random = 0;
- fuse_device_table[i].mtx = NULL;
- }
- ret = cdevsw_remove(fuse_cdev_major, &fuse_device_cdevsw);
- if (ret != fuse_cdev_major) {
- IOLog("MacFUSE: fuse_cdev_major != return from cdevsw_remove()\n");
- }
- fuse_cdev_major = -1;
- FUSE_DEVICE_GLOBAL_UNLOCK();
- return KERN_SUCCESS;
- }
- /* Control/Debug Utilities */
- int
- fuse_device_ioctl(dev_t dev, u_long cmd, caddr_t udata,
- __unused int flags, __unused proc_t proc)
- {
- int ret = EINVAL;
- struct fuse_device *fdev;
- struct fuse_data *data;
- fuse_trace_printf_func();
- fdev = FUSE_DEVICE_FROM_UNIT_FAST(minor(dev));
- if (!fdev) {
- return ENXIO;
- }
- FUSE_DEVICE_LOCAL_LOCK(fdev);
- data = fdev->data;
- if (!data) {
- FUSE_DEVICE_LOCAL_UNLOCK(fdev);
- return ENXIO;
- }
- switch (cmd) {
- case FUSEDEVIOCSETIMPLEMENTEDBITS:
- ret = fuse_set_implemented_custom(data, *(uint64_t *)udata);
- break;
- case FUSEDEVIOCGETHANDSHAKECOMPLETE:
- if (data->mount_state == FM_NOTMOUNTED) {
- ret = ENXIO;
- } else {
- *(u_int32_t *)udata = (data->dataflags & FSESS_INITED);
- ret = 0;
- }
- break;
- case FUSEDEVIOCSETDAEMONDEAD:
- fdata_set_dead(data);
- fuse_lck_mtx_lock(data->timeout_mtx);
- data->timeout_status = FUSE_DAEMON_TIMEOUT_DEAD;
- fuse_lck_mtx_unlock(data->timeout_mtx);
- ret = 0;
- break;
- case FUSEDEVIOCGETRANDOM:
- *(u_int32_t *)udata = fdev->random;
- ret = 0;
- break;
- /*
- * The 'AVFI' (alter-vnode-for-inode) ioctls all require an inode number
- * as an argument. In the user-space library, you can get the inode number
- * from a path by using fuse_lookup_inode_by_path_np() [lib/fuse.c].
- *
- * To see an example of using this, see the implementation of
- * fuse_purge_path_np() in lib/fuse_darwin.c.
- */
- case FUSEDEVIOCALTERVNODEFORINODE:
- {
- HNodeRef hn;
- vnode_t vn;
- fuse_device_t dummy_device = data->fdev;
- struct fuse_avfi_ioctl *avfi = (struct fuse_avfi_ioctl *)udata;
- ret = (int)HNodeLookupRealQuickIfExists(dummy_device,
- (ino_t)avfi->inode,
- 0, /* fork index */
- &hn,
- &vn);
- if (ret) {
- break;
- }
- assert(vn != NULL);
- ret = fuse_internal_ioctl_avfi(vn, (vfs_context_t)0, avfi);
- if (vn) {
- vnode_put(vn);
- }
- }
- break;
- default:
- break;
-
- }
- FUSE_DEVICE_LOCAL_UNLOCK(fdev);
- return ret;
- }
- #if M_MACFUSE_ENABLE_DSELECT
- int
- fuse_device_select(dev_t dev, int events, void *wql, struct proc *p)
- {
- int unit, revents = 0;
- struct fuse_device *fdev;
- struct fuse_data *data;
- fuse_trace_printf_func();
- unit = minor(dev);
- if (unit >= MACFUSE_NDEVICES) {
- return ENOENT;
- }
- fdev = FUSE_DEVICE_FROM_UNIT_FAST(unit);
- if (!fdev) {
- return ENXIO;
- }
- data = fdev->data;
- if (!data) {
- panic("MacFUSE: no device private data in device_select");
- }
- if (events & (POLLIN | POLLRDNORM)) {
- fuse_lck_mtx_lock(data->ms_mtx);
- if (fdata_dead_get(data) || STAILQ_FIRST(&data->ms_head)) {
- revents |= (events & (POLLIN | POLLRDNORM));
- } else {
- selrecord((proc_t)p, (struct selinfo*)&data->d_rsel, wql);
- }
- fuse_lck_mtx_unlock(data->ms_mtx);
- }
- if (events & (POLLOUT | POLLWRNORM)) {
- revents |= (events & (POLLOUT | POLLWRNORM));
- }
- return revents;
- }
- #endif /* M_MACFUSE_ENABLE_DSELECT */
- int
- fuse_device_kill(int unit, struct proc *p)
- {
- int error = ENOENT;
- struct fuse_device *fdev;
- if ((unit < 0) || (unit >= MACFUSE_NDEVICES)) {
- return EINVAL;
- }
- fdev = FUSE_DEVICE_FROM_UNIT_FAST(unit);
- if (!fdev) {
- return ENOENT;
- }
- FUSE_DEVICE_LOCAL_LOCK(fdev);
- if (fdev->data) {
- error = EPERM;
- if (p) {
- kauth_cred_t request_cred = kauth_cred_proc_ref(p);
- if ((kauth_cred_getuid(request_cred) == 0) ||
- (fuse_match_cred(fdev->data->daemoncred, request_cred) == 0)) {
- /* The following can block. */
- fdata_set_dead(fdev->data);
- error = 0;
- fuse_lck_mtx_lock(fdev->data->aw_mtx);
- {
- struct fuse_ticket *ftick;
- while ((ftick = fuse_aw_pop(fdev->data))) {
- fuse_lck_mtx_lock(ftick->tk_aw_mtx);
- fticket_set_answered(ftick);
- ftick->tk_aw_errno = ENOTCONN;
- fuse_wakeup(ftick);
- fuse_lck_mtx_unlock(ftick->tk_aw_mtx);
- }
- }
- fuse_lck_mtx_unlock(fdev->data->aw_mtx);
- }
- kauth_cred_unref(&request_cred);
- }
- }
- FUSE_DEVICE_LOCAL_UNLOCK(fdev);
- return error;
- }
- int
- fuse_device_print_vnodes(int unit_flags, struct proc *p)
- {
- int error = ENOENT;
- struct fuse_device *fdev;
- int unit = unit_flags;
- if ((unit < 0) || (unit >= MACFUSE_NDEVICES)) {
- return EINVAL;
- }
- fdev = FUSE_DEVICE_FROM_UNIT_FAST(unit);
- if (!fdev) {
- return ENOENT;
- }
- FUSE_DEVICE_LOCAL_LOCK(fdev);
- if (fdev->data) {
- mount_t mp = fdev->data->mp;
- if (vfs_busy(mp, LK_NOWAIT)) {
- FUSE_DEVICE_LOCAL_UNLOCK(fdev);
- return EBUSY;
- }
-
- error = EPERM;
- if (p) {
- kauth_cred_t request_cred = kauth_cred_proc_ref(p);
- if ((kauth_cred_getuid(request_cred) == 0) ||
- (fuse_match_cred(fdev->data->daemoncred, request_cred) == 0)) {
- fuse_internal_print_vnodes(fdev->data->mp);
- error = 0;
- }
- kauth_cred_unref(&request_cred);
- }
- vfs_unbusy(mp);
- }
- FUSE_DEVICE_LOCAL_UNLOCK(fdev);
- return error;
- }