From 9bd0d68e7711a593c1f071c76cdb513918523462 Mon Sep 17 00:00:00 2001
From: "Rob Swindell (on macOS)" <rob@synchro.net>
Date: Tue, 31 Dec 2024 19:07:37 -0800
Subject: [PATCH] Check return value of lseek() before reading/writing node.dab
 records

I don't know if these seek failures are actually happening or not, but
reading from or writing to the wrong offset in the node.dab file could
explain some of the node.dab corruption I'm seeing from macOS (over
SMB share).
---
 src/sbbs3/getnode.cpp | 18 +++++++++---------
 src/sbbs3/putnode.cpp | 11 ++++++-----
 src/sbbs3/userdat.c   | 38 ++++++++++++++++++++++++++------------
 src/sbbs3/userdat.h   |  2 ++
 4 files changed, 43 insertions(+), 26 deletions(-)

diff --git a/src/sbbs3/getnode.cpp b/src/sbbs3/getnode.cpp
index 4ec584b233..475e59fda4 100644
--- a/src/sbbs3/getnode.cpp
+++ b/src/sbbs3/getnode.cpp
@@ -55,18 +55,18 @@ bool sbbs_t::getnodedat(uint number, node_t *node, bool lockit)
 	else
 		utime(str,NULL);		/* NFS fix... utime() forces a cache refresh */
 #endif
-	number--;	/* make zero based */
 	for(count=0;count<LOOP_NODEDAB;count++) {
-		if(lockit && lock(nodefile,(long)number*sizeof(node_t),sizeof(node_t))!=0) {
-			unlock(nodefile,(long)number*sizeof(node_t),sizeof(node_t));
+		if(lockit && lock(nodefile, nodedatoffset(number), sizeof(node_t))!=0) {
+			unlock(nodefile, nodedatoffset(number), sizeof(node_t));
 			continue;
 		}
-		lseek(nodefile,(long)number*sizeof(node_t),SEEK_SET);
-		rd=read(nodefile,node,sizeof(node_t));
-		if(rd!=sizeof(node_t))
-			unlock(nodefile,(long)number*sizeof(node_t),sizeof(node_t));
-		if(rd==sizeof(node_t))
-			break;
+		if(seeknodedat(nodefile, number)) {
+			rd=read(nodefile,node,sizeof(node_t));
+			if(rd!=sizeof(node_t))
+				unlock(nodefile, nodedatoffset(number), sizeof(node_t));
+			if(rd==sizeof(node_t))
+				break;
+		}
 		FILE_RETRY_DELAY(count + 1);
 	}
 	if(!lockit && cfg.node_misc&NM_CLOSENODEDAB) {
diff --git a/src/sbbs3/putnode.cpp b/src/sbbs3/putnode.cpp
index e8b9c183ae..4ebb40427a 100644
--- a/src/sbbs3/putnode.cpp
+++ b/src/sbbs3/putnode.cpp
@@ -60,11 +60,12 @@ bool sbbs_t::putnodedat(uint number, node_t* node)
 	}
 
 	for(attempts=0;attempts<10;attempts++) {
-		lseek(nodefile, (number - 1) * sizeof(node_t), SEEK_SET);
-		wr=write(nodefile,node,sizeof(node_t));
-		if(wr==sizeof(node_t))
-			break;
-		wrerr=errno;	/* save write error */
+		if(seeknodedat(nodefile, number)) {
+			wr=write(nodefile,node,sizeof(node_t));
+			if(wr==sizeof(node_t))
+				break;
+			wrerr=errno;	/* save write error */
+		}
 		FILE_RETRY_DELAY(attempts + 1);
 	}
 	unlocknodedat(number);
diff --git a/src/sbbs3/userdat.c b/src/sbbs3/userdat.c
index 4b77d6d108..42c6ab0a83 100644
--- a/src/sbbs3/userdat.c
+++ b/src/sbbs3/userdat.c
@@ -1101,6 +1101,16 @@ int opennodedat(scfg_t* cfg)
 	return nopen(fname, O_RDWR | O_CREAT | O_DENYNONE);
 }
 
+off_t nodedatoffset(unsigned node_number)
+{
+	return (node_number - 1) * sizeof(node_t);
+}
+
+bool seeknodedat(int file, unsigned node_number)
+{
+	return lseek(file, nodedatoffset(node_number), SEEK_SET) == nodedatoffset(node_number);
+}
+
 /****************************************************************************/
 /****************************************************************************/
 int opennodeext(scfg_t* cfg)
@@ -1138,18 +1148,20 @@ int getnodedat(scfg_t* cfg, uint number, node_t *node, bool lockit, int* fdp)
 
 	int result = USER_SIZE_ERROR;
 	if(filelength(file)>=(long)(number*sizeof(node_t))) {
-		number--;	/* make zero based */
 		for(count=0;count<LOOP_NODEDAB;count++) {
-			(void)lseek(file,(long)number*sizeof(node_t),SEEK_SET);
+			if(!seeknodedat(file, number)) {
+				result = USER_SEEK_ERROR;
+				continue;
+			}
 			if(lockit
-				&& lock(file,(long)number*sizeof(node_t),sizeof(node_t))!=0) {
+				&& lock(file, nodedatoffset(number), sizeof(node_t))!=0) {
 				result = USER_LOCK_ERROR;
 				continue;
 			}
 			rd=read(file,node,sizeof(node_t));
 			if(rd!=sizeof(node_t)) {
 				result = USER_READ_ERROR;
-				unlock(file,(long)number*sizeof(node_t),sizeof(node_t));
+				unlock(file, nodedatoffset(number), sizeof(node_t));
 			} else {
 				result = USER_SUCCESS;
 				break;
@@ -1172,8 +1184,8 @@ int getnodedat(scfg_t* cfg, uint number, node_t *node, bool lockit, int* fdp)
 /****************************************************************************/
 int putnodedat(scfg_t* cfg, uint number, node_t* node, bool closeit, int file)
 {
-	size_t	wr=0;
 	int		attempts;
+	int		result = -1;
 
 	if(file<0)
 		return USER_INVALID_ARG;
@@ -1184,20 +1196,22 @@ int putnodedat(scfg_t* cfg, uint number, node_t* node, bool closeit, int file)
 		return USER_INVALID_ARG;
 	}
 
-	number--;	/* make zero based */
 	for(attempts=0;attempts<LOOP_USERDAT;attempts++) {
-		(void)lseek(file,(long)number*sizeof(node_t),SEEK_SET);
-		if((wr=write(file,node,sizeof(node_t)))==sizeof(node_t))
+		if(!seeknodedat(file, number))
+			result = USER_SEEK_ERROR;
+		else if(write(file,node,sizeof(node_t)) != sizeof(node_t))
+			result = USER_WRITE_ERROR;
+		else {
+			result = USER_SUCCESS;
 			break;
+		}
 		FILE_RETRY_DELAY(attempts + 1);
 	}
-	unlock(file,(long)number*sizeof(node_t),sizeof(node_t));
+	unlock(file, nodedatoffset(number), sizeof(node_t));
 	if(closeit)
 		close(file);
 
-	if(wr!=sizeof(node_t))
-		return USER_WRITE_ERROR;
-	return USER_SUCCESS;
+	return result;
 }
 
 /****************************************************************************/
diff --git a/src/sbbs3/userdat.h b/src/sbbs3/userdat.h
index 0d60262daa..4f6077c05d 100644
--- a/src/sbbs3/userdat.h
+++ b/src/sbbs3/userdat.h
@@ -90,6 +90,8 @@ DLLEXPORT char*	username(scfg_t*, int usernumber, char * str);
 DLLEXPORT char* usermailaddr(scfg_t*, char* addr, const char* name);
 DLLEXPORT void	smtp_netmailaddr(scfg_t*, smbmsg_t*, char* name, size_t namelen, char* addr, size_t addrlen);
 DLLEXPORT int	opennodedat(scfg_t*);
+DLLEXPORT off_t nodedatoffset(unsigned node_number);
+DLLEXPORT bool	seeknodedat(int file, unsigned node_number);
 DLLEXPORT int	opennodeext(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);
-- 
GitLab