使用 C 处理 POSIX 中的目录

working with directories in POSIX with C

我会继续说这是 Linux class 介绍的家庭作业。如果我自己没有进行广泛的尝试,我不会发布它,而且由于我本学期是一名远程学生,所以我无法到校园接受辅导。我需要一些帮助来找出问题所在。

本质上,作业要求我们编写一个程序,其基本功能与 POSIX 中的 pwd 命令相同,以显示当前目录的绝对路径。我们将与 main 一起使用三个函数。我们也不要使用 getcwd 命令。我将列出它们及其用途

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>

void inum_to_filename (int inode_arg, char *pathBuffer, int size_arg) {
    DIR *dir_ptr = opendir(".");
    struct dirent *dirent_ptr = readdir(dir_ptr);
    int counter = 0;

    while (counter != 1) {
        if (inode_arg == dirent_ptr->d_ino) {

            strcat(pathBuffer, "/");
            strcat(pathBuffer, dirent_ptr->d_name);

            counter = counter + 1;
            return;

        } else {
            dirent_ptr = readdir(dir_ptr);
        }
    }

    closedir(dir_ptr);
}

int filename_to_inum (char *src) {
    int res = 0;
    struct stat info;
    int result = stat(src, &info);

    if (result != 0) {
        fprintf(stderr, "Cannot stat ");
        perror(src);
        exit(EXIT_FAILURE);
    } else {
        res = info.st_ino;
    }

    return res;
} 

void display_path (int ino_src) {
    int bufSize = 4096;
    char pathBuffer[bufSize];
    int ino_prnt = filename_to_inum("..");

    if (ino_src == ino_prnt) {
        //print for test
        inum_to_filename(ino_src, pathBuffer, bufSize);
        printf("%s", pathBuffer);
        return;
    } else {
        //print for test
        chdir("..");
        inum_to_filename(ino_src, pathBuffer, bufSize);
        display_path(ino_prnt);
        printf("%s", pathBuffer);
    }
}

int main (int argc, char *argv[]) {
    int c_ino = filename_to_inum(".");
    display_path(c_ino);
    printf("\n");
}

截至目前,它正在显示“/./MyName”,其中 MyName 是我在服务器上的个人命名目录。这是我 运行 程序所在的目录。当使用 pwd I return “/home/MyName”。我不太确定我的下一步是什么来获得正确的绝对路径。

这是一个非常好的任务:)。 我阅读并尝试了您的代码,它几乎是正确的。有两个小问题导致了不正确的行为。

第一期

display_path 到达根文件夹时,您不需要调用 inum_to_filename 并打印文件夹的名称,因为您已经在上一次迭代中打印了路径的第一个文件夹。这可以防止您的代码在路径的开头显示“./”。 即if条件变为:

 if (ino_src == ino_prnt) {
        return;
    } else {
        chdir("..");
        inum_to_filename(ino_src, pathBuffer, bufSize);
        display_path(ino_prnt);
        printf("%s", pathBuffer);
    }

第二期:

您没有正确初始化保存目录名称的缓冲区。这会导致显示随机值。要解决此问题,您可以使用 memset 将缓冲区的初始值设置为零。

void inum_to_filename (int inode_arg, char *pathBuffer, int size_arg) {
    DIR *dir_ptr = opendir(".");
    struct dirent *dirent_ptr = readdir(dir_ptr);
    int counter = 0;

    memset(pathBuffer, 0, size_arg);

    while (counter != 1) {
     ...
    }

    closedir(dir_ptr);
}

完整代码工作:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>

void inum_to_filename (int inode_arg, char *pathBuffer, int size_arg) {
    DIR *dir_ptr = opendir(".");
    struct dirent *dirent_ptr = readdir(dir_ptr);
    int counter = 0;

    memset(pathBuffer, 0, size_arg);

    while (counter != 1) {
        if (inode_arg == dirent_ptr->d_ino) {

            strcat(pathBuffer, "/");
            strcat(pathBuffer, dirent_ptr->d_name);

            counter = counter + 1;
            return;

        } else {
            dirent_ptr = readdir(dir_ptr);
        }
    }

    closedir(dir_ptr);
}

int filename_to_inum (char *src) {
    int res = 0;
    struct stat info;
    int result = stat(src, &info);

    if (result != 0) {
        fprintf(stderr, "Cannot stat ");
        perror(src);
        exit(EXIT_FAILURE);
    } else {
        res = info.st_ino;
    }

    return res;
} 

/*
  - Create an array of characters to use as a buffer for the name of the directory.
  - Get the inode for the parent directory using filename_to_inode.
  - If the parent inode is equal to the current inode, we have reached root and can return.
  - Otherwise, change to the parent directory and use inum_to_filename to find the name for 
    the inode that was passed into the function. Use the buffer from step 1 to store it.
  - Recursively call display_path to display the absolute path.
*/

void display_path (int ino_src) {
    int bufSize = 4096;
    char pathBuffer[bufSize];
    int ino_prnt = filename_to_inum("..");

    if (ino_src == ino_prnt) {
        return;
    } else {
        chdir("..");
        inum_to_filename(ino_src, pathBuffer, bufSize);
        display_path(ino_prnt);
        printf("%s", pathBuffer);
    }
}

int main (int argc, char *argv[]) {
    int c_ino = filename_to_inum(".");
    display_path(c_ino);
    printf("\n");
}

输出:

ubuntu@ubuntu-VirtualBox:~/dev$ vi pwd.c 
ubuntu@ubuntu-VirtualBox:~/dev$ gcc pwd.c 
ubuntu@ubuntu-VirtualBox:~/dev$ ./a.out 
/home/ubuntu/dev
ubuntu@ubuntu-VirtualBox:~/dev$ pwd
/home/ubuntu/dev
ubuntu@ubuntu-VirtualBox:~/dev$ 

代码主要设置为按正确顺序一次打印一个名称,因此主要问题是使用 strcat() 而不是 strcpy()。此外,在开始时检测您何时位于根目录中也很重要;如果你不这样做,当当前目录是根目录时,你可以得到 /. 或类似的东西(取决于你如何协调打印)。

这个版本的代码有:

  • 压缩了 inum_to_filename() 中的循环,但也添加了错误报告。请记住,一个进程可以 运行 在一个它没有权限访问的目录中(它通常需要一个 setuid 程序——尽管权限可以在程序启动后更改)。在这种情况下,它可能无法打开 ..(或 .)。

  • 丢失变量count;它没有用处。使用分配和测试习惯用法允许代码包含对 readdir().

  • 的单个调用
  • 使用strcpy()代替strcat()

  • 使用类型ino_t存储inode号。使用 size_t 作为尺寸。

  • 减少filename_to_inum()中的中间变量个数。

  • 注意if (ino_src == ino_prnt)语句体中的代码是针对根目录的;在没有测试打印的情况下,它什么也做不了。

  • 注意else部分的打印是操作的主要部分,不仅仅是测试打印。

  • 错误检查chdir("..");

  • main() 中检测根目录。

  • 观察到这段代码不适合直接改写成函数,因为它在成功时将进程的当前目录更改为/

修改后的代码:

#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

static void inum_to_filename(ino_t inode_arg, char *pathBuffer, size_t size_arg)
{
    assert(size_arg > 0);
    DIR *dir_ptr = opendir(".");
    if (dir_ptr == 0)
    {
        fprintf(stderr, "Failed to open directory '.' (%d: %s)\n", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }
    struct dirent *dirent_ptr;

    while ((dirent_ptr = readdir(dir_ptr)) != 0)
    {
        if (inode_arg == dirent_ptr->d_ino)
        {
            if (strlen(dirent_ptr->d_name) >= size_arg)
            {
                fprintf(stderr, "File name %s too long (%zu vs %zu max)\n",
                        dirent_ptr->d_name, strlen(dirent_ptr->d_name), size_arg);
                exit(EXIT_FAILURE);
            }
            strcpy(pathBuffer, dirent_ptr->d_name);
            break;
        }
    }

    closedir(dir_ptr);
}

static ino_t filename_to_inum(char *src)
{
    struct stat info;

    if (stat(src, &info) != 0)
    {
        fprintf(stderr, "Cannot stat ");
        perror(src);
        exit(EXIT_FAILURE);
    }
    return info.st_ino;
}

static void display_path(ino_t ino_src)
{
    size_t bufSize = 4096;
    char pathBuffer[bufSize];
    ino_t ino_prnt = filename_to_inum("..");

    if (ino_src == ino_prnt)
    {
        // print for test
        inum_to_filename(ino_src, pathBuffer, bufSize);
        printf("%s", "(root): /\n");
    }
    else
    {
        // print for real
        if (chdir("..") != 0)
        {
            fprintf(stderr, "Failed to chdir to .. (%d: %s)\n",
                    errno, strerror(errno));
        }
        inum_to_filename(ino_src, pathBuffer, bufSize);
        display_path(ino_prnt);
        printf("/%s", pathBuffer);
    }
}

int main(void)
{
    ino_t c_ino = filename_to_inum(".");
    ino_t r_ino = filename_to_inum("/");
    if (r_ino == c_ino)
        putchar('/');
    else
        display_path(c_ino);
    printf("\n");
}

毫无疑问,还有其他方法可以解决这个问题。

警告:这让我在 /Volumes/CRUZER/Sub-Directory 中工作时感到有些悲伤,这是一个记忆棒。扫描 /Volumes 时找不到索引节点(1,这很奇怪),我也没有弄清楚为什么。我的一个程序——getpwd 实现——运行良好;另一个有不同的问题。我希望我能查个水落石出。使用 GCC 5.1.0.

在 Mac OS X 10.10.5 上进行测试