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

时间:2015-11-30 08:43:59

标签: c ls stat dirent.h

我试图编写类似于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完全相同,而不是它们的完整路径。

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分: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);
}