Я пытаюсь написать программу ls-alike, которая производит выходные данные, такие как ls -l
с разрешениями, владельцами, временем и именем файла. Он работает хорошо, если я передаю .
(или ничего), поэтому он работает с текущим каталогом. Но если я передаю любой другой каталог в или из текущего, perror
говорит, что он «не может получить доступ» к файлам.lstat: не удается получить доступ к файлам в другом каталоге
Пожалуйста, помогите мне выяснить, что мешает 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;
}
выхода, когда он работает отлично (только с текущим каталогом):
[email protected]:~/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
выхода, когда он терпит неудачу:
[email protected]:~/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
, а не их полные пути.
Наблюдение это хорошо, но вы должны присоединиться значение '' dir' и entry-> d_name', не результат 'getcwd()'. (Рабочий каталог никогда не изменяется, а относительные пути должны быть в порядке.) –
Передача './TestDir' не работает: показывает те же результаты. – yulian
@ m-Oehm Вы правы, не видели обращения к readdir. – KeylorSanchez