Skip site navigation (1)Skip section navigation (2)
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>