--- libaitsync/src/dir.c 2010/07/13 15:04:43 1.1 +++ libaitsync/src/dir.c 2010/07/13 15:04:43 1.1.2.1 @@ -0,0 +1,407 @@ +/************************************************************************* +* (C) 2010 AITNET ltd - Sofia/Bulgaria - +* by Michael Pounov +* +* $Author: misho $ +* $Id: dir.c,v 1.1.2.1 2010/07/13 15:04:43 misho Exp $ +* +*************************************************************************/ +#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; +}