diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c
index da1801f7ba9f50ae40f13e471679f41a1913d730..4fc2e822840a102de4fac191b5061c45ccc1c431 100644
--- a/src/sbbs3/ftpsrvr.c
+++ b/src/sbbs3/ftpsrvr.c
@@ -2607,7 +2607,7 @@ static void ctrl_thread(void* arg)
 			if(user.rest & FLAG('Q')) { // QWKnet accont
 				char mutex_fname[MAX_PATH + 1];
 				snprintf(mutex_fname, sizeof mutex_fname, "%suser/%04u.ftp", scfg.data_dir, user.number);
-				if(!fmutex_open(mutex_fname, startup->host_name, /* max_age: */60 * 60, /* auto_remove: */true, &mutex_file)) {
+				if(!fmutex_open(mutex_fname, startup->host_name, /* max_age: */60 * 60, &mutex_file)) {
 					lprintf(LOG_NOTICE, "%04d <%s> QWKnet account already logged-in to FTP server: %s (since %s)"
 						,sock, user.alias, mutex_fname, time_as_hhmm(&scfg, mutex_file.time, str));
 					sockprintf(sock, sess, "421 QWKnet accounts are limited to one concurrent FTP session");
diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp
index 1b0d511aeb734105254f7c62c99667a95f958ade..eeff877bd4dffffe55a3722d23791b7ab9aaabcb 100644
--- a/src/sbbs3/main.cpp
+++ b/src/sbbs3/main.cpp
@@ -2920,7 +2920,7 @@ void event_thread(void* arg)
 				if(sbbs->useron.number != 0 && !(sbbs->useron.misc&(DELETED|INACTIVE))) {
 					SAFEPRINTF(lockfname,"%s.lock",fname);
 					fmutex_t lockfile;
-					if(!fmutex_open(lockfname, startup->host_name, TIMEOUT_MUTEX_FILE, true, &lockfile)) {
+					if(!fmutex_open(lockfname, startup->host_name, TIMEOUT_MUTEX_FILE, &lockfile)) {
 						if(difftime(time(NULL), lockfile.time) > 60)
 							sbbs->lprintf(LOG_INFO," %s exists (unpack in progress?) since %s", lockfname, time_as_hhmm(&sbbs->cfg, lockfile.time, str));
 						continue;
@@ -2988,7 +2988,7 @@ void event_thread(void* arg)
 				}
 				SAFEPRINTF2(lockfname,"%spack%04u.lock",sbbs->cfg.data_dir,usernum);
 				fmutex_t lockfile;
-				if(!fmutex_open(lockfname,startup->host_name,TIMEOUT_MUTEX_FILE, true, &lockfile)) {
+				if(!fmutex_open(lockfname, startup->host_name, TIMEOUT_MUTEX_FILE, &lockfile)) {
 					if(difftime(time(NULL), lockfile.time) > 60)
 						sbbs->lprintf(LOG_INFO,"%s exists (pack in progress?) since %s", lockfname, time_as_hhmm(&sbbs->cfg, lockfile.time, str));
 					continue;
diff --git a/src/sbbs3/nopen.c b/src/sbbs3/nopen.c
index c176330ac3491d0a7b7ba675bde0ecb0574f0492..8d2e0cb34017086b93650d91d298326c6775e50f 100644
--- a/src/sbbs3/nopen.c
+++ b/src/sbbs3/nopen.c
@@ -112,8 +112,9 @@ bool ftouch(const char* fname)
 	return true;
 }
 
-// Opens a mutex file and returns its file descriptor or -1 on failure
-bool fmutex_open(const char* fname, const char* text, long max_age, bool auto_remove, fmutex_t* fm)
+// Opens a mutex file (implementation)
+static
+bool _fmutex_open(const char* fname, const char* text, long max_age, bool auto_remove, fmutex_t* fm)
 {
 	size_t len;
 #if !defined(NO_SOCKET_SUPPORT)
@@ -128,7 +129,6 @@ bool fmutex_open(const char* fname, const char* text, long max_age, bool auto_re
 		return false;
 	memset(fm, 0, sizeof *fm);
 	snprintf(fm->name, sizeof fm->name, fname);
-	fm->remove = auto_remove;
 	if(max_age > 0) {
 		fm->time = fdate(fname);
 		if(max_age > 0 && fm->time != -1 && (time(NULL) - fm->time) > max_age) {
@@ -171,6 +171,12 @@ bool fmutex_open(const char* fname, const char* text, long max_age, bool auto_re
 	return true;
 }
 
+// Opens a mutex file (public API: always auto-removes upon close)
+bool fmutex_open(const char* fname, const char* text, long max_age, fmutex_t* fm)
+{
+	return _fmutex_open(fname, text, max_age, /* auto-remove: */true, fm);
+}
+
 bool fmutex_close(fmutex_t* fm)
 {
 	if(fm == NULL)
@@ -178,10 +184,8 @@ bool fmutex_close(fmutex_t* fm)
 	if(fm->fd < 0) // already closed (or never opened)
 		return true;
 #if !defined _WIN32 // should only be necessary (and possible) on *nix
-	if(fm->remove) {
-		if(unlink(fm->name) != 0)
-			return false;
-	}
+	if(unlink(fm->name) != 0)
+		return false;
 #endif
 	if(close(fm->fd) != 0)
 		return false;
@@ -194,12 +198,12 @@ bool fmutex(const char* fname, const char* text, long max_age, time_t* tp)
 {
 	fmutex_t fm;
 
-	if(!fmutex_open(fname, text, max_age, /* auto_remove: */false, &fm)) {
+	if(!_fmutex_open(fname, text, max_age, /* auto_remove: */false, &fm)) {
 		if(tp != NULL)
 			*tp = fm.time;
 		return false;
 	}
-	return fmutex_close(&fm);
+	return close(fm.fd) == 0;
 }
 
 bool fcompare(const char* fn1, const char* fn2)
diff --git a/src/sbbs3/nopen.h b/src/sbbs3/nopen.h
index 067acbd9de73ad1bf186ca5afb11cad51deb5b76..6c25d5065a2acfb7dcb4af041f2ebb1f6a206fc8 100644
--- a/src/sbbs3/nopen.h
+++ b/src/sbbs3/nopen.h
@@ -33,7 +33,6 @@
 typedef struct {
 	int fd;
 	time_t time;
-	bool remove;
 	char name[MAX_PATH + 1];
 } fmutex_t;
 
@@ -45,7 +44,7 @@ int		nopen(const char* str, uint access);
 FILE *	fnopen(int* file, const char* str, uint access);
 bool	ftouch(const char* fname);
 bool	fmutex(const char* fname, const char* text, long max_age, time_t*);
-bool	fmutex_open(const char* fname, const char* text, long max_age, bool auto_remove, fmutex_t*);
+bool	fmutex_open(const char* fname, const char* text, long max_age, fmutex_t*);
 bool	fmutex_close(fmutex_t*);
 bool	fcompare(const char* fn1, const char* fn2);
 bool	backup(const char* org, int backup_level, bool ren);