Index | Thread | Search

From:
Stuart Henderson <stu@spacehopper.org>
Subject:
Re: ftp(1): parse fractional seconds in MDTM responses
To:
Benjamin Lee McQueen <mcq@disroot.org>
Cc:
obsd-tech <tech@openbsd.org>
Date:
Fri, 13 Feb 2026 09:48:34 +0000

Download raw body.

Thread
On 2026/02/12 17:30, Benjamin Lee McQueen wrote:
> sorry for the bad formatting and incompatible diff,
> i'll put the new one here
> 
> On 260212-123226, Stuart Henderson wrote:
> > also, ftp(1) changes (at least outside of !small) need testing on
> > ramdisks, see release(8) for info on building
> just to clarify, you want me to build and test the full installation
> media?

Somebody needs to, it can't be committed without checking that.
Generally it's easier if the person sending the diff does it because
they'll need that environment to test if it needs fixing..

> i don't know if i have the time currently to do this.
> can somebody else with a buildenv test this?
> 
> either way here's the diff:

diff was mangled by your MTA (which used "format=flowed"). I hand
applied, fixed some >80 column lines, and renamed result back to the
original rtime as it doesn't seem to need changing, I can try it in
'make release' next time I'm running one..

Index: cmds.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/cmds.c,v
diff -u -p -r1.85 cmds.c
--- cmds.c	8 Mar 2023 04:43:11 -0000	1.85
+++ cmds.c	13 Feb 2026 09:42:45 -0000
@@ -1594,7 +1594,7 @@ modtime(int argc, char *argv[])
 		code = -1;
 		return;
 	}
-	mtime = remotemodtime(argv[1], 1);
+	mtime = remotemodtime(argv[1], 1).tv_sec;
 	if (mtime != -1)
 		fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime)));
 	code = mtime;
Index: extern.h
===================================================================
RCS file: /cvs/src/usr.bin/ftp/extern.h,v
diff -u -p -r1.54 extern.h
--- extern.h	21 May 2024 05:00:48 -0000	1.54
+++ extern.h	13 Feb 2026 09:42:45 -0000
@@ -101,7 +101,7 @@ void	recvrequest(const char *, const cha
 	    const char *, int, int);
 char   *remglob(char **, int, char **);
 off_t	remotesize(const char *, int);
-time_t	remotemodtime(const char *, int);
+struct  timespec remotemodtime(const char *, int);
 void	reset(int, char **);
 void	rmthelp(int, char **);
 void	sethash(int, char **);
Index: ftp.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/ftp.c,v
diff -u -p -r1.109 ftp.c
--- ftp.c	8 Mar 2023 04:43:11 -0000	1.109
+++ ftp.c	13 Feb 2026 09:42:45 -0000
@@ -1204,7 +1204,7 @@ break2:
 		if (bytes > 0)
 			ptransfer(0);
 		if (preserve && (closefunc == fclose)) {
-			mtime = remotemodtime(remote, 0);
+			mtime = remotemodtime(remote, 0).tv_sec;
 			if (mtime != -1) {
 				struct timespec times[2];
 
Index: small.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/small.c,v
diff -u -p -r1.13 small.c
--- small.c	8 Mar 2023 04:43:11 -0000	1.13
+++ small.c	13 Feb 2026 09:42:45 -0000
@@ -269,7 +269,7 @@ usage:
 			if (ret == 0) {
 				time_t mtime;
 
-				mtime = remotemodtime(argv[1], 0);
+				mtime = remotemodtime(argv[1], 0).tv_sec;
 				if (mtime == -1)
 					goto freegetit;
 				if (stbuf.st_mtime >= mtime) {
Index: util.c
===================================================================
RCS file: /cvs/src/usr.bin/ftp/util.c,v
diff -u -p -r1.99 util.c
--- util.c	21 Dec 2025 07:29:03 -0000	1.99
+++ util.c	13 Feb 2026 09:42:45 -0000
@@ -602,16 +602,16 @@ remotesize(const char *file, int noisy)
 /*
  * determine last modification time (in GMT) of remote file
  */
-time_t
+struct timespec
 remotemodtime(const char *file, int noisy)
 {
-	int overbose;
-	time_t rtime;
-	int ocode;
+	struct timespec rtime;
+	int ocode, overbose;
 
 	overbose = verbose;
 	ocode = code;
-	rtime = -1;
+	rtime.tv_sec = -1;
+	rtime.tv_nsec = 0;
 #ifndef SMALL
 	if (!debug)
 #endif /* !SMALL */
@@ -619,12 +619,14 @@ remotemodtime(const char *file, int nois
 	if (command("MDTM %s", file) == COMPLETE) {
 		struct tm timebuf;
 		int yy, mo, day, hour, min, sec;
+		char *cp, *ep;
+		long frac, nsec;
+		int i, multiplier, num_digits;
 		/*
 		 * time-val = 14DIGIT [ "." 1*DIGIT ]
 		 *		YYYYMMDDHHMMSS[.sss]
 		 * mdtm-response = "213" SP time-val CRLF / error-response
 		 */
-		/* TODO: parse .sss as well, use timespecs. */
 		char *timestr = reply_string;
 
 		/* Repair `19%02d' bug on server side */
@@ -649,15 +651,40 @@ remotemodtime(const char *file, int nois
 		timebuf.tm_mon = mo - 1;
 		timebuf.tm_year = yy - 1900;
 		timebuf.tm_isdst = -1;
-		rtime = mktime(&timebuf);
-		if (rtime == -1 && (noisy
+
+		cp = strchr(reply_string, '.');
+		if (cp != NULL) {
+			cp++;
+			ep = cp;
+			while (isdigit((unsigned char)*ep))
+				ep++;
+			num_digits = ep - cp;
+			if (num_digits == 0 || num_digits > 9)
+				nsec = 0;
+			else {
+				frac = 0;
+				for (i = 0; i < num_digits; i++)
+					frac = frac * 10 + (cp[i] - '0');
+				multiplier = 1;
+				for (i = num_digits; i < 9; i++)
+					multiplier *= 10;
+				nsec = frac * multiplier;
+			}
+		} else
+			nsec = 0;
+
+		rtime.tv_sec = mktime(&timebuf);
+		rtime.tv_nsec = nsec;
+		if (rtime.tv_sec == -1 && (noisy
 #ifndef SMALL
 		    || debug
 #endif /* !SMALL */
 		    ))
-			fprintf(ttyout, "Can't convert %s to a time.\n", reply_string);
+			fprintf(ttyout, "Can't convert %s to a time.\n",
+			    reply_string);
 		else
-			rtime += timebuf.tm_gmtoff;	/* conv. local -> GMT */
+			/* conv. local -> GMT */
+			rtime.tv_sec += timebuf.tm_gmtoff;
 	} else if (noisy
 #ifndef SMALL
 	    && !debug
@@ -667,7 +694,7 @@ remotemodtime(const char *file, int nois
 		fputc('\n', ttyout);
 	}
 	verbose = overbose;
-	if (rtime == -1)
+	if (rtime.tv_sec == -1)
 		code = ocode;
 	return (rtime);
 }