20

As a part of an assignment from one of my classes, I have to write a program in C to duplicate the results of the ls -al command. I have read up on the necessary materials but I am still not getting the right output. Here is my code so far, its only supposed to print out the file size and the file name, but the file sizes its printing are not correct.

Code:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>

int main(int argc, char* argv[])
{
    DIR *mydir;
    struct dirent *myfile;
    struct stat mystat;

    mydir = opendir(argv[1]);
    while((myfile = readdir(mydir)) != NULL)
    {
        stat(myfile->d_name, &mystat);    
        printf("%d",mystat.st_size);
        printf(" %s\n", myfile->d_name);
    }
    closedir(mydir);
}

These are my results after executing the code:

[root@localhost ~]# ./a.out Downloads
4096 ..
4096 hw22.c
4096 ankur.txt
4096 .
4096 destination.txt

Here are the correct sizes:

[root@localhost ~]# ls -al Downloads
total 20
drwxr-xr-x.  2 root root 4096 Nov 26 01:35 .
dr-xr-x---. 24 root root 4096 Nov 26 01:29 ..
-rw-r--r--.  1 root root   27 Nov 21 06:32 ankur.txt
-rw-r--r--.  1 root root   38 Nov 21 06:50 destination.txt
-rw-r--r--.  1 root root 1139 Nov 25 23:38 hw22.c

Can anyone please point out my mistake.

Thanks,

Ankur

Nikos C.
  • 50,738
  • 9
  • 71
  • 96
ankur3000
  • 203
  • 1
  • 2
  • 5
  • 2
    Works okay for me. Check the return value of stat() to see if there is an error. – Neal Nov 25 '12 at 18:46
  • 1
    Works fine here too. Introduce error checks for everything (opendir(), readdir(), stat()). – Nikos C. Nov 25 '12 at 18:48
  • Works fine if you run it on `.`, but not if you give it another directory. – Kristian Glass Nov 25 '12 at 18:52
  • This is besides the point of the question, but I would suggest you not run programs (especially when learning) as root. I would instead use `sudo` to run specific programs as root – 0xff0000 Mar 21 '13 at 17:40

4 Answers4

17

myfile->d_name is the file name not the path, so you need to append the file name to the directory "Downloads/file.txt" first, if it's is not the working directory:

char buf[512];    
while((myfile = readdir(mydir)) != NULL)
{
    sprintf(buf, "%s/%s", argv[1], myfile->d_name);
    stat(buf, &mystat);
....

As to why it prints 4096 that is the size of the links . and .. from the last call to stat().

Note: you should allocate a buffer large enough to hold the directory name, the file name the NULL byte and the separator, something like this

strlen(argv[1]) + NAME_MAX + 2;
iabdalkader
  • 17,009
  • 4
  • 47
  • 74
  • Thank you very much for your response, making your suggested additions worked. The program is giving the correct file sizes now. Thanks. – ankur3000 Nov 25 '12 at 19:13
  • This is unbelievable useful when doing my OS papers that i needed to to install drivers in the kernel 2.6.39 emulating with qemu and just one init.. I still didn't figure somethings out but thanks!! haha!! – Lucas Lazaro Jul 04 '13 at 21:17
8

This is the final code I got to work for anyone interested. It prints the correct file sizes. Credit goes to asker and mux for answering, just putting the code together. Input I got this to work for is "./main ." .

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>

int main(int argc, char* argv[])
{
    DIR *mydir;
    struct dirent *myfile;
    struct stat mystat;

    char buf[512];
    mydir = opendir(argv[1]);
    while((myfile = readdir(mydir)) != NULL)
    {
        sprintf(buf, "%s/%s", argv[1], myfile->d_name);
        stat(buf, &mystat);
        printf("%zu",mystat.st_size);
        printf(" %s\n", myfile->d_name);
    }
    closedir(mydir);
}
hbteibet
  • 159
  • 1
  • 9
2

I believe you'll observe that if you ./a.out . you will get the behaviour you expect.

You have a slightly subtle bug, observable if you examine the return code of your call to stat(2).

The fundamental mistake: the dirents returned by readdir(2) (the myfile in your code) will have a d_name relative to mydir. Your code will stat .. first, succeed, and so mystat will contain valid data for .., then all subsequent calls to stat(2) will fail, returning -1, which you do not check for, so mystat will not be modified, and you will print the st_size for the old value, i.e. that of ...

Kristian Glass
  • 37,325
  • 7
  • 45
  • 73
  • Thanks for your reply, I see the problem in my code now. But exactly why does this happen? I thought that when we execute the opendir() command, the program is automatically adjusting the file paths for that directory. – ankur3000 Nov 25 '12 at 19:12
1

The trouble is that when you stat("ankur.txt", &mystat), you are not working on the file "Downloads/ankur.txt". Most likely, the stat() is failing; alternatively, it is reporting on a different file.

Consequently, you need to look at whether your system supports fstatat() — new in POSIX 2008 — or arrange to prefix the name of the file with name of the directory.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278