问题
I'm trying to write ls-alike program that produces output like ls -l
with permissions, owners, time and name of the file.
It works good if I pass .
(or nothing), so it works with the current directory. But if I pass any other directory in or out of the current one, perror
says it "can't access" the files.
Please, help me figure out what prevents lstat
from accessing files in another dirs.
I use gcc and a text editor, no IDE, started learning to use gdb (tried to debug but didn't find something that would point me to what kind of solution shall I look for). That's why I decided to put here all the code, so anybody can run it. Maybe I pass wrong arguments, maybe it is some kind of lstat
's wrong behavior, I don't know. I've been trying to find something about it on the web, but with no result.
Here is what I've done up to this moment:
#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;
}
Output when it works fine (only with current directory):
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
Output when 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
update: with the suggested solution I added to 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
so the output now produces file names exactly as ls -l
, not their full paths.
回答1:
What you're missing is to add the dir you want to explore into the pathbuf variable. Simply consider using the following implementation of 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:
I think that your program fails because you give it a relative URL as a parameter, and it doesn't know exactly where you are. you should call the getcwd function to first have your current directory, join it with the parameter of the program and it should work better. Or maybe try to pass ./testDir as a parameter and not just testDir/ to specify that you want a directory located in the current directory.
来源:https://stackoverflow.com/questions/33994503/lstat-cant-access-files-in-another-directory