/* 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 #include #include #include "runtime/exception.h" #include "runtime/sstream.h" #include "util/file_lock.h" #if defined(LEAN_WINDOWS) && !defined(LEAN_CYGWIN) #include #include #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 #include #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 lock file '" << fname << "'"); } } else { int status = flock(m_fd, exclusive ? LOCK_EX : LOCK_SH); if (status == -1) throw exception(sstream() << "failed to lock file '" << fname << "'"); } #endif } file_lock::~file_lock() { #if !defined(LEAN_EMSCRIPTEN) if (m_fd != -1) { #if !defined(LEAN_WINDOWS) || defined(LEAN_CYGWIN) /* 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) && !defined(LEAN_CYGWIN) /* 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 } }