Subject: bug#58446: 28.2; file-attribute-device-number returns a cons cell instead of an integer

From: Michael Albinus <[email-masked]>
Date: Wed, 12 Oct 2022 16:57:04 +0200

Lars Ingebrigtsen <[email-masked]> writes:

Hi Lars,

>> The device-number in file-attributes (nth 11) is a cons cell when called
>> on remote files (see tramp-get-device).
>> It would be great to notify this in Emacs documentation and how to
>> interpret this value e.g. (-1 . 1).
>> I noticed this in fixing a bug in dired-async.el, the code was using `=`
>> to compare the two values which is legitimate according to docstring.
>
> Hm...  would it be possible for Tramp to stash that information
> somewhere else?  It is (as you say) documented to be a number.

Yes, Tramp is out of the documented API in this case. And no, it cannot
be kept somewhere else.

Inodes are unique only on the given file system. That's why there are
device numbers, which identify a file system.

A remote file is not related to any (local) file system. That's why
ange-ftp returns -1 as device-number, always. This is good enough for
practical purposes, because it is different to any device number of a
file system visible on the local host. For the inode number, ange-ftp
uses a "virtual" one, which means for every accessed file it increases
its internal counter. By this, all files accessed by ange-ftp are
regarded as different if they differ in the remote file name, even if
they are equal.

Tramp did inherit this approach from ange-ftp, with the difference that
it uses virtual inode numbers only in case it couldn't determine the
real inode number. It has its own counter for virtual inode numbers,
divided from ange-ftp.

This had several problems. The device number of all connected remote
devices, be it via any Tramp method or via ange-ftp, was always the
same. Remote files were regarded as equal if just the inode number was
the same, be it a real inode number or a virtual number.

Therefore, in January 2003 (almost 20 years ago :-) the device number in
Tramp has changed. It is now a cons cell (-1 . REMOTE), with REMOTE
being a virtual device number generated by Tramp for very connection.

By this, the tupel (inode, device) still identifies a file uniquely,
being it local or remote.

As said, this change happend almost 20 years ago. Device numbers are
usually ignored by Emacs and external packages, I don't remember a
problem report for this over the years. Emacs core creates the
buffer-local variable buffer-file-number, which is indeed (INODE
DEVNUM). See basic-save-buffer:

	      (setq buffer-file-number
		    (nthcdr 10 (file-attributes buffer-file-name)))

And this variable is used for checking whether two files are equal, see
find-buffer-visiting:

                            (equal buffer-file-number number)

That's why it works also with Tramp's interpretation of device
numbers. And again, w/o blame over the years.

I tend to agree with Thierry: we shall document the status quo,
i.e. device numbers can be more than just an integer, and should be
compared via equal. The details of that structure don't matter I
believe.

And perhaps we could even add a helper function to extract the
information for a file's uniqueness:

(defsubst file-attribute-file-number (attributes)
  "The inode and device numbers in ATTRIBUTES returned by `file-attributes'.
It can be used to determine whether two files are identical."
  (nthcdr 10 attributes))

WDYT?

Best regards, Michael.