probe-file returns NIL on existing file

Bug #2089439 reported by Guilherme Janczak
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
SBCL
New
Undecided
Unassigned

Bug Description

On Unix, if I have a file inside a directory and I have no execute permission to that directory, the file exists, but sbcl's probe-file returns nil. Internally, it gets an access denied error from the OS and still returns nil.

You can create such a directory as follows:
```sh
mkdir /tmp/no-exec-dir
touch /tmp/no-exec-dir/file
chmod a-x /tmp/no-exec-dir
```

Example sbcl run after setting up ("*" is the sbcl REPL):
```
* (probe-file "/tmp/no-exec-dir/file")
NIL
```

Here's a C program that sets up a directory with no execute permission and does the C equivalent of probe-file (access/faccessat in POSIX C):
```c
#define _POSIX_C_SOURCE 200809L

#include <sys/stat.h>

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int
main(void)
{
 const char dir_path[] = "/tmp/no-exec-dir", file_path[] = "file";
 int dir = -1, file = -1, ret;

 if ((ret = mkdir(dir_path, 0777)) == -1)
  goto err;
 dir = open(dir_path, O_CREAT|O_RDONLY|O_DIRECTORY, (mode_t)0777);
 if (dir == -1)
  goto err;
 file = openat(dir, file_path, O_CREAT|O_TRUNC|O_WRONLY, (mode_t)0777);
 if (file == -1)
  goto err;
 if (fchmod(dir, 0666 /* Remove exec permission. */) == -1)
  goto err;

 ret = faccessat(dir, file_path, F_OK, 0);
 printf("access to existing file returned %d\nerrno = %d: %s\n\n", ret,
     errno, strerror(errno));

 ret = faccessat(dir, file_path, F_OK, 0);
 printf("access to non-existing file returned %d\nerrno = %d: %s\n",
     ret, errno, strerror(errno));

 return 0;
err:
 fchmod(dir, 0777 /* Restore exec permission. */);
 unlinkat(dir, file_path, 0);
 rmdir(dir_path);
 return 1;
}
```

Example run:
```console
$ ./a.out
access to existing file returned -1
errno = 13: Permission denied

access to non-existing file returned -1
errno = 13: Permission denied
```

Whether the file exists or not, the OS returns an error, and it's impossible for the program to tell the difference. sbcl's probe-file claims the file doesn't exist in such a case which might be untrue, it should raise a file-error instead.

description: updated
Revision history for this message
Stas Boukarev (stassats) wrote :

Where does it say that this should happen?

Revision history for this message
Richard M Kreuter (kreuter) wrote :
Download full text (4.7 KiB)

Although I think it would be reasonable for an implementation's PROBE-FILE to error as Guilherme suggests, I doubt SBCL's user population would enjoy the incompatible change, and the de facto tradition across most implementations has been not to error in PROBE-FILE.

Guilherme: practically speaking, I would propose either implementing a routine with the precise semantics you need using sb-posix or /maybe/ wrapping TRUENAME in something like

```
(defun probe-file* (pathname)
  (handler-bind
      ((file-error (lambda (e)
                     #+sbcl ;; probably elsewhere, too
                     (when
                         ;; This is kind of a gross way to understand what the
                         ;; error is, it seems that no implementation offers
                         ;; any principled interface to its file errors.
                         (search "No such file or directory" (princ-to-string e)
                                 ;; Pure speculative paranoia.
                                 :test #'char-equal)
                       ;; Unix happens not to distinguish whether what's missing
         ;; is the last component in a pathname or any apparent
                       ;; directory. You could do some extra test for the
                       ;; directory here, if that matters to you.
                       (return-from probe-file* nil)))))
    (truename pathname)))
```

But it's really not that practical to rely on any implementation's Chapter 20 operators.

Anyhow, here's how I understand the matter. The Exceptional Situation language in PROBE-FILE (& other Chapter 20 operators) came from the Issue, FILE-OPEN-ERROR:

https://www.lispworks.com/documentation/HyperSpec/Issues/iss157_w.htm

The Issue writeup says the circumstances under consideration include "read- or write-protected [files]... directory components in the pathname that don't really name directories, or invalid symbolic links". This would include Guilherme's scenario (provided directories are files, as is the case on Unix). Its Proposal (1) says the standard was to include a statement that "an error of type FILE-ERROR is signaled if the file system cannot perform the requested operation". (Those words only ended up in the dictionary entries for PROBE-FILE, FILE-WRITE-DATE, FILE-AUTHOR, TRUENAME, and LOAD. The Exceptional Situations for DIRECTORY, ED, DRIBBLE, OPEN, WITH-OPEN-FILE, and COMPILE-FILE are worded somewhat differently.) So ISTM the standard was "supposed to mean" that PROBE-FILE would signal an error in the case Guilherme is considering (and others).

However, even if the standard was intended to mean that, erroring would probably have been an incompatible change for most existing implementation of the time: CLtL1 mandated file-related errors only for RENAME-FILE, DELETE-FILE, OPEN, WITH-OPEN-FILE, and LOAD, and looking at available early implementation sources, it seems that early implementors decided that PROBE-FILE should not error, for whatever reasons. The standardization process aspired to be as backwardly compatible as possible, and so it might have come as a surprise to implementors & users of the time if things changed t...

Read more...

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.