/* $Id$ */ /*** This file is part of syrep. syrep is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. syrep is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with syrep; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ***/ #define USE_SENDFILE #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_SENDFILE #include #endif #include "util.h" #include "syrep.h" static int stderr_tty = -1; void statistics(DB *db) { DB_BTREE_STAT *statp; int ret; assert(db); if ((ret = db->stat(db, &statp, 0)) != 0) { db->err(db, ret, "DB->stat"); return; } printf("Database contains %lu records\n", (long unsigned) statp->bt_ndata); free(statp); } char* normalize_path(char *s) { char *l, *p, *d; // deletes /./ and // if (*s == '/') l = p = d = s+1; else l = p = d = s; for (; *p; p++) { if (*p == '/') { if (l-p == 0) { l++; continue; } if (p-l == 1 && *l == '.') { l += 2; continue; } while (l <= p) *(d++) = *(l++); } } while (l <= p) *(d++) = *(l++); return s; } void rotdash(void) { static const char dashes[] = /* ".oOo"; */ "|/-\\"; const static char *d = dashes; if (stderr_tty < 0) stderr_tty = isatty(fileno(stderr)); if (stderr_tty) { fprintf(stderr, "%c\b", *d); d++; if (!*d) d = dashes; } } void rotdash_hide(void) { if (stderr_tty < 0) stderr_tty = isatty(fileno(stderr)); if (stderr_tty) fputs(" \b", stderr); } const char* get_snapshot_filename(const char *path, const char *fn) { static char npath[PATH_MAX]; struct stat st; if (stat(path, &st) < 0) { if (errno == ENOENT) return path; fprintf(stderr, "stat(%s) failed: %s\n", path, strerror(errno)); return NULL; } if (S_ISREG(st.st_mode)) return path; if (S_ISDIR(st.st_mode)) { snprintf(npath, sizeof(npath), "%s/.syrep", path); mkdir(npath, 0777); snprintf(npath, sizeof(npath), "%s/.syrep/%s", path, fn); return npath; } fprintf(stderr, "<%s> is not a valid syrep snapshot\n", path); return NULL; } int isdirectory(const char *path) { struct stat st; if (stat(path, &st) < 0) return -1; return !!S_ISDIR(st.st_mode); } #define MMAPSIZE (100*1024*1024) #define BUFSIZE (1024*1024) int copy_fd(int sfd, int dfd, uint32_t l) { off_t src_o, dst_o; void *src_p, *dst_p; void *buf = NULL; int r = -1; struct stat st; #ifdef USE_SENDFILE ssize_t k; off_t sfo; #endif if ((src_o = lseek(sfd, 0, SEEK_CUR)) == (off_t) -1) { fprintf(stderr, "lseek(): %s\n", strerror(errno)); goto finish; } if (fstat(sfd, &st) < 0) { fprintf(stderr, "fstat(): %s\n", strerror(errno)); goto finish; } if (l == (uint32_t) -1 || l >= st.st_size - src_o) l = st.st_size - src_o; if ((dst_o = lseek(dfd, 0, SEEK_CUR)) == (off_t) -1) { fprintf(stderr, "lseek(): %s\n", strerror(errno)); goto finish; } if (fstat(dfd, &st) < 0) { fprintf(stderr, "fstat(): %s\n", strerror(errno)); goto finish; } if (dst_o+l > st.st_size) if (ftruncate(dfd, dst_o+l) < 0) { fprintf(stderr, "ftruncate(): %s\n", strerror(errno)); goto finish; } #ifdef USE_SENDFILE sfo = src_o; if ((k = sendfile(dfd, sfd, &sfo, l)) > 0) { l -= k; dst_o += k; src_o = sfo; if (lseek(sfd, src_o, SEEK_SET) == (off_t) -1) { fprintf(stderr, "lseek(): %s\n", strerror(errno)); goto finish; } } else if (k < 0) fprintf(stderr, "sendfile() failed for file copy, trying mmap(): %s\n", strerror(errno)); #endif if (l > BUFSIZE) { uint32_t m = l < MMAPSIZE ? l : MMAPSIZE; while (l > 0) { if ((src_p = mmap(NULL, m, PROT_READ, MAP_SHARED, sfd, src_o)) == MAP_FAILED) break; if ((dst_p = mmap(NULL, m, PROT_READ|PROT_WRITE, MAP_SHARED, dfd, dst_o)) == MAP_FAILED) { munmap(src_p, m); break; } memcpy(dst_p, src_p, m); munmap(src_p, m); munmap(dst_p, m); if (interrupted) { fprintf(stderr, "Canceled.\n"); goto finish; } src_o += m; dst_o += m; l -= m; m = l < MMAPSIZE ? l : MMAPSIZE; } if (lseek(sfd, src_o, SEEK_SET) == (off_t) -1) { fprintf(stderr, "lseek(): %s\n", strerror(errno)); goto finish; } if (lseek(dfd, dst_o, SEEK_SET) == (off_t) -1) { fprintf(stderr, "lseek(): %s\n", strerror(errno)); goto finish; } if (l > 0) fprintf(stderr, "mmap() failed for file copy, trying read()/write(): %s\n", strerror(errno)); } if (l > 0) { if (!(buf = malloc(BUFSIZE))) goto finish; for (;;) { ssize_t m; if (interrupted) { fprintf(stderr, "Canceled.\n"); goto finish; } if ((m = read(sfd, buf, BUFSIZE)) < 0) { fprintf(stderr, "read(): %s\n", strerror(errno)); goto finish; } if (!m) break; if (write(dfd, buf, m) < 0) { fprintf(stderr, "write(): %s\n", strerror(errno)); goto finish; } } } r = 0; finish: return r; } int copy_file(const char *src, const char *dst, int c) { int sfd = -1, dfd = -1, r = -1; if ((sfd = open(src, O_RDONLY)) < 0) { fprintf(stderr, "open(\"%s\", O_RDONLY): %s\n", src, strerror(errno)); goto finish; } if ((dfd = open(dst, O_RDWR|O_TRUNC|O_CREAT|( c ? 0 : O_EXCL), 0666)) < 0) { fprintf(stderr, "open(\"%s\", O_RDWR|O_TRUNC|O_CREAT%s): %s\n", dst, c ? "" : "|O_EXCL", strerror(errno)); goto finish; } if (copy_fd(sfd, dfd, (uint32_t) -1) < 0) goto finish; r = 0; finish: if (sfd >= 0) close(sfd); if (dfd >= 0) close(dfd); return r; } int copy_or_link_file(const char *src, const char *dst, int c) { if (c) unlink(dst); if (link(src, dst) < 0) { if (errno == EXDEV || errno == EPERM) return copy_file(src, dst, c); fprintf(stderr, "link(%s, %s): %s\n", src, dst, strerror(errno)); return -1; } return 0; } int move_file(const char *src, const char *dst, int c) { int r; if ((r = copy_or_link_file(src, dst, c)) < 0) return -1; if (unlink(src) < 0) { fprintf(stderr, "unlink(%s): %s\n", src, strerror(errno)); unlink(dst); return -1; } return 0; } int prune_empty_directories(const char *path, const char *root) { char rroot[PATH_MAX], rpath[PATH_MAX]; strncpy(rroot, root, PATH_MAX); rroot[PATH_MAX-1] = 0; normalize_path(rroot); strncpy(rpath, path, PATH_MAX); rpath[PATH_MAX-1] = 0; normalize_path(rpath); for (;;) { char *e; if (!rpath[0]) break; if (!strcmp(rpath, "/")) break; if (!strcmp(rpath, rroot)) break; if (rmdir(rpath) < 0) { if (errno == ENOTEMPTY) break; if (errno != ENOENT) { fprintf(stderr, "rmdir(\"%s\"): %s\n", rpath, strerror(errno)); return -1; } } if (!(e = strrchr(rpath, '/'))) break; *e = 0; } return 0; } int mkdir_p(const char *path, mode_t m) { char tmp[PATH_MAX]; char *e, *b; int quit = 0; strncpy(tmp, path, PATH_MAX); tmp[PATH_MAX-1] = 0; normalize_path(tmp); for (b = tmp, quit = 0; !quit;) { if (!(e = strchr(b, '/'))) { e = strchr(b, 0); quit = 1; } *e = 0; if (mkdir(tmp, m) < 0) { if (errno != EEXIST) { fprintf(stderr, "mkdir(\"%s\"): %s\n", tmp, strerror(errno)); return -1; } } *e = '/'; b = e+1; } return 0; } /* Create all leading directories in path */ int makeprefixpath(const char *path, mode_t m) { char tmp[PATH_MAX], *e; strncpy(tmp, path, PATH_MAX); tmp[PATH_MAX-1] = 0; normalize_path(tmp); if (!(e = strrchr(tmp, '/'))) return 0; *e = 0; return mkdir_p(tmp, m); } int question(const char *text, const char *replies) { int r = 0; FILE *f; assert(text && replies && *replies); if (!(f = fopen("/dev/tty", "r"))) f = stdin; for (;;) { const char *q; char reply[256]; if (interrupted) { fprintf(stderr, "Canceled.\n"); goto finish; } fprintf(stderr, "\r%s [", text); for (q = replies; *q; q++) { fputc(q == replies ? toupper(*q) : tolower(*q), stderr); if (*(q+1)) fputc('|', stderr); } fprintf(stderr, "] "); if (!fgets(reply, sizeof(reply), f)) goto finish; if (reply[0] == '\r' || reply[0] == '\n') reply[0] = *replies; reply[0] = tolower(reply[0]); for (q = replies; *q; q++) if (tolower(*q) == reply[0]) { r = *q; goto finish; } } finish: fputc('\r', stderr); if (f != stdin) fclose(f); return r; } int rm_rf(const char *root, int rec) { DIR *dir = NULL; int r = -1; struct dirent *de; if (!(dir = opendir(root))) { if (errno == ENOENT) { r = 0; goto finish; } fprintf(stderr, "opendir(\"%s\"): %s", root, strerror(errno)); goto finish; } while ((de = readdir(dir))) { char path[PATH_MAX]; if (!strcmp(de->d_name, ".")) continue; if (!strcmp(de->d_name, "..")) continue; snprintf(path, sizeof(path), "%s/%s", root, de->d_name); if (rec) { struct stat st; if (stat(path, &st) < 0) { fprintf(stderr, "stat(\"%s\"): %s\n", path, strerror(errno)); goto finish; } if (S_ISDIR(st.st_mode)) { if (rm_rf(path, rec) < 0) return -1; continue; } } if (unlink(path) < 0) { fprintf(stderr, "unlink(\"%s\"): %s\n", path, strerror(errno)); goto finish; } } if (rmdir(root) < 0) { fprintf(stderr, "rmdir(\"%s\"): %s\n", root, strerror(errno)); goto finish; } r = 0; finish: if (dir) closedir(dir); return r; }