diff --git a/HISTORY b/HISTORY index b657e656..6cc1aa70 100644 --- a/HISTORY +++ b/HISTORY @@ -10,9 +10,9 @@ SnapRAID HISTORY * Added a new -i, --import option to automatically import deleted files when fixing. * Added support also for hardlinks and empty directories. - * Added support to save symlinks in Windows. Note that only the symlink - is saved and not the linked file or directory. - Junctions are still not supported. + * Added support to save symlinks to files in Windows. Note that only the + symlink is saved and not the linked file. + Symlinks to dirs and junctions are still not supported. * Files without read permission generates now an error instead of a warning. You now must explicitely exclude them in the configuration file with exclusion rules. diff --git a/check.c b/check.c index 7b98d5bd..9811773b 100644 --- a/check.c +++ b/check.c @@ -1109,7 +1109,7 @@ static int state_check_process(struct snapraid_state* state, int check, int fix, continue; } - if (link_is_hardlink(link)) { + if (link_flag_has(link, FILE_IS_HARDLINK)) { /* stat the link */ pathprint(path, sizeof(path), "%s%s", disk->dir, link->sub); ret = stat(path, &st); @@ -1196,7 +1196,7 @@ static int state_check_process(struct snapraid_state* state, int check, int fix, } /* create it */ - if (link_is_hardlink(link)) { + if (link_flag_has(link, FILE_IS_HARDLINK)) { ret = hardlink(pathto, path); if (ret != 0) { fprintf(stderr, "Error writing hardlink '%s'. %s.\n", path, strerror(errno)); diff --git a/elem.c b/elem.c index e3c00954..0db0d212 100644 --- a/elem.c +++ b/elem.c @@ -391,14 +391,14 @@ int file_path_compare(const void* void_arg, const void* void_data) return strcmp(arg, file->sub); } -struct snapraid_link* link_alloc(const char* sub, const char* linkto, int is_hardlink) +struct snapraid_link* link_alloc(const char* sub, const char* linkto, unsigned link_flag) { struct snapraid_link* link; link = malloc_nofail(sizeof(struct snapraid_link)); pathcpy(link->sub, sizeof(link->sub), sub); pathcpy(link->linkto, sizeof(link->linkto), linkto); - link->flag = is_hardlink != 0 ? FILE_IS_HARDLINK : 0; + link->flag = link_flag; return link; } diff --git a/elem.h b/elem.h index d3eefbf9..3722cf31 100644 --- a/elem.h +++ b/elem.h @@ -135,12 +135,17 @@ struct snapraid_block { */ #define BLOCK_EMPTY 0 -#define FILE_IS_PRESENT 1 /**< If it's seen as present. */ -#define FILE_IS_EXCLUDED 2 /**< If it's an excluded file from the processing. */ -#define FILE_IS_LARGER 4 /**< If a larger file was already detected. Just to avoid to report it more times. */ -#define FILE_IS_DAMAGED 8 /**< If a fix was attempted but it failed. */ -#define FILE_IS_FIXED 16 /**< If a fix was done. */ -#define FILE_IS_HARDLINK 32 /**< If it's an hardlink. */ +#define FILE_IS_PRESENT 0x01 /**< If it's seen as present. */ +#define FILE_IS_EXCLUDED 0x02 /**< If it's an excluded file from the processing. */ +#define FILE_IS_LARGER 0x04 /**< If a larger file was already detected. Just to avoid to report it more times. */ +#define FILE_IS_DAMAGED 0x08 /**< If a fix was attempted but it failed. */ +#define FILE_IS_FIXED 0x10 /**< If a fix was done. */ + +#define FILE_IS_HARDLINK 0x100 /**< If it's an hardlink. */ +#define FILE_IS_SYMLINK 0x200 /**< If it's a file symlink. */ +#define FILE_IS_SYMDIR 0x400 /**< If it's a dir symlink for Windows. Not yet supported. */ +#define FILE_IS_JUNCTION 0x800 /**< If it's a junction for Windows. Not yet supported. */ +#define FILE_IS_LINK_MASK 0xF00 /**< Mask for link type. */ /** * Value used to mark files without the nanoseconds value. @@ -515,18 +520,21 @@ static inline void link_flag_clear(struct snapraid_link* link, unsigned mask) link->flag &= ~mask; } -/** - * Checks if the link is a hardlink. Otherwise it's a symbolic link. - */ -static inline int link_is_hardlink(struct snapraid_link* link) +static inline void link_flag_let(struct snapraid_link* link, unsigned flag, unsigned mask) { - return link_flag_has(link, FILE_IS_HARDLINK); + link->flag &= ~mask; + link->flag |= flag & mask; +} + +static inline unsigned link_flag_get(struct snapraid_link* link, unsigned mask) +{ + return link->flag & mask; } /** * Allocates a link. */ -struct snapraid_link* link_alloc(const char* name, const char* link, int is_hardlink); +struct snapraid_link* link_alloc(const char* name, const char* link, unsigned link_flag); /** * Deallocates a link. diff --git a/mingw.c b/mingw.c index 7f8a0c3b..5904a201 100644 --- a/mingw.c +++ b/mingw.c @@ -330,8 +330,13 @@ static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, const FILE st->st_desc = "reparse-point-nfs"; break; case IO_REPARSE_TAG_SYMLINK : - st->st_mode = S_IFLNK; - st->st_desc = "reparse-point-symlink"; + if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { + st->st_mode = S_IFLNKDIR; + st->st_desc = "reparse-point-symlink-dir"; + } else { + st->st_mode = S_IFLNK; + st->st_desc = "reparse-point-symlink-file"; + } break; default: st->st_mode = S_IFCHR; @@ -418,8 +423,13 @@ static void windows_finddata2stat(const WIN32_FIND_DATAW* info, struct windows_s st->st_desc = "reparse-point-nfs"; break; case IO_REPARSE_TAG_SYMLINK : - st->st_mode = S_IFLNK; - st->st_desc = "reparse-point-symlink"; + if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { + st->st_mode = S_IFLNKDIR; + st->st_desc = "reparse-point-symlink-dir"; + } else { + st->st_mode = S_IFLNK; + st->st_desc = "reparse-point-symlink-file"; + } break; default: st->st_mode = S_IFCHR; diff --git a/mingw.h b/mingw.h index 0a6ca7e0..94640dc2 100644 --- a/mingw.h +++ b/mingw.h @@ -58,8 +58,13 @@ #define dirent_lstat windows_dirent_lstat #define stat_desc windows_stat_desc #define sleep windows_sleep -#define S_IFLNK 0x7000 /* 4==DIR, 8==REG */ +/* 4==DIR, 5,6,7=free, 8==REG */ +#define S_IFLNK 0x5000 /* Symbolic link to file */ #define S_ISLNK(m) (((m) & _S_IFMT) == S_IFLNK) +#define S_IFLNKDIR 0x6000 /* Symbolic link to directory */ +#define S_ISLNKDIR(m) (((m) & _S_IFMT) == S_IFLNKDIR) +#define S_IFJUN 0x6000 /* Junction */ +#define S_ISJUN(m) (((m) & _S_IFMT) == S_IFJUN) #define readlink windows_readlink #define symlink windows_symlink #define link windows_link diff --git a/scan.c b/scan.c index fa32fe43..ac8ec61f 100644 --- a/scan.c +++ b/scan.c @@ -71,7 +71,7 @@ static void scan_link_insert(struct snapraid_state* state, struct snapraid_disk* /** * Processes a symbolic link. */ -static void scan_link(struct snapraid_scan* scan, struct snapraid_state* state, int output, struct snapraid_disk* disk, const char* sub, const char* linkto, int is_hardlink) +static void scan_link(struct snapraid_scan* scan, struct snapraid_state* state, int output, struct snapraid_disk* disk, const char* sub, const char* linkto, unsigned link_flag) { struct snapraid_link* link; @@ -80,10 +80,7 @@ static void scan_link(struct snapraid_scan* scan, struct snapraid_state* state, if (link) { /* check if multiple files have the same name */ if (link_flag_has(link, FILE_IS_PRESENT)) { - if (link_is_hardlink(link)) - fprintf(stderr, "Internal inconsistency for hardlink '%s%s'\n", disk->dir, sub); - else - fprintf(stderr, "Internal inconsistency for symlink '%s%s'\n", disk->dir, sub); + fprintf(stderr, "Internal inconsistency for link '%s%s'\n", disk->dir, sub); exit(EXIT_FAILURE); } @@ -91,7 +88,7 @@ static void scan_link(struct snapraid_scan* scan, struct snapraid_state* state, link_flag_set(link, FILE_IS_PRESENT); /* check if the link is not changed and it's of the same kind */ - if (strcmp(link->linkto, linkto) == 0 && is_hardlink == link_is_hardlink(link)) { + if (strcmp(link->linkto, linkto) == 0 && link_flag == link_flag_get(link, FILE_IS_LINK_MASK)) { /* it's equal */ ++scan->count_equal; @@ -116,10 +113,7 @@ static void scan_link(struct snapraid_scan* scan, struct snapraid_state* state, /* update it */ pathcpy(link->linkto, sizeof(link->linkto), linkto); - if (is_hardlink) - link_flag_set(link, FILE_IS_HARDLINK); - else - link_flag_clear(link, FILE_IS_HARDLINK); + link_flag_let(link, link_flag, FILE_IS_LINK_MASK); /* nothing more to do */ return; @@ -140,7 +134,7 @@ static void scan_link(struct snapraid_scan* scan, struct snapraid_state* state, } /* insert it */ - link = link_alloc(sub, linkto, is_hardlink); + link = link_alloc(sub, linkto, link_flag); /* mark it as present */ link_flag_set(link, FILE_IS_PRESENT); @@ -290,7 +284,7 @@ static void scan_file(struct snapraid_scan* scan, struct snapraid_state* state, if (file_flag_has(file, FILE_IS_PRESENT)) { if (st->st_nlink > 1) { /* it's a hardlink */ - scan_link(scan, state, output, disk, sub, file->sub, 1); + scan_link(scan, state, output, disk, sub, file->sub, FILE_IS_HARDLINK); return; } else { fprintf(stderr, "Internal inode '%"PRIu64"' inconsistency for file '%s%s'\n", (uint64_t)st->st_ino, disk->dir, sub); @@ -500,7 +494,7 @@ static void scan_emptydir(struct snapraid_scan* scan, struct snapraid_state* sta 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); + fprintf(stderr, "Internal inconsistency for dir '%s%s'\n", disk->dir, sub); exit(EXIT_FAILURE); } @@ -655,7 +649,7 @@ static int scan_dir(struct snapraid_scan* scan, struct snapraid_state* state, in subnew[ret] = 0; /* process as a symbolic link */ - scan_link(scan, state, output, disk, sub_next, subnew, 0); + scan_link(scan, state, output, disk, sub_next, subnew, FILE_IS_SYMLINK); processed = 1; } else { if (state->verbose) { diff --git a/state.c b/state.c index 78ad7fab..2354b581 100644 --- a/state.c +++ b/state.c @@ -713,16 +713,16 @@ void state_read(struct snapraid_state* state) unsigned line; unsigned count_file; unsigned count_block; - unsigned count_symlink; unsigned count_hardlink; + unsigned count_symlink; unsigned count_dir; tommy_node* node; oathash_t hash; count_file = 0; count_block = 0; - count_symlink = 0; count_hardlink = 0; + count_symlink = 0; count_dir = 0; hash = 0; @@ -1168,7 +1168,7 @@ void state_read(struct snapraid_state* state) } /* allocate the link as symbolic link */ - link = link_alloc(sub, linkto, 0); + link = link_alloc(sub, linkto, FILE_IS_SYMLINK); /* insert the link in the link containers */ tommy_hashdyn_insert(&disk->linkset, &link->nodeset, link, link_name_hash(link->sub)); @@ -1253,7 +1253,7 @@ void state_read(struct snapraid_state* state) } /* allocate the link as hard link */ - link = link_alloc(sub, linkto, 1); + link = link_alloc(sub, linkto, FILE_IS_HARDLINK); /* insert the link in the link containers */ tommy_hashdyn_insert(&disk->linkset, &link->nodeset, link, link_name_hash(link->sub)); @@ -1455,8 +1455,8 @@ void state_read(struct snapraid_state* state) if (state->verbose) { printf("\tfile %u\n", count_file); printf("\tblock %u\n", count_block); - printf("\tsymlink %u\n", count_symlink); printf("\thardlink %u\n", count_hardlink); + printf("\tsymlink %u\n", count_symlink); printf("\temptydir %u\n", count_dir); } } @@ -1466,8 +1466,8 @@ void state_write(struct snapraid_state* state) STREAM* f; unsigned count_file; unsigned count_block; - unsigned count_symlink; unsigned count_hardlink; + unsigned count_symlink; unsigned count_dir; unsigned count_content; tommy_node* i; @@ -1476,8 +1476,8 @@ void state_write(struct snapraid_state* state) count_file = 0; count_block = 0; - count_symlink = 0; count_hardlink = 0; + count_symlink = 0; count_dir = 0; hash = 0; @@ -1649,14 +1649,17 @@ void state_write(struct snapraid_state* state) for(j=disk->linklist;j!=0;j=j->next) { struct snapraid_link* link = j->data; - if (link_is_hardlink(link)) { + switch (link_flag_get(link, FILE_IS_LINK_MASK)) { + case FILE_IS_HARDLINK : sputsl("hardlink ", f); hash = oathash8(hash, 'a'); ++count_hardlink; - } else { + break; + case FILE_IS_SYMLINK : sputsl("symlink ", f); hash = oathash8(hash, 's'); ++count_symlink; + break; } sputs(disk->name, f); @@ -1785,8 +1788,8 @@ void state_write(struct snapraid_state* state) if (state->verbose) { printf("\tfile %u\n", count_file); printf("\tblock %u\n", count_block); - printf("\tsymlink %u\n", count_symlink); printf("\thardlink %u\n", count_hardlink); + printf("\tsymlink %u\n", count_symlink); printf("\temptydir %u\n", count_dir); }