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>