lstat:无法访问另一个目录中的文件

lstat: can't access files in another directory

我正在尝试编写类似 ls 的程序来生成类似 ls -l 的输出,其中包含文件的权限、所有者、时间和名称。 如果我传递 .(或什么都不传递),效果很好,因此它适用于当前目录。但是,如果我将任何其他目录传入或传出当前目录,perror 表示 "can't access" 文件。

请帮我弄清楚是什么阻止了 lstat 访问另一个目录中的文件。

我使用 gcc 和一个文本编辑器,没有 IDE,开始学习使用 gdb(尝试调试但没有找到可以指出我应该寻找哪种解决方案的东西)。这就是我决定将所有代码放在这里的原因,这样任何人都可以 运行 它。也许我传递了错误的论据,也许这是某种 lstat 的错误行为,我不知道。我一直试图在网上找到一些关于它的东西,但没有结果。

这是我到目前为止所做的事情:

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>     // open()
#include <unistd.h>    // close()
#include <sys/param.h>
#include <string.h>
#include <limits.h>    // PATH_MAX
#include <pwd.h>       // structure for getpwuid()
#include <grp.h>       // structure for getgrgid()
#include <time.h>      // ctime()

static void ls(const char *dir);
void get_info(const char *name, int offset);
int get_maxsize(const char *name);


int main(int argc, char ** argv)
{
    if (argc == 1)
        ls(".");
    else
        while (--argc > 0)
            ls(*++argv);
    return 0;
}


static void ls(const char *dir)
{
    struct dirent * entry;
    DIR *d = opendir(dir);
    char pathbuf[PATH_MAX + 1];
    int offset = 0;

    if (d == 0) {
        perror("ls");
        return;
    }

    /* find max file size for better ls-alike output */
    while ((entry = readdir(d)) != NULL) {
        realpath(entry->d_name, pathbuf);
        /* pathbuf OR entry->d_name here: */
        if (get_maxsize(entry->d_name) > offset)
            offset = get_maxsize(pathbuf);
    }
    closedir(d);

    d = opendir(dir);
    while ((entry = readdir(d)) != NULL) {
        /* pathbuf OR entry->d_name here: */
        realpath(entry->d_name, pathbuf);
        get_info(entry->d_name, offset);
    }
    closedir(d);
}


void get_info(const char *name, int offset)
{
    struct stat statbuf;
    struct passwd *pwdPtr;
    struct group *grpPtr;
    int length = 0;
    char *time = NULL;

    /* skip . and .. dirs */
    if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
        return;

    if (lstat(name, &statbuf) == -1) {
        fprintf(stderr, "can't access %s\n", name);
        perror("get_info");
        return;
    } 
    else
        switch (statbuf.st_mode & S_IFMT) {
            case S_IFREG: printf("-"); break;
            case S_IFDIR: printf("d"); break;
            case S_IFCHR: printf("c"); break;
            case S_IFBLK: printf("b"); break;
            case S_IFLNK: printf("l"); break;
            case S_IFSOCK: printf("s"); break;
            case S_IFIFO: printf("p"); break;
        }

    /* owner */
    if (statbuf.st_mode & S_IREAD) printf("r");
    else printf("-");
    if (statbuf.st_mode & S_IWRITE) printf("w");
    else printf("-");
    if (statbuf.st_mode & S_IEXEC) printf("x");
    else printf("-");

    /* group */
    if (statbuf.st_mode & S_IRGRP) printf("r");
    else printf("-");
    if (statbuf.st_mode & S_IWGRP) printf("w");
    else printf("-");
    if (statbuf.st_mode & S_IXGRP) printf("x");
    else printf("-");

    /* other users */
    if (statbuf.st_mode & S_IROTH) printf("r");
    else printf("-");
    if (statbuf.st_mode & S_IWOTH) printf("w");
    else printf("-");
    if (statbuf.st_mode & S_IXOTH) printf("x");
    else printf("-");

    /* hard links */
    printf(" %2zu", statbuf.st_nlink);

    /* owner name */
    if ((pwdPtr = getpwuid(statbuf.st_uid)) == NULL) {
        perror("getpwuid");
        exit(EXIT_FAILURE);
    } 
    else {
        printf(" %s", pwdPtr->pw_name);
    }

    /* gruop name */
    if ((grpPtr = getgrgid(statbuf.st_gid)) == NULL) {
        perror("getgrgid");
        exit(EXIT_FAILURE);
    } 
    else {
        printf(" %s", grpPtr->gr_name);
    }

    /* size in bytes */
    /* "C uses an asterisk in the position of the field
       width specifier to indicate to printf that it will
       find the variable that contains the value of the field
       width as an additional parameter." 
       http://www.eecs.wsu.edu/~cs150/reading/printf.htm */
    while(offset != 0)
    {
        offset /= 10;             /* n=n/10 */
        ++length;
    }
    printf(" %*d", length, (int)statbuf.st_size);

    /* last modifying time */
    time = ctime(&statbuf.st_mtime);
    time[strlen(time) - 1] = 0;
    printf(" %s", time);

    /* index */
    // ToDo

    /* filename */
    printf(" %s", name);

    printf("\n");
    // -,d,c,b,l,s,p
    //if ((statbuf.st_mode & S_IFMT) == S_IFREG)
    //    printf("- %8ld %s\n", statbuf.st_size, name);
}


int get_maxsize(const char *name)
{
    struct stat statbuf;

    if (lstat(name, &statbuf) == -1) {
        fprintf(stderr, "can't access %s\n", name);
        perror("get_maxsize");
        return -1;
    }
    return statbuf.st_size;
}

正常工作时的输出(仅限当前目录):

yulian@deb:~/programming/os$ ./readDir .
-rw-rw-rw-  1 yulian yulian  4387 Mon Nov 30 06:31:51 2015 readDir.c
-rw-rw-rw-  1 yulian yulian   282 Sun Nov 29 04:43:03 2015 sometext.txt
-rwxr-xr-x  1 yulian yulian 13792 Sat Nov 28 11:54:09 2015 readDir
drwxr-xr-x  2 yulian yulian  4096 Fri Nov 27 05:26:42 2015 testDir 
// there is test dir called `testDir` where it fails

失败时的输出:

yulian@deb:~/programming/os$ ./readDir testDir/
can't access 2.jpg
get_maxsize: No such file or directory
can't access ETicket_edc7cb12cdc23e6c04a308f34fd31c28.pdf
get_maxsize: No such file or directory

更新: 使用我添加到 get_info:

的建议解决方案
....
char *filemane = NULL;

filemane = strrchr(name, '/') + 1;
/* to prevent . and .. from output */
if (strcmp(filemane, ".") == 0 || strcmp(filemane, "..") == 0)
    return;
...
/* filename */
printf(" %s", filemane);          // and changed the argument here

因此输出现在生成的文件名与 ls -l 完全相同,而不是它们的完整路径。

我认为你的程序失败是因为你给它一个相对的 URL 作为参数,它不知道你在哪里。你应该调用 getcwd 函数来首先拥有你的当前目录,将它与程序的参数结合起来,它应该会更好地工作。或者尝试将 ./testDir 作为参数传递,而不仅仅是 testDir/ 以指定您想要位于当前目录中的目录。

您缺少的是将要探索的 dir 添加到 pathbuf 变量中。只需考虑使用 ls

的以下实现
static void ls(const char *dir)
{
    struct dirent * entry;
    DIR *d = opendir(dir);
    char pathbuf[PATH_MAX + 1];
    char tmp[PATH_MAX+1];
    int offset = 0;

    if (d == 0) {
        perror("ls");
        return;
    }

    /* find max file size for better ls-alike output */
    while ((entry = readdir(d)) != NULL) {
        /* pathbuf OR entry->d_name here: */
        // realpath(entry->d_name, pathbuf);
        snprintf (tmp, PATH_MAX, "%s/%s", dir, entry->d_name);
        if (get_maxsize(tmp) > offset)
            offset = get_maxsize(tmp);
    }
    closedir(d);

    d = opendir(dir);
    while ((entry = readdir(d)) != NULL) {
        /* pathbuf OR entry->d_name here: */
        // realpath(entry->d_name, pathbuf);
        snprintf (tmp, PATH_MAX, "%s/%s", dir, entry->d_name);
        get_info(tmp, offset);
    }
    closedir(d);
}