mirror of
https://github.com/troglobit/libite.git
synced 2026-10-01 13:32:42 +07:00
202 lines
4.8 KiB
C
202 lines
4.8 KiB
C
/* Micro "rsync" implementation.
|
|
*
|
|
* Copyright (c) 2011, 2012 Joachim Nilsson <troglobit@gmail.com>
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h> /* NULL, free() */
|
|
#include <string.h> /* strlen() */
|
|
#include <strings.h> /* rindex() */
|
|
#include <stdio.h>
|
|
#include <sys/param.h> /* MAX(), isset(), setbit(), TRUE, FALSE, et consortes. :-) */
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "lite.h"
|
|
|
|
static int copy(char *src, char *dst);
|
|
static int mdir(char *buf, size_t buf_len, char *dir, char *name, mode_t mode);
|
|
static int prune(char *dst, char **new_files, int new_num);
|
|
|
|
|
|
/**
|
|
* rsync - Synchronize contents and optionally remove non-existing backups
|
|
* @src: Source directory
|
|
* @dst: Destination directory
|
|
* @delete: Prune files from @dst that no longer exist in @src.
|
|
* @filter: Optional filtering function for source directory.
|
|
*
|
|
* This is a miniature implementation of the famous rsync for local use only.
|
|
* In fact, it is not even a true rsync since it copies all files from @src
|
|
* to @dst. The @delete option is useful for creating backups, when set all
|
|
* files removed from src since last backup are pruned from the destination
|
|
* (backup) directory.
|
|
*
|
|
* The filter callback, @filter, if provided, is used to determine what files to
|
|
* include from the source directory when backing up. If a file is to be skipped
|
|
* the callback should simply return zero.
|
|
*
|
|
* Returns:
|
|
* POSIX OK(0), or non-zero with @errno set on error.
|
|
*/
|
|
int rsync(char *src, char *dst, int delete, int (*filter) (const char *file))
|
|
{
|
|
char source[256];
|
|
char dest[256];
|
|
int i = 0, num = 0, result = 0;
|
|
char **files; /* Array of file names. */
|
|
|
|
if (!fisdir(dst))
|
|
makedir(dst, 0755);
|
|
|
|
if (!fisdir(src)) {
|
|
if (!fexist(src))
|
|
return 1;
|
|
|
|
if (copy(src, dst))
|
|
result++;
|
|
|
|
return errno;
|
|
}
|
|
|
|
/* Copy dir as well? */
|
|
if (!fisslashdir(src)) {
|
|
char *ptr = rindex(src, '/');
|
|
|
|
if (!ptr)
|
|
ptr = src;
|
|
else
|
|
ptr++;
|
|
|
|
if (mdir(dest, sizeof(dest), dst, ptr, fmode(src)))
|
|
return 1;
|
|
dst = dest;
|
|
}
|
|
|
|
num = dir(src, "", filter, &files, 0);
|
|
for (i = 0; i < num; i++) {
|
|
/* Recursively copy sub-directries */
|
|
snprintf(source, sizeof(source), "%s%s%s", src, fisslashdir(src) ? "" : "/", files[i]);
|
|
if (fisdir(source)) {
|
|
char dst2[256];
|
|
|
|
strcat(source, "/");
|
|
if (mdir (dst2, sizeof(dst2), dst, files[i], fmode(source))) {
|
|
result++;
|
|
continue;
|
|
}
|
|
|
|
rsync(source, dst2, delete, filter);
|
|
continue; /* Next file/dir in @src to copy... */
|
|
}
|
|
|
|
if (copy(source, dst))
|
|
result++;
|
|
}
|
|
|
|
/* We ignore any errors from the pruning, that phase albeit useful is only
|
|
* cosmetic. --Jocke 2011-03-24 */
|
|
if (delete)
|
|
prune(dst, files, num);
|
|
|
|
if (num) {
|
|
for (i = 0; i < num; i++)
|
|
free(files[i]);
|
|
free(files);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int copy(char *src, char *dst)
|
|
{
|
|
errno = 0;
|
|
|
|
copyfile(src, dst, 0, 1);
|
|
if (errno) {
|
|
if (errno != EEXIST)
|
|
return 1;
|
|
|
|
errno = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Creates dir/name @mode ... skipping / if dir already ends so. */
|
|
static int mdir(char *buf, size_t buf_len, char *dir, char *name, mode_t mode)
|
|
{
|
|
snprintf(buf, buf_len, "%s%s%s/", dir, fisslashdir(dir) ? "" : "/", name);
|
|
if (mkdir(buf, mode)) {
|
|
if (EEXIST != errno)
|
|
return 1;
|
|
|
|
errno = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int find(char *file, char **files, int num)
|
|
{
|
|
int n;
|
|
|
|
for (n = 0; n < num; n++)
|
|
if (!strncmp (files[n], file, MAX(strlen(files[n]), strlen(file))))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Prune old files, no longer existing on source, from destination directory. */
|
|
static int prune(char *dst, char **new_files, int new_num)
|
|
{
|
|
int num, result = 0;
|
|
char **files;
|
|
|
|
num = dir(dst, "", NULL, &files, 0);
|
|
if (num) {
|
|
int i;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
if (!find(files[i], new_files, new_num)) {
|
|
char *name;
|
|
size_t len = strlen(files[i]) + 2 + strlen(dst);
|
|
|
|
name = malloc(len);
|
|
if (name) {
|
|
snprintf(name, len, "%s%s%s", dst, fisslashdir(dst) ? "" : "/", files[i]);
|
|
if (remove(name))
|
|
result++;
|
|
free(name);
|
|
}
|
|
}
|
|
free(files[i]);
|
|
}
|
|
free(files);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Local Variables:
|
|
* indent-tabs-mode: t
|
|
* c-file-style: "linux"
|
|
* End:
|
|
*/
|