summaryrefslogtreecommitdiff
path: root/lib/lchown.c
diff options
context:
space:
mode:
authorEric Blake <ebb9@byu.net>2009-11-16 14:35:41 -0700
committerEric Blake <ebb9@byu.net>2009-11-17 22:30:00 -0700
commit84405cbc35207f178b1b50617254cb85ea803128 (patch)
treea6d4b75f1704db36681f0801e4e3d7c22fb89ebd /lib/lchown.c
parent83e045030538b7838367305c0b92c7c756c90651 (diff)
downloadgnulib-84405cbc35207f178b1b50617254cb85ea803128.tar.gz
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>
Diffstat (limited to 'lib/lchown.c')
-rw-r--r--lib/lchown.c46
1 files changed, 42 insertions, 4 deletions
diff --git a/lib/lchown.c b/lib/lchown.c
index 265c2f72ec..19eb9c6c57 100644
--- 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 @@ lchown (const char *file, uid_t uid, gid_t gid)
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 */