exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / lchown.c
blob03836bcb3b2f52bd2fc9ed14d857d8928880d8ef
1 /* Provide a stub lchown function for systems that lack it.
3 Copyright (C) 1998-1999, 2002, 2004, 2006-2007, 2009-2024 Free Software
4 Foundation, Inc.
6 This file is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 This file is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with this program. If not, see <https://www.gnu.org/licenses/>. */
19 /* written by Jim Meyering */
21 #include <config.h>
23 #include <unistd.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <sys/stat.h>
29 #if !HAVE_LCHOWN
31 /* If the system chown does not follow symlinks, we don't want it
32 replaced by gnulib's chown, which does follow symlinks. */
33 # if CHOWN_MODIFIES_SYMLINK /* native Windows and some very old platforms */
34 # undef chown
35 # endif
37 /* Work just like chown, except when FILE is a symbolic link.
38 In that case, set errno to EOPNOTSUPP and return -1.
39 But if autoconf tests determined that chown modifies
40 symlinks, then just call chown. */
42 int
43 lchown (const char *file, uid_t uid, gid_t gid)
45 # if HAVE_CHOWN
46 # if ! CHOWN_MODIFIES_SYMLINK
47 char readlink_buf[1];
49 if (0 <= readlink (file, readlink_buf, sizeof readlink_buf))
51 errno = EOPNOTSUPP;
52 return -1;
54 # endif
56 return chown (file, uid, gid);
58 # else /* !HAVE_CHOWN */
59 errno = ENOSYS;
60 return -1;
61 # endif
64 #else /* HAVE_LCHOWN */
66 # undef lchown
68 /* Work around trailing slash bugs in lchown. */
69 int
70 rpl_lchown (const char *file, uid_t uid, gid_t gid)
72 bool stat_valid = false;
73 int result;
75 # if CHOWN_CHANGE_TIME_BUG
76 struct stat st;
78 if (gid != (gid_t) -1 || uid != (uid_t) -1)
80 if (lstat (file, &st))
81 return -1;
82 stat_valid = true;
83 if (!S_ISLNK (st.st_mode))
84 return chown (file, uid, gid);
86 # endif
88 # if CHOWN_TRAILING_SLASH_BUG
89 if (!stat_valid)
91 size_t len = strlen (file);
92 if (len && file[len - 1] == '/')
93 return chown (file, uid, gid);
95 # endif
97 result = lchown (file, uid, gid);
99 # if CHOWN_CHANGE_TIME_BUG && HAVE_LCHMOD
100 if (result == 0 && stat_valid
101 && (uid == st.st_uid || uid == (uid_t) -1)
102 && (gid == st.st_gid || gid == (gid_t) -1))
104 /* No change in ownership, but at least one argument was not -1,
105 so we are required to update ctime. Since lchown succeeded,
106 we assume that lchmod will do likewise. But if the system
107 lacks lchmod and lutimes, we are out of luck. Oh well. */
108 result = lchmod (file, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO
109 | S_ISUID | S_ISGID | S_ISVTX));
111 # endif
113 return result;
116 #endif /* HAVE_LCHOWN */