diff options
Diffstat (limited to 'source3/lib/time.c')
-rw-r--r-- | source3/lib/time.c | 1505 |
1 files changed, 1505 insertions, 0 deletions
diff --git a/source3/lib/time.c b/source3/lib/time.c new file mode 100644 index 0000000000..8cefef6e23 --- /dev/null +++ b/source3/lib/time.c @@ -0,0 +1,1505 @@ +/* + Unix SMB/CIFS implementation. + time handling functions + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +/** + * @file + * @brief time handling functions + */ + + +#ifndef TIME_T_MIN +#define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \ + : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1)) +#endif +#ifndef TIME_T_MAX +#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN) +#endif + +#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. +****************************************************************************/ + +void GetTimeOfDay(struct timeval *tval) +{ +#ifdef HAVE_GETTIMEOFDAY_TZ + gettimeofday(tval,NULL); +#else + gettimeofday(tval); +#endif +} + +#if (SIZEOF_LONG == 8) +#define TIME_FIXUP_CONSTANT_INT 11644473600L +#elif (SIZEOF_LONG_LONG == 8) +#define TIME_FIXUP_CONSTANT_INT 11644473600LL +#endif + +/**************************************************************************** + Interpret an 8 byte "filetime" structure to a time_t + It's originally in "100ns units since jan 1st 1601" + + An 8 byte value of 0xffffffffffffffff will be returned as a timespec of + + tv_sec = 0 + tv_nsec = 0; + + Returns GMT. +****************************************************************************/ + +time_t nt_time_to_unix(NTTIME nt) +{ + return convert_timespec_to_time_t(nt_time_to_unix_timespec(&nt)); +} + +/**************************************************************************** + Put a 8 byte filetime from a time_t. Uses GMT. +****************************************************************************/ + +void unix_to_nt_time(NTTIME *nt, time_t t) +{ + uint64_t t2; + + if (t == (time_t)-1) { + *nt = (NTTIME)-1LL; + return; + } + + if (t == TIME_T_MAX) { + *nt = 0x7fffffffffffffffLL; + return; + } + + if (t == 0) { + *nt = 0; + return; + } + + t2 = t; + t2 += TIME_FIXUP_CONSTANT_INT; + t2 *= 1000*1000*10; + + *nt = t2; +} + +/**************************************************************************** + Check if it's a null unix time. +****************************************************************************/ + +bool null_time(time_t t) +{ + return t == 0 || + t == (time_t)0xFFFFFFFF || + t == (time_t)-1; +} + +/**************************************************************************** + Check if it's a null NTTIME. +****************************************************************************/ + +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 +********************************************************************/ +static uint16_t make_dos_date1(struct tm *t) +{ + uint16_t ret=0; + ret = (((unsigned int)(t->tm_mon+1)) >> 3) | ((t->tm_year-80) << 1); + ret = ((ret&0xFF)<<8) | (t->tm_mday | (((t->tm_mon+1) & 0x7) << 5)); + return ret; +} + +/******************************************************************* + create a 16 bit dos packed time +********************************************************************/ +static uint16_t make_dos_time1(struct tm *t) +{ + uint16_t ret=0; + ret = ((((unsigned int)t->tm_min >> 3)&0x7) | (((unsigned int)t->tm_hour) << 3)); + ret = ((ret&0xFF)<<8) | ((t->tm_sec/2) | ((t->tm_min & 0x7) << 5)); + return ret; +} + +/******************************************************************* + create a 32 bit dos packed date/time from some parameters + This takes a GMT time and returns a packed localtime structure +********************************************************************/ +static uint32_t make_dos_date(time_t unixdate, int zone_offset) +{ + struct tm *t; + uint32_t ret=0; + + if (unixdate == 0) { + return 0; + } + + unixdate -= zone_offset; + + t = gmtime(&unixdate); + if (!t) { + return 0xFFFFFFFF; + } + + ret = make_dos_date1(t); + ret = ((ret&0xFFFF)<<16) | make_dos_time1(t); + + return ret; +} + +/** +put a dos date into a buffer (time/date format) +This takes GMT time and puts local time in the buffer +**/ +void push_dos_date(uint8_t *buf, int offset, time_t unixdate, int zone_offset) +{ + uint32_t x = make_dos_date(unixdate, zone_offset); + SIVAL(buf,offset,x); +} + +/** +put a dos date into a buffer (date/time format) +This takes GMT time and puts local time in the buffer +**/ +void push_dos_date2(uint8_t *buf,int offset,time_t unixdate, int zone_offset) +{ + uint32_t x; + x = make_dos_date(unixdate, zone_offset); + x = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16); + SIVAL(buf,offset,x); +} + +/** +put a dos 32 bit "unix like" date into a buffer. This routine takes +GMT and converts it to LOCAL time before putting it (most SMBs assume +localtime for this sort of date) +**/ +void push_dos_date3(uint8_t *buf,int offset,time_t unixdate, int zone_offset) +{ + if (!null_time(unixdate)) { + unixdate -= zone_offset; + } + SIVAL(buf,offset,unixdate); +} + +/******************************************************************* + interpret a 32 bit dos packed date/time to some parameters +********************************************************************/ +static void interpret_dos_date(uint32_t date,int *year,int *month,int *day,int *hour,int *minute,int *second) +{ + uint32_t p0,p1,p2,p3; + + p0=date&0xFF; p1=((date&0xFF00)>>8)&0xFF; + p2=((date&0xFF0000)>>16)&0xFF; p3=((date&0xFF000000)>>24)&0xFF; + + *second = 2*(p0 & 0x1F); + *minute = ((p0>>5)&0xFF) + ((p1&0x7)<<3); + *hour = (p1>>3)&0xFF; + *day = (p2&0x1F); + *month = ((p2>>5)&0xFF) + ((p3&0x1)<<3) - 1; + *year = ((p3>>1)&0xFF) + 80; +} + +/** + create a unix date (int GMT) from a dos date (which is actually in + localtime) +**/ +time_t pull_dos_date(const uint8_t *date_ptr, int zone_offset) +{ + uint32_t dos_date=0; + struct tm t; + time_t ret; + + dos_date = IVAL(date_ptr,0); + + if (dos_date == 0) return (time_t)0; + + interpret_dos_date(dos_date,&t.tm_year,&t.tm_mon, + &t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec); + t.tm_isdst = -1; + + ret = timegm(&t); + + ret += zone_offset; + + return ret; +} + +/** +like make_unix_date() but the words are reversed +**/ +time_t pull_dos_date2(const uint8_t *date_ptr, int zone_offset) +{ + uint32_t x,x2; + + x = IVAL(date_ptr,0); + x2 = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16); + SIVAL(&x,0,x2); + + return pull_dos_date((const uint8_t *)&x, zone_offset); +} + +/** + create a unix GMT date from a dos date in 32 bit "unix like" format + these generally arrive as localtimes, with corresponding DST +**/ +time_t pull_dos_date3(const uint8_t *date_ptr, int zone_offset) +{ + time_t t = (time_t)IVAL(date_ptr,0); + if (!null_time(t)) { + t += zone_offset; + } + return t; +} + +/*************************************************************************** + Return a HTTP/1.0 time string. +***************************************************************************/ + +char *http_timestring(time_t t) +{ + fstring buf; + struct tm *tm = localtime(&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); + fstrcpy(buf, asct ? asct : "unknown"); + } + if(buf[strlen(buf)-1] == '\n') { + buf[strlen(buf)-1] = 0; +#else /* !HAVE_STRFTIME */ + strftime(buf, sizeof(buf)-1, "%a, %d %b %Y %H:%M:%S %Z", tm); +#endif /* !HAVE_STRFTIME */ + } + return talloc_strdup(talloc_tos(), buf); +} + + +/** + Return the date and time as a string +**/ +char *timestring(TALLOC_CTX *mem_ctx, time_t t) +{ + char *TimeBuf; + char tempTime[80]; + struct tm *tm; + + tm = localtime(&t); + if (!tm) { + return talloc_asprintf(mem_ctx, + "%ld seconds since the Epoch", + (long)t); + } + +#ifdef HAVE_STRFTIME + /* some versions of gcc complain about using %c. This is a bug + in the gcc warning, not a bug in this code. See a recent + strftime() manual page for details. + */ + strftime(tempTime,sizeof(tempTime)-1,"%c %Z",tm); + TimeBuf = talloc_strdup(mem_ctx, tempTime); +#else + TimeBuf = talloc_strdup(mem_ctx, asctime(tm)); +#endif + + return TimeBuf; +} + +/** + return a talloced string representing a NTTIME for human consumption +*/ +const char *nt_time_string(TALLOC_CTX *mem_ctx, NTTIME nt) +{ + time_t t; + if (nt == 0) { + return "NTTIME(0)"; + } + t = nt_time_to_unix(nt); + return timestring(mem_ctx, t); +} + + +/** + parse a nttime as a large integer in a string and return a NTTIME +*/ +NTTIME nttime_from_string(const char *s) +{ + return strtoull(s, NULL, 0); +} + +/** + return (tv1 - tv2) in microseconds +*/ +int64_t usec_time_diff(struct timeval *tv1, struct timeval *tv2) +{ + int64_t sec_diff = tv1->tv_sec - tv2->tv_sec; + return (sec_diff * 1000000) + (int64_t)(tv1->tv_usec - tv2->tv_usec); +} + + +/** + return a zero timeval +*/ +struct timeval timeval_zero(void) +{ + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + return tv; +} + +/** + return True if a timeval is zero +*/ +bool timeval_is_zero(const struct timeval *tv) +{ + return tv->tv_sec == 0 && tv->tv_usec == 0; +} + +/** + return a timeval for the current time +*/ +struct timeval timeval_current(void) +{ + struct timeval tv; + GetTimeOfDay(&tv); + return tv; +} + +/** + return a timeval struct with the given elements +*/ +struct timeval timeval_set(uint32_t secs, uint32_t usecs) +{ + struct timeval tv; + tv.tv_sec = secs; + tv.tv_usec = usecs; + return tv; +} + + +/** + return a timeval ofs microseconds after tv +*/ +struct timeval timeval_add(const struct timeval *tv, + uint32_t secs, uint32_t usecs) +{ + struct timeval tv2 = *tv; + const unsigned int million = 1000000; + tv2.tv_sec += secs; + tv2.tv_usec += usecs; + tv2.tv_sec += tv2.tv_usec / million; + tv2.tv_usec = tv2.tv_usec % million; + return tv2; +} + +/** + return the sum of two timeval structures +*/ +struct timeval timeval_sum(const struct timeval *tv1, + const struct timeval *tv2) +{ + return timeval_add(tv1, tv2->tv_sec, tv2->tv_usec); +} + +/** + return a timeval secs/usecs into the future +*/ +struct timeval timeval_current_ofs(uint32_t secs, uint32_t usecs) +{ + struct timeval tv = timeval_current(); + return timeval_add(&tv, secs, usecs); +} + +/** + compare two timeval structures. + Return -1 if tv1 < tv2 + Return 0 if tv1 == tv2 + Return 1 if tv1 > tv2 +*/ +int timeval_compare(const struct timeval *tv1, const struct timeval *tv2) +{ + if (tv1->tv_sec > tv2->tv_sec) return 1; + if (tv1->tv_sec < tv2->tv_sec) return -1; + if (tv1->tv_usec > tv2->tv_usec) return 1; + if (tv1->tv_usec < tv2->tv_usec) return -1; + return 0; +} + +/** + return True if a timer is in the past +*/ +bool timeval_expired(const struct timeval *tv) +{ + struct timeval tv2 = timeval_current(); + if (tv2.tv_sec > tv->tv_sec) return True; + if (tv2.tv_sec < tv->tv_sec) return False; + return (tv2.tv_usec >= tv->tv_usec); +} + +/** + return the number of seconds elapsed between two times +*/ +double timeval_elapsed2(const struct timeval *tv1, const struct timeval *tv2) +{ + return (tv2->tv_sec - tv1->tv_sec) + + (tv2->tv_usec - tv1->tv_usec)*1.0e-6; +} + +/** + return the number of seconds elapsed since a given time +*/ +double timeval_elapsed(const struct timeval *tv) +{ + struct timeval tv2 = timeval_current(); + return timeval_elapsed2(tv, &tv2); +} + +/** + return the lesser of two timevals +*/ +struct timeval timeval_min(const struct timeval *tv1, + const struct timeval *tv2) +{ + if (tv1->tv_sec < tv2->tv_sec) return *tv1; + if (tv1->tv_sec > tv2->tv_sec) return *tv2; + if (tv1->tv_usec < tv2->tv_usec) return *tv1; + return *tv2; +} + +/** + return the greater of two timevals +*/ +struct timeval timeval_max(const struct timeval *tv1, + const struct timeval *tv2) +{ + if (tv1->tv_sec > tv2->tv_sec) return *tv1; + if (tv1->tv_sec < tv2->tv_sec) return *tv2; + if (tv1->tv_usec > tv2->tv_usec) return *tv1; + return *tv2; +} + +/** + return the difference between two timevals as a timeval + if tv1 comes after tv2, then return a zero timeval + (this is *tv2 - *tv1) +*/ +struct timeval timeval_until(const struct timeval *tv1, + const struct timeval *tv2) +{ + struct timeval t; + if (timeval_compare(tv1, tv2) >= 0) { + return timeval_zero(); + } + t.tv_sec = tv2->tv_sec - tv1->tv_sec; + if (tv1->tv_usec > tv2->tv_usec) { + t.tv_sec--; + t.tv_usec = 1000000 - (tv1->tv_usec - tv2->tv_usec); + } else { + t.tv_usec = tv2->tv_usec - tv1->tv_usec; + } + return t; +} + + +/** + convert a timeval to a NTTIME +*/ +NTTIME timeval_to_nttime(const struct timeval *tv) +{ + return 10*(tv->tv_usec + + ((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. +********************************************************************/ + +static int tm_diff(struct tm *a, struct tm *b) +{ + int ay = a->tm_year + (1900 - 1); + int by = b->tm_year + (1900 - 1); + int intervening_leap_days = + (ay/4 - by/4) - (ay/100 - by/100) + (ay/400 - by/400); + int years = ay - by; + int days = 365*years + intervening_leap_days + (a->tm_yday - b->tm_yday); + int hours = 24*days + (a->tm_hour - b->tm_hour); + int minutes = 60*hours + (a->tm_min - b->tm_min); + int seconds = 60*minutes + (a->tm_sec - b->tm_sec); + + return seconds; +} + +int extra_time_offset=0; + +/******************************************************************* + 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); + struct tm tm_utc; + if (!tm) + return 0; + tm_utc = *tm; + tm = localtime(&t); + if (!tm) + return 0; + return tm_diff(&tm_utc,tm)+60*extra_time_offset; +} + +/**************************************************************************** + Check if NTTIME is 0. +****************************************************************************/ + +bool nt_time_is_zero(const NTTIME *nt) +{ + return (*nt == 0); +} + +/**************************************************************************** + Convert ASN.1 GeneralizedTime string to unix-time. + Returns 0 on failure; Currently ignores timezone. +****************************************************************************/ + +time_t generalized_to_unix_time(const char *str) +{ + struct tm tm; + + ZERO_STRUCT(tm); + + if (sscanf(str, "%4d%2d%2d%2d%2d%2d", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { + return 0; + } + tm.tm_year -= 1900; + tm.tm_mon -= 1; + + return timegm(&tm); +} + +/******************************************************************* + Accessor function for the server time zone offset. + set_server_zone_offset() must have been called first. +******************************************************************/ + +static int server_zone_offset; + +int get_server_zone_offset(void) +{ + return server_zone_offset; +} + +/******************************************************************* + Initialize the server time zone offset. Called when a client connects. +******************************************************************/ + +int set_server_zone_offset(time_t t) +{ + server_zone_offset = get_time_zone(t); + return server_zone_offset; +} + +/**************************************************************************** + Return the date and time as a string +****************************************************************************/ + +char *current_timestring(TALLOC_CTX *ctx, bool hires) +{ + fstring TimeBuf; + struct timeval tp; + time_t t; + struct tm *tm; + + if (hires) { + GetTimeOfDay(&tp); + t = (time_t)tp.tv_sec; + } else { + t = time(NULL); + } + tm = localtime(&t); + if (!tm) { + if (hires) { + slprintf(TimeBuf, + sizeof(TimeBuf)-1, + "%ld.%06ld seconds since the Epoch", + (long)tp.tv_sec, + (long)tp.tv_usec); + } else { + slprintf(TimeBuf, + sizeof(TimeBuf)-1, + "%ld seconds since the Epoch", + (long)t); + } + } else { +#ifdef HAVE_STRFTIME + if (hires) { + strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %H:%M:%S",tm); + slprintf(TimeBuf+strlen(TimeBuf), + sizeof(TimeBuf)-1 - strlen(TimeBuf), + ".%06ld", + (long)tp.tv_usec); + } else { + strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %H:%M:%S",tm); + } +#else + if (hires) { + const char *asct = asctime(tm); + slprintf(TimeBuf, + sizeof(TimeBuf)-1, + "%s.%06ld", + asct ? asct : "unknown", + (long)tp.tv_usec); + } else { + const char *asct = asctime(tm); + fstrcpy(TimeBuf, asct ? asct : "unknown"); + } +#endif + } + return talloc_strdup(ctx, TimeBuf); +} + + +/******************************************************************* + Put a dos date into a buffer (time/date format). + This takes GMT time and puts local time in the buffer. +********************************************************************/ + +static void put_dos_date(char *buf,int offset,time_t unixdate, int zone_offset) +{ + uint32 x = make_dos_date(unixdate, zone_offset); + SIVAL(buf,offset,x); +} + +/******************************************************************* + Put a dos date into a buffer (date/time format). + This takes GMT time and puts local time in the buffer. +********************************************************************/ + +static void put_dos_date2(char *buf,int offset,time_t unixdate, int zone_offset) +{ + uint32 x = make_dos_date(unixdate, zone_offset); + x = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16); + SIVAL(buf,offset,x); +} + +/******************************************************************* + Put a dos 32 bit "unix like" date into a buffer. This routine takes + GMT and converts it to LOCAL time before putting it (most SMBs assume + localtime for this sort of date) +********************************************************************/ + +static void put_dos_date3(char *buf,int offset,time_t unixdate, int zone_offset) +{ + if (!null_mtime(unixdate)) { + unixdate -= zone_offset; + } + SIVAL(buf,offset,unixdate); +} + + +/*************************************************************************** + Server versions of the above functions. +***************************************************************************/ + +void srv_put_dos_date(char *buf,int offset,time_t unixdate) +{ + put_dos_date(buf, offset, unixdate, server_zone_offset); +} + +void srv_put_dos_date2(char *buf,int offset, time_t unixdate) +{ + put_dos_date2(buf, offset, unixdate, server_zone_offset); +} + +void srv_put_dos_date3(char *buf,int offset,time_t unixdate) +{ + put_dos_date3(buf, offset, unixdate, server_zone_offset); +} + +/**************************************************************************** + Take a Unix time and convert to an NTTIME structure and place in buffer + pointed to by p. +****************************************************************************/ + +void put_long_date_timespec(char *p, struct timespec ts) +{ + NTTIME nt; + unix_timespec_to_nt_time(&nt, ts); + SIVAL(p, 0, nt & 0xFFFFFFFF); + SIVAL(p, 4, nt >> 32); +} + +void put_long_date(char *p, time_t t) +{ + struct timespec ts; + ts.tv_sec = t; + ts.tv_nsec = 0; + put_long_date_timespec(p, ts); +} + +/**************************************************************************** + Return the best approximation to a 'create time' under UNIX from a stat + structure. +****************************************************************************/ + +static time_t calc_create_time(const SMB_STRUCT_STAT *st) +{ + time_t ret, ret1; + + ret = MIN(st->st_ctime, st->st_mtime); + ret1 = MIN(ret, st->st_atime); + + if(ret1 != (time_t)0) { + return ret1; + } + + /* + * One of ctime, mtime or atime was zero (probably atime). + * Just return MIN(ctime, mtime). + */ + return ret; +} + +/**************************************************************************** + 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 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) + ret = pst->st_birthtimespec; +#elif defined(HAVE_STAT_ST_BIRTHTIMENSEC) + ret.tv_sec = pst->st_birthtime; + ret.tv_nsec = pst->st_birthtimenspec; +#elif defined(HAVE_STAT_ST_BIRTHTIME) + ret.tv_sec = pst->st_birthtime; + ret.tv_nsec = 0; +#else + ret.tv_sec = calc_create_time(pst); + ret.tv_nsec = 0; +#endif + + /* Deal with systems that don't initialize birthtime correctly. + * Pointed out by SATOH Fumiyasu <fumiyas@osstech.jp>. + */ + if (null_timespec(ret)) { + ret.tv_sec = calc_create_time(pst); + ret.tv_nsec = 0; + } + return ret; +} + +/**************************************************************************** + Get/Set all the possible time fields from a stat struct as a timespec. +****************************************************************************/ + +struct timespec get_atimespec(const SMB_STRUCT_STAT *pst) +{ +#if !defined(HAVE_STAT_HIRES_TIMESTAMPS) + struct timespec ret; + + /* Old system - no ns timestamp. */ + ret.tv_sec = pst->st_atime; + ret.tv_nsec = 0; + return ret; +#else +#if defined(HAVE_STAT_ST_ATIM) + return pst->st_atim; +#elif defined(HAVE_STAT_ST_ATIMENSEC) + struct timespec ret; + ret.tv_sec = pst->st_atime; + ret.tv_nsec = pst->st_atimensec; + return ret; +#else +#error CONFIGURE_ERROR_IN_DETECTING_TIMESPEC_IN_STAT +#endif +#endif +} + +void set_atimespec(SMB_STRUCT_STAT *pst, struct timespec ts) +{ +#if !defined(HAVE_STAT_HIRES_TIMESTAMPS) + /* Old system - no ns timestamp. */ + pst->st_atime = ts.tv_sec; +#else +#if defined(HAVE_STAT_ST_ATIM) + pst->st_atim = ts; +#elif defined(HAVE_STAT_ST_ATIMENSEC) + pst->st_atime = ts.tv_sec; + pst->st_atimensec = ts.tv_nsec +#else +#error CONFIGURE_ERROR_IN_DETECTING_TIMESPEC_IN_STAT +#endif +#endif +} + +struct timespec get_mtimespec(const SMB_STRUCT_STAT *pst) +{ +#if !defined(HAVE_STAT_HIRES_TIMESTAMPS) + struct timespec ret; + + /* Old system - no ns timestamp. */ + ret.tv_sec = pst->st_mtime; + ret.tv_nsec = 0; + return ret; +#else +#if defined(HAVE_STAT_ST_MTIM) + return pst->st_mtim; +#elif defined(HAVE_STAT_ST_MTIMENSEC) + struct timespec ret; + ret.tv_sec = pst->st_mtime; + ret.tv_nsec = pst->st_mtimensec; + return ret; +#else +#error CONFIGURE_ERROR_IN_DETECTING_TIMESPEC_IN_STAT +#endif +#endif +} + +void set_mtimespec(SMB_STRUCT_STAT *pst, struct timespec ts) +{ +#if !defined(HAVE_STAT_HIRES_TIMESTAMPS) + /* Old system - no ns timestamp. */ + pst->st_mtime = ts.tv_sec; +#else +#if defined(HAVE_STAT_ST_MTIM) + pst->st_mtim = ts; +#elif defined(HAVE_STAT_ST_MTIMENSEC) + pst->st_mtime = ts.tv_sec; + pst->st_mtimensec = ts.tv_nsec +#else +#error CONFIGURE_ERROR_IN_DETECTING_TIMESPEC_IN_STAT +#endif +#endif +} + +struct timespec get_ctimespec(const SMB_STRUCT_STAT *pst) +{ +#if !defined(HAVE_STAT_HIRES_TIMESTAMPS) + struct timespec ret; + + /* Old system - no ns timestamp. */ + ret.tv_sec = pst->st_ctime; + ret.tv_nsec = 0; + return ret; +#else +#if defined(HAVE_STAT_ST_CTIM) + return pst->st_ctim; +#elif defined(HAVE_STAT_ST_CTIMENSEC) + struct timespec ret; + ret.tv_sec = pst->st_ctime; + ret.tv_nsec = pst->st_ctimensec; + return ret; +#else +#error CONFIGURE_ERROR_IN_DETECTING_TIMESPEC_IN_STAT +#endif +#endif +} + +void set_ctimespec(SMB_STRUCT_STAT *pst, struct timespec ts) +{ +#if !defined(HAVE_STAT_HIRES_TIMESTAMPS) + /* Old system - no ns timestamp. */ + pst->st_ctime = ts.tv_sec; +#else +#if defined(HAVE_STAT_ST_CTIM) + pst->st_ctim = ts; +#elif defined(HAVE_STAT_ST_CTIMENSEC) + pst->st_ctime = ts.tv_sec; + pst->st_ctimensec = ts.tv_nsec +#else +#error CONFIGURE_ERROR_IN_DETECTING_TIMESPEC_IN_STAT +#endif +#endif +} + +void dos_filetime_timespec(struct timespec *tsp) +{ + tsp->tv_sec &= ~1; + tsp->tv_nsec = 0; +} + +/******************************************************************* + Create a unix date (int GMT) from a dos date (which is actually in + localtime). +********************************************************************/ + +static time_t make_unix_date(const void *date_ptr, int zone_offset) +{ + uint32 dos_date=0; + struct tm t; + time_t ret; + + dos_date = IVAL(date_ptr,0); + + if (dos_date == 0) { + return 0; + } + + interpret_dos_date(dos_date,&t.tm_year,&t.tm_mon, + &t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec); + t.tm_isdst = -1; + + ret = timegm(&t); + + ret += zone_offset; + + return(ret); +} + +/******************************************************************* + Like make_unix_date() but the words are reversed. +********************************************************************/ + +static time_t make_unix_date2(const void *date_ptr, int zone_offset) +{ + uint32 x,x2; + + x = IVAL(date_ptr,0); + x2 = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16); + SIVAL(&x,0,x2); + + return(make_unix_date((const void *)&x, zone_offset)); +} + +/******************************************************************* + Create a unix GMT date from a dos date in 32 bit "unix like" format + these generally arrive as localtimes, with corresponding DST. +******************************************************************/ + +static time_t make_unix_date3(const void *date_ptr, int zone_offset) +{ + time_t t = (time_t)IVAL(date_ptr,0); + if (!null_mtime(t)) { + t += zone_offset; + } + return(t); +} + +time_t srv_make_unix_date(const void *date_ptr) +{ + return make_unix_date(date_ptr, server_zone_offset); +} + +time_t srv_make_unix_date2(const void *date_ptr) +{ + return make_unix_date2(date_ptr, server_zone_offset); +} + +time_t srv_make_unix_date3(const void *date_ptr) +{ + return make_unix_date3(date_ptr, server_zone_offset); +} + +time_t convert_timespec_to_time_t(struct timespec ts) +{ + /* 1 ns == 1,000,000,000 - one thousand millionths of a second. + increment if it's greater than 500 millionth of a second. */ + if (ts.tv_nsec > 500000000) { + return ts.tv_sec + 1; + } + 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 + will be returned as (time_t)-1, whereas nt_time_to_unix returns 0 in this case. +****************************************************************************/ + +struct timespec interpret_long_date(const char *p) +{ + NTTIME nt; + nt = IVAL(p,0) + ((uint64_t)IVAL(p,4) << 32); + if (nt == (uint64_t)-1) { + struct timespec ret; + ret.tv_sec = (time_t)-1; + ret.tv_nsec = 0; + return ret; + } + return nt_time_to_unix_timespec(&nt); +} + +/*************************************************************************** + Client versions of the above functions. +***************************************************************************/ + +void cli_put_dos_date(struct cli_state *cli, char *buf, int offset, time_t unixdate) +{ + put_dos_date(buf, offset, unixdate, cli->serverzone); +} + +void cli_put_dos_date2(struct cli_state *cli, char *buf, int offset, time_t unixdate) +{ + put_dos_date2(buf, offset, unixdate, cli->serverzone); +} + +void cli_put_dos_date3(struct cli_state *cli, char *buf, int offset, time_t unixdate) +{ + put_dos_date3(buf, offset, unixdate, cli->serverzone); +} + +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, const void *date_ptr) +{ + return make_unix_date2(date_ptr, cli->serverzone); +} + +time_t cli_make_unix_date3(struct cli_state *cli, const void *date_ptr) +{ + return make_unix_date3(date_ptr, cli->serverzone); +} + +/* Large integer version. */ +struct timespec nt_time_to_unix_timespec(NTTIME *nt) +{ + int64 d; + struct timespec ret; + + if (*nt == 0 || *nt == (int64)-1) { + ret.tv_sec = 0; + ret.tv_nsec = 0; + return ret; + } + + d = (int64)*nt; + /* d is now in 100ns units, since jan 1st 1601". + Save off the ns fraction. */ + + /* + * 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; + + /* Now adjust by 369 years to make the secs since 1970 */ + d -= TIME_FIXUP_CONSTANT_INT; + + if (d <= (int64)TIME_T_MIN) { + ret.tv_sec = TIME_T_MIN; + ret.tv_nsec = 0; + return ret; + } + + if (d >= (int64)TIME_T_MAX) { + ret.tv_sec = TIME_T_MAX; + ret.tv_nsec = 0; + return ret; + } + + ret.tv_sec = (time_t)d; + return ret; +} +/**************************************************************************** + Check if two NTTIMEs are the same. +****************************************************************************/ + +bool nt_time_equals(const NTTIME *nt1, const NTTIME *nt2) +{ + return (*nt1 == *nt2); +} + +/******************************************************************* + Re-read the smb serverzone value. +******************************************************************/ + +static struct timeval start_time_hires; + +void TimeInit(void) +{ + set_server_zone_offset(time(NULL)); + + DEBUG(4,("TimeInit: Serverzone is %d\n", server_zone_offset)); + + /* Save the start time of this process. */ + if (start_time_hires.tv_sec == 0 && start_time_hires.tv_usec == 0) { + GetTimeOfDay(&start_time_hires); + } +} + +/********************************************************************** + Return a timeval struct of the uptime of this process. As TimeInit is + done before a daemon fork then this is the start time from the parent + daemon start. JRA. +***********************************************************************/ + +void get_process_uptime(struct timeval *ret_time) +{ + struct timeval time_now_hires; + + GetTimeOfDay(&time_now_hires); + ret_time->tv_sec = time_now_hires.tv_sec - start_time_hires.tv_sec; + if (time_now_hires.tv_usec < start_time_hires.tv_usec) { + ret_time->tv_sec -= 1; + ret_time->tv_usec = 1000000 + (time_now_hires.tv_usec - start_time_hires.tv_usec); + } else { + ret_time->tv_usec = time_now_hires.tv_usec - start_time_hires.tv_usec; + } +} + +/**************************************************************************** + Convert a NTTIME structure to a time_t. + It's originally in "100ns units". + + This is an absolute version of the one above. + By absolute I mean, it doesn't adjust from 1/1/1601 to 1/1/1970 + if the NTTIME was 5 seconds, the time_t is 5 seconds. JFM +****************************************************************************/ + +time_t nt_time_to_unix_abs(const NTTIME *nt) +{ + uint64 d; + + if (*nt == 0) { + return (time_t)0; + } + + if (*nt == (uint64)-1) { + return (time_t)-1; + } + + if (*nt == NTTIME_INFINITY) { + return (time_t)-1; + } + + /* reverse the time */ + /* it's a negative value, turn it to positive */ + d=~*nt; + + d += 1000*1000*10/2; + d /= 1000*1000*10; + + if (!(TIME_T_MIN <= ((time_t)d) && ((time_t)d) <= TIME_T_MAX)) { + return (time_t)0; + } + + return (time_t)d; +} + +time_t uint64s_nt_time_to_unix_abs(const uint64_t *src) +{ + NTTIME nttime; + nttime = *src; + return nt_time_to_unix_abs(&nttime); +} + +/**************************************************************************** + Put a 8 byte filetime from a struct timespec. Uses GMT. +****************************************************************************/ + +void unix_timespec_to_nt_time(NTTIME *nt, struct timespec ts) +{ + uint64 d; + + if (ts.tv_sec ==0 && ts.tv_nsec == 0) { + *nt = 0; + return; + } + if (ts.tv_sec == TIME_T_MAX) { + *nt = 0x7fffffffffffffffLL; + return; + } + if (ts.tv_sec == (time_t)-1) { + *nt = (uint64)-1; + return; + } + + d = ts.tv_sec; + d += TIME_FIXUP_CONSTANT_INT; + d *= 1000*1000*10; + /* d is now in 100ns units. */ + d += (ts.tv_nsec / 100); + + *nt = d; +} + +/**************************************************************************** + Convert a time_t to a NTTIME structure + + This is an absolute version of the one above. + By absolute I mean, it doesn't adjust from 1/1/1970 to 1/1/1601 + If the time_t was 5 seconds, the NTTIME is 5 seconds. JFM +****************************************************************************/ + +void unix_to_nt_time_abs(NTTIME *nt, time_t t) +{ + double d; + + if (t==0) { + *nt = 0; + return; + } + + if (t == TIME_T_MAX) { + *nt = 0x7fffffffffffffffLL; + return; + } + + if (t == (time_t)-1) { + /* that's what NT uses for infinite */ + *nt = NTTIME_INFINITY; + return; + } + + d = (double)(t); + d *= 1.0e7; + + *nt = (NTTIME)d; + + /* convert to a negative value */ + *nt=~*nt; +} + + +/**************************************************************************** + Check if it's a null mtime. +****************************************************************************/ + +bool null_mtime(time_t mtime) +{ + if (mtime == 0 || mtime == (time_t)0xFFFFFFFF || mtime == (time_t)-1) + return(True); + return(False); +} + +/**************************************************************************** + Utility function that always returns a const string even if localtime + and asctime fail. +****************************************************************************/ + +const char *time_to_asc(const time_t t) +{ + const char *asct; + struct tm *lt = localtime(&t); + + if (!lt) { + return "unknown time"; + } + + asct = asctime(lt); + if (!asct) { + return "unknown time"; + } + return asct; +} + +const char *display_time(NTTIME nttime) +{ + float high; + float low; + int sec; + int days, hours, mins, secs; + + if (nttime==0) + return "Now"; + + if (nttime==NTTIME_INFINITY) + return "Never"; + + high = 65536; + high = high/10000; + high = high*65536; + high = high/1000; + high = high * (~(nttime >> 32)); + + low = ~(nttime & 0xFFFFFFFF); + low = low/(1000*1000*10); + + 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); + + 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) +{ + if (*nt == 0x7FFFFFFFFFFFFFFFLL) { + return False; + } + + if (*nt == NTTIME_INFINITY) { + return False; + } + + return True; +} |