【问题标题】:What is the return value of telldir() function?telldir() 函数的返回值是多少?
【发布时间】:2015-09-15 11:14:42
【问题描述】:

程序:

#include<stdio.h>
#include<dirent.h>
void main()
{
    int i=0;
    DIR *dir=opendir("dir");
    struct dirent *dent;
    while((dent=readdir(dir))!=NULL){
            printf("Filename: %s\t\t Location in Directory Stream: %ld\n",
                    dent->d_name,telldir(dir));
    }
}

输出:

$ ./a.out
Filename: b.txt              Location in Directory Stream: 32
Filename: a.txt              Location in Directory Stream: 64
Filename: d.c                Location in Directory Stream: 96
Filename: .                  Location in Directory Stream: 128
Filename: test               Location in Directory Stream: 160
Filename: ..                 Location in Directory Stream: 192
$

在上面的程序中,telldir() 函数的返回值是 32 的倍数。根据 telldir() 的手册页参考 是“返回目录流中的当前位置”。所以,我希望目录包含 5 个文件,所以第一次调用 telldir() 返回 1 和 在下一个调用它返回 2。但是这里的输出是 32 的倍数。为什么输出是这样的?而telldir()函数为什么会返回这样的值呢?

【问题讨论】:

  • 您可能需要阅读this manual page 中的备注部分。
  • 类似于手册页。此处未提及更多其他信息。
  • 来自linked manual page:“现代文件系统使用树或散列结构,而不是平面表来表示目录。在这样的文件系统上,由telldir()返回的值(并在内部由readdir( 3)) 是一个“cookie”,实现使用它来导出目录中的位置。应用程序应严格将此视为不透明值,对其内容不做任何假设。” telldir函数返回一个值,这个值是什么无关紧要,不要用特定的方式解释。

标签: c linux unix


【解决方案1】:

POSIX states 关于telldir() 返回的“位置”的唯一信息是:

seekdir() 函数应将 dirp 指定的目录流上的下一个 readdir() 操作的位置设置为 loc 指定的位置。 loc 的值应该是从先前对telldir() 的调用中返回的。执行telldir() 时,新位置恢复为与目录流关联的位置。

如果 loc 的值不是从先前的 telldir() 调用中获得的,或者如果在调用 telldir() 和调用 seekdir() 之间发生了对 rewinddir() 的调用,则后续调用的结果readdir() 未指定。

因此,telldir() 的返回值是实现定义的。 不是当前文件的编号。


如果您查看 glibc 实现,您会发现:

  • 对于some hoststelldir() 返回一个基于全局计数器的 cookie,该计数器在每次 telldir() 调用时递增(对于任何目录);
  • 对于other hosts,包括Linux,telldir() 返回一个“文件偏移”,is later passedlseek()

即使telldir() 返回“文件偏移量”,这个偏移量对于目录也可能有特殊含义,具体取决于文件系统实现。

例如,对于ext4,请参阅ext4_dir_llseek()

ext4_dir_llseek() 调用 generic_file_llseek_size 来处理 htree 目录,其中“偏移量”是文件名哈希值而不是字节偏移量。


在我的系统中,您的程序的示例输出是(对于ext4):

Filename: ..         Location in Directory Stream: 3540738803800888240
Filename: foo        Location in Directory Stream: 5674377099084065539
Filename: .          Location in Directory Stream: 9223372036854775807

【讨论】:

    【解决方案2】:

    telldir()函数返回目录中的位置,代码可能是这样的:

        long int
        telldir (DIR *dirp)
        {
          return dirp->filepos;
        }
    

    当你调用opendir()时,filepos为0,那么readdir()会调用gentdents(),并加上dirp-&gt;filepos。例如调用readdir(),然后调用dirp-&gt;filepos = dp-&gt;d_offdp-&gt;d_off 将由file-&gt;f_op-&gt;iterate() 设置。对于文件系统PFS(我自己设计的一个linux的文件系统驱动),iterate()就是pfs_readdir()

    static int
    pfs_readdir(struct file *file, struct dir_context *ctx)
    {
            int64_t dno;
            unsigned long off;
            struct buffer_head *bh;
            struct pfs_dir_entry *de;
            struct inode *inode = file_inode(file);
    
            if(ctx->pos == 0)
                    ctx->pos = PFS_DIRHASHSIZ * sizeof(int64_t) + sizeof(int64_t);
            for(off = ctx->pos & (PFS_BLOCKSIZ - 1); ctx->pos < inode->i_size; off = ctx->pos & (PFS_BLOCKSIZ - 1)){
                    if(!(dno = pfs_get_block_number(inode, pfs_block_number(ctx->pos), 0)))
                            goto skip;
                    if(!(bh = sb_bread(inode->i_sb, dno / PFS_STRS_PER_BLOCK))){
                            pr_err("pfs: device %s: %s: failed to read block %lld of dir %lld\n",
                                    inode->i_sb->s_id, "pfs_readdir", pfs_block_number(ctx->pos), PFS_I(inode)->i_ino);
                            goto skip;
                    }
                    do{
                            de = (struct pfs_dir_entry *)((char *)bh->b_data + off);
                            if(de->d_ino){
                                    if(!(dir_emit(ctx, pfs_get_de_name(de), de->d_len, (int32_t)le64_to_cpu(de->d_ino), DT_UNKNOWN))){
                                            brelse(bh);
                                            return 0;
                                    }
                            }
                            off += pfs_get_de_size(de);
                            ctx->pos += pfs_get_de_size(de);
                    }while(off < PFS_BLOCKSIZ && ctx->pos < inode->i_size);
                    brelse(bh);
                    continue;
    skip:
                    ctx->pos += PFS_BLOCKSIZ - off;
            }
            return 0;
    }
    

    dp-&gt;d_offdir_emit()设置,来源为:

    static inline bool dir_emit(struct dir_context *ctx, const char *name, int namelen, u64 ino, unsigned type)
    {
             return ctx->actor(ctx, name, namelen, ctx->pos, ino, type) == 0;
    }
    

    如你所见,ctx-&gt;actor 将调用filldir()filldir() will setdp - previousand call__put_user(ctx->pos, &dp->d_off), then thedp->d_offis set (as you see, thectx ->posis the offset of previous entry's end), andtelldir()will return the position of the last entry you got viareaddir()`.

    注意void main()不是个好办法,你可以用int main(int argc, char *argv[])。而是。

    【讨论】:

      猜你喜欢
      • 2016-10-28
      • 2012-02-02
      • 2017-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-24
      • 1970-01-01
      相关资源
      最近更新 更多