【问题标题】:Directory traversal with chdir() instead of with absolute paths使用 chdir() 而不是绝对路径进行目录遍历
【发布时间】:2023-03-31 20:50:02
【问题描述】:

在涵盖文件和目录的“Unix 环境中的高级编程”一书的第 4 章中,有一个代码示例旨在类似于 ftw 命令并遍历文件层次结构。它使用指向绝对文件路径的指针,以及带有回调的递归函数来遍历目录,在此过程中调用opendir()readdir()

有一个练习要求读者使用chdir() 和文件名而不是使用绝对路径来完成相同的任务并比较两个程序的时间。我使用chdir() 编写了一个程序,并没有注意到时间上的差异。这是预期的吗?我原以为对chdir() 的额外调用会增加一些开销。这可能是一个相对微不足道的电话吗?任何见解将不胜感激。

这是使用绝对路径的递归函数:

static int                  /* we return whatever func() returns */
dopath(Myfunc* func)
{
    struct stat     statbuf;
    struct dirent   *dirp;
    DIR             *dp;
    int             ret;
    char            *ptr;

    if (lstat(fullpath, &statbuf) < 0) /* stat error */
        return(func(fullpath, &statbuf, FTW_NS));
    if (S_ISDIR(statbuf.st_mode) == 0) /* not a directory */
        return(func(fullpath, &statbuf, FTW_F));

     /*
      * It's a directory. First call func() for the directory,
      * then process each filename in the directory.
      */
    if ((ret = func(fullpath, &statbuf, FTW_D)) != 0)
        return(ret);

    ptr = fullpath + strlen(fullpath);      /* point to end of fullpath */
    *ptr++ = '/';
    *ptr = 0;

     if ((dp = opendir(fullpath)) == NULL)     /* can't read directory */
         return(func(fullpath, &statbuf, FTW_DNR));

     while ((dirp = readdir(dp)) != NULL) {
         if (strcmp(dirp->d_name, ".") == 0 ||
             strcmp(dirp->d_name, "..") == 0)
                 continue;        /* ignore dot and dot-dot */

         strcpy(ptr, dirp->d_name);   /* append name after slash */

         if ((ret = dopath(func)) != 0)          /* recursive */
              break; /* time to leave */
     }
     ptr[-1] = 0;    /* erase everything from slash onwards */

     if (closedir(dp) < 0)
         err_ret("can't close directory %s", fullpath);

     return(ret);
}

以下是我所做更改的功能:

static int                  /* we return whatever func() returns */
dopath(Myfunc* func, char* path)
{
    struct stat     statbuf;
    struct dirent   *dirp;
    DIR             *dp;
    int             ret;

    if (lstat(path, &statbuf) < 0) /* stat error */
        return(func(path, &statbuf, FTW_NS));
    if (S_ISDIR(statbuf.st_mode) == 0) /* not a directory */
        return(func(path, &statbuf, FTW_F));

 /*
 * It's a directory. First call func() for the directory,
 * then process each filename in the directory.
 */
    if ((ret = func(path, &statbuf, FTW_D)) != 0)
        return(ret);

    if ( chdir(path) < 0 )
      return(func(path, &statbuf, FTW_DNR));

     if ((dp = opendir(".")) == NULL)     /* can't read directory */
         return(func(path, &statbuf, FTW_DNR));

     while ((dirp = readdir(dp)) != NULL) {
         if (strcmp(dirp->d_name, ".") == 0 ||
             strcmp(dirp->d_name, "..") == 0)
                 continue;        /* ignore dot and dot-dot */

         if ((ret = dopath(func, dirp->d_name)) != 0)          /* recursive */
              break; /* time to leave */
     }
     if ( chdir("..") < 0 )
       err_ret("can't go up directory");

     if (closedir(dp) < 0)
         err_ret("can't close directory %s", fullpath);

     return(ret);
}

【问题讨论】:

    标签: c unix directory chdir directory-walk


    【解决方案1】:

    我认为您不应该期望绝对路径版本和chdir() 版本之间存在显着的时间性能差异。相反,两个版本的优缺点如下:

    • 完整路径名版本可能无法遍历非常深的目录结构,因为完整路径名的长度最终会超过PATH_MAXchdir() 版本没有这个问题。
    • chdir() 版本操纵 pwd,如果可以避免它,这通常被认为是不好的做法:它不是线程安全的,最终用户可能希望它不会受到影响。例如,在命令行中给出并由程序的不同部分使用的文件名可能与用户认为的密码有关,当您更改密码时会中断。
    • 如果不特别注意并且在遍历时目录结构发生变化,则在备份到更高的目录 (chdir("..")) 时,chdir() 版本可能会失控。那么在这些情况下,完整路径名版本可能会以不同的方式中断......

    现代 POSIX 系统上可用的 openat() 系列函数提供了两全其美的优势。如果这些功能可用,openat()fdopendir()fstatat() 等一起实现了一个非常好的目录遍历实现。

    【讨论】:

    • 感谢您的洞察力和其他功能!
    猜你喜欢
    • 2014-09-23
    • 1970-01-01
    • 2016-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多