【问题标题】:lstat: can't access files in another directorylstat:无法访问其他目录中的文件
【发布时间】:2016-03-03 20:19:33
【问题描述】:

我正在尝试编写类似 ls 的程序,该程序产生像 ls -l 这样的输出,带有文件的权限、所有者、时间和名称。 如果我通过.(或什么都不通过),它会很好用,所以它适用于当前目录。但是,如果我将任何其他目录传入或传出当前目录,perror 会说它“无法访问”文件。

请帮我弄清楚是什么阻止了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 完全相同,而不是它们的完整路径。

【问题讨论】:

    标签: c ls stat dirent.h


    【解决方案1】:

    您缺少的是将要探索的 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);
    }
    

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 观察是好的,但是你应该加入direntry-&gt;d_name的值,而不是getcwd()的结果。 (工作目录永远不会改变,相对路径应该没问题。)
      • 传递 ./testDir 无效:显示相同的结果。
      猜你喜欢
      • 2018-11-09
      • 2020-07-10
      • 1970-01-01
      • 1970-01-01
      • 2021-06-17
      • 2012-06-30
      • 1970-01-01
      • 2019-05-29
      • 2020-11-09
      相关资源
      最近更新 更多