From bd0a51c0dd117eed5879441f9d14a3112ff27cc7 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Fri, 9 Jul 2010 20:17:00 +0200 Subject: [PATCH] Add support for atomic file locking The struct 'git_filelock' represents an atomically-locked file, git-style. Locked files can be modified atomically through the new file lock interface: int git_filelock_init(git_filelock *lock, const char *path); int git_filelock_lock(git_filelock *lock, int append); void git_filelock_unlock(git_filelock *lock); int git_filelock_commit(git_filelock *lock); int git_filelock_write(git_filelock *lock, const char *buffer, size_t length); Signed-off-by: Vicent Marti --- src/filelock.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++ src/filelock.h | 23 +++++++++ 2 files changed, 155 insertions(+) create mode 100644 src/filelock.c create mode 100644 src/filelock.h diff --git a/src/filelock.c b/src/filelock.c new file mode 100644 index 000000000..d8310141b --- /dev/null +++ b/src/filelock.c @@ -0,0 +1,132 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file 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 this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "common.h" +#include "filelock.h" +#include "fileops.h" + +static const char *GIT_FILELOCK_EXTENSION = ".lock\0"; +static const size_t GIT_FILELOCK_EXTLENGTH = 6; + +#define BUILD_PATH_LOCK(_lock, _path) { \ + memcpy(_path, _lock->path, _lock->path_length); \ + memcpy(_path + _lock->path_length, GIT_FILELOCK_EXTENSION,\ + GIT_FILELOCK_EXTLENGTH);\ +} + +int git_filelock_init(git_filelock *lock, const char *path) +{ + if (lock == NULL || path == NULL) + return GIT_ERROR; + + memset(lock, 0x0, sizeof(git_filelock)); + + lock->path_length = strlen(path); + + if (lock->path_length + GIT_FILELOCK_EXTLENGTH >= GIT_PATH_MAX) + return GIT_ERROR; + + memcpy(lock->path, path, lock->path_length); + return 0; +} + +int git_filelock_lock(git_filelock *lock, int append) +{ + char path_lock[GIT_PATH_MAX]; + BUILD_PATH_LOCK(lock, path_lock); + + /* If file already exists, we cannot create a lock */ + if (gitfo_exists(path_lock) == 0) + return GIT_EOSERR; + + lock->file_lock = gitfo_creat(path_lock, 0666); + + if (lock->file_lock < 0) + return GIT_EOSERR; + + lock->is_locked = 1; + + /* TODO: do a flock() in the descriptor file_lock */ + + if (append && gitfo_exists(lock->path) == 0) { + git_file source; + char buffer[2048]; + size_t read_bytes; + + source = gitfo_open(lock->path, O_RDONLY); + if (source < 0) + return GIT_EOSERR; + + while ((read_bytes = gitfo_read(source, buffer, 2048)) > 0) + gitfo_write(lock->file_lock, buffer, read_bytes); + + gitfo_close(source); + } + + return 0; +} + +void git_filelock_unlock(git_filelock *lock) +{ + char path_lock[GIT_PATH_MAX]; + BUILD_PATH_LOCK(lock, path_lock); + + if (lock->is_locked) { + /* The flock() in lock->file_lock is removed + * automatically when closing the descriptor */ + gitfo_close(lock->file_lock); + gitfo_unlink(path_lock); + lock->is_locked = 0; + } +} + +int git_filelock_commit(git_filelock *lock) +{ + int error; + char path_lock[GIT_PATH_MAX]; + BUILD_PATH_LOCK(lock, path_lock); + + if (!lock->is_locked || lock->file_lock < 0) + return GIT_ERROR; + + /* FIXME: flush the descriptor? */ + gitfo_close(lock->file_lock); + + error = gitfo_move_file(path_lock, lock->path); + + if (error < 0) + gitfo_unlink(path_lock); + + lock->is_locked = 0; + return error; +} + +int git_filelock_write(git_filelock *lock, const void *buffer, size_t length) +{ + if (!lock->is_locked || lock->file_lock < 0) + return GIT_ERROR; + + return gitfo_write(lock->file_lock, (void *)buffer, length); +} diff --git a/src/filelock.h b/src/filelock.h new file mode 100644 index 000000000..bff861811 --- /dev/null +++ b/src/filelock.h @@ -0,0 +1,23 @@ +#ifndef INCLUDE_filelock_h__ +#define INCLUDE_filelock_h__ + +#include "fileops.h" + +struct git_filelock { + + char path[GIT_PATH_MAX]; + size_t path_length; + + git_file file_lock; + int is_locked; +}; + +typedef struct git_filelock git_filelock; + +int git_filelock_init(git_filelock *lock, const char *path); +int git_filelock_lock(git_filelock *lock, int append); +void git_filelock_unlock(git_filelock *lock); +int git_filelock_commit(git_filelock *lock); +int git_filelock_write(git_filelock *lock, const void *buffer, size_t length); + +#endif