1: /*************************************************************************
2: * (C) 2010 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
3: * by Michael Pounov <misho@openbsd-bg.org>
4: *
5: * $Author: misho $
6: * $Id: dir.c,v 1.5.4.1 2016/08/18 09:53:02 misho Exp $
7: *
8: **************************************************************************
9: The ELWIX and AITNET software is distributed under the following
10: terms:
11:
12: All of the documentation and software included in the ELWIX and AITNET
13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
14:
15: Copyright 2004 - 2016
16: by Michael Pounov <misho@elwix.org>. All rights reserved.
17:
18: Redistribution and use in source and binary forms, with or without
19: modification, are permitted provided that the following conditions
20: are met:
21: 1. Redistributions of source code must retain the above copyright
22: notice, this list of conditions and the following disclaimer.
23: 2. Redistributions in binary form must reproduce the above copyright
24: notice, this list of conditions and the following disclaimer in the
25: documentation and/or other materials provided with the distribution.
26: 3. All advertising materials mentioning features or use of this software
27: must display the following acknowledgement:
28: This product includes software developed by Michael Pounov <misho@elwix.org>
29: ELWIX - Embedded LightWeight unIX and its contributors.
30: 4. Neither the name of AITNET nor the names of its contributors
31: may be used to endorse or promote products derived from this software
32: without specific prior written permission.
33:
34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37: ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44: SUCH DAMAGE.
45: */
46: #include "global.h"
47:
48:
49: static int
50: func_comp(struct tagDirName const *d1, struct tagDirName const *d2)
51: {
52: return d1->tag - d2->tag;
53: }
54:
55: static struct tagDirName *
56: find_tag(int const * __restrict tags, struct tagDirName const * __restrict l, u_short t, u_int hash)
57: {
58: struct tagDirName *find = NULL;
59: register int i;
60:
61: /* search in index tags */
62: if (tags[t] != -1 && l[tags[t]].tag == t) {
63: /* search in sorted hashes */
64: for (i = 0; l[tags[t] + i].tag == t; i++)
65: if (l[tags[t] + i].hash == hash) {
66: /* finded & marked for delete! */
67: find = (struct tagDirName*) &l[tags[t] + i];
68: find->ch = DIFF_D0;
69: break;
70: }
71: }
72:
73: return find;
74: }
75:
76: static int *
77: create_tags()
78: {
79: int *tags;
80:
81: tags = e_calloc(TABLESIZ, sizeof(int));
82: if (!tags) {
83: LOGERR;
84: } else
85: memset(tags, -1, TABLESIZ * sizeof(int));
86:
87: return tags;
88: }
89:
90: static int
91: create_diridx(const char *csDir, int lm, int *tags, struct tagDirName **list)
92: {
93: struct tagDirName *l = *list;
94: DIR *dir;
95: struct dirent d, *pd;
96: int n;
97: char szStr[STRSIZ], szType[STRSIZ], *old;
98: struct stat sb;
99: register int i;
100:
101: l = e_malloc(sizeof(struct tagDirName));
102: if (!l) {
103: LOGERR;
104: *list = NULL;
105: return -1;
106: } else {
107: n = 0;
108: memset(l, 0, sizeof(struct tagDirName));
109: }
110:
111: old = getcwd(NULL, 0);
112: if (chdir(csDir) == -1) {
113: LOGERR;
114: e_free(old);
115: e_free(l);
116: *list = NULL;
117: return -1;
118: }
119: dir = opendir(".");
120: if (!dir) {
121: LOGERR;
122: chdir(old);
123: e_free(old);
124: e_free(l);
125: *list = NULL;
126: return -1;
127: }
128: while (!readdir_r(dir, &d, &pd) && pd) {
129: if (d.d_type == DT_DIR && (!strcmp(d.d_name, ".") || !strcmp(d.d_name, "..")))
130: continue;
131:
132: l = e_realloc(l, sizeof(struct tagDirName) * (n + 2));
133: if (!l) {
134: LOGERR;
135: chdir(old);
136: e_free(old);
137: e_free(l);
138: *list = NULL;
139: closedir(dir);
140: return -1;
141: } else
142: memset(&l[n + 1], 0, sizeof(struct tagDirName));
143:
144: l[n].ch = DIFF_D1;
145: #ifndef __linux__
146: l[n].tag = crcFletcher16((u_short*) d.d_name,
147: d.d_namlen / 2 + d.d_namlen % 2);
148: l[n].hash = crcAdler((u_char*) d.d_name, d.d_namlen);
149: #else
150: l[n].tag = crcFletcher16((u_short*) d.d_name,
151: strlen(d.d_name) / 2 + strlen(d.d_name) % 2);
152: l[n].hash = crcAdler((u_char*) d.d_name, strlen(d.d_name));
153: #endif
154: strlcpy(l[n].name, d.d_name, sizeof l[n].name);
155: if (lm & 1) {
156: if (lstat(d.d_name, &sb) != -1) {
157: memset(szStr, 0, sizeof szStr);
158: #if defined(__OpenBSD__)
159: strftime(szStr, sizeof szStr, "%Y-%m-%d %H:%M:%S",
160: localtime((time_t*) &sb.st_mtim));
161: #else
162: strftime(szStr, sizeof szStr, "%Y-%m-%d %H:%M:%S",
163: localtime((time_t*) &sb.st_mtime));
164: #endif
165: switch (d.d_type) {
166: case DT_FIFO:
167: strlcpy(szType, "fifo", sizeof szType);
168: break;
169: case DT_CHR:
170: strlcpy(szType, "char", sizeof szType);
171: break;
172: case DT_DIR:
173: strlcpy(szType, "dir", sizeof szType);
174: break;
175: case DT_BLK:
176: strlcpy(szType, "block", sizeof szType);
177: break;
178: case DT_REG:
179: strlcpy(szType, "file", sizeof szType);
180: break;
181: case DT_LNK:
182: strlcpy(szType, "link", sizeof szType);
183: break;
184: case DT_SOCK:
185: strlcpy(szType, "socket", sizeof szType);
186: break;
187: /* OpenBSD does not have this type */
188: #ifdef DT_WHT
189: case DT_WHT:
190: strlcpy(szType, "wht", sizeof szType);
191: break;
192: #endif
193: case DT_UNKNOWN:
194: default:
195: strlcpy(szType, "unknown", sizeof szType);
196: break;
197: }
198: snprintf(l[n].extra, sizeof l[n].extra,
199: "%s links=%ld inode=%ld %d:%d perm=0%o size=%ld %s",
200: szType, (long) sb.st_nlink, (long) sb.st_ino,
201: sb.st_uid, sb.st_gid, sb.st_mode & 0x1fff,
202: (long) sb.st_size, szStr);
203: }
204: }
205:
206: n++;
207: }
208: closedir(dir);
209:
210: qsort(l, n, sizeof(struct tagDirName), (int (*)(const void*, const void*)) func_comp);
211: for (i = n - 1; i > -1; i--)
212: tags[l[i].tag] = i;
213:
214: chdir(old);
215: e_free(old);
216:
217: *list = l;
218: return n;
219: }
220:
221: /* ------------------------------------------------------ */
222:
223: /*
224: * sync_dirChkSum() - Calculate checksum of directory
225: *
226: * @csDir = Directory
227: * @md = Message digest allocated memory, must be e_free() after use!
228: * return: -1 error or !=-1 ok
229: */
230: int
231: sync_dirChkSum(const char *csDir, u_char **md)
232: {
233: DIR *dir;
234: struct dirent d, *pd;
235: MD5_CTX ctx;
236: register int ret = 0;
237:
238: *md = e_malloc(MD5_DIGEST_LENGTH);
239: if (!*md) {
240: LOGERR;
241: return -1;
242: } else
243: memset(*md, 0, MD5_DIGEST_LENGTH);
244:
245: dir = opendir(csDir);
246: if (!dir) {
247: LOGERR;
248: e_free(*md);
249: return -1;
250: }
251:
252: MD5_Init(&ctx);
253: while (!readdir_r(dir, &d, &pd) && pd) {
254: if (d.d_type == DT_DIR && (!strcmp(d.d_name, ".") || !strcmp(d.d_name, "..")))
255: continue;
256: #ifndef __linux__
257: MD5_Update(&ctx, d.d_name, d.d_namlen);
258: #else
259: MD5_Update(&ctx, d.d_name, strlen(d.d_name));
260: #endif
261: ret++;
262: }
263: MD5_Final(*md, &ctx);
264:
265: closedir(dir);
266: return ret;
267: }
268:
269: /*
270: * sync_dircmp() - Compare directories
271: *
272: * @csDir1 = Directory 1
273: * @csDir2 = Directory 2
274: * return: -1 error, 0 is equal or 1 different
275: */
276: int
277: sync_dircmp(const char *csDir1, const char *csDir2)
278: {
279: u_char *md[2] = { NULL, NULL };
280: int ret = -1;
281:
282: if (!csDir1 || !csDir2)
283: return ret;
284:
285: if (sync_dirChkSum(csDir1, &md[0]) == -1)
286: return ret;
287: if (sync_dirChkSum(csDir2, &md[1]) == -1) {
288: e_free(md[0]);
289: return ret;
290: }
291:
292: if (!memcmp(md[0], md[1], MD5_DIGEST_LENGTH))
293: ret = 0;
294: else
295: ret = 1;
296:
297: e_free(md[1]);
298: e_free(md[0]);
299: return ret;
300: }
301:
302: /*
303: * sync_dircmpList() - Compare directories or directory and file list
304: *
305: * @csDir1 = Directory 1
306: * @csDir2 = Directory 2 or File list, if "-" get input from console
307: * @lm = Long mode options, !=NULL long output
308: * @list = Output diff list, after use must be e_free()!
309: * return: -1 error, 0 is equal or >0 count of returned list items
310: */
311: int
312: sync_dircmpList(const char *csDir1, const char *csDir2, int lm, struct tagDirName **list)
313: {
314: struct tagDirName *l, *find;
315: int n, cx;
316: DIR *dir;
317: FILE *f = stdin;
318: struct dirent d, *pd;
319: int *tags;
320: register int i;
321: u_short t;
322: u_int hash;
323: struct stat sb;
324: char szLine[STRSIZ], szStr[STRSIZ], szType[STRSIZ], *str, *pbrk, *old;
325:
326: if (!csDir1 || !list || !(tags = create_tags()))
327: return -1;
328:
329: n = create_diridx(csDir1, lm, tags, &l);
330: if (n == -1 || !csDir2) {
331: *list = l;
332: return n;
333: }
334:
335: if (lstat(csDir2, &sb) == -1) {
336: LOGERR;
337: e_free(tags);
338: e_free(l);
339: return -1;
340: }
341: if (S_ISDIR(sb.st_mode)) {
342: old = getcwd(NULL, 0);
343: if (chdir(csDir2) == -1) {
344: LOGERR;
345: chdir(old);
346: e_free(old);
347: e_free(tags);
348: e_free(l);
349: return -1;
350: }
351: dir = opendir(".");
352: if (!dir) {
353: LOGERR;
354: chdir(old);
355: e_free(old);
356: e_free(tags);
357: e_free(l);
358: return -1;
359: }
360: while (!readdir_r(dir, &d, &pd) && pd) {
361: if (d.d_type == DT_DIR && (!strcmp(d.d_name, ".") || !strcmp(d.d_name, "..")))
362: continue;
363: else {
364: #ifndef __linux__
365: t = crcFletcher16((u_short*) d.d_name, d.d_namlen / 2 + d.d_namlen % 2);
366: hash = crcAdler((u_char*) d.d_name, d.d_namlen);
367: #else
368: t = crcFletcher16((u_short*) d.d_name, strlen(d.d_name) / 2 + strlen(d.d_name) % 2);
369: hash = crcAdler((u_char*) d.d_name, strlen(d.d_name));
370: #endif
371: }
372:
373: find = find_tag(tags, l, t, hash);
374: /* element not find in dir1, added */
375: if (!find) {
376: l = e_realloc(l, sizeof(struct tagDirName) * (n + 2));
377: if (!l) {
378: LOGERR;
379: chdir(old);
380: e_free(old);
381: closedir(dir);
382: e_free(tags);
383: return -1;
384: } else
385: memset(&l[n + 1], 0, sizeof(struct tagDirName));
386:
387: l[n].ch = DIFF_D2;
388: l[n].tag = t;
389: l[n].hash = hash;
390: strlcpy(l[n].name, d.d_name, sizeof l[n].name);
391: if (lm & 1) {
392: if (lstat(d.d_name, &sb) != -1) {
393: memset(szStr, 0, sizeof szStr);
394: #if defined(__OpenBSD__)
395: strftime(szStr, sizeof szStr, "%Y-%m-%d %H:%M:%S",
396: localtime((time_t*) &sb.st_mtim));
397: #else
398: strftime(szStr, sizeof szStr, "%Y-%m-%d %H:%M:%S",
399: localtime((time_t*) &sb.st_mtime));
400: #endif
401: switch (d.d_type) {
402: case DT_FIFO:
403: strlcpy(szType, "fifo", sizeof szType);
404: break;
405: case DT_CHR:
406: strlcpy(szType, "char", sizeof szType);
407: break;
408: case DT_DIR:
409: strlcpy(szType, "dir", sizeof szType);
410: break;
411: case DT_BLK:
412: strlcpy(szType, "block", sizeof szType);
413: break;
414: case DT_REG:
415: strlcpy(szType, "file", sizeof szType);
416: break;
417: case DT_LNK:
418: strlcpy(szType, "link", sizeof szType);
419: break;
420: case DT_SOCK:
421: strlcpy(szType, "socket", sizeof szType);
422: break;
423: /* OpenBSD does not have this type */
424: #ifdef DT_WHT
425: case DT_WHT:
426: strlcpy(szType, "wht", sizeof szType);
427: break;
428: #endif
429: case DT_UNKNOWN:
430: default:
431: strlcpy(szType, "unknown", sizeof szType);
432: break;
433: }
434: snprintf(l[n].extra, sizeof l[n].extra,
435: "%s links=%ld inode=%ld %d:%d perm=0%o "
436: "size=%ld %s", szType, (long) sb.st_nlink,
437: (long) sb.st_ino, sb.st_uid, sb.st_gid,
438: sb.st_mode & 0x1fff, (long) sb.st_size,
439: szStr);
440: }
441: }
442:
443: n++;
444: }
445: }
446: closedir(dir);
447: chdir(old);
448: e_free(old);
449: } else {
450: if (strcmp(csDir2, "-")) {
451: f = fopen(csDir2, "r");
452: if (!f) {
453: LOGERR;
454: e_free(tags);
455: e_free(l);
456: return -1;
457: }
458: }
459: while (fgets(szLine, sizeof szLine, f)) {
460: if (!*szLine || *szLine == '#')
461: continue;
462:
463: str = strtok_r(szLine, " \t", &pbrk);
464: if (!str)
465: continue;
466: str = strtok_r(NULL, " \t", &pbrk);
467: if (!str)
468: continue;
469: else {
470: i = strlen(str);
471: t = crcFletcher16((u_short*) str, i / 2 + i % 2);
472: hash = crcAdler((u_char*) str, i);
473: }
474:
475: find = find_tag(tags, l, t, hash);
476: /* element not find in dir1, added */
477: if (!find) {
478: l = e_realloc(l, sizeof(struct tagDirName) * (n + 2));
479: if (!l) {
480: LOGERR;
481: if (strcmp(csDir2, "-"))
482: fclose(f);
483: e_free(tags);
484: return -1;
485: } else
486: memset(&l[n + 1], 0, sizeof(struct tagDirName));
487:
488: l[n].ch = DIFF_D2;
489: l[n].tag = t;
490: l[n].hash = hash;
491: strlcpy(l[n].name, str, sizeof l[n].name);
492: if (lm & 1 && (str = strtok_r(NULL, "\r\n", &pbrk)))
493: strlcpy(l[n].extra, str, sizeof l[n].extra);
494:
495: n++;
496: }
497: }
498: if (strcmp(csDir2, "-"))
499: fclose(f);
500: }
501:
502: /* delete equal elemets !!! */
503: for (i = cx = 0; i < n; i++)
504: if (l[i].ch == DIFF_D0) {
505: memmove(&l[i], &l[i + 1], (n - i + 1) * sizeof(struct tagDirName));
506: cx++;
507: i--;
508: }
509: n -= cx;
510:
511: e_free(tags);
512: *list = l;
513: return n;
514: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>