Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

stat() error 'No such file or directory' when file name is returned by readdir()

Tags:

c

unix

stat

I'm not able to identify the error thrown by stat. The below program reads all files in a directory and prints the file name:

DIR *dp;
struct dirent *dirp;
struct stat sb;

if((dp = opendir(argv[1]))==NULL)
{
    perror("can't open dir");
}
while((dirp = readdir(dp))!=NULL)
{
    if (stat(dirp->d_name, &sb) == -1) {
        perror("stat");
    }   
    printf("File name:               %s \n",dirp->d_name);
}

Sample output:

/home/eipe
stat error: No such file or directory
File name:               copyofsample 
File name:               a.out 
File name:               . 
stat error: No such file or directory
File name:               udpclient.c 
File name:               .. 
stat error: No such file or directory
File name:               client.c 
stat error: No such file or directory
File name:               ftpclient.c 

Here are the contents:

ls -l /home/eipe/c

-rwxr-xr-x 1 eipe egroup 7751 2011-02-24 15:18 a.out
-rw-r--r-- 1 eipe egroup  798 2011-02-24 13:50 client.c
-rw-r--r-- 1 eipe egroup   15 2011-02-24 15:34 copyofsample
-rw-r--r-- 1 eipe egroup 1795 2011-02-24 15:33 ftpclient.c
-rw-r--r-- 1 eipe egroup  929 2011-02-24 13:34 udpclient.c
like image 359
John Avatar asked Feb 26 '11 08:02

John


2 Answers

dirp->d_name is the name of the file in the directory: for example, "udpclient.c". The full name of the file is thus "/home/eipe/c/udpclient.c" - but your current working directory is /home/eipe, so stat() is trying to access "/home/eipe/udpclient.c", which doesn't exist.

You can either change your working directory to argv[1] using chdir(), or you can prepend argv[1] to each filename before you call stat().

like image 75
caf Avatar answered Nov 20 '22 11:11

caf


Note that POSIX 2008 introduces fstatat() and related functions (system calls), all distinguished by the at suffix to a familiar function name. It also defines dirfd() to get the file descriptor associated with a directory stream.

The *at() functions take one (or two in the case of renameat()) open file descriptors that refer to a directory. This means that another way of coding this, on a system that supports fstatat() would be:

const char *name = argv[i];

DIR *dp = opendir(dirname);
if (dp == NULL)
{
    fprintf(stderr, "failed to open directory %s (%d: %s)\n",
            name, errno, strerror(errno));
    return -1;
}

int dfd = dirfd(dp);    /* Very, very unlikely to fail */

struct dirent *dirp;
while ((dirp = readdir(dp)) != NULL)
{
    struct stat sb;
    if (fstatat(dfd, dirp->d_name, &sb, 0) == -1) {
        fprintf(stderr, "fstatat(\"%s/%s\") failed (%d: %s)\n",
                name, dirp->d_name, errno, strerror(errno));
    }
    else
        printf("%-20s %s/%s\n", "File name:", name, dirp->d_name);
}

Using fstatat() and related functions allows you to use relative pathnames without using chdir() (which is dangerous; it is hard to get back to where you started without using fchdir()), or concatenating names as shown in the main accepted answer. For portability, it is probably still advisable to use concatenation anyway — but I was able to test this on Mac OS X (10.10.1) and Linux (Ubuntu 14.04), using the code below.

Developed into a complete program (test-fstatat.c):

/* SO 0512-5919 */
#define _XOPEN_SOURCE 700   /* POSIX 2008 plus ... */
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char **argv)
{

    if (argc < 2)
    {
        fprintf(stderr, "Usage: %s directory [...]\n", argv[0]);
        return -1;
    }

    for (int i = 1; i < argc; i++)
    {
        const char *name = argv[i];

        DIR *dp = opendir(name);
        if (dp == NULL)
        {
            fprintf(stderr, "failed to open directory %s (%d: %s)\n",
                    name, errno, strerror(errno));
            return -1;
        }

        int dfd = dirfd(dp);    /* Very, very unlikely to fail */

        printf("%-20s %s\n", "Directory:", name);

        struct dirent *dirp;
        while ((dirp = readdir(dp)) != NULL)
        {
            struct stat sb;
            if (fstatat(dfd, dirp->d_name, &sb, 0) == -1) {
                fprintf(stderr, "fstatat(\"%s/%s\") failed (%d: %s)\n",
                        name, dirp->d_name, errno, strerror(errno));
            }
            else
                printf("%-20s %s/%s\n", "File name:", name, dirp->d_name);
        }

        closedir(dp);
    }
    return 0;
}

Sample run:

$ test-fstatat ~/bin/JLSS-Dist/RCS ../../../src/sqltools/idsmon
Directory:           /Users/jonathanleffler/bin/JLSS-Dist/RCS
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/.
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/..
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/mkbod.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/jlssdist.jdc,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/old.msd.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/chksumtool.pl,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/msd2nmd.pl,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/mknmd.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/publictimestamp.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/new.mknmd.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/PRODCODE,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/prodverstamp.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/md5.create.pl,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/jdcrelease.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/JLSS-Dist.mk,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/PRODUCT,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/msd.create.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/distribution.mk,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/nmd.create.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/jlss.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/VERSION,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/cvtjdc.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/redonmd.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/updmsd.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/setnmd.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/list2msd.pl,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/chkmsdnmd.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/vercmp.pl,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/MSD.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/setjdcversion.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/sortnmd.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/gennmd.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/md5.verify.pl,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/setbomversion.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/chkbodlst.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/updnmd.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/domsd.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/md5.chksum.pl,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/bomrelease.pl,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/mkmsd.sh,v
File name:           /Users/jonathanleffler/bin/JLSS-Dist/RCS/fixnmd.sh,v
Directory:           ../../../src/sqltools/idsmon
File name:           ../../../src/sqltools/idsmon/.
File name:           ../../../src/sqltools/idsmon/..
File name:           ../../../src/sqltools/idsmon/idstest.c
File name:           ../../../src/sqltools/idsmon/idslen
File name:           ../../../src/sqltools/idsmon/install-sh
File name:           ../../../src/sqltools/idsmon/scatterinfo
File name:           ../../../src/sqltools/idsmon/ltmain.sh
File name:           ../../../src/sqltools/idsmon/idsmon.msd
File name:           ../../../src/sqltools/idsmon/idsmon.o
File name:           ../../../src/sqltools/idsmon/configure
File name:           ../../../src/sqltools/idsmon/genscatter
File name:           ../../../src/sqltools/idsmon/config
File name:           ../../../src/sqltools/idsmon/idspacket
File name:           ../../../src/sqltools/idsmon/genconnpacket
File name:           ../../../src/sqltools/idsmon/Makefile
File name:           ../../../src/sqltools/idsmon/config.h.in
File name:           ../../../src/sqltools/idsmon/config.guess
File name:           ../../../src/sqltools/idsmon/depcomp
File name:           ../../../src/sqltools/idsmon/sqlihexdump.o
File name:           ../../../src/sqltools/idsmon/missing
File name:           ../../../src/sqltools/idsmon/install.mk
File name:           ../../../src/sqltools/idsmon/sqlihexdump
File name:           ../../../src/sqltools/idsmon/RCS
File name:           ../../../src/sqltools/idsmon/Makefile.am
File name:           ../../../src/sqltools/idsmon/test.istar.logs.tar.gz
File name:           ../../../src/sqltools/idsmon/idstest.o
File name:           ../../../src/sqltools/idsmon/esqlc.mk
File name:           ../../../src/sqltools/idsmon/config.sub
File name:           ../../../src/sqltools/idsmon/idspacket.o
File name:           ../../../src/sqltools/idsmon/compile
File name:           ../../../src/sqltools/idsmon/Old.Releases
File name:           ../../../src/sqltools/idsmon/esqlc-nosfx.mk
File name:           ../../../src/sqltools/idsmon/osiris_11
File name:           ../../../src/sqltools/idsmon/config.h.in~
File name:           ../../../src/sqltools/idsmon/idstest
File name:           ../../../src/sqltools/idsmon/idsmon.c
File name:           ../../../src/sqltools/idsmon/acr.decode
File name:           ../../../src/sqltools/idsmon/sqlihexdump.dSYM
File name:           ../../../src/sqltools/idsmon/toru.istar
File name:           ../../../src/sqltools/idsmon/dumpdblflt
File name:           ../../../src/sqltools/idsmon/sqlipacket.c
File name:           ../../../src/sqltools/idsmon/toru
File name:           ../../../src/sqltools/idsmon/data.info.tgz
File name:           ../../../src/sqltools/idsmon/idsmon.nmd
File name:           ../../../src/sqltools/idsmon/idsmon.jdc
File name:           ../../../src/sqltools/idsmon/idsmon
File name:           ../../../src/sqltools/idsmon/idsmon.black_19
File name:           ../../../src/sqltools/idsmon/Makefile.in
File name:           ../../../src/sqltools/idsmon/aclocal.m4
File name:           ../../../src/sqltools/idsmon/sqlihexdump.c
File name:           ../../../src/sqltools/idsmon/dumpdblflt.dSYM
File name:           ../../../src/sqltools/idsmon/dumpdblflt.c
$

POSIX *at() functions

The relevant set of functions (other than dirfd()) all have names ending at, but there are some other POSIX functions that also have names ending at.

Relevant:

  • dirfd()
  • faccessat()
  • fchmodat()
  • fchownat()
  • fstatat()
  • linkat()
  • mkdirat() *mkfifoat()
  • mknodat()
  • openat()
  • readlinkat()
  • renameat()
  • symlinkat()
  • unlinkat()
  • utimensat()

Not directly relevant:

  • creat()
  • fstat()
  • lstat()
  • shmat()
  • stat()
  • strcat()
  • strncat()
  • wcscat()
  • wcsncat()

If you plan to process files using file streams (FILE *), you need to be aware of:

  • fdopen()
like image 3
Jonathan Leffler Avatar answered Nov 20 '22 11:11

Jonathan Leffler