diff --git a/cmdline/elem.c b/cmdline/elem.c index 6a0ed1d1..fba4b4d8 100644 --- a/cmdline/elem.c +++ b/cmdline/elem.c @@ -349,31 +349,54 @@ int filter_correctness(int filter_error, tommy_arrayblkof* infoarr, struct snapr return 1; } -int filter_content(tommy_list* contentlist, const char* path) +int filter_content(tommy_list* contentlist, const char* mount_point, size_t mount_point_len, const char* sub, size_t sub_len, const char* name) { tommy_node* i; + size_t name_len = strlen(name); + for (i = tommy_list_head(contentlist); i != 0; i = i->next) { struct snapraid_content* content = i->data; - char tmp[PATH_MAX]; - if (pathcmp(content->content, path) == 0) + /* if the mount point doesn't match, it's a different disk */ + if (pathncmp(content->content, mount_point, mount_point_len) != 0) + continue; + + /* if the sub dir doesn't match, it's a different dir */ + if (pathncmp(content->content + mount_point_len, sub, sub_len) != 0) + continue; + + /* if the name doesn't match, it's a different name */ + if (pathncmp(content->content + mount_point_len + sub_len, name, name_len) != 0) + continue; + + /* remaining part */ + const char* postfix = content->content + mount_point_len + sub_len + name_len; + + /* if it's an exact match */ + if (*postfix == 0) return -1; /* exclude also the ".tmp" copy used to save it */ - pathprint(tmp, sizeof(tmp), "%s.tmp", content->content); - if (pathcmp(tmp, path) == 0) + if (pathcmp(postfix, ".tmp") == 0) return -1; /* exclude also the ".lock" file */ - pathprint(tmp, sizeof(tmp), "%s.lock", content->content); - if (pathcmp(tmp, path) == 0) + if (pathcmp(postfix, ".lock") == 0) return -1; } return 0; } +int filter_snapshot(const char* sub, const char* name) +{ + if (*sub == 0 && pathcmp(name, SNAPSHOT_CONTAINER) == 0) + return -1; + + return 0; +} + 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; diff --git a/cmdline/elem.h b/cmdline/elem.h index 3257ee0b..2e7fbb6c 100644 --- a/cmdline/elem.h +++ b/cmdline/elem.h @@ -632,7 +632,13 @@ int filter_emptydir(tommy_list* filterlist, struct snapraid_filter** reason, con * Filter a path if it's a content file. * Return !=0 if should be excluded. */ -int filter_content(tommy_list* contentlist, const char* path); +int filter_content(tommy_list* contentlist, const char* mount_point, size_t mount_point_len, const char* sub, size_t sub_len, const char* name); + +/** + * Filter a path that is in a snapshot + * Return !=0 if should be excluded. + */ +int filter_snapshot(const char* sub, const char* name); /** * Check if the specified hash is invalid. diff --git a/cmdline/portable.h b/cmdline/portable.h index 7753f495..a1ba71b0 100644 --- a/cmdline/portable.h +++ b/cmdline/portable.h @@ -385,7 +385,7 @@ int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_har #define SNAPSHOT_PENDING "pending" #define SNAPSHOT_STABLE "stable" -/** +/* * Snapshots context */ struct fssnapshot_struct { diff --git a/cmdline/scan.c b/cmdline/scan.c index 78e96001..5af145be 100644 --- a/cmdline/scan.c +++ b/cmdline/scan.c @@ -1329,9 +1329,11 @@ static int scan_sub(struct snapraid_scan* scan, int level, int is_diff, char* pa DIR* d; tommy_list list; tommy_node* node; + size_t mount_point_len; size_t path_len; size_t sub_len; + mount_point_len = strlen(disk->mount_point); path_len = strlen(path_next); sub_len = strlen(sub_next); @@ -1404,11 +1406,17 @@ static int scan_sub(struct snapraid_scan* scan, int level, int is_diff, char* pa } /* exclude content files even before calling lstat() */ - if (filter_content(&state->contentlist, path_next) != 0) { + if (filter_content(&state->contentlist, disk->mount_point, mount_point_len, sub_next, sub_len, name) != 0) { msg_verbose("Excluding content '%s'\n", path_next); continue; } + /* exclude snapshot container even before calling lstat() */ + if (filter_snapshot(sub_next, name) != 0) { + msg_verbose("Excluding snapshots directory '%s'\n", path_next); + continue; + } + name_len = strlen(dd->d_name); entry = malloc_nofail(sizeof(struct dirent_sorted) + name_len + 1); diff --git a/cmdline/search.c b/cmdline/search.c index 48aa5770..49a4c80e 100644 --- a/cmdline/search.c +++ b/cmdline/search.c @@ -138,6 +138,9 @@ static void search_dir(struct snapraid_state* state, struct snapraid_disk* disk, { DIR* d; + size_t mount_point_len = disk != 0 ? strlen(disk->mount_point) : 0; + size_t sub_len = strlen(sub); + d = opendir(dir); if (!d) { /* LCOV_EXCL_START */ @@ -183,11 +186,17 @@ static void search_dir(struct snapraid_state* state, struct snapraid_disk* disk, } /* exclude content files even before calling lstat() */ - if (disk != 0 && filter_content(&state->contentlist, path_next) != 0) { + if (disk != 0 && filter_content(&state->contentlist, disk->mount_point, mount_point_len, sub, sub_len, name) != 0) { msg_verbose("Excluding content '%s'\n", path_next); continue; } + /* exclude snapshot container even before calling lstat() */ + if (disk != 0 && filter_snapshot(sub, name) != 0) { + msg_verbose("Excluding snapshots directory '%s'\n", path_next); + continue; + } + #if HAVE_STRUCT_DIRENT_D_STAT /* convert dirent to lstat result */ dirent_lstat(dd, &st); diff --git a/cmdline/support.c b/cmdline/support.c index 96cc4451..72fb94e9 100644 --- a/cmdline/support.c +++ b/cmdline/support.c @@ -1193,20 +1193,61 @@ void pathup(char* dst) int pathcmp(const char* a, const char* b) { #ifdef _WIN32 - char ai[PATH_MAX]; - char bi[PATH_MAX]; + while (1) { + char ca = *a++; + char cb = *b++; - /* import to convert \ to / */ - pathimport(ai, sizeof(ai), a); - pathimport(bi, sizeof(bi), b); + if (ca == '/') + ca = '\\'; - /* case insensitive compare in Windows */ - return stricmp(ai, bi); + if (cb == '/') + cb = '\\'; + + int la = tolower((unsigned char)ca); + int lb = tolower((unsigned char)cb); + + if (la != lb) + return la - lb; + + if (la == 0) + return 0; + } #else return strcmp(a, b); #endif } +int pathncmp(const char* a, const char* b, size_t n) +{ +#ifdef _WIN32 + while (n > 0) { + char ca = *a++; + char cb = *b++; + + if (ca == '/') + ca = '\\'; + + if (cb == '/') + cb = '\\'; + + int la = tolower((unsigned char)ca); + int lb = tolower((unsigned char)cb); + + if (la != lb) + return la - lb; + + if (la == '\0') + return 0; + + --n; + } + + return 0; +#else + return strncmp(a, b, n); +#endif +} + int path_is_root_of(const char* root, const char* path) { while (*root && *path) { diff --git a/cmdline/support.h b/cmdline/support.h index 31b1b754..1a0156f8 100644 --- a/cmdline/support.h +++ b/cmdline/support.h @@ -373,6 +373,12 @@ void pathup(char* dst); */ int pathcmp(const char* a, const char* b); +/** + * Compare two paths up to n chars. + * In Windows it's case insensitive and assumes \ equal at /. + */ +int pathncmp(const char* a, const char* b, size_t n); + /** * Check if the root is matching the start of the specified path. * diff --git a/cmdline/unix.c b/cmdline/unix.c index adecc2c7..af59c767 100644 --- a/cmdline/unix.c +++ b/cmdline/unix.c @@ -2490,8 +2490,6 @@ int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_har return 0; } -#define SNAPSHOT_CONTAINER ".snapraid" - #if HAVE_LINUX_DEVICE static int fssnapshot_inode(const char* path, uint32_t magic, uint64_t root_inode, struct fssnapshot_struct* fss) { diff --git a/snapraid.conf.example b/snapraid.conf.example index 3929f964..91db9a68 100644 --- a/snapraid.conf.example +++ b/snapraid.conf.example @@ -84,7 +84,6 @@ extra boot / exclude *.unrecoverable exclude /tmp/ exclude /lost+found/ -exclude .snapraid/ # Defines the block size in kibi bytes (1024 bytes) (uncomment to enable). # WARNING: Changing this value is for experts only! diff --git a/test/test-snap.conf b/test/test-snap.conf index 268c4aa8..e8df0812 100644 --- a/test/test-snap.conf +++ b/test/test-snap.conf @@ -6,4 +6,3 @@ disk snap1 bench/snap1/ disk snap2 bench/snap2/ disk snap3 bench/snap3/ snapshot -exclude .snapraid/