From 7be2420cf3ff5436911eb2c170b4af0336672c2c Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Sat, 14 Dec 2013 17:54:04 +0100 Subject: [PATCH] Avoids to use inodes in disk with UUID changed. --- HISTORY | 4 ++++ elem.c | 5 +++-- elem.h | 5 +++-- scan.c | 65 ++++++++++++++++++++++++++++++++++++++++----------------- state.c | 8 +++++-- 5 files changed, 62 insertions(+), 25 deletions(-) diff --git a/HISTORY b/HISTORY index dca7917b..cb3989a3 100644 --- a/HISTORY +++ b/HISTORY @@ -3,6 +3,10 @@ SnapRAID HISTORY 5.2 2013/12 (BETA) ================== + * If a disk changes UUID, automatically disable the inode + recognition, because this is likely a new filesystem with + all the inodes reassigned, and we don't want to risk a false + positive when searching for inode. * Does not count empty dirs as files in the diff counters. 5.1 2013/12 diff --git a/elem.c b/elem.c index 324e6c41..52f9fb52 100644 --- a/elem.c +++ b/elem.c @@ -530,8 +530,9 @@ struct snapraid_disk* disk_alloc(const char* name, const char* dir, uint64_t dev disk->device = dev; disk->first_free_block = 0; - disk->has_not_persistent_inodes = 0; - disk->has_not_reliable_physical = 0; + disk->has_volatile_inodes = 0; + disk->has_unreliable_physical = 0; + disk->has_different_uuid = 0; disk->mapping = -1; tommy_list_init(&disk->filelist); tommy_list_init(&disk->deletedlist); diff --git a/elem.h b/elem.h index f0fd490c..427b845e 100644 --- a/elem.h +++ b/elem.h @@ -283,8 +283,9 @@ struct snapraid_disk { char dir[PATH_MAX]; /**< Mount point of the disk. It always terminates with /. */ uint64_t device; /**< Device identifier. */ block_off_t first_free_block; /**< First free searching block. */ - int has_not_persistent_inodes; /**< If the underline filesystem has dynamic inodes. */ - int has_not_reliable_physical; /**< If the physical offset of files has duplicates. */ + int has_volatile_inodes; /**< If the underline filesystem has not persistent inodes. */ + int has_unreliable_physical; /**< If the physical offset of files has duplicates. */ + int has_different_uuid; /**< If the disk has a different UUID, meaning that it is not the same filesystem. */ int mapping; /**< Index in the mapping vector. Used only as buffer when writing the content file. */ /**< diff --git a/scan.c b/scan.c index 39ae028c..aca16835 100644 --- a/scan.c +++ b/scan.c @@ -290,12 +290,9 @@ static void scan_file_insert(struct snapraid_state* state, struct snapraid_disk* static void scan_file(struct snapraid_scan* scan, struct snapraid_state* state, int output, struct snapraid_disk* disk, const char* sub, const struct stat* st, uint64_t physical) { struct snapraid_file* file; - uint64_t inode; - - file = 0; /* - * If the disk has persistent inodes, try a search by inode, + * If the disk has persistent inodes and UUID, try a search on the past inodes, * to detect moved files. * * For persistent inodes we mean inodes that keep their values when the filesystem @@ -311,15 +308,23 @@ static void scan_file(struct snapraid_scan* scan, struct snapraid_state* state, * In such cases, to avoid possible random collisions, it's better to disable the moved * file recognition. * - * We do this implicitely removing all the inode before searching for files. - * This ensures that no file is found with an old inode, but at the same time, - * it allows to find new files with the same inode, and to identify them as hardlinks. + * For persistent UUID we mean that it has the same UUID as before. + * Otherwise, if the UUID is changed, likely it's a new recreated filesystem, + * and then the inode have no meaning. + * + * Note that to disable the search by past inode, we do this implicitely + * removing all the past inode before searching for files. + * This ensures that no file is found with a past inode, but at the same time, + * it allows to find new files with the same inode, to identify them as hardlinks. */ + int has_past_inodes = !disk->has_volatile_inodes && !disk->has_different_uuid; - inode = st->st_ino; + /* always search with the new inode, in the all new inodes found until now, */ + /* with the eventual presence of also the past inodes */ + uint64_t inode = st->st_ino; file = tommy_hashdyn_search(&disk->inodeset, file_inode_compare_to_arg, &inode, file_inode_hash(inode)); - /* identify moved files searching by inode */ + /* identify moved files with past inodes and hardlinks with the new inodes */ if (file) { /* check if the file is not changed */ if (file->size == st->st_size @@ -473,8 +478,8 @@ static void scan_file(struct snapraid_scan* scan, struct snapraid_state* state, state->need_write = 1; } - /* if the disk support persistent inodes */ - if (!disk->has_not_persistent_inodes) { + /* if when processing the disk we used the past inodes values */ + if (has_past_inodes) { /* if persistent inodes are supported, we are sure that the inode number */ /* is now different, because otherwise the file would have been found */ /* when searching by inode. */ @@ -791,7 +796,7 @@ static int scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, in #if HAVE_STRUCT_DIRENT_D_INO /* if inodes are persistent */ - if (!disk->has_not_persistent_inodes) { + if (!disk->has_volatile_inodes) { /* sort the list of dir entries by inodes */ tommy_list_sort(&list, dd_ino_compare); } @@ -999,9 +1004,11 @@ void state_scan(struct snapraid_state* state, int output) exit(EXIT_FAILURE); } if (!has_persistent_inode) { - /* mask the disk */ - disk->has_not_persistent_inodes = 1; + disk->has_volatile_inodes = 1; + } + /* if inodes or UUID are not persistent */ + if (disk->has_volatile_inodes || disk->has_different_uuid) { /* removes all the inodes from the inode collection */ /* if they are not persistent, all of them could be changed now */ /* and we don't want to find false matching ones */ @@ -1138,7 +1145,7 @@ void state_scan(struct snapraid_state* state, int output) /* mark the disk without reliable physical offset if it has duplicates */ /* here it should never happen because we already sorted out hardlinks */ if (state->opt.force_order == SORT_PHYSICAL && phy_dup > 0) { - disk->has_not_reliable_physical = 1; + disk->has_unreliable_physical = 1; } /* insert all the new links */ @@ -1199,7 +1206,7 @@ void state_scan(struct snapraid_state* state, int output) for(i=state->disklist;i!=0;i=i->next) { struct snapraid_disk* disk = i->data; - if (disk->has_not_reliable_physical) { + if (disk->has_unreliable_physical) { if (!done) { done = 1; fprintf(stderr, "WARNING! Physical offsets not supported for disk '%s'", disk->name); @@ -1213,16 +1220,36 @@ void state_scan(struct snapraid_state* state, int output) } } - /* Check for disks without persisten inodes */ + /* Check for disks without persistent inodes */ { int done = 0; for(i=state->disklist;i!=0;i=i->next) { struct snapraid_disk* disk = i->data; - if (disk->has_not_persistent_inodes) { + if (disk->has_volatile_inodes) { if (!done) { done = 1; - fprintf(stderr, "WARNING! Inodes are not persistent for disk '%s'", disk->name); + fprintf(stderr, "WARNING! Inodes are not persistent for disks: '%s'", disk->name); + } else { + fprintf(stderr, ", '%s", disk->name); + } + } + } + if (done) { + fprintf(stderr, ". Move operations won't be optimized.\n"); + } + } + + /* Check for disks with changed UUID */ + { + int done = 0; + for(i=state->disklist;i!=0;i=i->next) { + struct snapraid_disk* disk = i->data; + + if (disk->has_different_uuid) { + if (!done) { + done = 1; + fprintf(stderr, "WARNING! UUID is changed for disks: '%s'", disk->name); } else { fprintf(stderr, ", '%s", disk->name); } diff --git a/state.c b/state.c index 8fb07828..c0f82365 100644 --- a/state.c +++ b/state.c @@ -801,7 +801,7 @@ static void state_map(struct snapraid_state* state) tommy_list_insert_tail(&state->maplist, &map->node, map); } - /* without configuration don't check for number of data disks */ + /* without configuration don't check for number of data disks or uuid changes */ if (state->no_conf) return; @@ -829,12 +829,16 @@ static void state_map(struct snapraid_state* state) ret = devuuid(disk->device, uuid, sizeof(uuid)); if (ret != 0) { - /* uuid not available, just ignore */ + /* uuid not available, just ignore but marks the disk with not persistent UUID */ + disk->has_different_uuid = 1; continue; } /* if the uuid is changed */ if (strcmp(uuid, map->uuid) != 0) { + /* mark the disk as with an UUID change */ + disk->has_different_uuid = 1; + /* if the previous uuid is available */ if (map->uuid[0] != 0) { /* count the number of uuid change */