diff --git a/TODO b/TODO index 06851850..aeddce8b 100644 --- a/TODO +++ b/TODO @@ -3,6 +3,8 @@ SnapRAID TODO This is the list of TODO items for SnapRAID. ++test with restore disk cp -a before and after rm + - v4.x * Enable storing of creation time NTFS, crtime/birth time EXT4. diff --git a/elem.c b/elem.c index 5fed17e3..75834998 100644 --- a/elem.c +++ b/elem.c @@ -523,6 +523,7 @@ 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->mapping = -1; tommy_list_init(&disk->filelist); tommy_list_init(&disk->deletedlist); diff --git a/elem.h b/elem.h index 67d805a0..1b045763 100644 --- a/elem.h +++ b/elem.h @@ -273,6 +273,7 @@ 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 mapping; /**< Index in the mapping vector. Used only as buffer when writing the content file. */ /**< diff --git a/mingw.c b/mingw.c index 7f21c80e..7057ee11 100644 --- a/mingw.c +++ b/mingw.c @@ -1148,11 +1148,14 @@ int filephy(const char* file, struct stat* st, uint64_t* physical) return 0; } -int fstype(const char* path) +int fsinfo(const char* path, int* has_persistent_inode) { (void)path; - return FSTYPE_UNKNOWN; + /* all FAT/exFAT/NTFS when managed from Windows have persistent inodes */ + *has_persistent_inode = 1; + + return 0; } #undef strerror diff --git a/portable.h b/portable.h index c9eab0b4..b5e96b14 100644 --- a/portable.h +++ b/portable.h @@ -181,15 +181,11 @@ int devuuid(uint64_t device, char* uuid, size_t size); */ int filephy(const char* path, struct stat* st, uint64_t* physical); -/* Types of filesystem */ -#define FSTYPE_UNKNOWN 0 -#define FSTYPE_FUSE 1 - /** - * Get the filesystem type. - * Returns -1 on error or one of the FSTYPE define. + * Checks if the underline filesystem support persistent inodes. + * Returns -1 on error, 0 on success. */ -int fstype(const char* path); +int fsinfo(const char* path, int* has_persistent_inode); /** * Initializes the system. diff --git a/scan.c b/scan.c index 424bd19f..34c51eb2 100644 --- a/scan.c +++ b/scan.c @@ -277,10 +277,31 @@ 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 = st->st_ino; - /* first try finding by inode */ - file = tommy_hashdyn_search(&disk->inodeset, file_inode_compare_to_arg, &inode, file_inode_hash(inode)); + file = 0; + + /* + * If the disk has persistent inodes, try a search by inode. + * + * For persistent inodes we mean inodes that keep their values when the filesystem + * is unmounted and remounted. This don't always happen. + * + * Cases found are: + * - Linux FUSE with exFAT driver from https://code.google.com/p/exfat/. + * Inodes are reassigned at every mount restarting from 1 and incrementing. + * As worse, the exFAT support in FUSE doesn't use subsecond precision in timestamps + * making inode collision more easy (exFAT by design supports 10ms precision). + * - Linux VFAT kernel (3.2) driver. Inodes are fully reassigned at every mount. + * + * In such cases, to avoid possible random collisions, it's better to just avoid + * to use inodes at all. + */ + if (!disk->has_not_persistent_inodes) { + /* first try finding by inode */ + uint64_t inode = st->st_ino; + file = tommy_hashdyn_search(&disk->inodeset, file_inode_compare_to_arg, &inode, file_inode_hash(inode)); + } + if (file) { /* check if the file is not changed */ if (file->size == st->st_size @@ -793,6 +814,8 @@ void state_scan(struct snapraid_state* state, int output) struct snapraid_disk* disk = i->data; struct snapraid_scan* scan; tommy_node* node; + int ret; + int has_persistent_inode; scan = malloc_nofail(sizeof(struct snapraid_scan)); scan->count_equal = 0; @@ -809,6 +832,18 @@ void state_scan(struct snapraid_state* state, int output) printf("Scanning disk %s...\n", disk->name); + /* check if the disk supports persistent inodes */ + ret = fsinfo(disk->dir, &has_persistent_inode); + if (ret < 0) { + fprintf(stderr, "Error accessing disk '%s' to get filesystem info. %s.\n", disk->dir, strerror(errno)); + exit(EXIT_FAILURE); + } + /* mark the disk if required */ + if (!has_persistent_inode) { + fprintf(stderr, "WARNING! Inodes use disabled for disk '%s' because they are not persistent.\n", disk->dir); + disk->has_not_persistent_inodes = 1; + } + scan_dir(scan, state, output, disk, disk->dir, ""); /* check for removed files */ diff --git a/state.c b/state.c index 3a0706e6..2b5f0408 100644 --- a/state.c +++ b/state.c @@ -113,26 +113,6 @@ static void state_config_check(struct snapraid_state* state, const char* path) #endif if (!state->opt.force_device) { - int ret; - - ret = fstype(disk->dir); - if (ret < 0) { - fprintf(stderr, "Error accessing disk '%s' to get the filesystem type. %s.\n", disk->dir, strerror(errno)); - exit(EXIT_FAILURE); - } - if (ret == FSTYPE_FUSE) { - fprintf(stderr, "Disk '%s' has a filesystem managed by FUSE.\n", disk->dir); - fprintf(stderr, "FUSE doesn't support persistent inodes, and it cannot be\n"); - fprintf(stderr, "used safely with SnapRAID.\n"); - - /* in "fix" we allow to continue anyway */ - if (strcmp(state->command, "fix") == 0) { - fprintf(stderr, "You can '%s' anyway, using 'snapraid --force-device %s'.\n", state->command, state->command); - } - - exit(EXIT_FAILURE); - } - for(j=i->next;j!=0;j=j->next) { struct snapraid_disk* other = j->data; if (disk->device == other->device) { diff --git a/unix.c b/unix.c index 21302895..beb2a401 100644 --- a/unix.c +++ b/unix.c @@ -168,7 +168,7 @@ int filephy(const char* path, struct stat* st, uint64_t* physical) return 0; } -int fstype(const char* path) +int fsinfo(const char* path, int* has_persistent_inode) { /* statfs() is really not portable, and to limit possible incompatibility we limit it to Linux */ /* for example, Solaris doesn't have the f_type field, BSD has it as "short" */ @@ -178,14 +178,26 @@ int fstype(const char* path) if (statfs(path, &st) != 0) return -1; + /* to get the fs type check "man stat" or "stat -f -t FILE" */ + switch (st.f_type) { - case 0x65735546 : return FSTYPE_FUSE; + case 0x65735546 : /* FUSE, "fuseblk" in the stat command */ + case 0x4d44 : /* VFAT, "msdos" in the stat command */ + *has_persistent_inode = 0; + break; + default: + /* by default assume yes */ + *has_persistent_inode = 1; + break; } #else (void)path; + + /* by default assume yes */ + *has_persistent_inode = 1; #endif - return FSTYPE_UNKNOWN; + return 0; } void os_init(void)