From 17bec43be28612fe214f11cb9692fc169cab8696 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Mon, 1 May 2017 15:01:38 +0200 Subject: [PATCH] Set time of pool links This avoid index refresh from various applications. --- HISTORY | 1 + TODO | 3 --- cmdline/mingw.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++ cmdline/mingw.h | 10 ++++++++ cmdline/pool.c | 16 +++++++++--- cmdline/support.c | 46 ++++++++++++++++++++++++++++++++++ cmdline/support.h | 6 +++++ configure.ac | 2 +- 8 files changed, 141 insertions(+), 7 deletions(-) diff --git a/HISTORY b/HISTORY index 362fa3d8..6f2522c1 100644 --- a/HISTORY +++ b/HISTORY @@ -13,6 +13,7 @@ SnapRAID HISTORY compatibility. * Fixed building in platforms that don't provide major/minor definitions in sys/types.h. + * When creating 'pool' symbolic links, set their time as the linked files. * Added support for the Windows 10 symbolic link unprivileged creation, using SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE. * Windows binaries built with gcc 4.9.4 using the MXE cross compiler at diff --git a/TODO b/TODO index 6ea75e92..ff6520bb 100644 --- a/TODO +++ b/TODO @@ -5,9 +5,6 @@ This is the list of TODO items for SnapRAID. - Next -* When creating links, set the mtime like the linked file. -See: https://sourceforge.net/p/snapraid/discussion/1677233/thread/936f0087/?limit=25#b3d6 - - Minor * Allow to filter in diff/list by disk name. diff --git a/cmdline/mingw.c b/cmdline/mingw.c index 781d6e39..69ef0898 100644 --- a/cmdline/mingw.c +++ b/cmdline/mingw.c @@ -1040,6 +1040,70 @@ int windows_futimens(int fd, struct windows_timespec tv[2]) return 0; } +int windows_utimensat(int fd, const char* file, struct windows_timespec tv[2], int flags) +{ + wchar_t conv_buf[CONV_MAX]; + HANDLE h; + FILETIME ft; + uint64_t mtime; + DWORD wflags; + + /* + * Support only the absolute paths + */ + if (fd != AT_FDCWD) { + errno = EBADF; + return -1; + } + + /* + * Open the handle of the file. + * + * Use FILE_FLAG_BACKUP_SEMANTICS to open directories (it's just ignored for files). + * Use FILE_FLAG_OPEN_REPARSE_POINT to open symbolic links and not the their target. + * + * Note that even with FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + * and FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT some paths + * cannot be opened like "C:\System Volume Information" resulting + * in error ERROR_ACCESS_DENIED. + */ + wflags = FILE_FLAG_BACKUP_SEMANTICS; + if ((flags & AT_SYMLINK_NOFOLLOW) != 0) + wflags |= FILE_FLAG_OPEN_REPARSE_POINT; + h = CreateFileW(convert(conv_buf, file), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, wflags, 0); + if (h == INVALID_HANDLE_VALUE) { + windows_errno(GetLastError()); + 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()); + CloseHandle(h); + return -1; + } + + if (!CloseHandle(h)) { + windows_errno(GetLastError()); + return -1; + } + + return 0; +} + int windows_rename(const char* from, const char* to) { wchar_t conv_buf_from[CONV_MAX]; diff --git a/cmdline/mingw.h b/cmdline/mingw.h index 22b9acf4..4925405c 100644 --- a/cmdline/mingw.h +++ b/cmdline/mingw.h @@ -85,6 +85,11 @@ #define HAVE_FUTIMENS 1 #undef futimens #define futimens windows_futimens +#define HAVE_UTIMENSAT 1 +#define AT_FDCWD -1 +#define AT_SYMLINK_NOFOLLOW 1 +#undef utimensat +#define utimensat windows_utimensat #define O_NOFOLLOW 0 #define dirent_hidden windows_dirent_hidden #define HAVE_STRUCT_DIRENT_D_STAT 1 @@ -241,6 +246,11 @@ struct windows_timespec { */ int windows_futimens(int fd, struct windows_timespec tv[2]); +/** + * Like the C utimensat(). + */ +int windows_utimensat(int fd, const char* file, struct windows_timespec tv[2], int flags); + /** * Like the C rename(). */ diff --git a/cmdline/pool.c b/cmdline/pool.c index 034ce674..89818ddd 100644 --- a/cmdline/pool.c +++ b/cmdline/pool.c @@ -147,7 +147,7 @@ static int clean_dir(struct snapraid_state* state, const char* dir) /** * Create a link to the specified disk entry. */ -static void make_link(const char* pool_dir, const char* share_dir, struct snapraid_disk* disk, const char* sub) +static void make_link(const char* pool_dir, const char* share_dir, struct snapraid_disk* disk, const char* sub, int64_t mtime_sec, int mtime_nsec) { char path[PATH_MAX]; char linkto[PATH_MAX]; @@ -196,6 +196,16 @@ static void make_link(const char* pool_dir, const char* share_dir, struct snapra /* LCOV_EXCL_STOP */ } } + + if (mtime_sec) { + ret = lmtime(path, mtime_sec, mtime_nsec); + if (ret != 0) { + /* LCOV_EXCL_START */ + log_fatal("Error setting time to symlink '%s'. %s.\n", path, strerror(errno)); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + } } void state_pool(struct snapraid_state* state) @@ -236,14 +246,14 @@ void state_pool(struct snapraid_state* state) /* for each file */ for (j = disk->filelist; j != 0; j = j->next) { struct snapraid_file* file = j->data; - make_link(pool_dir, share_dir, disk, file->sub); + make_link(pool_dir, share_dir, disk, file->sub, file->mtime_sec, file->mtime_nsec); ++count; } /* for each link */ for (j = disk->linklist; j != 0; j = j->next) { struct snapraid_link* slink = j->data; - make_link(pool_dir, share_dir, disk, slink->sub); + make_link(pool_dir, share_dir, disk, slink->sub, 0, 0); ++count; } diff --git a/cmdline/support.c b/cmdline/support.c index eacb3581..80dfa5c2 100644 --- a/cmdline/support.c +++ b/cmdline/support.c @@ -897,6 +897,52 @@ int fmtime(int f, int64_t mtime_sec, int mtime_nsec) return ret; } +int lmtime(const char* path, int64_t mtime_sec, int mtime_nsec) +{ +#if HAVE_UTIMENSAT + struct timespec tv[2]; +#else + struct timeval tv[2]; +#endif + int ret; + +#if HAVE_UTIMENSAT /* utimensat() is preferred because it gives nanosecond precision */ + tv[0].tv_sec = mtime_sec; + if (mtime_nsec != STAT_NSEC_INVALID) + tv[0].tv_nsec = 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 = utimensat(AT_FDCWD, path, tv, AT_SYMLINK_NOFOLLOW); +#elif HAVE_LUTIMES /* fallback to lutimes() if nanosecond precision is not available */ + tv[0].tv_sec = mtime_sec; + if (mtime_nsec != STAT_NSEC_INVALID) + tv[0].tv_usec = 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 = lutimes(path, tv); +#elif HAVE_FUTIMESAT /* fallback to futimesat() for Solaris, it only has futimesat() */ + tv[0].tv_sec = mtime_sec; + if (mtime_nsec != STAT_NSEC_INVALID) + tv[0].tv_usec = 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 = futimesat(AT_FDCWD, path, tv); +#else +#error No function available to set file timestamps with sub-second precision +#endif + + return ret; +} + /****************************************************************************/ /* advise */ diff --git a/cmdline/support.h b/cmdline/support.h index 21249e31..9cdf220e 100644 --- a/cmdline/support.h +++ b/cmdline/support.h @@ -322,6 +322,12 @@ int mkancestor(const char* file); */ int fmtime(int f, int64_t mtime_sec, int mtime_nsec); +/** + * Change the modification time of a file or link. + * Note that links are NOT deferenced. + */ +int lmtime(const char* path, int64_t mtime_sec, int mtime_nsec); + /****************************************************************************/ /* advise */ diff --git a/configure.ac b/configure.ac index a7876f70..17f5f005 100644 --- a/configure.ac +++ b/configure.ac @@ -99,7 +99,7 @@ AC_CHECK_FUNCS([getopt getopt_long snprintf vsnprintf sigaction]) AC_CHECK_FUNCS([ftruncate fallocate access]) AC_CHECK_FUNCS([fsync posix_fadvise sync_file_range]) AC_CHECK_FUNCS([getc_unlocked ferror_unlocked fnmatch]) -AC_CHECK_FUNCS([futimes futimens futimesat localtime_r]) +AC_CHECK_FUNCS([futimes futimens futimesat localtime_r lutimes utimensat]) AC_CHECK_FUNCS([fstatat flock statfs]) AC_CHECK_FUNCS([mach_absolute_time]) AC_CHECK_FUNCS([backtrace backtrace_symbols])