Mercurial > hg > octave-nkf > gnulib-hg
changeset 12314:e92d9385e4ca
chown: work around OpenBSD bug
chown(name,geteuid(),-1) failed to update the change time if
name was already owned by the current effective user. Work
around it by using chmod, which does not have this bug.
Unfortunately, lchown has the same bug, but OpenBSD 4.0 lacks
lchmod and lutimes, so there is no way to affect ctime without
unlinking and recreating the symlink, which is too dangerous.
* lib/chown.c (rpl_chown): Work around the bug.
* lib/lchown.c (rpl_lchown): Attempt to do likewise.
* m4/chown.m4 (gl_FUNC_CHOWN): Test for ctime bug.
* m4/lchown.m4 (gl_FUNC_LCHOWN): Check for lchmod.
* modules/chown (Depends-on): Add stdbool.
* modules/lchown (Depends-on): Likewise.
* doc/posix-functions/chown.texi (chown): Document the bug.
* doc/posix-functions/lchown.texi (lchown): Likewise.
* tests/test-lchown.h (test_chown): Relax test.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Mon, 16 Nov 2009 14:35:41 -0700 |
parents | 17f04ebac8e3 |
children | 8cb02ee162a1 |
files | ChangeLog doc/posix-functions/chown.texi doc/posix-functions/lchown.texi lib/chown.c lib/lchown.c m4/chown.m4 m4/lchown.m4 modules/chown modules/lchown tests/test-lchown.h |
diffstat | 10 files changed, 172 insertions(+), 35 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,16 @@ 2009-11-17 Eric Blake <ebb9@byu.net> + chown: work around OpenBSD bug + * lib/chown.c (rpl_chown): Work around the bug. + * lib/lchown.c (rpl_lchown): Attempt to do likewise. + * m4/chown.m4 (gl_FUNC_CHOWN): Test for ctime bug. + * m4/lchown.m4 (gl_FUNC_LCHOWN): Check for lchmod. + * modules/chown (Depends-on): Add stdbool. + * modules/lchown (Depends-on): Likewise. + * doc/posix-functions/chown.texi (chown): Document the bug. + * doc/posix-functions/lchown.texi (lchown): Likewise. + * tests/test-lchown.h (test_chown): Relax test. + mkstemp: avoid conflict with C++ keyword template * lib/mkdtemp.c (mkdtemp): Change spelling of template. * lib/mkostemp.c (mkostemp): Likewise.
--- a/doc/posix-functions/chown.texi +++ b/doc/posix-functions/chown.texi @@ -13,6 +13,10 @@ @code{chown("link-to-file/",uid,gid)}: FreeBSD 7.2, Solaris 9. @item +Some platforms fail to update the change time when at least one +argument was not -1, but no ownership changes resulted: +OpenBSD 4.0. +@item When passed an argument of -1, some implementations really set the owner user/group id of the file to this value, rather than leaving that id of the file alone.
--- a/doc/posix-functions/lchown.texi +++ b/doc/posix-functions/lchown.texi @@ -13,6 +13,11 @@ @code{lchown("link-to-file/",uid,gid)}: FreeBSD 7.2, Solaris 9. @item +Some platforms fail to update the change time when at least one +argument was not -1, but no ownership changes resulted. However, +without @code{lchmod}, the replacement only fixes this for non-symlinks: +OpenBSD 4.0. +@item This function is missing on some platforms; however, the replacement fails on symlinks if @code{chown} is supported, and fails altogether with @code{ENOSYS} otherwise:
--- a/lib/chown.c +++ b/lib/chown.c @@ -59,20 +59,29 @@ int rpl_chown (const char *file, uid_t uid, gid_t gid) { + struct stat st; + bool stat_valid = false; + int result; + +# if CHOWN_CHANGE_TIME_BUG + if (gid != (gid_t) -1 || uid != (uid_t) -1) + { + if (stat (file, &st)) + return -1; + stat_valid = true; + } +# endif + # if CHOWN_FAILS_TO_HONOR_ID_OF_NEGATIVE_ONE if (gid == (gid_t) -1 || uid == (uid_t) -1) { - struct stat file_stats; - /* Stat file to get id(s) that should remain unchanged. */ - if (stat (file, &file_stats)) + if (!stat_valid && stat (file, &st)) return -1; - if (gid == (gid_t) -1) - gid = file_stats.st_gid; - + gid = st.st_gid; if (uid == (uid_t) -1) - uid = file_stats.st_uid; + uid = st.st_uid; } # endif @@ -89,15 +98,18 @@ || (errno == EACCES && 0 <= (fd = open (file, O_WRONLY | open_flags)))) { - int result = fchown (fd, uid, gid); - int saved_errno = errno; + int saved_errno; + bool fchown_socket_failure; + + result = fchown (fd, uid, gid); + saved_errno = errno; - /* POSIX says fchown can fail with errno == EINVAL on sockets, - so fall back on chown in that case. */ - struct stat sb; - bool fchown_socket_failure = + /* POSIX says fchown can fail with errno == EINVAL on sockets + and pipes, so fall back on chown in that case. */ + fchown_socket_failure = (result != 0 && saved_errno == EINVAL - && fstat (fd, &sb) == 0 && S_ISFIFO (sb.st_mode)); + && fstat (fd, &st) == 0 + && (S_ISFIFO (st.st_mode) || S_ISSOCK (st.st_mode))); close (fd); @@ -113,15 +125,32 @@ # endif # if CHOWN_TRAILING_SLASH_BUG - { - size_t len = strlen (file); - struct stat st; - if (len && file[len - 1] == '/' && stat (file, &st)) - return -1; - } + if (!stat_valid) + { + size_t len = strlen (file); + if (len && file[len - 1] == '/' && stat (file, &st)) + return -1; + } # endif - return chown (file, uid, gid); + result = chown (file, uid, gid); + +# if CHOWN_CHANGE_TIME_BUG + if (result == 0 && stat_valid + && (uid == st.st_uid || uid == (uid_t) -1) + && (gid == st.st_gid || gid == (gid_t) -1)) + { + /* No change in ownership, but at least one argument was not -1, + so we are required to update ctime. Since chown succeeded, + we assume that chmod will do likewise. Fortunately, on all + known systems where a 'no-op' chown skips the ctime update, a + 'no-op' chmod still does the trick. */ + result = chmod (file, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO + | S_ISUID | S_ISGID | S_ISVTX)); + } +# endif + + return result; } #endif /* HAVE_CHOWN */
--- a/lib/lchown.c +++ b/lib/lchown.c @@ -23,6 +23,7 @@ #include <unistd.h> #include <errno.h> +#include <stdbool.h> #include <string.h> #include <sys/stat.h> @@ -69,10 +70,47 @@ int rpl_lchown (const char *file, uid_t uid, gid_t gid) { - size_t len = strlen (file); - if (len && file[len - 1] == '/') - return chown (file, uid, gid); - return lchown (file, uid, gid); + struct stat st; + bool stat_valid = false; + int result; + +# if CHOWN_CHANGE_TIME_BUG + if (gid != (gid_t) -1 || uid != (uid_t) -1) + { + if (lstat (file, &st)) + return -1; + stat_valid = true; + if (!S_ISLNK (st.st_mode)) + return chown (file, uid, gid); + } +# endif + +# if CHOWN_TRAILING_SLASH_BUG + if (!stat_valid) + { + size_t len = strlen (file); + if (len && file[len - 1] == '/') + return chown (file, uid, gid); + } +# endif + + result = lchown (file, uid, gid); + +# if CHOWN_CHANGE_TIME_BUG && HAVE_LCHMOD + if (result == 0 && stat_valid + && (uid == st.st_uid || uid == (uid_t) -1) + && (gid == st.st_gid || gid == (gid_t) -1)) + { + /* No change in ownership, but at least one argument was not -1, + so we are required to update ctime. Since lchown succeeded, + we assume that lchmod will do likewise. But if the system + lacks lchmod and lutimes, we are out of luck. Oh well. */ + result = lchmod (file, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO + | S_ISUID | S_ISGID | S_ISVTX)); + } +# endif + + return result; } #endif /* HAVE_LCHOWN */
--- a/m4/chown.m4 +++ b/m4/chown.m4 @@ -1,4 +1,4 @@ -# serial 21 +# serial 22 # Determine whether we need the chown wrapper. dnl Copyright (C) 1997-2001, 2003-2005, 2007, 2009 @@ -22,20 +22,27 @@ AC_REQUIRE([gl_FUNC_CHOWN_FOLLOWS_SYMLINK]) AC_CHECK_FUNCS_ONCE([chown fchown]) + dnl mingw lacks chown altogether. if test $ac_cv_func_chown = no; then HAVE_CHOWN=0 AC_LIBOBJ([chown]) else + dnl Some old systems treated chown like lchown. if test $gl_cv_func_chown_follows_symlink = no; then REPLACE_CHOWN=1 AC_LIBOBJ([chown]) fi + + dnl Some old systems tried to use uid/gid -1 literally. if test $ac_cv_func_chown_works = no; then AC_DEFINE([CHOWN_FAILS_TO_HONOR_ID_OF_NEGATIVE_ONE], [1], [Define if chown is not POSIX compliant regarding IDs of -1.]) REPLACE_CHOWN=1 AC_LIBOBJ([chown]) fi + + dnl Solaris 9 ignores trailing slash. + dnl FreeBSD 7.2 mishandles trailing slash on symlinks. AC_CACHE_CHECK([whether chown honors trailing slash], [gl_cv_func_chown_slash_works], [touch conftest.file && rm -f conftest.link @@ -52,10 +59,39 @@ rm -f conftest.link conftest.file]) if test "$gl_cv_func_chown_slash_works" != yes; then AC_DEFINE([CHOWN_TRAILING_SLASH_BUG], [1], - [Define if chown mishandles trailing slash.]) + [Define to 1 if chown mishandles trailing slash.]) REPLACE_CHOWN=1 AC_LIBOBJ([chown]) fi + + dnl OpenBSD fails to update ctime if ownership does not change. + AC_CACHE_CHECK([whether chown always updates ctime], + [gl_cv_func_chown_ctime_works], + [AC_RUN_IFELSE([AC_LANG_PROGRAM([[ +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +]], [[ struct stat st1, st2; + if (close (creat ("conftest.file", 0600))) return 1; + if (stat ("conftest.file", &st1)) return 2; + sleep (1); + if (chown ("conftest.file", st1.st_uid, st1.st_gid)) return 3; + if (stat ("conftest.file", &st2)) return 4; + if (st2.st_ctime <= st1.st_ctime) return 5; + ]])], + [gl_cv_func_chown_ctime_works=yes], + [gl_cv_func_chown_ctime_works=no], + [gl_cv_func_chown_ctime_works="guessing no"]) + rm -f conftest.file]) + if test "$gl_cv_func_chown_ctime_works" != yes; then + AC_DEFINE([CHOWN_CHANGE_TIME_BUG], [1], [Define to 1 if chown fails + to change ctime when at least one argument was not -1.]) + REPLACE_CHOWN=1 + AC_LIBOBJ([chown]) + fi + if test $REPLACE_CHOWN = 1 && test $ac_cv_func_fchown = no; then AC_LIBOBJ([fchown-stub]) fi
--- a/m4/lchown.m4 +++ b/m4/lchown.m4 @@ -1,4 +1,4 @@ -# serial 14 +# serial 15 # Determine whether we need the lchown wrapper. dnl Copyright (C) 1998, 2001, 2003-2007, 2009 Free Software @@ -9,18 +9,20 @@ dnl with or without modifications, as long as this notice is preserved. dnl From Jim Meyering. -dnl Provide lchown on systems that lack it, and work around trailing -dnl slash bugs on systems that have it. +dnl Provide lchown on systems that lack it, and work around bugs +dnl on systems that have it. AC_DEFUN([gl_FUNC_LCHOWN], [ AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) AC_REQUIRE([gl_FUNC_CHOWN]) + AC_CHECK_FUNCS_ONCE([lchmod]) AC_REPLACE_FUNCS([lchown]) if test $ac_cv_func_lchown = no; then HAVE_LCHOWN=0 - elif test "$gl_cv_func_chown_slash_works" != yes; then - dnl Trailing slash bugs in chown also occur in lchown. + elif test "$gl_cv_func_chown_slash_works" != yes \ + || test "$gl_cv_func_chown_ctime_works" != yes; then + dnl Trailing slash and ctime bugs in chown also occur in lchown. AC_LIBOBJ([lchown]) REPLACE_LCHOWN=1 fi
--- a/modules/chown +++ b/modules/chown @@ -8,9 +8,10 @@ Depends-on: open +stat +stdbool +sys_stat unistd -stat -sys_stat configure.ac: gl_FUNC_CHOWN
--- a/modules/lchown +++ b/modules/lchown @@ -9,6 +9,7 @@ chown errno lstat +stdbool sys_stat unistd
--- a/tests/test-lchown.h +++ b/tests/test-lchown.h @@ -75,6 +75,14 @@ # define getegid() (-1) #endif +#ifndef HAVE_LCHMOD +# define HAVE_LCHMOD 0 +#endif + +#ifndef CHOWN_CHANGE_TIME_BUG +# define CHOWN_CHANGE_TIME_BUG 0 +#endif + /* This file is designed to test lchown(n,o,g) and chownat(AT_FDCWD,n,o,g,AT_SYMLINK_NOFOLLOW). FUNC is the function to test. Assumes that BASE and ASSERT are already defined, and @@ -251,8 +259,10 @@ ASSERT (st1.st_uid == st2.st_uid); ASSERT (gids[0] == st2.st_gid); } - else + else if (!CHOWN_CHANGE_TIME_BUG || HAVE_LCHMOD) { + /* If we don't have lchmod, and lchown fails to change ctime, + then we can't test this part of lchown. */ struct stat l1; struct stat l2; ASSERT (stat (BASE "dir/file", &st1) == 0);