// SPDX-License-Identifier: GPL-3.0-or-later // Copyright (C) 2014 Andrea Mazzoleni #include "os/portable.h" #include "support.h" #include "elem.h" #include "state.h" #include "handle.h" void state_touch(struct snapraid_state* state) { tommy_node* i; unsigned counter = 0; msg_progress("Setting sub-second 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 * note that symbolic links are not in the file list * and then are not processed */ if (file->mtime_nsec == 0) { char path[PATH_MAX]; struct stat st; int f; int ret; int nsec; int flags; pathprint(path, sizeof(path), "%s%s", disk->dir, file->sub); /* set a new nanosecond timestamp different than 0 */ do { uint32_t nano; /* get a random nanosecond value */ if (os_randomize(&nano, sizeof(nano)) != 0) { /* LCOV_EXCL_START */ log_fatal(errno, "Failed to retrieve random values.\n"); exit(EXIT_FAILURE); /* LCOV_EXCL_STOP */ } nsec = nano % 1000000000; } while (nsec == 0); /* * O_BINARY: open as binary file (Windows only) * O_NOFOLLOW: do not follow links to ensure to open the real file */ flags = O_BINARY | O_NOFOLLOW; #ifdef _WIN32 /* in Windows we must have write access at the file */ flags |= O_RDWR; #else /* in all others platforms, read access is enough */ flags |= O_RDONLY; #endif /* open it */ f = open(path, flags); #ifdef _WIN32 if (f == -1) { /* LCOV_EXCL_START */ if (errno == EACCES) { int attributes = windows_get_file_attributes(path); if (attributes != -1 && (attributes & FILE_ATTRIBUTE_READONLY) != 0) { /* remove read only attribute */ if (windows_set_file_attributes(path, attributes & ~FILE_ATTRIBUTE_READONLY) == 0) { f = open(path, flags); if (windows_set_file_attributes(path, attributes) != 0) { log_error(errno, "Error restoring read-only attribute for '%s'. %s.\n", path, strerror(errno)); } } } } /* LCOV_EXCL_STOP */ } #endif if (f == -1) { /* LCOV_EXCL_START */ log_error(errno, "Error opening file '%s'. %s.\n", path, strerror(errno)); continue; /* LCOV_EXCL_STOP */ } /* * Get the present timestamp, that may be different than the one * in the content file */ ret = fstat(f, &st); if (ret == -1) { /* LCOV_EXCL_START */ close(f); log_error(errno, "Error accessing file '%s'. %s.\n", path, strerror(errno)); continue; /* LCOV_EXCL_STOP */ } /* set the tweaked modification time, with new nano seconds */ ret = fmtime(f, st.st_mtime, nsec); if (ret != 0) { /* LCOV_EXCL_START */ close(f); log_error(errno, "Error timing file '%s'. %s.\n", path, strerror(errno)); continue; /* LCOV_EXCL_STOP */ } /* * Uses fstat again to get the present timestamp * this is needed because the value read * may be different than the written one */ ret = fstat(f, &st); if (ret == -1) { /* LCOV_EXCL_START */ close(f); log_error(errno, "Error accessing file '%s'. %s.\n", path, strerror(errno)); continue; /* LCOV_EXCL_STOP */ } /* close it */ ret = close(f); if (ret != 0) { /* LCOV_EXCL_START */ log_error(errno, "Error closing file '%s'. %s.\n", path, strerror(errno)); continue; /* LCOV_EXCL_STOP */ } /* * Set the same nanosecond value in the content file * note that if the seconds value is already matching * the file won't be synced 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; log_tag("touch:%s:%s: %" PRIu64 ".%d\n", disk->name, esc_tag(file->sub), (uint64_t)st.st_mtime, STAT_NSEC(&st)); msg_info("touch %s\n", fmt_term(disk, file->sub)); ++counter; } } } msg_status("\n"); msg_status("%8u touched files\n", counter); }