|
|
| version 1.1, 2010/07/13 15:04:43 | version 1.1.2.1, 2010/07/13 15:04:43 |
|---|---|
| Line 0 | Line 1 |
| /************************************************************************* | |
| * (C) 2010 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com> | |
| * by Michael Pounov <misho@openbsd-bg.org> | |
| * | |
| * $Author$ | |
| * $Id$ | |
| * | |
| *************************************************************************/ | |
| #include "global.h" | |
| static int | |
| func_comp(struct tagDirName const *d1, struct tagDirName const *d2) | |
| { | |
| return d1->tag - d2->tag; | |
| } | |
| static struct tagDirName * | |
| find_tag(int const * __restrict tags, struct tagDirName const * __restrict l, u_short t, u_int hash) | |
| { | |
| struct tagDirName *find = NULL; | |
| register int i; | |
| // search in index tags | |
| if (tags[t] != -1 && l[tags[t]].tag == t) { | |
| // search in sorted hashes | |
| for (i = 0; l[tags[t] + i].tag == t; i++) | |
| if (l[tags[t] + i].hash == hash) { | |
| // finded & marked for delete! | |
| find = (struct tagDirName*) &l[tags[t] + i]; | |
| find->ch = '*'; | |
| break; | |
| } | |
| } | |
| return find; | |
| } | |
| static int * | |
| create_tags() | |
| { | |
| int *tags; | |
| tags = calloc(TABLESIZ, sizeof(int)); | |
| if (!tags) { | |
| SETERR; | |
| } else | |
| memset(tags, -1, TABLESIZ * sizeof(int)); | |
| return tags; | |
| } | |
| static int | |
| create_diridx(const char *csDir, int lm, int *tags, struct tagDirName **list) | |
| { | |
| struct tagDirName *l = *list; | |
| DIR *dir; | |
| struct dirent d, *pd; | |
| int n; | |
| char szStr[STRSIZ], szType[STRSIZ]; | |
| struct stat sb; | |
| register int i; | |
| l = malloc(sizeof(struct tagDirName)); | |
| if (!l) { | |
| SETERR; | |
| *list = NULL; | |
| return -1; | |
| } else { | |
| n = 0; | |
| memset(l, 0, sizeof(struct tagDirName)); | |
| } | |
| if (chdir(csDir) == -1) { | |
| SETERR; | |
| free(l); | |
| *list = NULL; | |
| return -1; | |
| } | |
| dir = opendir("."); | |
| if (!dir) { | |
| SETERR; | |
| free(l); | |
| *list = NULL; | |
| return -1; | |
| } | |
| while (!readdir_r(dir, &d, &pd) && pd) { | |
| if (d.d_type == DT_DIR && (!strcmp(d.d_name, ".") || !strcmp(d.d_name, ".."))) | |
| continue; | |
| l = realloc(l, sizeof(struct tagDirName) * (n + 2)); | |
| if (!l) { | |
| SETERR; | |
| free(l); | |
| *list = NULL; | |
| closedir(dir); | |
| return -1; | |
| } else | |
| memset(&l[n + 1], 0, sizeof(struct tagDirName)); | |
| l[n].ch = '<'; | |
| l[n].tag = crcFletcher16((u_short*) d.d_name, d.d_namlen / 2 + d.d_namlen % 2); | |
| l[n].hash = crcAdler((u_char*) d.d_name, d.d_namlen); | |
| strlcpy(l[n].name, d.d_name, MAXPATHLEN); | |
| if (lm & 1) { | |
| if (lstat(d.d_name, &sb) != -1) { | |
| memset(szStr, 0, STRSIZ); | |
| strftime(szStr, STRSIZ, "%Y-%m-%d %H:%M:%S", localtime((time_t*) &sb.st_mtim)); | |
| switch (d.d_type) { | |
| case DT_FIFO: | |
| strlcpy(szType, "fifo", STRSIZ); | |
| break; | |
| case DT_CHR: | |
| strlcpy(szType, "char", STRSIZ); | |
| break; | |
| case DT_DIR: | |
| strlcpy(szType, "dir", STRSIZ); | |
| break; | |
| case DT_BLK: | |
| strlcpy(szType, "block", STRSIZ); | |
| break; | |
| case DT_REG: | |
| strlcpy(szType, "file", STRSIZ); | |
| break; | |
| case DT_LNK: | |
| strlcpy(szType, "link", STRSIZ); | |
| break; | |
| case DT_SOCK: | |
| strlcpy(szType, "socket", STRSIZ); | |
| break; | |
| case DT_WHT: | |
| strlcpy(szType, "wht", STRSIZ); | |
| break; | |
| case DT_UNKNOWN: | |
| default: | |
| strlcpy(szType, "unknown", STRSIZ); | |
| break; | |
| } | |
| snprintf(l[n].extra, STRSIZ, "%s links=%d inode=%u %d:%d perm=0%o size=%llu %s", | |
| szType, sb.st_nlink, sb.st_ino, sb.st_uid, sb.st_gid, | |
| sb.st_mode & 0x1fff, sb.st_size, szStr); | |
| } | |
| } | |
| n++; | |
| } | |
| closedir(dir); | |
| qsort(l, n, sizeof(struct tagDirName), (int (*)(const void*, const void*)) func_comp); | |
| for (i = n - 1; i > -1; i--) | |
| tags[l[i].tag] = i; | |
| *list = l; | |
| return n; | |
| } | |
| // ------------------------------------------------------ | |
| /* | |
| * sync_dirCSum() Calculate checksum of directory | |
| * @csDir = Directory | |
| * @md = Message digest allocated memory, must be free after use! | |
| * return: -1 error or !=-1 ok | |
| */ | |
| int | |
| sync_dirCSum(const char *csDir, u_char **md) | |
| { | |
| DIR *dir; | |
| struct dirent d, *pd; | |
| MD5_CTX ctx; | |
| register int ret = 0; | |
| *md = malloc(MD5_DIGEST_LENGTH); | |
| if (!*md) { | |
| SETERR; | |
| return -1; | |
| } else | |
| memset(*md, 0, MD5_DIGEST_LENGTH); | |
| dir = opendir(csDir); | |
| if (!dir) { | |
| SETERR; | |
| free(*md); | |
| return -1; | |
| } | |
| MD5_Init(&ctx); | |
| while (!readdir_r(dir, &d, &pd) && pd) { | |
| if (d.d_type == DT_DIR && (!strcmp(d.d_name, ".") || !strcmp(d.d_name, ".."))) | |
| continue; | |
| MD5_Update(&ctx, d.d_name, d.d_namlen); | |
| ret++; | |
| } | |
| MD5_Final(*md, &ctx); | |
| closedir(dir); | |
| return ret; | |
| } | |
| /* | |
| * sync_dircmp() Compare directories | |
| * @csDir1 = Directory 1 | |
| * @csDir2 = Directory 2 | |
| * return: -1 error, 0 is equal or 1 different | |
| */ | |
| int | |
| sync_dircmp(const char *csDir1, const char *csDir2) | |
| { | |
| u_char *md[2] = { NULL, NULL }; | |
| int ret = -1; | |
| if (!csDir1 || !csDir2) | |
| return ret; | |
| if (sync_dirCSum(csDir1, &md[0]) == -1) | |
| return ret; | |
| if (sync_dirCSum(csDir2, &md[1]) == -1) { | |
| free(md[0]); | |
| return ret; | |
| } | |
| if (!memcmp(md[0], md[1], MD5_DIGEST_LENGTH)) | |
| ret = 0; | |
| else | |
| ret = 1; | |
| free(md[1]); | |
| free(md[0]); | |
| return ret; | |
| } | |
| /* | |
| * sync_dircmpList() Compare directories or directory and file list | |
| * @csDir1 = Directory 1 | |
| * @csDir2 = Directory 2 or File list, if "-" get input from console | |
| * @lm = Long mode options, 1 long output | |
| * @list = Output diff list, after use must be free! | |
| * return: -1 error, 0 is equal or >0 count of returned list items | |
| */ | |
| int | |
| sync_dircmpList(const char *csDir1, const char *csDir2, int lm, struct tagDirName **list) | |
| { | |
| struct tagDirName *l, *find; | |
| int n, cx; | |
| DIR *dir; | |
| FILE *f = stdin; | |
| struct dirent d, *pd; | |
| int *tags; | |
| register int i; | |
| u_short t; | |
| u_int hash; | |
| struct stat sb; | |
| char szLine[STRSIZ], szStr[STRSIZ], szType[STRSIZ], *str, *pbrk; | |
| if (!csDir1 || !list || !(tags = create_tags())) | |
| return -1; | |
| n = create_diridx(csDir1, lm, tags, &l); | |
| if (n == -1 || !csDir2) { | |
| *list = l; | |
| return n; | |
| } | |
| if (!lstat(csDir2, &sb) && S_ISDIR(sb.st_mode)) { | |
| if (chdir(csDir2) == -1) { | |
| SETERR; | |
| free(l); | |
| return -1; | |
| } | |
| dir = opendir("."); | |
| if (!dir) { | |
| SETERR; | |
| free(l); | |
| return -1; | |
| } | |
| while (!readdir_r(dir, &d, &pd) && pd) { | |
| if (d.d_type == DT_DIR && (!strcmp(d.d_name, ".") || !strcmp(d.d_name, ".."))) | |
| continue; | |
| else { | |
| t = crcFletcher16((u_short*) d.d_name, d.d_namlen / 2 + d.d_namlen % 2); | |
| hash = crcAdler((u_char*) d.d_name, d.d_namlen); | |
| } | |
| find = find_tag(tags, l, t, hash); | |
| // element not find in dir1, added | |
| if (!find) { | |
| l = realloc(l, sizeof(struct tagDirName) * (n + 2)); | |
| if (!l) { | |
| SETERR; | |
| closedir(dir); | |
| return -1; | |
| } else | |
| memset(&l[n + 1], 0, sizeof(struct tagDirName)); | |
| l[n].ch = '>'; | |
| l[n].tag = t; | |
| l[n].hash = hash; | |
| strlcpy(l[n].name, d.d_name, MAXPATHLEN); | |
| if (lm & 1) { | |
| if (lstat(d.d_name, &sb) != -1) { | |
| memset(szStr, 0, STRSIZ); | |
| strftime(szStr, STRSIZ, "%Y-%m-%d %H:%M:%S", | |
| localtime((time_t*) &sb.st_mtim)); | |
| switch (d.d_type) { | |
| case DT_FIFO: | |
| strlcpy(szType, "fifo", STRSIZ); | |
| break; | |
| case DT_CHR: | |
| strlcpy(szType, "char", STRSIZ); | |
| break; | |
| case DT_DIR: | |
| strlcpy(szType, "dir", STRSIZ); | |
| break; | |
| case DT_BLK: | |
| strlcpy(szType, "block", STRSIZ); | |
| break; | |
| case DT_REG: | |
| strlcpy(szType, "file", STRSIZ); | |
| break; | |
| case DT_LNK: | |
| strlcpy(szType, "link", STRSIZ); | |
| break; | |
| case DT_SOCK: | |
| strlcpy(szType, "socket", STRSIZ); | |
| break; | |
| case DT_WHT: | |
| strlcpy(szType, "wht", STRSIZ); | |
| break; | |
| case DT_UNKNOWN: | |
| default: | |
| strlcpy(szType, "unknown", STRSIZ); | |
| break; | |
| } | |
| snprintf(l[n].extra, STRSIZ, | |
| "%s links=%d inode=%u %d:%d perm=0%o size=%llu %s", | |
| szType, sb.st_nlink, sb.st_ino, sb.st_uid, sb.st_gid, | |
| sb.st_mode & 0x1fff, sb.st_size, szStr); | |
| } | |
| } | |
| n++; | |
| } | |
| } | |
| closedir(dir); | |
| } else { | |
| if (strcmp(csDir2, "-")) { | |
| f = fopen(csDir2, "r"); | |
| if (!f) { | |
| SETERR; | |
| free(l); | |
| return -1; | |
| } | |
| } | |
| while (fgets(szLine, STRSIZ, f)) { | |
| if (!*szLine || *szLine == '#') | |
| continue; | |
| str = strtok_r(szLine, " \t", &pbrk); | |
| if (!str) | |
| continue; | |
| str = strtok_r(NULL, " \t", &pbrk); | |
| if (!str) | |
| continue; | |
| else { | |
| i = strlen(str); | |
| t = crcFletcher16((u_short*) str, i / 2 + i % 2); | |
| hash = crcAdler((u_char*) str, i); | |
| } | |
| find = find_tag(tags, l, t, hash); | |
| // element not find in dir1, added | |
| if (!find) { | |
| l = realloc(l, sizeof(struct tagDirName) * (n + 2)); | |
| if (!l) { | |
| SETERR; | |
| if (strcmp(csDir2, "-")) | |
| fclose(f); | |
| return -1; | |
| } else | |
| memset(&l[n + 1], 0, sizeof(struct tagDirName)); | |
| l[n].ch = '>'; | |
| l[n].tag = t; | |
| l[n].hash = hash; | |
| strlcpy(l[n].name, str, MAXPATHLEN); | |
| if (lm & 1 && (str = strtok_r(NULL, "\r\n", &pbrk))) | |
| strlcpy(l[n].extra, str, STRSIZ); | |
| n++; | |
| } | |
| } | |
| if (strcmp(csDir2, "-")) | |
| fclose(f); | |
| } | |
| // delete equal elemets !!! | |
| for (i = cx = 0; i < n; i++) | |
| if (l[i].ch == '*') { | |
| memmove(&l[i], &l[i + 1], (n - i + 1) * sizeof(struct tagDirName)); | |
| cx++; | |
| i--; | |
| } | |
| n -= cx; | |
| *list = l; | |
| return n; | |
| } |