bug-coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

coreutils-6.0 on platforms without fchdir


From: Bruno Haible
Subject: coreutils-6.0 on platforms without fchdir
Date: Fri, 1 Sep 2006 14:15:20 +0200
User-agent: KMail/1.9.1

While compiling on BeOS, a more difficult problem shows up in the testsuite:
recursive "chown -R ..." and recursive removals "rm -r ..." fail with error
message "Function not implemented" (= strerror(ENOSYS)). The reason:

BeOS doesn't have an fchdir() function, nor fdopendir(), nor a /proc filesystem.
The fdopendir() function substitute can therefore never be expected to work.

I expect that the same problem exists on DJGPP and on older Unices.

You can also reproduce parts of the problems on Linux:
  $ ./configure
  Then comment out the definitions of HAVE_OPENAT, HAVE_FDOPENDIR, HAVE_FCHDIR,
  HAVE_FCHMOD in config.h. In lib/Makefile, add ${LIBOBJDIR}fchdir-stub$U.o to
  am__DEPENDENCIES_1 and to LIBOBJS. Also unmount /proc or make it inaccessible
  through "chmod 000 /proc".
  Then
  $ make
  $ make -k check
  chgrp/no-x fails
  chgrp/posix-H fails
  chgrp/basic fails
  chgrp/recurse fails
  chmod/no-x fails
  cp/link-preserve fails
  cp/same-file fails
  cp/dir-slash fails
  du/deref-args fails
  du/slash fails
  du/hard-link fails
  du/basic fails
  du/restore-wd fails
  du/exclude fails
  du/no-x fails
  du/trailing-slash fails
  du/deref fails
  du/two-args fails
  ln/misc fails
  misc/pwd-long fails
  mkdir/perm fails
  mv/part-hardlink fails
  mv/mv-special-1 fails
  mv/into-self-3 fails
  mv/hard-link-1 fails
  mv/part-symlink fails
  rm/empty-inacc fails
  rm/dot-rel fails
  rm/unread3 fails
  rm/cycle fails
  rm/rm1 fails
  rm/rm2 fails
  rm/rm3 fails
  rm/rm5 fails
  rm/r-1 fails
  rm/r-2 fails
  rm/r-3 fails
  rm/ir-1 fails
  rm/deep-1 fails
  du fails
  rmdir fails
  help-version fails

Attached you find a fix. It allows to build coreutils on platforms without
fchdir(). The tests that then still fail are:

  chgrp/no-x (just a slightly different error message)
  chmod/no-x (just a slightly different error message)
  du/no-x (just a slightly different error message)
  misc/pwd-long ("File name too long" errors, expected)
  rm/rm3 (asks different questions)
  rm/rm5 (asks different questions)

Note: I made no attempt to minimize the code execution path on platforms
without fchdir(); I tried to minimize the changes.


I'm not sure if openat_needs_fchdir () should be used here. Using it means to
bet that if the /proc based technique works once, it will work always. Hmm,
platforms without fchdir() probably don't have a /proc filesystem anyway...

2006-08-19  Bruno Haible  <address@hidden>

        * lib/fts.c (fts_open): If fchdir() does not exist, set FTS_NOCHDIR
        and unset FTS_CWDFD.
        (fts_stat): If fchdir() does not exist, use lstat instead of fstatat.
        * src/remove.c (AD_pop_and_chdir): If fchdir() does not exist, try
        opendir as an alternative to fdopendir.
        (lstatat, rmat, rmdirat): New macros.
        (write_protected_non_symlink, prompt): If fchdir() does not exist, use
        lstat instead of fstatat.
        (DO_UNLINK, DO_RMDIR): If fchdir() does not exist, use unlink or
        rmdir instead of unlinkat.
        (remove_entry): If fchdir() does not exist, use lstat instead of
        fstatat.
        (fd_to_subdirp): If either openat_permissive or fdopendir fails and
        fchdir() does not exist, try opendir + fstat as an alternative to
        fstat + fdopendir.
        (remove_cwd_entries, remove_dir): Use rmdirat instead of unlinkat.

--- lib/fts.c.bak       2006-07-11 20:22:27.000000000 +0200
+++ lib/fts.c   2006-08-20 19:33:49.000000000 +0200
@@ -292,6 +292,17 @@
                CLR(FTS_CWDFD);
        }
 
+#if !HAVE_FDOPENDIR && !HAVE_FCHDIR
+       if (!ISSET(FTS_NOCHDIR) && openat_needs_fchdir ()) {
+               /* If the filenames in /proc are not accessible and fchdir()
+                  is a stub, our fdopendir() substitute can not work reliably,
+                  therefore opendirat() does not work reliably either, hence
+                  it should not be called.  */
+               SET(FTS_NOCHDIR);
+               CLR(FTS_CWDFD);
+       }
+#endif
+
        /* Initialize fts_cwd_fd.  */
        sp->fts_cwd_fd = AT_FDCWD;
        if ( ISSET(FTS_CWDFD) && ! HAVE_OPENAT_SUPPORT)
@@ -1177,8 +1188,16 @@
                        p->fts_errno = saved_errno;
                        goto err;
                }
-       } else if (fstatat(sp->fts_cwd_fd, p->fts_accpath, sbp,
-                          AT_SYMLINK_NOFOLLOW)) {
+       }
+#if HAVE_OPENAT || HAVE_FCHDIR
+# define lstatat(Fd_cwd, Filename, Buf) \
+  fstatat (Fd_cwd, Filename, Buf, AT_SYMLINK_NOFOLLOW)
+#else
+/* If fchdir() is a stub, our fstatat() substitute cannot work reliably.  */
+# define lstatat(Fd_cwd, Filename, Buf) \
+  lstat (Filename, Buf)
+#endif
+       else if (lstatat(sp->fts_cwd_fd, p->fts_accpath, sbp)) {
                p->fts_errno = errno;
 err:           memset(sbp, 0, sizeof(struct stat));
                return (FTS_NS);
--- src/remove.c.bak    2006-07-05 08:56:17.000000000 +0200
+++ src/remove.c        2006-08-20 19:34:40.000000000 +0200
@@ -471,15 +471,24 @@
       *dirp = fdopendir (fd);
       if (*dirp == NULL)
        {
-         error (0, errno, _("FATAL: cannot return to .. from %s"),
-                quote (full_filename (".")));
+#if !HAVE_FDOPENDIR && !HAVE_FCHDIR
+         /* If the filenames in /proc are not accessible and fchdir() is a
+            stub, our fdopendir() substitute can not work reliably.  Need to
+            fall back to filename manipulations.  */
+         *dirp = opendir (full_filename ("."));
+#endif
+         if (*dirp == NULL)
+           {
+             error (0, errno, _("FATAL: cannot return to .. from %s"),
+                    quote (full_filename (".")));
 
-       close_and_next:;
-         close (fd);
+           close_and_next:;
+             close (fd);
 
-       next_cmdline_arg:;
-         free (*prev_dir);
-         longjmp (ds->current_arg_jumpbuf, 1);
+           next_cmdline_arg:;
+             free (*prev_dir);
+             longjmp (ds->current_arg_jumpbuf, 1);
+           }
        }
     }
   else
@@ -629,6 +638,38 @@
   return ! (top->unremovable && hash_lookup (top->unremovable, file));
 }
 
+#if HAVE_OPENAT || HAVE_FCHDIR
+
+# define lstatat(Fd_cwd, Filename, Buf) \
+  fstatat (Fd_cwd, Filename, Buf, AT_SYMLINK_NOFOLLOW)
+# define rmat(Fd_cwd, Filename) \
+  unlinkat (Fd_cwd, Filename, 0)
+# define rmdirat(Fd_cwd, Filename) \
+  unlinkat (Fd_cwd, Filename, AT_REMOVEDIR)
+
+#else
+
+/* If fchdir() is a stub, our fstatat() substitute cannot work reliably.  */
+
+# define lstatat(Fd_cwd, Filename, Buf)                                        
\
+  lstat ((Fd_cwd) == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (Filename)      \
+        ? Filename                                                     \
+        : full_filename (Filename),                                    \
+        Buf)
+
+/* If fchdir() is a stub, our unlinkat() substitute cannot work reliably.  */
+
+# define rmat(Fd_cwd, Filename)                                                
\
+  unlink ((Fd_cwd) == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (Filename)     \
+         ? Filename                                                    \
+         : full_filename (Filename))
+# define rmdirat(Fd_cwd, Filename)                                     \
+  rmdir ((Fd_cwd) == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (Filename)      \
+        ? Filename                                                     \
+        : full_filename (Filename))
+
+#endif
+
 /* Return true if DIR is determined to be an empty directory.  */
 static bool
 is_empty_dir (int fd_cwd, char const *dir)
@@ -670,7 +711,7 @@
                             struct stat **buf_p,
                             struct stat *buf)
 {
-  if (fstatat (fd_cwd, file, buf, AT_SYMLINK_NOFOLLOW) != 0)
+  if (lstatat (fd_cwd, file, buf) != 0)
     return false;
   *buf_p = buf;
   if (S_ISLNK (buf->st_mode))
@@ -767,7 +808,7 @@
       if (sbuf == NULL)
        {
          sbuf = &buf;
-         if (fstatat (fd_cwd, filename, sbuf, AT_SYMLINK_NOFOLLOW))
+         if (lstatat (fd_cwd, filename, sbuf))
            {
              /* lstat failed.  This happens e.g., with `rm '''.  */
              error (0, errno, _("cannot remove %s"),
@@ -859,7 +900,7 @@
 #define DO_UNLINK(Fd_cwd, Filename, X)                                 \
   do                                                                   \
     {                                                                  \
-      if (unlinkat (Fd_cwd, Filename, 0) == 0)                         \
+      if (rmat (Fd_cwd, Filename) == 0)                                        
\
        {                                                               \
          if ((X)->verbose)                                             \
            printf (_("removed %s\n"), quote (full_filename (Filename))); \
@@ -874,7 +915,7 @@
 #define DO_RMDIR(Fd_cwd, Filename, X)                  \
   do                                                   \
     {                                                  \
-      if (unlinkat (Fd_cwd, Filename, AT_REMOVEDIR) == 0) /* rmdir */ \
+      if (rmdirat (Fd_cwd, Filename) == 0)             \
        {                                               \
          if ((X)->verbose)                             \
            printf (_("removed directory: %s\n"),       \
@@ -974,7 +1015,7 @@
          else
            {
              struct stat sbuf;
-             if (fstatat (fd_cwd, filename, &sbuf, AT_SYMLINK_NOFOLLOW))
+             if (lstatat (fd_cwd, filename, &sbuf))
                {
                  if (errno == ENOENT && x->ignore_missing_files)
                    return RM_OK;
@@ -1042,13 +1083,15 @@
   int open_flags = O_RDONLY | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
   int fd_sub = openat_permissive (fd_cwd, f, open_flags, 0, cwd_errno);
 
+  if (fd_sub < 0)
+    goto failed;
+
   /* Record dev/ino of F.  We may compare them against saved values
      to thwart any attempt to subvert the traversal.  They are also used
      to detect directory cycles.  */
-  if (fd_sub < 0 || fstat (fd_sub, subdir_sb) != 0)
+  if (fstat (fd_sub, subdir_sb) != 0)
     {
-      if (0 <= fd_sub)
-       close_preserve_errno (fd_sub);
+      close_preserve_errno (fd_sub);
       return NULL;
     }
 
@@ -1063,10 +1106,32 @@
   if (subdir_dirp == NULL)
     {
       close_preserve_errno (fd_sub);
-      return NULL;
+      goto failed;
     }
 
   return subdir_dirp;
+
+ failed:
+#if !HAVE_FDOPENDIR && !HAVE_FCHDIR
+    /* If the filenames in /proc are not accessible and fchdir() is a stub,
+       our fdopendir() substitute can not work reliably.  Need to fall back
+       to filename manipulations.  */
+  subdir_dirp = opendir (fd_cwd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (f)
+                        ? f
+                        : full_filename (f));
+  if (subdir_dirp != NULL)
+    {
+      if (fstat (dirfd (subdir_dirp), subdir_sb) != 0)
+       {
+         int saved_errno = errno;
+         closedir (subdir_dirp);
+         errno = saved_errno;
+         return NULL;
+       }
+      return subdir_dirp;
+    }
+#endif
+  return NULL;
 }
 
 /* Remove entries in the directory open on DIRP
@@ -1166,7 +1231,7 @@
                    /* Upon fd_to_subdirp failure, try to remove F directly,
                       in case it's just an empty directory.  */
                    int saved_errno = errno;
-                   if (unlinkat (dirfd (*dirp), f, AT_REMOVEDIR) == 0)
+                   if (rmdirat (dirfd (*dirp), f) == 0)
                      status = RM_OK;
                    else
                      error (0, saved_errno,
@@ -1252,7 +1317,7 @@
          /* Upon fd_to_subdirp failure, try to remove DIR directly,
             in case it's just an empty directory.  */
          int saved_errno = errno;
-         if (unlinkat (fd_cwd, dir, AT_REMOVEDIR) == 0)
+         if (rmdirat (fd_cwd, dir) == 0)
            return RM_OK;
 
          error (0, saved_errno,
@@ -1326,7 +1391,7 @@
                goto closedir_and_return;
              }
 
-           if (unlinkat (fd, empty_dir, AT_REMOVEDIR) == 0)
+           if (rmdirat (fd, empty_dir) == 0)
              {
                if (x->verbose)
                  printf (_("removed directory: %s\n"),




reply via email to

[Prev in Thread] Current Thread [Next in Thread]