mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
In Windows ensures to use synced information for new files.
Keeps track of the sync state of info with a new st_sync field in the stat struct.
This commit is contained in:
parent
a0c6965959
commit
ca5e635aa1
6 changed files with 108 additions and 29 deletions
2
TODO
2
TODO
|
|
@ -15,6 +15,8 @@ same timestamp/size are present.
|
|||
* In the log print file name as escaped to avoid that internal ":" and "\n"
|
||||
get misinterpreted.
|
||||
|
||||
* Shall CreateFileW use FILE_SHARE_DELETE ?
|
||||
|
||||
- Minor
|
||||
|
||||
* Provides a way to rename disks in content file.
|
||||
|
|
|
|||
|
|
@ -377,7 +377,7 @@ void deleted_free(struct snapraid_deleted* deleted)
|
|||
free(deleted);
|
||||
}
|
||||
|
||||
struct snapraid_file* file_alloc(unsigned block_size, const char* sub, uint64_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode, uint64_t physical)
|
||||
struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode, uint64_t physical)
|
||||
{
|
||||
struct snapraid_file* file;
|
||||
block_off_t i;
|
||||
|
|
|
|||
|
|
@ -643,7 +643,7 @@ static inline void file_flag_clear(struct snapraid_file* file, unsigned mask)
|
|||
/**
|
||||
* Allocates a file.
|
||||
*/
|
||||
struct snapraid_file* file_alloc(unsigned block_size, const char* sub, uint64_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode, uint64_t physical);
|
||||
struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode, uint64_t physical);
|
||||
|
||||
/**
|
||||
* Deallocates a file.
|
||||
|
|
|
|||
|
|
@ -366,7 +366,7 @@ static void windows_attr2stat(DWORD FileAttributes, DWORD ReparseTag, struct win
|
|||
switch (ReparseTag) {
|
||||
/* if we don't have the ReparseTag information */
|
||||
case 0 :
|
||||
/* don't set the st_mode, to set it later calling lstat_ex() */
|
||||
/* don't set the st_mode, to set it later calling lstat_sync() */
|
||||
st->st_mode = 0;
|
||||
st->st_desc = "unknown";
|
||||
break;
|
||||
|
|
@ -457,6 +457,9 @@ static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, const FILE
|
|||
st->st_ino |= info->nFileIndexLow;
|
||||
|
||||
st->st_dev = info->dwVolumeSerialNumber;
|
||||
|
||||
/* GetFileInformationByHandle() ensures to return synced information */
|
||||
st->st_sync = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -488,6 +491,9 @@ static void windows_stream2stat(const BY_HANDLE_FILE_INFORMATION* info, const FI
|
|||
st->st_ino = stream->FileId.QuadPart;
|
||||
|
||||
st->st_dev = info->dwVolumeSerialNumber;
|
||||
|
||||
/* directory listing doesn't ensure to return synced information */
|
||||
st->st_sync = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -522,6 +528,9 @@ static void windows_finddata2stat(const WIN32_FIND_DATAW* info, struct windows_s
|
|||
|
||||
/* No device information available */
|
||||
st->st_dev = 0;
|
||||
|
||||
/* directory listing doesn't ensure to return synced information */
|
||||
st->st_sync = 0;
|
||||
}
|
||||
|
||||
static void windows_finddata2dirent(const WIN32_FIND_DATAW* info, struct windows_dirent* dirent)
|
||||
|
|
@ -681,7 +690,7 @@ int windows_rmdir(const char* file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int lstat_ex(const char* file, struct windows_stat* st)
|
||||
int lstat_sync(const char* file, struct windows_stat* st)
|
||||
{
|
||||
BY_HANDLE_FILE_INFORMATION info;
|
||||
FILE_ATTRIBUTE_TAG_INFO tag;
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ struct windows_stat {
|
|||
uint32_t st_dev;
|
||||
int st_hidden;
|
||||
const char* st_desc;
|
||||
int st_sync; /**< If the information are in sync with the filesystem. */
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -162,7 +163,7 @@ int windows_fstat(int fd, struct windows_stat* st);
|
|||
* 'However, the directory entry size and attribute information is updated only'
|
||||
* 'for the link through which the change was made.'
|
||||
*
|
||||
* Use lstat_ex() to override these limitations.
|
||||
* Use lstat_sync() to override these limitations.
|
||||
*/
|
||||
int windows_lstat(const char* file, struct windows_stat* st);
|
||||
|
||||
|
|
@ -192,8 +193,8 @@ int windows_rmdir(const char* file);
|
|||
*
|
||||
* Note that instead lstat() works for all the files.
|
||||
*/
|
||||
#define HAVE_LSTAT_EX 1
|
||||
int lstat_ex(const char* file, struct windows_stat* st);
|
||||
#define HAVE_LSTAT_SYNC 1
|
||||
int lstat_sync(const char* file, struct windows_stat* st);
|
||||
|
||||
/**
|
||||
* Like the C ftruncate().
|
||||
|
|
@ -282,7 +283,7 @@ int windows_closedir(windows_dir* dirstream);
|
|||
* Specifically this happens if we are using FindFirst/FindNext to enumerate
|
||||
* the directory.
|
||||
*
|
||||
* In such cases, call lstat_ex() to fill the missing fields.
|
||||
* In such cases, call lstat_sync() to fill the missing fields.
|
||||
*/
|
||||
void windows_dirent_lstat(const struct windows_dirent* dd, struct windows_stat* st);
|
||||
|
||||
|
|
|
|||
109
cmdline/scan.c
109
cmdline/scan.c
|
|
@ -312,6 +312,63 @@ static void scan_file_delayed_insert(struct snapraid_scan* scan, struct snapraid
|
|||
tommy_list_insert_tail(&scan->file_insert_list, &file->nodelist, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the file info.
|
||||
*
|
||||
* This is needed by Windows as the normal way to list directories may report not
|
||||
* updated info. Only the GetFileInformationByHandle() func, called file-by-file,
|
||||
* relly ensures to return synced info.
|
||||
*/
|
||||
static void scan_file_refresh(struct snapraid_scan* scan, const char* sub, struct stat* st)
|
||||
{
|
||||
#if HAVE_LSTAT_SYNC
|
||||
struct snapraid_disk* disk = scan->disk;
|
||||
|
||||
/* if the st_sync is not set, ensure to get synced info */
|
||||
if (st->st_sync == 0) {
|
||||
char path_next[PATH_MAX];
|
||||
struct stat synced_st;
|
||||
|
||||
pathprint(path_next, sizeof(path_next), "%s%s", disk->dir, sub);
|
||||
|
||||
if (lstat_sync(path_next, &synced_st) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Error in stat file '%s'. %s.\n", path_next, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
if (st->st_mtime != synced_st.st_mtime
|
||||
|| st->st_mtimensec != synced_st.st_mtimensec
|
||||
) {
|
||||
fprintf(stderr, "WARNING! Detected uncached time change for file '%s'\n", sub);
|
||||
fprintf(stderr, "It's better if you run SnapRAID without other processes running.\n");
|
||||
st->st_mtime = synced_st.st_mtime;
|
||||
st->st_mtimensec = synced_st.st_mtimensec;
|
||||
}
|
||||
|
||||
if (st->st_size != synced_st.st_size) {
|
||||
fprintf(stderr, "WARNING! Detected uncached size change for file '%s'\n", sub);
|
||||
fprintf(stderr, "It's better if you run SnapRAID without other processes running.\n");
|
||||
st->st_size = synced_st.st_size;
|
||||
}
|
||||
|
||||
if (st->st_ino != synced_st.st_ino) {
|
||||
fprintf(stderr, "DANGER! Detected uncached inode change for file '%s'\n", sub);
|
||||
fprintf(stderr, "Please ensure to run SnapRAID without other processes running.\n");
|
||||
/* at this point, it's too late to change inode */
|
||||
/* and having inconsistent inodes may result to internal failures */
|
||||
/* so, it's better to abort */
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)scan;
|
||||
(void)sub;
|
||||
(void)st;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps the file as it's (or with only a name/inode modification).
|
||||
*
|
||||
|
|
@ -464,7 +521,7 @@ static void scan_file_insert(struct snapraid_scan* scan, struct snapraid_file* f
|
|||
/**
|
||||
* Processes a file.
|
||||
*/
|
||||
static void scan_file(struct snapraid_scan* scan, int output, const char* sub, const struct stat* st, uint64_t physical)
|
||||
static void scan_file(struct snapraid_scan* scan, int output, const char* sub, struct stat* st, uint64_t physical)
|
||||
{
|
||||
struct snapraid_state* state = scan->state;
|
||||
struct snapraid_disk* disk = scan->disk;
|
||||
|
|
@ -472,6 +529,7 @@ static void scan_file(struct snapraid_scan* scan, int output, const char* sub, c
|
|||
int add_insert;
|
||||
int add_change;
|
||||
tommy_node* i;
|
||||
int is_original_file_size_different_than_zero;
|
||||
|
||||
/*
|
||||
* If the disk has persistent inodes and UUID, try a search on the past inodes,
|
||||
|
|
@ -619,6 +677,7 @@ static void scan_file(struct snapraid_scan* scan, int output, const char* sub, c
|
|||
|
||||
add_insert = 0;
|
||||
add_change = 0;
|
||||
is_original_file_size_different_than_zero = 0;
|
||||
|
||||
/* then try finding it by name */
|
||||
file = tommy_hashdyn_search(&disk->pathset, file_path_compare_to_arg, sub, file_path_hash(sub));
|
||||
|
|
@ -720,19 +779,8 @@ static void scan_file(struct snapraid_scan* scan, int output, const char* sub, c
|
|||
|
||||
/* here if the file is changed but with the correct name */
|
||||
|
||||
/* do a safety check to ensure that the common ext4 case of zeroing */
|
||||
/* the size of a file after a crash doesn't propagate to the backup */
|
||||
if (file->size != 0 && st->st_size == 0) {
|
||||
if (!state->opt.force_zero) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "The file '%s%s' has unexpected zero size! If this an expected state\n", disk->dir, sub);
|
||||
fprintf(stderr, "you can '%s' anyway usinge 'snapraid --force-zero %s'\n", state->command, state->command);
|
||||
fprintf(stderr, "Instead, it's possible that after a kernel crash this file was lost,\n");
|
||||
fprintf(stderr, "and you can use 'snapraid --filter %s fix' to recover it.\n", sub);
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
}
|
||||
/* keep track if the original file was not of zero size */
|
||||
is_original_file_size_different_than_zero = file->size != 0;
|
||||
|
||||
/* it has the same name, so it's an update */
|
||||
add_change = 1;
|
||||
|
|
@ -742,10 +790,10 @@ static void scan_file(struct snapraid_scan* scan, int output, const char* sub, c
|
|||
printf("update %s%s\n", disk->dir, file->sub);
|
||||
}
|
||||
|
||||
/* remove it */
|
||||
/* remove it, and continue to insert it again */
|
||||
scan_file_remove(scan, file);
|
||||
|
||||
/* and continue to reinsert it */
|
||||
/* and continue to reinsert it again */
|
||||
} else {
|
||||
/* if the name doesn't exist, it's a new file */
|
||||
add_insert = 1;
|
||||
|
|
@ -758,6 +806,25 @@ static void scan_file(struct snapraid_scan* scan, int output, const char* sub, c
|
|||
/* and continue to insert it */
|
||||
}
|
||||
|
||||
/* refresh the info, to ensure that they are synced, */
|
||||
/* not that we refresh only the info of the new or modified files */
|
||||
/* because this is slow operation */
|
||||
scan_file_refresh(scan, sub, st);
|
||||
|
||||
/* do a safety check to ensure that the common ext4 case of zeroing */
|
||||
/* the size of a file after a crash doesn't propagate to the backup */
|
||||
if (is_original_file_size_different_than_zero && st->st_size == 0) {
|
||||
if (!state->opt.force_zero) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "The file '%s%s' has unexpected zero size! If this an expected state\n", disk->dir, sub);
|
||||
fprintf(stderr, "you can '%s' anyway usinge 'snapraid --force-zero %s'\n", state->command, state->command);
|
||||
fprintf(stderr, "Instead, it's possible that after a kernel crash this file was lost,\n");
|
||||
fprintf(stderr, "and you can use 'snapraid --filter %s fix' to recover it.\n", sub);
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
}
|
||||
|
||||
/* insert it */
|
||||
file = file_alloc(state->block_size, sub, st->st_size, st->st_mtime, STAT_NSEC(st), st->st_ino, physical);
|
||||
|
||||
|
|
@ -1110,8 +1177,8 @@ static int scan_dir(struct snapraid_scan* scan, int output, const char* dir, con
|
|||
|
||||
#if HAVE_STRUCT_DIRENT_D_STAT
|
||||
/* if the st_mode field is missing, takes care to fill it using normal lstat() */
|
||||
/* at now this can happen only in Windows, but we cannot call here lstat_ex(), */
|
||||
/* because we don't know what kind of file is it, and lstat_ex() doesn't always work */
|
||||
/* at now this can happen only in Windows, but we cannot call here lstat_sync(), */
|
||||
/* because we don't know what kind of file is it, and lstat_sync() doesn't always work */
|
||||
if (st->st_mode == 0) {
|
||||
if (lstat(path_next, st) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -1139,13 +1206,13 @@ static int scan_dir(struct snapraid_scan* scan, int output, const char* dir, con
|
|||
if (!st)
|
||||
st = DSTAT(path_next, dd, &st_buf);
|
||||
|
||||
#if HAVE_LSTAT_EX
|
||||
#if HAVE_LSTAT_SYNC
|
||||
/* if the st_ino field is missing, takes care to fill it using the extended lstat() */
|
||||
/* this can happen only in Windows */
|
||||
if (st->st_ino == 0) {
|
||||
if (lstat_ex(path_next, st) != 0) {
|
||||
if (lstat_sync(path_next, st) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
|
||||
fprintf(stderr, "Error in stat file '%s'. %s.\n", path_next, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue