diff --git a/configure.ac b/configure.ac index 177f2871..abe0d624 100644 --- a/configure.ac +++ b/configure.ac @@ -313,7 +313,7 @@ AC_CHECK_FUNCS([ \ atexit basename daemon dup2 fdatasync ffs getopt_long hasmntopt \ mbsinit memmove memset realpath regcomp setlocale setxattr \ strcasecmp strchr strdup strerror strnlen strsep strtol strtoul \ - sysconf utime utimensat fork \ + sysconf utime utimensat gettimeofday clock_gettime fork \ ]) AC_SYS_LARGEFILE diff --git a/include/ntfs-3g/inode.h b/include/ntfs-3g/inode.h index 468c0f24..5a6f7da6 100644 --- a/include/ntfs-3g/inode.h +++ b/include/ntfs-3g/inode.h @@ -32,6 +32,7 @@ typedef struct _ntfs_inode ntfs_inode; #include "layout.h" #include "support.h" #include "volume.h" +#include "ntfstime.h" /** * enum ntfs_inode_state_bits - @@ -153,10 +154,10 @@ struct _ntfs_inode { * STANDARD_INFORMATION attribute and used to sync it and FILE_NAME * attribute in the index. */ - time_t creation_time; - time_t last_data_change_time; - time_t last_mft_change_time; - time_t last_access_time; + ntfs_time creation_time; + ntfs_time last_data_change_time; + ntfs_time last_mft_change_time; + ntfs_time last_access_time; /* NTFS 3.x extensions added by JPA */ /* only if NI_v3_Extensions is set in state */ le32 owner_id; diff --git a/include/ntfs-3g/ntfstime.h b/include/ntfs-3g/ntfstime.h index e933b53c..e20492fb 100644 --- a/include/ntfs-3g/ntfstime.h +++ b/include/ntfs-3g/ntfstime.h @@ -3,6 +3,7 @@ * * Copyright (c) 2005 Anton Altaparmakov * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2010 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -26,44 +27,105 @@ #ifdef HAVE_TIME_H #include #endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_GETTIMEOFDAY +#include +#endif #include "types.h" +/* + * assume "struct timespec" is not defined if st_mtime is not defined + */ +#ifndef st_mtime +struct timespec { + time_t tv_sec; + long tv_nsec; +} ; +#endif + +/* + * There are four times more conversions of internal representation + * to ntfs representation than any other conversion, so the most + * efficient internal representation is ntfs representation + * (with low endianness) + */ +typedef sle64 ntfs_time; + #define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000) /** - * ntfs2utc - Convert an NTFS time to Unix time + * ntfs2timespec - Convert an NTFS time to Unix time * @ntfs_time: An NTFS time in 100ns units since 1601 * * NTFS stores times as the number of 100ns intervals since January 1st 1601 at * 00:00 UTC. This system will not suffer from Y2K problems until ~57000AD. * - * Return: n A Unix time (number of seconds since 1970) + * Return: A Unix time (number of seconds since 1970, and nanoseconds) */ -static __inline__ time_t ntfs2utc(s64 ntfs_time) +static __inline__ struct timespec ntfs2timespec(ntfs_time ntfstime) { - return (sle64_to_cpu(ntfs_time) - (NTFS_TIME_OFFSET)) / 10000000; + struct timespec spec; + s64 cputime; + + cputime = sle64_to_cpu(ntfstime); + spec.tv_sec = (cputime - (NTFS_TIME_OFFSET)) / 10000000; + spec.tv_nsec = (cputime - (NTFS_TIME_OFFSET) + - (s64)spec.tv_sec*10000000)*100; + /* force zero nsec for overflowing dates */ + if ((spec.tv_nsec < 0) || (spec.tv_nsec > 999999999)) + spec.tv_nsec = 0; + return (spec); } /** - * utc2ntfs - Convert Linux time to NTFS time + * timespec2ntfs - Convert Linux time to NTFS time * @utc_time: Linux time to convert to NTFS * * Convert the Linux time @utc_time to its corresponding NTFS time. * * Linux stores time in a long at present and measures it as the number of - * 1-second intervals since 1st January 1970, 00:00:00 UTC. + * 1-second intervals since 1st January 1970, 00:00:00 UTC + * with a separated non-negative nanosecond value * - * NTFS uses Microsoft's standard time format which is stored in a s64 and is + * NTFS uses Microsoft's standard time format which is stored in a sle64 and is * measured as the number of 100 nano-second intervals since 1st January 1601, * 00:00:00 UTC. * - * Return: n An NTFS time (100ns units since Jan 1601) + * Return: An NTFS time (100ns units since Jan 1601) */ -static __inline__ s64 utc2ntfs(time_t utc_time) +static __inline__ ntfs_time timespec2ntfs(struct timespec spec) { - /* Convert to 100ns intervals and then add the NTFS time offset. */ - return cpu_to_sle64((s64)utc_time * 10000000 + NTFS_TIME_OFFSET); + s64 units; + + units = (s64)spec.tv_sec * 10000000 + + NTFS_TIME_OFFSET + spec.tv_nsec/100; + return (cpu_to_le64(units)); +} + +/* + * Return the current time in ntfs format + */ + +static __inline__ ntfs_time ntfs_current_time(void) +{ + struct timespec now; + +#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_SYS_CLOCK_GETTIME) + clock_gettime(CLOCK_REALTIME, &now); +#elif defined(HAVE_GETTIMEOFDAY) + struct timeval microseconds; + + gettimeofday(µseconds, (struct timezone*)NULL); + now.tv_sec = microseconds.tv_sec; + now.tv_nsec = microseconds.tv_usec*1000; +#else + now.tv_sec = time((time_t*)NULL); + now.tv_nsec = 0; +#endif + return (timespec2ntfs(now)); } #endif /* _NTFS_NTFSTIME_H */ diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index e629ed0f..03e98c74 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2004-2008 Szabolcs Szakacsits * Copyright (c) 2005-2007 Yura Pakhuchiy - * Copyright (c) 2008-2009 Jean-Pierre Andre + * Copyright (c) 2008-2010 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -1380,10 +1380,10 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, err = errno; goto err_out; } - si->creation_time = utc2ntfs(ni->creation_time); - si->last_data_change_time = utc2ntfs(ni->last_data_change_time); - si->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - si->last_access_time = utc2ntfs(ni->last_access_time); + si->creation_time = ni->creation_time; + si->last_data_change_time = ni->last_data_change_time; + si->last_mft_change_time = ni->last_mft_change_time; + si->last_access_time = ni->last_access_time; if (securid) { set_nino_flag(ni, v3_Extensions); ni->owner_id = si->owner_id = 0; @@ -1525,10 +1525,10 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, fn->file_attributes = FILE_ATTR_SYSTEM; else fn->file_attributes |= ni->flags & FILE_ATTR_COMPRESSED; - fn->creation_time = utc2ntfs(ni->creation_time); - fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); - fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - fn->last_access_time = utc2ntfs(ni->last_access_time); + fn->creation_time = ni->creation_time; + fn->last_data_change_time = ni->last_data_change_time; + fn->last_mft_change_time = ni->last_mft_change_time; + fn->last_access_time = ni->last_access_time; if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) fn->data_size = fn->allocated_size = const_cpu_to_le64(0); else { @@ -2007,10 +2007,10 @@ static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, fn->allocated_size = cpu_to_sle64(ni->allocated_size); fn->data_size = cpu_to_sle64(ni->data_size); } - fn->creation_time = utc2ntfs(ni->creation_time); - fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); - fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - fn->last_access_time = utc2ntfs(ni->last_access_time); + fn->creation_time = ni->creation_time; + fn->last_data_change_time = ni->last_data_change_time; + fn->last_mft_change_time = ni->last_mft_change_time; + fn->last_access_time = ni->last_access_time; memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); /* Add FILE_NAME attribute to index. */ if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 981fba9e..cfafa965 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -5,7 +5,7 @@ * Copyright (c) 2002-2008 Szabolcs Szakacsits * Copyright (c) 2004-2007 Yura Pakhuchiy * Copyright (c) 2004-2005 Richard Russon - * Copyright (c) 2009 Jean-Pierre Andre + * Copyright (c) 2009-2010 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -194,10 +194,10 @@ static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref) std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); ni->flags = std_info->file_attributes; - ni->creation_time = ntfs2utc(std_info->creation_time); - ni->last_data_change_time = ntfs2utc(std_info->last_data_change_time); - ni->last_mft_change_time = ntfs2utc(std_info->last_mft_change_time); - ni->last_access_time = ntfs2utc(std_info->last_access_time); + ni->creation_time = std_info->creation_time; + ni->last_data_change_time = std_info->last_data_change_time; + ni->last_mft_change_time = std_info->last_mft_change_time; + ni->last_access_time = std_info->last_access_time; /* JPA insert v3 extensions if present */ /* length may be seen as 72 (v1.x) or 96 (v3.x) */ lthle = ctx->attr->length; @@ -718,10 +718,10 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni) le16_to_cpu(ctx->attr->value_offset)); std_info->file_attributes = ni->flags; if (!test_nino_flag(ni, TimesSet)) { - std_info->creation_time = utc2ntfs(ni->creation_time); - std_info->last_data_change_time = utc2ntfs(ni->last_data_change_time); - std_info->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - std_info->last_access_time = utc2ntfs(ni->last_access_time); + std_info->creation_time = ni->creation_time; + std_info->last_data_change_time = ni->last_data_change_time; + std_info->last_mft_change_time = ni->last_mft_change_time; + std_info->last_access_time = ni->last_access_time; } /* JPA update v3.x extensions, ensuring consistency */ @@ -831,10 +831,10 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni) fnx->data_size = cpu_to_sle64(ni->data_size); } if (!test_nino_flag(ni, TimesSet)) { - fnx->creation_time = utc2ntfs(ni->creation_time); - fnx->last_data_change_time = utc2ntfs(ni->last_data_change_time); - fnx->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - fnx->last_access_time = utc2ntfs(ni->last_access_time); + fnx->creation_time = ni->creation_time; + fnx->last_data_change_time = ni->last_data_change_time; + fnx->last_mft_change_time = ni->last_mft_change_time; + fnx->last_access_time = ni->last_access_time; } else { fnx->creation_time = fn->creation_time; fnx->last_data_change_time = fn->last_data_change_time; @@ -1322,7 +1322,7 @@ put_err_out: */ void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) { - time_t now; + ntfs_time now; if (!ni) { ntfs_log_error("%s(): Invalid arguments.\n", __FUNCTION__); @@ -1333,7 +1333,7 @@ void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) NVolReadOnly(ni->vol) || !mask) return; - now = time(NULL); + now = ntfs_current_time(); if (mask & NTFS_UPDATE_ATIME) ni->last_access_time = now; if (mask & NTFS_UPDATE_MTIME) @@ -1466,14 +1466,14 @@ int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size, STANDARD_INFORMATION *std_info; FILE_NAME_ATTR *fn; const u64 *times; - le64 now; + ntfs_time now; int cnt; int ret; ret = -1; if ((size >= 8) && !(flags & XATTR_CREATE)) { times = (const u64*)value; - now = utc2ntfs(time((time_t*)NULL)); + now = ntfs_current_time(); /* update the standard information attribute */ ctx = ntfs_attr_get_search_ctx(ni, NULL); if (ctx) { @@ -1496,19 +1496,19 @@ int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size, set_nino_flag(ni, TimesSet); std_info->creation_time = cpu_to_le64(times[0]); ni->creation_time - = ntfs2utc(std_info->creation_time); + = std_info->creation_time; if (size >= 16) { std_info->last_data_change_time = cpu_to_le64(times[1]); ni->last_data_change_time - = ntfs2utc(std_info->last_data_change_time); + = std_info->last_data_change_time; } if (size >= 24) { std_info->last_access_time = cpu_to_le64(times[2]); ni->last_access_time - = ntfs2utc(std_info->last_access_time); + = std_info->last_access_time; } std_info->last_mft_change_time = now; - ni->last_mft_change_time = ntfs2utc(now); + ni->last_mft_change_time = now; ntfs_inode_mark_dirty(ctx->ntfs_ino); NInoFileNameSetDirty(ni); diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 28ad5d19..e93c6646 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -1478,7 +1478,7 @@ found_free_rec: ni->flags = 0; ni->creation_time = ni->last_data_change_time = ni->last_mft_change_time = - ni->last_access_time = time(NULL); + ni->last_access_time = ntfs_current_time(); /* Update the default mft allocation position if it was used. */ if (!base_ni) vol->mft_data_pos = bit + 1; @@ -1780,7 +1780,7 @@ found_free_rec: ni->flags = 0; ni->creation_time = ni->last_data_change_time = ni->last_mft_change_time = - ni->last_access_time = time(NULL); + ni->last_access_time = ntfs_current_time(); /* Update the default mft allocation position if it was used. */ if (!base_ni) vol->mft_data_pos = bit + 1; diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index e7fb8f1b..d55dc51f 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2007 Yura Pakhuchiy * Copyright (c) 2005 Yuval Fledel * Copyright (c) 2006-2009 Szabolcs Szakacsits - * Copyright (c) 2007-2009 Jean-Pierre Andre + * Copyright (c) 2007-2010 Jean-Pierre Andre * Copyright (c) 2009 Erik Larsson * * This file is originated from the Linux-NTFS project. @@ -217,7 +217,7 @@ static const char *usage_msg = "\n" "Copyright (C) 2005-2007 Yura Pakhuchiy\n" "Copyright (C) 2006-2009 Szabolcs Szakacsits\n" -"Copyright (C) 2007-2009 Jean-Pierre Andre\n" +"Copyright (C) 2007-2010 Jean-Pierre Andre\n" "Copyright (C) 2009 Erik Larsson\n" "\n" "Usage: %s [-o option[,...]] \n" @@ -275,8 +275,10 @@ static void ntfs_fuse_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) if (ctx->atime == ATIME_DISABLED) mask &= ~NTFS_UPDATE_ATIME; else if (ctx->atime == ATIME_RELATIVE && mask == NTFS_UPDATE_ATIME && - ni->last_access_time >= ni->last_data_change_time && - ni->last_access_time >= ni->last_mft_change_time) + (le64_to_cpu(ni->last_access_time) + >= le64_to_cpu(ni->last_data_change_time)) && + (le64_to_cpu(ni->last_access_time) + >= le64_to_cpu(ni->last_mft_change_time))) return; ntfs_inode_update_times(ni, mask); } @@ -860,9 +862,9 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf) if (S_ISLNK(stbuf->st_mode)) stbuf->st_mode |= 0777; stbuf->st_ino = ni->mft_no; - stbuf->st_atime = ni->last_access_time; - stbuf->st_ctime = ni->last_mft_change_time; - stbuf->st_mtime = ni->last_data_change_time; + stbuf->st_atim = ntfs2timespec(ni->last_access_time); + stbuf->st_ctim = ntfs2timespec(ni->last_mft_change_time); + stbuf->st_mtim = ntfs2timespec(ni->last_data_change_time); exit: if (ntfs_inode_close(ni)) set_fuse_error(&res); @@ -2223,12 +2225,14 @@ static int ntfs_fuse_utimens(const char *path, const struct timespec tv[2]) mask |= NTFS_UPDATE_ATIME; else if (tv[0].tv_nsec != UTIME_OMIT) - ni->last_access_time = tv[0].tv_sec; + ni->last_access_time + = timespec2ntfs(tv[0]); if (tv[1].tv_nsec == UTIME_NOW) mask |= NTFS_UPDATE_MTIME; else if (tv[1].tv_nsec != UTIME_OMIT) - ni->last_data_change_time = tv[1].tv_sec; + ni->last_data_change_time + = timespec2ntfs(tv[1]); ntfs_inode_update_times(ni, mask); #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) } else @@ -2246,6 +2250,8 @@ static int ntfs_fuse_utime(const char *path, struct utimbuf *buf) { ntfs_inode *ni; int res = 0; + struct timespec actime; + struct timespec modtime; #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) BOOL ownerok; BOOL writeok; @@ -2282,8 +2288,12 @@ static int ntfs_fuse_utime(const char *path, struct utimbuf *buf) if (!ownerok && !writeok) res = (buf->actime == buf->modtime ? -EACCES : -EPERM); else { - ni->last_access_time = buf->actime; - ni->last_data_change_time = buf->modtime; + actime.tv_sec = buf->actime; + actime.tv_nsec = 0; + modtime.tv_sec = buf->modtime; + modtime.tv_nsec = 0; + ni->last_access_time = timespec2ntfs(actime); + ni->last_data_change_time = timespec2ntfs(modtime); ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); } } else { @@ -2297,8 +2307,12 @@ static int ntfs_fuse_utime(const char *path, struct utimbuf *buf) } #else if (buf) { - ni->last_access_time = buf->actime; - ni->last_data_change_time = buf->modtime; + actime.tv_sec = buf->actime; + actime.tv_nsec = 0; + modtime.tv_sec = buf->modtime; + modtime.tv_nsec = 0; + ni->last_access_time = timespec2ntfs(actime); + ni->last_data_change_time = timespec2ntfs(modtime); ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); } else ntfs_inode_update_times(ni, NTFS_UPDATE_AMCTIME);