我正在使用保险丝实现虚拟文件系统,并且需要对readdir中的offset参数有一些了解。
以前我们忽略了偏移量,并在填充函数中传递了0,在这种情况下,内核应该注意。
我们的文件系统数据库正在存储:目录名,文件长度,索引节点号和父索引节点号。
我如何计算得到的偏移量?
然后是每个组件的偏移量,等于它们的大小,以其inode编号的增量形式排序?发生的情况是目录中有目录,在这种情况下,偏移量等于内部文件的总和吗?
Example: in case the dir listing is - a.txt b.txt c.txt
And inode number of a.txt=3, b.txt=5, c.txt=7
Offset of a.txt= directory offset
Offset of b.txt=dir offset + size of a.txt
Offset of c.txt=dir offset + size of b.txt
以上假设正确吗?
传递给填充函数的偏移量是目录中下一项的偏移量。您可以按任意顺序在目录中输入条目。如果您不想一次返回整个目录,则需要使用偏移量来确定要存储的内容。目录中项目的顺序取决于您,而名称或索引节点或其他任何顺序都无关紧要。
特别是在readdir调用中,传递了一个偏移量。您想开始使用此回调或更晚版本的条目调用填充函数。在最简单的情况下,每个条目的长度为24字节+ strlen(条目名称),四舍五入到最接近的8字节倍数。但是,如果不是这种情况,请参见http://sourceforge.net/projects/fuse/处的保险丝源代码。
我有一个简单的示例,在我的readdir函数中有一个循环(伪C代码):
int my_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi)
{
(a bunch of prep work has been omitted)
struct stat st;
int off, nextoff=0, lenentry, i;
char namebuf[(long enough for any one name)];
for (i=0; i<NumDirectoryEntries; i++)
{
(fill st with the stat information, including inode, etc.)
(fill namebuf with the name of the directory entry)
lenentry = ((24+strlen(namebuf)+7)&~7);
off = nextoff; /* offset of this entry */
nextoff += lenentry;
/* Skip this entry if we weren't asked for it */
if (off<offset)
continue;
/* Add this to our response until we are asked to stop */
if (filler(buf, namebuf, &st, nextoff))
break;
}
/* All done because we were asked to stop or because we finished */
return 0;
}
我在自己的代码中进行了测试(我以前从未使用过偏移量,并且可以正常工作。
其他答案中的方法显然有问题。填充函数似乎没有使用offset
值来填充其opaque buf。如果offset
值不能一次读取所有文件名,则对客户端代码有意义。上一次传递给填充符的值是在下次调用时传递回客户端的readdir()
回调的值。
[整个过程中最重要的信息似乎在'net上的任何来源都完全缺失:如何向Fuse系统发出信号,表明readdir()
回调已完成,列出了文件名的一部分。
下面的代码可能会为我们提供一些启示。在我有时间设置我的IDE以逐步执行fuselib代码之后,我可能会找到一种更好的方法。但是,这就是我发现的效果:
列出文件的各个部分
int readdir_callback(char *path, void *buf, fuse_fill_dir_t *filler, off_t off,
struct fuse_file_info *fi)
{
my_file_list *list = get_some_filenames(path, off);
if (list->length == 0)
{
// If get_some_filenames() returns an empty list, the files have all
// been listed. Let Fuse know we're done...
memset(list->filenames[0], 0, 8); // 8 is based on total guesswork.
// Just one null byte might be
// enough.
((struct fuse_dh *)buf)->filled = 1; // I shouldn't have to do this.
// Need to step through fuselib
// to see how this should get
// set.
filler(buf, list->filenames[0], NULL, 0);
}
else
{
for (int i = 0; i < list->length; i++)
{
filler(buf, list->filenames[i], NULL, off + i + 1);
}
}
return 0;
}
我不必担心buf
的内容,所以我知道有一种更好的方法来表示文件夹的文件名结尾。如果我知道了,我会回来再更新。
但是无论如何,以上内容确实有效地向Fuse表示上市已完成;而且它不需要任何神奇的数学来计算偏移值,也不需要任何关于inode的计算(我正在安装的文件系统甚至都无法实现)。
同时列出所有文件
int readdir_callback(char *path, void *buf, fuse_fill_dir_t *filler, off_t off,
struct fuse_file_info *fi)
{
my_file_list *list = get_all_filenames(path);
for (int i = 0; i < list->length; i++)
{
filler(buf, list->filenames[i], NULL, 0);
}
return 0;
}
一键列出所有文件要简单得多。请注意,这不需要了解buf
的结构,并且保险丝填充器内部知道如何填充它,而无需任何外部帮助(应该如此)。
您希望以其他方式列出文件的各个部分会有一些好处。例如,文件浏览器可能会在读取文件和文件夹之前就开始显示它们。但是我还没有看到这种情况。我有一个安装ftp URL的模块,并且两种方法都需要很长时间才能开始在Nemo文件浏览器中看到任何操作。
那么,为什么还要烦恼首先读取部分文件夹内容的方法?