From: Stephan Bosch Date: Sun, 19 Sep 2021 11:09:29 +0000 (+0200) Subject: lib: file-lock - Rework API to make it extensible. X-Git-Tag: 2.3.17~64 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=083439ed737b8a8edbfec28fce7b59e003983fc1;p=thirdparty%2Fdovecot%2Fcore.git lib: file-lock - Rework API to make it extensible. --- diff --git a/src/doveadm/dsync/dsync-brain.c b/src/doveadm/dsync/dsync-brain.c index 03dd58e21b..17fecb8930 100644 --- a/src/doveadm/dsync/dsync-brain.c +++ b/src/doveadm/dsync/dsync-brain.c @@ -402,7 +402,9 @@ dsync_brain_lock(struct dsync_brain *brain, const char *remote_hostname) { const struct file_create_settings lock_set = { .lock_timeout_secs = brain->lock_timeout, - .lock_method = FILE_LOCK_METHOD_FCNTL, + .lock_settings = { + .lock_method = FILE_LOCK_METHOD_FCNTL, + }, }; const char *home, *error, *local_hostname = my_hostdomain(); bool created; diff --git a/src/lib-dict/dict-file.c b/src/lib-dict/dict-file.c index 54ff92b745..c9228a865a 100644 --- a/src/lib-dict/dict-file.c +++ b/src/lib-dict/dict-file.c @@ -520,10 +520,12 @@ file_dict_lock(struct file_dict *dict, struct file_lock **lock_r, } *lock_r = NULL; + struct file_lock_settings lock_set = { + .lock_method = dict->lock_method, + }; do { file_lock_free(lock_r); - if (file_wait_lock(dict->fd, dict->path, F_WRLCK, - dict->lock_method, + if (file_wait_lock(dict->fd, dict->path, F_WRLCK, &lock_set, file_dict_dotlock_settings.timeout, lock_r, &error) <= 0) { *error_r = t_strdup_printf( diff --git a/src/lib-fs/fs-posix.c b/src/lib-fs/fs-posix.c index 66859b8880..657938e60f 100644 --- a/src/lib-fs/fs-posix.c +++ b/src/lib-fs/fs-posix.c @@ -694,6 +694,9 @@ fs_posix_lock(struct fs_file *_file, unsigned int secs, struct fs_lock **lock_r) i_zero(&fs_lock); fs_lock.lock.file = _file; + struct file_lock_settings lock_set = { + .lock_method = FILE_LOCK_METHOD_FLOCK, + }; switch (fs->lock_method) { case FS_POSIX_LOCK_METHOD_FLOCK: #ifndef HAVE_FLOCK @@ -703,11 +706,11 @@ fs_posix_lock(struct fs_file *_file, unsigned int secs, struct fs_lock **lock_r) #else if (secs == 0) { ret = file_try_lock(file->fd, file->full_path, F_WRLCK, - FILE_LOCK_METHOD_FLOCK, - &fs_lock.file_lock, &error); + &lock_set, &fs_lock.file_lock, + &error); } else { ret = file_wait_lock(file->fd, file->full_path, F_WRLCK, - FILE_LOCK_METHOD_FLOCK, secs, + &lock_set, secs, &fs_lock.file_lock, &error); } if (ret < 0) { diff --git a/src/lib-index/mail-index-lock.c b/src/lib-index/mail-index-lock.c index 83e106ec90..cdf62e4651 100644 --- a/src/lib-index/mail-index-lock.c +++ b/src/lib-index/mail-index-lock.c @@ -35,8 +35,11 @@ int mail_index_lock_fd(struct mail_index *index, const char *path, int fd, return 1; } - ret = file_wait_lock(fd, path, lock_type, index->set.lock_method, - timeout_secs, lock_r, &error); + struct file_lock_settings lock_set = { + .lock_method = index->set.lock_method, + }; + ret = file_wait_lock(fd, path, lock_type, &lock_set, timeout_secs, + lock_r, &error); if (ret < 0) e_error(index->event, "%s", error); return ret; diff --git a/src/lib-index/mail-index-strmap.c b/src/lib-index/mail-index-strmap.c index c377508b92..1287c7386d 100644 --- a/src/lib-index/mail-index-strmap.c +++ b/src/lib-index/mail-index-strmap.c @@ -1047,12 +1047,14 @@ static int mail_index_strmap_lock(struct mail_index_strmap *strmap) if (strmap->index->set.lock_method != FILE_LOCK_METHOD_DOTLOCK) { i_assert(strmap->file_lock == NULL); + struct file_lock_settings lock_set = { + .lock_method = strmap->index->set.lock_method, + }; timeout_secs = I_MIN(MAIL_INDEX_STRMAP_TIMEOUT_SECS, strmap->index->set.max_lock_timeout_secs); ret = file_wait_lock(strmap->fd, strmap->path, F_WRLCK, - strmap->index->set.lock_method, - timeout_secs, &strmap->file_lock, - &error); + &lock_set, timeout_secs, + &strmap->file_lock, &error); if (ret <= 0) { mail_index_set_error(strmap->index, "file_wait_lock() failed with strmap index file %s: %s", diff --git a/src/lib-storage/index/dbox-common/dbox-file.c b/src/lib-storage/index/dbox-common/dbox-file.c index fca8f44023..16810b0c34 100644 --- a/src/lib-storage/index/dbox-common/dbox-file.c +++ b/src/lib-storage/index/dbox-common/dbox-file.c @@ -313,8 +313,11 @@ int dbox_file_try_lock(struct dbox_file *file) i_assert(file->fd != -1); #ifdef DBOX_FILE_LOCK_METHOD_FLOCK + struct file_lock_settings lock_set = { + .lock_method = FILE_LOCK_METHOD_FLOCK, + }; ret = file_try_lock(file->fd, file->cur_path, F_WRLCK, - FILE_LOCK_METHOD_FLOCK, &file->lock, &error); + &lock_set, &file->lock, &error); if (ret < 0) { mail_storage_set_critical(&file->storage->storage, "file_try_lock(%s) failed: %s", file->cur_path, error); diff --git a/src/lib-storage/mail-storage.c b/src/lib-storage/mail-storage.c index 910e848403..d7792507a3 100644 --- a/src/lib-storage/mail-storage.c +++ b/src/lib-storage/mail-storage.c @@ -3195,19 +3195,20 @@ int mail_storage_lock_create(const char *lock_path, const struct mail_storage_settings *mail_set, struct file_lock **lock_r, const char **error_r) { + struct file_create_settings lock_set_new = *lock_set; bool created; - if (lock_set->lock_method == FILE_LOCK_METHOD_DOTLOCK) + if (lock_set->lock_settings.lock_method == FILE_LOCK_METHOD_DOTLOCK) return mail_storage_dotlock_create(lock_path, lock_set, mail_set, lock_r, error_r); - if (file_create_locked(lock_path, lock_set, lock_r, + lock_set_new.lock_settings.close_on_free = TRUE; + lock_set_new.lock_settings.unlink_on_free = TRUE; + if (file_create_locked(lock_path, &lock_set_new, lock_r, &created, error_r) == -1) { *error_r = t_strdup_printf("file_create_locked(%s) failed: %s", lock_path, *error_r); return errno == EAGAIN ? 0 : -1; } - file_lock_set_close_on_free(*lock_r, TRUE); - file_lock_set_unlink_on_free(*lock_r, TRUE); return 1; } @@ -3223,7 +3224,7 @@ int mailbox_lock_file_create(struct mailbox *box, const char *lock_fname, i_zero(&set); set.lock_timeout_secs = mail_storage_get_lock_timeout(box->storage, lock_secs); - set.lock_method = box->storage->set->parsed_lock_method; + set.lock_settings.lock_method = box->storage->set->parsed_lock_method; set.mode = perm->file_create_mode; set.gid = perm->file_create_gid; set.gid_origin = perm->file_create_gid_origin; diff --git a/src/lib-storage/mail-user.c b/src/lib-storage/mail-user.c index 4c02a74a48..23158d408c 100644 --- a/src/lib-storage/mail-user.c +++ b/src/lib-storage/mail-user.c @@ -590,7 +590,9 @@ int mail_user_lock_file_create(struct mail_user *user, const char *lock_fname, mail_user_set_get_storage_set(user); struct file_create_settings lock_set = { .lock_timeout_secs = lock_secs, - .lock_method = mail_set->parsed_lock_method, + .lock_settings = { + .lock_method = mail_set->parsed_lock_method, + }, }; struct mailbox_list *inbox_list = mail_namespace_find_inbox(user->namespaces)->list; diff --git a/src/lib-storage/mailbox-list.c b/src/lib-storage/mailbox-list.c index e2840d3d12..c8f5d445f7 100644 --- a/src/lib-storage/mailbox-list.c +++ b/src/lib-storage/mailbox-list.c @@ -2107,7 +2107,7 @@ int mailbox_list_lock(struct mailbox_list *list) set.lock_timeout_secs = list->mail_set->mail_max_lock_timeout == 0 ? MAILBOX_LIST_LOCK_SECS : I_MIN(MAILBOX_LIST_LOCK_SECS, list->mail_set->mail_max_lock_timeout); - set.lock_method = list->mail_set->parsed_lock_method; + set.lock_settings.lock_method = list->mail_set->parsed_lock_method; set.mode = perm.file_create_mode; set.gid = perm.file_create_gid; set.gid_origin = perm.file_create_gid_origin; diff --git a/src/lib/file-create-locked.c b/src/lib/file-create-locked.c index 38c9137713..47fe7f9f33 100644 --- a/src/lib/file-create-locked.c +++ b/src/lib/file-create-locked.c @@ -22,15 +22,19 @@ try_lock_existing(int fd, const char *path, const struct file_create_settings *set, struct file_lock **lock_r, const char **error_r) { + struct file_lock_settings lock_set = set->lock_settings; struct stat st1, st2; int ret; + lock_set.unlink_on_free = FALSE; + lock_set.close_on_free = FALSE; + if (fstat(fd, &st1) < 0) { *error_r = t_strdup_printf("fstat(%s) failed: %m", path); return -1; } - if (file_wait_lock(fd, path, F_WRLCK, set->lock_method, - set->lock_timeout_secs, lock_r, error_r) <= 0) + if (file_wait_lock(fd, path, F_WRLCK, &lock_set, set->lock_timeout_secs, + lock_r, error_r) <= 0) return -1; if (stat(path, &st2) == 0) { ret = st1.st_ino == st2.st_ino && @@ -44,6 +48,11 @@ try_lock_existing(int fd, const char *path, if (ret <= 0) { /* the fd is closed next - no need to unlock */ file_lock_free(lock_r); + } else { + file_lock_set_unlink_on_free( + *lock_r, set->lock_settings.unlink_on_free); + file_lock_set_close_on_free( + *lock_r, set->lock_settings.close_on_free); } return ret; } @@ -103,8 +112,12 @@ try_create_new(const char *path, const struct file_create_settings *set, return -1; } + struct file_lock_settings lock_set = set->lock_settings; + lock_set.unlink_on_free = FALSE; + lock_set.close_on_free = FALSE; + ret = -1; - if (file_try_lock(fd, str_c(temp_path), F_WRLCK, set->lock_method, + if (file_try_lock(fd, str_c(temp_path), F_WRLCK, &lock_set, lock_r, error_r) <= 0) { } else if (link(str_c(temp_path), path) < 0) { if (errno == EEXIST) { @@ -123,6 +136,10 @@ try_create_new(const char *path, const struct file_create_settings *set, file_lock_free(lock_r); } else { file_lock_set_path(*lock_r, path); + file_lock_set_unlink_on_free( + *lock_r, set->lock_settings.unlink_on_free); + file_lock_set_close_on_free( + *lock_r, set->lock_settings.close_on_free); i_unlink_if_exists(str_c(temp_path)); *fd_r = fd; return 1; diff --git a/src/lib/file-create-locked.h b/src/lib/file-create-locked.h index 7d21040347..b88e3c871f 100644 --- a/src/lib/file-create-locked.h +++ b/src/lib/file-create-locked.h @@ -7,7 +7,8 @@ struct file_create_settings { /* 0 = try locking without waiting */ unsigned int lock_timeout_secs; - enum file_lock_method lock_method; + struct file_lock_settings lock_settings; + /* 0 = 0600 */ int mode; /* 0 = default */ diff --git a/src/lib/file-lock.c b/src/lib/file-lock.c index 5cfe00a2bc..38ad23db09 100644 --- a/src/lib/file-lock.c +++ b/src/lib/file-lock.c @@ -13,15 +13,14 @@ #endif struct file_lock { + struct file_lock_settings set; + int fd; char *path; struct dotlock *dotlock; struct timeval locked_time; int lock_type; - enum file_lock_method lock_method; - bool unlink_on_free; - bool close_on_free; }; static struct timeval lock_wait_start; @@ -57,11 +56,10 @@ const char *file_lock_method_to_str(enum file_lock_method method) } int file_try_lock(int fd, const char *path, int lock_type, - enum file_lock_method lock_method, + const struct file_lock_settings *set, struct file_lock **lock_r, const char **error_r) { - return file_wait_lock(fd, path, lock_type, lock_method, 0, - lock_r, error_r); + return file_wait_lock(fd, path, lock_type, set, 0, lock_r, error_r); } static const char * @@ -161,7 +159,7 @@ static bool err_is_lock_timeout(time_t started, unsigned int timeout_secs) } static int file_lock_do(int fd, const char *path, int lock_type, - enum file_lock_method lock_method, + const struct file_lock_settings *set, unsigned int timeout_secs, const char **error_r) { const char *lock_type_str; @@ -178,7 +176,7 @@ static int file_lock_do(int fd, const char *path, int lock_type, lock_type_str = lock_type == F_UNLCK ? "unlock" : (lock_type == F_RDLCK ? "read-lock" : "write-lock"); - switch (lock_method) { + switch (set->lock_method) { case FILE_LOCK_METHOD_FCNTL: { #ifndef HAVE_FCNTL *error_r = t_strdup_printf( @@ -216,13 +214,17 @@ static int file_lock_do(int fd, const char *path, int lock_type, "fcntl(%s, %s, F_SETLKW) locking failed: " "Timed out after %u seconds%s", path, lock_type_str, timeout_secs, - file_lock_find(fd, lock_method, lock_type)); + file_lock_find(fd, set->lock_method, + lock_type)); return 0; } *error_r = t_strdup_printf("fcntl(%s, %s, %s) locking failed: %m", path, lock_type_str, timeout_secs == 0 ? "F_SETLK" : "F_SETLKW"); - if (errno == EDEADLK) - i_panic("%s%s", *error_r, file_lock_find(fd, lock_method, lock_type)); + if (errno == EDEADLK) { + i_panic("%s%s", *error_r, + file_lock_find(fd, set->lock_method, + lock_type)); + } return -1; #endif } @@ -267,13 +269,17 @@ static int file_lock_do(int fd, const char *path, int lock_type, *error_r = t_strdup_printf("flock(%s, %s) failed: " "Timed out after %u seconds%s", path, lock_type_str, timeout_secs, - file_lock_find(fd, lock_method, lock_type)); + file_lock_find(fd, set->lock_method, + lock_type)); return 0; } *error_r = t_strdup_printf("flock(%s, %s) failed: %m", path, lock_type_str); - if (errno == EDEADLK) - i_panic("%s%s", *error_r, file_lock_find(fd, lock_method, lock_type)); + if (errno == EDEADLK) { + i_panic("%s%s", *error_r, + file_lock_find(fd, set->lock_method, + lock_type)); + } return -1; #endif } @@ -286,21 +292,22 @@ static int file_lock_do(int fd, const char *path, int lock_type, } int file_wait_lock(int fd, const char *path, int lock_type, - enum file_lock_method lock_method, unsigned int timeout_secs, + const struct file_lock_settings *set, + unsigned int timeout_secs, struct file_lock **lock_r, const char **error_r) { struct file_lock *lock; int ret; - ret = file_lock_do(fd, path, lock_type, lock_method, timeout_secs, error_r); + ret = file_lock_do(fd, path, lock_type, set, timeout_secs, error_r); if (ret <= 0) return ret; lock = i_new(struct file_lock, 1); + lock->set = *set; lock->fd = fd; lock->path = i_strdup(path); lock->lock_type = lock_type; - lock->lock_method = lock_method; i_gettimeofday(&lock->locked_time); *lock_r = lock; return 1; @@ -311,8 +318,8 @@ int file_lock_try_update(struct file_lock *lock, int lock_type) const char *error; int ret; - ret = file_lock_do(lock->fd, lock->path, lock_type, - lock->lock_method, 0, &error); + ret = file_lock_do(lock->fd, lock->path, lock_type, &lock->set, 0, + &error); if (ret <= 0) return ret; file_lock_log_warning_if_slow(lock); @@ -322,12 +329,12 @@ int file_lock_try_update(struct file_lock *lock, int lock_type) void file_lock_set_unlink_on_free(struct file_lock *lock, bool set) { - lock->unlink_on_free = set; + lock->set.unlink_on_free = set; } void file_lock_set_close_on_free(struct file_lock *lock, bool set) { - lock->close_on_free = set; + lock->set.close_on_free = set; } struct file_lock *file_lock_from_dotlock(struct dotlock **dotlock) @@ -335,10 +342,10 @@ struct file_lock *file_lock_from_dotlock(struct dotlock **dotlock) struct file_lock *lock; lock = i_new(struct file_lock, 1); + lock->set.lock_method = FILE_LOCK_METHOD_DOTLOCK; lock->fd = -1; lock->path = i_strdup(file_dotlock_get_lock_path(*dotlock)); lock->lock_type = F_WRLCK; - lock->lock_method = FILE_LOCK_METHOD_DOTLOCK; i_gettimeofday(&lock->locked_time); lock->dotlock = *dotlock; @@ -350,8 +357,8 @@ static void file_unlock_real(struct file_lock *lock) { const char *error; - if (file_lock_do(lock->fd, lock->path, F_UNLCK, - lock->lock_method, 0, &error) == 0) { + if (file_lock_do(lock->fd, lock->path, F_UNLCK, &lock->set, 0, + &error) == 0) { /* this shouldn't happen */ i_error("file_unlock(%s) failed: %m", lock->path); } @@ -366,7 +373,7 @@ void file_unlock(struct file_lock **_lock) /* unlocking is unnecessary when the file is unlinked. or alternatively the unlink() must be done before unlocking, because otherwise it could be deleting the new lock. */ - i_assert(!lock->unlink_on_free); + i_assert(!lock->set.unlink_on_free); if (lock->dotlock == NULL) file_unlock_real(lock); @@ -376,12 +383,16 @@ void file_unlock(struct file_lock **_lock) static void file_try_unlink_locked(struct file_lock *lock) { struct file_lock *temp_lock = NULL; + struct file_lock_settings temp_set = lock->set; struct stat st1, st2; const char *error; int ret; + temp_set.close_on_free = FALSE; + temp_set.unlink_on_free = FALSE; + file_unlock_real(lock); - ret = file_try_lock(lock->fd, lock->path, F_WRLCK, lock->lock_method, + ret = file_try_lock(lock->fd, lock->path, F_WRLCK, &temp_set, &temp_lock, &error); if (ret < 0) { i_error("file_lock_free(): Unexpectedly failed to retry locking %s: %s", @@ -415,9 +426,9 @@ void file_lock_free(struct file_lock **_lock) if (lock->dotlock != NULL) file_dotlock_delete(&lock->dotlock); - if (lock->unlink_on_free) + if (lock->set.unlink_on_free) file_try_unlink_locked(lock); - if (lock->close_on_free) + if (lock->set.close_on_free) i_close_fd(&lock->fd); file_lock_log_warning_if_slow(lock); diff --git a/src/lib/file-lock.h b/src/lib/file-lock.h index 4769c0ed71..525c1d2be8 100644 --- a/src/lib/file-lock.h +++ b/src/lib/file-lock.h @@ -15,6 +15,18 @@ enum file_lock_method { FILE_LOCK_METHOD_DOTLOCK }; +struct file_lock_settings { + enum file_lock_method lock_method; + + /* When the lock is freed, close the fd automatically. This can + be useful for files that are only created to exist as lock files. */ + bool unlink_on_free:1; + /* When the lock is freed, unlink() the file automatically, unless other + processes are already waiting on the lock. This can be useful for + files that are only created to exist as lock files. */ + bool close_on_free:1; +}; + /* Parse lock method from given string. Returns TRUE if ok, FALSE if name is unknown. */ bool file_lock_method_parse(const char *name, enum file_lock_method *method_r); @@ -24,12 +36,13 @@ const char *file_lock_method_to_str(enum file_lock_method method); /* Lock the file. Returns 1 if successful, 0 if file is already locked, or -1 if error. lock_type is F_WRLCK or F_RDLCK. */ int file_try_lock(int fd, const char *path, int lock_type, - enum file_lock_method lock_method, + const struct file_lock_settings *set, struct file_lock **lock_r, const char **error_r); /* Like lock_try_lock(), but return 0 only after having tried to lock for timeout_secs. */ int file_wait_lock(int fd, const char *path, int lock_type, - enum file_lock_method lock_method, unsigned int timeout_secs, + const struct file_lock_settings *set, + unsigned int timeout_secs, struct file_lock **lock_r, const char **error_r); /* Change the lock type. WARNING: This isn't an atomic operation! The result is the same as file_unlock() + file_try_lock(). */ diff --git a/src/lib/test-file-create-locked.c b/src/lib/test-file-create-locked.c index 20ecdb6abd..f5b4f6a991 100644 --- a/src/lib/test-file-create-locked.c +++ b/src/lib/test-file-create-locked.c @@ -43,7 +43,9 @@ static void test_file_create_locked_basic(void) { struct file_create_settings set = { .lock_timeout_secs = 0, - .lock_method = FILE_LOCK_METHOD_FCNTL, + .lock_settings = { + .lock_method = FILE_LOCK_METHOD_FCNTL, + }, }; const char *path = ".test-file-create-locked"; struct file_lock *lock; @@ -92,7 +94,9 @@ static void test_file_create_locked_mkdir(void) { struct file_create_settings set = { .lock_timeout_secs = 0, - .lock_method = FILE_LOCK_METHOD_FCNTL, + .lock_settings = { + .lock_method = FILE_LOCK_METHOD_FCNTL, + }, }; const char *path; struct file_lock *lock; diff --git a/src/plugins/fts-squat/squat-trie.c b/src/plugins/fts-squat/squat-trie.c index d18b004951..d006817364 100644 --- a/src/plugins/fts-squat/squat-trie.c +++ b/src/plugins/fts-squat/squat-trie.c @@ -292,9 +292,11 @@ static int squat_trie_lock(struct squat_trie *trie, int lock_type, for (;;) { if (trie->lock_method != FILE_LOCK_METHOD_DOTLOCK) { - ret = file_wait_lock(trie->fd, trie->path, - lock_type, trie->lock_method, - SQUAT_TRIE_LOCK_TIMEOUT, + struct file_lock_settings lock_set = { + .lock_method = trie->lock_method, + }; + ret = file_wait_lock(trie->fd, trie->path, lock_type, + &lock_set, SQUAT_TRIE_LOCK_TIMEOUT, file_lock_r, &error); if (ret < 0) { i_error("squat trie %s: %s", @@ -1629,8 +1631,10 @@ static int squat_trie_write(struct squat_trie_build_context *ctx) return -1; if (trie->lock_method != FILE_LOCK_METHOD_DOTLOCK) { - ret = file_wait_lock(fd, path, F_WRLCK, - trie->lock_method, + struct file_lock_settings lock_set = { + .lock_method = trie->lock_method, + }; + ret = file_wait_lock(fd, path, F_WRLCK, &lock_set, SQUAT_TRIE_LOCK_TIMEOUT, &file_lock, &error); if (ret <= 0) { diff --git a/src/plugins/fts-squat/squat-uidlist.c b/src/plugins/fts-squat/squat-uidlist.c index caf9854acc..facb8d0b0e 100644 --- a/src/plugins/fts-squat/squat-uidlist.c +++ b/src/plugins/fts-squat/squat-uidlist.c @@ -609,9 +609,11 @@ static int squat_uidlist_lock(struct squat_uidlist *uidlist) i_assert(uidlist->dotlock == NULL); if (uidlist->trie->lock_method != FILE_LOCK_METHOD_DOTLOCK) { + struct file_lock_settings lock_set = { + .lock_method = uidlist->trie->lock_method, + }; ret = file_wait_lock(uidlist->fd, uidlist->path, - F_WRLCK, - uidlist->trie->lock_method, + F_WRLCK, &lock_set, SQUAT_TRIE_LOCK_TIMEOUT, &uidlist->file_lock, &error); if (ret < 0) {