Annotation of embedaddon/pimd/libite/rsync.c, revision 1.1.1.1
1.1 misho 1: /* Micro "rsync" implementation.
2: *
3: * Copyright (c) 2011, 2012 Joachim Nilsson <troglobit@gmail.com>
4: *
5: * Permission to use, copy, modify, and/or distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17:
18: #include <errno.h>
19: #include <stdlib.h> /* NULL, free() */
20: #include <string.h> /* strlen() */
21: #include <strings.h> /* rindex() */
22: #include <stdio.h>
23: #include <sys/param.h> /* MAX(), isset(), setbit(), TRUE, FALSE, et consortes. :-) */
24: #include <sys/stat.h>
25: #include <sys/types.h>
26:
27: #include "lite.h"
28:
29: static int copy(char *src, char *dst);
30: static int mdir(char *buf, size_t buf_len, char *dir, char *name, mode_t mode);
31: static int prune(char *dst, char **new_files, int new_num);
32:
33:
34: /**
35: * rsync - Synchronize contents and optionally remove non-existing backups
36: * @src: Source directory
37: * @dst: Destination directory
38: * @delete: Prune files from @dst that no longer exist in @src.
39: * @filter: Optional filtering function for source directory.
40: *
41: * This is a miniature implementation of the famous rsync for local use only.
42: * In fact, it is not even a true rsync since it copies all files from @src
43: * to @dst. The @delete option is useful for creating backups, when set all
44: * files removed from src since last backup are pruned from the destination
45: * (backup) directory.
46: *
47: * The filter callback, @filter, if provided, is used to determine what files to
48: * include from the source directory when backing up. If a file is to be skipped
49: * the callback should simply return zero.
50: *
51: * Returns:
52: * POSIX OK(0), or non-zero with @errno set on error.
53: */
54: int rsync(char *src, char *dst, int delete, int (*filter) (const char *file))
55: {
56: char source[256];
57: char dest[256];
58: int i = 0, num = 0, result = 0;
59: char **files; /* Array of file names. */
60:
61: if (!fisdir(dst))
62: makedir(dst, 0755);
63:
64: if (!fisdir(src)) {
65: if (!fexist(src))
66: return 1;
67:
68: if (copy(src, dst))
69: result++;
70:
71: return errno;
72: }
73:
74: /* Copy dir as well? */
75: if (!fisslashdir(src)) {
76: char *ptr = rindex(src, '/');
77:
78: if (!ptr)
79: ptr = src;
80: else
81: ptr++;
82:
83: if (mdir(dest, sizeof(dest), dst, ptr, fmode(src)))
84: return 1;
85: dst = dest;
86: }
87:
88: num = dir(src, "", filter, &files, 0);
89: for (i = 0; i < num; i++) {
90: /* Recursively copy sub-directries */
91: snprintf(source, sizeof(source), "%s%s%s", src, fisslashdir(src) ? "" : "/", files[i]);
92: if (fisdir(source)) {
93: char dst2[256];
94:
95: strcat(source, "/");
96: if (mdir (dst2, sizeof(dst2), dst, files[i], fmode(source))) {
97: result++;
98: continue;
99: }
100:
101: rsync(source, dst2, delete, filter);
102: continue; /* Next file/dir in @src to copy... */
103: }
104:
105: if (copy(source, dst))
106: result++;
107: }
108:
109: /* We ignore any errors from the pruning, that phase albeit useful is only
110: * cosmetic. --Jocke 2011-03-24 */
111: if (delete)
112: prune(dst, files, num);
113:
114: if (num) {
115: for (i = 0; i < num; i++)
116: free(files[i]);
117: free(files);
118: }
119:
120: return result;
121: }
122:
123: static int copy(char *src, char *dst)
124: {
125: errno = 0;
126:
127: copyfile(src, dst, 0, 1);
128: if (errno) {
129: if (errno != EEXIST)
130: return 1;
131:
132: errno = 0;
133: }
134:
135: return 0;
136: }
137:
138: /* Creates dir/name @mode ... skipping / if dir already ends so. */
139: static int mdir(char *buf, size_t buf_len, char *dir, char *name, mode_t mode)
140: {
141: snprintf(buf, buf_len, "%s%s%s/", dir, fisslashdir(dir) ? "" : "/", name);
142: if (mkdir(buf, mode)) {
143: if (EEXIST != errno)
144: return 1;
145:
146: errno = 0;
147: }
148:
149: return 0;
150: }
151:
152:
153: static int find(char *file, char **files, int num)
154: {
155: int n;
156:
157: for (n = 0; n < num; n++)
158: if (!strncmp (files[n], file, MAX(strlen(files[n]), strlen(file))))
159: return 1;
160:
161: return 0;
162: }
163:
164:
165: /* Prune old files, no longer existing on source, from destination directory. */
166: static int prune(char *dst, char **new_files, int new_num)
167: {
168: int num, result = 0;
169: char **files;
170:
171: num = dir(dst, "", NULL, &files, 0);
172: if (num) {
173: int i;
174:
175: for (i = 0; i < num; i++) {
176: if (!find(files[i], new_files, new_num)) {
177: char *name;
178: size_t len = strlen(files[i]) + 2 + strlen(dst);
179:
180: name = malloc(len);
181: if (name) {
182: snprintf(name, len, "%s%s%s", dst, fisslashdir(dst) ? "" : "/", files[i]);
183: if (remove(name))
184: result++;
185: free(name);
186: }
187: }
188: free(files[i]);
189: }
190: free(files);
191: }
192:
193: return result;
194: }
195:
196: #ifdef UNITTEST
197: #define BASE "/tmp/.unittest/"
198: #define SRC BASE "src/"
199: #define DST BASE "dst/"
200:
201: static int verbose = 0;
202: static char *files[] = {
203: SRC "sub1/1.tst",
204: SRC "sub1/2.tst",
205: SRC "sub1/3.tst",
206: SRC "sub2/4.tst",
207: SRC "sub2/5.tst",
208: SRC "sub2/6.tst",
209: SRC "sub3/7.tst",
210: SRC "sub3/8.tst",
211: SRC "sub3/9.tst",
212: NULL
213: };
214:
215: void cleanup_test(void)
216: {
217: system("rm -rf " BASE);
218: }
219:
220: void setup_test(void)
221: {
222: int i;
223: char cmd[256];
224: mode_t dir_modes[] = { 755, 700 };
225: mode_t file_modes[] = { 644, 600 };
226:
227: cleanup_test();
228:
229: mkdir(BASE, 0755);
230: mkdir(SRC, 0755);
231: mkdir(DST, 0755);
232:
233: for (i = 0; files[i]; i++) {
234: snprintf(cmd, sizeof(cmd), "mkdir -m %d -p `dirname %s`",
235: dir_modes[i % 2], files[i]);
236: system(cmd);
237:
238: snprintf(cmd, sizeof(cmd), "touch %s; chmod %d %s", files[i],
239: file_modes[i % 2], files[i]);
240: system(cmd);
241: }
242: }
243:
244: static void check_tree(char *heading, char *dir)
245: {
246: if (verbose) {
247: char cmd[128];
248:
249: if (heading)
250: puts(heading);
251:
252: tree(dir, 1);
253: }
254: }
255:
256: int run_test(void)
257: {
258: int result = 0;
259:
260: #if 0
261: setup_test();
262: check_tree("Before:", BASE);
263:
264: result += rsync(SRC, DST, 0, NULL);
265: check_tree("After:", BASE);
266: cleanup_test();
267: #endif
268:
269: setup_test();
270: result += rsync(BASE "src", DST, 0, NULL);
271: check_tree("Only partial rsync of src <-- No slash!", BASE);
272: #if 0
273: cleanup_test();
274: setup_test();
275: result += rsync(BASE "src/sub1", BASE "dst", 0, NULL);
276: check_tree("Only partial rsync of src/sub1 <-- No slashes!!", BASE);
277:
278: cleanup_test();
279: setup_test();
280: result += rsync(BASE "src/sub1/", DST, 0, NULL);
281: check_tree("Only partial rsync of src/sub1/", BASE);
282:
283: cleanup_test();
284: setup_test();
285: result += rsync(BASE "src/sub1", DST, 0, NULL);
286: check_tree("Only partial rsync of src/sub1 <-- No slash!", BASE);
287:
288: result += rsync("/etc", "/var/tmp", 0, NULL);
289: check_tree("Real life test:", "/var/tmp");
290: #endif
291:
292: return result;
293: }
294:
295: int main(int argc, char *argv[])
296: {
297: if (argc > 1)
298: verbose = !strncmp("-v", argv[1], 2);
299:
300: atexit(cleanup_test);
301:
302: return run_test();
303: }
304: #endif /* UNITTEST */
305:
306: /**
307: * Local Variables:
308: * compile-command: "make V=1 -f rsync.mk"
309: * version-control: t
310: * indent-tabs-mode: t
311: * c-file-style: "linux"
312: * End:
313: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>