diff --git a/src/cmd_ed2k.c b/src/cmd_ed2k.c index 4fbea6a..4ba8bb5 100644 --- a/src/cmd_ed2k.c +++ b/src/cmd_ed2k.c @@ -53,9 +53,8 @@ enum error cmd_ed2k(void *data) opts.link = *link; for (int i = 0; i < fcount; i++) { - err = ed2k_util_iterpath(config_get_nonopt(i), &ed2k_opts); - if (err != NOERR) - break; + ed2k_util_iterpath(config_get_nonopt(i), &ed2k_opts); + /* Above may fail if the path doesn't exists or smth, but still continue */ } return err; } diff --git a/src/ed2k_util.c b/src/ed2k_util.c index ce90e82..4f2e1d2 100644 --- a/src/ed2k_util.c +++ b/src/ed2k_util.c @@ -2,28 +2,27 @@ #include #include #include -#include #include +#include "util.h" #include "ed2k.h" #include "ed2k_util.h" #include "uio.h" #include "globals.h" -static struct ed2k_util_opts l_opts; - -static enum error ed2k_util_hash(const char *file_path, blksize_t blksize, - const struct stat *st) +static enum error ed2k_util_hash(const char *file_path, const struct stat *st, void *data) { + off_t blksize = st->st_blksize; unsigned char buf[blksize], hash[ED2K_HASH_SIZE]; + struct ed2k_util_opts *opts = data; struct ed2k_ctx ed2k; enum error err; FILE *f; size_t read_len; int en; - if (l_opts.pre_hash_fn) { - err = l_opts.pre_hash_fn(file_path, st, l_opts.data); + if (opts->pre_hash_fn) { + err = opts->pre_hash_fn(file_path, st, opts->data); if (err == ED2KUTIL_DONTHASH) return NOERR; else if (err != NOERR) @@ -38,7 +37,7 @@ static enum error ed2k_util_hash(const char *file_path, blksize_t blksize, if (en == EINTR && should_exit) return ERR_SHOULD_EXIT; else - return ERR_ED2KUTIL_FS; + return ERR_ITERPATH; } ed2k_init(&ed2k); @@ -54,7 +53,7 @@ static enum error ed2k_util_hash(const char *file_path, blksize_t blksize, } if (ferror(f)) { /* Loop stopped bcuz of error, not EOF */ uio_error("Failure while reading file"); - err = ERR_ED2KUTIL_FS; + err = ERR_ITERPATH; goto fail; } assert(feof(f)); @@ -67,11 +66,11 @@ static enum error ed2k_util_hash(const char *file_path, blksize_t blksize, if (en == EINTR && should_exit) return ERR_SHOULD_EXIT; else - return ERR_ED2KUTIL_FS; + return ERR_ITERPATH; } - if (l_opts.post_hash_fn) - return l_opts.post_hash_fn(file_path, hash, st, l_opts.data); + if (opts->post_hash_fn) + return opts->post_hash_fn(file_path, hash, st, opts->data); return NOERR; fail: @@ -80,46 +79,7 @@ fail: return err; } -static int ed2k_util_walk(const char *fpath, const struct stat *sb, - int typeflag, struct FTW *ftwbuf) -{ - if (typeflag == FTW_DNR) { - uio_error("Cannot read directory '%s'. Skipping", fpath); - return NOERR; - } - if (typeflag == FTW_D) - return NOERR; - if (typeflag != FTW_F) { - uio_error("Unhandled error '%d'", typeflag); - return ERR_ED2KUTIL_UNSUP; - } - - return ed2k_util_hash(fpath, sb->st_blksize, sb); -} - enum error ed2k_util_iterpath(const char *path, const struct ed2k_util_opts *opts) { - struct stat ts; - - if (stat(path, &ts) != 0) { - uio_error("Stat failed for path: '%s' (%s)", - path, strerror(errno)); - return ERR_ED2KUTIL_FS; - } - - l_opts = *opts; - - if (S_ISREG(ts.st_mode)) { - return ed2k_util_hash(path, ts.st_blksize, &ts); - } else if (S_ISDIR(ts.st_mode)) { - int ftwret = nftw(path, ed2k_util_walk, 20, 0); - if (ftwret == -1) { - uio_error("nftw failure"); - return ERR_ED2KUTIL_FS; - } - return ftwret; - } - - uio_error("Unsupported file type: %d", ts.st_mode & S_IFMT); - return ERR_ED2KUTIL_UNSUP; + return util_iterpath(path, ed2k_util_hash, (void*)opts); } diff --git a/src/error.h b/src/error.h index a5ca7a2..e6509a4 100644 --- a/src/error.h +++ b/src/error.h @@ -25,8 +25,7 @@ E(ERR_CMD_NONE) /* No command was run */ \ E(ERR_CMD_ARG) /* Some problem with the command arguments */ \ \ - E(ERR_ED2KUTIL_FS) /* Some filesystem problem */ \ - E(ERR_ED2KUTIL_UNSUP) /* Operation or file type is unsupported */ \ + E(ERR_ITERPATH) /* Couldn't start iterating over the given path */ \ E(ED2KUTIL_DONTHASH) /* Skip the hashing part. pre_hash_fn can return this */ \ \ E(ERR_API_ENCRYPTFAIL) /* Cannot start encryption with the api */ \ diff --git a/src/util.c b/src/util.c index 5904aac..821996c 100644 --- a/src/util.c +++ b/src/util.c @@ -1,9 +1,15 @@ -#define _XOPEN_SOURCE +#define _XOPEN_SOURCE 500 #include #include #include +#include +#include +#include +#include #include "util.h" +#include "error.h" +#include "uio.h" void util_byte2hex(const uint8_t* bytes, size_t bytes_len, bool uppercase, char* out) @@ -86,3 +92,62 @@ uint64_t util_iso2unix(const char *isotime) return mktime(&tm); } + +/* nftw doesn't support passing in user data to the callback function :/ */ +static util_itercb global_iterpath_cb = NULL; +static void *global_iterpath_data = NULL; + +static int util_iterpath_walk(const char *fpath, const struct stat *sb, + int typeflag, struct FTW *ftwbuf) +{ + if (typeflag == FTW_DNR) { + uio_error("Cannot read directory '%s'. Skipping", fpath); + return NOERR; + } + if (typeflag == FTW_D) + return NOERR; + if (typeflag != FTW_F) { + uio_error("Unhandled error '%d'", typeflag); + return ERR_ITERPATH; + } + + return global_iterpath_cb(fpath, sb, global_iterpath_data); +} + +enum error util_iterpath(const char *path, util_itercb cb, void *data) +{ + assert(global_iterpath_cb == NULL); + enum error ret = ERR_ITERPATH; + struct stat ts; + + if (stat(path, &ts) != 0) { + uio_error("Stat failed for path: '%s' (%s)", + path, strerror(errno)); + goto error; + } + + if (S_ISREG(ts.st_mode)) { + /* If the path is a regular file, call the cb once */ + ret = cb(path, &ts, data); + goto end; + } else if (S_ISDIR(ts.st_mode)) { + global_iterpath_cb = cb; + global_iterpath_data = data; + + /* If a directory, walk over it */ + ret = nftw(path, util_iterpath_walk, 20, 0); + if (ret == -1) { + uio_error("nftw failure"); + goto error; + } + goto end; + } + + uio_error("Unsupported file type: %d", ts.st_mode & S_IFMT); +end: + global_iterpath_cb = global_iterpath_data = NULL; + return ret; +error: + ret = ERR_ITERPATH; + goto end; +} diff --git a/src/util.h b/src/util.h index bfd5fe8..896645e 100644 --- a/src/util.h +++ b/src/util.h @@ -1,9 +1,13 @@ #ifndef _UTIL_H #define _UTIL_H +#include +#include #include #include #include +#include "error.h" + #define MS_TO_TIMESPEC(ts, ms) { \ ts->tv_sec = ms / 1000; \ ts->tv_nsec = (ms % 1000) * 1000000; \ @@ -50,4 +54,13 @@ uint64_t util_timespec_diff(const struct timespec *past, */ uint64_t util_iso2unix(const char *isotime); +/* + * Iterate over a given path and call the 'cb' function for each file + * If 'cb' returns anything other than NOERR, the iteration will stop and + * that error will be returned. + * !! THIS FUNCTION IS NOT THREAD SAFE !! + */ +typedef enum error (*util_itercb)(const char *path, const struct stat *fstat, void *data); +enum error util_iterpath(const char *path, util_itercb cb, void *data); + #endif /* _UTIL_H */