If I get a file handle with let file = FileHandle(forReadingAtPath: "/path/to/file.json")
, is there a way to retrieve the filename "/path/to/file.json" back from the file
instance?
There's no portable way to do this, but file
provides a fileDescriptor
which can be mapped to a filename on macOS using fcntl
with F_GETPATH
:
var buffer = [CChar](repeating: 0, count: 100)
_ = fcntl(file.fileDescriptor, F_GETPATH, &buffer)
print(String(cString: buffer))
In general you can't do that. For one thing there's no guarantee that there still is a path to it. (The file could have been unlinked after it was opened.) Also, which path should be returned if the file is hardlinked to multiple names? Due to these issues the POSIX standard provides no mechanism for this.
Darwin does have the nonstandard F_GETPATH
option to fcntl()
but it's not safe to depend on it. It does seem to track files being renamed, but it returns stale information when the file is unlinked.
Example:
import Darwin
var fd = open("/tmp/foo", O_RDWR|O_CREAT, 438)
var buffer = [CChar](repeating: 0, count: Int(MAXPATHLEN))
_ = fcntl(fd, F_GETPATH, &buffer)
print("The path is", String(cString: buffer))
rename("/tmp/foo","/tmp/bar")
_ = fcntl(fd, F_GETPATH, &buffer)
print("Now the path is", String(cString: buffer))
unlink("/tmp/bar")
_ = fcntl(fd, F_GETPATH, &buffer)
print("And now that the file has been deleted, fcntl() still says it's at", String(cString: buffer))
close(fd)
Output:
The path is /private/tmp/foo
Now the path is /private/tmp/bar
And now that the file has been deleted, fcntl() still says it's at /private/tmp/bar
On Linux you can call readlink()
on /proc/self/fd/N
where N is the number of the file descriptor. It tracks file renaming and when a file is unlinked it appends " (deleted)" to the path (at least on the kernel version I tested).
Some other operating systems have their own mechanisms to do this but not all of them do. Reliability and behavior in edge cases varies.