coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

realpath edge cases


From: Assaf Gordon
Subject: realpath edge cases
Date: Sun, 25 Jun 2017 00:02:53 +0000
User-agent: Mutt/1.5.23 (2014-03-12)

Hello all,

I've encountered two interesting (?) edge-cases with realpath.
Perhaps they warrent a change in documentation (or even error-checking code).

First,
Running 'realpath' in a deleted (unlinked) directory leads to confusing
error messages. Example:

   # In terminal 1
   mkdir /tmp/a
   cd /tmp/a

   # In another terminal
   rmdir /tmp/a

   # back in terminal 1
   $ realpath /foo
   /foo

   $ realpath foo
   realpath: foo: No such file or directory

   $ realpath ../foo
   realpath: ../foo: No such file or directory

   $ realpath -m foo
   realpath: foo: No such file or directory

I'd say this error message is confusing, because naively 'realpath' does
not require the last component to exist, and with '-m' it doesn't
require any component to exist. Yet the error message hints that
something that should exist doesn't.

The reason for the error message is this flow:
  src/realpath.c:process_path()
    src/realpath.c:realpath_canon()
      gnulib/lib/canonicalize.c:canonicalize_filename_mode()
        gnulib/lib/xgetcwd.c:xgetcwd()

Then xgetcwd fails (because the directory was deleted),
but despite the 'x' prefix, it only dies on ENOMEM,
not on ENOENT. It returns NULL, and the rest of the program
fails, but errno remains at ENOENT.

Not sure what would be a good gnulib fix without breaking
existing code that uses 'canonicalize_filename_mode'.

Of course, it is always recommended not to run programs
in a deleted directory... many programs will fail with confusing
errors.


Second,
`realpath --help` shows:
 --relative-to=FILE       print the resolved path relative to FILE
 --relative-base=FILE     print absolute paths unless paths below FILE

This hints (to a naive person like me :) ) that realpath will calculate
relative path to any file. But in fact, it assumes that the FILE
argument is always a directory, and with 'realpath -e' it indeeds
verifies it's a directory.

Example:

   $ mkdir /tmp/b
   $ cd /tmp/b
   $ touch 1
   $ realpath --relative-to=/tmp/b/1 2
   ../2
   $ realpath -e --relative-to=/tmp/b/1 2
   realpath: /tmp/b/1: Not a directory

One could argue it doesn't matter since 'realpath'
is mainly used to construct path strings in script.
But the resulting concatenated string is not a valid path:

   $ touch /tmp/b/1/../2
   touch: cannot touch '/tmp/b/1/../2': Not a directory

That is, if someone writes a shell script like:

   base=/foo/bar/file.txt
   rel=$(realpath --relative-to="$base" another.txt)

There might be assumption that "$base/$rel" should always work,
but it only works correctly if '$base' is a directory, despite
realpath not returning error message.

I assume that checking if the argument to --relative-{to,base}
is actually a directory when not using 'realpath -e' is not
acceptable as it will break existing behaviour.

So perhaps just update the usage text? (patch attached).
If not, then add a new 'gotcha' ?

regards,
- assaf

Attachment: 0001-realpath-improve-usage-description-for-relative-to-b.patch
Description: Text Data


reply via email to

[Prev in Thread] Current Thread [Next in Thread]