Author Topic: opendir() requires chdir()?  (Read 1926 times)

0 Members and 4 Guests are viewing this topic.

Offline MikeKTopic starter

  • Super Contributor
  • ***
  • Posts: 1364
  • Country: us
opendir() requires chdir()?
« on: October 04, 2023, 06:35:14 pm »
I have a short program to output a directory's files.  If I open the current directory with opendir("."), it works fine.  But if I specify a path, it will list the filenames correctly, but the attributes are wrong.  The mtime is wrong, the size is wrong, and the directory flag is wrong (probably others as well).

I added chdir() as a fluke and it works.  But I'm not understanding why chdir() is necessary.  Shouldn't opendir() return the data for that directory?

(I've taken this code from a book which, presumably, was tested and worked without chdir().)

Code: [Select]
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <time.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>

int main(void) {
    DIR *folder;
    struct dirent *file;
    struct stat filestat;
    char s[6];
    char *dir = "/home/mike/python";

    folder = opendir(dir);
    chdir(dir);
    if (folder == NULL) {
        puts("Unable to read directory");
        exit(1);
    }

    while ((file=readdir(folder)) != NULL) {
        stat(file->d_name, &filestat);
       
        sprintf(s, "%5ld", filestat.st_size);
        printf("%-14s %s %s",
                file->d_name,
                S_ISDIR(filestat.st_mode) ?  "<DIR>" : s,
                ctime(&filestat.st_mtime));
    }
    closedir(folder);
    return(0);
}
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 8349
  • Country: fi
    • My home page and email address
Re: opendir() requires chdir()?
« Reply #1 on: October 04, 2023, 06:55:40 pm »
I have a short program to output a directory's files.
Why don't you use the proper interface for that, scandir() or nftw()?

If I open the current directory with opendir("."), it works fine.  But if I specify a path, it will list the filenames correctly, but the attributes are wrong.
The d_name field of a struct dirent only contains the file name part.

When you do
    DIR *dir = opendir("/home/myself/python");
and some
    struct dirent *ent = readdir(dir);
you might have ent->d_name containing say example.py .
The problem is when you do
    stat(ent->d_name, &filestat)
Because relative paths (including plain file names) given to stat() refer to files and paths starting from the current working directory, you won't 'stat' the correct paths at all!
You are trying to obtain the information on files in the current working directory, that have the same name as files in the directory opened via opendir().

The solution is to either construct the full path by combining the directory you used, a slash, and the entry d_name field, or to use fstatat(dirfd(DIR), ent->d_name, &filestat, 0);.  However, if you do have fstatat() available, you also have scandir() available, in which case you're using the wrong tool for the job anyway.

Alternatively, you can just do a chdir(target-directory) first, and DIR *dir = opendir(".");.

And if you want to traverse entire directory trees, you'd better use nftw(), or if you have a Microsoft-like deep dislike towards anything POSIX, the BSD-originating fts interface.

Other than in very specific cases like kernel pseudofilesystems and having to be compatible with Windows, you should never use opendir()/readdir() directly, because you won't handle the cases where the directories are modified during scanning anyway.  The proper interfaces are supposed to handle that sanely.
« Last Edit: October 04, 2023, 06:58:36 pm by Nominal Animal »
 
The following users thanked this post: MikeK

Offline MikeKTopic starter

  • Super Contributor
  • ***
  • Posts: 1364
  • Country: us
Re: opendir() requires chdir()?
« Reply #2 on: October 04, 2023, 07:30:54 pm »
Great.  Thank you.

EDIT: Yes, thanks for that thorough explanation.
« Last Edit: October 05, 2023, 05:15:31 pm by MikeK »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf