From owner-freebsd-stable@FreeBSD.ORG Fri Jan 27 21:01:21 2006 Return-Path: X-Original-To: freebsd-stable@freebsd.org Delivered-To: freebsd-stable@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id DFA6516A420 for ; Fri, 27 Jan 2006 21:01:21 +0000 (GMT) (envelope-from dworkin@village.org) Received: from green-dome.village.org (green-dome.village.org [168.103.84.186]) by mx1.FreeBSD.org (Postfix) with ESMTP id 5409A43D46 for ; Fri, 27 Jan 2006 21:01:20 +0000 (GMT) (envelope-from dworkin@village.org) Received: from green-dome.village.org (localhost.village.org [127.0.0.1]) by green-dome.village.org (8.11.0/8.11.0) with ESMTP id k0RL1JB09430 for ; Fri, 27 Jan 2006 14:01:20 -0700 (MST) Message-Id: <200601272101.k0RL1JB09430@green-dome.village.org> To: freebsd-stable@freebsd.org From: dlm-fb@weaselfish.com Date: Fri, 27 Jan 2006 14:01:19 -0700 Subject: [5.4] mode bits changed by close(2) X-BeenThere: freebsd-stable@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Production branch of FreeBSD source code List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 27 Jan 2006 21:01:22 -0000 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 #include #include #include #include #include #include #include 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); }