上个月给文件系统添加额外的属性设置接口,但却是完全脱离文件系统实现的,直接使用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只对文件生效,不能对目录进行操作。
怀疑是fuse对inode进行初始化时,目录的inode的i_fop结构中不含ioctl接口,读fuse内核模块的代码,验证了我的想法:
fuse_init_inode为fuse初始化inode的代码,在代码中,对于普通的文件调用了fuse_init_common,fuse_init_inode方法,最终普通文件的i_fop为fuse_file_operations;而对于目录文件,其i_fop为fuse_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, };
|
fuse的ChangeLog节选:(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.