I've checked in another batch of changes to ls. This time, it's a cross between a feature addition and a bug fix.
Before this change, if you ran "ls DIR" and some file in DIR could not be stat'ed (or lstat'ed), then that file would not be listed in a long listing. However, it *would* be listed in some short listings (i.e., on a file system with d_type support) and with some combinations of options (any that don't require stat information -- type info is ok). You can even demonstrate the problem in a short listing, if you try hard enough. Now, for a stat-failed entry in a long listing, ls prints "---------" for the permission bits and a "?" for each requested field other than the name. Here's an example (run on a Linux-2.6.15 system): $ ls -lL /proc/6/task/6 ls: cannot access /proc/6/task/6/cwd: Permission denied ls: cannot access /proc/6/task/6/root: Permission denied ls: cannot access /proc/6/task/6/exe: Permission denied total 0 dr-xr-xr-x 2 root root 0 Jul 25 18:39 attr -r-------- 1 root root 0 Jul 25 18:39 auxv -r--r--r-- 1 root root 0 Jul 25 18:39 cmdline -r--r--r-- 1 root root 0 Jul 25 18:39 cpuset ?--------- ? ? ? ? ? cwd -r-------- 1 root root 0 Jul 25 18:39 environ ?--------- ? ? ? ? ? exe dr-x------ 2 root root 0 Jul 25 18:39 fd -r--r--r-- 1 root root 0 Jul 25 18:39 maps -rw------- 1 root root 0 Jul 25 18:39 mem -r--r--r-- 1 root root 0 Jul 25 18:39 mounts -rw-r--r-- 1 root root 0 Jul 25 18:39 oom_adj -r--r--r-- 1 root root 0 Jul 25 18:39 oom_score ?--------- ? ? ? ? ? root -rw------- 1 root root 0 Jul 25 18:39 seccomp -r--r--r-- 1 root root 0 Jul 25 18:39 smaps -r--r--r-- 1 root root 0 Jul 25 18:39 stat -r--r--r-- 1 root root 0 Jul 25 18:39 statm -r--r--r-- 1 root root 0 Jul 25 18:39 status -r--r--r-- 1 root root 0 Jul 25 18:39 wchan This is not the sort of thing you run into every day, but with SELinux, I hear that it happens more often. See the tests for examples of how to trigger the undesirable behavior. Here are the ChangeLog entries. I've included the net diff for src/ls.c below. See CVS for the individual deltas: 2006-07-25 Jim Meyering <[EMAIL PROTECTED]> * src/ls.c (gobble_file): When handling a stat-failed entry, print the entry name not the absolute_name -- to be consistent with the usual case. * tests/ls/stat-failed: Update accordingly. * src/ls.c: Add parens around the new uses of ?: ternary operator. * src/dircolors.hin: Mention that ORPHAN refers not just to dangling symlinks. Get --dired offsets right when handling stat-failed entries. * src/ls.c (print_long_format): Be careful to increment P by the appropriate amount, even when inode_number_width and nlink_width are zero. * tests/ls/stat-failed: Test for the above. * src/ls.c (gobble_file) [USE_ACL]: Don't use-uninitialized the have_acl member. That would happen for a directory with both a non-stat'able entry and one with an ACL. * src/ls.c (gobble_file): Make it so failure to stat a non-command-line file provokes an exit status of 1, not 0. Say "cannot access" rather than "cannot stat". * tests/ls/stat-failed: New file/test, for the above. * tests/ls/Makefile.am (TESTS): Add stat-failed. * tests/ls-2/tests (no-a-isdir-b): Update to reflect addition of "cannot access " to diagnostic. * src/ls.c: Declare stat_failed to be "bool", not "int" everywhere. * src/ls.c [enum filetype] (command_line): Remove member. Not needed. Replace all occurrences of "type == command_line" with the equivalent, "command_line_arg". * src/ls.c: Apply the stat-failed parts of Red Hat's coreutils-selinux.patch. From Ulrich Drepper. This makes it so files not mentioned on the command line (e.g., names read from a directory that *is* mentioned on the command line) for which stat fails are still listed. With --color, such files are colored just like ORPHANs (aka dangling symlinks). Index: src/ls.c =================================================================== RCS file: /fetish/cu/src/ls.c,v retrieving revision 1.422 retrieving revision 1.430 diff -u -p -r1.422 -r1.430 --- src/ls.c 21 Jul 2006 09:14:39 -0000 1.422 +++ src/ls.c 25 Jul 2006 16:35:59 -0000 1.430 @@ -172,6 +172,7 @@ struct fileinfo char *name; struct stat stat; + bool stat_failed; /* For symbolic link, name of the file linked to, otherwise zero. */ char *linkname; @@ -226,7 +227,8 @@ static bool file_ignored (char const *na static uintmax_t gobble_file (char const *name, enum filetype type, ino_t inode, bool command_line_arg, char const *dirname); -static void print_color_indicator (const char *name, mode_t mode, int linkok); +static void print_color_indicator (const char *name, mode_t mode, int linkok, + bool stat_failed); static void put_indicator (const struct bin_str *ind); static void add_ignore_pattern (const char *pattern); static void attach (char *dest, const char *dirname, const char *name); @@ -247,7 +249,7 @@ static int format_group_width (gid_t g); static void print_long_format (const struct fileinfo *f); static void print_many_per_line (void); static void print_name_with_quoting (const char *p, mode_t mode, - int linkok, + int linkok, bool stat_failed, struct obstack *stack); static void prep_non_filename_text (void); static void print_type_indicator (mode_t mode); @@ -2546,7 +2548,7 @@ gobble_file (char const *name, enum file || (dereference == DEREF_ALWAYS && (type == symbolic_link || type == unknown)))) || (format_needs_type - && (type == unknown + && (type == unknown || command_line_arg /* --indicator-style=classify (aka -F) requires that we stat each regular file to see if it's executable. */ @@ -2606,9 +2608,26 @@ gobble_file (char const *name, enum file break; } - if (err < 0) + f->stat_failed = (err < 0); + if (f->stat_failed) { - file_failure (command_line_arg, "%s", absolute_name); + /* Failure to stat a command line argument leads to + an exit status of 2. For other files, stat failure + provokes an exit status of 1. */ + file_failure (command_line_arg, + _("cannot access %s"), absolute_name); + if (command_line_arg) + return 0; + + f->filetype = type; + memset (&f->stat, '\0', sizeof (f->stat)); + +#if USE_ACL + f->have_acl = false; +#endif + f->name = xstrdup (name); + files_index++; + return 0; } @@ -3271,17 +3290,19 @@ format_user_or_group (char const *name, WIDTH. */ static void -format_user (uid_t u, int width) +format_user (uid_t u, int width, bool stat_failed) { - format_user_or_group (numeric_ids ? NULL : getuser (u), u, width); + format_user_or_group (stat_failed ? "?" : + (numeric_ids ? NULL : getuser (u)), u, width); } /* Likewise, for groups. */ static void -format_group (gid_t g, int width) +format_group (gid_t g, int width, bool stat_failed) { - format_user_or_group (numeric_ids ? NULL : getgroup (g), g, width); + format_user_or_group (stat_failed ? "?" : + (numeric_ids ? NULL : getgroup (g)), g, width); } /* Return the number of columns that format_user_or_group will print. */ @@ -3373,16 +3394,20 @@ print_long_format (const struct fileinfo { char hbuf[INT_BUFSIZE_BOUND (uintmax_t)]; sprintf (p, "%*s ", inode_number_width, - umaxtostr (f->stat.st_ino, hbuf)); - p += inode_number_width + 1; + f->stat_failed ? "?" : umaxtostr (f->stat.st_ino, hbuf)); + /* 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); } if (print_block_size) { char hbuf[LONGEST_HUMAN_READABLE + 1]; char const *blocks = - human_readable (ST_NBLOCKS (f->stat), hbuf, human_output_opts, - ST_NBLOCKSIZE, output_block_size); + (f->stat_failed + ? "?" + : human_readable (ST_NBLOCKS (f->stat), hbuf, human_output_opts, + ST_NBLOCKSIZE, output_block_size)); int pad; for (pad = block_size_width - mbswidth (blocks, 0); 0 < pad; pad--) *p++ = ' '; @@ -3396,9 +3421,12 @@ print_long_format (const struct fileinfo { char hbuf[INT_BUFSIZE_BOUND (uintmax_t)]; sprintf (p, "%s %*s ", modebuf, nlink_width, - umaxtostr (f->stat.st_nlink, hbuf)); + f->stat_failed ? "?" : umaxtostr (f->stat.st_nlink, hbuf)); } - p += sizeof modebuf - 2 + any_has_acl + 1 + nlink_width + 1; + /* Increment by strlen (p) here, rather than by, e.g., + sizeof modebuf - 2 + any_has_acl + 1 + nlink_width + 1. + The latter is wrong when nlink_width is zero. */ + p += strlen (p); DIRED_INDENT (); @@ -3407,18 +3435,19 @@ print_long_format (const struct fileinfo DIRED_FPUTS (buf, stdout, p - buf); if (print_owner) - format_user (f->stat.st_uid, owner_width); + format_user (f->stat.st_uid, owner_width, f->stat_failed); if (print_group) - format_group (f->stat.st_gid, group_width); + format_group (f->stat.st_gid, group_width, f->stat_failed); if (print_author) - format_user (f->stat.st_author, author_width); + format_user (f->stat.st_author, author_width, f->stat_failed); p = buf; } - if (S_ISCHR (f->stat.st_mode) || S_ISBLK (f->stat.st_mode)) + if (!f->stat_failed + && (S_ISCHR (f->stat.st_mode) || S_ISBLK (f->stat.st_mode))) { char majorbuf[INT_BUFSIZE_BOUND (uintmax_t)]; char minorbuf[INT_BUFSIZE_BOUND (uintmax_t)]; @@ -3436,8 +3465,10 @@ print_long_format (const struct fileinfo { char hbuf[LONGEST_HUMAN_READABLE + 1]; char const *size = - human_readable (unsigned_file_size (f->stat.st_size), - hbuf, human_output_opts, 1, file_output_block_size); + (f->stat_failed + ? "?" + : human_readable (unsigned_file_size (f->stat.st_size), + hbuf, human_output_opts, 1, file_output_block_size)); int pad; for (pad = file_size_width - mbswidth (size, 0); 0 < pad; pad--) *p++ = ' '; @@ -3450,7 +3481,7 @@ print_long_format (const struct fileinfo s = 0; *p = '\1'; - if (when_local) + if (!f->stat_failed && when_local) { time_t six_months_ago; bool recent; @@ -3497,15 +3528,17 @@ print_long_format (const struct fileinfo print it as a huge integer number of seconds. */ char hbuf[INT_BUFSIZE_BOUND (intmax_t)]; sprintf (p, "%*s ", long_time_expected_width (), - (TYPE_SIGNED (time_t) - ? imaxtostr (when, hbuf) - : umaxtostr (when, hbuf))); + (f->stat_failed + ? "?" + : (TYPE_SIGNED (time_t) + ? imaxtostr (when, hbuf) + : umaxtostr (when, hbuf)))); p += strlen (p); } DIRED_FPUTS (buf, stdout, p - buf); print_name_with_quoting (f->name, FILE_OR_LINK_MODE (f), f->linkok, - &dired_obstack); + f->stat_failed, &dired_obstack); if (f->filetype == symbolic_link) { @@ -3513,7 +3546,7 @@ print_long_format (const struct fileinfo { DIRED_FPUTS_LITERAL (" -> ", stdout); print_name_with_quoting (f->linkname, f->linkmode, f->linkok - 1, - NULL); + f->stat_failed, NULL); if (indicator_style != none) print_type_indicator (f->linkmode); } @@ -3695,10 +3728,10 @@ quote_name (FILE *out, const char *name, static void print_name_with_quoting (const char *p, mode_t mode, int linkok, - struct obstack *stack) + bool stat_failed, struct obstack *stack) { if (print_with_color) - print_color_indicator (p, mode, linkok); + print_color_indicator (p, mode, linkok, stat_failed); if (stack) PUSH_CURRENT_DIRED_POS (stack); @@ -3746,7 +3779,8 @@ print_file_name_and_frills (const struct human_readable (ST_NBLOCKS (f->stat), buf, human_output_opts, ST_NBLOCKSIZE, output_block_size)); - print_name_with_quoting (f->name, FILE_OR_LINK_MODE (f), f->linkok, NULL); + print_name_with_quoting (f->name, FILE_OR_LINK_MODE (f), f->linkok, + f->stat_failed, NULL); if (indicator_style != none) print_type_indicator (f->stat.st_mode); @@ -3787,7 +3821,8 @@ print_type_indicator (mode_t mode) } static void -print_color_indicator (const char *name, mode_t mode, int linkok) +print_color_indicator (const char *name, mode_t mode, int linkok, + bool stat_failed) { int type = C_FILE; struct color_ext_type *ext; /* Color extension */ @@ -3826,6 +3861,8 @@ print_color_indicator (const char *name, type = C_CHR; else if (S_ISDOOR (mode)) type = C_DOOR; + else if (stat_failed) + type = C_ORPHAN; if (type == C_FILE) { _______________________________________________ Bug-coreutils mailing list Bug-coreutils@gnu.org http://lists.gnu.org/mailman/listinfo/bug-coreutils