| /* 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; |
| unsigned 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; |
| } |
| } |