Date: Fri, 27 Jan 2006 14:01:19 -0700 From: dlm-fb@weaselfish.com To: freebsd-stable@freebsd.org Subject: [5.4] mode bits changed by close(2) Message-ID: <200601272101.k0RL1JB09430@green-dome.village.org>
next in thread | raw e-mail | index | archive | help
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); }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200601272101.k0RL1JB09430>