diff -Nurp memcached-1.5.10/assoc.c memcached-1.5.10-old/assoc.c --- memcached-1.5.10/assoc.c 2018-07-18 16:39:52.000000000 -0400 +++ memcached-1.5.10-old/assoc.c 2019-05-31 04:34:02.477000000 -0400 @@ -28,7 +28,7 @@ static pthread_cond_t maintenance_cond = PTHREAD_COND_INITIALIZER; static pthread_mutex_t maintenance_lock = PTHREAD_MUTEX_INITIALIZER; -typedef unsigned long int ub4; /* unsigned 4-byte quantities */ +typedef uint32_t ub4; /* unsigned 4-byte quantities */ typedef unsigned char ub1; /* unsigned 1-byte quantities */ /* how many powers of 2's worth of buckets we use */ diff -Nurp memcached-1.5.10/cache.h memcached-1.5.10-old/cache.h --- memcached-1.5.10/cache.h 2018-07-18 16:39:52.000000000 -0400 +++ memcached-1.5.10-old/cache.h 2019-05-31 04:34:36.887000000 -0400 @@ -34,8 +34,7 @@ typedef int cache_constructor_t(void* ob * returned to the operating system. * * @param obj pointer to the object to clean up. - * @param notused1 This parameter is currently not used. - * @param notused2 This parameter is currently not used. + * @param notused This parameter is currently not used. * @return you should return 0, but currently this is not checked */ typedef void cache_destructor_t(void* obj, void* notused); diff -Nurp memcached-1.5.10/configure.ac memcached-1.5.10-old/configure.ac --- memcached-1.5.10/configure.ac 2018-07-18 16:39:52.000000000 -0400 +++ memcached-1.5.10-old/configure.ac 2019-05-31 04:35:28.038000000 -0400 @@ -629,9 +629,18 @@ AC_CHECK_FUNCS(pledge, [ ], []) ],[]) +AC_CHECK_FUNCS(cap_enter, [ + AC_CHECK_HEADER(sys/capsicum.h, [ + AC_DEFINE([HAVE_DROP_PRIVILEGES], 1, + [Define this if you have an implementation of drop_privileges()]) + build_freebsd_privs=yes + ], []) +],[]) + AM_CONDITIONAL([BUILD_SOLARIS_PRIVS],[test "$build_solaris_privs" = "yes"]) AM_CONDITIONAL([BUILD_LINUX_PRIVS],[test "$build_linux_privs" = "yes"]) AM_CONDITIONAL([BUILD_OPENBSD_PRIVS],[test "$build_openbsd_privs" = "yes"]) +AM_CONDITIONAL([BUILD_FREEBSD_PRIVS],[test "$build_freebsd_privs" = "yes"]) AC_CHECK_HEADER(umem.h, [ AC_DEFINE([HAVE_UMEM_H], 1, diff -Nurp memcached-1.5.10/doc/protocol.txt memcached-1.5.10-old/doc/protocol.txt --- memcached-1.5.10/doc/protocol.txt 2018-07-18 16:39:52.000000000 -0400 +++ memcached-1.5.10-old/doc/protocol.txt 2019-05-31 04:35:50.981000000 -0400 @@ -633,7 +633,7 @@ memcache developers. General-purpose statistics -------------------------- -Upon receiving the "stats" command without arguments, the server sents +Upon receiving the "stats" command without arguments, the server sends a number of lines which look like this: STAT \r\n diff -Nurp memcached-1.5.10/extstore.c memcached-1.5.10-old/extstore.c --- memcached-1.5.10/extstore.c 2018-08-10 16:36:38.000000000 -0400 +++ memcached-1.5.10-old/extstore.c 2019-05-31 04:37:14.321000000 -0400 @@ -183,6 +183,9 @@ const char *extstore_err(enum extstore_r case EXTSTORE_INIT_PAGE_WBUF_ALIGNMENT: rv = "page_size and wbuf_size must be divisible by 1024*1024*2"; break; + case EXTSTORE_INIT_TOO_MANY_PAGES: + rv = "page_count must total to < 65536. Increase page_size or lower path sizes"; + break; case EXTSTORE_INIT_OOM: rv = "failed calloc for engine"; break; @@ -230,6 +233,7 @@ void *extstore_init(struct extstore_conf } 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); if (f->fd < 0) { @@ -240,10 +244,17 @@ void *extstore_init(struct extstore_conf free(e); return NULL; } - e->page_count += f->page_count; + temp_page_count += f->page_count; f->offset = 0; } + if (temp_page_count >= UINT16_MAX) { + *res = EXTSTORE_INIT_TOO_MANY_PAGES; + free(e); + return NULL; + } + e->page_count = temp_page_count; + e->pages = calloc(e->page_count, sizeof(store_page)); if (e->pages == NULL) { *res = EXTSTORE_INIT_OOM; diff -Nurp memcached-1.5.10/extstore.h memcached-1.5.10-old/extstore.h --- memcached-1.5.10/extstore.h 2018-08-10 16:36:38.000000000 -0400 +++ memcached-1.5.10-old/extstore.h 2019-05-31 04:37:35.803000000 -0400 @@ -93,6 +93,7 @@ enum extstore_res { EXTSTORE_INIT_NEED_MORE_WBUF, EXTSTORE_INIT_NEED_MORE_BUCKETS, EXTSTORE_INIT_PAGE_WBUF_ALIGNMENT, + EXTSTORE_INIT_TOO_MANY_PAGES, EXTSTORE_INIT_OOM, EXTSTORE_INIT_OPEN_FAIL, EXTSTORE_INIT_THREAD_FAIL diff -Nurp memcached-1.5.10/freebsd_priv.c memcached-1.5.10-old/freebsd_priv.c --- memcached-1.5.10/freebsd_priv.c 1969-12-31 19:00:00.000000000 -0500 +++ memcached-1.5.10-old/freebsd_priv.c 2019-05-31 04:37:52.842000000 -0400 @@ -0,0 +1,18 @@ +#include +#include +#include +#include +#include +#include +#include "memcached.h" + +/* + * * dropping privileges is entering in capability mode + * * in FreeBSD vocabulary. + * */ +void drop_privileges() { + if (cap_enter() != 0) { + fprintf(stderr, "cap_enter failed: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } +} diff -Nurp memcached-1.5.10/logger.c memcached-1.5.10-old/logger.c --- memcached-1.5.10/logger.c 2018-08-10 16:36:38.000000000 -0400 +++ memcached-1.5.10-old/logger.c 2019-05-31 04:38:14.125000000 -0400 @@ -6,6 +6,7 @@ #include #include #include +#include #if defined(__sun) #include diff -Nurp memcached-1.5.10/Makefile.am memcached-1.5.10-old/Makefile.am --- memcached-1.5.10/Makefile.am 2018-07-18 16:39:52.000000000 -0400 +++ memcached-1.5.10-old/Makefile.am 2019-05-31 04:33:27.162000000 -0400 @@ -42,6 +42,10 @@ if BUILD_OPENBSD_PRIVS memcached_SOURCES += openbsd_priv.c endif +if BUILD_FREEBSD_PRIVS +memcached_SOURCES += freebsd_priv.c +endif + if ENABLE_SASL memcached_SOURCES += sasl_defs.c endif diff -Nurp memcached-1.5.10/memcached.c memcached-1.5.10-old/memcached.c --- memcached-1.5.10/memcached.c 2018-08-10 16:36:38.000000000 -0400 +++ memcached-1.5.10-old/memcached.c 2019-05-31 04:48:48.677000000 -0400 @@ -55,6 +55,10 @@ #include #endif +#if defined(__FreeBSD__) +#include +#endif + /* FreeBSD 4.x doesn't have IOV_MAX exposed. */ #ifndef IOV_MAX #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__GNU__) @@ -2599,6 +2603,7 @@ static void process_bin_flush(conn *c) { static void process_bin_delete(conn *c) { item *it; + uint32_t hv; protocol_binary_request_delete* req = binary_get_request(c); @@ -2620,7 +2625,7 @@ static void process_bin_delete(conn *c) stats_prefix_record_delete(key, nkey); } - it = item_get(key, nkey, c, DONT_UPDATE); + it = item_get_locked(key, nkey, c, DONT_UPDATE, &hv); if (it) { uint64_t cas = ntohll(req->message.header.request.cas); if (cas == 0 || cas == ITEM_get_cas(it)) { @@ -2628,19 +2633,20 @@ static void process_bin_delete(conn *c) pthread_mutex_lock(&c->thread->stats.mutex); c->thread->stats.slab_stats[ITEM_clsid(it)].delete_hits++; pthread_mutex_unlock(&c->thread->stats.mutex); - item_unlink(it); + do_item_unlink(it, hv); STORAGE_delete(c->thread->storage, it); write_bin_response(c, NULL, 0, 0, 0); } else { write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, NULL, 0); } - item_remove(it); /* release our reference */ + do_item_remove(it); /* release our reference */ } else { write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0); pthread_mutex_lock(&c->thread->stats.mutex); c->thread->stats.delete_misses++; pthread_mutex_unlock(&c->thread->stats.mutex); } + item_unlock(hv); } static void complete_nread_binary(conn *c) { @@ -4323,6 +4329,7 @@ static void process_delete_command(conn char *key; size_t nkey; item *it; + uint32_t hv; assert(c != NULL); @@ -4351,7 +4358,7 @@ static void process_delete_command(conn stats_prefix_record_delete(key, nkey); } - it = item_get(key, nkey, c, DONT_UPDATE); + it = item_get_locked(key, nkey, c, DONT_UPDATE, &hv); if (it) { MEMCACHED_COMMAND_DELETE(c->sfd, ITEM_key(it), it->nkey); @@ -4359,9 +4366,9 @@ static void process_delete_command(conn c->thread->stats.slab_stats[ITEM_clsid(it)].delete_hits++; pthread_mutex_unlock(&c->thread->stats.mutex); - item_unlink(it); + do_item_unlink(it, hv); STORAGE_delete(c->thread->storage, it); - item_remove(it); /* release our reference */ + do_item_remove(it); /* release our reference */ out_string(c, "DELETED"); } else { pthread_mutex_lock(&c->thread->stats.mutex); @@ -4370,6 +4377,7 @@ static void process_delete_command(conn out_string(c, "NOT_FOUND"); } + item_unlock(hv); } static void process_verbosity_command(conn *c, token_t *tokens, const size_t ntokens) { @@ -4536,7 +4544,7 @@ static void process_lru_command(conn *c, out_string(c, "OK"); } } - } else if (strcmp(tokens[1].value, "mode") == 0 && ntokens >= 3 && + } else if (strcmp(tokens[1].value, "mode") == 0 && ntokens >= 4 && settings.lru_maintainer_thread) { if (strcmp(tokens[2].value, "flat") == 0) { settings.lru_segmented = false; @@ -4547,7 +4555,7 @@ static void process_lru_command(conn *c, } else { out_string(c, "ERROR"); } - } else if (strcmp(tokens[1].value, "temp_ttl") == 0 && ntokens >= 3 && + } else if (strcmp(tokens[1].value, "temp_ttl") == 0 && ntokens >= 4 && settings.lru_maintainer_thread) { if (!safe_strtol(tokens[2].value, &ttl)) { out_string(c, "ERROR"); @@ -6233,7 +6241,7 @@ static void usage(void) { printf("-b, --listen-backlog= set the backlog queue limit (default: 1024)\n"); printf("-B, --protocol= protocol - one of ascii, binary, or auto (default)\n"); printf("-I, --max-item-size= adjusts max item size\n" - " (default: 1mb, min: 1k, max: 128m)\n"); + " (default: 1mb, min: 1k, max: 1024m)\n"); #ifdef ENABLE_SASL printf("-S, --enable-sasl turn on Sasl authentication\n"); #endif @@ -6491,6 +6499,21 @@ static int enable_large_pages(void) { return -1; } return 0; +#elif defined(__FreeBSD__) + int spages; + size_t spagesl = sizeof(spages); + + if (sysctlbyname("vm.pmap.pg_ps_enabled", &spages, + &spagesl, NULL, 0) != 0) { + fprintf(stderr, "Could not evaluate the presence of superpages features."); + return -1; + } + if (spages != 1) { + fprintf(stderr, "Superpages support not detected.\n"); + fprintf(stderr, "Will use default page size.\n"); + return -1; + } + return 0; #else return -1; #endif @@ -6951,7 +6974,7 @@ int main (int argc, char **argv) { preallocate = true; } else { fprintf(stderr, "Cannot enable large pages on this system\n" - "(There is no Linux support as of this version)\n"); + "(There is no support as of this version)\n"); return 1; } break; @@ -7247,6 +7270,10 @@ int main (int argc, char **argv) { break; #ifdef EXTSTORE case EXT_PAGE_SIZE: + if (storage_file) { + fprintf(stderr, "Must specify ext_page_size before any ext_path arguments\n"); + return 1; + } if (subopts_value == NULL) { fprintf(stderr, "Missing ext_page_size argument\n"); return 1; diff -Nurp memcached-1.5.10/memcached.h memcached-1.5.10-old/memcached.h --- memcached-1.5.10/memcached.h 2018-08-10 16:36:38.000000000 -0400 +++ memcached-1.5.10-old/memcached.h 2019-05-31 04:49:17.529000000 -0400 @@ -756,6 +756,7 @@ item *item_alloc(char *key, size_t nkey, #define DO_UPDATE true #define DONT_UPDATE false item *item_get(const char *key, const size_t nkey, conn *c, const bool do_update); +item *item_get_locked(const char *key, const size_t nkey, conn *c, const bool do_update, uint32_t *hv); item *item_touch(const char *key, const size_t nkey, uint32_t exptime, conn *c); int item_link(item *it); void item_remove(item *it); diff -Nurp memcached-1.5.10/scripts/memcached-tool memcached-1.5.10-old/scripts/memcached-tool --- memcached-1.5.10/scripts/memcached-tool 2018-07-18 16:39:52.000000000 -0400 +++ memcached-1.5.10-old/scripts/memcached-tool 2019-05-31 05:40:26.276000000 -0400 @@ -98,13 +98,13 @@ if ($mode eq 'dump') { # return format looks like this # key=foo exp=2147483647 la=1521046038 cas=717111 fetch=no cls=13 size=1232 if (/^key=(\S+) exp=(-?\d+) .*/) { - my $k = $1; + my ($k, $exp) = ($1, $2); $k =~ s/%(.{2})/chr hex $1/eg; - if ($2 == -1) { + if ($exp == -1) { $keyexp{$k} = 0; } else { - $keyexp{$k} = $2; + $keyexp{$k} = $exp; } } $keycount++; diff -Nurp memcached-1.5.10/thread.c memcached-1.5.10-old/thread.c --- memcached-1.5.10/thread.c 2018-08-10 16:36:38.000000000 -0400 +++ memcached-1.5.10-old/thread.c 2019-05-31 05:42:46.727000000 -0400 @@ -566,6 +566,17 @@ item *item_get(const char *key, const si return it; } +// returns an item with the item lock held. +// lock will still be held even if return is NULL, allowing caller to replace +// an item atomically if desired. +item *item_get_locked(const char *key, const size_t nkey, conn *c, const bool do_update, uint32_t *hv) { + item *it; + *hv = hash(key, nkey); + item_lock(*hv); + it = do_item_get(key, nkey, *hv, c, do_update); + return it; +} + item *item_touch(const char *key, size_t nkey, uint32_t exptime, conn *c) { item *it; uint32_t hv;