diff --git a/configure.ac b/configure.ac index b61155c4..f7d6fe5f 100644 --- a/configure.ac +++ b/configure.ac @@ -38,12 +38,13 @@ AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_TYPE_UINT8_T AC_TYPE_INT8_T +AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec, struct stat.st_mtimensec, struct stat.st_mtimespec.tv_nsec], [], [], [[#include ]]) dnl Checks for library functions. AC_SYS_LARGEFILE AC_CHECK_FUNCS([memset strchr strerror strrchr mkdir gettimeofday strtoul]) AC_CHECK_FUNCS([getopt getopt_long snprintf vsnprintf]) -AC_CHECK_FUNCS([ftruncate fallocate fsync access posix_fallocate posix_fadvise pread pwrite getc_unlocked ferror_unlocked fnmatch]) +AC_CHECK_FUNCS([ftruncate fallocate fsync access posix_fallocate posix_fadvise pread pwrite getc_unlocked ferror_unlocked fnmatch futimes futimens]) dnl Checks for architecture AC_C_BIGENDIAN diff --git a/handle.c b/handle.c index 6bbddc0d..ed1ee176 100644 --- a/handle.c +++ b/handle.c @@ -327,20 +327,39 @@ int handle_write(struct snapraid_handle* handle, struct snapraid_block* block, u int handle_utime(struct snapraid_handle* handle) { +#if HAVE_FUTIMENS struct timespec tv[2]; +#else + struct timeval tv[2]; +#endif int ret; /* do nothing if not opened */ if (handle->f == -1) return 0; +#if HAVE_FUTIMENS /* futimens is preferred because it gives nanosecond precision */ tv[0].tv_sec = handle->file->mtime_sec; - tv[0].tv_nsec = handle->file->mtime_nsec; - + if (handle->file->mtime_nsec != FILE_MTIME_NSEC_INVALID) + tv[0].tv_nsec = handle->file->mtime_nsec; + else + tv[0].tv_nsec = 0; tv[1].tv_sec = tv[0].tv_sec; tv[1].tv_nsec = tv[0].tv_nsec; ret = futimens(handle->f, tv); +#else + tv[0].tv_sec = handle->file->mtime_sec; + if (handle->file->mtime_nsec != FILE_MTIME_NSEC_INVALID) + tv[0].tv_usec = handle->file->mtime_nsec / 1000; + else + tv[0].tv_usec = 0; + tv[1].tv_sec = tv[0].tv_sec; + tv[1].tv_usec = tv[0].tv_usec; + + ret = futimes(handle->f, tv); +#endif + if (ret != 0) { fprintf(stderr, "Error timing file '%s'. %s.\n", handle->file->sub, strerror(errno)); return -1; diff --git a/mingw.c b/mingw.c index b1a5af7b..0f186ed2 100644 --- a/mingw.c +++ b/mingw.c @@ -203,7 +203,7 @@ static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, struct win */ mtime -= 116444736000000000LL; st->st_mtime = mtime / 10000000; - st->st_mtim.tv_nsec = (mtime % 10000000) * 100; + st->st_mtimensec = (mtime % 10000000) * 100; st->st_ino = info->nFileIndexHigh; st->st_ino <<= 32; @@ -264,7 +264,7 @@ static void windows_finddata2stat(const WIN32_FIND_DATAW* info, struct windows_s */ mtime -= 116444736000000000LL; st->st_mtime = mtime / 10000000; - st->st_mtim.tv_nsec = (mtime % 10000000) * 100; + st->st_mtimensec = (mtime % 10000000) * 100; /* No inode information available */ st->st_ino = 0; @@ -470,45 +470,6 @@ int windows_ftruncate(int fd, off64_t off) return 0; } -int windows_futimes(int fd, struct timeval tv[2]) -{ - HANDLE h; - FILETIME ft; - uint64_t mtime; - - if (fd == -1) { - errno = EBADF; - return -1; - } - - h = (HANDLE)_get_osfhandle(fd); - if (h == INVALID_HANDLE_VALUE) { - errno = EBADF; - return -1; - } - - /* - * Convert to windows time - * - * How To Convert a UNIX time_t to a Win32 FILETIME or SYSTEMTIME - * http://support.microsoft.com/kb/167296 - */ - mtime = tv[0].tv_sec; - mtime *= 10000000; - mtime += tv[0].tv_usec * 10; - mtime += 116444736000000000; - - ft.dwHighDateTime = mtime >> 32; - ft.dwLowDateTime = mtime; - - if (!SetFileTime(h, 0, 0, &ft)) { - windows_errno(GetLastError()); - return -1; - } - - return 0; -} - int windows_futimens(int fd, struct windows_timespec tv[2]) { HANDLE h; diff --git a/mingw.h b/mingw.h index 9d08d6f7..6f5f8162 100644 --- a/mingw.h +++ b/mingw.h @@ -49,7 +49,7 @@ #define opendir windows_opendir #define readdir windows_readdir #define closedir windows_closedir -#define futimes windows_futimes +#define HAVE_FUTIMENS 1 #define futimens windows_futimens #define O_NOFOLLOW 0 #define stat_hidden windows_stat_hidden @@ -60,10 +60,6 @@ #define readlink(a,b,c) -1 #define symlink(a,b) -1 -struct windows_mtim { - int tv_nsec; -}; - /** * Generic stat information. */ @@ -71,7 +67,7 @@ struct windows_stat { uint64_t st_ino; int64_t st_size; int64_t st_mtime; - struct windows_mtim st_mtim; + int32_t st_mtimensec; uint32_t st_mode; uint32_t st_nlink; uint32_t st_dev; diff --git a/portable.h b/portable.h index 4358c426..9f67d682 100644 --- a/portable.h +++ b/portable.h @@ -112,10 +112,31 @@ #ifdef __MINGW32__ /* Specific for MingW */ #include "mingw.h" + +/* We have nano second support */ +#define STAT_NSEC(st) (st)->st_mtimensec + #else /* Specific for Unix */ + #define O_BINARY 0 /**< Not used in Unix. */ #define O_SEQUENTIAL 0 /**< In Unix posix_fadvise() shall be used. */ +/* Check if we have nanoseconds support */ +#if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC +#define STAT_NSEC(st) (st)->st_mtim.tv_nsec /* Linux */ +#elif HAVE_STRUCT_STAT_ST_MTIMENSEC +#define STAT_NSEC(st) (st)->st_mtimensec /* FreeBSD, Mac OS X if _POSIX_C_SOURCE defined */ +#elif HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC +#define STAT_NSEC(st) (st)->st_mtimespec.tv_nsec /* Mac OS X if _POSIX_C_SOURCE not defined */ +#else +/** + * If nanoseconds are not supported, we report the special FILE_MTIME_NSEC_INVALID value, + * to mark that it's undefined. + * This result in not having it saved into the content file as a dummy 0. + */ +#define STAT_NSEC(st) -1 +#endif + /** * Check if the specified file is hidden. */ diff --git a/scan.c b/scan.c index 7667451d..9e95d47a 100644 --- a/scan.c +++ b/scan.c @@ -174,14 +174,16 @@ static void scan_file(struct snapraid_scan* scan, struct snapraid_state* state, && file->mtime_sec == st->st_mtime /* always accept the value if it's FILE_MTIME_NSEC_INVALID */ /* it happens when upgrading from an old version of SnapRAID */ - && (file->mtime_nsec == st->st_mtim.tv_nsec || file->mtime_nsec == FILE_MTIME_NSEC_INVALID) + && (file->mtime_nsec == STAT_NSEC(st) || file->mtime_nsec == FILE_MTIME_NSEC_INVALID) ) { /* mark as present */ file_flag_set(file, FILE_IS_PRESENT); /* update the nano seconds mtime if required */ - if (file->mtime_nsec == FILE_MTIME_NSEC_INVALID) { - file->mtime_nsec = st->st_mtim.tv_nsec; + if (file->mtime_nsec == FILE_MTIME_NSEC_INVALID + && STAT_NSEC(st) != FILE_MTIME_NSEC_INVALID + ) { + file->mtime_nsec = STAT_NSEC(st); /* we have to save the new mtime */ state->need_write = 1; @@ -315,7 +317,7 @@ static void scan_file(struct snapraid_scan* scan, struct snapraid_state* state, } /* insert it */ - file = file_alloc(state->block_size, sub, st->st_size, st->st_mtime, st->st_mtim.tv_nsec, st->st_ino); + file = file_alloc(state->block_size, sub, st->st_size, st->st_mtime, STAT_NSEC(st), st->st_ino); /* mark it as present */ file_flag_set(file, FILE_IS_PRESENT); diff --git a/state.c b/state.c index d38975bd..1658153e 100644 --- a/state.c +++ b/state.c @@ -900,7 +900,7 @@ void state_read(struct snapraid_state* state) /* use a special value, meaning that we don't have this information */ v_mtime_nsec = FILE_MTIME_NSEC_INVALID; } - + if (c != ' ') { fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); @@ -1303,7 +1303,7 @@ void state_write(struct snapraid_state* state) block_off_t k; uint64_t size; uint64_t mtime_sec; - uint32_t mtime_nsec; + int32_t mtime_nsec; uint64_t inode; size = file->size; @@ -1321,9 +1321,11 @@ void state_write(struct snapraid_state* state) sputc(' ', f); sputu64(mtime_sec, f); hash = oathash64(hash, mtime_sec); - sputc('.', f); - sputu32(mtime_nsec, f); - hash = oathash32(hash, mtime_nsec); + if (mtime_nsec != FILE_MTIME_NSEC_INVALID) { + sputc('.', f); + sputu32(mtime_nsec, f); + hash = oathash32(hash, mtime_nsec); + } sputc(' ', f); sputu64(inode, f); hash = oathash64(hash, inode); diff --git a/sync.c b/sync.c index 9dce8791..460e8d71 100644 --- a/sync.c +++ b/sync.c @@ -204,13 +204,13 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari /* check if the file is changed */ if (handle[j].st.st_size != block_file_get(block)->size || handle[j].st.st_mtime != block_file_get(block)->mtime_sec - || handle[j].st.st_mtim.tv_nsec != block_file_get(block)->mtime_nsec + || STAT_NSEC(&handle[j].st) != block_file_get(block)->mtime_nsec || handle[j].st.st_ino != block_file_get(block)->inode ) { if (handle[j].st.st_size != block_file_get(block)->size) fprintf(stderr, "Unexpected size change at file '%s'.\n", handle[j].path); else if (handle[j].st.st_mtime != block_file_get(block)->mtime_sec - || handle[j].st.st_mtim.tv_nsec != block_file_get(block)->mtime_nsec) + || STAT_NSEC(&handle[j].st) != block_file_get(block)->mtime_nsec) fprintf(stderr, "Unexpected time change at file '%s'.\n", handle[j].path); else fprintf(stderr, "Unexpected inode change from %"PRIu64" to %"PRIu64" at file '%s'.\n", block_file_get(block)->inode, handle[j].st.st_ino, handle[j].path);