C – Linux, C : access() not catching permission problems, etc

Linux, C : access() not catching permission problems, etc… here is a solution to the problem.

Linux, C : access() not catching permission problems, etc

I’m writing a program to mimic some behavior of find that traverses the directory tree and calls lstat on found files to determine their type. A true find ignores files for which the user does not have R or X access in that directory. I can’t seem to replicate this behavior; My code will continue to execute lstat calls and get an illegal lookup error (which is the one I’m trying to prevent), even if the code that does this is inside the block that checks for access().

My first thought was that maybe the second access() call should be on the path instead of the path/filename, but that doesn’t seem to work either (and isn’t it redundant?). )

Any guidance would be appreciated.

My code (I’ve removed bug catches and other things for brevity):

    void open_dir( char *dir, char *pattern, char type )
    {
        DIR *d;
        struct dirent *de;

if ( access(dir, (R_OK | X_OK)) == 0 )
        {
            d = opendir(dir);

while( ( de = readdir(d) ) )
                examine_de( de, dir, pattern, type );

closedir(d);
        }
    }

void examine_de( struct dirent *de, char *dir, char *pattern, char type )
    {
        char fn[ _POSIX_PATH_MAX ];
        strcpy(fn, dir);
        strcat(fn, "/");
        strcat(fn, de->d_name);

if ( access(fn, (R_OK | X_OK)) == 0 )
        {
            struct stat buf;
            lstat(fn, &buf);
            check pattern matches, etc., printf fn if appropriate
            if ( ( S_ISDIR(buf.st_mode) ) &&
                 ( strcmp(de->d_name, ".") != 0 ) &&
                 ( strcmp(de->d_name, "..") != 0 ) )
                open_dir(fn, pattern, type);
        }
        return;
    }

Solution

lstat() should not always return ESPIPE. Are you sure it’s not another system call that is returned, or the errno value that hasn’t changed after lstat() succeeds? (In other words, the error may actually exist in the error-checking code that you have omitted).

That said, it doesn’t make sense to use access() in this way – it just introduces a race condition (since file permissions may be in both access() calls and opendir()/lstat() calls) and nothing is gained. Just check the return values of opendir() and lstat():

void open_dir( char *dir, char *pattern, char type )
{
    DIR *d;
    struct dirent *de;

if (d = opendir(dir))
    {
        while( ( de = readdir(d) ) )
            examine_de( de, dir, pattern, type );

closedir(d);
    }
}

void examine_de( struct dirent *de, char *dir, char *pattern, char type )
{
    char fn[ _POSIX_PATH_MAX ];
    struct stat buf;

strcpy(fn, dir);
    strcat(fn, "/");
    strcat(fn, de->d_name);

if (lstat(fn, &buf) == 0)
    {
        check pattern matches, etc., printf fn if appropriate
        if ( ( S_ISDIR(buf.st_mode) ) &&
             ( strcmp(de->d_name, ".") != 0 ) &&
             ( strcmp(de->d_name, "..") != 0 ) )
            open_dir(fn, pattern, type);
    }
    return;
}

This is usually the correct pattern – instead of checking if the action is feasible and then trying the action, try the operation unconditionally and then check the reason for the failure.

Related Problems and Solutions