diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c
index c82690fe94c61df045edc97a2da84d6905292ca7..95b988344e2cb726e10ea1470cfcc02fc9356b83 100644
--- a/src/sbbs3/ftpsrvr.c
+++ b/src/sbbs3/ftpsrvr.c
@@ -3558,7 +3558,7 @@ static void ctrl_thread(void* arg)
 		if(!stricmp(cmd, "SITE WHO")) {
 			sockprintf(sock,sess,"211-Active Telnet Nodes:");
 			for(i=0;i<scfg.sys_nodes && i<scfg.sys_lastnode;i++) {
-				if((result=getnodedat(&scfg, i+1, &node, 0))!=0) {
+				if((result=getnodedat(&scfg, i+1, &node, FALSE, NULL))!=0) {
 					sockprintf(sock,sess," Error %d getting data for Telnet Node %d",result,i+1);
 					continue;
 				}
diff --git a/src/sbbs3/js_system.c b/src/sbbs3/js_system.c
index 38c73acbdbcb1f8c7cca761b2b6e25a4518c4a9f..e3f6c5c4f40c515d12c41a79df2ffbf7f4170b96 100644
--- a/src/sbbs3/js_system.c
+++ b/src/sbbs3/js_system.c
@@ -2014,7 +2014,7 @@ static JSBool js_node_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 
 	rc=JS_SUSPENDREQUEST(cx);
 	memset(&node,0,sizeof(node));
-	if(getnodedat(cfg, node_num, &node, NULL)) {
+	if(getnodedat(cfg, node_num, &node, /* lockit: */FALSE, &cfg->nodefile)) {
 		JS_RESUMEREQUEST(cx, rc);
 		return(JS_TRUE);
 	}
@@ -2058,7 +2058,6 @@ static JSBool js_node_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict,
 {
 	jsval idval;
 	uint		node_num;
-	int			file;
 	jsint		val=0;
     jsint       tiny;
 	node_t		node;
@@ -2080,7 +2079,7 @@ static JSBool js_node_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict,
 
 	rc=JS_SUSPENDREQUEST(cx);
 	memset(&node,0,sizeof(node));
-	if(getnodedat(cfg, node_num, &node, &file)) {
+	if(getnodedat(cfg, node_num, &node, /* lockit: */TRUE, &cfg->nodefile)) {
 		JS_RESUMEREQUEST(cx, rc);
 		return(JS_TRUE);
 	}
@@ -2119,7 +2118,7 @@ static JSBool js_node_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict,
 			node.extaux=val;
 			break;
 	}
-	putnodedat(cfg,node_num,&node,file);
+	putnodedat(cfg,node_num,&node, /* closeit: */FALSE, cfg->nodefile);
 	JS_RESUMEREQUEST(cx, rc);
 
 	return(JS_TRUE);
@@ -2396,6 +2395,16 @@ static JSBool js_system_enumerate(JSContext *cx, JSObject *obj)
 	return(js_system_resolve(cx, obj, JSID_VOID));
 }
 
+static void js_system_finalize(JSContext *cx, JSObject *obj)
+{
+	scfg_t* cfg;
+
+	if((cfg = (scfg_t*)JS_GetPrivate(cx, obj)) == NULL)
+		return;
+
+	CLOSE_OPEN_FILE(cfg->nodefile);
+}
+
 JSClass js_system_class = {
      "System"				/* name			*/
     ,JSCLASS_HAS_PRIVATE	/* flags		*/
@@ -2406,7 +2415,7 @@ JSClass js_system_class = {
 	,js_system_enumerate	/* enumerate	*/
 	,js_system_resolve		/* resolve		*/
 	,JS_ConvertStub			/* convert		*/
-	,JS_FinalizeStub		/* finalize		*/
+	,js_system_finalize		/* finalize		*/
 };
 
 JSObject* DLLCALL js_CreateSystemObject(JSContext* cx, JSObject* parent
@@ -2423,6 +2432,9 @@ JSObject* DLLCALL js_CreateSystemObject(JSContext* cx, JSObject* parent
 	if(sysobj==NULL)
 		return(NULL);
 
+	if(cfg->nodefile < 1)	// An initialized scfg_t is usually all 0's
+		cfg->nodefile = -1;
+
 	if(!JS_SetPrivate(cx, sysobj, cfg))	/* Store a pointer to scfg_t */
 		return(NULL);
 
diff --git a/src/sbbs3/mailsrvr.c b/src/sbbs3/mailsrvr.c
index f72fc96868b17f93604f946fb0049ef3a5943f5c..285a009f4cfd60b7fd35dd2a27e158fd7bf3e871 100644
--- a/src/sbbs3/mailsrvr.c
+++ b/src/sbbs3/mailsrvr.c
@@ -3933,7 +3933,7 @@ static void smtp_thread(void* arg)
 					if(!(startup->options&MAIL_OPT_NO_NOTIFY) && usernum) {
 						if(newmsg.idx.to)
 							for(i=1;i<=scfg.sys_nodes;i++) {
-								getnodedat(&scfg, i, &node, 0);
+								getnodedat(&scfg, i, &node, FALSE, NULL);
 								if(node.useron==usernum
 									&& (node.status==NODE_INUSE || node.status==NODE_QUIET))
 									break;
@@ -4816,7 +4816,7 @@ static void smtp_thread(void* arg)
 			}
 			else if(cmd==SMTP_CMD_SEND) { /* Check if user online */
 				for(i=0;i<scfg.sys_nodes;i++) {
-					getnodedat(&scfg, i+1, &node, 0);
+					getnodedat(&scfg, i+1, &node, FALSE, NULL);
 					if(node.status==NODE_INUSE && node.useron==user.number
 						&& !(node.misc&NODE_POFF))
 						break;
diff --git a/src/sbbs3/sbbscon.c b/src/sbbs3/sbbscon.c
index a277a88c1683525aab453e89a560314eaba4a3d7..3c14ef48c448d34794531032eb8eda67be4b63ba 100644
--- a/src/sbbs3/sbbscon.c
+++ b/src/sbbs3/sbbscon.c
@@ -2190,7 +2190,7 @@ int main(int argc, char** argv)
 					printf("\n");
 					count=0;
 					for(i=1;i<=scfg.sys_nodes;i++) {
-						getnodedat(&scfg,i,&node,NULL /* file */);
+						getnodedat(&scfg,i,&node, /* lockit: */FALSE, NULL /* file */);
 						if(ch=='w' && node.status!=NODE_INUSE && node.status!=NODE_QUIET)
 							continue;
 						printnodedat(&scfg, i,&node);
@@ -2210,7 +2210,8 @@ int main(int argc, char** argv)
 						break;
 					fflush(stdin);
 					printf("\n");
-					if((i=getnodedat(&scfg,n,&node,&file))!=0) {
+					CLOSE_OPEN_FILE(file);
+					if((i=getnodedat(&scfg,n,&node, /* lockit: */TRUE, &file))!=0) {
 						printf("!Error %d getting node %d data\n",i,n);
 						break;
 					}
@@ -2225,7 +2226,7 @@ int main(int argc, char** argv)
 							node.misc^=NODE_INTR;
 							break;
 					}
-					putnodedat(&scfg,n,&node,file);
+					putnodedat(&scfg,n,&node,/* closeit: */TRUE, file);
 					printnodedat(&scfg,n,&node);
 #ifdef __unix__
 	                _echo_off(); /* turn off echoing - failsafe */
diff --git a/src/sbbs3/scfgdefs.h b/src/sbbs3/scfgdefs.h
index f9421474e482db2f8c75cf59a1ebc2936cf3a330..f9f07e7d5808f44f218146b27c7dd92214e38b06 100644
--- a/src/sbbs3/scfgdefs.h
+++ b/src/sbbs3/scfgdefs.h
@@ -613,7 +613,9 @@ typedef struct
 	uint16_t		user_backup_level;
 	uint16_t		mail_backup_level;
 
+	// Run-time state information (not configuration)
 	int				tls_certificate;
+	int				nodefile;
 
 } scfg_t;
 
diff --git a/src/sbbs3/scfgsave.c b/src/sbbs3/scfgsave.c
index 9d5386c35850e3298189351ac32328ff4f1b39a5..b5182d99c02c7e2aa9b2f0ecdf06502bde461feb 100644
--- a/src/sbbs3/scfgsave.c
+++ b/src/sbbs3/scfgsave.c
@@ -1096,16 +1096,17 @@ void DLLCALL refresh_cfg(scfg_t* cfg)
 {
 	char	str[MAX_PATH+1];
     int		i;
-	int		file;
+	int		file = -1;
     node_t	node;
     
     for(i=0;i<cfg->sys_nodes;i++) {
-       	if(getnodedat(cfg,i+1,&node,&file)!=0)
+       	if(getnodedat(cfg,i+1,&node, /* lockit: */TRUE, &file)!=0)
 			continue;
         node.misc|=NODE_RRUN;
-        if(putnodedat(cfg,i+1,&node,file))
+        if(putnodedat(cfg,i+1,&node, /* closeit: */FALSE, file))
             break;
     }
+	CLOSE_OPEN_FILE(file);
 
 	SAFEPRINTF(str,"%srecycle",cfg->ctrl_dir);		ftouch(str);
 }
diff --git a/src/sbbs3/userdat.c b/src/sbbs3/userdat.c
index 311bad88596c2166dfced7e5732cb8e983d1dd72..d74485b93ad3e78700373b2cdbb32b73a97d7695 100644
--- a/src/sbbs3/userdat.c
+++ b/src/sbbs3/userdat.c
@@ -455,23 +455,24 @@ int fgetuserdat(scfg_t* cfg, user_t *user, int file)
 /****************************************************************************/
 static void dirtyuserdat(scfg_t* cfg, uint usernumber)
 {
-	int	i,file;
+	int	i,file = -1;
     node_t	node;
 
 	for(i=1;i<=cfg->sys_nodes;i++) { /* instant user data update */
 //		if(i==cfg->node_num)
 //			continue;
-		if(getnodedat(cfg, i,&node,NULL) != 0)
+		if(getnodedat(cfg, i,&node, /* lockit: */FALSE, &file) != 0)
 			continue;
 		if(node.useron==usernumber && (node.status==NODE_INUSE
 			|| node.status==NODE_QUIET)) {
-			if(getnodedat(cfg, i,&node,&file) == 0) {
+			if(getnodedat(cfg, i,&node, /* lockit: */TRUE, &file) == 0) {
 				node.misc|=NODE_UDAT;
-				putnodedat(cfg, i,&node,file);
+				putnodedat(cfg, i,&node, /* closeit: */FALSE, file);
 			}
 			break;
 		}
 	}
+	CLOSE_OPEN_FILE(file);
 }
 
 /****************************************************************************/
@@ -479,14 +480,17 @@ static void dirtyuserdat(scfg_t* cfg, uint usernumber)
 int is_user_online(scfg_t* cfg, uint usernumber)
 {
 	int i;
+	int file = -1;
 	node_t	node;
 
 	for(i=1; i<=cfg->sys_nodes; i++) {
-		getnodedat(cfg, i, &node, 0);
+		getnodedat(cfg, i, &node, /* lockit: */FALSE, &file);
 		if((node.status==NODE_INUSE || node.status==NODE_QUIET
 			|| node.status==NODE_LOGON) && node.useron==usernumber)
 			return i;
 	}
+	if(file >= 0)
+		close(file);
 	return 0;
 }
 
@@ -766,28 +770,40 @@ uint getage(scfg_t* cfg, char *birth)
 	return(age);
 }
 
+/****************************************************************************/
+/****************************************************************************/
+int opennodedat(scfg_t* cfg)
+{
+	char	fname[MAX_PATH+1];
+
+	if(!VALID_CFG(cfg))
+		return -1;
+
+	SAFEPRINTF(fname, "%snode.dab", cfg->ctrl_dir);
+	return nopen(fname, O_RDWR|O_DENYNONE);
+}
+
 /****************************************************************************/
 /* Reads the data for node number 'number' into the structure 'node'        */
 /* from node.dab															*/
 /****************************************************************************/
-int getnodedat(scfg_t* cfg, uint number, node_t *node, int* fdp)
+int getnodedat(scfg_t* cfg, uint number, node_t *node, BOOL lockit, int* fdp)
 {
-	char	str[MAX_PATH+1];
 	int		rd;
 	int		count=0;
 	int		file;
 
-	if(fdp!=NULL)
-		*fdp=-1;
-
 	if(!VALID_CFG(cfg)
 		|| node==NULL || number<1 || number>cfg->sys_nodes)
 		return(-1);
 
 	memset(node,0,sizeof(node_t));
-	SAFEPRINTF(str,"%snode.dab",cfg->ctrl_dir);
-	if((file=nopen(str,O_RDWR|O_DENYNONE))==-1)
-		return(errno);
+	if(fdp != NULL && *fdp > 0)
+		file = *fdp;
+	else {
+		if((file = opennodedat(cfg)) == -1)
+			return errno;
+	}
 
 	if(filelength(file)>=(long)(number*sizeof(node_t))) {
 		number--;	/* make zero based */
@@ -795,7 +811,7 @@ int getnodedat(scfg_t* cfg, uint number, node_t *node, int* fdp)
 			if(count)
 				mswait(100);
 			lseek(file,(long)number*sizeof(node_t),SEEK_SET);
-			if(fdp!=NULL
+			if(lockit
 				&& lock(file,(long)number*sizeof(node_t),sizeof(node_t))!=0)
 				continue;
 			rd=read(file,node,sizeof(node_t));
@@ -820,7 +836,7 @@ int getnodedat(scfg_t* cfg, uint number, node_t *node, int* fdp)
 /****************************************************************************/
 /* Write the data from the structure 'node' into node.dab  					*/
 /****************************************************************************/
-int putnodedat(scfg_t* cfg, uint number, node_t* node, int file)
+int putnodedat(scfg_t* cfg, uint number, node_t* node, BOOL closeit, int file)
 {
 	size_t	wr=0;
 	int		wrerr=0;
@@ -843,7 +859,8 @@ int putnodedat(scfg_t* cfg, uint number, node_t* node, int file)
 		mswait(100);
 	}
 	unlock(file,(long)number*sizeof(node_t),sizeof(node_t));
-	close(file);
+	if(closeit)
+		close(file);
 
 	if(wr!=sizeof(node_t))
 		return(wrerr);
@@ -1244,17 +1261,19 @@ int putsmsg(scfg_t* cfg, int usernumber, char *strin)
 		return(errno);
 	}
 	close(file);
+	file = -1;
 	for(i=1;i<=cfg->sys_nodes;i++) {     /* flag node if user on that msg waiting */
-		getnodedat(cfg,i,&node,NULL);
+		getnodedat(cfg,i,&node,/* lockit: */FALSE, &file);
 		if(node.useron==usernumber
 			&& (node.status==NODE_INUSE || node.status==NODE_QUIET)
 			&& !(node.misc&NODE_MSGW)) {
-			if(getnodedat(cfg,i,&node,&file)==0) {
+			if(getnodedat(cfg,i,&node, /* lockit: */TRUE, &file)==0) {
 				node.misc|=NODE_MSGW;
-				putnodedat(cfg,i,&node,file);
+				putnodedat(cfg,i,&node, /* closeit: */FALSE, file);
 			}
 		}
 	}
+	CLOSE_OPEN_FILE(file);
 	return(0);
 }
 
@@ -1265,7 +1284,7 @@ char* getsmsg(scfg_t* cfg, int usernumber)
 {
 	char	str[MAX_PATH+1], *buf;
 	int		i;
-    int		file;
+    int		file = -1;
     long	length;
 	node_t	node;
 
@@ -1273,16 +1292,17 @@ char* getsmsg(scfg_t* cfg, int usernumber)
 		return(NULL);
 
 	for(i=1;i<=cfg->sys_nodes;i++) {	/* clear msg waiting flag */
-		getnodedat(cfg,i,&node,NULL);
+		getnodedat(cfg,i,&node, /* lockit: */FALSE, &file);
 		if(node.useron==usernumber
 			&& (node.status==NODE_INUSE || node.status==NODE_QUIET)
 			&& node.misc&NODE_MSGW) {
-			if(getnodedat(cfg,i,&node,&file) == 0) {
+			if(getnodedat(cfg,i,&node, /* lockit: */TRUE, &file) == 0) {
 				node.misc&=~NODE_MSGW;
-				putnodedat(cfg,i,&node,file);
+				putnodedat(cfg,i,&node, /* closeit: */FALSE, file);
 			}
 		}
 	}
+	CLOSE_OPEN_FILE(file);
 
 	SAFEPRINTF2(str,"%smsgs/%4.4u.msg",cfg->data_dir,usernumber);
 	if(flength(str)<1L)
@@ -1311,16 +1331,16 @@ char* getnmsg(scfg_t* cfg, int node_num)
 {
 	char	str[MAX_PATH+1];
 	char*	buf;
-	int		file;
+	int		file = -1;
 	long	length;
 	node_t	node;
 
 	if(!VALID_CFG(cfg) || node_num<1)
 		return(NULL);
 
-	if(getnodedat(cfg,node_num,&node,&file) == 0) {
+	if(getnodedat(cfg,node_num,&node, /* lockit: */TRUE, &file) == 0) {
 		node.misc&=~NODE_NMSG;          /* clear the NMSG flag */
-		putnodedat(cfg,node_num,&node,file);
+		putnodedat(cfg,node_num,&node, /* closeit: */TRUE, file);
 	}
 
 	SAFEPRINTF2(str,"%smsgs/n%3.3u.msg",cfg->data_dir,node_num);
@@ -1373,15 +1393,16 @@ int putnmsg(scfg_t* cfg, int num, char *strin)
 		close(file);
 		return(errno);
 	}
-	close(file);
-	getnodedat(cfg,num,&node,NULL);
+	CLOSE_OPEN_FILE(file);
+	getnodedat(cfg,num,&node, /* lockit: */FALSE, &file);
 	if((node.status==NODE_INUSE || node.status==NODE_QUIET)
 		&& !(node.misc&NODE_NMSG)) {
-		if(getnodedat(cfg,num,&node,&file) == 0) {
+		if(getnodedat(cfg,num,&node, /* lockit: */TRUE, &file) == 0) {
 			node.misc|=NODE_NMSG;
-			putnodedat(cfg,num,&node,file);
+			putnodedat(cfg,num,&node, /* closeit: */FALSE, file);
 		}
 	}
+	CLOSE_OPEN_FILE(file);
 
 	return(0);
 }
diff --git a/src/sbbs3/userdat.h b/src/sbbs3/userdat.h
index 5030c83ad16a44c5fc0ecd3ee6c1b950bcbbad3d..0945463c308ccad4e21aaf9fa3908ed8efa45438 100644
--- a/src/sbbs3/userdat.h
+++ b/src/sbbs3/userdat.h
@@ -82,8 +82,9 @@ DLLEXPORT BOOL	del_lastuser(scfg_t*);
 DLLEXPORT uint	getage(scfg_t*, char *birthdate);
 DLLEXPORT char*	username(scfg_t*, int usernumber, char * str);
 DLLEXPORT char* usermailaddr(scfg_t*, char* addr, const char* name);
-DLLEXPORT int	getnodedat(scfg_t*, uint number, node_t *node, int* file);
-DLLEXPORT int	putnodedat(scfg_t*, uint number, node_t *node, int file);
+DLLEXPORT int	opennodedat(scfg_t*);
+DLLEXPORT int	getnodedat(scfg_t*, uint number, node_t *node, BOOL lockit, int* file);
+DLLEXPORT int	putnodedat(scfg_t*, uint number, node_t *node, BOOL closeit, int file);
 DLLEXPORT char* nodestatus(scfg_t*, node_t* node, char* buf, size_t buflen);
 DLLEXPORT void	printnodedat(scfg_t*, uint number, node_t* node);
 DLLEXPORT int	is_user_online(scfg_t*, uint usernumber);