FUSE的ioctl接口

上个月给文件系统添加额外的属性设置接口,但却是完全脱离文件系统实现的,直接使用rpc连接元数据服务器来设置/获取属性,而没有通过文件系统的客户端。当时想着还有一种方法就是在fuse-2.8中支持了ioctl接口,可通过为分布式文件系统实现ioctl接口实现扩展属性的获取与设置。

 

使用ioctl首先要理解ioctl命令的封装格式,ioctl的命令对应一个32bits的整数,其格式如下:

2位,访问模式

14位,参数的大小

8位,魔数

8位序号

/usr/include/asm-generic/ioctl.h中提供了一系列操作ioctl命令的宏:

/* ioctl command encoding: 32 bits total, command in lower 16 bits,
 * size of the parameter structure in the lower 14 bits of the
 * upper 16 bits.
 * Encoding the size of the parameter structure in the ioctl request
 * is useful for catching programs compiled with old versions
 * and to avoid overwriting user space outside the user buffer area.
 * The highest 2 bits are reserved for indicating the ``access mode''.
 * NOTE: This limits the max parameter size to 16kB -1 !
 */


/*
 * The following is for compatibility across the various Linux
 * platforms. The generic ioctl numbering scheme doesn't really enforce
 * a type field. De facto, however, the top 8 bits of the lower 16
 * bits are indeed used as a type field, so we might just as well make
 * this explicit here. Please be sure to use the decoding macros
 * below from now on.
 */

#define _IOC_NRBITS    8
#define _IOC_TYPEBITS    8
#define _IOC_SIZEBITS    14
#define _IOC_DIRBITS    2

#define _IOC_NRMASK    ((1 << _IOC_NRBITS)-1)
#define _IOC_TYPEMASK    ((1 << _IOC_TYPEBITS)-1)
#define _IOC_SIZEMASK    ((1 << _IOC_SIZEBITS)-1)
#define _IOC_DIRMASK    ((1 << _IOC_DIRBITS)-1)

#define _IOC_NRSHIFT    0
#define _IOC_TYPESHIFT    (_IOC_NRSHIFT+_IOC_NRBITS)
#define _IOC_SIZESHIFT    (_IOC_TYPESHIFT+_IOC_TYPEBITS)
#define _IOC_DIRSHIFT    (_IOC_SIZESHIFT+_IOC_SIZEBITS)

/*
 * Direction bits.
 */

#define _IOC_NONE    0U
#define _IOC_WRITE    1U
#define _IOC_READ    2U

#define _IOC(dir,type,nr,size) \
    (((dir) << _IOC_DIRSHIFT) | \
     ((type) << _IOC_TYPESHIFT) | \
     ((nr) << _IOC_NRSHIFT) | \
     ((size) << _IOC_SIZESHIFT))

/* provoke compile error for invalid uses of size argument */
extern unsigned int __invalid_size_argument_for_IOC;
#define _IOC_TYPECHECK(t) \
    ((sizeof(t) == sizeof(t[1]) && \
     sizeof(t) < (1 << _IOC_SIZEBITS)) ? \
     sizeof(t) : __invalid_size_argument_for_IOC)

/* used to create numbers */
#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)    _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)    _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size)    _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOR_BAD(type,nr,size)    _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW_BAD(type,nr,size)    _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR_BAD(type,nr,size)    _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))

/* used to decode ioctl numbers.. */
#define _IOC_DIR(nr)        (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr)        (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr)        (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr)        (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

/* ...and for the drivers/sound files... */

#define IOC_IN        (_IOC_WRITE << _IOC_DIRSHIFT)
#define IOC_OUT        (_IOC_READ << _IOC_DIRSHIFT)
#define IOC_INOUT    ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
#define IOCSIZE_MASK    (_IOC_SIZEMASK << _IOC_SIZESHIFT)
#define IOCSIZE_SHIFT    (_IOC_SIZESHIFT)

 

fu***mp.c中的简单的实现了ioctl接口,我系统上安装的fuse-2.7,提示fuse_operations中没有ioctl成员,这是由于fuse-2.8才引入ioctl接口,于是重新安装fuse-2.8,编译OK,但不能正常工作。

 

由于从fuse-2.8开始,fuse包中不再附带内核模块,内核模块已经加入到内核源代码中,而我机器上之前安装的fuse中带有内核模块,故虽然fuse的用户编程库更新到2.8版本,但运行的内核模块仍不支持ioctl接口,重启进入2.6.30.5内核,同时发现fuse-2.8.0中带了ioctl接口使用的实例(fioc.h, fioc.c, fioclient.c),运行OK。在测试自己下的例子时,发现ioctl只对文件生效,不能对目录进行操作。

 

怀疑是fuseinode进行初始化时,目录的inodei_fop结构中不含ioctl接口,读fuse内核模块的代码,验证了我的想法:

 

fuse_init_inodefuse初始化inode的代码,在代码中,对于普通的文件调用了fuse_init_commonfuse_init_inode方法,最终普通文件的i_fopfuse_file_operations;而对于目录文件,其i_fopfuse_dir_operations,而该结构中的ioctl接口并没有被实现。

 

static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
{
    inode->i_mode = attr->mode & S_IFMT;
    inode->i_size = attr->size;
    if (S_ISREG(inode->i_mode)) {
        fuse_init_common(inode);
        fuse_init_file_inode(inode);
    } else if (S_ISDIR(inode->i_mode))
        fuse_init_dir(inode);
    else if (S_ISLNK(inode->i_mode))
        fuse_init_symlink(inode);
    else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
         S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
        fuse_init_common(inode);
        init_special_inode(inode, inode->i_mode,
                 new_decode_dev(attr->rdev));
    } else
        BUG();
}

void fuse_init_common(struct inode *inode)
{
    inode->i_op = &fuse_common_inode_operations;
}

void fuse_init_file_inode(struct inode *inode)
{
    inode->i_fop = &fuse_file_operations;
    inode->i_data.a_ops = &fuse_file_aops;
}

void fuse_init_dir(struct inode *inode)
{
    inode->i_op = &fuse_dir_inode_operations;
    inode->i_fop = &fuse_dir_operations;
}

static const struct file_operations fuse_dir_operations = {
    .llseek        = generic_file_llseek,
    .read        = generic_read_dir,
    .readdir    = fuse_readdir,
    .open        = fuse_dir_open,
    .release    = fuse_dir_release,
    .fsync        = fuse_dir_fsync,
};

static const struct file_operations fuse_file_operations = {
    .llseek        = fuse_file_llseek,
    .read        = do_sync_read,
    .aio_read    = fuse_file_aio_read,
    .write        = do_sync_write,
    .aio_write    = fuse_file_aio_write,
    .mmap        = fuse_file_mmap,
    .open        = fuse_open,
    .flush        = fuse_flush,
    .release    = fuse_release,
    .fsync        = fuse_fsync,
    .lock        = fuse_file_lock,
    .flock        = fuse_file_flock,
    .splice_read    = generic_file_splice_read,
    .unlocked_ioctl    = fuse_file_ioctl,
    .compat_ioctl    = fuse_file_compat_ioctl,
    .poll        = fuse_file_poll,
};


fuseChangeLog节选:(fuse-2.8.0/ChangeLog

2008-12-05  Miklos Szeredi <miklos@szeredi.hu>

* Implement ioctl support.  On high level interface only "restricted" ioctls are supported (which are defined with the _IO(), _IOR(), _IOW() or _IOWR() macros).  Unrestricted ioctls will only be allwed to CUSE (Character Device in Userspace) servers.  Patch by Tejun Heo

2008-06-16  Miklos Szeredi <miklos@szeredi.hu>

* Remove fuse kernel module sources.  Linux 2.6.27 will support NFS exporting.

(故要支持fuse-2.8的新特性,最好使用linux-2.6.27+的内核)

2008-05-09  Miklos Szeredi <miklos@szeredi.hu>

* Don't allow bigger than 4kB writes by default on 2.6.26 and later kernels, so that filesystems not expecting this are not broken on a kernel upgrade.  Provide a 'big_writes' mount option to enable this feature.  In future API revisions this may become the default.

posted @ 2013-04-19 14:07  ydzhang  阅读(1295)  评论(0编辑  收藏  举报