diff --git a/cmdline/check.c b/cmdline/check.c index c55dc9cd..1a332059 100644 --- a/cmdline/check.c +++ b/cmdline/check.c @@ -1343,11 +1343,12 @@ close_and_continue: } /* set the original modification time */ - ret = file_utime(file, f); + ret = fmtime(f, file->mtime_sec, file->mtime_nsec); if (ret != 0) { /* LCOV_EXCL_START */ close(f); + fprintf(stderr, "Error timing file '%s'. %s.\n", file->sub, strerror(errno)); fprintf(stderr, "WARNING! Without a working data disk, it isn't possible to fix errors on it.\n"); printf("Stopping\n"); ++unrecoverable_error; diff --git a/cmdline/handle.c b/cmdline/handle.c index 7dba07be..8d0df88a 100644 --- a/cmdline/handle.c +++ b/cmdline/handle.c @@ -353,52 +353,19 @@ int handle_write(struct snapraid_handle* handle, struct snapraid_block* block, u return 0; } -int file_utime(struct snapraid_file* file, int f) +int handle_utime(struct snapraid_handle* handle) { -#if HAVE_FUTIMENS - struct timespec tv[2]; -#else - struct timeval tv[2]; -#endif int ret; -#if HAVE_FUTIMENS /* futimens() is preferred because it gives nanosecond precision */ - tv[0].tv_sec = file->mtime_sec; - if (file->mtime_nsec != STAT_NSEC_INVALID) - tv[0].tv_nsec = 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; + /* do nothing if not opened */ + if (handle->f == -1) + return 0; - ret = futimens(f, tv); -#elif HAVE_FUTIMES /* fallback to futimes() if nanosecond precision is not available */ - tv[0].tv_sec = file->mtime_sec; - if (file->mtime_nsec != STAT_NSEC_INVALID) - tv[0].tv_usec = 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(f, tv); -#elif HAVE_FUTIMESAT /* fallback to futimesat() for Solaris, it only has futimesat() */ - tv[0].tv_sec = file->mtime_sec; - if (file->mtime_nsec != STAT_NSEC_INVALID) - tv[0].tv_usec = 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 = futimesat(f, 0, tv); -#else -#error No function available to set file timestamps with sub-second precision -#endif + ret = fmtime(handle->f, handle->file->mtime_sec, handle->file->mtime_nsec); if (ret != 0) { /* LCOV_EXCL_START */ - fprintf(stderr, "Error timing file '%s'. %s.\n", file->sub, strerror(errno)); + fprintf(stderr, "Error timing file '%s'. %s.\n", handle->file->sub, strerror(errno)); return -1; /* LCOV_EXCL_STOP */ } @@ -406,15 +373,6 @@ int file_utime(struct snapraid_file* file, int f) return 0; } -int handle_utime(struct snapraid_handle* handle) -{ - /* do nothing if not opened */ - if (handle->f == -1) - return 0; - - return file_utime(handle->file, handle->f); -} - struct snapraid_handle* handle_map(struct snapraid_state* state, unsigned* handlemax) { tommy_node* i; diff --git a/cmdline/handle.h b/cmdline/handle.h index 297474e7..7205d641 100644 --- a/cmdline/handle.h +++ b/cmdline/handle.h @@ -20,14 +20,6 @@ #include "state.h" -/****************************************************************************/ -/* file */ - -/** - * Changes the modification time of a open file to the saved value. - */ -int file_utime(struct snapraid_file* file, int f); - /****************************************************************************/ /* handle */ diff --git a/cmdline/nano.c b/cmdline/nano.c new file mode 100644 index 00000000..d9299dc7 --- /dev/null +++ b/cmdline/nano.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2014 Andrea Mazzoleni + * + * 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 . + */ + +#include "portable.h" + +#include "util.h" +#include "elem.h" +#include "state.h" +#include "handle.h" + +void state_nano(struct snapraid_state* state) +{ + tommy_node* i; + + printf("Setting nanosecond timestamps...\n"); + + /* for all disks */ + for(i=state->disklist;i!=0;i=i->next) { + struct snapraid_disk* disk = i->data; + tommy_node* j; + + /* for all files */ + for(j=disk->filelist;j!=0;j=j->next) { + struct snapraid_file* file = j->data; + + /* if the file has a zero nanosecond timestamp */ + if (file->mtime_nsec == 0) { + char path[PATH_MAX]; + struct stat st; + int f; + int ret; + int nsec; + + pathprint(path, sizeof(path), "%s%s", disk->dir, file->sub); + + /* generate a new nanosecond timestamp different than 0 */ + do { + nsec = rand() % 1000000000; + } while (nsec == 0); + + /* uses stat to get the present timestamp */ + ret = stat(path, &st); + if (ret == -1) { + /* LCOV_EXCL_START */ + fprintf(stderr, "Error accessing file '%s'. %s.\n", path, strerror(errno)); + continue; + /* LCOV_EXCL_STOP */ + } + + /* open it */ + /* O_NOFOLLOW: do not follow links to ensure to open the real file */ + f = open(path, O_WRONLY | O_BINARY | O_NOFOLLOW); + if (f == -1) { + /* LCOV_EXCL_START */ + fprintf(stderr, "Error opening file '%s'. %s.\n", path, strerror(errno)); + continue; + /* LCOV_EXCL_STOP */ + } + + /* set the tweaked modification time */ + ret = fmtime(f, st.st_mtime, nsec); + if (ret != 0) { + /* LCOV_EXCL_START */ + close(f); + + fprintf(stderr, "Error timing file '%s'. %s.\n", path, strerror(errno)); + continue; + /* LCOV_EXCL_STOP */ + } + + /* close it */ + ret = close(f); + if (ret != 0) { + /* LCOV_EXCL_START */ + fprintf(stderr, "Error closing file '%s'. %s.\n", path, strerror(errno)); + continue; + /* LCOV_EXCL_STOP */ + } + + /* uses stat again to get the present timestamp */ + /* this is needed because the value read */ + /* may be different than the written one */ + ret = stat(path, &st); + if (ret == -1) { + /* LCOV_EXCL_START */ + fprintf(stderr, "Error accessing file '%s'. %s.\n", path, strerror(errno)); + continue; + /* LCOV_EXCL_STOP */ + } + + /* set the same nanosecond value in the content file */ + /* note that if the second value is already matching */ + /* the file won't be synched because the content file will */ + /* contain the new updated timestamp */ + file->mtime_nsec = STAT_NSEC(&st); + + /* state changed, we need to update it */ + state->need_write = 1; + + fprintf(stdlog, "nano:%s:%s\n", disk->name, file->sub); + printf("nano %s%s\n", disk->dir, file->sub); + } + } + } +} + diff --git a/cmdline/util.c b/cmdline/util.c index cde900a3..99fff70c 100644 --- a/cmdline/util.c +++ b/cmdline/util.c @@ -1690,3 +1690,52 @@ int lock_unlock(int f) } #endif +/****************************************************************************/ +/* file */ + +int fmtime(int f, int64_t mtime_sec, int mtime_nsec) +{ +#if HAVE_FUTIMENS + struct timespec tv[2]; +#else + struct timeval tv[2]; +#endif + int ret; + +#if HAVE_FUTIMENS /* futimens() 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 = futimens(f, tv); +#elif HAVE_FUTIMES /* fallback to futimes() 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 = futimes(f, 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(f, 0, tv); +#else +#error No function available to set file timestamps with sub-second precision +#endif + + return ret; +} + diff --git a/cmdline/util.h b/cmdline/util.h index a6ed2e13..d443834e 100644 --- a/cmdline/util.h +++ b/cmdline/util.h @@ -558,5 +558,13 @@ int lock_lock(const char* file); */ int lock_unlock(int f); +/****************************************************************************/ +/* file */ + +/** + * Changes the modification time of an open file. + */ +int fmtime(int f, int64_t mtime_sec, int mtime_nsec); + #endif