ange-ftp can't list directory pointed to by link on WUFTPD server (and perhaps others)

Skip to first unread message

Jonathan Kamens

Apr 6, 2000, 3:00:00 AM4/6/00
This bug report will be sent to the Free Software Foundation,
not to your local site managers!!
Please write in English, because the Emacs maintainers do not have
translators to read other languages for them.

In GNU Emacs 20.5.1 (i386-redhat-linux-gnu, X toolkit)
of Tue Feb 15 2000 on
configured using `configure --prefix=/usr --libexecdir=/usr/lib --sharedstatedir=/var --with-gcc --with-pop --with-x-toolkit i386-redhat-linux-gnu'

Please describe exactly what actions triggered the bug
and the precise symptoms of the bug:

emacs -q &
C-x C-f / RET

Note that /pub/ggi is actually a symbolic link to "packages/ggi".
However, instead of listing the contents of that directory, you'll

lrwxrwxrwx 1 root other 12 Dec 22 1998 /pub/ggi -> packages/ggi

The problem here is that WUFTPD, and perhaps some other FTP servers as
well, are "smart" about stripping "/" and "/." off of the end of file
names. Therefore, when the client sends "LIST /pub/ggi/" or "LIST
/pub/ggi/." to the server, it still gets back a listing of the link
instead of its contents (because the server turns the path into
"/pub/ggi" before calling ls).

This is arguably a bug in the FTP server, but that's another can of
worms, and in any case, Emacs needs to be able to talk to even buggy
FTP servers, because there's no way every WUFTPD server in the world
is going to be upgraded, at least not in the short term.

The patch below causes ange-ftp-ls to check if the path being listed
is in fact a symbolic link, and if so, chase links before actually
trying to list the file.

The second part of the patch, to ange-ftp-file-symlink-p, is necessary
to prevent an infinite loop. It asserts, essentially, "/ is never a
symbolic link."

This patch forces ange-ftp to try to list all the directories back to
"/" on the server, if they haven't already been read, whenever
ange-ftp-ls is called. This is because it can't determine if
/foo/bar/baz is a symlink without reading /foo/bar, and it can't read
/foo/bar without determining if it's a symlink, and it can't determine
if /foo/bar is a symlink without reading /foo, etc. This is bad, but
I believe that the current behavior is worse.

There are two heuristics which might be employed to make this
unnecessary in some cases, but I don't have time to implement either
of them; perhaps the maintainers of ange-ftp can:

1) Try to figure out from the FTP server connect message whether we're
talking to an FTP server with this problem (using a regexp to define
which FTP servers have this problem), and only behave as in the patch
if we are.

2) After doing an ls, check if the results contain only a single file
name whose name is identical to the name being listed and which is a
symbolic link. If so, do the ls again, this time behaving as in the

Jonathan Kamens

--- /usr/share/emacs/20.5/lisp/ange-ftp.el Thu Jul 1 15:39:37 1999
+++ /tmp/ange-ftp.el Thu Apr 6 15:29:06 2000
@@ -2448,12 +2448,16 @@
;; If parse is t, we assume that file is a directory. i.e. we only parse
;; full directory listings.
(let* ((ange-ftp-this-file (ange-ftp-expand-file-name file))
- (parsed (ange-ftp-ftp-name ange-ftp-this-file)))
+ (key (directory-file-name ange-ftp-this-file))
+ (ange-ftp-chased-file
+ (if (file-symlink-p key)
+ (file-chase-links key)
+ ange-ftp-this-file))
+ (parsed (ange-ftp-ftp-name ange-ftp-chased-file)))
(if parsed
(let* ((host (nth 0 parsed))
(user (nth 1 parsed))
(name (ange-ftp-quote-string (nth 2 parsed)))
- (key (directory-file-name ange-ftp-this-file))
(host-type (ange-ftp-host-type host user))
(dumb (memq host-type ange-ftp-dumb-host-types))
@@ -3263,17 +3267,19 @@
;; expand-file-name to stop loops when using a package that
;; redefines both file-symlink-p and expand-file-name.
(setq file (ange-ftp-expand-file-name file))
- (if (ange-ftp-ftp-name file)
- (let ((file-ent
- (ange-ftp-get-hash-entry
- (ange-ftp-get-file-part file)
- (ange-ftp-get-files (file-name-directory file)))))
- (if (stringp file-ent)
- (if (file-name-absolute-p file-ent)
- (ange-ftp-replace-name-component
- (file-name-directory file) file-ent)
- file-ent)))
- (ange-ftp-real-file-symlink-p file)))
+ (let ((parsed (ange-ftp-ftp-name file)))
+ (if parsed
+ (and (not (string= (nth 2 parsed) "/"))
+ (let ((file-ent
+ (ange-ftp-get-hash-entry
+ (ange-ftp-get-file-part file)
+ (ange-ftp-get-files (file-name-directory file)))))
+ (if (stringp file-ent)
+ (if (file-name-absolute-p file-ent)
+ (ange-ftp-replace-name-component
+ (file-name-directory file) file-ent)
+ file-ent))))
+ (ange-ftp-real-file-symlink-p file))))

(defun ange-ftp-file-exists-p (name)
(setq name (expand-file-name name))

Reply all
Reply to author
0 new messages