diff -Nru httpdirfs-fuse-1.2.3/CHANGELOG.md httpdirfs-fuse-1.2.4/CHANGELOG.md --- httpdirfs-fuse-1.2.3/CHANGELOG.md 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/CHANGELOG.md 2023-01-11 23:56:19.000000000 +0000 @@ -5,6 +5,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [1.2.4] - 2023-01-11 + +### Added +- Add ``--cacert`` and ``--proxy-cacert`` options + +### Fixed +- ``Link_download_full``: don't ``FREE(NULL)`` +- Correct error message in ``FREE()`` +- Error handling for ``fs_open`` and ``getopt_long`` +- Fix IO error with funkwhale subsonic API +- Fix ``--insecure-tls`` in help and README ## [1.2.3] - 2021-08-31 @@ -200,7 +211,8 @@ ## [1.0] - 2018-08-22 - Initial release, everything works correctly, as far as I know. -[Unreleased]: https://github.com/fangfufu/httpdirfs/compare/1.2.3...master +[Unreleased]: https://github.com/fangfufu/httpdirfs/compare/1.2.4...master +[1.2.4]: https://github.com/fangfufu/httpdirfs/compare/1.2.3...1.2.4 [1.2.3]: https://github.com/fangfufu/httpdirfs/compare/1.2.2...1.2.3 [1.2.2]: https://github.com/fangfufu/httpdirfs/compare/1.2.1...1.2.2 [1.2.1]: https://github.com/fangfufu/httpdirfs/compare/1.2.0...1.2.1 diff -Nru httpdirfs-fuse-1.2.3/debian/changelog httpdirfs-fuse-1.2.4/debian/changelog --- httpdirfs-fuse-1.2.3/debian/changelog 2022-04-22 16:25:54.000000000 +0000 +++ httpdirfs-fuse-1.2.4/debian/changelog 2023-01-31 19:12:23.000000000 +0000 @@ -1,3 +1,23 @@ +httpdirfs-fuse (1.2.4-2) unstable; urgency=medium + + [ Amin Bandali ] + * d/rules: fix build on architectures where undefined behaviour + sanitizer is not supported. + + -- Jérôme Charaoui Tue, 31 Jan 2023 14:12:23 -0500 + +httpdirfs-fuse (1.2.4-1) unstable; urgency=medium + + * New upstream version 1.2.4 + * d/gbp.conf: add config with new debian-branch name + * d/control: fix my name + * d/control: bump Standards-Version, no changes needed + * d/rules: fix version number in Makefile + * d/watch: fix URL and patterns + * run wrap-and-sort -bastk + + -- Jérôme Charaoui Sat, 28 Jan 2023 09:34:33 -0500 + httpdirfs-fuse (1.2.3-1) unstable; urgency=medium [ Debian Janitor ] diff -Nru httpdirfs-fuse-1.2.3/debian/control httpdirfs-fuse-1.2.4/debian/control --- httpdirfs-fuse-1.2.3/debian/control 2022-04-22 16:25:54.000000000 +0000 +++ httpdirfs-fuse-1.2.4/debian/control 2023-01-31 19:12:23.000000000 +0000 @@ -1,27 +1,29 @@ Source: httpdirfs-fuse Section: utils Priority: optional -Maintainer: Jerome Charaoui -Build-Depends: debhelper-compat (= 12), - help2man, - libexpat1-dev, - libgumbo-dev, - libfuse-dev, - libssl-dev, - libcurl4-openssl-dev, - uuid-dev, - pkg-config -Standards-Version: 4.6.0.1 +Maintainer: Jérôme Charaoui +Build-Depends: + debhelper-compat (= 12), + help2man, + libcurl4-openssl-dev, + libexpat1-dev, + libfuse-dev, + libgumbo-dev, + libssl-dev, + pkg-config, + uuid-dev, +Standards-Version: 4.6.2 Homepage: https://github.com/fangfufu/httpdirfs Vcs-Git: https://salsa.debian.org/lavamind/httpdirfs-fuse.git Vcs-Browser: https://salsa.debian.org/lavamind/httpdirfs-fuse Rules-Requires-Root: no Package: httpdirfs -Architecture: linux-any kfreebsd-any -Depends: fuse, - ${misc:Depends}, - ${shlibs:Depends} +Architecture: kfreebsd-any linux-any +Depends: + fuse, + ${misc:Depends}, + ${shlibs:Depends}, Description: filesystem client for HTTP directory listings httpdirfs is program that can be used to mount HTTP directory listings (generated using an Apache DirectoryIndex, for example) as a virtual filesystem diff -Nru httpdirfs-fuse-1.2.3/debian/gbp.conf httpdirfs-fuse-1.2.4/debian/gbp.conf --- httpdirfs-fuse-1.2.3/debian/gbp.conf 1970-01-01 00:00:00.000000000 +0000 +++ httpdirfs-fuse-1.2.4/debian/gbp.conf 2023-01-31 19:12:23.000000000 +0000 @@ -0,0 +1,4 @@ +[DEFAULT] +pristine-tar = True +debian-branch = debian/latest +upstream-branch = upstream diff -Nru httpdirfs-fuse-1.2.3/debian/rules httpdirfs-fuse-1.2.4/debian/rules --- httpdirfs-fuse-1.2.3/debian/rules 2022-04-22 16:25:54.000000000 +0000 +++ httpdirfs-fuse-1.2.4/debian/rules 2023-01-31 19:12:23.000000000 +0000 @@ -1,5 +1,8 @@ #!/usr/bin/make -f +include /usr/share/dpkg/pkg-info.mk +include /usr/share/dpkg/architecture.mk + export DEB_BUILD_MAINT_OPTIONS=hardening=+all %: @@ -8,5 +11,12 @@ execute_after_dh_auto_clean: $(RM) .depend +execute_before_dh_auto_configure: + sed -i "s,^VERSION\s\+=.*,VERSION = $(DEB_VERSION_UPSTREAM)," Makefile +ifneq (,$(filter $(DEB_HOST_ARCH), mips64el mipsel alpha hppa ia64 powerpc riscv64 sh4)) +# Disable undefined behaviour sanitizer on unsupported architectures + sed -i "s/-fsanitize=undefined//" Makefile +endif + execute_after_dh_auto_build: $(MAKE) man diff -Nru httpdirfs-fuse-1.2.3/debian/tests/0001-smoketest httpdirfs-fuse-1.2.4/debian/tests/0001-smoketest --- httpdirfs-fuse-1.2.3/debian/tests/0001-smoketest 2022-04-22 16:25:54.000000000 +0000 +++ httpdirfs-fuse-1.2.4/debian/tests/0001-smoketest 2023-01-31 19:12:23.000000000 +0000 @@ -10,6 +10,9 @@ exit 0 fi +# show version number +httpdirfs --version + # prepare mkdir mnt diff -Nru httpdirfs-fuse-1.2.3/debian/tests/control httpdirfs-fuse-1.2.4/debian/tests/control --- httpdirfs-fuse-1.2.3/debian/tests/control 2022-04-22 16:25:54.000000000 +0000 +++ httpdirfs-fuse-1.2.4/debian/tests/control 2023-01-31 19:12:23.000000000 +0000 @@ -1,7 +1,7 @@ Tests: 0001-smoketest Depends: - @, - ca-certificates, - gzip, - kmod + ca-certificates, + gzip, + kmod, + @, Restrictions: allow-stderr, needs-root, needs-internet, isolation-machine diff -Nru httpdirfs-fuse-1.2.3/debian/watch httpdirfs-fuse-1.2.4/debian/watch --- httpdirfs-fuse-1.2.3/debian/watch 2022-04-22 16:25:54.000000000 +0000 +++ httpdirfs-fuse-1.2.4/debian/watch 2023-01-31 19:12:23.000000000 +0000 @@ -1,3 +1,3 @@ version=4 -opts=filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/httpdirfs-fuse-$1\.tar\.gz/ \ - https://github.com/fangfufu/httpdirfs/releases .*/v?(\d\S+)\.tar\.gz +opts=filenamemangle=s/@ANY_VERSION@\.tar\.gz/httpdirfs-fuse-$1\.tar\.gz/ \ + https://github.com/fangfufu/httpdirfs/tags (?:.*?/)?v?@ANY_VERSION@@ARCHIVE_EXT@ diff -Nru httpdirfs-fuse-1.2.3/doc/man/httpdirfs.1 httpdirfs-fuse-1.2.4/doc/man/httpdirfs.1 --- httpdirfs-fuse-1.2.3/doc/man/httpdirfs.1 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/doc/man/httpdirfs.1 1970-01-01 00:00:00.000000000 +0000 @@ -1,269 +0,0 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.1. -.TH PRINT_VERSION: "1" "August 2021" "print_version: HTTPDirFS version 1.2.3" "User Commands" -.SH NAME -print_version: \- manual page for print_version: HTTPDirFS version 1.2.3 -.SH DESCRIPTION -print_version: HTTPDirFS version 1.2.3 -print_version: libcurl SSL engine: OpenSSL/1.1.1k -usage: ./httpdirfs [options] URL mountpoint -.SS "general options:" -.TP -\fB\-\-config\fR -Specify a configuration file -.TP -\fB\-o\fR opt,[opt...] -Mount options -.TP -\fB\-h\fR \fB\-\-help\fR -Print help -.TP -\fB\-V\fR \fB\-\-version\fR -Print version -.SS "HTTPDirFS options:" -.TP -\fB\-u\fR \fB\-\-username\fR -HTTP authentication username -.TP -\fB\-p\fR \fB\-\-password\fR -HTTP authentication password -.TP -\fB\-P\fR \fB\-\-proxy\fR -Proxy for libcurl, for more details refer to -https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html -.TP -\fB\-\-proxy\-username\fR -Username for the proxy -.TP -\fB\-\-proxy\-password\fR -Password for the proxy -.TP -\fB\-\-cache\fR -Enable cache (default: off) -.TP -\fB\-\-cache\-location\fR -Set a custom cache location -(default: "${XDG_CACHE_HOME}/httpdirfs") -.TP -\fB\-\-dl\-seg\-size\fR -Set cache download segment size, in MB (default: 8) -Note: this setting is ignored if previously -cached data is found for the requested file. -.TP -\fB\-\-max\-seg\-count\fR -Set maximum number of download segments a file -can have. (default: 128*1024) -With the default setting, the maximum memory usage -per file is 128KB. This allows caching files up -to 1TB in size using the default segment size. -.TP -\fB\-\-max\-conns\fR -Set maximum number of network connections that -libcurl is allowed to make. (default: 10) -.TP -\fB\-\-retry\-wait\fR -Set delay in seconds before retrying an HTTP request -after encountering an error. (default: 5) -.TP -\fB\-\-user\-agent\fR -Set user agent string (default: "HTTPDirFS") -.TP -\fB\-\-no\-range\-check\fR -Disable the build\-in check for the server's support -for HTTP range requests -.TP -\fB\-\-insecure_tls\fR -Disable licurl TLS certificate verification by -setting CURLOPT_SSL_VERIFYHOST to 0 -.TP -\fB\-\-single\-file\-mode\fR -Single file mode \- rather than mounting a whole -directory, present a single file inside a virtual -directory. -.IP -For mounting a Airsonic / Subsonic server: -.TP -\fB\-\-sonic\-username\fR -The username for your Airsonic / Subsonic server -.TP -\fB\-\-sonic\-password\fR -The password for your Airsonic / Subsonic server -.TP -\fB\-\-sonic\-id3\fR -Enable ID3 mode \- this present the server content in -Artist/Album/Song layout -.TP -\fB\-\-sonic\-insecure\fR -Authenticate against your Airsonic / Subsonic server -using the insecure username / hex encoded password -scheme -.SS "FUSE options:" -.TP -\fB\-d\fR \fB\-o\fR debug -enable debug output (implies \fB\-f\fR) -.TP -\fB\-f\fR -foreground operation -.TP -\fB\-s\fR -disable multi\-threaded operation -.TP -\fB\-o\fR allow_other -allow access to other users -.TP -\fB\-o\fR allow_root -allow access to root -.TP -\fB\-o\fR auto_unmount -auto unmount on process termination -.TP -\fB\-o\fR nonempty -allow mounts over non\-empty file/dir -.HP -\fB\-o\fR default_permissions enable permission checking by kernel -.TP -\fB\-o\fR fsname=NAME -set filesystem name -.TP -\fB\-o\fR subtype=NAME -set filesystem type -.TP -\fB\-o\fR large_read -issue large read requests (2.4 only) -.TP -\fB\-o\fR max_read=N -set maximum size of read requests -.TP -\fB\-o\fR hard_remove -immediate removal (don't hide files) -.TP -\fB\-o\fR use_ino -let filesystem set inode numbers -.TP -\fB\-o\fR readdir_ino -try to fill in d_ino in readdir -.TP -\fB\-o\fR direct_io -use direct I/O -.TP -\fB\-o\fR kernel_cache -cache files in kernel -.TP -\fB\-o\fR [no]auto_cache -enable caching based on modification times (off) -.TP -\fB\-o\fR umask=M -set file permissions (octal) -.TP -\fB\-o\fR uid=N -set file owner -.TP -\fB\-o\fR gid=N -set file group -.TP -\fB\-o\fR entry_timeout=T -cache timeout for names (1.0s) -.TP -\fB\-o\fR negative_timeout=T -cache timeout for deleted names (0.0s) -.TP -\fB\-o\fR attr_timeout=T -cache timeout for attributes (1.0s) -.TP -\fB\-o\fR ac_attr_timeout=T -auto cache timeout for attributes (attr_timeout) -.TP -\fB\-o\fR noforget -never forget cached inodes -.TP -\fB\-o\fR remember=T -remember cached inodes for T seconds (0s) -.TP -\fB\-o\fR nopath -don't supply path if not necessary -.TP -\fB\-o\fR intr -allow requests to be interrupted -.TP -\fB\-o\fR intr_signal=NUM -signal to send on interrupt (10) -.TP -\fB\-o\fR modules=M1[:M2...] -names of modules to push onto filesystem stack -.TP -\fB\-o\fR max_write=N -set maximum size of write requests -.TP -\fB\-o\fR max_readahead=N -set maximum readahead -.TP -\fB\-o\fR max_background=N -set number of maximum background requests -.TP -\fB\-o\fR congestion_threshold=N -set kernel's congestion threshold -.TP -\fB\-o\fR async_read -perform reads asynchronously (default) -.TP -\fB\-o\fR sync_read -perform reads synchronously -.TP -\fB\-o\fR atomic_o_trunc -enable atomic open+truncate support -.TP -\fB\-o\fR big_writes -enable larger than 4kB writes -.TP -\fB\-o\fR no_remote_lock -disable remote file locking -.TP -\fB\-o\fR no_remote_flock -disable remote file locking (BSD) -.HP -\fB\-o\fR no_remote_posix_lock disable remove file locking (POSIX) -.TP -\fB\-o\fR [no_]splice_write -use splice to write to the fuse device -.TP -\fB\-o\fR [no_]splice_move -move data while splicing to the fuse device -.TP -\fB\-o\fR [no_]splice_read -use splice to read from the fuse device -.PP -Module options: -.PP -[iconv] -.TP -\fB\-o\fR from_code=CHARSET -original encoding of file names (default: UTF\-8) -.TP -\fB\-o\fR to_code=CHARSET -new encoding of the file names (default: ANSI_X3.4\-1968) -.PP -[subdir] -.TP -\fB\-o\fR subdir=DIR -prepend this directory to all paths (mandatory) -.TP -\fB\-o\fR [no]rellinks -transform absolute symlinks to relative -.PP -print_version: libcurl SSL engine: OpenSSL/1.1.1k -print_version: HTTPDirFS version 1.2.3 -print_version: libcurl SSL engine: OpenSSL/1.1.1k -FUSE library version: 2.9.9 -fusermount3 version: 3.10.3 -using FUSE kernel interface version 7.19 -.SH "SEE ALSO" -The full documentation for -.B print_version: -is maintained as a Texinfo manual. If the -.B info -and -.B print_version: -programs are properly installed at your site, the command -.IP -.B info print_version: -.PP -should give you access to the complete manual. diff -Nru httpdirfs-fuse-1.2.3/Doxyfile httpdirfs-fuse-1.2.4/Doxyfile --- httpdirfs-fuse-1.2.3/Doxyfile 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/Doxyfile 2023-01-11 23:56:19.000000000 +0000 @@ -901,7 +901,7 @@ # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = CALLOC lprintf +EXCLUDE_SYMBOLS = CALLOC lprintf FREE # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include diff -Nru httpdirfs-fuse-1.2.3/.gitignore httpdirfs-fuse-1.2.4/.gitignore --- httpdirfs-fuse-1.2.3/.gitignore 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/.gitignore 2023-01-11 23:56:19.000000000 +0000 @@ -1,6 +1,5 @@ # Binaries httpdirfs -sonicfs # Intermediates *.o @@ -14,3 +13,6 @@ .vscode *.c~ *.h~ + +#Others +mnt diff -Nru httpdirfs-fuse-1.2.3/Makefile httpdirfs-fuse-1.2.4/Makefile --- httpdirfs-fuse-1.2.3/Makefile 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/Makefile 2023-01-11 23:56:19.000000000 +0000 @@ -1,12 +1,13 @@ VERSION = 1.2.3 -CFLAGS += -O2 -Wall -Wextra -Wshadow -rdynamic -D_GNU_SOURCE\ - -D_FILE_OFFSET_BITS=64 -DVERSION=\"$(VERSION)\"\ +CFLAGS += -g -O2 -Wall -Wextra -Wshadow \ + -fsanitize=undefined -fanalyzer -Wno-analyzer-file-leak \ + -rdynamic -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -DVERSION=\"$(VERSION)\"\ `pkg-config --cflags-only-I gumbo libcurl fuse uuid expat` LDFLAGS += `pkg-config --libs-only-L gumbo libcurl fuse uuid expat` LIBS = -pthread -lgumbo -lcurl -lfuse -lcrypto -lexpat COBJS = main.o network.o fuse_local.o link.o cache.o util.o sonic.o log.o\ - config.o + config.o memcache.o OS := $(shell uname) ifeq ($(OS),Darwin) @@ -56,17 +57,20 @@ endif man: httpdirfs - help2man --no-discard-stderr ./httpdirfs > doc/man/httpdirfs.1 + mkdir -p doc/man + help2man --name "mount HTTP directory as a virtual filesystem" \ + --no-discard-stderr ./httpdirfs > doc/man/httpdirfs.1 doc: doxygen Doxyfile format: - indent -kr -nut src/*.c src/*.h + astyle --style=kr --align-pointer=name --max-code-length=80 src/*.c src/*.h clean: -rm -f src/*.h~ -rm -f src/*.c~ + -rm -f src/*orig -rm -f *.o -rm -f httpdirfs diff -Nru httpdirfs-fuse-1.2.3/README.md httpdirfs-fuse-1.2.4/README.md --- httpdirfs-fuse-1.2.3/README.md 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/README.md 2023-01-11 23:56:19.000000000 +0000 @@ -141,7 +141,7 @@ --user-agent Set user agent string (default: "HTTPDirFS") --no-range-check Disable the build-in check for the server's support for HTTP range requests - --insecure_tls Disable licurl TLS certificate verification by + --insecure-tls Disable licurl TLS certificate verification by setting CURLOPT_SSL_VERIFYHOST to 0 --single-file-mode Single file mode - rather than mounting a whole directory, present a single file inside a virtual diff -Nru httpdirfs-fuse-1.2.3/src/cache.c httpdirfs-fuse-1.2.4/src/cache.c --- httpdirfs-fuse-1.2.3/src/cache.c 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/cache.c 2023-01-11 23:56:19.000000000 +0000 @@ -1,6 +1,8 @@ #include "cache.h" + #include "config.h" #include "log.h" +#include "util.h" #include @@ -44,14 +46,14 @@ xdg_cache_home = path_append(home, xdg_cache_home_default); } if (mkdir - (xdg_cache_home, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { + (xdg_cache_home, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) + && (errno != EEXIST)) { lprintf(fatal, "mkdir(): %s\n", strerror(errno)); } char *cache_dir_root = path_append(xdg_cache_home, "/httpdirfs/"); if (mkdir - (cache_dir_root, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { + (cache_dir_root, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) + && (errno != EEXIST)) { lprintf(fatal, "mkdir(): %s\n", strerror(errno)); } @@ -75,7 +77,7 @@ char *escaped_url = curl_easy_escape(c, url, 0); char *full_path = path_append(cache_dir_root, escaped_url); if (mkdir(full_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { + && (errno != EEXIST)) { lprintf(fatal, "mkdir(): %s\n", strerror(errno)); } FREE(fn); @@ -87,6 +89,8 @@ void CacheSystem_init(const char *path, int url_supplied) { + lprintf(cache_lock_debug, + "thread %x: initialise cf_lock;\n", pthread_self()); if (pthread_mutex_init(&cf_lock, NULL)) { lprintf(fatal, "cf_lock initialisation failed!\n"); } @@ -103,12 +107,12 @@ * Check if directories exist, if not, create them */ if (mkdir(META_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { + && (errno != EEXIST)) { lprintf(fatal, "mkdir(): %s\n", strerror(errno)); } if (mkdir(DATA_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { + && (errno != EEXIST)) { lprintf(fatal, "mkdir(): %s\n", strerror(errno)); } @@ -119,8 +123,8 @@ */ sonic_path = path_append(META_DIR, "rest/"); if (mkdir - (sonic_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { + (sonic_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) + && (errno != EEXIST)) { lprintf(fatal, "mkdir(): %s\n", strerror(errno)); } FREE(sonic_path); @@ -130,8 +134,8 @@ */ sonic_path = path_append(DATA_DIR, "rest/"); if (mkdir - (sonic_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { + (sonic_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) + && (errno != EEXIST)) { lprintf(fatal, "mkdir(): %s\n", strerror(errno)); } FREE(sonic_path); @@ -144,7 +148,7 @@ * \brief read a metadata file * \return 0 on success, errno on error. */ -static int Meta_read(Cache * cf) +static int Meta_read(Cache *cf) { FILE *fp = cf->mfp; rewind(fp); @@ -232,7 +236,7 @@ * - -1 on error, * - 0 on success */ -static int Meta_write(Cache * cf) +static int Meta_write(Cache *cf) { FILE *fp = cf->mfp; rewind(fp); @@ -275,7 +279,7 @@ * \details We use sparse creation here * \return exit on failure */ -static void Data_create(Cache * cf) +static void Data_create(Cache *cf) { int fd; int mode; @@ -322,7 +326,7 @@ * - negative values on error, * - otherwise, the number of bytes read. */ -static long Data_read(Cache * cf, uint8_t * buf, off_t len, off_t offset) +static long Data_read(Cache *cf, uint8_t *buf, off_t len, off_t offset) { if (len == 0) { lprintf(error, "requested to read 0 byte!\n"); @@ -375,7 +379,7 @@ } } - end: +end: lprintf(cache_lock_debug, "thread %x: unlocking seek_lock;\n", pthread_self()); @@ -393,7 +397,7 @@ * - -1 when the data file does not exist * - otherwise, the number of bytes written. */ -static long Data_write(Cache * cf, const uint8_t * buf, off_t len, +static long Data_write(Cache *cf, const uint8_t *buf, off_t len, off_t offset) { if (len == 0) { @@ -433,7 +437,7 @@ lprintf(error, "fwrite(): encountered error!\n"); } - end: +end: lprintf(cache_lock_debug, "thread %x: unlocking seek_lock;\n", pthread_self()); PTHREAD_MUTEX_UNLOCK(&cf->seek_lock); @@ -495,7 +499,7 @@ /** * \brief free a cache data structure */ -static void Cache_free(Cache * cf) +static void Cache_free(Cache *cf) { if (pthread_mutex_destroy(&cf->seek_lock)) { lprintf(fatal, "could not destroy seek_lock!\n"); @@ -575,7 +579,7 @@ { if (CONFIG.mode == SONIC) { Link *link = path_to_Link(fn); - fn = link->sonic_id; + fn = link->sonic.id; } char *metafn = path_append(META_DIR, fn); @@ -601,18 +605,19 @@ * - 0 on success * - -1 on failure, with appropriate errno set. */ -static int Data_open(Cache * cf) +static int Data_open(Cache *cf) { char *datafn = path_append(DATA_DIR, cf->path); cf->dfp = fopen(datafn, "r+"); - FREE(datafn); if (!cf->dfp) { /* * Failed to open the data file */ lprintf(error, "fopen(%s): %s\n", datafn, strerror(errno)); + FREE(datafn); return -1; } + FREE(datafn); return 0; } @@ -622,7 +627,7 @@ * - 0 on success * - -1 on failure, with appropriate errno set. */ -static int Meta_open(Cache * cf) +static int Meta_open(Cache *cf) { char *metafn = path_append(META_DIR, cf->path); cf->mfp = fopen(metafn, "r+"); @@ -642,7 +647,7 @@ * \brief Create a metafile * \return exit on error */ -static void Meta_create(Cache * cf) +static void Meta_create(Cache *cf) { char *metafn = path_append(META_DIR, cf->path); cf->mfp = fopen(metafn, "w"); @@ -652,6 +657,11 @@ */ lprintf(fatal, "fopen(%s): %s\n", metafn, strerror(errno)); } + if (fclose(cf->mfp)) { + lprintf(error, + "cannot close metadata after creation: %s.\n", + strerror(errno)); + } FREE(metafn); } @@ -667,7 +677,7 @@ } else if (CONFIG.mode == SINGLE) { fn = curl_easy_unescape(NULL, this_link->linkname, 0, NULL); } else if (CONFIG.mode == SONIC) { - fn = this_link->sonic_id; + fn = this_link->sonic.id; } else { lprintf(fatal, "Invalid CONFIG.mode\n"); } @@ -683,12 +693,6 @@ Meta_create(cf); - if (fclose(cf->mfp)) { - lprintf(error, - "cannot close metadata after creation: %s.\n", - strerror(errno)); - } - if (Meta_open(cf)) { Cache_free(cf); lprintf(error, "cannot open metadata file, %s.\n", fn); @@ -734,8 +738,8 @@ "thread %x: locking cf_lock;\n", pthread_self()); PTHREAD_MUTEX_LOCK(&cf_lock); - if (link->cache_opened) { - link->cache_opened++; + if (link->cache_ptr) { + link->cache_ptr->cache_opened++; lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", pthread_self()); @@ -755,7 +759,7 @@ return NULL; } } else if (CONFIG.mode == SONIC) { - if (Cache_exist(link->sonic_id)) { + if (Cache_exist(link->sonic.id)) { lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", pthread_self()); @@ -781,7 +785,7 @@ * Set the path for the local cache file, if we are in sonic mode */ if (CONFIG.mode == SONIC) { - fn = link->sonic_id; + fn = link->sonic.id; } cf->path = strndup(fn, MAX_PATH_LEN); @@ -821,7 +825,8 @@ */ if (cf->content_length > Data_size(fn)) { lprintf(error, "metadata inconsistency %s, \ -cf->content_length: %ld, Data_size(fn): %ld.\n", fn, cf->content_length, Data_size(fn)); +cf->content_length: %ld, Data_size(fn): %ld.\n", fn, cf->content_length, + Data_size(fn)); Cache_free(cf); lprintf(cache_lock_debug, @@ -853,7 +858,7 @@ return NULL; } - cf->link->cache_opened = 1; + cf->cache_opened = 1; /* * Yup, we just created a circular loop. ;) */ @@ -865,15 +870,15 @@ return cf; } -void Cache_close(Cache * cf) +void Cache_close(Cache *cf) { lprintf(cache_lock_debug, "thread %x: locking cf_lock;\n", pthread_self()); PTHREAD_MUTEX_LOCK(&cf_lock); - cf->link->cache_opened--; + cf->cache_opened--; - if (cf->link->cache_opened > 0) { + if (cf->cache_opened > 0) { lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", pthread_self()); @@ -893,6 +898,8 @@ lprintf(error, "cannot close data file %s.\n", strerror(errno)); } + cf->link->cache_ptr = NULL; + lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", pthread_self()); PTHREAD_MUTEX_UNLOCK(&cf_lock); @@ -903,7 +910,7 @@ * \brief Check if a segment exists. * \return 1 if the segment exists */ -static int Seg_exist(Cache * cf, off_t offset) +static int Seg_exist(Cache *cf, off_t offset) { off_t byte = offset / cf->blksz; return cf->seg[byte]; @@ -916,7 +923,7 @@ * \param[in] i 1 for exist, 0 for doesn't exist * \note Call this after downloading a segment. */ -static void Seg_set(Cache * cf, off_t offset, int i) +static void Seg_set(Cache *cf, off_t offset, int i) { off_t byte = offset / cf->blksz; cf->seg[byte] = i; @@ -938,7 +945,7 @@ uint8_t *recv_buf = CALLOC(cf->blksz, sizeof(uint8_t)); lprintf(debug, "thread %x spawned.\n ", pthread_self()); - long recv = path_download(cf->fs_path, (char *) recv_buf, cf->blksz, + long recv = Link_download(cf->link, (char *) recv_buf, cf->blksz, cf->next_dl_offset); if (recv < 0) { lprintf(error, "thread %x received %ld bytes, \ @@ -946,8 +953,8 @@ } if ((recv == cf->blksz) || - (cf->next_dl_offset == - (cf->content_length / cf->blksz * cf->blksz))) { + (cf->next_dl_offset == + (cf->content_length / cf->blksz * cf->blksz))) { Data_write(cf, recv_buf, recv, cf->next_dl_offset); Seg_set(cf, cf->next_dl_offset, 1); } else { @@ -965,12 +972,14 @@ "thread %x: unlocking w_lock;\n", pthread_self()); PTHREAD_MUTEX_UNLOCK(&cf->w_lock); - pthread_detach(pthread_self()); + if (pthread_detach(pthread_self())) { + lprintf(error, "%s\n", strerror(errno)); + }; pthread_exit(NULL); } long -Cache_read(Cache * cf, char *const output_buf, const off_t len, +Cache_read(Cache *cf, char *const output_buf, const off_t len, const off_t offset_start) { long send; @@ -1017,7 +1026,7 @@ uint8_t *recv_buf = CALLOC(cf->blksz, sizeof(uint8_t)); lprintf(debug, "thread %x: spawned.\n ", pthread_self()); - long recv = path_download(cf->fs_path, (char *) recv_buf, cf->blksz, + long recv = Link_download(cf->link, (char *) recv_buf, cf->blksz, dl_offset); if (recv < 0) { lprintf(error, "thread %x received %ld bytes, \ @@ -1030,7 +1039,7 @@ * Condition 2: offset is the last segment */ if ((recv == cf->blksz) || - (dl_offset == (cf->content_length / cf->blksz * cf->blksz))) { + (dl_offset == (cf->content_length / cf->blksz * cf->blksz))) { Data_write(cf, recv_buf, recv, dl_offset); Seg_set(cf, dl_offset, 1); } else { @@ -1047,12 +1056,11 @@ /* * ----------- Download the next segment in background ----------------- */ - bgdl: - { +bgdl: { } off_t next_dl_offset = round_div(offset_start, cf->blksz) * cf->blksz; if ((next_dl_offset > dl_offset) && !Seg_exist(cf, next_dl_offset) - && next_dl_offset < cf->content_length) { + && next_dl_offset < cf->content_length) { /* * Stop the spawning of multiple background pthreads */ diff -Nru httpdirfs-fuse-1.2.3/src/cache.h httpdirfs-fuse-1.2.4/src/cache.h --- httpdirfs-fuse-1.2.3/src/cache.h 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/cache.h 2023-01-11 23:56:19.000000000 +0000 @@ -9,14 +9,14 @@ * separate folders. */ -#include - -/** - * \brief cache data type - */ typedef struct Cache Cache; #include "link.h" +#include "network.h" + +#include +#include +#include /** * \brief Type definition for a cache segment @@ -27,6 +27,9 @@ * \brief cache data type in-memory data structure */ struct Cache { + /** \brief How many times the cache has been opened */ + int cache_opened; + /** \brief the FILE pointer for the data file*/ FILE *dfp; /** \brief the FILE pointer for the metadata */ @@ -110,7 +113,7 @@ * \brief Close a cache data structure * \note This function is called by fs_release() */ -void Cache_close(Cache * cf); +void Cache_close(Cache *cf); /** * \brief create a cache file set if it doesn't exist already @@ -138,6 +141,6 @@ * \return the length of the segment the cache system managed to obtain. * \note Called by fs_read(), verified to be working */ -long Cache_read(Cache * cf, char *const output_buf, const off_t len, +long Cache_read(Cache *cf, char *const output_buf, const off_t len, const off_t offset_start); #endif diff -Nru httpdirfs-fuse-1.2.3/src/config.c httpdirfs-fuse-1.2.4/src/config.c --- httpdirfs-fuse-1.2.3/src/config.c 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/config.c 2023-01-11 23:56:19.000000000 +0000 @@ -70,7 +70,4 @@ CONFIG.sonic_id3 = 0; CONFIG.sonic_insecure = 0; - - /*---------- Print version number -----------*/ - print_version(); } diff -Nru httpdirfs-fuse-1.2.3/src/config.h httpdirfs-fuse-1.2.4/src/config.h --- httpdirfs-fuse-1.2.3/src/config.h 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/config.h 2023-01-11 23:56:19.000000000 +0000 @@ -53,6 +53,8 @@ char *proxy_username; /** \brief HTTP proxy password */ char *proxy_password; + /** \brief HTTP proxy certificate file */ + char *proxy_cafile; /** \brief HTTP maximum connection count */ long max_conns; /** \brief HTTP user agent*/ @@ -63,6 +65,8 @@ int no_range_check; /** \brief Disable TLS certificate verification */ int insecure_tls; + /** \brief Server certificate file */ + char *cafile; /*--------------- Cache related ---------------*/ /** \brief Whether cache mode is enabled */ int cache_enabled; diff -Nru httpdirfs-fuse-1.2.3/src/fuse_local.c httpdirfs-fuse-1.2.4/src/fuse_local.c --- httpdirfs-fuse-1.2.3/src/fuse_local.c 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/fuse_local.c 2023-01-11 23:56:19.000000000 +0000 @@ -1,6 +1,6 @@ #include "fuse_local.h" -#include "cache.h" +#include "link.h" #include "log.h" /* @@ -95,8 +95,8 @@ if (!link) { return -ENOENT; } - if ((fi->flags & 3) != O_RDONLY) { - return -EACCES; + if ((fi->flags & O_RDWR) != O_RDONLY) { + return -EROFS; } if (CACHE_SYSTEM_INIT) { fi->fh = (uint64_t) Cache_open(path); diff -Nru httpdirfs-fuse-1.2.3/src/link.c httpdirfs-fuse-1.2.4/src/link.c --- httpdirfs-fuse-1.2.3/src/link.c 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/link.c 2023-01-11 23:56:19.000000000 +0000 @@ -1,9 +1,8 @@ #include "link.h" -#include "cache.h" -#include "network.h" -#include "sonic.h" #include "log.h" +#include "memcache.h" +#include "util.h" #include @@ -42,8 +41,7 @@ /* * remove the '/' from linkname if it exists */ - char *c = - &(link->linkname[strnlen(link->linkname, MAX_FILENAME_LEN) - 1]); + char *c = &(link->linkname[strnlen(link->linkname, MAX_FILENAME_LEN) - 1]); if (*c == '/') { *c = '\0'; } @@ -51,7 +49,7 @@ return link; } -static CURL *Link_to_curl(Link * link) +static CURL *Link_to_curl(Link *link) { CURL *curl = curl_easy_init(); if (!curl) { @@ -60,64 +58,156 @@ /* * set up some basic curl stuff */ - curl_easy_setopt(curl, CURLOPT_USERAGENT, CONFIG.user_agent); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + CURLcode ret = + curl_easy_setopt(curl, CURLOPT_USERAGENT, CONFIG.user_agent); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } /* * for following directories without the '/' */ - curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 2); - curl_easy_setopt(curl, CURLOPT_URL, link->f_url); - curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1); - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15); - curl_easy_setopt(curl, CURLOPT_SHARE, CURL_SHARE); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory_callback); + ret = curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 2); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_URL, link->f_url); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_SHARE, CURL_SHARE); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory_callback); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + if (CONFIG.cafile) { + /* + * Having been given a certificate file, disable any search directory + * built into libcurl, so that we exclusively use the explicitly given + * certificate(s). + * + * If we ever add a CAPATH option, we should do the mirror for CAINFO, + * too: disable both and then enable whichever one(s) were given. + */ + ret = curl_easy_setopt(curl, CURLOPT_CAPATH, NULL); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + + ret = curl_easy_setopt(curl, CURLOPT_CAINFO, CONFIG.cafile); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + } if (CONFIG.insecure_tls) { - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); + ret = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + } + + if (CONFIG.log_type & libcurl_debug) { + ret = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } } - // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); if (CONFIG.http_username) { - curl_easy_setopt(curl, CURLOPT_USERNAME, CONFIG.http_username); + ret = curl_easy_setopt(curl, CURLOPT_USERNAME, CONFIG.http_username); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } } if (CONFIG.http_password) { - curl_easy_setopt(curl, CURLOPT_PASSWORD, CONFIG.http_password); + ret = curl_easy_setopt(curl, CURLOPT_PASSWORD, CONFIG.http_password); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } } if (CONFIG.proxy) { - curl_easy_setopt(curl, CURLOPT_PROXY, CONFIG.proxy); + ret = curl_easy_setopt(curl, CURLOPT_PROXY, CONFIG.proxy); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } } if (CONFIG.proxy_username) { - curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, - CONFIG.proxy_username); + ret = curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, + CONFIG.proxy_username); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } } if (CONFIG.proxy_password) { - curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, - CONFIG.proxy_password); + ret = curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, + CONFIG.proxy_password); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + } + + if (CONFIG.proxy_cafile) { + /* See CONFIG.cafile above */ + ret = curl_easy_setopt(curl, CURLOPT_PROXY_CAPATH, NULL); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + + ret = curl_easy_setopt(curl, CURLOPT_PROXY_CAINFO, + CONFIG.proxy_cafile); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } } return curl; } -static void Link_req_file_stat(Link * this_link) +static void Link_req_file_stat(Link *this_link) { CURL *curl = Link_to_curl(this_link); - curl_easy_setopt(curl, CURLOPT_NOBODY, 1); - curl_easy_setopt(curl, CURLOPT_FILETIME, 1L); + CURLcode ret = curl_easy_setopt(curl, CURLOPT_NOBODY, 1); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_FILETIME, 1L); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } /* * We need to put the variable on the heap, because otherwise the * variable gets popped from the stack as the function returns. * - * It gets freed in curl_multi_perform_once(); + * It gets freed in curl_process_msgs(); */ TransferStruct *transfer = CALLOC(1, sizeof(TransferStruct)); transfer->link = this_link; transfer->type = FILESTAT; - curl_easy_setopt(curl, CURLOPT_PRIVATE, transfer); + ret = curl_easy_setopt(curl, CURLOPT_PRIVATE, transfer); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } transfer_nonblocking(curl); } @@ -127,11 +217,11 @@ * \details Try and get the stats for each link in the link table. This will get * repeated until the uninitialised entry count drop to zero. */ -static void LinkTable_uninitialised_fill(LinkTable * linktbl) +static void LinkTable_uninitialised_fill(LinkTable *linktbl) { int u; char s[STATUS_LEN]; - lprintf(debug, "LinkTable_uninitialised_fill(): ... "); + lprintf(debug, " ... "); do { u = 0; for (int i = 0; i < linktbl->num; i++) { @@ -157,8 +247,7 @@ j++; } } - } - while (u); + } while (u); if (CONFIG.log_type & debug) { erase_string(stderr, STATUS_LEN, s); fprintf(stderr, "... Done!\n"); @@ -234,7 +323,7 @@ return ROOT_LINK_TBL; } -void LinkTable_add(LinkTable * linktbl, Link * link) +void LinkTable_add(LinkTable *linktbl, Link *link) { linktbl->num++; linktbl->links = @@ -285,10 +374,10 @@ * check if the link names differ by a single '/' */ if (!strncmp - (linkname, linkname_new, strnlen(linkname, MAX_FILENAME_LEN))) { + (linkname, linkname_new, strnlen(linkname, MAX_FILENAME_LEN))) { size_t linkname_new_len = strnlen(linkname_new, MAX_FILENAME_LEN); if ((linkname_new_len - strnlen(linkname, MAX_FILENAME_LEN) == 1) - && (linkname_new[linkname_new_len - 1] == '/')) { + && (linkname_new[linkname_new_len - 1] == '/')) { return 1; } } @@ -299,15 +388,15 @@ * Shamelessly copied and pasted from: * https://github.com/google/gumbo-parser/blob/master/examples/find_links.cc */ -static void HTML_to_LinkTable(GumboNode * node, LinkTable * linktbl) +static void HTML_to_LinkTable(GumboNode *node, LinkTable *linktbl) { if (node->type != GUMBO_NODE_ELEMENT) { return; } GumboAttribute *href; if (node->v.element.tag == GUMBO_TAG_A && - (href = - gumbo_get_attribute(&node->v.element.attributes, "href"))) { + (href = + gumbo_get_attribute(&node->v.element.attributes, "href"))) { /* * if it is valid, copy the link onto the heap */ @@ -322,8 +411,8 @@ comp_len--; } if (((type == LINK_DIR) || (type == LINK_UNINITIALISED_FILE)) && - !linknames_equal(linktbl->links[linktbl->num - 1]->linkname, - href->value)) { + !linknames_equal(linktbl->links[linktbl->num - 1]->linkname, + href->value)) { LinkTable_add(linktbl, Link_new(href->value, type)); } } @@ -337,14 +426,26 @@ return; } -void Link_set_file_stat(Link * this_link, CURL * curl) +void Link_set_file_stat(Link *this_link, CURL *curl) { long http_resp; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + CURLcode ret = + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } if (http_resp == HTTP_OK) { double cl = 0; - curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &cl); - curl_easy_getinfo(curl, CURLINFO_FILETIME, &(this_link->time)); + ret = + curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &cl); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = + curl_easy_getinfo(curl, CURLINFO_FILETIME, &(this_link->time)); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } if (cl <= 0) { this_link->type = LINK_INVALID; } else { @@ -362,7 +463,7 @@ } } -static void LinkTable_fill(LinkTable * linktbl) +static void LinkTable_fill(LinkTable *linktbl) { Link *head_link = linktbl->links[0]; for (int i = 1; i < linktbl->num; i++) { @@ -385,7 +486,7 @@ /** * \brief Reset invalid links in the link table */ -static void LinkTable_invalid_reset(LinkTable * linktbl) +static void LinkTable_invalid_reset(LinkTable *linktbl) { int j = 0; for (int i = 0; i < linktbl->num; i++) { @@ -398,7 +499,7 @@ lprintf(debug, "%d invalid links\n", j); } -void LinkTable_free(LinkTable * linktbl) +void LinkTable_free(LinkTable *linktbl) { for (int i = 0; i < linktbl->num; i++) { FREE(linktbl->links[i]); @@ -407,7 +508,7 @@ FREE(linktbl); } -void LinkTable_print(LinkTable * linktbl) +void LinkTable_print(LinkTable *linktbl) { if (CONFIG.log_type & info) { int j = 0; @@ -423,8 +524,8 @@ this_link->content_length, this_link->linkname, this_link->f_url); if ((this_link->type != LINK_FILE) - && (this_link->type != LINK_DIR) - && (this_link->type != LINK_HEAD)) { + && (this_link->type != LINK_DIR) + && (this_link->type != LINK_HEAD)) { j++; } } @@ -434,44 +535,6 @@ } } -DataStruct Link_to_DataStruct(Link * head_link) -{ - char *url = head_link->f_url; - CURL *curl = Link_to_curl(head_link); - - DataStruct buf; - buf.size = 0; - buf.data = NULL; - - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &buf); - - /* - * If we get temporary HTTP failure, wait for 5 seconds before retry - */ - long http_resp = 0; - do { - transfer_blocking(curl); - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); - if (HTTP_temp_failure(http_resp)) { - lprintf(warning, - "URL: %s, HTTP %ld, retrying later.\n", - url, http_resp); - sleep(CONFIG.http_wait_sec); - } else if (http_resp != HTTP_OK) { - lprintf(warning, - "cannot retrieve URL: %s, HTTP %ld\n", url, http_resp); - buf.size = 0; - curl_easy_cleanup(curl); - return buf; - } - } - while (HTTP_temp_failure(http_resp)); - - curl_easy_getinfo(curl, CURLINFO_FILETIME, &(head_link->time)); - curl_easy_cleanup(curl); - return buf; -} - LinkTable *LinkTable_alloc(const char *url) { LinkTable *linktbl = CALLOC(1, sizeof(LinkTable)); @@ -493,8 +556,8 @@ /* * start downloading the base URL */ - DataStruct buf = Link_to_DataStruct(linktbl->links[0]); - if (buf.size == 0) { + TransferStruct ts = Link_download_full(linktbl->links[0]); + if (ts.curr_size == 0) { LinkTable_free(linktbl); return NULL; } @@ -502,10 +565,10 @@ /* * Otherwise parsed the received data */ - GumboOutput *output = gumbo_parse(buf.data); + GumboOutput *output = gumbo_parse(ts.data); HTML_to_LinkTable(output->root, linktbl); gumbo_destroy_output(&kGumboDefaultOptions, output); - FREE(buf.data); + FREE(ts.data); int skip_fill = 0; char *unescaped_path; @@ -575,7 +638,7 @@ FREE(metadirn); } -int LinkTable_disk_save(LinkTable * linktbl, const char *dirn) +int LinkTable_disk_save(LinkTable *linktbl, const char *dirn) { char *metadirn = path_append(META_DIR, dirn); char *path; @@ -677,11 +740,11 @@ next_table = LinkTable_new(link->f_url); } else if (CONFIG.mode == SONIC) { if (!CONFIG.sonic_id3) { - next_table = sonic_LinkTable_new_index(link->sonic_id); + next_table = sonic_LinkTable_new_index(link->sonic.id); } else { next_table = - sonic_LinkTable_new_id3(link->sonic_depth, - link->sonic_id); + sonic_LinkTable_new_id3(link->sonic.depth, + link->sonic.id); } } else { lprintf(fatal, "Invalid CONFIG.mode\n"); @@ -691,7 +754,7 @@ return next_table; } -static Link *path_to_Link_recursive(char *path, LinkTable * linktbl) +static Link *path_to_Link_recursive(char *path, LinkTable *linktbl) { /* * skip the leading '/' if it exists @@ -715,7 +778,7 @@ */ for (int i = 1; i < linktbl->num; i++) { if (!strncmp - (path, linktbl->links[i]->linkname, MAX_FILENAME_LEN)) { + (path, linktbl->links[i]->linkname, MAX_FILENAME_LEN)) { /* * We found our link */ @@ -739,7 +802,7 @@ char *next_path = slash + 1; for (int i = 1; i < linktbl->num; i++) { if (!strncmp - (path, linktbl->links[i]->linkname, MAX_FILENAME_LEN)) { + (path, linktbl->links[i]->linkname, MAX_FILENAME_LEN)) { /* * The next sub-directory exists */ @@ -752,13 +815,13 @@ if (!CONFIG.sonic_id3) { next_table = sonic_LinkTable_new_index - (linktbl->links[i]->sonic_id); + (linktbl->links[i]->sonic.id); } else { next_table = sonic_LinkTable_new_id3 (linktbl->links - [i]->sonic_depth, - linktbl->links[i]->sonic_id); + [i]->sonic.depth, + linktbl->links[i]->sonic.id); } } else { lprintf(fatal, "Invalid CONFIG.mode\n"); @@ -791,75 +854,179 @@ return link; } -long -path_download(const char *path, char *output_buf, size_t size, - off_t offset) +TransferStruct Link_download_full(Link *link) { - if (!path) { - lprintf(fatal, "\npath_download(): NULL path supplied\n"); + char *url = link->f_url; + CURL *curl = Link_to_curl(link); + + TransferStruct ts; + ts.curr_size = 0; + ts.data = NULL; + ts.type = DATA; + ts.transferring = 1; + + CURLcode ret = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &ts); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_PRIVATE, (void *) &ts); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); } - Link *link; - link = path_to_Link(path); + + /* + * If we get temporary HTTP failure, wait for 5 seconds before retry + */ + long http_resp = 0; + do { + transfer_blocking(curl); + ret = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + if (HTTP_temp_failure(http_resp)) { + lprintf(warning, + "URL: %s, HTTP %ld, retrying later.\n", + url, http_resp); + sleep(CONFIG.http_wait_sec); + } else if (http_resp != HTTP_OK) { + lprintf(warning, + "cannot retrieve URL: %s, HTTP %ld\n", url, http_resp); + ts.curr_size = 0; + free(ts.data); /* not FREE(); can be NULL on error path! */ + curl_easy_cleanup(curl); + return ts; + } + } while (HTTP_temp_failure(http_resp)); + + ret = curl_easy_getinfo(curl, CURLINFO_FILETIME, &(link->time)); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + curl_easy_cleanup(curl); + return ts; +} + +static CURL *Link_download_curl_setup(Link *link, size_t req_size, off_t offset, + TransferStruct *header, + TransferStruct *ts) +{ if (!link) { - return -ENOENT; + lprintf(fatal, "Invalid supplied\n"); } size_t start = offset; - size_t end = start + size; + size_t end = start + req_size; + char range_str[64]; snprintf(range_str, sizeof(range_str), "%lu-%lu", start, end); - lprintf(debug, "path_download(%s, %s);\n", path, range_str); - - DataStruct buf; - buf.size = 0; - buf.data = NULL; + lprintf(debug, "%s: %s\n", link->linkname, range_str); CURL *curl = Link_to_curl(link); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &buf); - curl_easy_setopt(curl, CURLOPT_RANGE, range_str); - - DataStruct header; - header.size = 0; - header.data = NULL; - curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *) &header); + CURLcode ret = + curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *) header); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) ts); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_PRIVATE, (void *) ts); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_RANGE, range_str); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } - transfer_blocking(curl); + return curl; +} +static curl_off_t Link_download_cleanup(CURL *curl, TransferStruct *header) +{ /* * Check for range seek support */ if (!CONFIG.no_range_check) { - if (!strcasestr((header.data), "Accept-Ranges: bytes")) { + if (!strcasestr((header->data), "Accept-Ranges: bytes")) { fprintf(stderr, "This web server does not support HTTP \ range requests\n"); exit(EXIT_FAILURE); } } - FREE(header.data); + FREE(header->data); long http_resp; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + CURLcode ret = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } if (!((http_resp != HTTP_OK) || - (http_resp != HTTP_PARTIAL_CONTENT) || - (http_resp != HTTP_RANGE_NOT_SATISFIABLE))) { - lprintf(warning, - "Could not download %s, HTTP %ld\n", - link->f_url, http_resp); + (http_resp != HTTP_PARTIAL_CONTENT) || + (http_resp != HTTP_RANGE_NOT_SATISFIABLE))) { + char *url; + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); + lprintf(warning, "Could not download %s, HTTP %ld\n", url, http_resp); return -ENOENT; } - double dl; - curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &dl); - - size_t recv = dl; - if (recv > size) { - recv = size; + curl_off_t recv; + ret = curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD_T, &recv); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); } - memmove(output_buf, buf.data, recv); curl_easy_cleanup(curl); - FREE(buf.data); return recv; } + +long Link_download(Link *link, char *output_buf, size_t req_size, off_t offset) +{ + TransferStruct ts; + ts.curr_size = 0; + ts.data = NULL; + ts.type = DATA; + ts.transferring = 1; + + TransferStruct header; + header.curr_size = 0; + header.data = NULL; + + CURL *curl = Link_download_curl_setup(link, req_size, offset, &header, &ts); + + transfer_blocking(curl); + + curl_off_t recv = Link_download_cleanup(curl, &header); + + /* The extra 1 byte is probably for '\0' */ + if (recv - 1 == (long int) req_size) { + recv--; + } else if (offset + req_size < link->content_length) { + lprintf(error, "req_size: %lu, recv: %ld\n", req_size, recv); + } + + memmove(output_buf, ts.data, recv); + FREE(ts.data); + + return recv; +} + +long path_download(const char *path, char *output_buf, size_t req_size, + off_t offset) +{ + if (!path) { + lprintf(fatal, "NULL path supplied\n"); + } + + Link *link; + link = path_to_Link(path); + if (!link) { + return -ENOENT; + } + + return Link_download(link, output_buf, req_size, offset); +} diff -Nru httpdirfs-fuse-1.2.3/src/link.h httpdirfs-fuse-1.2.4/src/link.h --- httpdirfs-fuse-1.2.3/src/link.h 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/link.h 2023-01-11 23:56:19.000000000 +0000 @@ -5,16 +5,20 @@ * \file link.h * \brief link related structures and functions */ -#include "config.h" -#include "util.h" -#include -/** \brief Link type */ typedef struct Link Link; +typedef struct LinkTable LinkTable; #include "cache.h" +#include "config.h" +#include "network.h" +#include "sonic.h" -/** \brief the link type */ +#include + +/** + * \brief the link type + */ typedef enum { LINK_HEAD = 'H', LINK_DIR = 'D', @@ -23,30 +27,14 @@ LINK_UNINITIALISED_FILE = 'U' } LinkType; -/** \brief for storing downloaded data in memory */ -typedef struct { - char *data; - size_t size; -} DataStruct; - -/** \brief specify the type of data transfer */ -typedef enum { - FILESTAT = 's', - DATA = 'd' -} TransferType; - -/** \brief for storing the link being transferred, and metadata */ -typedef struct { - TransferType type; - int transferring; - Link *link; -} TransferStruct; - /** * \brief link table type * \details index 0 contains the Link for the base URL */ -typedef struct LinkTable LinkTable; +struct LinkTable { + int num; + Link **links; +}; /** * \brief Link type data structure @@ -64,31 +52,10 @@ LinkTable *next_table; /** \brief CURLINFO_FILETIME obtained from the server */ long time; - /** \brief How many times associated cache has been opened */ - int cache_opened; /** \brief The pointer associated with the cache file */ Cache *cache_ptr; - /** - * \brief Sonic id field - * \details This is used to store the followings: - * - Arist ID - * - Album ID - * - Song ID - * - Sub-directory ID (in the XML response, this is the ID on the "child" - * element) - */ - char *sonic_id; - /** - * \brief Sonic directory depth - * \details This is used exclusively in ID3 mode to store the depth of the - * current directory. - */ - int sonic_depth; -}; - -struct LinkTable { - int num; - Link **links; + /** \brief Stores *sonic related data */ + Sonic sonic; }; /** @@ -109,7 +76,7 @@ /** * \brief Set the stats of a link, after curl multi handle finished querying */ -void Link_set_file_stat(Link * this_link, CURL * curl); +void Link_set_file_stat(Link *this_link, CURL *curl); /** * \brief create a new LinkTable @@ -117,13 +84,20 @@ LinkTable *LinkTable_new(const char *url); /** - * \brief download a link + * \brief download a path * \return the number of bytes downloaded */ long path_download(const char *path, char *output_buf, size_t size, off_t offset); /** + * \brief Download a Link + * \return the number of bytes downloaded + */ +long Link_download(Link *link, char *output_buf, size_t req_size, + off_t offset); + +/** * \brief find the link associated with a path */ Link *path_to_Link(const char *path); @@ -136,7 +110,7 @@ /** * \brief dump a link table to the disk. */ -int LinkTable_disk_save(LinkTable * linktbl, const char *dirn); +int LinkTable_disk_save(LinkTable *linktbl, const char *dirn); /** * \brief load a link table from the disk. @@ -145,9 +119,9 @@ /** * \brief Download a link's content to the memory - * \warning You MUST free the memory field in DataStruct after use! + * \warning You MUST free the memory field in TransferStruct after use! */ -DataStruct Link_to_DataStruct(Link * head_link); +TransferStruct Link_download_full(Link *head_link); /** * \brief Allocate a LinkTable @@ -158,15 +132,15 @@ /** * \brief free a LinkTable */ -void LinkTable_free(LinkTable * linktbl); +void LinkTable_free(LinkTable *linktbl); /** * \brief print a LinkTable */ -void LinkTable_print(LinkTable * linktbl); +void LinkTable_print(LinkTable *linktbl); /** * \brief add a Link to a LinkTable */ -void LinkTable_add(LinkTable * linktbl, Link * link); +void LinkTable_add(LinkTable *linktbl, Link *link); #endif diff -Nru httpdirfs-fuse-1.2.3/src/log.c httpdirfs-fuse-1.2.4/src/log.c --- httpdirfs-fuse-1.2.3/src/log.c 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/log.c 2023-01-11 23:56:19.000000000 +0000 @@ -42,8 +42,7 @@ fprintf(stderr, "%s:%d:", file, line); - print_actual_message: - { +print_actual_message: { } fprintf(stderr, "%s: ", func); va_list args; @@ -60,10 +59,10 @@ void print_version() { /* FUSE prints its help to stderr */ - lprintf(info, "HTTPDirFS version " VERSION "\n"); + fprintf(stderr, "HTTPDirFS version " VERSION "\n"); /* * --------- Print off SSL engine version --------- */ curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); - lprintf(info, "libcurl SSL engine: %s\n", data->ssl_version); + fprintf(stderr, "libcurl SSL engine: %s\n", data->ssl_version); } diff -Nru httpdirfs-fuse-1.2.3/src/log.h httpdirfs-fuse-1.2.4/src/log.h --- httpdirfs-fuse-1.2.3/src/log.h 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/log.h 2023-01-11 23:56:19.000000000 +0000 @@ -13,6 +13,8 @@ link_lock_debug = 1 << 5, network_lock_debug = 1 << 6, cache_lock_debug = 1 << 7, + memcache_debug = 1 << 8, + libcurl_debug = 1 << 9, } LogType; /** diff -Nru httpdirfs-fuse-1.2.3/src/main.c httpdirfs-fuse-1.2.4/src/main.c --- httpdirfs-fuse-1.2.3/src/main.c 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/main.c 2023-01-11 23:56:19.000000000 +0000 @@ -1,8 +1,7 @@ -#include "config.h" -#include "cache.h" #include "fuse_local.h" -#include "network.h" +#include "link.h" #include "log.h" +#include "util.h" #include #include @@ -90,7 +89,7 @@ */ char *base_url = argv[argc - 2]; if (strncmp(base_url, "http://", 7) - && strncmp(base_url, "https://", 8)) { + && strncmp(base_url, "https://", 8)) { fprintf(stderr, "Error: Please supply a valid URL.\n"); print_help(argv[0], 0); exit(EXIT_FAILURE); @@ -109,7 +108,7 @@ } } - fuse_start: +fuse_start: fuse_local_init(fuse_argc, fuse_argv); return 0; @@ -144,11 +143,11 @@ char *space; space = strchr(buf, ' '); if (!space) { - *argv = realloc(*argv, *argc * sizeof(char **)); + *argv = realloc(*argv, *argc * sizeof(char *)); (*argv)[*argc - 1] = strndup(buf, buf_len); } else { (*argc)++; - *argv = realloc(*argv, *argc * sizeof(char **)); + *argv = realloc(*argv, *argc * sizeof(char *)); /* * Only copy up to the space character */ @@ -162,6 +161,7 @@ } } } + fclose(config); } FREE(full_path); } @@ -169,7 +169,7 @@ static int parse_arg_list(int argc, char **argv, char ***fuse_argv, int *fuse_argc) { - char c; + int c; int long_index = 0; const char *short_opts = "o:hVdfsp:u:P:"; const struct option long_opts[] = { @@ -199,11 +199,13 @@ { "insecure-tls", no_argument, NULL, 'L' }, /* 20 */ { "config", required_argument, NULL, 'L' }, /* 21 */ { "single-file-mode", required_argument, NULL, 'L' }, /* 22 */ + { "cacert", required_argument, NULL, 'L' }, /* 23 */ + { "proxy-cacert", required_argument, NULL, 'L' }, /* 24 */ { 0, 0, 0, 0 } }; while ((c = - getopt_long(argc, argv, short_opts, long_opts, - &long_index)) != -1) { + getopt_long(argc, argv, short_opts, long_opts, + &long_index)) != -1) { switch (c) { case 'o': add_arg(fuse_argv, fuse_argc, "-o"); @@ -296,6 +298,12 @@ case 22: CONFIG.mode = SINGLE; break; + case 23: + CONFIG.cafile = strdup(optarg); + break; + case 24: + CONFIG.proxy_cafile = strdup(optarg); + break; default: fprintf(stderr, "see httpdirfs -h for usage\n"); return 1; @@ -347,9 +355,11 @@ https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html\n\ --proxy-username Username for the proxy\n\ --proxy-password Password for the proxy\n\ + --proxy-cacert Certificate authority for the proxy\n\ --cache Enable cache (default: off)\n\ --cache-location Set a custom cache location\n\ (default: \"${XDG_CACHE_HOME}/httpdirfs\")\n\ + --cacert Certificate authority for the server\n\ --dl-seg-size Set cache download segment size, in MB (default: 8)\n\ Note: this setting is ignored if previously\n\ cached data is found for the requested file.\n\ @@ -365,7 +375,7 @@ --user-agent Set user agent string (default: \"HTTPDirFS\")\n\ --no-range-check Disable the build-in check for the server's support\n\ for HTTP range requests\n\ - --insecure_tls Disable licurl TLS certificate verification by\n\ + --insecure-tls Disable licurl TLS certificate verification by\n\ setting CURLOPT_SSL_VERIFYHOST to 0\n\ --single-file-mode Single file mode - rather than mounting a whole\n\ directory, present a single file inside a virtual\n\ diff -Nru httpdirfs-fuse-1.2.3/src/memcache.c httpdirfs-fuse-1.2.4/src/memcache.c --- httpdirfs-fuse-1.2.3/src/memcache.c 1970-01-01 00:00:00.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/memcache.c 2023-01-11 23:56:19.000000000 +0000 @@ -0,0 +1,28 @@ +#include "memcache.h" + +#include "log.h" +#include "util.h" + +#include +#include + +size_t write_memory_callback(void *recv_data, size_t size, size_t nmemb, + void *userp) +{ + size_t recv_size = size * nmemb; + TransferStruct *ts = (TransferStruct *) userp; + + ts->data = realloc(ts->data, ts->curr_size + recv_size + 1); + if (!ts->data) { + /* + * out of memory! + */ + lprintf(fatal, "realloc failure!\n"); + } + + memmove(&ts->data[ts->curr_size], recv_data, recv_size); + ts->curr_size += recv_size; + ts->data[ts->curr_size] = '\0'; + + return recv_size; +} \ No newline at end of file diff -Nru httpdirfs-fuse-1.2.3/src/memcache.h httpdirfs-fuse-1.2.4/src/memcache.h --- httpdirfs-fuse-1.2.3/src/memcache.h 1970-01-01 00:00:00.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/memcache.h 2023-01-11 23:56:19.000000000 +0000 @@ -0,0 +1,35 @@ +#ifndef memcache_H +#define memcache_H +#include "link.h" + +/** + * \brief specify the type of data transfer + */ +typedef enum { + FILESTAT = 's', + DATA = 'd' +} TransferType; + +/** + * \brief For storing transfer data and metadata + */ +struct TransferStruct { + /** \brief The array to store the data */ + char *data; + /** \brief The current size of the array */ + size_t curr_size; + /** \brief The type of transfer being done */ + TransferType type; + /** \brief Whether transfer is in progress */ + volatile int transferring; + /** \brief The link associated with the transfer */ + Link *link; +}; + +/** + * \brief Callback function for file transfer + */ +size_t write_memory_callback(void *contents, size_t size, size_t nmemb, + void *userp); + +#endif \ No newline at end of file diff -Nru httpdirfs-fuse-1.2.3/src/network.c httpdirfs-fuse-1.2.4/src/network.c --- httpdirfs-fuse-1.2.3/src/network.c 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/network.c 2023-01-11 23:56:19.000000000 +0000 @@ -1,8 +1,8 @@ #include "network.h" -#include "cache.h" -#include "config.h" #include "log.h" +#include "memcache.h" +#include "util.h" #include @@ -86,7 +86,7 @@ * https://curl.haxx.se/libcurl/c/threaded-shared-conn.html */ static void -curl_callback_lock(CURL * handle, curl_lock_data data, +curl_callback_lock(CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr) { (void) access; /* unused */ @@ -97,7 +97,7 @@ } static void -curl_callback_unlock(CURL * handle, curl_lock_data data, void *userptr) +curl_callback_unlock(CURL *handle, curl_lock_data data, void *userptr) { (void) userptr; /* unused */ (void) handle; /* unused */ @@ -111,25 +111,35 @@ * https://curl.haxx.se/libcurl/c/10-at-a-time.html */ static void -curl_process_msgs(CURLMsg * curl_msg, int n_running_curl, int n_mesgs) +curl_process_msgs(CURLMsg *curl_msg, int n_running_curl, int n_mesgs) { (void) n_running_curl; (void) n_mesgs; static volatile int slept = 0; if (curl_msg->msg == CURLMSG_DONE) { - TransferStruct *transfer; + TransferStruct *ts; CURL *curl = curl_msg->easy_handle; - curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, - &transfer); - transfer->transferring = 0; + CURLcode ret = + curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, + &ts); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ts->transferring = 0; char *url = NULL; - curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); + ret = curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } /* * Wait for 5 seconds if we get HTTP 429 */ long http_resp = 0; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + ret = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } if (HTTP_temp_failure(http_resp)) { if (!slept) { lprintf(warning, @@ -146,8 +156,8 @@ /* * Transfer successful, set the file size */ - if (transfer->type == FILESTAT) { - Link_set_file_stat(transfer->link, curl); + if (ts->type == FILESTAT) { + Link_set_file_stat(ts->link, curl); } } else { lprintf(error, "%d - %s <%s>\n", @@ -158,9 +168,9 @@ /* * clean up the handle, if we are querying the file size */ - if (transfer->type == FILESTAT) { + if (ts->type == FILESTAT) { curl_easy_cleanup(curl); - FREE(transfer); + FREE(ts); } } else { lprintf(warning, "curl_msg->msg: %d\n", curl_msg->msg); @@ -182,56 +192,13 @@ */ int n_running_curl; CURLMcode mc = curl_multi_perform(curl_multi, &n_running_curl); - if (mc > 0) { + if (mc) { lprintf(error, "%s\n", curl_multi_strerror(mc)); } - fd_set fdread; - fd_set fdwrite; - fd_set fdexcep; - int maxfd = -1; - - long curl_timeo = -1; - - FD_ZERO(&fdread); - FD_ZERO(&fdwrite); - FD_ZERO(&fdexcep); - - /* - * set a default timeout for select() - */ - struct timeval timeout; - timeout.tv_sec = 1; - timeout.tv_usec = 0; - - curl_multi_timeout(curl_multi, &curl_timeo); - /* - * We effectively cap timeout to 1 sec - */ - if (curl_timeo >= 0) { - timeout.tv_sec = curl_timeo / 1000; - if (timeout.tv_sec > 1) { - timeout.tv_sec = 1; - } else { - timeout.tv_usec = (curl_timeo % 1000) * 1000; - } - } - - /* - * get file descriptors from the transfers - */ - mc = curl_multi_fdset(curl_multi, &fdread, &fdwrite, &fdexcep, &maxfd); - - if (mc > 0) { - lprintf(error, "%s.\n", curl_multi_strerror(mc)); - } - - if (maxfd == -1) { - usleep(100 * 1000); - } else { - if (select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout) < 0) { - lprintf(error, "select(): %s.\n", strerror(errno)); - } + mc = curl_multi_poll(curl_multi, NULL, 0, 100, NULL); + if (mc) { + lprintf(error, "%s\n", curl_multi_strerror(mc)); } /* @@ -305,73 +272,46 @@ crypto_lock_init(); } -void transfer_blocking(CURL * curl) +void transfer_blocking(CURL *curl) { - /* - * We don't need to malloc here, as the transfer is finished before - * the variable gets popped from the stack - */ - volatile TransferStruct transfer; - transfer.type = DATA; - transfer.transferring = 1; - curl_easy_setopt(curl, CURLOPT_PRIVATE, &transfer); + TransferStruct *ts; + CURLcode ret = curl_easy_getinfo(curl, CURLINFO_PRIVATE, &ts); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } lprintf(network_lock_debug, "thread %x: locking transfer_lock;\n", pthread_self()); PTHREAD_MUTEX_LOCK(&transfer_lock); CURLMcode res = curl_multi_add_handle(curl_multi, curl); + if (res > 0) { + lprintf(error, "%d, %s\n", res, curl_multi_strerror(res)); + } lprintf(network_lock_debug, "thread %x: unlocking transfer_lock;\n", pthread_self()); PTHREAD_MUTEX_UNLOCK(&transfer_lock); - if (res > 0) { - lprintf(error, "%d, %s\n", res, curl_multi_strerror(res)); - } - - while (transfer.transferring) { + while (ts->transferring) { curl_multi_perform_once(); } } -void transfer_nonblocking(CURL * curl) +void transfer_nonblocking(CURL *curl) { lprintf(network_lock_debug, "thread %x: locking transfer_lock;\n", pthread_self()); PTHREAD_MUTEX_LOCK(&transfer_lock); CURLMcode res = curl_multi_add_handle(curl_multi, curl); - - lprintf(network_lock_debug, - "thread %x: unlocking transfer_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&transfer_lock); - if (res > 0) { lprintf(error, "%s\n", curl_multi_strerror(res)); } -} - -size_t -write_memory_callback(void *contents, size_t size, size_t nmemb, - void *userp) -{ - size_t realsize = size * nmemb; - DataStruct *mem = (DataStruct *) userp; - mem->data = realloc(mem->data, mem->size + realsize + 1); - if (!mem->data) { - /* - * out of memory! - */ - lprintf(fatal, "realloc failure!\n"); - } - - memmove(&mem->data[mem->size], contents, realsize); - mem->size += realsize; - mem->data[mem->size] = 0; - - return realsize; + lprintf(network_lock_debug, + "thread %x: unlocking transfer_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&transfer_lock); } int HTTP_temp_failure(HTTPResponseCode http_resp) diff -Nru httpdirfs-fuse-1.2.3/src/network.h httpdirfs-fuse-1.2.4/src/network.h --- httpdirfs-fuse-1.2.3/src/network.h 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/network.h 2023-01-11 23:56:19.000000000 +0000 @@ -6,8 +6,12 @@ * \brief network related functions */ +typedef struct TransferStruct TransferStruct; + #include "link.h" +#include + /** \brief HTTP response codes */ typedef enum { HTTP_OK = 200, @@ -28,15 +32,10 @@ void NetworkSystem_init(void); /** \brief blocking file transfer */ -void transfer_blocking(CURL * curl); +void transfer_blocking(CURL *curl); /** \brief non blocking file transfer */ -void transfer_nonblocking(CURL * curl); - -/** \brief callback function for file transfer */ -size_t -write_memory_callback(void *contents, size_t size, size_t nmemb, - void *userp); +void transfer_nonblocking(CURL *curl); /** * \brief check if a HTTP response code corresponds to a temporary failure diff -Nru httpdirfs-fuse-1.2.3/src/sonic.c httpdirfs-fuse-1.2.4/src/sonic.c --- httpdirfs-fuse-1.2.3/src/sonic.c 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/sonic.c 2023-01-11 23:56:19.000000000 +0000 @@ -1,10 +1,10 @@ #include "sonic.h" #include "config.h" -#include "util.h" -#include "link.h" -#include "network.h" #include "log.h" +#include "link.h" +#include "memcache.h" +#include "util.h" #include @@ -191,22 +191,22 @@ */ link->type = LINK_DIR; } else if (!strcmp(elem, "artist") - && linktbl->links[0]->sonic_depth != 3) { + && linktbl->links[0]->sonic.depth != 3) { /* * We want to skip the first "artist" element in the album table */ link = CALLOC(1, sizeof(Link)); link->type = LINK_DIR; } else if (!strcmp(elem, "album") - && linktbl->links[0]->sonic_depth == 3) { + && linktbl->links[0]->sonic.depth == 3) { link = CALLOC(1, sizeof(Link)); link->type = LINK_DIR; /* * The new table should be a level 4 song table */ - link->sonic_depth = 4; + link->sonic.depth = 4; } else if (!strcmp(elem, "song") - && linktbl->links[0]->sonic_depth == 4) { + && linktbl->links[0]->sonic.depth == 4) { link = CALLOC(1, sizeof(Link)); link->type = LINK_FILE; } else { @@ -224,8 +224,8 @@ char *suffix = ""; for (int i = 0; attr[i]; i += 2) { if (!strcmp("id", attr[i])) { - link->sonic_id = CALLOC(MAX_FILENAME_LEN + 1, sizeof(char)); - strncpy(link->sonic_id, attr[i + 1], MAX_FILENAME_LEN); + link->sonic.id = CALLOC(MAX_FILENAME_LEN + 1, sizeof(char)); + strncpy(link->sonic.id, attr[i + 1], MAX_FILENAME_LEN); id_set = 1; continue; } @@ -252,7 +252,7 @@ */ if (!linkname_set) { if (!strcmp("title", attr[i]) - || !strcmp("name", attr[i])) { + || !strcmp("name", attr[i])) { strncpy(link->linkname, attr[i + 1], MAX_FILENAME_LEN); linkname_set = 1; continue; @@ -296,7 +296,7 @@ } if (!linkname_set && strnlen(title, MAX_PATH_LEN) > 0 && - strnlen(suffix, MAX_PATH_LEN) > 0) { + strnlen(suffix, MAX_PATH_LEN) > 0) { snprintf(link->linkname, MAX_FILENAME_LEN, "%02d - %s.%s", track, title, suffix); linkname_set = 1; @@ -311,7 +311,7 @@ } if (link->type == LINK_FILE) { - char *url = sonic_stream_link(link->sonic_id); + char *url = sonic_stream_link(link->sonic.id); strncpy(link->f_url, url, MAX_PATH_LEN); FREE(url); } @@ -319,21 +319,44 @@ LinkTable_add(linktbl, link); } +static void sanitise_LinkTable(LinkTable *linktbl) { + for (int i = 0; i < linktbl->num; i++) { + if (!strcmp(linktbl->links[i]->linkname, ".")) { + /* Note the super long sanitised name to avoid collision */ + strcpy(linktbl->links[i]->linkname, "__DOT__"); + } + + if (!strcmp(linktbl->links[i]->linkname, "/")) { + /* Ditto */ + strcpy(linktbl->links[i]->linkname, "__FORWARD-SLASH__"); + } + + for (size_t j = 0; j < strlen(linktbl->links[i]->linkname); j++) { + if (linktbl->links[i]->linkname[j] == '/') { + linktbl->links[i]->linkname[j] = '-'; + } + } + + if (linktbl->links[i]->next_table != NULL) { + sanitise_LinkTable(linktbl->links[i]->next_table); + } + } +} + /** * \brief parse a XML string in order to fill in the LinkTable */ static LinkTable *sonic_url_to_LinkTable(const char *url, - XML_StartElementHandler handler, - int depth) + XML_StartElementHandler handler, int depth) { LinkTable *linktbl = LinkTable_alloc(url); - linktbl->links[0]->sonic_depth = depth; + linktbl->links[0]->sonic.depth = depth; /* * start downloading the base URL */ - DataStruct xml = Link_to_DataStruct(linktbl->links[0]); - if (xml.size == 0) { + TransferStruct xml = Link_download_full(linktbl->links[0]); + if (xml.curr_size == 0) { LinkTable_free(linktbl); return NULL; } @@ -343,7 +366,7 @@ XML_SetStartElementHandler(parser, handler); - if (XML_Parse(parser, xml.data, xml.size, 1) == XML_STATUS_ERROR) { + if (XML_Parse(parser, xml.data, xml.curr_size, 1) == XML_STATUS_ERROR) { lprintf(error, "Parse error at line %lu: %s\n", XML_GetCurrentLineNumber(parser), @@ -356,6 +379,8 @@ LinkTable_print(linktbl); + sanitise_LinkTable(linktbl); + return linktbl; } @@ -429,7 +454,7 @@ /* * The new table should be a level 3 album table */ - link->sonic_depth = 3; + link->sonic.depth = 3; for (int i = 0; attr[i]; i += 2) { if (!strcmp("name", attr[i])) { strncpy(link->linkname, attr[i + 1], MAX_FILENAME_LEN); @@ -438,9 +463,9 @@ } if (!strcmp("id", attr[i])) { - link->sonic_id = + link->sonic.id = CALLOC(MAX_FILENAME_LEN + 1, sizeof(char)); - strncpy(link->sonic_id, attr[i + 1], MAX_FILENAME_LEN); + strncpy(link->sonic.id, attr[i + 1], MAX_FILENAME_LEN); id_set = 1; continue; } @@ -467,25 +492,25 @@ char *url; LinkTable *linktbl = ROOT_LINK_TBL; switch (depth) { - /* - * Root table - */ + /* + * Root table + */ case 0: url = sonic_gen_url_first_part("getArtists"); linktbl = sonic_url_to_LinkTable(url, XML_parser_id3_root, 0); FREE(url); break; - /* - * Album table - get all the albums of an artist - */ + /* + * Album table - get all the albums of an artist + */ case 3: url = sonic_getArtist_link(id); linktbl = sonic_url_to_LinkTable(url, XML_parser_general, depth); FREE(url); break; - /* - * Song table - get all the songs of an album - */ + /* + * Song table - get all the songs of an album + */ case 4: url = sonic_getAlbum_link(id); linktbl = sonic_url_to_LinkTable(url, XML_parser_general, depth); @@ -498,5 +523,6 @@ lprintf(fatal, "case %d.\n", depth); break; } + return linktbl; } diff -Nru httpdirfs-fuse-1.2.3/src/sonic.h httpdirfs-fuse-1.2.4/src/sonic.h --- httpdirfs-fuse-1.2.3/src/sonic.h 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/sonic.h 2023-01-11 23:56:19.000000000 +0000 @@ -5,6 +5,25 @@ * \brief Sonic related function */ +typedef struct { + /** + * \brief Sonic id field + * \details This is used to store the followings: + * - Arist ID + * - Album ID + * - Song ID + * - Sub-directory ID (in the XML response, this is the ID on the "child" + * element) + */ + char *id; + /** + * \brief Sonic directory depth + * \details This is used exclusively in ID3 mode to store the depth of the + * current directory. + */ + int depth; +} Sonic; + #include "link.h" /** diff -Nru httpdirfs-fuse-1.2.3/src/util.c httpdirfs-fuse-1.2.4/src/util.c --- httpdirfs-fuse-1.2.3/src/util.c 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/util.c 2023-01-11 23:56:19.000000000 +0000 @@ -1,15 +1,17 @@ -#include "config.h" #include "util.h" + +#include "config.h" #include "log.h" #include #include +#include #include -#include #include #include -#include +#include + /** * \brief Backtrace buffer size @@ -31,7 +33,7 @@ { int needs_separator = 0; if ((path[strnlen(path, MAX_PATH_LEN) - 1] != '/') - && (filename[0] != '/')) { + && (filename[0] != '/')) { needs_separator = 1; } @@ -52,25 +54,23 @@ return (a + (b / 2)) / b; } -void PTHREAD_MUTEX_UNLOCK(pthread_mutex_t * x) +void PTHREAD_MUTEX_UNLOCK(pthread_mutex_t *x) { int i; i = pthread_mutex_unlock(x); if (i) { lprintf(fatal, - "thread %x: pthread_mutex_unlock() failed, %d, %s\n", - pthread_self(), i, strerror(i)); + "thread %x: %d, %s\n", pthread_self(), i, strerror(i)); } } -void PTHREAD_MUTEX_LOCK(pthread_mutex_t * x) +void PTHREAD_MUTEX_LOCK(pthread_mutex_t *x) { int i; i = pthread_mutex_lock(x); if (i) { lprintf(fatal, - "thread %x: pthread_mutex_lock() failed, %d, %s\n", - pthread_self(), i, strerror(i)); + "thread %x: %d, %s\n", pthread_self(), i, strerror(i)); } } @@ -88,7 +88,7 @@ exit(EXIT_FAILURE); } -void erase_string(FILE * file, size_t max_len, char *s) +void erase_string(FILE *file, size_t max_len, char *s) { size_t l = strnlen(s, max_len); for (size_t k = 0; k < l; k++) { @@ -142,9 +142,8 @@ { if (ptr) { free(ptr); - ptr = NULL; } else { - lprintf(fatal, "attempted to double free a pointer!\n"); + lprintf(fatal, "attempted to free NULL ptr!\n"); } } diff -Nru httpdirfs-fuse-1.2.3/src/util.h httpdirfs-fuse-1.2.4/src/util.h --- httpdirfs-fuse-1.2.3/src/util.h 2021-08-31 18:54:03.000000000 +0000 +++ httpdirfs-fuse-1.2.4/src/util.h 2023-01-11 23:56:19.000000000 +0000 @@ -25,12 +25,12 @@ /** * \brief wrapper for pthread_mutex_lock(), with error handling */ -void PTHREAD_MUTEX_LOCK(pthread_mutex_t * x); +void PTHREAD_MUTEX_LOCK(pthread_mutex_t *x); /** * \brief wrapper for pthread_mutex_unlock(), with error handling */ -void PTHREAD_MUTEX_UNLOCK(pthread_mutex_t * x); +void PTHREAD_MUTEX_UNLOCK(pthread_mutex_t *x); /** * \brief wrapper for exit(EXIT_FAILURE), with error handling @@ -40,7 +40,7 @@ /** * \brief erase a string from the terminal */ -void erase_string(FILE * file, size_t max_len, char *s); +void erase_string(FILE *file, size_t max_len, char *s); /** * \brief generate the salt for authentication string