[Top][All Lists]
[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"),
- coreutils-6.0 on platforms without fchdir,
Bruno Haible <=