2

I have a problem in malloc/free. I want to implement a simple 'ls' to show unhidden files in a dir.

Here is my code:

void do_ls(char *dirname, _Bool ls_list) {
    DIR *dir_ptr;
    const char * d_name;
    struct dirent *direntp, ** dirarray;
    int fcnt, d_name_max;
    if((dir_ptr = opendir(dirname))==NULL) {
        fprintf(stderr,"ls: cannot open %s\n", dirname);
        exit(-1);
    }
    else {
        fcnt = 0; // count unhidden files
        d_name_max = -1; // max length of filename
        while((direntp = readdir(dir_ptr))!=NULL) {
            d_name = direntp->d_name;
            if(d_name[0]!='.' ||\
                    strcmp(d_name, ".")==0 ||\
                    strcmp(d_name, "..")==0) {
                fcnt++;
                if((int)strlen(d_name)>d_name_max)
                    d_name_max = strlen(d_name);
            }
        }
        // use a array to store dirent printer
        dirarray = (struct dirent**)malloc(sizeof(struct dirent *)*fcnt);
        // reset read position
        seekdir(dir_ptr, 0);
        int i = 0;
        while((direntp=readdir(dir_ptr))!=NULL) {
            if(d_name[0]!='.' ||\
                    strcmp(d_name, ".")==0 ||\
                    strcmp(d_name, "..")==0) {
                dirarray[i++] = direntp; // save pointer
            }
        }
        qsort(dirarray, fcnt, sizeof(struct dirent*), alphacmp); // qsort for lexicographical ordering
        for(int i=0;i<fcnt;i++)
            fprintf(stdout, "%s\n", dirarray[i]->d_name); // print result
        if(dirarray!=NULL)
            free(dirarray); // free if not NULL
        closedir(dir_ptr);
    }
}

It's confusing to me, thatit works or crashes in some cases. And I guess there must be some wrongs in do_ls but still cannot find out.

Files under my dir are:

`ls -a ~/file/c/`
./  ../  1*  a.c  db/  mpi/  test1.c  .test1.c.swo  .test1.c.swp  unix/  .ycm_extra_conf.py  .ycm_extra_conf.pyc    

But when run ./my-own-ls ~/file/c/, I'm getting these errors:

/home/panhzh3/file/c:
..
.test1.c.swo
.test1.c.swp
.ycm_extra_conf.py
.ycm_extra_conf.pyc
1
mpi
unix
*** glibc detected *** ./ls: free(): invalid next size (fast): 0x08d9b028 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x75b12)[0xb7664b12]
./ls[0x80488e5]
./ls[0x8048a6f]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xb76084d3]
./ls[0x8048621]
======= Memory map: ========
08048000-08049000 r-xp 00000000 08:0a 1183201    /home/panhzh3/file/c/unix/hw/ls
08049000-0804a000 r--p 00000000 08:0a 1183201    /home/panhzh3/file/c/unix/hw/ls
0804a000-0804b000 rw-p 00001000 08:0a 1183201    /home/panhzh3/file/c/unix/hw/ls
08d93000-08dbc000 rw-p 00000000 00:00 0          [heap]
b75b4000-b75d0000 r-xp 00000000 08:09 392702     /lib/i386-linux-gnu/libgcc_s.so.1
b75d0000-b75d1000 r--p 0001b000 08:09 392702     /lib/i386-linux-gnu/libgcc_s.so.1
b75d1000-b75d2000 rw-p 0001c000 08:09 392702     /lib/i386-linux-gnu/libgcc_s.so.1
b75ee000-b75ef000 rw-p 00000000 00:00 0 
b75ef000-b7793000 r-xp 00000000 08:09 392617     /lib/i386-linux-gnu/libc-2.15.so
b7793000-b7795000 r--p 001a4000 08:09 392617     /lib/i386-linux-gnu/libc-2.15.so
b7795000-b7796000 rw-p 001a6000 08:09 392617     /lib/i386-linux-gnu/libc-2.15.so
b7796000-b7799000 rw-p 00000000 00:00 0 
b77b3000-b77b7000 rw-p 00000000 00:00 0 
b77b7000-b77b8000 r-xp 00000000 00:00 0          [vdso]
b77b8000-b77d8000 r-xp 00000000 08:09 392601     /lib/i386-linux-gnu/ld-2.15.so
b77d8000-b77d9000 r--p 0001f000 08:09 392601     /lib/i386-linux-gnu/ld-2.15.so
b77d9000-b77da000 rw-p 00020000 08:09 392601     /lib/i386-linux-gnu/ld-2.15.so
bff21000-bff43000 rw-p 00000000 00:00 0          [stack]
[2]    32254 abort (core dumped)  ./ls ~/file/c

Source codes:

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <getopt.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>

int alphacmp(const void * a, const void * b) {
    struct dirent ** ta = (struct dirent**)a;
    struct dirent ** tb = (struct dirent**)b;
    return strcmp((*ta)->d_name, (*tb)->d_name);
}

void do_ls(char *dirname, _Bool ls_list) {
    DIR *dir_ptr;
    const char * d_name;
    struct dirent *direntp, ** dirarray;
    int fcnt, d_name_max;
    if((dir_ptr = opendir(dirname))==NULL) {
        fprintf(stderr,"ls2: cannot open %s\n", dirname);
        exit(-1);
    }
    else {
        fcnt = 0;
        d_name_max = -1;
        while((direntp = readdir(dir_ptr))!=NULL) {
            d_name = direntp->d_name;
            if(d_name[0]!='.' ||\
                    strcmp(d_name, ".")==0 ||\
                    strcmp(d_name, "..")==0) {
                fcnt++;
                if((int)strlen(d_name)>d_name_max)
                    d_name_max = strlen(d_name);
            }
        }
        dirarray = (struct dirent**)malloc(sizeof(struct dirent *)*fcnt);
        seekdir(dir_ptr, 0);
        int i = 0;
        while((direntp=readdir(dir_ptr))!=NULL) {
            if(d_name[0]!='.' ||\
                    strcmp(d_name, ".")==0 ||\
                    strcmp(d_name, "..")==0) {
                dirarray[i++] = direntp;
            }
        }
        qsort(dirarray, fcnt, sizeof(struct dirent*), alphacmp);
        for(int i=0;i<fcnt;i++)
            fprintf(stdout, "%s\n", dirarray[i]->d_name);
        if(dirarray!=NULL)
            free(dirarray);
        closedir(dir_ptr);
    }
}

int main(int argc, char **argv) {
    const char * const short_options = "l";
    const struct option long_options[] = {
       {"list", 0, NULL, 'l'},
       {NULL, 0, NULL, 0},
    };
    const char * program_name = argv[0];

    opterr = 0;

    int next_option;
    _Bool ls_list = false;

    do {
        next_option = getopt_long(argc, argv, short_options, long_options, NULL);
        switch(next_option) {
            case 'l':
                ls_list = true;
                break; 
            case '?':
                fprintf(stderr, "Invalid option: -%c", optopt);
                exit(-1);
                break;
            case ':':
                fprintf(stderr, "Option -%c needs argument.", optopt);
            case -1:
                break;
            default:
                abort();
        }
    } while(next_option!=-1);

    if(optind == argc)
        do_ls(".", ls_list);
    else
        for(;optind<argc;optind++) {
            printf("%s:\n", argv[optind]);
            do_ls(argv[optind], ls_list);
        }
    return 0;
}
trejder
  • 17,148
  • 27
  • 124
  • 216
Hayes Pan
  • 585
  • 1
  • 4
  • 19
  • 1
    You have corrupted your memory arena by overwriting memory somewhere. And, since only students tend to write `ls` replacements, you probably should learn how to debug your code yourself :-) – paxdiablo Dec 17 '14 at 08:05
  • [Valgrind](http://valgrind.org/) may help you tracking down memory corruption bugs. – flyx Dec 17 '14 at 08:08
  • 1
    Sidenote: Please [do not cast](http://stackoverflow.com/q/605845/2173917) the return value of `malloc()`. – Sourav Ghosh Dec 17 '14 at 08:09
  • @paxdiablo Yes I am a student^_^ and in fact I've debuged for 2 hours and really cannot find out the bug...I think wrongs may be simple to catch but difficult for a green hand like me, sorry~ – Hayes Pan Dec 17 '14 at 08:13
  • @flyx Thanks! Let me try. – Hayes Pan Dec 17 '14 at 08:15

1 Answers1

1

In your second while loop, you are missing the line:

d_name = direntp->d_name;

One other thing you might need to know is that you can't depend on storing the dirent pointers in an array and sorting them. The man page for readdir says that the data may be overwritten by subsequent calls, so you might have to make copies of each entry and sort the copies instead.

JS1
  • 4,745
  • 1
  • 14
  • 19