Blame SOURCES/0001-Fix-timestamp-handling-in-MDTM.patch

cb5a1f
From 6a4dc470e569df38b8a7ea09ee6aace3c73b7353 Mon Sep 17 00:00:00 2001
cb5a1f
From: =?UTF-8?q?Ond=C5=99ej=20Lyson=C4=9Bk?= <olysonek@redhat.com>
cb5a1f
Date: Wed, 28 Mar 2018 09:06:34 +0200
cb5a1f
Subject: [PATCH] Fix timestamp handling in MDTM
cb5a1f
cb5a1f
There were two problems with the timestamp handling with MDTM:
cb5a1f
cb5a1f
1. In vsf_sysutil_parse_time(), the `the_time.tm_isdst` attribute was
cb5a1f
   always set to 0, regardless of whether DST (daylight saving time)
cb5a1f
   is active on the given date or not.
cb5a1f
cb5a1f
   This made glibc shift the timestamp when DST was in fact active on
cb5a1f
   the given date, in an attempt to correct the discrepancy between
cb5a1f
   the given timestamp and the `tm_isdst` attribute. The shifting
cb5a1f
   produced incorrect results however.
cb5a1f
cb5a1f
   We fix this by setting `tm_isdst` to -1 to let glibc decide if DST
cb5a1f
   is active or not at the time of the timestamp. glibc won't touch
cb5a1f
   the timestamp then.
cb5a1f
cb5a1f
2. vsftpd used to record the offset from UTC of the current timezone
cb5a1f
   in the global variable `s_timezone`. This variable was then
cb5a1f
   subtracted from the variable `the_time` in vsf_sysutil_setmodtime()
cb5a1f
   when the config option use_localtime=NO was set. This was done to
cb5a1f
   compensate for the fact that mktime(), used in
cb5a1f
   vsf_sysutil_parse_time(), expects a timestamp expressed as local
cb5a1f
   time, whereas vsftpd is dealing with universal time.
cb5a1f
cb5a1f
   However, this did not work in the case when the offset stored in
cb5a1f
   `s_timezone` did not match the timezone of the timestamp given to
cb5a1f
   mktime() - this happens when DST is active at the current time, but
cb5a1f
   DST is not active at the time of the timestamp, or vice versa.
cb5a1f
cb5a1f
   We fix this by subtracting the real timezone offset directly in
cb5a1f
   vsf_sysutil_parse_time().
cb5a1f
cb5a1f
   Note that the `tm_gmtoff` attribute, used in this fix, is a
cb5a1f
   BSD/glic extension. However, using `tm_gmtoff` seems like the
cb5a1f
   simplest solution and we need to make this work only with glibc
cb5a1f
   anyway.
cb5a1f
cb5a1f
The fix was tested in the following way. We checked that the timestamp
cb5a1f
given to the MDTM command when setting modification time exactly
cb5a1f
matches the timestamp received as response from MDTM when reading back
cb5a1f
the modification time. Additionally, we checked that the modification
cb5a1f
time was set correctly on the given file on disk.
cb5a1f
cb5a1f
These two checks were performed under various conditions - all the
cb5a1f
combinations of DST/non-DST system time, DST/non-DST modification
cb5a1f
time, use_localtime=YES/NO.
cb5a1f
cb5a1f
Note that (I think) this will still not work if the rules for when DST
cb5a1f
is active change. For example, if DST is ever completely cancelled in
cb5a1f
the Europe/Prague timezone, and vsftpd is dealing with a timestamp
cb5a1f
from a time when DST was active, it will produce incorrect results. I
cb5a1f
think we would need the full zone file to fix this, but the zone file
cb5a1f
is hard to provide when we're chroot-ed.
cb5a1f
cb5a1f
Resolves: rhbz#1567855
cb5a1f
---
cb5a1f
 postlogin.c |  5 +++--
cb5a1f
 sysutil.c   | 17 ++++++++++-------
cb5a1f
 sysutil.h   |  4 ++--
cb5a1f
 3 files changed, 15 insertions(+), 11 deletions(-)
cb5a1f
cb5a1f
diff --git a/postlogin.c b/postlogin.c
cb5a1f
index 7c749ef..8a3d9d2 100644
cb5a1f
--- a/postlogin.c
cb5a1f
+++ b/postlogin.c
cb5a1f
@@ -1788,7 +1788,8 @@ handle_mdtm(struct vsf_session* p_sess)
cb5a1f
   if (do_write != 0)
cb5a1f
   {
cb5a1f
     str_split_char(&p_sess->ftp_arg_str, &s_filename_str, ' ');
cb5a1f
-    modtime = vsf_sysutil_parse_time(str_getbuf(&p_sess->ftp_arg_str));
cb5a1f
+    modtime = vsf_sysutil_parse_time(
cb5a1f
+      str_getbuf(&p_sess->ftp_arg_str), tunable_use_localtime);
cb5a1f
     str_copy(&p_sess->ftp_arg_str, &s_filename_str);
cb5a1f
   }
cb5a1f
   resolve_tilde(&p_sess->ftp_arg_str, p_sess);
cb5a1f
@@ -1809,7 +1810,7 @@ handle_mdtm(struct vsf_session* p_sess)
cb5a1f
     else
cb5a1f
     {
cb5a1f
       retval = vsf_sysutil_setmodtime(
cb5a1f
-        str_getbuf(&p_sess->ftp_arg_str), modtime, tunable_use_localtime);
cb5a1f
+        str_getbuf(&p_sess->ftp_arg_str), modtime);
cb5a1f
       if (retval != 0)
cb5a1f
       {
cb5a1f
         vsf_cmdio_write(p_sess, FTP_FILEFAIL,
cb5a1f
diff --git a/sysutil.c b/sysutil.c
cb5a1f
index e847650..66d4c5e 100644
cb5a1f
--- a/sysutil.c
cb5a1f
+++ b/sysutil.c
cb5a1f
@@ -2819,11 +2819,13 @@ vsf_sysutil_syslog(const char* p_text, int severe)
cb5a1f
 }
cb5a1f
 
cb5a1f
 long
cb5a1f
-vsf_sysutil_parse_time(const char* p_text)
cb5a1f
+vsf_sysutil_parse_time(const char* p_text, int is_localtime)
cb5a1f
 {
cb5a1f
+  long res;
cb5a1f
   struct tm the_time;
cb5a1f
   unsigned int len = vsf_sysutil_strlen(p_text);
cb5a1f
   vsf_sysutil_memclr(&the_time, sizeof(the_time));
cb5a1f
+  the_time.tm_isdst = -1;
cb5a1f
   if (len >= 8)
cb5a1f
   {
cb5a1f
     char yr[5];
cb5a1f
@@ -2848,17 +2850,18 @@ vsf_sysutil_parse_time(const char* p_text)
cb5a1f
     the_time.tm_min = vsf_sysutil_atoi(mins);
cb5a1f
     the_time.tm_sec = vsf_sysutil_atoi(sec);
cb5a1f
   }
cb5a1f
-  return mktime(&the_time);
cb5a1f
+  res = mktime(&the_time);
cb5a1f
+  if (!is_localtime)
cb5a1f
+  {
cb5a1f
+    res += the_time.tm_gmtoff;
cb5a1f
+  }
cb5a1f
+  return res;
cb5a1f
 }
cb5a1f
 
cb5a1f
 int
cb5a1f
-vsf_sysutil_setmodtime(const char* p_file, long the_time, int is_localtime)
cb5a1f
+vsf_sysutil_setmodtime(const char* p_file, long the_time)
cb5a1f
 {
cb5a1f
   struct utimbuf new_times;
cb5a1f
-  if (!is_localtime)
cb5a1f
-  {
cb5a1f
-    the_time -= s_timezone;
cb5a1f
-  }
cb5a1f
   vsf_sysutil_memclr(&new_times, sizeof(new_times));
cb5a1f
   new_times.actime = the_time;
cb5a1f
   new_times.modtime = the_time;
cb5a1f
diff --git a/sysutil.h b/sysutil.h
cb5a1f
index 7a59f13..b90f6ca 100644
cb5a1f
--- a/sysutil.h
cb5a1f
+++ b/sysutil.h
cb5a1f
@@ -349,9 +349,9 @@ void vsf_sysutil_chroot(const char* p_root_path);
cb5a1f
  */
cb5a1f
 long vsf_sysutil_get_time_sec(void);
cb5a1f
 long vsf_sysutil_get_time_usec(void);
cb5a1f
-long vsf_sysutil_parse_time(const char* p_text);
cb5a1f
+long vsf_sysutil_parse_time(const char* p_text, int is_localtime);
cb5a1f
 void vsf_sysutil_sleep(double seconds);
cb5a1f
-int vsf_sysutil_setmodtime(const char* p_file, long the_time, int is_localtime);
cb5a1f
+int vsf_sysutil_setmodtime(const char* p_file, long the_time);
cb5a1f
 
cb5a1f
 /* Limits */
cb5a1f
 void vsf_sysutil_set_address_space_limit(unsigned long bytes);
cb5a1f
-- 
cb5a1f
2.24.1
cb5a1f