summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordormando <dormando@rydia.net>2020-11-10 22:20:10 -0800
committerdormando <dormando@rydia.net>2020-11-11 21:23:35 -0800
commit2251d98cd7ecb7dd4b2f58fe9b97f5799eb39edc (patch)
tree089383cc7b09fdf7b98e778b354ce612200cf8bd
parent19aac5aeca5aaf588dc58d5c04264c6be14ff7c0 (diff)
downloadmemcached-2251d98cd7ecb7dd4b2f58fe9b97f5799eb39edc.tar.gz
extstore: use fcntl locking on disk file
if you accidentally start memcached with the same options twice, extstore is initiated before the listener sockets and will happily truncate its own file. So this avoids that. Keep in mind any other process can still wipe the file clean!
-rw-r--r--extstore.c21
-rw-r--r--t/extstore.t5
2 files changed, 24 insertions, 2 deletions
diff --git a/extstore.c b/extstore.c
index 9e2a981..8f32f51 100644
--- a/extstore.c
+++ b/extstore.c
@@ -253,15 +253,32 @@ void *extstore_init(struct extstore_conf_file *fh, struct extstore_conf *cf,
e->page_size = cf->page_size;
uint64_t temp_page_count = 0;
for (f = fh; f != NULL; f = f->next) {
- f->fd = open(f->file, O_RDWR | O_CREAT | O_TRUNC, 0644);
+ f->fd = open(f->file, O_RDWR | O_CREAT, 0644);
if (f->fd < 0) {
*res = EXTSTORE_INIT_OPEN_FAIL;
#ifdef EXTSTORE_DEBUG
- perror("open");
+ perror("extstore open");
#endif
free(e);
return NULL;
}
+ // use an fcntl lock to help avoid double starting.
+ struct flock lock;
+ lock.l_type = F_WRLCK;
+ lock.l_start = 0;
+ lock.l_whence = SEEK_SET;
+ lock.l_len = 0;
+ if (fcntl(f->fd, F_SETLK, &lock) < 0) {
+ *res = EXTSTORE_INIT_OPEN_FAIL;
+ free(e);
+ return NULL;
+ }
+ if (ftruncate(f->fd, 0) < 0) {
+ *res = EXTSTORE_INIT_OPEN_FAIL;
+ free(e);
+ return NULL;
+ }
+
temp_page_count += f->page_count;
f->offset = 0;
}
diff --git a/t/extstore.t b/t/extstore.t
index 1790a54..116a7f4 100644
--- a/t/extstore.t
+++ b/t/extstore.t
@@ -20,6 +20,11 @@ $ext_path = "/tmp/extstore.$$";
my $server = new_memcached("-m 64 -U 0 -o ext_page_size=8,ext_wbuf_size=2,ext_threads=1,ext_io_depth=2,ext_item_size=512,ext_item_age=2,ext_recache_rate=10000,ext_max_frag=0.9,ext_path=$ext_path:64m,slab_automove=0,ext_compact_under=1");
my $sock = $server->sock;
+eval {
+ my $server = new_memcached("-o ext_path=$ext_path:64m");
+};
+ok($@, "failed to start a second server with the same file path");
+
# Wait until all items have flushed
sub wait_for_ext {
my $target = shift || 0;