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.1.2.6 2011/05/09 14:35:56 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, 2005, 2006, 2007, 2008, 2009, 2010, 2011
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 = '*';
69: break;
70: }
71: }
72:
73: return find;
74: }
75:
76: static int *
77: create_tags()
78: {
79: int *tags;
80:
81: tags = calloc(TABLESIZ, sizeof(int));
82: if (!tags) {
83: SETERR;
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 = malloc(sizeof(struct tagDirName));
102: if (!l) {
103: SETERR;
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: SETERR;
114: free(old);
115: free(l);
116: *list = NULL;
117: return -1;
118: }
119: dir = opendir(".");
120: if (!dir) {
121: SETERR;
122: chdir(old);
123: free(old);
124: 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 = realloc(l, sizeof(struct tagDirName) * (n + 2));
133: if (!l) {
134: SETERR;
135: chdir(old);
136: free(old);
137: 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 = '<';
145: l[n].tag = crcFletcher16((u_short*) d.d_name, d.d_namlen / 2 + d.d_namlen % 2);
146: l[n].hash = crcAdler((u_char*) d.d_name, d.d_namlen);
147: strlcpy(l[n].name, d.d_name, MAXPATHLEN);
148: if (lm & 1) {
149: if (lstat(d.d_name, &sb) != -1) {
150: memset(szStr, 0, STRSIZ);
151: #if defined(__OpenBSD__)
152: strftime(szStr, STRSIZ, "%Y-%m-%d %H:%M:%S", localtime((time_t*) &sb.st_mtim));
153: #else
154: strftime(szStr, STRSIZ, "%Y-%m-%d %H:%M:%S", localtime((time_t*) &sb.st_mtime));
155: #endif
156: switch (d.d_type) {
157: case DT_FIFO:
158: strlcpy(szType, "fifo", STRSIZ);
159: break;
160: case DT_CHR:
161: strlcpy(szType, "char", STRSIZ);
162: break;
163: case DT_DIR:
164: strlcpy(szType, "dir", STRSIZ);
165: break;
166: case DT_BLK:
167: strlcpy(szType, "block", STRSIZ);
168: break;
169: case DT_REG:
170: strlcpy(szType, "file", STRSIZ);
171: break;
172: case DT_LNK:
173: strlcpy(szType, "link", STRSIZ);
174: break;
175: case DT_SOCK:
176: strlcpy(szType, "socket", STRSIZ);
177: break;
178: /* OpenBSD does not have this type */
179: #ifdef DT_WHT
180: case DT_WHT:
181: strlcpy(szType, "wht", STRSIZ);
182: break;
183: #endif
184: case DT_UNKNOWN:
185: default:
186: strlcpy(szType, "unknown", STRSIZ);
187: break;
188: }
189: snprintf(l[n].extra, STRSIZ, "%s links=%d inode=%lu %d:%d perm=0%o size=%ld %s",
190: szType, sb.st_nlink, sb.st_ino, sb.st_uid, sb.st_gid,
191: sb.st_mode & 0x1fff, sb.st_size, szStr);
192: }
193: }
194:
195: n++;
196: }
197: closedir(dir);
198:
199: qsort(l, n, sizeof(struct tagDirName), (int (*)(const void*, const void*)) func_comp);
200: for (i = n - 1; i > -1; i--)
201: tags[l[i].tag] = i;
202:
203: chdir(old);
204: free(old);
205:
206: *list = l;
207: return n;
208: }
209:
210: // ------------------------------------------------------
211:
212: /*
213: * sync_dirCSum() Calculate checksum of directory
214: * @csDir = Directory
215: * @md = Message digest allocated memory, must be free after use!
216: * return: -1 error or !=-1 ok
217: */
218: int
219: sync_dirCSum(const char *csDir, u_char **md)
220: {
221: DIR *dir;
222: struct dirent d, *pd;
223: MD5_CTX ctx;
224: register int ret = 0;
225:
226: *md = malloc(MD5_DIGEST_LENGTH);
227: if (!*md) {
228: SETERR;
229: return -1;
230: } else
231: memset(*md, 0, MD5_DIGEST_LENGTH);
232:
233: dir = opendir(csDir);
234: if (!dir) {
235: SETERR;
236: free(*md);
237: return -1;
238: }
239:
240: MD5_Init(&ctx);
241: while (!readdir_r(dir, &d, &pd) && pd) {
242: if (d.d_type == DT_DIR && (!strcmp(d.d_name, ".") || !strcmp(d.d_name, "..")))
243: continue;
244: MD5_Update(&ctx, d.d_name, d.d_namlen);
245: ret++;
246: }
247: MD5_Final(*md, &ctx);
248:
249: closedir(dir);
250: return ret;
251: }
252:
253: /*
254: * sync_dircmp() Compare directories
255: * @csDir1 = Directory 1
256: * @csDir2 = Directory 2
257: * return: -1 error, 0 is equal or 1 different
258: */
259: int
260: sync_dircmp(const char *csDir1, const char *csDir2)
261: {
262: u_char *md[2] = { NULL, NULL };
263: int ret = -1;
264:
265: if (!csDir1 || !csDir2)
266: return ret;
267:
268: if (sync_dirCSum(csDir1, &md[0]) == -1)
269: return ret;
270: if (sync_dirCSum(csDir2, &md[1]) == -1) {
271: free(md[0]);
272: return ret;
273: }
274:
275: if (!memcmp(md[0], md[1], MD5_DIGEST_LENGTH))
276: ret = 0;
277: else
278: ret = 1;
279:
280: free(md[1]);
281: free(md[0]);
282: return ret;
283: }
284:
285: /*
286: * sync_dircmpList() Compare directories or directory and file list
287: * @csDir1 = Directory 1
288: * @csDir2 = Directory 2 or File list, if "-" get input from console
289: * @lm = Long mode options, 1 long output
290: * @list = Output diff list, after use must be free!
291: * return: -1 error, 0 is equal or >0 count of returned list items
292: */
293: int
294: sync_dircmpList(const char *csDir1, const char *csDir2, int lm, struct tagDirName **list)
295: {
296: struct tagDirName *l, *find;
297: int n, cx;
298: DIR *dir;
299: FILE *f = stdin;
300: struct dirent d, *pd;
301: int *tags;
302: register int i;
303: u_short t;
304: u_int hash;
305: struct stat sb;
306: char szLine[STRSIZ], szStr[STRSIZ], szType[STRSIZ], *str, *pbrk, *old;
307:
308: if (!csDir1 || !list || !(tags = create_tags()))
309: return -1;
310:
311: n = create_diridx(csDir1, lm, tags, &l);
312: if (n == -1 || !csDir2) {
313: *list = l;
314: return n;
315: }
316:
317: if (lstat(csDir2, &sb) == -1) {
318: SETERR;
319: free(l);
320: return -1;
321: }
322: if (S_ISDIR(sb.st_mode)) {
323: old = getcwd(NULL, 0);
324: if (chdir(csDir2) == -1) {
325: SETERR;
326: chdir(old);
327: free(old);
328: free(l);
329: return -1;
330: }
331: dir = opendir(".");
332: if (!dir) {
333: SETERR;
334: chdir(old);
335: free(old);
336: free(l);
337: return -1;
338: }
339: while (!readdir_r(dir, &d, &pd) && pd) {
340: if (d.d_type == DT_DIR && (!strcmp(d.d_name, ".") || !strcmp(d.d_name, "..")))
341: continue;
342: else {
343: t = crcFletcher16((u_short*) d.d_name, d.d_namlen / 2 + d.d_namlen % 2);
344: hash = crcAdler((u_char*) d.d_name, d.d_namlen);
345: }
346:
347: find = find_tag(tags, l, t, hash);
348: // element not find in dir1, added
349: if (!find) {
350: l = realloc(l, sizeof(struct tagDirName) * (n + 2));
351: if (!l) {
352: SETERR;
353: chdir(old);
354: free(old);
355: closedir(dir);
356: return -1;
357: } else
358: memset(&l[n + 1], 0, sizeof(struct tagDirName));
359:
360: l[n].ch = '>';
361: l[n].tag = t;
362: l[n].hash = hash;
363: strlcpy(l[n].name, d.d_name, MAXPATHLEN);
364: if (lm & 1) {
365: if (lstat(d.d_name, &sb) != -1) {
366: memset(szStr, 0, STRSIZ);
367: #if defined(__OpenBSD__)
368: strftime(szStr, STRSIZ, "%Y-%m-%d %H:%M:%S",
369: localtime((time_t*) &sb.st_mtim));
370: #else
371: strftime(szStr, STRSIZ, "%Y-%m-%d %H:%M:%S",
372: localtime((time_t*) &sb.st_mtime));
373: #endif
374: switch (d.d_type) {
375: case DT_FIFO:
376: strlcpy(szType, "fifo", STRSIZ);
377: break;
378: case DT_CHR:
379: strlcpy(szType, "char", STRSIZ);
380: break;
381: case DT_DIR:
382: strlcpy(szType, "dir", STRSIZ);
383: break;
384: case DT_BLK:
385: strlcpy(szType, "block", STRSIZ);
386: break;
387: case DT_REG:
388: strlcpy(szType, "file", STRSIZ);
389: break;
390: case DT_LNK:
391: strlcpy(szType, "link", STRSIZ);
392: break;
393: case DT_SOCK:
394: strlcpy(szType, "socket", STRSIZ);
395: break;
396: /* OpenBSD does not have this type */
397: #ifdef DT_WHT
398: case DT_WHT:
399: strlcpy(szType, "wht", STRSIZ);
400: break;
401: #endif
402: case DT_UNKNOWN:
403: default:
404: strlcpy(szType, "unknown", STRSIZ);
405: break;
406: }
407: snprintf(l[n].extra, STRSIZ,
408: "%s links=%d inode=%lu %d:%d perm=0%o size=%ld %s",
409: szType, sb.st_nlink, sb.st_ino, sb.st_uid, sb.st_gid,
410: sb.st_mode & 0x1fff, sb.st_size, szStr);
411: }
412: }
413:
414: n++;
415: }
416: }
417: closedir(dir);
418: chdir(old);
419: free(old);
420: } else {
421: if (strcmp(csDir2, "-")) {
422: f = fopen(csDir2, "r");
423: if (!f) {
424: SETERR;
425: free(l);
426: return -1;
427: }
428: }
429: while (fgets(szLine, STRSIZ, f)) {
430: if (!*szLine || *szLine == '#')
431: continue;
432:
433: str = strtok_r(szLine, " \t", &pbrk);
434: if (!str)
435: continue;
436: str = strtok_r(NULL, " \t", &pbrk);
437: if (!str)
438: continue;
439: else {
440: i = strlen(str);
441: t = crcFletcher16((u_short*) str, i / 2 + i % 2);
442: hash = crcAdler((u_char*) str, i);
443: }
444:
445: find = find_tag(tags, l, t, hash);
446: // element not find in dir1, added
447: if (!find) {
448: l = realloc(l, sizeof(struct tagDirName) * (n + 2));
449: if (!l) {
450: SETERR;
451: if (strcmp(csDir2, "-"))
452: fclose(f);
453: return -1;
454: } else
455: memset(&l[n + 1], 0, sizeof(struct tagDirName));
456:
457: l[n].ch = '>';
458: l[n].tag = t;
459: l[n].hash = hash;
460: strlcpy(l[n].name, str, MAXPATHLEN);
461: if (lm & 1 && (str = strtok_r(NULL, "\r\n", &pbrk)))
462: strlcpy(l[n].extra, str, STRSIZ);
463:
464: n++;
465: }
466: }
467: if (strcmp(csDir2, "-"))
468: fclose(f);
469: }
470:
471: // delete equal elemets !!!
472: for (i = cx = 0; i < n; i++)
473: if (l[i].ch == '*') {
474: memmove(&l[i], &l[i + 1], (n - i + 1) * sizeof(struct tagDirName));
475: cx++;
476: i--;
477: }
478: n -= cx;
479:
480: *list = l;
481: return n;
482: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>