snapraid/cmdline/touch.c
Andrea Mazzoleni c6951393e8
Some checks are pending
Build / build-mac (push) Waiting to run
Build / build-linux (push) Waiting to run
Build / build-arm32 (push) Waiting to run
Build / build-win (push) Waiting to run
Build / build-freebsd (push) Waiting to run
Build / build-musl (push) Waiting to run
CodeQL Advanced / Analyze (c-cpp) (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run
Exit code and logging invariants for log_fatal and hardware log_error
2026-09-21 17:27:07 +02:00

176 lines
4.4 KiB
C

// 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"
int state_touch(struct snapraid_state* state)
{
tommy_node* i;
unsigned counter = 0;
int error = 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));
++error;
}
}
}
}
/* LCOV_EXCL_STOP */
}
#endif
if (f == -1) {
/* LCOV_EXCL_START */
log_error(errno, "Error opening file '%s'. %s.\n", path, strerror(errno));
++error;
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));
++error;
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));
++error;
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));
++error;
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));
++error;
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);
if (error != 0)
return -1;
return 0;
}