3

My program is running in a Linux environment, compiled with gcc version 4.4.7.

I am using realpath() to "canonicalize" file paths. The path of every directory and file I feed to realpath() definitely exists, which of course is essential for realpath() to work properly.

However, sometimes realpath() will fail with error code 17, name EEXIST, string description "File exists".

That baffles me. Of course it exists, I scream at realpath(). But realpath() is unmoved by my ranting.

Documentation for realpath() at http://pubs.opengroup.org/onlinepubs/009695399/functions/realpath.html lists the errors that cause it to fail, but EEXIST is not one of them.

Why does realpath() fail in this way?

Examples of directory and file paths that cause the EEXIST error:

  • An absolute path to a directory: /alpha/bravo/charlie/delta
  • An absolute path to a file: /alpha/bravo/charlie/foo.txt
  • A relative path to a file: ../../charlie/foo.txt
  • A path to a file that has a extra dot in it: /alpha/bravo/Charlie/./foo.txt

But those examples are not definitive, because other files with those exact same patterns, and in the same directories, will succeeed.

There does not seem to be any rhyme or reason to what directory or file will cause the EEXIST error. The error typically only happens for the first file path I try to canonicalize, and then not for subsequent ones. However, I cannot kludge around it by merely trying to canonicalize that first file again; the error will keep happening just for it.

Program snippet:

#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>    // for PATH_MAX

using std;

string PathCanonicalize( string const & path )
{
  string result;

  char szResult[ PATH_MAX ];
  ::realpath( path.c_str(), szResult );
  if ( errno == EEXIST )
  {
    // Why?
    cerr << "realpath: error code " << errno << ", " << ::strerror( errno ) << ": '" << path << "'.  Of course the file exists!" << endl;

    result = path;
  }
  else if ( errno )
  {
    cerr << "realpath: error code " << errno << ", " << ::strerror( errno ) << ": '" << path << "'" << endl;

    result = path;
  }
  else
  {
    result = szResult;
  }

  return result;      
}
Mike Finch
  • 746
  • 1
  • 7
  • 20

2 Answers2

7

You should never, ever check errno without a specific reason.

Perhaps whatever internal operation realpath happened to do last failed with EEXIST. Or maybe errno happened to be EEXIST from some previous operation that failed and realpath didn't change it.

If this didn't cause realpath to fail, why do you care?

From your own link:

Upon successful completion, realpath() shall return a pointer to the resolved name. Otherwise, realpath() shall return a null pointer and set errno to indicate the error, and the contents of the buffer pointed to by resolved_name are undefined.

Notice it doesn't say that errno is set to anything in particular if realpath succeeds. So why are you checking errno before checking if realpath succeeded?

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • To add, not related to `realpath` directly but in general, a few rare functions don't have a return value that indicates an error and checking `errno` is the only way to determine whether the operation succeeded. But even for those functions, `errno` is not cleared on success. If you want to check `errno` for those functions, you need to explicitly set `errno` to zero before calling those functions. (As this answer rightly points out: don't do that except when the function is supposed to be used like that. `realpath` is allowed to set `errno` even if the only error is some internal error.) –  Jun 04 '15 at 17:30
  • Good point. I was checking for null a few edits ago, but I recombobulated it incorrectly when something else was going wrong. Thanks. – Mike Finch Jun 04 '15 at 17:32
  • @MikeFinch, hehe, thx for that word. :) (Found e.g. at the English Learners SX [sister site](https://ell.stackexchange.com/a/78358/4015): _"Discombobulate is the original nonsense word. It was an invented word. It means 'to destroy, tear apart, fragment'. So to re-combobulate is to recombine, make good what was destroyed, or to undo a bad action."_) – Sz. Feb 16 '22 at 20:02
-1

That is, you shouldn't think the realpath() been failed/succeeded by checking the 'errno' value, you should check its returned value against the NULL. If NULL returned, then you can check the 'errno' to dive the root cause. In other words, if realpath() succeed, it might not change/reset/clear the 'errno' value - assign 0 for it. Only when failed, it will set 'errno' as the error code.