Thread: directory operations, linux

  1. #1
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300

    Talking directory operations, linux

    I just started working with c but have done a lot of perl and shellcode. I keep getting back an inappropriate "errno" from opendir ("No such file or directory") regarding directories which do exist and have no ownership or permission irregularities.

    here's a concise version of the problem, also at nopaste (http://rafb.net/p/6HHgvD40.html). argv[1] should be the path to the top of a directory tree. the binary should then spit out an unsorted list of all the directories in the tree. it works sometimes -- the errno comes from line 15...

    Code:
    #include <stdio.h>
    #include <dirent.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <errno.h>
    
    char fullname[256], parentpath[256];
    
    int nextdir () {
            printf ("nextdir: &#37;s\n", fullname);
            errno = 0;
            DIR *dir = opendir (fullname); 
            if (dir == NULL ) {
    		printf ("For %s ERROR: %s\n", fullname, strerror(errno)); 
    			exit(1);}
            struct dirent *dcon;
            struct stat dstuff;
    
            strcpy(parentpath, fullname);  // since fullname is operated on 
    
            while (dcon = readdir (dir)) {
                    if (stat(fullname,&dstuff) != 0) 
    			printf("stat error for %s\n", dcon->d_name);  
                    if ((dcon->d_type == 4) && 
    		(strcmp (dcon->d_name, ".")) != 0 && 
    		(strcmp (dcon->d_name, "..")) != 0) { 
                            strcat(fullname, "/"); 	
    			strcat(fullname, dcon->d_name); nextdir(); } 
            }   
            closedir (dir);
            strcpy(fullname,parentpath);  // resets the path
    }
    
    int main (int argc, char *argv[]) {
            errno = 0;
            DIR *dir = opendir (argv[1]); 
            if (dir == NULL ) {
    		printf ("For %s ERROR: %s\n", argv[1], strerror(errno)); 
    			exit(1);}
            struct dirent *dcon;
            struct stat dstuff;
            
            printf ("Top Level '%s':\n", argv[1]);
                
            while (dcon = readdir (dir)) {
                    strcpy(fullname, argv[1]);
                    strcat(fullname, "/");
                    strcat(fullname, dcon->d_name);
                    if (stat(fullname,&dstuff) != 0) 
    			printf("stat error for %s\n", dcon->d_name);
                    if ((dcon->d_type == 4) && 
    		(strcmp (dcon->d_name, ".")) != 0 && 
    		(strcmp (dcon->d_name, "..")) != 0) {
                            nextdir(); }
            }
            closedir (dir);
    }
    Last edited by MK27; 07-09-2008 at 06:17 PM. Reason: code error

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    What directory name(s) are you seeing this for? Does it work for the same directory again?

    This line in main does nothing useful:
    Code:
            strcpy(parentpath, fullname);
    as parentpath and fullname are both unset [which means, since they are global variables, that they are both empty strings].

    Surely it's easier to follow this code segment if it's written a bit more "spaced out":
    Code:
    ...
       strcat(fullname, dcon->d_name); 
       nextdir(); 
    }
    It took me three attempts to see that the code was recursive (which I expected, I just couldn't see it on the first two reads - because the recursive call was hidden on the same line as the strcat).

    Also, your 4 when you check if it's a directory should really be:
    DT_DIR

    And instead of 256 in your name, I think you should use PATH_MAX (and why do you need to have a GLOBAL variable for this?)

    You are also using C99 extensions (variable declarations after code for example) - which may make your code a bit less portable than you wish for.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300

    Red face reply to reply

    Dealing with Mats points in order:

    1) It always hangs on the same directories, but there is nothing special about them to indicate why.

    2) yes, that line in main was pointless and is now gone.

    3) Regarding formatting, you may have the weight of popular opinion behind you but that doesn't mean that you don't have to read a whole line when you think it's too long! Also: how could you have thought this was anything BUT recursive, esp. since I have two comments to this effect in the code itself...what i really need is someone to compile, test it, and speculate on the error produced, since scanning it three or three hundred times may still leave the scanner clueless.

    4) regarding DT_DIR, PATH_MAX, and C99: thanks for some pointers! Memory allocation is a new issue for me (coming from perl). Nb. that there were no paths long enough to cause an overflow in my tests anyway.

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,656
    Formatting counts, and yours sucks. Multiple statements on the same line just make it harder to read.

    My first though is "WTF - global variables and recursion"
    That gets you through exactly ONE level of recursion. After that, there's no way back to where you were originally.

    > if ((dcon->d_type == 4)
    How about using the IS_DIR macro to test for a directory rather than assuming a specific implementation?
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Woof, woof! zacs7's Avatar
    Join Date
    Mar 2007
    Location
    Australia
    Posts
    3,459
    Just to check that it wasn't an error on your side, I ran it:
    Note I re-ordered the source a bit to C89 conformance, and re-indented so I could read it.

    You should probably:
    • Add more error checking (esp. bounds checking including main's argc/argv).
    • Avoid magic numbers like matsp said, ie PATH_MAX
    • Bump up the warning level of the compiler, you'll see quite a few!
    • Get a nicer brace style
    • Don't abuse global variables


    Code:
    [zac@neux codetest]$ gcc -W -Wall -std=c89 -pedantic -O0 codetest.c -o codetest
    codetest.c:48: warning: unused parameter 'argc'
    
    [zac@neux codetest]$ ./codetest /mnt/media2/Pictures
    Top Level '/mnt/media2/Pictures':
    nextdir: /mnt/media2/Pictures/100OLYMP
    nextdir: /mnt/media2/Pictures/Shooting
    nextdir: /mnt/media2/Pictures/smilleys
    nextdir: /mnt/media2/Pictures/smilleys/gifs
    
    [zac@neux codetest]$ ./codetest /root
    For /root ERROR: Permission denied
    
    [zac@neux codetest]$ ./codetest /    
    Top Level '/':
    nextdir: //bin
    nextdir: //dev
    nextdir: //dev/sound
    nextdir: //dev/sound/snd
    For //dev/sound/snd ERROR: No such file or directory
    Still terrible code IMO, no way is it a file sys corruption.
    Last edited by zacs7; 07-09-2008 at 11:18 PM. Reason: No free cookies

  6. #6
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by MK27 View Post
    Dealing with Mats points in order:

    1) It always hangs on the same directories, but there is nothing special about them to indicate why.
    What directory is it failing at, and can you provide an "ls -l" for the directory that holds the failing one?
    2) yes, that line in main was pointless and is now gone.

    3) Regarding formatting, you may have the weight of popular opinion behind you but that doesn't mean that you don't have to read a whole line when you think it's too long! Also: how could you have thought this was anything BUT recursive, esp. since I have two comments to this effect in the code itself...what i really need is someone to compile, test it, and speculate on the error produced, since scanning it three or three hundred times may still leave the scanner clueless.
    Yes, I even knew what I was looking for, but I see strcat() at the beginning of the line, I don't expect to find a "nextdir()" tagged on the end. I'd accept it if you put two strcat() on the same line since they may indeed belong together.

    No, the compiler doesn't care. A human reading the code does. You know what your code does, so it is FOR OTHER people that you format the code.

    Making the code easier to read is more likely to get you comments from people who are willing to help you, because they don't struggle to understand/read the code.

    I can't test the code, as I don't have a Linux machine available at this point in time. But I think others have commented on the matter sufficiently that you have some clues - the first one would be "don't use globals with recursion", something I thought but didn't write down - sorry about that.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  7. #7
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Contrary to Salem's assertion, this does work fine recursively BELIEVE ME. The problem is the "No such file or directory" produced as with zacs7 example. If you aren't willing to actually runtest it, please don't bother to comment, because it seems to me that no one so far has any idea why this error occurs. I am hoping someone will tho...

  8. #8
    Registered User
    Join Date
    Dec 2007
    Posts
    2,675
    The only way I could get it to recurse at all on Suse 10.1 was to change to use S_ISDIR, as suggested by Salem.

    EDIT: Of interest, a gzipped tar file (containing subdirectories) returns S_ISDIR(st.st_mode) == TRUE. That's somewhat unexpected.
    Last edited by rags_to_riches; 07-10-2008 at 08:54 AM.

  9. #9
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,656
    > Contrary to Salem's assertion, this does work fine recursively BELIEVE ME.
    Only because you don't actually use either of those globals at the moment.
    But sure as eggs are eggs, as soon as you did, you'd be back asking another question.
    I can test it, but not till the weekend.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  10. #10
    Registered User
    Join Date
    Jul 2008
    Posts
    133
    One note about that code: you shouldn't do strcpy(fullname,parentpath) because it won't work if nextdir() recurses more than one level deep. Instead do:
    Code:
    // skip restoring path if we're on root dir
    // declare 'p' somewhere else (as global) if you're gonna scan thousands of files...
    {char *p = strrchr(fullname,'/'); if (p && (p!=fullname)) *p = 0;}
    so you don't need "parentpath" at all.
    Note that SINGLE directory entry can be 256 bytes, so allocate fullname as at least 10*256 (to allow max 10 recursions of max length). Another note on global vars - in this case, it's preffered to use global instead of local vars, because believe me, when you scan 20,000+ files, as any local variable consumes precious stack space - it will crash. I've seen malloc() 'running fine' and overwritting my previous malloc()-ed memory - not because i've run out of memory, I've run out of stack... Use globals in this case...
    Last edited by rasta_freak; 07-31-2008 at 11:36 AM.

  11. #11
    Registered User
    Join Date
    Mar 2008
    Posts
    43
    This isn't exactly what you're looking for but a few pointers on directory content reading:
    http://udrepper.livejournal.com/tag/programming+linux

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Thinking of upgrading to linux...
    By Yarin in forum General Discussions
    Replies: 37
    Last Post: 07-24-2009, 11:40 AM
  2. installing linux for the first time
    By Micko in forum Tech Board
    Replies: 9
    Last Post: 12-06-2004, 05:15 AM
  3. Linux, forms, dirs, permissions, cgi-bin, and C
    By ronin in forum C Programming
    Replies: 0
    Last Post: 12-19-2003, 06:10 PM
  4. Linux? Windows Xp?
    By VooDoo in forum Linux Programming
    Replies: 15
    Last Post: 07-31-2002, 08:18 AM
  5. linux vs linux?
    By Dreamerv3 in forum Linux Programming
    Replies: 5
    Last Post: 01-22-2002, 09:39 AM