diff -urNp coreutils-7.6-orig/doc/coreutils.texi coreutils-7.6/doc/coreutils.texi --- coreutils-7.6-orig/doc/coreutils.texi 2009-09-22 15:12:55.000000000 +0200 +++ coreutils-7.6/doc/coreutils.texi 2009-10-02 16:09:57.000000000 +0200 @@ -6114,7 +6114,8 @@ Exit status: specified as a command line argument. This happens when listing a directory in which entries are actively being removed or renamed.) 2 serious trouble (e.g., memory exhausted, invalid option or failure - to access file or directory specified as a command line argument) + to access file or directory specified as a command line argument + or a directory loop) @end display Also see @ref{Common options}. diff -urNp coreutils-7.6-orig/src/ls.c coreutils-7.6/src/ls.c --- coreutils-7.6-orig/src/ls.c 2009-09-22 15:12:55.000000000 +0200 +++ coreutils-7.6/src/ls.c 2009-10-02 16:19:54.000000000 +0200 @@ -2494,6 +2494,7 @@ print_dir (char const *name, char const error (0, 0, _("%s: not listing already-listed directory"), quotearg_colon (name)); closedir (dirp); + set_exit_status (true); return; } @@ -3582,6 +3583,18 @@ format_user_width (uid_t u) return format_user_or_group_width (numeric_ids ? NULL : getuser (u), u); } +/* Return a pointer to a formatted version of F->stat.st_ino, + possibly using buffer, BUF, of length BUFLEN, which must be at least + INT_BUFSIZE_BOUND (uintmax_t) bytes. */ +static char * +format_inode (char *buf, size_t buflen, const struct fileinfo *f) +{ + assert (INT_BUFSIZE_BOUND (uintmax_t) <= buflen); + return (f->stat_ok && f->stat.st_ino != NOT_AN_INODE_NUMBER + ? umaxtostr (f->stat.st_ino, buf) + : (char *) "?"); +} + /* Likewise, for groups. */ static int @@ -3712,9 +3725,7 @@ print_long_format (const struct fileinfo { char hbuf[INT_BUFSIZE_BOUND (uintmax_t)]; sprintf (p, "%*s ", inode_number_width, - (f->stat.st_ino == NOT_AN_INODE_NUMBER - ? "?" - : umaxtostr (f->stat.st_ino, hbuf))); + format_inode (hbuf, sizeof hbuf, f)); /* Increment by strlen (p) here, rather than by inode_number_width + 1. The latter is wrong when inode_number_width is zero. */ p += strlen (p); @@ -4104,12 +4115,13 @@ print_file_name_and_frills (const struct if (print_inode) printf ("%*s ", format == with_commas ? 0 : inode_number_width, - umaxtostr (f->stat.st_ino, buf)); + format_inode (buf, sizeof buf, f)); if (print_block_size) printf ("%*s ", format == with_commas ? 0 : block_size_width, - human_readable (ST_NBLOCKS (f->stat), buf, human_output_opts, - ST_NBLOCKSIZE, output_block_size)); + ! f->stat_ok ? "?" + : human_readable (ST_NBLOCKS (f->stat), buf, human_output_opts, + ST_NBLOCKSIZE, output_block_size)); size_t width = print_name_with_quoting (f->name, FILE_OR_LINK_MODE (f), f->linkok, f->stat_ok, f->filetype, @@ -4320,9 +4332,10 @@ length_of_file_name_and_frills (const st if (print_block_size) len += 1 + (format == with_commas - ? strlen (human_readable (ST_NBLOCKS (f->stat), buf, - human_output_opts, ST_NBLOCKSIZE, - output_block_size)) + ? strlen (! f->stat_ok ? "?" + : human_readable (ST_NBLOCKS (f->stat), buf, + human_output_opts, ST_NBLOCKSIZE, + output_block_size)) : block_size_width); quote_name (NULL, f->name, filename_quoting_options, &name_width); diff -urNp coreutils-7.6-orig/tests/ls/dangle coreutils-7.6/tests/ls/dangle --- coreutils-7.6-orig/tests/ls/dangle 2009-09-01 13:01:16.000000000 +0200 +++ coreutils-7.6/tests/ls/dangle 2009-10-02 16:21:06.000000000 +0200 @@ -26,6 +26,10 @@ fi ln -s no-such-file dangle || framework_failure mkdir -p dir/sub || framework_failure ln -s dir slink-to-dir || framework_failure +mkdir d || framework_failure +ln -s no-such d/dangle || framework_failure +printf '? dangle\n' > subdir_Li_exp || framework_failure +printf 'total 0\n? dangle\n' > subdir_Ls_exp || framework_failure fail=0 @@ -50,4 +54,14 @@ EOF compare out exp || fail=1 +# Ensure that ls -Li prints "?" as the inode of a dangling symlink. +rm -f out +ls -Li d > out 2>/dev/null && fail=1 +compare out subdir_Li_exp || fail=1 + +# Ensure that ls -Ls prints "?" as the allocation of a dangling symlink. +rm -f out +ls -Ls d > out 2>/dev/null && fail=1 +compare out subdir_Ls_exp || fail=1 + Exit $fail diff -urNp coreutils-7.6-orig/tests/ls/infloop coreutils-7.6/tests/ls/infloop --- coreutils-7.6-orig/tests/ls/infloop 2009-09-01 13:01:16.000000000 +0200 +++ coreutils-7.6/tests/ls/infloop 2009-10-02 16:12:11.000000000 +0200 @@ -1,6 +1,7 @@ #!/bin/sh # show that the following no longer makes ls infloop # mkdir loop; cd loop; ln -s ../loop sub; ls -RL +# Also ensure ls exits with status = 2 in that case. # Copyright (C) 2001-2002, 2004, 2006-2009 Free Software Foundation, Inc. @@ -27,21 +28,22 @@ fi mkdir loop || framework_failure ln -s ../loop loop/sub || framework_failure -fail=0 - -ls -RL loop 2>err | head -n 7 > out -# With an inf-looping ls, out will contain these 7 lines: -cat < bad +cat <<\EOF > exp-out || framework_failure loop: sub +EOF -loop/sub: -sub - -loop/sub/sub: +cat <<\EOF > exp-err || framework_failure +ls: loop/sub: not listing already-listed directory EOF -# Make sure we don't get the "bad" output. -compare out bad > /dev/null 2>&1 && fail=1 +fail=0 + +timeout 1 ls -RL loop 2>err > out +# Ensure that ls exits with status 2 upon detecting a cycle +test $? = 2 || fail=1 + +compare err exp-err || fail=1 +compare out exp-out || fail=1 Exit $fail