snapraid/cmdline/touch.c

199 lines
5 KiB
C
Raw Permalink Normal View History

// SPDX-License-Identifier: GPL-3.0-or-later
2026-04-02 16:25:49 +02:00
// 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;
2016-01-18 19:38:42 +01:00
msg_progress("Setting sub-second timestamps...\n");
/* for all disks */
2014-09-27 00:32:39 +02:00
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
tommy_node* j;
/* for all files */
2014-09-27 00:32:39 +02:00
for (j = disk->filelist; j != 0; j = j->next) {
struct snapraid_file* file = j->data;
2026-04-27 15:43:06 +02:00
/*
* 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;
2016-02-02 18:55:47 +01:00
int flags;
pathprint(path, sizeof(path), "%s%s", disk->dir, file->sub);
2014-12-27 12:47:40 +01:00
/* set a new nanosecond timestamp different than 0 */
do {
2014-12-27 12:47:40 +01:00
uint32_t nano;
/* get a random nanosecond value */
2026-06-11 18:36:13 +02:00
if (os_randomize(&nano, sizeof(nano)) != 0) {
2014-12-27 12:47:40 +01:00
/* LCOV_EXCL_START */
log_fatal(errno, "Failed to retrieve random values.\n");
2014-12-27 12:47:40 +01:00
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
nsec = nano % 1000000000;
} while (nsec == 0);
2026-04-27 15:43:06 +02:00
/*
* O_BINARY: open as binary file (Windows only)
* O_NOFOLLOW: do not follow links to ensure to open the real file
*/
2016-02-02 18:55:47 +01:00
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) {
/*
* Ignore missing files because they may have been deleted
* or moved since the last sync. Also ignore symbolic links
* which fail with ELOOP due to O_NOFOLLOW.
*/
if (errno == ENOENT || errno == ENOTDIR || errno == ELOOP)
continue;
/* LCOV_EXCL_START */
2026-03-10 23:43:21 +01:00
log_error(errno, "Error opening file '%s'. %s.\n", path, strerror(errno));
++error;
continue;
/* LCOV_EXCL_STOP */
}
2026-04-27 15:43:06 +02:00
/*
* 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);
2026-03-10 23:43:21 +01:00
log_error(errno, "Error accessing file '%s'. %s.\n", path, strerror(errno));
++error;
continue;
/* LCOV_EXCL_STOP */
}
/*
* Touch only regular files that are still unchanged compared to
* the content file. Otherwise changing the timestamp here
* could hide a modification from the following scan.
*/
if (!S_ISREG(st.st_mode)
|| st.st_size != file->size
|| st.st_mtime != file->mtime_sec
|| STAT_NSEC(&st) != file->mtime_nsec
) {
close(f);
continue;
}
/* set the tweaked modification time, with new nano seconds */
ret = fmtime(f, st.st_mtime, nsec);
if (ret != 0) {
/* LCOV_EXCL_START */
close(f);
2026-03-10 23:43:21 +01:00
log_error(errno, "Error timing file '%s'. %s.\n", path, strerror(errno));
++error;
continue;
/* LCOV_EXCL_STOP */
}
2026-04-27 15:43:06 +02:00
/*
* 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);
2026-03-10 23:43:21 +01:00
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 */
2026-03-10 23:43:21 +01:00
log_error(errno, "Error closing file '%s'. %s.\n", path, strerror(errno));
++error;
continue;
/* LCOV_EXCL_STOP */
}
2026-04-27 15:43:06 +02:00
/*
* 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;
}