diff --git a/HISTORY b/HISTORY index 54df803f..fd36a26e 100644 --- a/HISTORY +++ b/HISTORY @@ -1,6 +1,10 @@ SnapRAID HISTORY ================ +2.2 2013/2 +========== + * Syncs and fixes also empty directories. + 2.1 2013/1 ========== * Checks for wrong empty fields in the configuration file. diff --git a/Makefile.am b/Makefile.am index df12a7c9..5c8e0628 100644 --- a/Makefile.am +++ b/Makefile.am @@ -105,8 +105,13 @@ check-local: $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid5.conf -a check $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf check $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -c $(srcdir)/test-raid6.conf -a check - echo --- Create some more files, sync raid5 and check + echo --- Create some more files and empty directories, sync raid5 and check $(TESTENV) ./mktest$(EXEEXT) generate 2 3 $(CHECKCOUNT) $(CHECKSIZE) + mkdir test/disk1/empty1 + mkdir test/disk2/empty2 + mkdir test/disk3/empty3a + mkdir test/disk3/empty3a/emptyinner + mkdir test/disk3/empty3b $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -v -c $(srcdir)/test-raid5.conf diff $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -v -c $(srcdir)/test-raid5.conf dup $(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-skip-self -v -c $(srcdir)/test-raid5.conf sync diff --git a/TODO b/TODO index 0295ad9f..1dab1c50 100644 --- a/TODO +++ b/TODO @@ -5,9 +5,10 @@ This is the list of TODO items for SnapRAID. - v2.2 -* Allow to specify more than one disk directories to cover the case of multi partitions. - -* Keep track of empty directories and restore them. +* Implement an "undelete" command to recover missing files, without the +integrity check of the existing ones. +It should restore only missing files, without recovering modified ones +or check validity of data of existing ones. * If a content file is missing, sync should rewrite it, even if no change was done at the content. @@ -18,17 +19,13 @@ This will also help when running with the parity filesystem unmounted. * Filtered out files should be opened in read-only mode. -* Implement an "undelete" command to recover missing files, without the -integrity check of the existing ones. -It should restore only missing files, without recovering modified ones -or check validity of data of existing ones. - * Adds a log option, to save log files with the date in the name. This could help to keep track of what happens during automated operations. We must keep the log to stdout/err, and only save a copy to the log file. * In fix an existing symlink with the same name of a file to be recovered may stop the process making the create() operation to fail. +The same for directories, when recreating the directory tree. * If a directory exists with the same name of a parity/content file be more explicative on the error message. See: https://sourceforge.net/projects/snapraid/forums/forum/1677233/topic/4861034 @@ -105,6 +102,10 @@ Rejected TODO This is a list of rejected TODO items. +* Allow to specify more than one disk directories to cover the case of multi partitions. +- Different partitions have duplicate inode. The only way to support this is to +add also a kind of device_id, increasing the memory required. + * Recognizes that a file is moved from one disk to another, and if the parity data doesn't overlap, do not recompute it. - It's going to work only in RAID5 mode and only in special cases. diff --git a/check.c b/check.c index 06531c4c..1a3f6bfd 100644 --- a/check.c +++ b/check.c @@ -1118,6 +1118,78 @@ static int state_check_process(struct snapraid_state* state, int check, int fix, } } + /* check all dirs */ + for(i=0;idirlist; + while (node) { + char path[PATH_MAX]; + struct stat st; + struct snapraid_dir* dir; + int failed = 0; + + dir = node->data; + node = node->next; /* next node */ + + /* if excluded continue to the next one */ + if (dir_flag_has(dir, FILE_IS_EXCLUDED)) { + continue; + } + + /* stat the dir */ + pathprint(path, sizeof(path), "%s%s", disk->dir, dir->sub); + ret = stat(path, &st); + if (ret == -1) { + failed = 1; + + fprintf(stderr, "Error stating dir '%s'. %s.\n", path, strerror(errno)); + fprintf(stderr, "dir_error:%s:%s: Dir stat error\n", disk->name, dir->sub); + ++error; + } else if (!S_ISDIR(st.st_mode)) { + failed = 1; + + fprintf(stderr, "dir_error:%s:%s: Dir error for not directory\n", disk->name, dir->sub); + ++error; + } + + if (fix && failed) { + /* create the ancestor directories */ + ret = handle_ancestor(path); + if (ret != 0) { + fprintf(stderr, "WARNING! Without a working data disk, it isn't possible to fix errors on it.\n"); + printf("Stopping\n"); + ++unrecoverable_error; + goto bail; + } + + /* create it */ + ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + if (ret != 0) { + fprintf(stderr, "Error creating dir '%s'. %s.\n", path, strerror(errno)); + if (errno == EACCES) { + fprintf(stderr, "WARNING! Please give write permission to the dir.\n"); + } else { + /* we do not use DANGER because it could be ENOSPC which is not always correctly reported */ + fprintf(stderr, "WARNING! Without a working data disk, it isn't possible to fix errors on it.\n"); + } + printf("Stopping\n"); + ++unrecoverable_error; + goto bail; + } + + fprintf(stderr, "dir_fixed:%s:%s: Fixed dir error\n", disk->name, dir->sub); + ++recovered_error; + } + } + } + state_progress_end(state, countpos, countmax, countsize); bail: diff --git a/elem.c b/elem.c index b7d91b83..d659a44b 100644 --- a/elem.c +++ b/elem.c @@ -373,6 +373,29 @@ int link_name_compare(const void* void_arg, const void* void_data) return strcmp(arg, link->sub); } +struct snapraid_dir* dir_alloc(const char* sub) +{ + struct snapraid_dir* dir; + + dir = malloc_nofail(sizeof(struct snapraid_dir)); + pathcpy(dir->sub, sizeof(dir->sub), sub); + dir->flag = 0; + + return dir; +} + +void dir_free(struct snapraid_dir* dir) +{ + free(dir); +} + +int dir_name_compare(const void* void_arg, const void* void_data) +{ + const char* arg = void_arg; + const struct snapraid_dir* dir = void_data; + return strcmp(arg, dir->sub); +} + struct snapraid_disk* disk_alloc(const char* name, const char* dir, uint64_t dev) { struct snapraid_disk* disk; @@ -391,6 +414,8 @@ struct snapraid_disk* disk_alloc(const char* name, const char* dir, uint64_t dev tommy_hashdyn_init(&disk->pathset); tommy_list_init(&disk->linklist); tommy_hashdyn_init(&disk->linkset); + tommy_list_init(&disk->dirlist); + tommy_hashdyn_init(&disk->dirset); tommy_array_init(&disk->blockarr); return disk; @@ -403,6 +428,8 @@ void disk_free(struct snapraid_disk* disk) tommy_hashdyn_done(&disk->pathset); tommy_list_foreach(&disk->linklist, (tommy_foreach_func*)link_free); tommy_hashdyn_done(&disk->linkset); + tommy_list_foreach(&disk->dirlist, (tommy_foreach_func*)dir_free); + tommy_hashdyn_done(&disk->dirset); tommy_array_done(&disk->blockarr); free(disk); } diff --git a/elem.h b/elem.h index 114c0b6f..c17a0013 100644 --- a/elem.h +++ b/elem.h @@ -175,6 +175,19 @@ struct snapraid_link { tommy_hashdyn_node nodeset; }; +/** + * Dir. + */ +struct snapraid_dir { + unsigned flag; /**< FILE_IS_* flags. */ + char sub[PATH_MAX]; /**< Sub path of the file. Without the disk dir. The disk is implicit. */ + + /* nodes for data structures */ + tommy_node nodelist; + tommy_hashdyn_node nodeset; +}; + + /** * Disk. */ @@ -188,6 +201,8 @@ struct snapraid_disk { tommy_hashdyn pathset; /**< Hashtable by path of all the files. */ tommy_list linklist; /**< List of all the links. */ tommy_hashdyn linkset; /**< Hashtable by name of all the links. */ + tommy_list dirlist; /**< List of all the dirs. */ + tommy_hashdyn dirset; /**< Hashtable by name of all the dirs. */ tommy_array blockarr; /**< Block array of the disk. */ /* nodes for data structures */ @@ -423,6 +438,44 @@ static inline tommy_uint32_t link_name_hash(const char* name) return tommy_hash_u32(0, name, strlen(name)); } +static inline int dir_flag_has(struct snapraid_dir* dir, unsigned mask) +{ + return (dir->flag & mask) == mask; +} + +static inline void dir_flag_set(struct snapraid_dir* dir, unsigned mask) +{ + dir->flag |= mask; +} + +static inline void dir_flag_clear(struct snapraid_dir* dir, unsigned mask) +{ + dir->flag &= ~mask; +} + +/** + * Allocates a dir. + */ +struct snapraid_dir* dir_alloc(const char* name); + +/** + * Deallocates a dir. + */ +void dir_free(struct snapraid_dir* dir); + +/** + * Compare a dir with a name. + */ +int dir_name_compare(const void* void_arg, const void* void_data); + +/** + * Computes the hash of a dir name. + */ +static inline tommy_uint32_t dir_name_hash(const char* name) +{ + return tommy_hash_u32(0, name, strlen(name)); +} + /** * Allocates a disk. */ diff --git a/scan.c b/scan.c index 270debc0..a80e2d31 100644 --- a/scan.c +++ b/scan.c @@ -33,6 +33,7 @@ struct snapraid_scan { tommy_list file_insert_list; /**< Files to insert. */ tommy_list link_insert_list; /**< Links to insert. */ + tommy_list dir_insert_list; /**< Dirs to insert. */ /* nodes for data structures */ tommy_node node; @@ -434,10 +435,95 @@ static void scan_link(struct snapraid_scan* scan, struct snapraid_state* state, } /** - * Processes a directory. + * Removes the specified dir from the data set. */ -static void scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, int output, struct snapraid_disk* disk, const char* dir, const char* sub) +static void scan_emptydir_remove(struct snapraid_state* state, struct snapraid_disk* disk, struct snapraid_dir* dir) { + /* state changed */ + state->need_write = 1; + + /* remove the file from the dir containers */ + tommy_hashdyn_remove_existing(&disk->dirset, &dir->nodeset); + tommy_list_remove_existing(&disk->dirlist, &dir->nodelist); + + /* deallocate */ + dir_free(dir); +} + +/** + * Inserts the specified dir in the data set. + */ +static void scan_emptydir_insert(struct snapraid_state* state, struct snapraid_disk* disk, struct snapraid_dir* dir) +{ + /* state changed */ + state->need_write = 1; + + /* insert the dir in the dir containers */ + tommy_hashdyn_insert(&disk->dirset, &dir->nodeset, dir, dir_name_hash(dir->sub)); + tommy_list_insert_tail(&disk->dirlist, &dir->nodelist, dir); +} + +/** + * Processes a dir. + */ +static void scan_emptydir(struct snapraid_scan* scan, struct snapraid_state* state, int output, struct snapraid_disk* disk, const char* sub) +{ + struct snapraid_dir* dir; + + /* check if the dir already exists */ + dir = tommy_hashdyn_search(&disk->dirset, dir_name_compare, sub, dir_name_hash(sub)); + if (dir) { + /* check if multiple files have the same name */ + if (dir_flag_has(dir, FILE_IS_PRESENT)) { + fprintf(stderr, "Internal inconsistency for symdir '%s%s'\n", disk->dir, sub); + exit(EXIT_FAILURE); + } + + /* mark as present */ + dir_flag_set(dir, FILE_IS_PRESENT); + + /* it's equal */ + ++scan->count_equal; + + if (state->gui) { + fprintf(stderr, "scan:equal:%s:%s\n", disk->name, dir->sub); + fflush(stderr); + } + + /* nothing more to do */ + return; + } else { + /* create the new dir */ + ++scan->count_insert; + + if (state->gui) { + fprintf(stderr, "scan:add:%s:%s\n", disk->name, sub); + fflush(stderr); + } + if (output) { + printf("Add '%s%s'\n", disk->dir, sub); + } + + /* and continue to insert it */ + } + + /* insert it */ + dir = dir_alloc(sub); + + /* mark it as present */ + dir_flag_set(dir, FILE_IS_PRESENT); + + /* insert it in the delayed insert list */ + tommy_list_insert_tail(&scan->dir_insert_list, &dir->nodelist, dir); +} + +/** + * Processes a directory. + * Return != 0 if at least one file or link is processed. + */ +static int scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, int output, struct snapraid_disk* disk, const char* dir, const char* sub) +{ + int processed = 0; DIR* d; d = opendir(dir); @@ -525,6 +611,7 @@ static void scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, i #endif scan_file(scan, state, output, disk, sub_next, &st); + processed = 1; } else { if (state->verbose) { printf("Excluding file '%s'\n", path_next); @@ -549,6 +636,7 @@ static void scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, i subnew[ret] = 0; scan_link(scan, state, output, disk, sub_next, subnew); + processed = 1; } else { if (state->verbose) { printf("Excluding link '%s'\n", path_next); @@ -556,9 +644,18 @@ static void scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, i } } else if (S_ISDIR(st.st_mode)) { if (filter_dir(&state->filterlist, disk->name, sub_next) == 0) { + char sub_dir[PATH_MAX]; + + /* recurse */ pathslash(path_next, sizeof(path_next)); - pathslash(sub_next, sizeof(sub_next)); - scan_dir(scan, state, output, disk, path_next, sub_next); + pathcpy(sub_dir, sizeof(sub_dir), sub_next); + pathslash(sub_dir, sizeof(sub_dir)); + if (scan_dir(scan, state, output, disk, path_next, sub_dir) == 0) { + /* scan the directory as empty dir */ + scan_emptydir(scan, state, output, disk, sub_next); + } + /* or we processed something internally, or we have added the empty dir */ + processed = 1; } else { if (state->verbose) { printf("Excluding directory '%s'\n", path_next); @@ -579,6 +676,8 @@ static void scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, i fprintf(stderr, "Error closing directory '%s'. %s.\n", dir, strerror(errno)); exit(EXIT_FAILURE); } + + return processed; } void state_scan(struct snapraid_state* state, int output) @@ -602,6 +701,7 @@ void state_scan(struct snapraid_state* state, int output) scan->count_insert = 0; tommy_list_init(&scan->file_insert_list); tommy_list_init(&scan->link_insert_list); + tommy_list_init(&scan->dir_insert_list); tommy_list_insert_tail(&scanlist, &scan->node, scan); @@ -657,6 +757,30 @@ void state_scan(struct snapraid_state* state, int output) } } + /* check for removed dirs */ + node = disk->dirlist; + while (node) { + struct snapraid_dir* dir = node->data; + + /* next node */ + node = node->next; + + /* remove if not present */ + if (!dir_flag_has(dir, FILE_IS_PRESENT)) { + ++scan->count_remove; + + if (state->gui) { + fprintf(stderr, "scan:remove:%s:%s\n", disk->name, dir->sub); + fflush(stderr); + } + if (output) { + printf("Remove '%s%s'\n", disk->dir, dir->sub); + } + + scan_emptydir_remove(state, disk, dir); + } + } + /* insert all the new files, we insert them only after the deletion */ /* to reuse the just freed space */ node = scan->file_insert_list; @@ -681,6 +805,18 @@ void state_scan(struct snapraid_state* state, int output) /* insert it */ scan_link_insert(state, disk, link); } + + /* insert all the new dirs */ + node = scan->dir_insert_list; + while (node) { + struct snapraid_dir* dir = node->data; + + /* next node */ + node = node->next; + + /* insert it */ + scan_emptydir_insert(state, disk, dir); + } } /* checks for disks where all the previously existing files where removed */ diff --git a/snapraid.1 b/snapraid.1 index cd9235ec..1155f468 100644 --- a/snapraid.1 +++ b/snapraid.1 @@ -114,7 +114,7 @@ With a Backup you are able to recover from a complete failure of the whole disk array. .IP \(bu Only file data, time and symlinks are saved. Permissions, -extended attributes and hard\(hylinks are not saved. +ownership, extended attributes and hard\(hylinks are not saved. .PD .SH GETTING STARTED To use SnapRAID you need to first select one disk of your disk array @@ -904,14 +904,15 @@ This file is read and written by the \(a"sync\(a" command, and only read by Defines the size of the block in bytes. It must match the size defined in the configuration file. .SS checksum CHECKSUM -Defines the checksum kind used. It can be \(a"md5\(a" or \(a"murmur3\(a". +Defines the checksum kind used. It can be only \(a"murmur3\(a". +From SnapRAID 2.0 the old \(a"md5\(a" checksum is not supported anymore. .SS map NAME INDEX Defines the position INDEX of the disk NAME in the parity computation. .SS sign SIGN Signature checksum of the content file to ensure that it doesn\(cqt get corrupted. If you want to modify the content file manually, you have to remove this line to avoid this check. -.SS file DISK SIZE TIME_SEC.TIME_NSEC INODE PATH +.SS file DISK SIZE TIME_SEC[.TIME_NSEC] INODE PATH Defines a file in the specified DISK. .PP The INODE number is used to identify the file in the \(a"sync\(a" @@ -922,6 +923,10 @@ The SIZE and TIME information are used to identify if the file changed from the last \(a"sync\(a" command, and if there is the need to recompute the parity. .PP +The [.TIME_NSEC] information is optional, and only present if +it\(cqs possible to read a such precise time information. +It\(cqs present only from SnapRAID 2.0. +.PP The PATH information is used in the \(a"check\(a" and \(a"fix\(a" commands to identify the file. .SS blk BLOCK HASH @@ -962,6 +967,15 @@ for which the parity is computed using the previous value. .PP This field is used only when you interrupt manually the \(a"sync\(a" command. +.SS symlink DISK PATH +Defines a symbolic link in the specified DISK. +.SS to PATH +Defines where the previous symlink links to. +.SS dir DISK PATH +Defines an empty directory in the specified DISK. +.PP +Directory that contains saved files, are not included, as they +are implicitely defined in the file specifications. .SH PARITY SnapRAID stores the redundancy information of your array in the parity and q\(hyparity files. diff --git a/snapraid.d b/snapraid.d index 62cfe428..82746157 100644 --- a/snapraid.d +++ b/snapraid.d @@ -71,7 +71,7 @@ Limitations With a Backup you are able to recover from a complete failure of the whole disk array. * Only file data, time and symlinks are saved. Permissions, - extended attributes and hard-links are not saved. + ownership, extended attributes and hard-links are not saved. Getting Started To use SnapRAID you need to first select one disk of your disk array @@ -637,7 +637,8 @@ Content defined in the configuration file. checksum CHECKSUM - Defines the checksum kind used. It can be "md5" or "murmur3". + Defines the checksum kind used. It can be only "murmur3". + From SnapRAID 2.0 the old "md5" checksum is not supported anymore. map NAME INDEX Defines the position INDEX of the disk NAME in the parity computation. @@ -647,7 +648,7 @@ Content corrupted. If you want to modify the content file manually, you have to remove this line to avoid this check. - file DISK SIZE TIME_SEC.TIME_NSEC INODE PATH + file DISK SIZE TIME_SEC[.TIME_NSEC] INODE PATH Defines a file in the specified DISK. The INODE number is used to identify the file in the "sync" @@ -658,6 +659,10 @@ Content changed from the last "sync" command, and if there is the need to recompute the parity. + The [.TIME_NSEC] information is optional, and only present if + it's possible to read a such precise time information. + It's present only from SnapRAID 2.0. + The PATH information is used in the "check" and "fix" commands to identify the file. @@ -705,6 +710,18 @@ Content This field is used only when you interrupt manually the "sync" command. + symlink DISK PATH + Defines a symbolic link in the specified DISK. + + to PATH + Defines where the previous symlink links to. + + dir DISK PATH + Defines an empty directory in the specified DISK. + + Directory that contains saved files, are not included, as they + are implicitely defined in the file specifications. + Parity SnapRAID stores the redundancy information of your array in the parity and q-parity files. diff --git a/snapraid.txt b/snapraid.txt index e3ddaf9e..fa542f93 100644 --- a/snapraid.txt +++ b/snapraid.txt @@ -81,7 +81,7 @@ Other limitations are: With a Backup you are able to recover from a complete failure of the whole disk array. * Only file data, time and symlinks are saved. Permissions, - extended attributes and hard-links are not saved. + ownership, extended attributes and hard-links are not saved. 4 GETTING STARTED @@ -698,7 +698,8 @@ defined in the configuration file. 10.2 checksum CHECKSUM ---------------------- -Defines the checksum kind used. It can be "md5" or "murmur3". +Defines the checksum kind used. It can be only "murmur3". +From SnapRAID 2.0 the old "md5" checksum is not supported anymore. 10.3 map NAME INDEX ------------------- @@ -712,8 +713,8 @@ Signature checksum of the content file to ensure that it doesn't get corrupted. If you want to modify the content file manually, you have to remove this line to avoid this check. -10.5 file DISK SIZE TIME_SEC.TIME_NSEC INODE PATH -------------------------------------------------- +10.5 file DISK SIZE TIME_SEC[.TIME_NSEC] INODE PATH +--------------------------------------------------- Defines a file in the specified DISK. @@ -725,6 +726,10 @@ The SIZE and TIME information are used to identify if the file changed from the last "sync" command, and if there is the need to recompute the parity. +The [.TIME_NSEC] information is optional, and only present if +it's possible to read a such precise time information. +It's present only from SnapRAID 2.0. + The PATH information is used in the "check" and "fix" commands to identify the file. @@ -784,6 +789,24 @@ for which the parity is computed using the previous value. This field is used only when you interrupt manually the "sync" command. +10.12 symlink DISK PATH +----------------------- + +Defines a symbolic link in the specified DISK. + +10.13 to PATH +------------- + +Defines where the previous symlink links to. + +10.14 dir DISK PATH +------------------- + +Defines an empty directory in the specified DISK. + +Directory that contains saved files, are not included, as they +are implicitely defined in the file specifications. + 11 PARITY ========= diff --git a/state.c b/state.c index d635e5cd..7e8a4ca8 100644 --- a/state.c +++ b/state.c @@ -691,12 +691,14 @@ void state_read(struct snapraid_state* state) unsigned count_file; unsigned count_block; unsigned count_link; + unsigned count_dir; tommy_node* node; oathash_t hash; count_file = 0; count_block = 0; count_link = 0; + count_dir = 0; hash = 0; /* iterate over all the available content files and load the first one present */ @@ -1125,6 +1127,59 @@ void state_read(struct snapraid_state* state) /* stat */ ++count_link; + } else if (strcmp(tag, "dir") == 0) { + /* symlink */ + char sub[PATH_MAX]; + tommy_node* i; + struct snapraid_dir* dir; + + hash = oathash8(hash, 'r'); + + ret = sgettok(f, buffer, sizeof(buffer)); + if (ret < 0) { + fprintf(stderr, "Invalid 'dir' specification in '%s' at line %u\n", path, line); + exit(EXIT_FAILURE); + } + hash = oathashs(hash, buffer); + + c = sgetc(f); + if (c != ' ') { + fprintf(stderr, "Invalid 'dir' specification in '%s' at line %u\n", path, line); + exit(EXIT_FAILURE); + } + + ret = sgetline(f, sub, sizeof(sub)); + if (ret < 0) { + fprintf(stderr, "Invalid 'dir' specification in '%s' at line %u\n", path, line); + exit(EXIT_FAILURE); + } + + if (!*sub) { + fprintf(stderr, "Invalid 'dir' specification in '%s' at line %u\n", path, line); + exit(EXIT_FAILURE); + } + hash = oathashs(hash, sub); + + /* find the disk */ + for(i=state->disklist;i!=0;i=i->next) { + disk = i->data; + if (strcmp(disk->name, buffer) == 0) + break; + } + if (!i) { + fprintf(stderr, "Disk named '%s' not found in '%s' at line %u\n", buffer, path, line); + exit(EXIT_FAILURE); + } + + /* allocate the dir */ + dir = dir_alloc(sub); + + /* insert the dir in the dir containers */ + tommy_hashdyn_insert(&disk->dirset, &dir->nodeset, dir, dir_name_hash(dir->sub)); + tommy_list_insert_tail(&disk->dirlist, &dir->nodelist, dir); + + /* stat */ + ++count_dir; } else if (strcmp(tag, "checksum") == 0) { hash = oathash8(hash, 'c'); @@ -1258,6 +1313,7 @@ void state_read(struct snapraid_state* state) printf("\tfile %u\n", count_file); printf("\tblock %u\n", count_block); printf("\tsymlink %u\n", count_link); + printf("\temptydir %u\n", count_dir); } } @@ -1267,6 +1323,7 @@ void state_write(struct snapraid_state* state) unsigned count_file; unsigned count_block; unsigned count_link; + unsigned count_dir; unsigned count_content; tommy_node* i; unsigned k; @@ -1275,6 +1332,7 @@ void state_write(struct snapraid_state* state) count_file = 0; count_block = 0; count_link = 0; + count_dir = 0; hash = 0; /* count the content files */ @@ -1466,6 +1524,26 @@ void state_write(struct snapraid_state* state) ++count_link; } + /* for each dir */ + for(j=disk->dirlist;j!=0;j=j->next) { + struct snapraid_dir* dir = j->data; + + sputsl("dir ", f); + hash = oathash8(hash, 'r'); + sputs(disk->name, f); + hash = oathashs(hash, disk->name); + sputc(' ', f); + sputs(dir->sub, f); + hash = oathashs(hash, dir->sub); + sputeol(f); + if (serror(f)) { + fprintf(stderr, "Error writing the content file '%s'. %s.\n", serrorfile(f), strerror(errno)); + exit(EXIT_FAILURE); + } + + ++count_dir; + } + { block_off_t k; block_off_t blockmax; @@ -1552,6 +1630,7 @@ void state_write(struct snapraid_state* state) printf("\tfile %u\n", count_file); printf("\tblock %u\n", count_block); printf("\tsymlink %u\n", count_link); + printf("\temptydir %u\n", count_dir); } state->need_write = 0; /* no write needed anymore */ @@ -1619,6 +1698,21 @@ void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tom printf("Processing symlink '%s'\n", link->sub); } } + + /* for each dir */ + for(j=tommy_list_head(&disk->dirlist);j!=0;j=j->next) { + struct snapraid_dir* dir = j->data; + + if (filter_dir(filterlist_disk, disk->name, dir->sub) != 0 + || filter_dir(filterlist_file, disk->name, dir->sub) != 0 + ) { + dir_flag_set(dir, FILE_IS_EXCLUDED); + } + + if (state->verbose && !dir_flag_has(dir, FILE_IS_EXCLUDED)) { + printf("Processing dir '%s'\n", dir->sub); + } + } } } diff --git a/state.h b/state.h index 8b21dcb8..d2ccae68 100644 --- a/state.h +++ b/state.h @@ -108,8 +108,8 @@ void state_dry(struct snapraid_state* state, block_off_t blockstart, block_off_t void state_dup(struct snapraid_state* state); /** - * Filter files. - * Apply and additional filter to list of file currently loaded. + * Filter files, symlinks and dirs. + * Apply an additional filter to the list currently loaded. */ void state_filter(struct snapraid_state* state, tommy_list* filterlist_file, tommy_list* filterlist_disk);