aboutsummaryrefslogtreecommitdiff
path: root/src/fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs.c')
-rw-r--r--src/fs.c88
1 files changed, 88 insertions, 0 deletions
diff --git a/src/fs.c b/src/fs.c
index e4a6d7b..a1b5626 100644
--- a/src/fs.c
+++ b/src/fs.c
@@ -6,12 +6,19 @@
#include <fcntl.h>
#include <stdio.h>
#include <ctype.h>
+#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <limits.h>
+#include <dirent.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
+#ifdef __linux__
+#include <sys/sendfile.h>
+#endif
+
+#define BUFSIZE 8192
#define CONTENTDIR "content"
#define DEFAULTALBUM "unorganized"
@@ -142,3 +149,84 @@ setdatetime(const char *path, const struct timespec *mtim)
log_printl_errno(LOG_ERROR, "Warning: couldn't set times of %s", path);
}
}
+
+bool
+filesync(const char *restrict srcpath, const char *restrict dstpath)
+{
+ int fdsrc;
+ struct stat stsrc;
+
+ fdsrc = open(srcpath, O_RDONLY);
+ if (fdsrc < 0) return false;
+ if (fstat(fdsrc, &stsrc)) goto dir_error;
+
+ if (S_ISDIR(stsrc.st_mode)) {
+ if (mkdir(dstpath, 0755)) {
+ if (errno != EEXIST) goto dir_error;
+ if (stat(dstpath, &stsrc)) goto dir_error;
+ if (!S_ISDIR(stsrc.st_mode)){
+ errno = ENOTDIR;
+ goto dir_error;
+ }
+ }
+ DIR *dir = fdopendir(fdsrc);
+ if (dir == NULL) goto dir_error;
+ struct dirent *ent;
+ while ((ent = readdir(dir))) {
+ if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
+ continue;
+ }
+ char entsrc[PATH_MAX], entdst[PATH_MAX];
+ sprintf(entsrc, "%s/%s", srcpath, ent->d_name);
+ sprintf(entdst, "%s/%s", dstpath, ent->d_name);
+ if (filesync(entsrc, entdst)) {
+ closedir(dir);
+ goto dir_error;
+ }
+ }
+ closedir(dir);
+
+ goto dir_success;
+ }
+
+ int fddst, uptodate;
+ if ((uptodate = file_is_uptodate(dstpath, &stsrc.st_mtim)) > 0) {
+ goto dir_success;
+ } else if (uptodate < 0) {
+ goto dir_error;
+ }
+ fddst = open(dstpath, O_CREAT | O_WRONLY | O_TRUNC, 0644);
+ if (fddst < 0) goto dir_error;
+
+#ifdef __linux__
+ ssize_t nwrote = sendfile(fddst, fdsrc, NULL, stsrc.st_size);
+ if (nwrote != stsrc.st_size) goto copy_error;
+#else
+ char buf[BUFSIZE];
+ ssize_t nread;
+
+ while ((nread = read(fdsrc, buf, BUFSIZE)) > 0) {
+ ssize_t nwrote = write(fddst, buf, nread);
+ if (nread != nwrote) goto copy_error;
+ }
+ if (nread < 0) goto copy_error;
+#endif
+
+ struct timespec tms[] = {
+ { .tv_sec = stsrc.st_mtim.tv_sec, .tv_nsec = stsrc.st_mtim.tv_nsec },
+ { .tv_sec = stsrc.st_mtim.tv_sec, .tv_nsec = stsrc.st_mtim.tv_nsec },
+ };
+ futimens(fddst, tms);
+
+ log_printl(LOG_DETAIL, "Copied %s", srcpath);
+
+ close(fddst);
+dir_success:
+ close(fdsrc);
+ return true;
+copy_error:
+ close(fddst);
+dir_error:
+ close(fdsrc);
+ return false;
+}