blob: 3a6eab9bef15ba9f730f5a86b9317b9b5ed07cee [file] [log] [blame]
/* Copyright 2005,2009,2018 Alain Knaff.
* This file is part of mtools.
*
* Mtools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Mtools is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Mtools. If not, see <http://www.gnu.org/licenses/>.
*
* Create an advisory lock on the device to prevent concurrent writes.
* Uses either lockf, flock, or fcntl locking methods. See the Makefile
* and the Configure files for how to specify the proper method.
*/
#include "sysincludes.h"
#include "mtools.h"
#include "lockdev.h"
#if (defined HAVE_SIGACTION && defined HAVE_ALARM)
# define ALRM
#endif
#if (defined(HAVE_FLOCK) && defined (LOCK_EX) && (defined(LOCK_NB) || defined(ALRM)))
# ifdef ALRM
# define USE_FLOCK_W
# else
# define USE_FLOCK
# endif
#else /* FLOCK */
#if (defined(HAVE_LOCKF) && (defined(F_TLOCK) || defined(ALRM)))
# ifdef ALRM
# define USE_LOCKF_W
# else
# define USE_LOCKF
# endif
#else /* LOCKF */
#if (defined(F_SETLK) && defined(F_WRLCK))
# if (defined ALRM && defined F_SETLKW)
# define USE_SETLK_W
# else
# define USE_SETLK_W
# endif
#else
#endif /* FCNTL */
#endif /* LOCKF */
#endif /* FLOCK */
#if defined(USE_FLOCK_W) || defined(USE_LOCKF_W) || defined (USE_SETLK_W)
static void alrm(int a UNUSEDP) {
}
#endif
int lock_dev(int fd, int mode, struct device *dev)
{
unsigned int retries = 0;
if(IS_NOLOCK(dev))
return 0;
while(1) {
int ret=0;
#if defined(USE_FLOCK_W) || defined(USE_LOCKF_W) || defined (USE_SETLK_W)
struct sigaction alrm_action, old_alrm_action;
int old_alrm = alarm(0);
memset(&alrm_action, 0, sizeof(alrm_action));
alrm_action.sa_handler = alrm;
alrm_action.sa_flags = 0;
sigaction(SIGALRM, &alrm_action, &old_alrm_action);
alarm(mtools_lock_timeout);
#endif
#ifdef USE_FLOCK
ret = flock(fd, (mode ? LOCK_EX : LOCK_SH)|LOCK_NB);
#endif
#ifdef USE_FLOCK_W
ret = flock(fd, (mode ? LOCK_EX : LOCK_SH));
#endif
#if (defined(USE_LOCKF) || defined(USE_LOCKF_W))
if(mode)
# ifdef USE_LOCKF
ret = lockf(fd, F_TLOCK, 0);
# else
ret = lockf(fd, F_LOCK, 0);
# endif
else
ret = 0;
#endif
#if (defined(USE_SETLK) || defined(USE_SETLK_W))
{
struct flock flk;
flk.l_type = mode ? F_WRLCK : F_RDLCK;
flk.l_whence = 0;
flk.l_start = 0L;
flk.l_len = 0L;
# ifdef USE_SETLK_W
ret = fcntl(fd, F_SETLKW, &flk);
# else
ret = fcntl(fd, F_SETLK, &flk);
# endif
}
#endif
#if defined(USE_FLOCK_W) || defined(USE_LOCKF_W) || defined (USE_SETLK_W)
/* Cancel the alarm */
sigaction(SIGALRM, &old_alrm_action, NULL);
alarm(old_alrm);
#endif
if(ret < 0) {
#if defined(USE_FLOCK_W) || defined(USE_LOCKF_W) || defined (USE_SETLK_W)
/* ALARM fired ==> this means we are still locked */
if(errno == EINTR) {
return 1;
}
#endif
if(
#ifdef EWOULDBLOCK
(errno != EWOULDBLOCK)
#else
1
#endif
&&
#ifdef EAGAIN
(errno != EAGAIN)
#else
1
#endif
&&
#ifdef EINTR
(errno != EINTR)
#else
1
#endif
) {
/* Error other than simply being locked */
return -1;
}
/* Locked ==> continue until timeout */
} else /* no error => we got the lock! */
return 0;
#ifdef HAVE_USLEEP
if(retries++ < mtools_lock_timeout * 10)
usleep(100000);
#else
if(retries++ < mtools_lock_timeout)
sleep(1);
#endif
else
/* waited for too long => give up */
return 1;
}
}