diff --git a/HISTORY b/HISTORY index bbce098b..a2425299 100644 --- a/HISTORY +++ b/HISTORY @@ -1,6 +1,11 @@ SnapRAID HISTORY ================ +1.12 2012/7 +=========== + * Increased the file modification time precision. Now also nanoseconds are counted. + Note that content files written with this version are not backward compatible. + 1.11 2012/7 =========== * Fixed again directories inclusion. Exclusion rules for directories were diff --git a/TODO b/TODO index abd29270..a2c27426 100644 --- a/TODO +++ b/TODO @@ -3,7 +3,7 @@ SnapRAID TODO This is the list of TODO items for SnapRAID. -- v1.11 +- v1.13 * In the repair() function the euristic to detect if we recovered after the sync, can be extended to all the subsequent blocks, because we always proceeed in block order during a sync. diff --git a/check.c b/check.c index 1f34ad5d..1f5a1f18 100644 --- a/check.c +++ b/check.c @@ -880,7 +880,8 @@ static int state_check_process(struct snapraid_state* state, int fix, struct sna if (!collide_file /* if not in the database, there is no collision */ || strcmp(collide_file->sub, handle[j].file->sub) == 0 /* if the name is the same, it's the right collision */ || collide_file->size != handle[j].file->size /* if the size is different, the collision is identified */ - || collide_file->mtime != handle[j].file->mtime /* if the mtime is different, the collision is identified */ + || collide_file->mtime_sec != handle[j].file->mtime_sec /* if the mtime is different, the collision is identified */ + || collide_file->mtime_nsec != handle[j].file->mtime_nsec ) { /* set the original modification time */ ret = handle_utime(&handle[j]); diff --git a/configure.ac b/configure.ac index 071dd64d..6ce34dc1 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ([2.65]) -AC_INIT([snapraid], [1.11], [], [], [http://snapraid.sourceforge.net]) +AC_INIT([snapraid], [1.12], [], [], [http://snapraid.sourceforge.net]) AM_INIT_AUTOMAKE([foreign no-dependencies -Wall]) AC_CONFIG_SRCDIR([snapraid.c]) AC_CONFIG_HEADER([config.h]) diff --git a/elem.c b/elem.c index 0f018a49..91796a6c 100644 --- a/elem.c +++ b/elem.c @@ -256,7 +256,7 @@ unsigned block_file_size(struct snapraid_block* block, unsigned block_size) return block_size; } -struct snapraid_file* file_alloc(unsigned block_size, const char* sub, uint64_t size, uint64_t mtime, uint64_t inode) +struct snapraid_file* file_alloc(unsigned block_size, const char* sub, uint64_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode) { struct snapraid_file* file; block_off_t i; @@ -265,7 +265,8 @@ struct snapraid_file* file_alloc(unsigned block_size, const char* sub, uint64_t pathcpy(file->sub, sizeof(file->sub), sub); file->size = size; file->blockmax = (size + block_size - 1) / block_size; - file->mtime = mtime; + file->mtime_sec = mtime_sec; + file->mtime_nsec = mtime_nsec; file->inode = inode; file->flag = 0; file->blockvec = malloc_nofail(file->blockmax * sizeof(struct snapraid_block)); diff --git a/elem.h b/elem.h index 88fb39e0..1805736c 100644 --- a/elem.h +++ b/elem.h @@ -140,10 +140,11 @@ struct snapraid_block { * File. */ struct snapraid_file { - int64_t mtime; /**< Modification time. */ + int64_t mtime_sec; /**< Modification time. */ uint64_t inode; /**< Inode. */ data_off_t size; /**< Size of the file. */ struct snapraid_block* blockvec; /**< All the blocks of the file. */ + int mtime_nsec; /**< Modification time nanoseconds. Less than 1000000000. */ block_off_t blockmax; /**< Number of blocks. */ unsigned flag; /**< FILE_IS_* flags. */ char sub[PATH_MAX]; /**< Sub path of the file. Without the disk dir. The disk is implicit. */ @@ -334,7 +335,7 @@ static inline void file_flag_clear(struct snapraid_file* file, unsigned mask) /** * Allocates a file. */ -struct snapraid_file* file_alloc(unsigned block_size, const char* sub, uint64_t size, uint64_t mtime, uint64_t inode); +struct snapraid_file* file_alloc(unsigned block_size, const char* sub, uint64_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode); /** * Deallocates a file. diff --git a/handle.c b/handle.c index b83b7b85..6bbddc0d 100644 --- a/handle.c +++ b/handle.c @@ -183,7 +183,7 @@ int handle_open(struct snapraid_handle* handle, struct snapraid_file* file) return -1; } - /* get the size of the exising data */ + /* get the size of the existing data */ handle->valid_size = handle->st.st_size; #if HAVE_POSIX_FADVISE @@ -320,27 +320,27 @@ int handle_write(struct snapraid_handle* handle, struct snapraid_block* block, u } /* Here doesn't make sense to call posix_fadvise(..., POSIX_FADV_DONTNEED) because */ - /* at this time the data is still in not yet written and it cannot be discharged. */ + /* at this time the data is still not yet written and it cannot be discharged. */ return 0; } int handle_utime(struct snapraid_handle* handle) { - struct timeval tv[2]; + struct timespec tv[2]; int ret; /* do nothing if not opened */ if (handle->f == -1) return 0; - tv[0].tv_sec = handle->file->mtime; - tv[0].tv_usec = 0; + tv[0].tv_sec = handle->file->mtime_sec; + tv[0].tv_nsec = handle->file->mtime_nsec; tv[1].tv_sec = tv[0].tv_sec; - tv[1].tv_usec = tv[0].tv_usec; + tv[1].tv_nsec = tv[0].tv_nsec; - ret = futimes(handle->f, tv); + ret = futimens(handle->f, tv); 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 04ea492d..956978f2 100644 --- a/mingw.c +++ b/mingw.c @@ -94,7 +94,6 @@ static wchar_t* convert(const char* src) { int ret; wchar_t* dst; - int convert_slash; int count; if (++conv_utf16 == CONV_ROLL) @@ -159,6 +158,8 @@ static wchar_t* convert(const char* src) */ static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, struct windows_stat* st) { + uint64_t mtime; + /* Convert special attributes */ if ((info->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0) { st->st_mode = S_IFBLK; @@ -190,9 +191,9 @@ static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, struct win st->st_size <<= 32; st->st_size |= info->nFileSizeLow; - st->st_mtime = info->ftLastWriteTime.dwHighDateTime; - st->st_mtime <<= 32; - st->st_mtime |= info->ftLastWriteTime.dwLowDateTime; + mtime = info->ftLastWriteTime.dwHighDateTime; + mtime <<= 32; + mtime |= info->ftLastWriteTime.dwLowDateTime; /* * Convert to unix time @@ -200,7 +201,9 @@ static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, struct win * How To Convert a UNIX time_t to a Win32 FILETIME or SYSTEMTIME * http://support.microsoft.com/kb/167296 */ - st->st_mtime = (st->st_mtime - 116444736000000000LL) / 10000000; + mtime -= 116444736000000000LL; + st->st_mtime = mtime / 10000000; + st->st_mtim.tv_nsec = (mtime % 10000000) * 100; st->st_ino = info->nFileIndexHigh; st->st_ino <<= 32; @@ -216,6 +219,8 @@ static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, struct win */ static void windows_finddata2stat(const WIN32_FIND_DATAW* info, struct windows_stat* st) { + uint64_t mtime; + /* Convert special attributes */ if ((info->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0) { st->st_mode = S_IFBLK; @@ -247,9 +252,9 @@ static void windows_finddata2stat(const WIN32_FIND_DATAW* info, struct windows_s st->st_size <<= 32; st->st_size |= info->nFileSizeLow; - st->st_mtime = info->ftLastWriteTime.dwHighDateTime; - st->st_mtime <<= 32; - st->st_mtime |= info->ftLastWriteTime.dwLowDateTime; + mtime = info->ftLastWriteTime.dwHighDateTime; + mtime <<= 32; + mtime |= info->ftLastWriteTime.dwLowDateTime; /* * Convert to unix time @@ -257,7 +262,9 @@ static void windows_finddata2stat(const WIN32_FIND_DATAW* info, struct windows_s * How To Convert a UNIX time_t to a Win32 FILETIME or SYSTEMTIME * http://support.microsoft.com/kb/167296 */ - st->st_mtime = (st->st_mtime - 116444736000000000LL) / 10000000; + mtime -= 116444736000000000LL; + st->st_mtime = mtime / 10000000; + st->st_mtim.tv_nsec = (mtime % 10000000) * 100; /* No inode information available */ st->st_ino = 0; @@ -502,6 +509,45 @@ int windows_futimes(int fd, struct timeval tv[2]) return 0; } +int windows_futimens(int fd, struct windows_timespec 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_nsec / 100; + mtime += 116444736000000000; + + ft.dwHighDateTime = mtime >> 32; + ft.dwLowDateTime = mtime; + + if (!SetFileTime(h, 0, 0, &ft)) { + windows_errno(GetLastError()); + return -1; + } + + return 0; +} + int windows_rename(const char* a, const char* b) { /* diff --git a/mingw.h b/mingw.h index 6af01fbc..88158700 100644 --- a/mingw.h +++ b/mingw.h @@ -50,6 +50,7 @@ #define readdir windows_readdir #define closedir windows_closedir #define futimes windows_futimes +#define futimens windows_futimens #define O_NOFOLLOW 0 #define stat_hidden windows_stat_hidden #define stat_desc windows_stat_desc @@ -58,6 +59,10 @@ #define readlink(a,b,c) -1 #define symlink(a,b) -1 +struct windows_mtim { + int tv_nsec; +}; + /** * Generic stat information. */ @@ -65,6 +70,7 @@ struct windows_stat { uint64_t st_ino; int64_t st_size; int64_t st_mtime; + struct windows_mtim st_mtim; uint32_t st_mode; uint32_t st_nlink; uint32_t st_dev; @@ -132,6 +138,18 @@ int windows_ftruncate(int fd, off64_t off); */ int windows_futimes(int fd, struct timeval tv[2]); +struct windows_timespec { + int64_t tv_sec; + int tv_nsec; +}; + +#define timespec windows_timespec + +/** + * Like the C futimens(). + */ +int windows_futimens(int fd, struct windows_timespec tv[2]); + /** * Like the C rename(). */ diff --git a/scan.c b/scan.c index dbb6cf01..c69b9fd5 100644 --- a/scan.c +++ b/scan.c @@ -172,7 +172,7 @@ static void scan_file(struct snapraid_scan* scan, struct snapraid_state* state, } /* check if the file is not changed */ - if (file->size == st->st_size && file->mtime == st->st_mtime) { + if (file->size == st->st_size && file->mtime_sec == st->st_mtime && file->mtime_nsec == st->st_mtim.tv_nsec) { /* mark as present */ file_flag_set(file, FILE_IS_PRESENT); @@ -304,7 +304,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_ino); + file = file_alloc(state->block_size, sub, st->st_size, st->st_mtime, st->st_mtim.tv_nsec, st->st_ino); /* mark it as present */ file_flag_set(file, FILE_IS_PRESENT); diff --git a/snapraid.1 b/snapraid.1 index 4e9f21bc..aa9d64a7 100644 --- a/snapraid.1 +++ b/snapraid.1 @@ -866,7 +866,7 @@ Defines the position INDEX of the disk NAME in the parity computation. Signature checksum of the content file to ensure that it doesn\(cqt get corrupted. If you want to modify the content file manually, you have to remove this line to avoid this check. -.SS file DISK SIZE TIME INODE PATH +.SS file DISK SIZE TIME_SEC.TIME_NSEC INODE PATH Defines a file in the specified DISK. .PP The INODE number is used to identify the file in the \(a"sync\(a" diff --git a/snapraid.d b/snapraid.d index 252375e1..24c294be 100644 --- a/snapraid.d +++ b/snapraid.d @@ -608,7 +608,7 @@ Content corrupted. If you want to modify the content file manually, you have to remove this line to avoid this check. - file DISK SIZE TIME INODE PATH + file DISK SIZE TIME_SEC.TIME_NSEC INODE PATH Defines a file in the specified DISK. The INODE number is used to identify the file in the "sync" diff --git a/snapraid.txt b/snapraid.txt index 38ddf108..b6a11737 100644 --- a/snapraid.txt +++ b/snapraid.txt @@ -673,8 +673,8 @@ Signature checksum of the content file to ensure that it doesn't get corrupted. If you want to modify the content file manually, you have to remove this line to avoid this check. -10.5 file DISK SIZE TIME INODE PATH ------------------------------------ +10.5 file DISK SIZE TIME_SEC.TIME_NSEC INODE PATH +------------------------------------------------- Defines a file in the specified DISK. diff --git a/state.c b/state.c index 1c7cea0d..6433f059 100644 --- a/state.c +++ b/state.c @@ -841,7 +841,8 @@ void state_read(struct snapraid_state* state) /* file */ char sub[PATH_MAX]; uint64_t v_size; - uint64_t v_mtime; + uint64_t v_mtime_sec; + uint32_t v_mtime_nsec; uint64_t v_inode; tommy_node* i; @@ -878,14 +879,24 @@ void state_read(struct snapraid_state* state) exit(EXIT_FAILURE); } - ret = sgetu64(f, &v_mtime); + ret = sgetu64(f, &v_mtime_sec); if (ret < 0) { fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); } - hash = oathash64(hash, v_mtime); + hash = oathash64(hash, v_mtime_sec); c = sgetc(f); + if (c == '.') { /* the nanosecond field is present only from version 1.13 */ + ret = sgetu32(f, &v_mtime_nsec); + if (ret < 0) { + fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line); + exit(EXIT_FAILURE); + } + hash = oathash32(hash, v_mtime_nsec); + + c = sgetc(f); + } if (c != ' ') { fprintf(stderr, "Invalid 'file' specification in '%s' at line %u\n", path, line); exit(EXIT_FAILURE); @@ -928,7 +939,7 @@ void state_read(struct snapraid_state* state) } /* allocate the file */ - file = file_alloc(state->block_size, sub, v_size, v_mtime, v_inode); + file = file_alloc(state->block_size, sub, v_size, v_mtime_sec, v_mtime_nsec, v_inode); /* insert the file in the file containers */ tommy_hashdyn_insert(&disk->inodeset, &file->nodeset, file, file_inode_hash(file->inode)); @@ -1287,11 +1298,13 @@ void state_write(struct snapraid_state* state) struct snapraid_file* file = j->data; block_off_t k; uint64_t size; - uint64_t mtime; + uint64_t mtime_sec; + uint32_t mtime_nsec; uint64_t inode; size = file->size; - mtime = file->mtime; + mtime_sec = file->mtime_sec; + mtime_nsec = file->mtime_nsec; inode = file->inode; sputsl("file ", f); @@ -1302,8 +1315,11 @@ void state_write(struct snapraid_state* state) sputu64(size, f); hash = oathash64(hash, size); sputc(' ', f); - sputu64(mtime, f); - hash = oathash64(hash, mtime); + sputu64(mtime_sec, f); + hash = oathash64(hash, mtime_sec); + 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 a3107e0f..cb62fd08 100644 --- a/sync.c +++ b/sync.c @@ -196,12 +196,14 @@ 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 + || handle[j].st.st_mtime != block_file_get(block)->mtime_sec + || handle[j].st.st_mtim.tv_nsec != 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) + 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) 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); @@ -209,7 +211,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari ++unrecoverable_error; - /* if the file is charnged, it means that it was modified during sync */ + /* if the file is changed, it means that it was modified during sync */ /* this isn't a serious error, so we skip this block, and continue with others */ skip_this_block = 1; continue;