Error from substring-of-symlink in emms-source-file-directory-tree-internal

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Error from substring-of-symlink in emms-source-file-directory-tree-internal

Mike Kazantsev-2
Hello,


Having trouble with (args-out-of-range "..." 0 23) error when adding
directory to playlist, with somewhat weird directories-through-symlinks
configuration.

Tracked down this error to a line which causes it, but not entirely
sure if suggested fix would be appropriate.


How to reproduce it:

- Create this directory hierarchy:

    % mkdir -p /tmp/mp/mp-dir/mp-subdir/music-dir
    % ln -s ../mp/mp-dir /tmp/mp/mp-dir-link
    % cd /tmp/mp/mp-dir-link/mp-subdir/music-dir
    % mkdir ../music-dir-{A,B,C}
    % ln -s ../music-dir-{A,B,C} .

  I know it looks a bit convoluted, but a mock of actual configuration
  made here for convenience (with links to big mountpoint and "heap of
  unsorted stuff" dirs).

- (emms-source-directory-tree "/tmp/mp/mp-dir-link/mp-subdir/music-dir")

  Note that there should be one symlink in the middle of that
  source-dir (mp-dir-link), and symlinks to other dirs inside music-dir.


Result is this error:

  Debugger entered--Lisp error: (args-out-of-range "../music-dir-A" 0 39)
    emms-source-file-directory-tree-internal("/tmp/mp/mp-dir-link/mp-subdir/music-dir" "long-file-ext-regexp")
    emms-source-file-directory-tree("/tmp/mp/mp-dir-link/mp-subdir/music-dir" "long-file-ext-regexp")
    emms-source-directory-tree("/tmp/mp/mp-dir-link/mp-subdir/music-dir")
    eval((emms-source-directory-tree "/tmp/mp/mp-dir-link/mp-subdir/music-dir") nil)
    elisp--eval-last-sexp(nil)
    eval-last-sexp(nil)
    funcall-interactively(eval-last-sexp nil)
    call-interactively(eval-last-sexp nil nil)
    command-execute(eval-last-sexp)

I've snipped "long-file-ext-regexp" as it's very long and that's file
extension matching regexp, which afaict doesn't get used anyway -
there are no files in this mock-setup, only dirs.


Error happens in this part of emms-source-file-directory-tree-internal:

  (if
    (or (string-match "/\\.\\.?$" (car dirs))
      (let ((symlink (file-symlink-p (car dirs))))
        (and symlink
          (string-equal dir (substring symlink 0 (string-width dir))))))
    (setq dirs (cdr dirs))
    (setq dirs (condition-case nil ...)))

Specifically it's an error from (substring ...) where:

  (car dirs) = "/tmp/mp/mp-dir-link/mp-subdir/music-dir/music-dir-A"
  dir = "/tmp/mp/mp-dir-link/mp-subdir/music-dir"
  symlink = "../music-dir-A"
  (string-width dir) = 39
  (string-width symlink) = 14

So (string-width dir) goes beyond end of "symlink" string and produces
error above.

I believe purpose of that condition is to check for symlink referring
to directory itself and eliminate such loop, if that's the case.


One way to fix it can be to use:

  (substring symlink 0
    (min (string-width dir) (string-width symlink)))

But them what if it's a symlink to "music-dir-something-something" and
such (min ...) will cut it to "music-dir", resulting in t for
string-equal check, while symlink does not actually refer to same dir?

So this fix doesn't look right.

And I'm not entirely sure I'm understanding the problem correctly in
the first place, as it looks like there was a bug anyway, given that
any width can be longer than other, yet string was cut using only one
of them.


Given that it looks like something simple and maybe typical wrt
symlinks, thought I'd should ask for clarification on what this code
does and maybe what's the right way to fix it?


Also, I've seen this (args-out-of-range ...) error pop-up with
(substring ...) in other unexpected places for me, like completion
system suddenly stops working until emacs restart with no clear
backtrace (assuming it was optimized-out in .elc, same as here) and such.

So I suspect it's actually an upstream change in emacs that caused this
bug to surface - e.g. maybe (substring ...) didn't raise such error before.

But code looks bit strange to me anyway, due that
"music-dir-something-something cut to music-dir" case.

(emacs-version) gives:
  GNU Emacs 26.3 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.10) ...


I'm also a bit sleep-deprived and distracted atm, so maybe just can't
think right about the issue and it's actually a no-brainer (esp. if
it's indeed some kind of typical symlink check).


Thanks in advance for any info/clarification.
Cheers!

--
Mike Kazantsev // fraggod.net

Reply | Threaded
Open this post in threaded view
|

Re: Error from substring-of-symlink in emms-source-file-directory-tree-internal

Mike Kazantsev-2
On Fri, 24 Jan 2020 11:32:01 +0500
Mike Kazantsev <[hidden email]> wrote:

> One way to fix it can be to use:
>
>   (substring symlink 0
>     (min (string-width dir) (string-width symlink)))
>
> But them what if it's a symlink to "music-dir-something-something" and
> such (min ...) will cut it to "music-dir", resulting in t for
> string-equal check, while symlink does not actually refer to same dir?
>
> So this fix doesn't look right.
>
> And I'm not entirely sure I'm understanding the problem correctly in
> the first place, as it looks like there was a bug anyway, given that
> any width can be longer than other, yet string was cut using only one
> of them.

At the risk of biasing any potential info/solution some more, feel like
proper (tm) way to check for any kind of loops would be getting
(file-truename ...) for both paths and then checking if one is subdir
of another.

But it doesn't look like (file-truename ...) uses realpath(3) under the
hood, so not entirely sure if it works same as libc's realpath() and
can be used here.


--
Mike Kazantsev // fraggod.net