I've found an interesting little quirk that doesn't seem like it
should be working this way. If I:
fd = open(file)
write(fd, data)
fchmod(fd, 04711) /* or anything set{u,g}id */
close(fd)
The set{u,g}id bits get cleared by the close, but only if the amount
of data is not a multiple of the page size, and the target file is on
an nfs mount. It would seem to reduce the utility of fchmod()
somewhat....
A demonstration program is included below. I've verified this on a
fairly recently 5.4 client system, with both Linux and FBSD 5.4 NFS
servers. I don't have 6 or -current available to me at the moment.
There's the obvious work-around of doing a chmod() after the close,
but that has security implications.
Sticking an fsync() in between the fchmod() and the close() causes the
bits to be cleared as a side-effect of the fsync(). Doing another
fchmod() after the fsync() produces the final expected set{u,g}id
results even after the close. Unfortunately, fsync() is a rather
expensive operation.
Dworkin
===============================================================#include
<stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
char buf[25 * 1024];
int
main(int argc, char **argv)
{
int fd;
int count;
char *file;
struct stat sbuf;
if (argc != 2) {
fprintf(stderr, "usage: %s /file/name\n", argv[0]);
exit(1);
}
file = argv[1];
fd = open(file, O_RDWR | O_CREAT, 0666);
if (fd == -1) {
fprintf(stderr, "could not open %s: %s\n",
file, strerror(errno));
exit(1);
}
if (write(fd, buf, sizeof (buf)) == -1) {
fprintf(stderr, "could not write to %s: %s\n",
file, strerror(errno));
exit(1);
}
if (fstat(fd, &sbuf) == -1) {
fprintf(stderr, "could not fstat %s: %s\n",
file, strerror(errno));
exit(1);
}
printf("Initial permissions: 0%o\n", sbuf.st_mode & ALLPERMS);
if (fchmod(fd, 04711) == -1) {
fprintf(stderr, "could not fchmod %s: %s\n",
file, strerror(errno));
exit(1);
}
if (fstat(fd, &sbuf) == -1) {
fprintf(stderr, "could not fstat %s: %s\n",
file, strerror(errno));
exit(1);
}
printf("After write: 0%o\n", sbuf.st_mode & ALLPERMS);
if (close(fd) == -1) {
fprintf(stderr, "could not close %s: %s\n",
file, strerror(errno));
exit(1);
}
if (stat(file, &sbuf) == -1) {
fprintf(stderr, "could not stat %s: %s\n",
file, strerror(errno));
exit(1);
}
printf("After close: 0%o\n", sbuf.st_mode & ALLPERMS);
exit(0);
}