1

I'm using the getcwd function to retrieve the current working directory of my application. In some cases it fails and errno is ENOENT. I call getcwd from different threads, but serially and sometimes I face with ENOENT, but I don't know why. The link above says that ENOENT means that "The current working directory has been unlinked." but the directory exists.

Here is a snippet of the function I use:

UPDATE: Code updated with advice from @Aganju and @molbdnilo:

std::string get_working_dir()
{
    char buf[PATH_MAX];
    memset(buf, 0, sizeof(buf));
    char * result = getcwd(buf, sizeof(buf));
    std::string working_path = buf;
    // Check for possible errors.
    if (result == NULL)
    {        
        switch (errno)
        {
            case EACCES:
                break;
            case EFAULT:
                break;
            case EINVAL:
                break;
            case ENAMETOOLONG:
                break;
            case ENOENT:
            {
                if (working_path.empty())
                {
                    const char* pwd = getenv("PWD");
                    working_path = (pwd) ? pwd : "";
                }
                break;
            }
            case ERANGE:
                break;
            default:
                break;
        }
        return working_path;
    }
}

In case I face ENOENT, I retrieve the "PWD" environment variable because I work on CentOS 5.11 and Ubuntu 16.04, and when getcwd fails on CentOS it returns an empty buffer.

UPDATE:

I have noticed that the call from the main thread does not fail, but when I call the function from another thread it fails and errno is ENOENT.

rboc
  • 344
  • 3
  • 10
  • Run `strace` on your process, possibly with the `-f` flag, and observe which systemcall returns `ENOENT`. This will be your first clue as to what could be going on. – Sam Varshavchik Aug 24 '17 at 12:28
  • 3
    You need to check the return value of `getcwd` first. `errno` is meaningless unless `getcwd` returns a null pointer. – molbdnilo Aug 24 '17 at 12:36
  • I checked for NULL as you and @Aganju recommended. On CentOS 5 `getcwd` returns NULL and errno is ENOENT. I'm going to update the example code. One thing I have noticed is that as my app is multithreaded, the calls that fails with ENOENT are the ones that are not called from the main thread. – rboc Aug 24 '17 at 16:20

2 Answers2

3

As molbdnilo said, you need to check the return value first.

The manual you link to states:

[...] On failure, these functions return NULL, and errno is set to indicate the error.[...]

In other words, if there is no failure, errno is not set, and contains whatever it contained from whatever call happened before, maybe a long time before, which is without meaning in this context.

Use something like char * result = getcwd(buf, sizeof(buf));, and then check for if (result == NULL) { switch (errno) ..., etc.

Aganju
  • 6,295
  • 1
  • 12
  • 23
  • I checked for NULL as you recommended. On CentOS 5 getcwd returns NULL and errno is ENOENT. I have updated the question. – rboc Aug 24 '17 at 16:40
1

This can happen in the following situation, which we can illustrate with a multi-process sequence diagram, using shell script syntax:

process A                     process B
$ cd ~                        $ cd ~
user $ mkdir foo            
user $ cd foo
                              user $ rm -rf foo              
user/foo $ pwd
/home/user/foo
user/foo $ cd .
cd: error retrieving current
directory: getcwd: cannot access
parent directories: No such file or directory

Every process in a Unix-like operating system has a current working directory , which holds a reference to an object in the file system. That object cannot be reclaimed as free space until the process drops that current directory.

Above, Process A continues to hold on to the directory which used to be ~/foo. That path no longer exists in the directory structure, but the directory object continues to exist.

The getcwd system call realizes this: it sees that the current working directory the calling process is not linked into the directory structure and hence has no path, so it reports an error.

The pwd shell command works because it just regurgitates a piece of datum that the shell knows about without calling getcwd; but when we try cd ., an error occurs (reproduced with Bash on a GNU/Linux system; results may vary).

Kaz
  • 55,781
  • 9
  • 100
  • 149
  • My app is single process but multithreaded. Could this be happening anyway? Threads are supposed to share the working directory. – rboc Aug 24 '17 at 16:38
  • It's a misfeature of POSIX that the `chdir` system call has a process-wide side effect. In practice, what it means that a multithreaded process should establish a working directory at start up and not touch it again. Also, functions which implicitly call `chdir` have to be avoided like `nftw` and such. – Kaz Aug 24 '17 at 17:01