Add st_birthtime and friends for accurate create times on systems that support it...
[samba.git] / source / lib / time.c
index 3abe233c4ffcdbac9648f4d43b4e0435635c7ec9..8ab001ec9220e0eacf4b0fe8c37ab6382643a85c 100644 (file)
@@ -4,10 +4,11 @@
 
    Copyright (C) Andrew Tridgell               1992-2004
    Copyright (C) Stefan (metze) Metzmacher     2002   
+   Copyright (C) Jeremy Allison                        2007
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
@@ -16,8 +17,7 @@
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 
 #define NTTIME_INFINITY (NTTIME)0x8000000000000000LL
 
-/**
+/***************************************************************************
  External access to time_t_min and time_t_max.
-**/
+****************************************************************************/
+
 time_t get_time_t_max(void)
 {
        return TIME_T_MAX;
 }
 
-/**
-a gettimeofday wrapper
-**/
+/***************************************************************************
+ A gettimeofday wrapper.
+****************************************************************************/
+
 void GetTimeOfDay(struct timeval *tval)
 {
 #ifdef HAVE_GETTIMEOFDAY_TZ
@@ -58,14 +60,6 @@ void GetTimeOfDay(struct timeval *tval)
 #endif
 }
 
-struct timespec convert_time_t_to_timespec(time_t t)
-{
-       struct timespec ts;
-       ts.tv_sec = t;
-       ts.tv_nsec = 0;
-       return ts;
-}
-
 #if (SIZEOF_LONG == 8)
 #define TIME_FIXUP_CONSTANT_INT 11644473600L
 #elif (SIZEOF_LONG_LONG == 8)
@@ -100,7 +94,13 @@ void unix_to_nt_time(NTTIME *nt, time_t t)
        if (t == (time_t)-1) {
                *nt = (NTTIME)-1LL;
                return;
-       }               
+       }       
+
+       if (t == TIME_T_MAX) {
+               *nt = 0x7fffffffffffffffLL;
+               return;
+       }
+
        if (t == 0) {
                *nt = 0;
                return;
@@ -113,26 +113,37 @@ void unix_to_nt_time(NTTIME *nt, time_t t)
        *nt = t2;
 }
 
+/****************************************************************************
+ Check if it's a null unix time.
+****************************************************************************/
 
-/**
-check if it's a null unix time
-**/
-BOOL null_time(time_t t)
+bool null_time(time_t t)
 {
        return t == 0 || 
                t == (time_t)0xFFFFFFFF || 
                t == (time_t)-1;
 }
 
+/****************************************************************************
+ Check if it's a null NTTIME.
+****************************************************************************/
 
-/**
-check if it's a null NTTIME
-**/
-BOOL null_nttime(NTTIME t)
+bool null_nttime(NTTIME t)
 {
        return t == 0 || t == (NTTIME)-1;
 }
 
+/****************************************************************************
+ Check if it's a null timespec.
+****************************************************************************/
+
+bool null_timespec(struct timespec ts)
+{
+       return ts.tv_sec == 0 || 
+               ts.tv_sec == (time_t)0xFFFFFFFF || 
+               ts.tv_sec == (time_t)-1;
+}
+
 /*******************************************************************
   create a 16 bit dos packed date
 ********************************************************************/
@@ -292,11 +303,13 @@ time_t pull_dos_date3(const uint8_t *date_ptr, int zone_offset)
 
 char *http_timestring(time_t t)
 {
-       static fstring buf;
+       fstring buf;
        struct tm *tm = localtime(&t);
 
-       if (!tm) {
-               slprintf(buf,sizeof(buf)-1,"%ld seconds since the Epoch",(long)t);
+       if (t == TIME_T_MAX) {
+               fstrcpy(buf, "never");
+       } else if (!tm) {
+               fstr_sprintf(buf, "%ld seconds since the Epoch", (long)t);
        } else {
 #ifndef HAVE_STRFTIME
                const char *asct = asctime(tm);
@@ -308,7 +321,7 @@ char *http_timestring(time_t t)
                strftime(buf, sizeof(buf)-1, "%a, %d %b %Y %H:%M:%S %Z", tm);
 #endif /* !HAVE_STRFTIME */
        }
-       return buf;
+       return talloc_strdup(talloc_tos(), buf);
 }
 
 
@@ -388,7 +401,7 @@ struct timeval timeval_zero(void)
 /**
   return True if a timeval is zero
 */
-BOOL timeval_is_zero(const struct timeval *tv)
+bool timeval_is_zero(const struct timeval *tv)
 {
        return tv->tv_sec == 0 && tv->tv_usec == 0;
 }
@@ -466,7 +479,7 @@ int timeval_compare(const struct timeval *tv1, const struct timeval *tv2)
 /**
   return True if a timer is in the past
 */
-BOOL timeval_expired(const struct timeval *tv)
+bool timeval_expired(const struct timeval *tv)
 {
        struct timeval tv2 = timeval_current();
        if (tv2.tv_sec > tv->tv_sec) return True;
@@ -548,9 +561,41 @@ NTTIME timeval_to_nttime(const struct timeval *tv)
                  ((TIME_FIXUP_CONSTANT_INT + (uint64_t)tv->tv_sec) * 1000000));
 }
 
+/**************************************************************
+ Handle conversions between time_t and uint32, taking care to
+ preserve the "special" values.
+**************************************************************/
+
+uint32 convert_time_t_to_uint32(time_t t)
+{
+#if (defined(SIZEOF_TIME_T) && (SIZEOF_TIME_T == 8))
+       /* time_t is 64-bit. */
+       if (t == 0x8000000000000000LL) {
+               return 0x80000000;
+       } else if (t == 0x7FFFFFFFFFFFFFFFLL) {
+               return 0x7FFFFFFF;
+       }
+#endif
+       return (uint32)t;
+}
+
+time_t convert_uint32_to_time_t(uint32 u)
+{
+#if (defined(SIZEOF_TIME_T) && (SIZEOF_TIME_T == 8))
+       /* time_t is 64-bit. */
+       if (u == 0x80000000) {
+               return (time_t)0x8000000000000000LL;
+       } else if (u == 0x7FFFFFFF) {
+               return (time_t)0x7FFFFFFFFFFFFFFFLL;
+       }
+#endif
+       return (time_t)u;
+}
+
 /*******************************************************************
-yield the difference between *A and *B, in seconds, ignoring leap seconds
+ Yield the difference between *A and *B, in seconds, ignoring leap seconds.
 ********************************************************************/
+
 static int tm_diff(struct tm *a, struct tm *b)
 {
        int ay = a->tm_year + (1900 - 1);
@@ -568,9 +613,10 @@ static int tm_diff(struct tm *a, struct tm *b)
 
 int extra_time_offset=0;
 
-/**
-  return the UTC offset in seconds west of UTC, or 0 if it cannot be determined
- */
+/*******************************************************************
+ Return the UTC offset in seconds west of UTC, or 0 if it cannot be determined.
+********************************************************************/
+
 int get_time_zone(time_t t)
 {
        struct tm *tm = gmtime(&t);
@@ -588,7 +634,7 @@ int get_time_zone(time_t t)
  Check if NTTIME is 0.
 ****************************************************************************/
 
-BOOL nt_time_is_zero(const NTTIME *nt)
+bool nt_time_is_zero(const NTTIME *nt)
 {
        return (*nt == 0);
 }
@@ -641,9 +687,9 @@ int set_server_zone_offset(time_t t)
  Return the date and time as a string
 ****************************************************************************/
 
-char *current_timestring(BOOL hires)
+char *current_timestring(TALLOC_CTX *ctx, bool hires)
 {
-       static fstring TimeBuf;
+       fstring TimeBuf;
        struct timeval tp;
        time_t t;
        struct tm *tm;
@@ -693,7 +739,7 @@ char *current_timestring(BOOL hires)
                }
 #endif
        }
-       return(TimeBuf);
+       return talloc_strdup(ctx, TimeBuf);
 }
 
 
@@ -780,14 +826,10 @@ void put_long_date(char *p, time_t t)
  structure.
 ****************************************************************************/
 
-time_t get_create_time(SMB_STRUCT_STAT *st,BOOL fake_dirs)
+static time_t calc_create_time(const SMB_STRUCT_STAT *st)
 {
        time_t ret, ret1;
 
-       if(S_ISDIR(st->st_mode) && fake_dirs) {
-               return (time_t)315493200L;          /* 1/1/1980 */
-       }
-    
        ret = MIN(st->st_ctime, st->st_mtime);
        ret1 = MIN(ret, st->st_atime);
 
@@ -802,19 +844,43 @@ time_t get_create_time(SMB_STRUCT_STAT *st,BOOL fake_dirs)
        return ret;
 }
 
-struct timespec get_create_timespec(SMB_STRUCT_STAT *st,BOOL fake_dirs)
+/****************************************************************************
+ Return the 'create time' from a stat struct if it exists (birthtime) or else
+ use the best approximation.
+****************************************************************************/
+
+struct timespec get_create_timespec(const SMB_STRUCT_STAT *pst,bool fake_dirs)
 {
-       struct timespec ts;
-       ts.tv_sec = get_create_time(st, fake_dirs);
-       ts.tv_nsec = 0;
-       return ts;
+       struct timespec ret;
+
+       if(S_ISDIR(pst->st_mode) && fake_dirs) {
+               ret.tv_sec = 315493200L;          /* 1/1/1980 */
+               ret.tv_nsec = 0;
+               return ret;
+       }
+
+#if defined(HAVE_STAT_ST_BIRTHTIMESPEC)
+       return pst->st_birthtimespec;
+#elif defined(HAVE_STAT_ST_BIRTHTIMENSEC)
+       ret.tv_sec = pst->st_birthtime;
+       ret.tv_nsec = pst->st_birthtimenspec;
+       return ret;
+#elif defined(HAVE_STAT_ST_BIRTHTIME)
+       ret.tv_sec = pst->st_birthtime;
+       ret.tv_nsec = 0;
+       return ret;
+#else
+       ret.tv_sec = calc_create_time(pst);
+       ret.tv_nsec = 0;
+       return ret;
+#endif
 }
 
 /****************************************************************************
  Get/Set all the possible time fields from a stat struct as a timespec.
 ****************************************************************************/
 
-struct timespec get_atimespec(SMB_STRUCT_STAT *pst)
+struct timespec get_atimespec(const SMB_STRUCT_STAT *pst)
 {
 #if !defined(HAVE_STAT_HIRES_TIMESTAMPS)
        struct timespec ret;
@@ -854,7 +920,7 @@ void set_atimespec(SMB_STRUCT_STAT *pst, struct timespec ts)
 #endif
 }
 
-struct timespec get_mtimespec(SMB_STRUCT_STAT *pst)
+struct timespec get_mtimespec(const SMB_STRUCT_STAT *pst)
 {
 #if !defined(HAVE_STAT_HIRES_TIMESTAMPS)
        struct timespec ret;
@@ -894,7 +960,7 @@ void set_mtimespec(SMB_STRUCT_STAT *pst, struct timespec ts)
 #endif
 }
 
-struct timespec get_ctimespec(SMB_STRUCT_STAT *pst)
+struct timespec get_ctimespec(const SMB_STRUCT_STAT *pst)
 {
 #if !defined(HAVE_STAT_HIRES_TIMESTAMPS)
        struct timespec ret;
@@ -1022,6 +1088,81 @@ time_t convert_timespec_to_time_t(struct timespec ts)
        return ts.tv_sec;
 }
 
+struct timespec convert_time_t_to_timespec(time_t t)
+{
+       struct timespec ts;
+       ts.tv_sec = t;
+       ts.tv_nsec = 0;
+       return ts;
+}
+
+/****************************************************************************
+ Convert a normalized timeval to a timespec.
+****************************************************************************/
+
+struct timespec convert_timeval_to_timespec(const struct timeval tv)
+{
+       struct timespec ts;
+       ts.tv_sec = tv.tv_sec;
+       ts.tv_nsec = tv.tv_usec * 1000;
+       return ts;
+}
+
+/****************************************************************************
+ Convert a normalized timespec to a timeval.
+****************************************************************************/
+
+struct timeval convert_timespec_to_timeval(const struct timespec ts)
+{
+       struct timeval tv;
+       tv.tv_sec = ts.tv_sec;
+       tv.tv_usec = ts.tv_nsec / 1000;
+       return tv;
+}
+
+/****************************************************************************
+ Return a timespec for the current time
+****************************************************************************/
+
+struct timespec timespec_current(void)
+{
+       struct timeval tv;
+       struct timespec ts;
+       GetTimeOfDay(&tv);
+       ts.tv_sec = tv.tv_sec;
+       ts.tv_nsec = tv.tv_usec * 1000;
+       return ts;
+}
+
+/****************************************************************************
+ Return the lesser of two timespecs.
+****************************************************************************/
+
+struct timespec timespec_min(const struct timespec *ts1,
+                          const struct timespec *ts2)
+{
+       if (ts1->tv_sec < ts2->tv_sec) return *ts1;
+       if (ts1->tv_sec > ts2->tv_sec) return *ts2;
+       if (ts1->tv_nsec < ts2->tv_nsec) return *ts1;
+       return *ts2;
+}
+
+/****************************************************************************
+  compare two timespec structures. 
+  Return -1 if ts1 < ts2
+  Return 0 if ts1 == ts2
+  Return 1 if ts1 > ts2
+****************************************************************************/
+
+int timespec_compare(const struct timespec *ts1, const struct timespec *ts2)
+{
+       if (ts1->tv_sec  > ts2->tv_sec)  return 1;
+       if (ts1->tv_sec  < ts2->tv_sec)  return -1;
+       if (ts1->tv_nsec > ts2->tv_nsec) return 1;
+       if (ts1->tv_nsec < ts2->tv_nsec) return -1;
+       return 0;
+}
+
 /****************************************************************************
  Interprets an nt time into a unix struct timespec.
  Differs from nt_time_to_unix in that an 8 byte value of 0xffffffffffffffff
@@ -1060,17 +1201,17 @@ void cli_put_dos_date3(struct cli_state *cli, char *buf, int offset, time_t unix
        put_dos_date3(buf, offset, unixdate, cli->serverzone);
 }
 
-time_t cli_make_unix_date(struct cli_state *cli, void *date_ptr)
+time_t cli_make_unix_date(struct cli_state *cli, const void *date_ptr)
 {
        return make_unix_date(date_ptr, cli->serverzone);
 }
 
-time_t cli_make_unix_date2(struct cli_state *cli, void *date_ptr)
+time_t cli_make_unix_date2(struct cli_state *cli, const void *date_ptr)
 {
        return make_unix_date2(date_ptr, cli->serverzone);
 }
 
-time_t cli_make_unix_date3(struct cli_state *cli, void *date_ptr)
+time_t cli_make_unix_date3(struct cli_state *cli, const void *date_ptr)
 {
        return make_unix_date3(date_ptr, cli->serverzone);
 }
@@ -1090,8 +1231,12 @@ struct timespec nt_time_to_unix_timespec(NTTIME *nt)
        d = (int64)*nt;
        /* d is now in 100ns units, since jan 1st 1601".
           Save off the ns fraction. */
-       
-       ret.tv_nsec = (long) ((d % 100) * 100);
+
+       /*
+        * Take the last seven decimal digits and multiply by 100.
+        * to convert from 100ns units to 1ns units.
+        */
+        ret.tv_nsec = (long) ((d % (1000 * 1000 * 10)) * 100);
 
        /* Convert to seconds */
        d /= 1000*1000*10;
@@ -1118,7 +1263,7 @@ struct timespec nt_time_to_unix_timespec(NTTIME *nt)
  Check if two NTTIMEs are the same.
 ****************************************************************************/
 
-BOOL nt_time_equals(const NTTIME *nt1, const NTTIME *nt2)
+bool nt_time_equals(const NTTIME *nt1, const NTTIME *nt2)
 {
        return (*nt1 == *nt2);
 }
@@ -1261,7 +1406,7 @@ void unix_to_nt_time_abs(NTTIME *nt, time_t t)
        d = (double)(t);
        d *= 1.0e7;
 
-       *nt = d;
+       *nt = (NTTIME)d;
 
        /* convert to a negative value */
        *nt=~*nt;
@@ -1272,7 +1417,7 @@ void unix_to_nt_time_abs(NTTIME *nt, time_t t)
  Check if it's a null mtime.
 ****************************************************************************/
 
-BOOL null_mtime(time_t mtime)
+bool null_mtime(time_t mtime)
 {
        if (mtime == 0 || mtime == (time_t)0xFFFFFFFF || mtime == (time_t)-1)
                return(True);
@@ -1284,10 +1429,10 @@ BOOL null_mtime(time_t mtime)
  and asctime fail.
 ****************************************************************************/
 
-const char *time_to_asc(const time_t *t)
+const char *time_to_asc(const time_t t)
 {
        const char *asct;
-       struct tm *lt = localtime(t);
+       struct tm *lt = localtime(&t);
 
        if (!lt) {
                return "unknown time";
@@ -1302,8 +1447,6 @@ const char *time_to_asc(const time_t *t)
 
 const char *display_time(NTTIME nttime)
 {
-       static fstring string;
-
        float high;
        float low;
        int sec;
@@ -1324,18 +1467,18 @@ const char *display_time(NTTIME nttime)
        low = ~(nttime & 0xFFFFFFFF);
        low = low/(1000*1000*10);
 
-       sec=high+low;
+       sec=(int)(high+low);
 
        days=sec/(60*60*24);
        hours=(sec - (days*60*60*24)) / (60*60);
        mins=(sec - (days*60*60*24) - (hours*60*60) ) / 60;
        secs=sec - (days*60*60*24) - (hours*60*60) - (mins*60);
 
-       fstr_sprintf(string, "%u days, %u hours, %u minutes, %u seconds", days, hours, mins, secs);
-       return (string);
+       return talloc_asprintf(talloc_tos(), "%u days, %u hours, %u minutes, "
+                              "%u seconds", days, hours, mins, secs);
 }
 
-BOOL nt_time_is_set(const NTTIME *nt)
+bool nt_time_is_set(const NTTIME *nt)
 {
        if (*nt == 0x7FFFFFFFFFFFFFFFLL) {
                return False;
@@ -1347,4 +1490,3 @@ BOOL nt_time_is_set(const NTTIME *nt)
 
        return True;
 }
-