10 Commits

Author SHA1 Message Date
Godzil
1afa952fb0 Fix PEBCAK 2012-12-04 14:26:06 +01:00
Godzil
b208b10645 I was a bit optimistic. 2.6.32 does not support DECLARE_SEMAPHORE.
Now use DECLARE_MUTEX up to 2.6.35 (need to be checked)

Signed-off-by: Godzil <godzil@godzil.net>
2012-10-16 20:07:34 +02:00
Godzil
3c0b0cdd4b Correct support for kernel <2.6.33 2012-09-10 20:12:13 +02:00
Godzil
9f496af46b Correct fusd_destroy to lock and check before freeing. 2012-07-10 14:06:27 +02:00
Godzil
fd1f2a7374 Cosmetics, cosmetics and cosmetics. 2012-07-10 14:06:00 +02:00
Godzil
0678a66b3c add locking mechanism in the fusd_file_info_t structure to prevent potential race conditions. 2012-07-10 14:00:05 +02:00
Godzil
542d87ea75 Update libfusd accordingly with latest changes in kfusd. 2012-07-10 13:49:37 +02:00
Godzil
472265ae8a Add support for newers kernels, remove the temporary IOCTL and set back the writev function. 2012-07-10 12:48:23 +02:00
Godzil
789713fa0b Make change to allow to compile under kernel version > 2.6.36 (tested with a 3.2.9 kernel) 2012-04-04 11:31:16 +02:00
Godzil
efd39ff55a Update README file (and convert it to Markdown) 2012-02-13 18:36:24 +01:00
5 changed files with 416 additions and 202 deletions

View File

@@ -2,13 +2,20 @@
FUSD: A Linux Framework for User-Space Devices
----------------------------------------------
Welcome to FUSD!
**Welcome to FUSD!**
This is FUSD snapshot 20070111, released 11 January 2007. You can get
the most recent source, along with online documentation, from xiph.org
SVN:
This is FUSD snapshot 20110401, released 18 January 2012. This fork is based
on the found on the xiph.org SVN tracker. ( http://svn.xiph.org/trunk/fusd )
They seems to no longuer update this tool (since 11 January 2007) and since it
longuer compile with recent Linux kernel (at around 2.6.21) and since I need
it in personal project, I ported it to newer version (current version is 2.6.32)
http://svn.xiph.org/trunk/fusd
Some feature are still missing missing or buggy form the Xiph version (due to
changes on the kernel source tree), but it's completly useable.
The official URL for this fork is:
http://github.com/Godzil/fusd
There is extensive documentation available in the 'doc' directory.
The FUSD User Manual is available in PDF, Postscript, and HTML format.
@@ -17,7 +24,7 @@ it is fully updated, it may not cover all features that exist in the
current version of fusd.
FUSD is free and open source software, released under a BSD-style
license. See the file 'LICENSE' for details.
license. See the file 'LICENSE' for details.
QUICK START GUIDE
@@ -25,81 +32,81 @@ QUICK START GUIDE
Instructions for the impatient:
1- Make sure you're using a system running Linux 2.6.x with udev; this
version of fusd is incompatable with the now-deprecated devfs. If the
1. Make sure you're using a system running Linux 2.6.x with udev; this
version of fusd is incompatable with the now-deprecated devfs. If the
kernel is a packaged version from a distribution, also verify any
optional packages needed for building new kernel modules are also
installed.
2- 'make ; make install' builds everything including examples, then
2. 'make ; make install' builds everything including examples, then
installs the libraries, includes and kernel module.
3- Update the udev configuration (usually in /etc/udev/rules.d/) to
3. Update the udev configuration (usually in /etc/udev/rules.d/) to
include the following rule:
# fusd device
SUBSYSTEM=="fusd", NAME="fusd/%k"
fusd device
SUBSYSTEM=="fusd", NAME="fusd/%k"
After updating, restart udevd (skill udevd; udevd -d).
After updating, restart udevd (skill udevd; udevd -d).
4- Insert the FUSD kernel module (modprobe kfusd)
4. Insert the FUSD kernel module (`modprobe kfusd`)
5- Verify the fusd devices /dev/fusd/status and /dev/fusd/control
exist. If the modprobe succeeds but no fusd devices appear,
5. Verify the fusd devices /dev/fusd/status and /dev/fusd/control
exist. If the modprobe succeeds but no fusd devices appear,
doublecheck the udev rule config change and make sure udevd restarted
successfully. The kfusd kernel module must be inserted after udev has
successfully. The kfusd kernel module must be inserted after udev has
been correctly configured and restarted.
6- Try running the helloworld example program (examples/helloworld).
When helloworld is running, 'cat /dev/helloworld' should return
'Hello, world!'.
6. Try running the `helloworld` example program (examples/helloworld).
When helloworld is running, `cat /dev/helloworld` should return
`Hello, world!`.
7- For more information, read the User's Manual in the 'doc' directory.
7. For more information, read the User's Manual in the 'doc' directory.
WHAT IS FUSD?
=============
FUSD (pronounced "fused") is a Linux framework for proxying device
file callbacks into user-space, allowing device files to be
implemented by daemons instead of kernel code. Despite being
implemented by daemons instead of kernel code. Despite being
implemented in user-space, FUSD devices can look and act just like any
other file under /dev which is implemented by kernel callbacks.
A user-space device driver can do many of the things that kernel
drivers can't, such as perform a long-running computation, block while
waiting for an event, or read files from the file system. Unlike
waiting for an event, or read files from the file system. Unlike
kernel drivers, a user-space device driver can use other device
drivers--that is, access the network, talk to a serial port, get
interactive input from the user, pop up GUI windows, or read from
disks. User-space drivers implemented using FUSD can be much easier to
disks. User-space drivers implemented using FUSD can be much easier to
debug; it is impossible for them to crash the machine, are easily
traceable using tools such as gdb, and can be killed and restarted
without rebooting. FUSD drivers don't have to be in C--Perl, Python,
without rebooting. FUSD drivers don't have to be in C--Perl, Python,
or any other language that knows how to read from and write to a file
descriptor can work with FUSD. User-space drivers can be swapped out,
descriptor can work with FUSD. User-space drivers can be swapped out,
whereas kernel drivers lock physical memory.
FUSD drivers are conceptually similar to kernel drivers: a set of
callback functions called in response to system calls made on file
descriptors by user programs. FUSD's C library provides a device
descriptors by user programs. FUSD's C library provides a device
registration function, similar to the kernel's devfs_register_chrdev()
function, to create new devices. fusd_register() accepts the device
name and a structure full of pointers. Those pointers are callback
function, to create new devices. fusd_register() accepts the device
name and a structure full of pointers. Those pointers are callback
functions which are called in response to certain user system
calls--for example, when a process tries to open, close, read from, or
write to the device file. The callback functions should conform to
the standard definitions of POSIX system call behavior. In many ways,
write to the device file. The callback functions should conform to
the standard definitions of POSIX system call behavior. In many ways,
the user-space FUSD callback functions are identical to their kernel
counterparts.
The proxying of kernel system calls that makes this kind of program
possible is implemented by FUSD, using a combination of a kernel
module and cooperating user-space library. The kernel module
module and cooperating user-space library. The kernel module
implements a character device, /dev/fusd, which is used as a control
channel between the two. fusd_register() uses this channel to send a
channel between the two. fusd_register() uses this channel to send a
message to the FUSD kernel module, telling the name of the device the
user wants to register. The kernel module, in turn, registers that
device with the kernel proper using devfs. devfs and the kernel don't
user wants to register. The kernel module, in turn, registers that
device with the kernel proper using devfs. devfs and the kernel don't
know anything unusual is happening; it appears from their point of
view that the registered devices are simply being implemented by the
FUSD module.
@@ -107,22 +114,22 @@ FUSD module.
Later, when kernel makes a callback due to a system call (e.g. when
the character device file is opened or read), the FUSD kernel module's
callback blocks the calling process, marshals the arguments of the
callback into a message and sends it to user-space. Once there, the
callback into a message and sends it to user-space. Once there, the
library half of FUSD unmarshals it and calls whatever user-space
callback the FUSD driver passed to fusd_register(). When that
callback the FUSD driver passed to fusd_register(). When that
user-space callback returns a value, the process happens in reverse:
the return value and its side-effects are marshaled by the library
and sent to the kernel. The FUSD kernel module unmarshals this
and sent to the kernel. The FUSD kernel module unmarshals this
message, matches it up with a corresponding outstanding request, and
completes the system call. The calling process is completely unaware
completes the system call. The calling process is completely unaware
of this trickery; it simply enters the kernel once, blocks, unblocks,
and returns from the system call---just as it would for any other
blocking call.
One of the primary design goals of FUSD is stability. It should
One of the primary design goals of FUSD is stability. It should
not be possible for a FUSD driver to corrupt or crash the kernel,
either due to error or malice. Of course, a buggy driver itself may
corrupt itself (e.g., due to a buffer overrun). However, strict error
either due to error or malice. Of course, a buggy driver itself may
corrupt itself (e.g., due to a buffer overrun). However, strict error
checking is implemented at the user-kernel boundary which should
prevent drivers from corrupting the kernel or any other user-space
process---including the errant driver's own clients, and other FUSD
@@ -130,10 +137,14 @@ drivers.
For more information, please see the comprehensive documentation in
the 'doc' directory.
> Jeremy Elson <jelson@circlemud.org> <br>
> August 19, 2003 <br>
Jeremy Elson <jelson@circlemud.org>
August 19, 2003
> updated,<br>
> Monty <monty@xiph.org> <br>
> January 11, 2007 <br>
updated,
Monty <monty@xiph.org>
January 11, 2007
> Updated, <br>
> Godzil <godzil@godzil.net> <br>
> March 01, 2011 / January 18, 2012 (public release on github)

View File

@@ -66,6 +66,8 @@ __BEGIN_DECLS
#define FUSD_KOR_HACKED_VERSION
#ifndef __KERNEL__
struct fusd_file_info; /* forward decl */
typedef
@@ -105,6 +107,7 @@ struct fusd_file_info {
/* other info might be added later, e.g. state needed to complete
operations... */
pthread_mutex_t lock;
/* request message associated with this call */
int fd;
@@ -112,8 +115,8 @@ struct fusd_file_info {
} fusd_file_info_t;
#define FILE_LOCK(__f) pthread_mutex_lock(&__f->lock)
#define FILE_UNLOCK(__f) pthread_mutex_unlock(&__f->lock)
/*************************** Library Functions ****************************/
@@ -278,6 +281,8 @@ static inline int fusd_get_poll_diff_cached_state(struct fusd_file_info *file)
/* returns static string representing the flagset (e.g. RWE) */
char *fusd_unparse_flags(int flags);
#endif /* !__KERNEL__ */
#ifndef __KERNEL__
__END_DECLS
#endif

View File

@@ -44,6 +44,7 @@
# define __KFUSD_H__
# include "fusd_msg.h"
# include <linux/version.h>
/* magic numbers for structure checking; unique w.r.t
* /usr/src/linux/Documentation/magic-number.txt */
@@ -125,8 +126,11 @@ struct fusd_dev_t_s {
char *dev_name;
struct CLASS *clazz;
int owns_class;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
struct class_device *device;
#else
struct device *device;
#endif
void *private_data; /* User's private data */
struct cdev* handle;
dev_t dev_id;
@@ -269,8 +273,11 @@ static void fusd_vfree(void *ptr);
/* Functions like this should be in the kernel, but they are not. Sigh. */
# ifdef CONFIG_SMP
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
DECLARE_MUTEX(atomic_ops);
#else
DEFINE_SEMAPHORE(atomic_ops);
#endif
static __inline__ int atomic_inc_and_ret(int *i)
{
int val;

View File

@@ -38,7 +38,7 @@
* Copyright (c) 2001, Sensoria Corporation
* Copyright (c) 2002-2003, Regents of the University of California
* Copyright (c) 2007 Monty and Xiph.Org
* Copyright (c) 2009-2011 Manoel Trapier <godzil@godzil.net>
* Copyright (c) 2009-2012 Manoel Trapier <godzil@godzil.net>
*
* $Id: kfusd.c 12354 2007-01-19 17:26:14Z xiphmont $
*/
@@ -114,37 +114,22 @@
# define CLASS_DEVICE_DESTROY(a, b) class_simple_device_remove(b)
#else
# define CLASS class
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
# define CLASS_DEVICE_CREATE(a, b, c, d, e) device_create(a, c, d, e)
# define CLASS_DEVICE_CREATE(a, b, c, d, e) class_device_create(a, c, d, e)
# else
# if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)
# define CLASS_DEVICE_CREATE(a, b, c, d, e) device_create(a, b, c, d, e)
# else
# define CLASS_DEVICE_CREATE(a, b, c, d, e) device_create(a, b, c, d, e)
# endif
# endif
#endif
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)
# define CLASS_DEVICE_DESTROY(a, b) device_destroy(a, b)
#else
# define CLASS_DEVICE_DESTROY(a, b) device_destroy(a, b)
#endif
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19)
@@ -209,6 +194,11 @@ struct class_private {
#endif
/*
struct sysfs_dirent *attr_sd = dentry->d_fsdata;
struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
*/
static inline struct kobject * to_kobj (struct dentry * dentry)
{
struct sysfs_dirent * sd = dentry->d_fsdata;
@@ -216,7 +206,7 @@ static inline struct kobject * to_kobj (struct dentry * dentry)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
return ((struct kobject *) sd->s_element );
#else
return ((struct kobject *) sd->s_dir.kobj );
return ((struct kobject *) sd->s_parent->s_dir.kobj );
#endif
else
return NULL;
@@ -242,8 +232,14 @@ STATIC dev_t status_id;
static struct CLASS *fusd_class;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
static struct class_device *fusd_control_device;
static struct class_device *fusd_status_device;
#else
static struct device *fusd_control_device;
static struct device *fusd_status_device;
#endif
extern struct CLASS *sound_class;
@@ -258,11 +254,16 @@ STATIC DECLARE_WAIT_QUEUE_HEAD (new_device_wait);
/* the list of valid devices, and sem to protect it */
LIST_HEAD (fusd_devlist_head);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
DECLARE_MUTEX (fusd_devlist_sem);
#else
DEFINE_SEMAPHORE (fusd_devlist_sem);
#endif
//#ifdef MODULE_LICENSE
MODULE_AUTHOR ("Jeremy Elson <jelson@acm.org> (c)2001");
MODULE_AUTHOR ("Manoel Trapier <godzil@godzil.net> (c)2009-2011");
MODULE_AUTHOR ("Manoel Trapier <godzil@godzil.net> (c)2009-2012");
MODULE_LICENSE ("GPL");
//#endif
@@ -320,7 +321,12 @@ STATIC void rdebug_real (char *fmt, ...)
# define MAX_MEM_DEBUG 10000
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
DECLARE_MUTEX (fusd_memdebug_sem);
#else
DEFINE_SEMAPHORE (fusd_memdebug_sem);
#endif
typedef struct
{
@@ -469,6 +475,7 @@ STATIC inline void free_fusd_msg (fusd_msg_t **fusd_msg)
VFREE(( *fusd_msg )->data);
( *fusd_msg )->data = NULL;
}
RDEBUG(1, "Freeing fusd_msg [%p] then set to NULL", fusd_msg);
KFREE(*fusd_msg);
*fusd_msg = NULL;
}
@@ -840,8 +847,10 @@ STATIC int fusd_fops_call_send (fusd_file_t *fusd_file_arg,
/* fill the rest of the structure */
fusd_msg->parm.fops_msg.pid = current->pid;
// fusd_msg->parm.fops_msg.uid = current_uid();
// fusd_msg->parm.fops_msg.gid = current_gid();
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
fusd_msg->parm.fops_msg.uid = current_uid();
fusd_msg->parm.fops_msg.gid = current_gid();
#endif
fusd_msg->parm.fops_msg.flags = fusd_file->file->f_flags;
fusd_msg->parm.fops_msg.offset = fusd_file->file->f_pos;
fusd_msg->parm.fops_msg.device_info = fusd_dev->private_data;
@@ -1140,8 +1149,13 @@ int fusd_dev_add_file (struct file *file, fusd_dev_t *fusd_dev, fusd_file_t **fu
init_waitqueue_head(&fusd_file->file_wait);
init_waitqueue_head(&fusd_file->poll_wait);
INIT_LIST_HEAD(&fusd_file->transactions);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
init_MUTEX(&fusd_file->file_sem);
init_MUTEX(&fusd_file->transactions_sem);
#else
sema_init(&fusd_file->file_sem, 1);
sema_init(&fusd_file->transactions_sem, 1);
#endif
fusd_file->last_poll_sent = -1;
fusd_file->magic = FUSD_FILE_MAGIC;
fusd_file->fusd_dev = fusd_dev;
@@ -1589,8 +1603,13 @@ invalid_file:
return -EPIPE;
}
#ifndef HAVE_UNLOCKED_IOCTL
STATIC int fusd_client_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
#else
STATIC long fusd_client_unlocked_ioctl (struct file *file,
unsigned int cmd, unsigned long arg)
#endif
{
fusd_dev_t *fusd_dev;
fusd_file_t *fusd_file;
@@ -1937,8 +1956,7 @@ invalid_dev:
return POLLPRI;
}
#ifndef HAVE_UNLOCKED_IOCTL
STATIC struct file_operations fusd_client_fops = {
.owner = THIS_MODULE,
.open = fusd_client_open,
@@ -1949,7 +1967,18 @@ STATIC struct file_operations fusd_client_fops = {
.poll = fusd_client_poll,
.mmap = fusd_client_mmap
};
#else
STATIC struct file_operations fusd_client_fops = {
.owner = THIS_MODULE,
.open = fusd_client_open,
.release = fusd_client_release,
.read = fusd_client_read,
.write = fusd_client_write,
.unlocked_ioctl = fusd_client_unlocked_ioctl,
.poll = fusd_client_poll,
.mmap = fusd_client_mmap
};
#endif
/*************************************************************************/
/*************************************************************************/
@@ -2177,7 +2206,7 @@ STATIC int fusd_register_device (fusd_dev_t *fusd_dev,
if ( sysfs )
{
/* Get FS superblock */
/* Get FS superblock */
sb = sget(sysfs, systest, NULL, NULL);
/* because put_filesystem isn't exported */
@@ -2192,7 +2221,7 @@ STATIC int fusd_register_device (fusd_dev_t *fusd_dev,
{
struct qstr name;
/* Search for directory "class" in the root of this filesystem */
/* Search for directory "class" in the root of this filesystem */
name.name = "class";
name.len = 5;
name.hash = full_name_hash(name.name, name.len);
@@ -2200,7 +2229,7 @@ STATIC int fusd_register_device (fusd_dev_t *fusd_dev,
if ( classdir )
{
/* Found, now search for class wanted name */
/* Found, now search for class wanted name */
name.name = register_msg.clazz;
name.len = strlen(name.name);
name.hash = full_name_hash(name.name, name.len);
@@ -2212,15 +2241,16 @@ STATIC int fusd_register_device (fusd_dev_t *fusd_dev,
struct kobject *ko = to_kobj(classdir2);
sys_class = ( ko ? to_class(ko)->class : NULL );
if ( sys_class )
{
#if 0
if ( sys_class )
{
/* W T F ???? Using an existing sys_class will led to a NULL pointer crash
* during device creation.. Need more investigation, this comportement is clearly not
* normal. */
RDEBUG(1, "ERROR: Using existing class name is currently unsported !!!");
goto register_failed4;
}
RDEBUG(1, "ERROR: Using existing class name is currently unsported !!!");
goto register_failed4;
}
#endif
if ( !sys_class )
RDEBUG(2, "WARNING: sysfs entry for %s has no kobject!\n", register_msg.clazz);
}
@@ -2351,7 +2381,11 @@ STATIC int fusd_open (struct inode *inode, struct file *file)
goto file_malloc_failed;
init_waitqueue_head(&fusd_dev->dev_wait);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
init_MUTEX(&fusd_dev->dev_sem);
#else
sema_init(&fusd_dev->dev_sem, 1);
#endif
fusd_dev->magic = FUSD_DEV_MAGIC;
fusd_dev->pid = current->pid;
fusd_dev->task = current;
@@ -2609,7 +2643,7 @@ STATIC ssize_t fusd_write (struct file *file,
RDEBUG(1, "%s: [%p:%p:%d:%p] [sl: %d]!!", __func__, file, buffer, length, offset, sizeof (fusd_msg_t ));
return fusd_process_write(file, buffer, length, NULL, 0);
}
#ifndef HAVE_UNLOCKED_IOCTL
STATIC ssize_t fusd_writev (struct file *file,
const struct iovec *iov,
unsigned long count,
@@ -2625,11 +2659,34 @@ STATIC ssize_t fusd_writev (struct file *file,
iov[0].iov_base, iov[0].iov_len,
iov[1].iov_base, iov[1].iov_len);
}
#else
STATIC ssize_t fusd_aio_write (struct kiocb *iocb,
const struct iovec *iov,
unsigned long count,
loff_t offset)
{
if ( count != 2 )
{
RDEBUG(2, "fusd_writev: got illegal iov count of %ld", count);
return -EINVAL;
}
return fusd_process_write(iocb->ki_filp,
iov[0].iov_base, iov[0].iov_len,
iov[1].iov_base, iov[1].iov_len);
}
#endif
#ifndef HAVE_UNLOCKED_IOCTL
STATIC int fusd_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
#else
STATIC long fusd_unlocked_ioctl (struct file *file,
unsigned int cmd, unsigned long arg)
#endif
{
void __user *argp = (void __user *) arg;
#if 0
struct iovec iov;
if ( ( argp != NULL ) && ( cmd == 0xb16b00b5 ) )
@@ -2646,6 +2703,7 @@ STATIC int fusd_ioctl (struct inode *inode, struct file *file,
return -EIO;
}
}
#endif
RDEBUG(2, "%s: got illegal ioctl #%08X# Or ARG is null [%p]", __func__, cmd, argp);
return -EINVAL;
}
@@ -2839,7 +2897,7 @@ invalid_dev:
return 0;
}
#ifndef HAVE_UNLOCKED_IOCTL
STATIC struct file_operations fusd_fops = {
.owner = THIS_MODULE,
.open = fusd_open,
@@ -2850,6 +2908,19 @@ STATIC struct file_operations fusd_fops = {
.release = fusd_release,
.poll = fusd_poll,
};
#else
STATIC struct file_operations fusd_fops = {
.owner = THIS_MODULE,
.open = fusd_open,
.read = fusd_read,
.write = fusd_write,
.aio_write = fusd_aio_write,
.unlocked_ioctl = fusd_unlocked_ioctl,
.release = fusd_release,
.poll = fusd_poll,
};
#endif
/*************************************************************************/
@@ -2904,8 +2975,13 @@ STATIC int fusd_status_release (struct inode *inode, struct file *file)
}
/* ioctl() on /dev/fusd/status */
#ifndef HAVE_UNLOCKED_IOCTL
STATIC int fusd_status_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
#else
STATIC long fusd_status_unlocked_ioctl (struct file *file,
unsigned int cmd, unsigned long arg)
#endif
{
fusd_statcontext_t *fs = (fusd_statcontext_t *) file->private_data;
@@ -3123,7 +3199,7 @@ STATIC unsigned int fusd_status_poll (struct file *file, poll_table *wait)
return 0;
}
#ifndef HAVE_UNLOCKED_IOCTL
STATIC struct file_operations fusd_status_fops = {
.owner = THIS_MODULE,
.open = fusd_status_open,
@@ -3132,6 +3208,16 @@ STATIC struct file_operations fusd_status_fops = {
.release = fusd_status_release,
.poll = fusd_status_poll,
};
#else
STATIC struct file_operations fusd_status_fops = {
.owner = THIS_MODULE,
.open = fusd_status_open,
.unlocked_ioctl = fusd_status_unlocked_ioctl,
.read = fusd_status_read,
.release = fusd_status_release,
.poll = fusd_status_poll,
};
#endif
/*************************************************************************/

View File

@@ -27,7 +27,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* fusd userspace library: functions that know how to properly talk
@@ -53,6 +53,11 @@ char libfusd_c_id[] = "$Id: libfusd.c 12351 2007-01-19 07:22:54Z xiphmont $";
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#include "fusd.h"
#include "fusd_msg.h"
@@ -72,7 +77,7 @@ char *dev_root = NULL;
* struct for each fusd fd.
*/
static fusd_file_operations_t fusd_fops_set[FD_SETSIZE];
fusd_file_operations_t null_fops = { NULL };
fusd_file_operations_t null_fops = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
/*
* accessor macros
@@ -99,7 +104,8 @@ void fusd_init()
{
static int fusd_init_needed = 1;
if (fusd_init_needed) {
if (fusd_init_needed)
{
int i;
fusd_init_needed = 0;
@@ -123,7 +129,8 @@ int fusd_register(const char *name, const char* clazz, const char* devname, mode
fusd_init();
/* make sure the name is valid and we have a valid set of fops... */
if (name == NULL || fops == NULL) {
if (name == NULL || fops == NULL)
{
fprintf(stderr, "fusd_register: invalid name or fops argument\n");
retval = -EINVAL;
goto done;
@@ -134,27 +141,33 @@ int fusd_register(const char *name, const char* clazz, const char* devname, mode
* to register are SKIP_PREFIX (usually "/dev/"), skip over them.
*/
if (dev_root != NULL && strlen(name) > strlen(dev_root) &&
!strncmp(name, dev_root, strlen(dev_root))) {
!strncmp(name, dev_root, strlen(dev_root)))
{
name += strlen(dev_root);
}
if (strlen(name) > FUSD_MAX_NAME_LENGTH) {
fprintf(stderr, "name '%s' too long, sorry :(", name);
if (strlen(name) > FUSD_MAX_NAME_LENGTH)
{
fprintf(stderr, "libfusd: name '%s' too long, sorry :(\n", name);
retval = -EINVAL;
goto done;
}
/* open the fusd control channel */
if ((fd = open(FUSD_CONTROL_DEVNAME, O_RDWR | O_NONBLOCK)) < 0) {
if ((fd = open(FUSD_CONTROL_DEVNAME, O_RDWR | O_NONBLOCK)) < 0)
{
/* if the problem is that /dev/fusd does not exist, return the
* message "Package not installed", which is hopefully more
* illuminating than "no such file or directory" */
if (errno == ENOENT) {
if (errno == ENOENT)
{
fprintf(stderr, "libfusd: %s does not exist; ensure FUSD's kernel module is installed\n",
FUSD_CONTROL_DEVNAME);
retval = -ENOPKG;
} else {
}
else
{
perror("libfusd: trying to open FUSD control channel");
retval = -errno;
}
@@ -162,7 +175,8 @@ int fusd_register(const char *name, const char* clazz, const char* devname, mode
}
/* fd in use? */
if (FUSD_FD_VALID(fd)) {
if (FUSD_FD_VALID(fd))
{
retval = -EBADF;
goto done;
}
@@ -179,7 +193,8 @@ int fusd_register(const char *name, const char* clazz, const char* devname, mode
message.parm.register_msg.device_info = device_info;
/* make the request */
if (write(fd, &message, sizeof(fusd_msg_t)) < 0) {
if (write(fd, &message, sizeof(fusd_msg_t)) < 0)
{
retval = -errno;
goto done;
}
@@ -190,34 +205,38 @@ int fusd_register(const char *name, const char* clazz, const char* devname, mode
/* success! */
done:
if (retval < 0) {
if (retval < 0)
{
if (fd >= 0)
close(fd);
errno = -retval;
retval = -1;
} else {
}
else
{
errno = 0;
retval = fd;
}
return retval;
}
int fusd_unregister(int fd)
{
if (FUSD_FD_VALID(fd)) {
int ret = -1;
if (FUSD_FD_VALID(fd))
{
/* clear fd location */
FUSD_SET_FOPS(fd, &null_fops);
FD_CLR(fd, &fusd_fds);
/* close */
return close(fd);
ret = close(fd);
}
else {
else
{
errno = EBADF;
return -1;
}
return ret;
}
@@ -236,21 +255,25 @@ void fusd_run(void)
int i;
/* locate maxmimum fd in use */
for (maxfd=0, i=0; i < FD_SETSIZE; i++) {
if (FD_ISSET(i, &fusd_fds)) {
for (maxfd=0, i=0; i < FD_SETSIZE; i++)
{
if (FD_ISSET(i, &fusd_fds))
{
maxfd = i;
}
}
maxfd++;
while (1) {
while (1)
{
/* select */
memmove(&tfds, &fusd_fds, sizeof(fd_set));
status = select(maxfd, &tfds, NULL, NULL, NULL);
/* error? */
if (status < 0) {
if (status < 0)
{
perror("libfusd: fusd_run: error on select");
continue;
}
@@ -258,14 +281,13 @@ void fusd_run(void)
/* readable? */
for (i = 0; i < maxfd; i++)
if (FD_ISSET(i, &tfds))
fusd_dispatch(i);
fusd_dispatch(i);
}
}
/************************************************************************/
/* reads a fusd kernel-to-userspace message from fd, and puts a
* fusd_msg into the memory pointed to by msg (we assume we are passed
* a buffer managed by the caller). if there is a data portion to the
@@ -274,39 +296,51 @@ void fusd_run(void)
* managed by the caller. */
static int fusd_get_message(int fd, fusd_msg_t *msg)
{
int ret;
/* read the header part into the kernel */
if (read(fd, msg, sizeof(fusd_msg_t)) < 0) {
if (read(fd, msg, sizeof(fusd_msg_t)) < 0)
{
if (errno != EAGAIN)
perror("error talking to FUSD control channel on header read");
return -errno;
ret = -errno;
goto exit;
}
msg->data = NULL; /* pointers in kernelspace have no meaning */
if (msg->magic != FUSD_MSG_MAGIC) {
fprintf(stderr, "libfusd magic number failure\n");
return -EINVAL;
if (msg->magic != FUSD_MSG_MAGIC)
{
fprintf(stderr, "libfusd: magic number failure\n");
ret = -EINVAL;
goto exit;
}
/* if there's a data part to the message, read it from the kernel. */
if (msg->datalen) {
if ((msg->data = malloc(msg->datalen + 1)) == NULL) {
if (msg->datalen)
{
if ((msg->data = malloc(msg->datalen + 1)) == NULL)
{
fprintf(stderr, "libfusd: can't allocate memory\n");
return -ENOMEM; /* this is bad, we are now unsynced */
ret = -ENOMEM; /* this is bad, we are now unsynced */
goto exit;
}
if (read(fd, msg->data, msg->datalen) < 0) {
if (read(fd, msg->data, msg->datalen) < 0)
{
perror("error talking to FUSD control channel on data read");
free(msg->data);
msg->data = NULL;
return -EIO;
ret = -EIO;
goto exit;
}
/* For convenience, we now ensure that the byte *after* the buffer
* is set to 0. (Note we malloc'd one extra byte above.) */
msg->data[msg->datalen] = '\0';
}
return 0;
ret = 0;
exit:
return ret;
}
@@ -318,11 +352,14 @@ void fusd_fdset_add(fd_set *set, int *max)
{
int i;
for (i = 0; i < FD_SETSIZE; i++) {
if (FD_ISSET(i, &fusd_fds)) {
for (i = 0; i < FD_SETSIZE; i++)
{
if (FD_ISSET(i, &fusd_fds))
{
FD_SET(i, set);
if (i > *max) {
*max = i;
if (i > *max)
{
*max = i;
}
}
}
@@ -340,6 +377,7 @@ void fusd_dispatch_fdset(fd_set *set)
for (i = 0; i < FD_SETSIZE; i++)
if (FD_ISSET(i, set) && FD_ISSET(i, &fusd_fds))
fusd_dispatch(i);
}
@@ -361,14 +399,16 @@ static int fusd_dispatch_one(int fd, fusd_file_operations_t *fops)
int user_retval = 0; /* returned to the user who made the syscall */
/* check for valid, look up ops */
if (fops == NULL) {
if (fops == NULL)
{
fprintf(stderr, "fusd_dispatch: no fops provided!\n");
driver_retval = -EBADF;
goto out_noreply;
}
/* allocate memory for fusd_msg_t */
if ((msg = malloc(sizeof(fusd_msg_t))) == NULL) {
if ((msg = malloc(sizeof(fusd_msg_t))) == NULL)
{
driver_retval = -ENOMEM;
fprintf(stderr, "libfusd: can't allocate memory\n");
goto out_noreply;
@@ -381,7 +421,8 @@ static int fusd_dispatch_one(int fd, fusd_file_operations_t *fops)
/* allocate file info struct */
file = malloc(sizeof(fusd_file_info_t));
if (NULL == file) {
if (NULL == file)
{
fprintf(stderr, "libfusd: can't allocate memory\n");
driver_retval = -ENOMEM;
goto out_noreply;
@@ -389,6 +430,11 @@ static int fusd_dispatch_one(int fd, fusd_file_operations_t *fops)
/* fill the file info struct */
memset(file, '\0', sizeof(fusd_file_info_t));
pthread_mutex_init(&file->lock, NULL);
FILE_LOCK(file);
file->fd = fd;
file->device_info = msg->parm.fops_msg.device_info;
file->private_data = msg->parm.fops_msg.private_info;
@@ -398,49 +444,62 @@ static int fusd_dispatch_one(int fd, fusd_file_operations_t *fops)
file->gid = msg->parm.fops_msg.gid;
file->fusd_msg = msg;
FILE_UNLOCK(file);
/* right now we only handle fops requests */
if (msg->cmd != FUSD_FOPS_CALL && msg->cmd != FUSD_FOPS_NONBLOCK &&
msg->cmd != FUSD_FOPS_CALL_DROPREPLY) {
msg->cmd != FUSD_FOPS_CALL_DROPREPLY)
{
fprintf(stderr, "libfusd: got unknown msg->cmd from kernel\n");
user_retval = -EINVAL;
goto send_reply;
}
/* dispatch on operation type */
user_retval = -ENOSYS;
//printf("dispatch_one: subcmd: %d - ", msg->subcmd);
switch (msg->subcmd) {
switch (msg->subcmd)
{
case FUSD_OPEN:
//printf("FUSD_OPEN\n");
if (fops && fops->open)
user_retval = fops->open(file);
break;
case FUSD_CLOSE:
//printf("FUSD_CLOSE\n");
if (fops && fops->close)
user_retval = fops->close(file);
break;
case FUSD_READ:
//printf("FUSD_READ\n");
/* allocate a buffer and make the call */
if (fops && fops->read) {
if ((msg->data = malloc(msg->parm.fops_msg.length)) == NULL) {
user_retval = -ENOMEM;
fprintf(stderr, "libfusd: can't allocate memory\n");
} else {
msg->datalen = msg->parm.fops_msg.length;
user_retval = fops->read(file, msg->data, msg->datalen,
&msg->parm.fops_msg.offset);
if (fops && fops->read)
{
if ((msg->data = malloc(msg->parm.fops_msg.length)) == NULL)
{
user_retval = -ENOMEM;
fprintf(stderr, "libfusd: can't allocate memory\n");
}
else
{
msg->datalen = msg->parm.fops_msg.length;
user_retval = fops->read(file, msg->data, msg->datalen,
&msg->parm.fops_msg.offset);
}
}
break;
case FUSD_WRITE:
//printf("FUSD_WRITE\n");
if (fops && fops->write)
user_retval = fops->write(file, msg->data, msg->datalen,
&msg->parm.fops_msg.offset);
break;
case FUSD_MMAP:
//printf("FUSD_MMAP\n");
if (fops && fops->mmap)
@@ -449,54 +508,58 @@ static int fusd_dispatch_one(int fd, fusd_file_operations_t *fops)
&msg->parm.fops_msg.arg.ptr_arg, &msg->parm.fops_msg.length);
}
break;
case FUSD_IOCTL:
//printf("FUSD_IOCTL\n");
if (fops && fops->ioctl) {
if (fops && fops->ioctl)
{
/* in the case of an ioctl read, allocate a buffer for the
* driver to write to, IF there isn't already a buffer. (there
* might already be a buffer if this is a read+write) */
if ((_IOC_DIR(msg->parm.fops_msg.cmd) & _IOC_READ) &&
msg->data == NULL) {
msg->datalen = _IOC_SIZE(msg->parm.fops_msg.cmd);
if ((msg->data = malloc(msg->datalen)) == NULL) {
user_retval = -ENOMEM;
break;
}
msg->data == NULL)
{
msg->datalen = _IOC_SIZE(msg->parm.fops_msg.cmd);
if ((msg->data = malloc(msg->datalen)) == NULL)
{
user_retval = -ENOMEM;
break;
}
}
if (msg->data != NULL)
user_retval = fops->ioctl(file, msg->parm.fops_msg.cmd, msg->data);
user_retval = fops->ioctl(file, msg->parm.fops_msg.cmd, msg->data);
else
user_retval = fops->ioctl(file, msg->parm.fops_msg.cmd,
(void *) msg->parm.fops_msg.arg.ptr_arg);
user_retval = fops->ioctl(file, msg->parm.fops_msg.cmd,
(void *) msg->parm.fops_msg.arg.ptr_arg);
}
break;
case FUSD_POLL_DIFF:
//printf("FUSD_POLL_DIFF\n");
/* This callback requests notification when an event occurs on a file,
* e.g. becoming readable or writable */
if (fops && fops->poll_diff)
user_retval = fops->poll_diff(file, msg->parm.fops_msg.cmd);
break;
break;
case FUSD_UNBLOCK:
//printf("FUSD_UNBLOCK\n");
/* This callback is called when a system call is interrupted */
if (fops && fops->unblock)
user_retval = fops->unblock(file);
user_retval = fops->unblock(file);
break;
default:
fprintf(stderr, "libfusd: Got unsupported operation\n");
user_retval = -ENOSYS;
break;
}
goto send_reply;
/* out_noreply is only used for handling errors */
out_noreply:
out_noreply:
if (msg->data != NULL)
free(msg->data);
if (msg != NULL)
@@ -504,21 +567,26 @@ static int fusd_dispatch_one(int fd, fusd_file_operations_t *fops)
goto done;
/* send_reply is only used for success */
send_reply:
if (-user_retval <= 0xff) {
send_reply:
if (-user_retval <= 0xff)
{
/* 0xff is the maximum legal return value (?) - return val to user */
driver_retval = fusd_return(file, user_retval);
} else {
}
else
{
/* if we got a FUSD_NOREPLY, don't free the msg structure */
driver_retval = 0;
}
/* this is common to both errors and success */
done:
if (driver_retval < 0) {
done:
if (driver_retval < 0)
{
errno = -driver_retval;
driver_retval = -1;
}
return driver_retval;
}
@@ -538,7 +606,8 @@ void fusd_dispatch(int fd)
fusd_file_operations_t *fops = NULL;
/* make sure we have a valid FD, and get its fops structure */
if (!FUSD_FD_VALID(fd)) {
if (!FUSD_FD_VALID(fd))
{
errno = EBADF;
retval = -1;
fprintf(stderr, "libfusd: not a valid FUSD FD\n");
@@ -547,7 +616,8 @@ void fusd_dispatch(int fd)
fops = FUSD_GET_FOPS(fd);
/* now keep dispatching until a dispatch returns an error */
do {
do
{
retval = fusd_dispatch_one(fd, fops);
if (retval >= 0)
@@ -557,14 +627,16 @@ void fusd_dispatch(int fd)
/* if we've dispatched at least one message successfully, and then
* stopped because of EAGAIN - do not report an error. this is the
* common case. */
if (num_dispatches > 0 && errno == EAGAIN) {
if (num_dispatches > 0 && errno == EAGAIN)
{
retval = 0;
errno = 0;
}
out:
if (retval < 0 && errno != EPIPE)
fprintf(stderr, "libfusd: fusd_dispatch error on fd %d: [%d] %m \n", fd, retval);
fprintf(stderr, "libfusd: fusd_dispatch error on fd %d: [%d] %m\n", fd, retval);
}
@@ -578,13 +650,26 @@ void fusd_dispatch(int fd)
*/
void fusd_destroy(struct fusd_file_info *file)
{
if (file == NULL)
return;
if (file == NULL)
return;
if (file->fusd_msg->data != NULL)
free(file->fusd_msg->data);
free(file->fusd_msg);
free(file);
FILE_LOCK(file);
if (file->fusd_msg != NULL)
{
if (file->fusd_msg->data != NULL)
{
free(file->fusd_msg->data);
file->fusd_msg->data = NULL;
}
free(file->fusd_msg);
file->fusd_msg = NULL;
}
FILE_UNLOCK(file);
pthread_mutex_destroy(&file->lock);
free(file);
}
@@ -600,22 +685,31 @@ int fusd_return(fusd_file_info_t *file, ssize_t retval)
fusd_msg_t *msg = NULL;
int fd;
int driver_retval = 0;
int ret;
struct iovec iov[2];
if (file == NULL) {
if (file == NULL)
{
fprintf(stderr, "fusd_return: NULL file\n");
return -EINVAL;
ret = -EINVAL;
goto exit;
}
FILE_LOCK(file);
fd = file->fd;
if (!FUSD_FD_VALID(fd)) {
if (!FUSD_FD_VALID(fd))
{
fprintf(stderr, "fusd_return: badfd (fd %d)\n", fd);
return -EBADF;
ret = -EBADF;
goto exit_unlock;
}
if ((msg = file->fusd_msg) == NULL) {
if ((msg = file->fusd_msg) == NULL)
{
fprintf(stderr, "fusd_return: fusd_msg is gone\n");
return -EINVAL;
ret = -EINVAL;
goto exit_unlock;
}
/* if this was a "DONTREPLY" message, just free the struct */
@@ -623,24 +717,29 @@ int fusd_return(fusd_file_info_t *file, ssize_t retval)
goto free_memory;
/* do we copy data back to kernel? how much? */
switch(msg->subcmd) {
switch(msg->subcmd)
{
case FUSD_READ:
/* these operations can return data to userspace */
if (retval > 0) {
msg->datalen = MIN(retval, msg->parm.fops_msg.length);
if (retval > 0)
{
msg->datalen = MIN((int)retval, (int)msg->parm.fops_msg.length);
retval = msg->datalen;
} else {
}
else
{
msg->datalen = 0;
}
break;
case FUSD_IOCTL:
/* ioctl CAN (in read mode) return data to userspace */
if ((retval == 0) &&
(_IOC_DIR(msg->parm.fops_msg.cmd) & _IOC_READ))
if (/*(retval == 0) && */ (_IOC_DIR(msg->parm.fops_msg.cmd) & _IOC_READ) )
msg->datalen = _IOC_SIZE(msg->parm.fops_msg.cmd);
else
msg->datalen = 0;
break;
default:
/* open, close, write, etc. do not return data */
msg->datalen = 0;
@@ -656,29 +755,35 @@ int fusd_return(fusd_file_info_t *file, ssize_t retval)
/* pid is NOT copied back. */
/* send message to kernel */
if (msg->datalen && msg->data != NULL) {
if (msg->datalen && msg->data != NULL)
{
//printf("(msg->datalen [%d] && msg->data != NULL [%p]", msg->datalen, msg->data);
iov[0].iov_base = msg;
iov[0].iov_len = sizeof(fusd_msg_t);
iov[1].iov_base = msg->data;
iov[1].iov_len = msg->datalen;
#if 0
driver_retval = writev(fd, iov, 2);
#else
driver_retval = ioctl(fd, 0xb16b00b5, iov);
#endif
}
else {
else
{
driver_retval = write(fd, msg, sizeof(fusd_msg_t));
}
free_memory:
free_memory:
FILE_UNLOCK(file);
fusd_destroy(file);
ret = 0;
if (driver_retval < 0)
return -errno;
else
return 0;
ret = -errno;
goto exit;
exit_unlock:
FILE_UNLOCK(file);
exit:
return ret;
}
@@ -690,12 +795,12 @@ char *fusd_unparse_flags(int flags)
static char ringbuf[RING][5];
char *s = ringbuf[i];
i = (i + 1) % RING;
sprintf(s, "%c%c%c",
sprintf(s, "%c%c%c",
(flags & FUSD_NOTIFY_INPUT)?'R':'-',
(flags & FUSD_NOTIFY_OUTPUT)?'W':'-',
(flags & FUSD_NOTIFY_EXCEPT)?'E':'-');
return s;
}
#undef RING