Blame mtab.c

Packit Service 09cdfc
/*
Packit Service 09cdfc
 * mtab locking routines for use with mount.cifs and umount.cifs
Packit Service 09cdfc
 * Copyright (C) 2008 Jeff Layton (jlayton@samba.org)
Packit Service 09cdfc
 *
Packit Service 09cdfc
 * This program is free software; you can redistribute it and/or modify
Packit Service 09cdfc
 * it under the terms of the GNU General Public License as published by
Packit Service 09cdfc
 * the Free Software Foundation; either version 3 of the License, or
Packit Service 09cdfc
 * (at your option) any later version.
Packit Service 09cdfc
 *
Packit Service 09cdfc
 * This program is distributed in the hope that it will be useful,
Packit Service 09cdfc
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 09cdfc
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 09cdfc
 * GNU General Public License for more details.
Packit Service 09cdfc
 *
Packit Service 09cdfc
 * You should have received a copy of the GNU General Public License
Packit Service 09cdfc
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit Service 09cdfc
*/
Packit Service 09cdfc
Packit Service 09cdfc
/*
Packit Service 09cdfc
 * This code was copied from the util-linux-ng sources and modified:
Packit Service 09cdfc
 *
Packit Service 09cdfc
 * git://git.kernel.org/pub/scm/utils/util-linux-ng/util-linux-ng.git
Packit Service 09cdfc
 *
Packit Service 09cdfc
 * ...specifically from mount/fstab.c. That file has no explicit license. The
Packit Service 09cdfc
 * "default" license for anything in that tree is apparently GPLv2+, so I
Packit Service 09cdfc
 * believe we're OK to copy it here.
Packit Service 09cdfc
 *
Packit Service 09cdfc
 * Jeff Layton <jlayton@samba.org> 
Packit Service 09cdfc
 */
Packit Service 09cdfc
Packit Service 09cdfc
#include <unistd.h>
Packit Service 09cdfc
#include <errno.h>
Packit Service 09cdfc
#include <stdio.h>
Packit Service 09cdfc
#include <sys/time.h>
Packit Service 09cdfc
#include <sys/stat.h>
Packit Service 09cdfc
#include <time.h>
Packit Service 09cdfc
#include <fcntl.h>
Packit Service 09cdfc
#include <mntent.h>
Packit Service 09cdfc
#include <stdlib.h>
Packit Service 09cdfc
#include <signal.h>
Packit Service 09cdfc
#include <paths.h>
Packit Service 09cdfc
#include "mount.h"
Packit Service 09cdfc
#include "config.h"
Packit Service 09cdfc
Packit Service 09cdfc
Packit Service 09cdfc
/* Updating mtab ----------------------------------------------*/
Packit Service 09cdfc
Packit Service 09cdfc
/* Flag for already existing lock file. */
Packit Service 09cdfc
static int we_created_lockfile = 0;
Packit Service 09cdfc
static int lockfile_fd = -1;
Packit Service 09cdfc
Packit Service 09cdfc
/* Flag to indicate that signals have been set up. */
Packit Service 09cdfc
static int signals_have_been_setup = 0;
Packit Service 09cdfc
Packit Service 09cdfc
static void
Packit Service 09cdfc
handler (int sig __attribute__((unused))) {
Packit Service 09cdfc
     exit(EX_USER);
Packit Service 09cdfc
}
Packit Service 09cdfc
Packit Service 09cdfc
static void
Packit Service 09cdfc
setlkw_timeout (int sig __attribute__((unused))) {
Packit Service 09cdfc
     /* nothing, fcntl will fail anyway */
Packit Service 09cdfc
}
Packit Service 09cdfc
Packit Service 09cdfc
/* use monotonic time for timeouts */
Packit Service 09cdfc
static struct timeval
Packit Service 09cdfc
mono_time(void) {
Packit Service 09cdfc
	struct timeval ret;
Packit Service 09cdfc
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
Packit Service 09cdfc
	struct timespec ts;
Packit Service 09cdfc
	if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
Packit Service 09cdfc
		ret.tv_sec = ts.tv_sec;
Packit Service 09cdfc
		ret.tv_usec = ts.tv_nsec/1000;
Packit Service 09cdfc
		return ret;
Packit Service 09cdfc
	}
Packit Service 09cdfc
#endif
Packit Service 09cdfc
	gettimeofday(&ret,NULL);
Packit Service 09cdfc
	return ret;
Packit Service 09cdfc
}
Packit Service 09cdfc
Packit Service 09cdfc
/*
Packit Service 09cdfc
 * See if mtab is present and whether it's a symlink. Returns errno from stat()
Packit Service 09cdfc
 * call or EMLINK if it's a symlink.
Packit Service 09cdfc
 */
Packit Service 09cdfc
int
Packit Service 09cdfc
mtab_unusable(void)
Packit Service 09cdfc
{
Packit Service 09cdfc
	struct stat mstat;
Packit Service 09cdfc
Packit Service 09cdfc
	if(lstat(_PATH_MOUNTED, &mstat))
Packit Service 09cdfc
		return errno;
Packit Service 09cdfc
	else if (S_ISLNK(mstat.st_mode))
Packit Service 09cdfc
		return EMLINK;
Packit Service 09cdfc
	return 0;
Packit Service 09cdfc
}
Packit Service 09cdfc
Packit Service 09cdfc
/* Remove lock file.  */
Packit Service 09cdfc
void
Packit Service 09cdfc
unlock_mtab (void) {
Packit Service 09cdfc
	if (we_created_lockfile) {
Packit Service 09cdfc
		close(lockfile_fd);
Packit Service 09cdfc
		lockfile_fd = -1;
Packit Service 09cdfc
		unlink (_PATH_MOUNTED_LOCK);
Packit Service 09cdfc
		we_created_lockfile = 0;
Packit Service 09cdfc
	}
Packit Service 09cdfc
}
Packit Service 09cdfc
Packit Service 09cdfc
/* Create the lock file.
Packit Service 09cdfc
   The lock file will be removed if we catch a signal or when we exit. */
Packit Service 09cdfc
/* The old code here used flock on a lock file /etc/mtab~ and deleted
Packit Service 09cdfc
   this lock file afterwards. However, as rgooch remarks, that has a
Packit Service 09cdfc
   race: a second mount may be waiting on the lock and proceed as
Packit Service 09cdfc
   soon as the lock file is deleted by the first mount, and immediately
Packit Service 09cdfc
   afterwards a third mount comes, creates a new /etc/mtab~, applies
Packit Service 09cdfc
   flock to that, and also proceeds, so that the second and third mount
Packit Service 09cdfc
   now both are scribbling in /etc/mtab.
Packit Service 09cdfc
   The new code uses a link() instead of a creat(), where we proceed
Packit Service 09cdfc
   only if it was us that created the lock, and hence we always have
Packit Service 09cdfc
   to delete the lock afterwards. Now the use of flock() is in principle
Packit Service 09cdfc
   superfluous, but avoids an arbitrary sleep(). */
Packit Service 09cdfc
Packit Service 09cdfc
/* Where does the link point to? Obvious choices are mtab and mtab~~.
Packit Service 09cdfc
   HJLu points out that the latter leads to races. Right now we use
Packit Service 09cdfc
   mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
Packit Service 09cdfc
#define MOUNTLOCK_LINKTARGET		_PATH_MOUNTED_LOCK "%d"
Packit Service 09cdfc
#define MOUNTLOCK_LINKTARGET_LTH	(sizeof(_PATH_MOUNTED_LOCK)+20)
Packit Service 09cdfc
Packit Service 09cdfc
/*
Packit Service 09cdfc
 * The original mount locking code has used sleep(1) between attempts and
Packit Service 09cdfc
 * maximal number of attemps has been 5.
Packit Service 09cdfc
 *
Packit Service 09cdfc
 * There was very small number of attempts and extremely long waiting (1s)
Packit Service 09cdfc
 * that is useless on machines with large number of concurret mount processes.
Packit Service 09cdfc
 *
Packit Service 09cdfc
 * Now we wait few thousand microseconds between attempts and we have global
Packit Service 09cdfc
 * time limit (30s) rather than limit for number of attempts. The advantage
Packit Service 09cdfc
 * is that this method also counts time which we spend in fcntl(F_SETLKW) and
Packit Service 09cdfc
 * number of attempts is not so much restricted.
Packit Service 09cdfc
 *
Packit Service 09cdfc
 * -- kzak@redhat.com [2007-Mar-2007]
Packit Service 09cdfc
 */
Packit Service 09cdfc
Packit Service 09cdfc
/* maximum seconds between first and last attempt */
Packit Service 09cdfc
#define MOUNTLOCK_MAXTIME		30
Packit Service 09cdfc
Packit Service 09cdfc
/* sleep time (in microseconds, max=999999) between attempts */
Packit Service 09cdfc
#define MOUNTLOCK_WAITTIME		5000
Packit Service 09cdfc
Packit Service 09cdfc
int
Packit Service 09cdfc
lock_mtab (void) {
Packit Service 09cdfc
	int i;
Packit Service 09cdfc
	struct timespec waittime;
Packit Service 09cdfc
	struct timeval maxtime;
Packit Service 09cdfc
	char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
Packit Service 09cdfc
Packit Service 09cdfc
	if (!signals_have_been_setup) {
Packit Service 09cdfc
		int sig = 0;
Packit Service 09cdfc
		struct sigaction sa;
Packit Service 09cdfc
Packit Service 09cdfc
		sa.sa_handler = handler;
Packit Service 09cdfc
		sa.sa_flags = 0;
Packit Service 09cdfc
		sigfillset (&sa.sa_mask);
Packit Service 09cdfc
Packit Service 09cdfc
		while (sigismember (&sa.sa_mask, ++sig) != -1
Packit Service 09cdfc
		       && sig != SIGCHLD) {
Packit Service 09cdfc
			if (sig == SIGALRM)
Packit Service 09cdfc
				sa.sa_handler = setlkw_timeout;
Packit Service 09cdfc
			else
Packit Service 09cdfc
				sa.sa_handler = handler;
Packit Service 09cdfc
			sigaction (sig, &sa, (struct sigaction *) 0);
Packit Service 09cdfc
		}
Packit Service 09cdfc
		signals_have_been_setup = 1;
Packit Service 09cdfc
	}
Packit Service 09cdfc
Packit Service 09cdfc
	sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
Packit Service 09cdfc
Packit Service 09cdfc
	i = open (linktargetfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
Packit Service 09cdfc
	if (i < 0) {
Packit Service 09cdfc
		/* linktargetfile does not exist (as a file)
Packit Service 09cdfc
		   and we cannot create it. Read-only filesystem?
Packit Service 09cdfc
		   Too many files open in the system?
Packit Service 09cdfc
		   Filesystem full? */
Packit Service 09cdfc
		return EX_FILEIO;
Packit Service 09cdfc
	}
Packit Service 09cdfc
	close(i);
Packit Service 09cdfc
Packit Service 09cdfc
	maxtime = mono_time();
Packit Service 09cdfc
	maxtime.tv_sec += MOUNTLOCK_MAXTIME;
Packit Service 09cdfc
Packit Service 09cdfc
	waittime.tv_sec = 0;
Packit Service 09cdfc
	waittime.tv_nsec = (1000 * MOUNTLOCK_WAITTIME);
Packit Service 09cdfc
Packit Service 09cdfc
	/* Repeat until it was us who made the link */
Packit Service 09cdfc
	while (!we_created_lockfile) {
Packit Service 09cdfc
		struct timeval now;
Packit Service 09cdfc
		struct flock flock;
Packit Service 09cdfc
		int errsv, j;
Packit Service 09cdfc
Packit Service 09cdfc
		j = link(linktargetfile, _PATH_MOUNTED_LOCK);
Packit Service 09cdfc
		errsv = errno;
Packit Service 09cdfc
Packit Service 09cdfc
		if (j == 0)
Packit Service 09cdfc
			we_created_lockfile = 1;
Packit Service 09cdfc
Packit Service 09cdfc
		if (j < 0 && errsv != EEXIST) {
Packit Service 09cdfc
			(void) unlink(linktargetfile);
Packit Service 09cdfc
			return EX_FILEIO;
Packit Service 09cdfc
		}
Packit Service 09cdfc
Packit Service 09cdfc
		lockfile_fd = open (_PATH_MOUNTED_LOCK, O_WRONLY);
Packit Service 09cdfc
Packit Service 09cdfc
		if (lockfile_fd < 0) {
Packit Service 09cdfc
			/* Strange... Maybe the file was just deleted? */
Packit Service 09cdfc
			now = mono_time();
Packit Service 09cdfc
			if (errno == ENOENT && now.tv_sec < maxtime.tv_sec) {
Packit Service 09cdfc
				we_created_lockfile = 0;
Packit Service 09cdfc
				continue;
Packit Service 09cdfc
			}
Packit Service 09cdfc
			(void) unlink(linktargetfile);
Packit Service 09cdfc
			return EX_FILEIO;
Packit Service 09cdfc
		}
Packit Service 09cdfc
Packit Service 09cdfc
		flock.l_type = F_WRLCK;
Packit Service 09cdfc
		flock.l_whence = SEEK_SET;
Packit Service 09cdfc
		flock.l_start = 0;
Packit Service 09cdfc
		flock.l_len = 0;
Packit Service 09cdfc
Packit Service 09cdfc
		if (j == 0) {
Packit Service 09cdfc
			/* We made the link. Now claim the lock. If we can't
Packit Service 09cdfc
			 * get it, continue anyway
Packit Service 09cdfc
			 */
Packit Service 09cdfc
			fcntl (lockfile_fd, F_SETLK, &flock);
Packit Service 09cdfc
			(void) unlink(linktargetfile);
Packit Service 09cdfc
		} else {
Packit Service 09cdfc
			/* Someone else made the link. Wait. */
Packit Service 09cdfc
			now = mono_time();
Packit Service 09cdfc
			if (now.tv_sec < maxtime.tv_sec) {
Packit Service 09cdfc
				alarm(maxtime.tv_sec - now.tv_sec);
Packit Service 09cdfc
				if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
Packit Service 09cdfc
					(void) unlink(linktargetfile);
Packit Service 09cdfc
					return EX_FILEIO;
Packit Service 09cdfc
				}
Packit Service 09cdfc
				alarm(0);
Packit Service 09cdfc
				nanosleep(&waittime, NULL);
Packit Service 09cdfc
			} else {
Packit Service 09cdfc
				(void) unlink(linktargetfile);
Packit Service 09cdfc
				return EX_FILEIO;
Packit Service 09cdfc
			}
Packit Service 09cdfc
			close(lockfile_fd);
Packit Service 09cdfc
		}
Packit Service 09cdfc
	}
Packit Service 09cdfc
	return 0;
Packit Service 09cdfc
}
Packit Service 09cdfc
Packit Service 09cdfc
/*
Packit Service 09cdfc
 * Call fflush and fsync on the mtab, and then endmntent. If either fflush
Packit Service 09cdfc
 * or fsync fails, then truncate the file back to "size". endmntent is called
Packit Service 09cdfc
 * unconditionally, and the errno (if any) from fflush and fsync are returned.
Packit Service 09cdfc
 */
Packit Service 09cdfc
int
Packit Service 09cdfc
my_endmntent(FILE *stream, off_t size)
Packit Service 09cdfc
{
Packit Service 09cdfc
	int rc, fd;
Packit Service 09cdfc
Packit Service 09cdfc
	fd = fileno(stream);
Packit Service 09cdfc
	if (fd < 0)
Packit Service 09cdfc
		return -EBADF;
Packit Service 09cdfc
Packit Service 09cdfc
	rc = fflush(stream);
Packit Service 09cdfc
	if (!rc)
Packit Service 09cdfc
		rc = fsync(fd);
Packit Service 09cdfc
Packit Service 09cdfc
	/* truncate file back to "size" -- best effort here */
Packit Service 09cdfc
	if (rc) {
Packit Service 09cdfc
		int ignore __attribute__((unused));
Packit Service 09cdfc
Packit Service 09cdfc
		rc = errno;
Packit Service 09cdfc
		ignore = ftruncate(fd, size);
Packit Service 09cdfc
	}
Packit Service 09cdfc
Packit Service 09cdfc
	endmntent(stream);
Packit Service 09cdfc
	return rc;
Packit Service 09cdfc
}