Annotation of embedaddon/pimd/libite/rsync.c, revision 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>