| /* | |
| Copyright (c) 2015 Microsoft Corporation. All rights reserved. | |
| Released under Apache 2.0 license as described in the file LICENSE. | |
| Author: Leonardo de Moura | |
| */ | |
| #include <string> | |
| #include <cstring> | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <lean/exception.h> | |
| #include <lean/sstream.h> | |
| #include "util/file_lock.h" | |
| #if defined(LEAN_WINDOWS) | |
| #include <windows.h> | |
| #include <io.h> | |
| #define LOCK_SH 1 /* shared lock */ | |
| #define LOCK_EX 2 /* exclusive lock */ | |
| #define LOCK_NB 4 /* don't block when locking */ | |
| #define LOCK_UN 8 /* unlock */ | |
| #define O_CREAT _O_CREAT | |
| static BOOL lock(HANDLE h, int non_blocking, int exclusive) { | |
| DWORD lower, upper; | |
| OVERLAPPED ovlp; | |
| int flags = 0; | |
| lower = GetFileSize(h, &upper); | |
| memset (&ovlp, 0, sizeof(ovlp)); | |
| if (non_blocking) | |
| flags |= LOCKFILE_FAIL_IMMEDIATELY; | |
| if (exclusive) | |
| flags |= LOCKFILE_EXCLUSIVE_LOCK; | |
| return LockFileEx(h, flags, 0, lower, upper, &ovlp); | |
| } | |
| static BOOL unlock(HANDLE h) { | |
| DWORD lower, upper; | |
| lower = GetFileSize(h, &upper); | |
| return UnlockFile(h, 0, 0, lower, upper); | |
| } | |
| int flock(int fd, int op) { | |
| HANDLE h = (HANDLE)_get_osfhandle(fd); | |
| DWORD res; | |
| int non_blocking; | |
| if (h == INVALID_HANDLE_VALUE) | |
| return -1; | |
| non_blocking = op & LOCK_NB; | |
| op &= ~LOCK_NB; | |
| switch (op) { | |
| case LOCK_SH: | |
| res = lock(h, non_blocking, 0); | |
| break; | |
| case LOCK_EX: | |
| res = lock(h, non_blocking, 1); | |
| break; | |
| case LOCK_UN: | |
| res = unlock(h); | |
| break; | |
| default: | |
| return -1; | |
| } | |
| return !res ? -1 : 0; | |
| } | |
| #else | |
| #include <sys/file.h> | |
| #include <unistd.h> | |
| #endif | |
| namespace lean { | |
| file_lock::file_lock(char const * fname, bool exclusive): | |
| m_fname(fname), m_fd(-1) { | |
| #if !defined(LEAN_EMSCRIPTEN) | |
| m_fname += ".lock"; | |
| m_fd = open(m_fname.c_str(), O_CREAT, 0xFFFF); | |
| if (m_fd == -1) { | |
| if (errno == EACCES || errno == EROFS) { | |
| // We don't have permission to create the file, the folder containing fname is probably readonly. | |
| // fname is probably part of the Lean installation in a system directory. | |
| // So, we ignore the file_lock in this case. | |
| } else { | |
| throw exception(sstream() << "failed to open lock file for '" << fname << "': " << strerror(errno)); | |
| } | |
| } else { | |
| int status = flock(m_fd, exclusive ? LOCK_EX : LOCK_SH); | |
| if (status == -1) | |
| throw exception(sstream() << "failed to lock file '" << fname << "': " << strerror(errno)); | |
| } | |
| #endif | |
| } | |
| file_lock::~file_lock() { | |
| #if !defined(LEAN_EMSCRIPTEN) | |
| if (m_fd != -1) { | |
| #if !defined(LEAN_WINDOWS) | |
| /* On Windows, we cannot remove the file if it is locked. */ | |
| std::remove(m_fname.c_str()); | |
| #endif | |
| flock(m_fd, LOCK_UN); | |
| close(m_fd); | |
| #if defined(LEAN_WINDOWS) | |
| /* On Windows, we (to) to remove the file after we released the lock. The operation will fail if another | |
| process has a handle to it. However, this is better than always keeping all .lock files. */ | |
| std::remove(m_fname.c_str()); | |
| #endif | |
| } | |
| #endif | |
| } | |
| } |