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>