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

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