├── .bumpversion.cfg ├── .codespellrc ├── .github ├── FUNDING.yml └── workflows │ ├── CI.yml │ └── Release.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── README.md ├── openwrt ├── aliyundrive-fuse │ ├── Makefile │ └── files │ │ ├── aliyundrive-fuse.config │ │ └── aliyundrive-fuse.init └── luci-app-aliyundrive-fuse │ ├── Makefile │ ├── luasrc │ ├── controller │ │ └── aliyundrive-fuse.lua │ ├── model │ │ └── cbi │ │ │ └── aliyundrive-fuse │ │ │ ├── client.lua │ │ │ └── log.lua │ └── view │ │ └── aliyundrive-fuse │ │ ├── aliyundrive-fuse_log.htm │ │ └── aliyundrive-fuse_status.htm │ ├── po │ ├── zh-cn │ │ └── aliyundrive-fuse.po │ └── zh_Hans │ └── root │ ├── etc │ └── uci-defaults │ │ └── luci-aliyundrive-fuse │ └── usr │ └── share │ └── rpcd │ └── acl.d │ └── luci-app-aliyundrive-fuse.json ├── pyproject.toml ├── snap └── snapcraft.yaml ├── src ├── drive │ ├── mod.rs │ └── model.rs ├── error.rs ├── file_cache.rs ├── main.rs └── vfs.rs └── systemd.service /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | files = Cargo.toml README.md openwrt/aliyundrive-fuse/Makefile openwrt/luci-app-aliyundrive-fuse/Makefile snap/snapcraft.yaml 3 | commit = False 4 | tag = False 5 | current_version = 0.1.14 6 | -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | ignore-words-list = crate 3 | skip = ./.git,./target 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: messense 2 | custom: https://github.com/messense/messense/blob/master/SPONSOR.md#sponsor-my-open-source-works 3 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: CI 4 | 5 | jobs: 6 | check: 7 | name: Check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Install fuse 12 | run: | 13 | sudo apt-get update 14 | sudo apt-get install -y fuse3 libfuse3-dev 15 | - uses: actions-rs/toolchain@v1 16 | with: 17 | profile: minimal 18 | toolchain: stable 19 | override: true 20 | - uses: actions-rs/cargo@v1 21 | with: 22 | command: check 23 | args: --all 24 | 25 | test: 26 | name: Test Suite 27 | runs-on: ${{ matrix.os }} 28 | strategy: 29 | matrix: 30 | os: [ubuntu-latest] 31 | steps: 32 | - uses: actions/checkout@v2 33 | - name: Install fuse 34 | run: | 35 | sudo apt-get update 36 | sudo apt-get install -y fuse3 libfuse3-dev 37 | - uses: actions-rs/toolchain@v1 38 | with: 39 | profile: minimal 40 | toolchain: stable 41 | override: true 42 | - name: Cache cargo build 43 | uses: actions/cache@v1 44 | with: 45 | path: | 46 | ~/.cargo/registry 47 | ~/.cargo/git 48 | target 49 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 50 | - uses: actions-rs/cargo@v1 51 | with: 52 | command: test 53 | 54 | fmt: 55 | name: Rustfmt 56 | runs-on: ubuntu-latest 57 | steps: 58 | - uses: actions/checkout@v2 59 | - uses: actions-rs/toolchain@v1 60 | with: 61 | profile: minimal 62 | toolchain: stable 63 | components: rustfmt 64 | override: true 65 | - uses: actions-rs/cargo@v1 66 | with: 67 | command: fmt 68 | args: --all -- --check 69 | 70 | spellcheck: 71 | name: spellcheck 72 | runs-on: ubuntu-latest 73 | 74 | steps: 75 | - uses: actions/checkout@v2 76 | - uses: codespell-project/actions-codespell@master 77 | -------------------------------------------------------------------------------- /.github/workflows/Release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | tags: [ 'v*' ] 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | jobs: 11 | macos: 12 | runs-on: macos-latest 13 | env: 14 | PKG_CONFIG_PATH: /usr/local/lib/pkgconfig:$PKG_CONFIG_PATH 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Install macfuse 18 | run: | 19 | brew install macfuse 20 | - uses: actions/setup-python@v2 21 | with: 22 | python-version: 3.9 23 | - name: Install Rust toolchain 24 | uses: actions-rs/toolchain@v1 25 | with: 26 | toolchain: stable 27 | target: aarch64-apple-darwin 28 | profile: minimal 29 | default: true 30 | - name: Cache cargo build 31 | uses: Swatinem/rust-cache@v1 32 | - name: Build wheels - x86_64 33 | uses: messense/maturin-action@v1 34 | with: 35 | target: x86_64-apple-darwin 36 | args: --release --out dist --strip 37 | - name: Upload wheels 38 | uses: actions/upload-artifact@v2 39 | with: 40 | name: wheels 41 | path: dist 42 | if-no-files-found: error 43 | - name: Upload binary artifacts 44 | uses: actions/upload-artifact@v2 45 | with: 46 | name: apple-darwin-bin 47 | path: target/x86_64-apple-darwin/release/aliyundrive-fuse 48 | if-no-files-found: error 49 | - name: Get tag 50 | if: "startsWith(github.ref, 'refs/tags/')" 51 | id: tag 52 | uses: dawidd6/action-get-tag@v1 53 | - name: Archive binary 54 | if: "startsWith(github.ref, 'refs/tags/')" 55 | run: | 56 | cd target/x86_64-apple-darwin/release 57 | tar czvf aliyundrive-fuse-${{ steps.tag.outputs.tag }}.apple-darwin.tar.gz aliyundrive-fuse 58 | shasum -a 256 aliyundrive-fuse-${{ steps.tag.outputs.tag }}.apple-darwin.tar.gz > aliyundrive-fuse-${{ steps.tag.outputs.tag }}.apple-darwin.tar.gz.sha256 59 | cd - 60 | - name: Upload binary to GitHub Release 61 | uses: svenstaro/upload-release-action@v2 62 | if: "startsWith(github.ref, 'refs/tags/')" 63 | with: 64 | repo_token: ${{ secrets.GITHUB_TOKEN }} 65 | file: target/x86_64-apple-darwin/release/aliyundrive-fuse*.tar.gz 66 | file_glob: true 67 | overwrite: true 68 | tag: ${{ github.ref }} 69 | 70 | linux: 71 | runs-on: ubuntu-latest 72 | strategy: 73 | matrix: 74 | platform: [ 75 | { target: "x86_64-unknown-linux-musl", image_tag: "x86_64-musl", wheel: true, deb: true }, 76 | { target: "i686-unknown-linux-musl", image_tag: "i686-musl", wheel: true, deb: true }, 77 | { target: "aarch64-unknown-linux-musl", image_tag: "aarch64-musl", wheel: true, deb: true }, 78 | { target: "armv7-unknown-linux-musleabihf", image_tag: "armv7-musleabihf", wheel: true, deb: true }, 79 | { target: "armv7-unknown-linux-musleabi", image_tag: "armv7-musleabi", wheel: false, deb: true }, 80 | { target: "arm-unknown-linux-musleabihf", image_tag: "arm-musleabihf", wheel: false, deb: false }, 81 | { target: "arm-unknown-linux-musleabi", image_tag: "arm-musleabi", wheel: false, deb: false }, 82 | ] 83 | container: 84 | image: docker://messense/rust-musl-cross:${{ matrix.platform.image_tag }} 85 | env: 86 | CFLAGS_armv7_unknown_linux_musleabihf: '-mfpu=vfpv3-d16' 87 | steps: 88 | - uses: actions/checkout@v2 89 | - name: Cache cargo build 90 | uses: Swatinem/rust-cache@v1 91 | with: 92 | key: ${{ matrix.platform.target }} 93 | - name: Build wheels - manylinux 94 | uses: messense/maturin-action@v1 95 | with: 96 | target: ${{ matrix.platform.target }} 97 | manylinux: auto 98 | container: off 99 | args: --release -o dist --strip --no-default-features --features rustls-tls 100 | - name: Build wheels - musllinux 101 | if: matrix.platform.wheel 102 | uses: messense/maturin-action@v1 103 | with: 104 | target: ${{ matrix.platform.target }} 105 | manylinux: musllinux_1_1 106 | container: off 107 | args: --release -o dist --strip --no-default-features --features rustls-tls 108 | - name: Install cargo packages 109 | if: matrix.platform.deb 110 | run: | 111 | which cargo-deb > /dev/null || cargo install --target `rustc -vV | grep host: | awk '{print $2}'` cargo-deb 112 | which cargo-generate-rpm > /dev/null || cargo install --target `rustc -vV | grep host: | awk '{print $2}'` cargo-generate-rpm 113 | - name: Build Debian package 114 | if: matrix.platform.deb 115 | run: cargo deb --target=${{ matrix.platform.target }} --no-build --no-strip 116 | - name: Build RPM package 117 | if: matrix.platform.deb 118 | run: cargo generate-rpm --target=${{ matrix.platform.target }} --payload-compress none 119 | - name: Upload wheels 120 | if: matrix.platform.wheel 121 | uses: actions/upload-artifact@v2 122 | with: 123 | name: wheels 124 | path: dist 125 | if-no-files-found: error 126 | - name: Upx compress binary 127 | uses: crazy-max/ghaction-upx@v1 128 | with: 129 | version: latest 130 | files: target/${{ matrix.platform.target }}/release/aliyundrive-fuse 131 | - name: Upload binary artifacts 132 | uses: actions/upload-artifact@v2 133 | with: 134 | name: ${{ matrix.platform.target }}-bin 135 | path: target/${{ matrix.platform.target }}/release/aliyundrive-fuse 136 | if-no-files-found: error 137 | - name: Upload deb artifacts 138 | if: matrix.platform.deb 139 | uses: actions/upload-artifact@v2 140 | with: 141 | name: ${{ matrix.platform.target }}-deb 142 | path: target/${{ matrix.platform.target }}/debian/aliyundrive-fuse_*.deb 143 | if-no-files-found: error 144 | - name: Upload RPM artifacts 145 | if: matrix.platform.deb 146 | uses: actions/upload-artifact@v2 147 | with: 148 | name: ${{ matrix.platform.target }}-rpm 149 | path: target/${{ matrix.platform.target }}/generate-rpm/aliyundrive-fuse*.rpm 150 | if-no-files-found: error 151 | - name: Get tag 152 | if: "startsWith(github.ref, 'refs/tags/')" 153 | id: tag 154 | uses: dawidd6/action-get-tag@v1 155 | - name: Archive binary 156 | if: "startsWith(github.ref, 'refs/tags/')" 157 | run: | 158 | cd target/${{ matrix.platform.target }}/release 159 | tar czvf aliyundrive-fuse-${{ steps.tag.outputs.tag }}.${{ matrix.platform.target }}.tar.gz aliyundrive-fuse 160 | shasum -a 256 aliyundrive-fuse-${{ steps.tag.outputs.tag }}.${{ matrix.platform.target }}.tar.gz > aliyundrive-fuse-${{ steps.tag.outputs.tag }}.${{ matrix.platform.target }}.tar.gz.sha256 161 | cd - 162 | - name: Upload binary to GitHub Release 163 | uses: svenstaro/upload-release-action@v2 164 | if: "startsWith(github.ref, 'refs/tags/')" 165 | with: 166 | repo_token: ${{ secrets.GITHUB_TOKEN }} 167 | file: target/${{ matrix.platform.target }}/release/aliyundrive-fuse*.tar.gz* 168 | file_glob: true 169 | overwrite: true 170 | tag: ${{ github.ref }} 171 | - name: Upload deb to GitHub Release 172 | uses: svenstaro/upload-release-action@v2 173 | if: "startsWith(github.ref, 'refs/tags/') && matrix.platform.deb" 174 | with: 175 | repo_token: ${{ secrets.GITHUB_TOKEN }} 176 | file: target/${{ matrix.platform.target }}/debian/aliyundrive-fuse_*.deb 177 | file_glob: true 178 | overwrite: true 179 | tag: ${{ github.ref }} 180 | - name: Upload RPM to GitHub Release 181 | uses: svenstaro/upload-release-action@v2 182 | if: "startsWith(github.ref, 'refs/tags/') && matrix.platform.deb" 183 | with: 184 | repo_token: ${{ secrets.GITHUB_TOKEN }} 185 | file: target/${{ matrix.platform.target }}/generate-rpm/aliyundrive-fuse*.rpm 186 | file_glob: true 187 | overwrite: true 188 | tag: ${{ github.ref }} 189 | - name: Remove cached deb and RPM packages 190 | if: matrix.platform.deb 191 | run: | 192 | rm -rf target/${{ matrix.platform.target }}/debian 193 | rm -rf target/${{ matrix.platform.target }}/generate-rpm 194 | 195 | linux-others: 196 | runs-on: ubuntu-latest 197 | strategy: 198 | matrix: 199 | platform: 200 | - target: "armv5te-unknown-linux-musleabi" 201 | image_tag: "armv5te-musleabi" 202 | cargo_extra_args: --no-default-features --features rustls-tls 203 | - target: "mips-unknown-linux-musl" 204 | image_tag: "mips-musl" 205 | cargo_extra_args: --no-default-features --features native-tls-vendored 206 | - target: "mipsel-unknown-linux-musl" 207 | image_tag: "mipsel-musl" 208 | cargo_extra_args: --no-default-features --features native-tls-vendored 209 | container: 210 | image: docker://messense/rust-musl-cross:${{ matrix.platform.image_tag }} 211 | steps: 212 | - uses: actions/checkout@v2 213 | - name: Cache cargo build 214 | uses: Swatinem/rust-cache@v1 215 | with: 216 | key: ${{ matrix.platform.target }} 217 | - name: Build 218 | env: 219 | RUSTFLAGS: -C target-feature=+crt-static -C link-arg=-s 220 | run: | 221 | cargo build --release --target ${{ matrix.platform.target }} ${{ matrix.platform.cargo_extra_args }} 222 | - name: Upx compress binary 223 | uses: crazy-max/ghaction-upx@v1 224 | with: 225 | version: v3.95 # v3.96 breaks mipsel, https://github.com/upx/upx/issues/504 226 | files: target/${{ matrix.platform.target }}/release/aliyundrive-fuse 227 | - name: Upload binary artifacts 228 | uses: actions/upload-artifact@v2 229 | with: 230 | name: ${{ matrix.platform.target }}-bin 231 | path: target/${{ matrix.platform.target }}/release/aliyundrive-fuse 232 | if-no-files-found: error 233 | - name: Get tag 234 | if: "startsWith(github.ref, 'refs/tags/')" 235 | id: tag 236 | uses: dawidd6/action-get-tag@v1 237 | - name: Archive binary 238 | if: "startsWith(github.ref, 'refs/tags/')" 239 | run: | 240 | cd target/${{ matrix.platform.target }}/release 241 | tar czvf aliyundrive-fuse-${{ steps.tag.outputs.tag }}.${{ matrix.platform.target }}.tar.gz aliyundrive-fuse 242 | shasum -a 256 aliyundrive-fuse-${{ steps.tag.outputs.tag }}.${{ matrix.platform.target }}.tar.gz > aliyundrive-fuse-${{ steps.tag.outputs.tag }}.${{ matrix.platform.target }}.tar.gz.sha256 243 | cd - 244 | - name: Upload binary to GitHub Release 245 | uses: svenstaro/upload-release-action@v2 246 | if: "startsWith(github.ref, 'refs/tags/')" 247 | with: 248 | repo_token: ${{ secrets.GITHUB_TOKEN }} 249 | file: target/${{ matrix.platform.target }}/release/aliyundrive-fuse*.tar.gz* 250 | file_glob: true 251 | overwrite: true 252 | tag: ${{ github.ref }} 253 | 254 | openwrt: 255 | name: OpenWrt Package - ${{ matrix.target.arch }} 256 | runs-on: ubuntu-latest 257 | needs: [ linux, linux-others ] 258 | environment: OpenWrt 259 | strategy: 260 | matrix: 261 | target: 262 | - arch: "aarch64_generic" 263 | sdk: "https://downloads.openwrt.org/snapshots/targets/rockchip/armv8/openwrt-sdk-rockchip-armv8_gcc-11.3.0_musl.Linux-x86_64.tar.xz" 264 | - arch: "arm_cortex-a9" 265 | sdk: "https://downloads.openwrt.org/snapshots/targets/bcm53xx/generic/openwrt-sdk-bcm53xx-generic_gcc-11.3.0_musl_eabi.Linux-x86_64.tar.xz" 266 | - arch: "aarch64_cortex-a53" 267 | sdk: "https://downloads.openwrt.org/snapshots/targets/bcm27xx/bcm2710/openwrt-sdk-bcm27xx-bcm2710_gcc-11.3.0_musl.Linux-x86_64.tar.xz" 268 | - arch: "aarch64_cortex-a72" 269 | sdk: "https://downloads.openwrt.org/snapshots/targets/bcm27xx/bcm2711/openwrt-sdk-bcm27xx-bcm2711_gcc-11.3.0_musl.Linux-x86_64.tar.xz" 270 | - arch: "x86_64" 271 | sdk: "https://downloads.openwrt.org/snapshots/targets/x86/64/openwrt-sdk-x86-64_gcc-11.3.0_musl.Linux-x86_64.tar.xz" 272 | - arch: "i386_pentium4" 273 | sdk: "https://downloads.openwrt.org/snapshots/targets/x86/generic/openwrt-sdk-x86-generic_gcc-11.3.0_musl.Linux-x86_64.tar.xz" 274 | - arch: "arm_mpcore" 275 | sdk: "https://downloads.openwrt.org/snapshots/targets/oxnas/ox820/openwrt-sdk-oxnas-ox820_gcc-11.3.0_musl_eabi.Linux-x86_64.tar.xz" 276 | - arch: "arm_cortex-a5_vfpv4" 277 | sdk: "https://downloads.openwrt.org/snapshots/targets/at91/sama5/openwrt-sdk-at91-sama5_gcc-11.3.0_musl_eabi.Linux-x86_64.tar.xz" 278 | - arch: "arm_cortex-a7_neon-vfpv4" 279 | sdk: "https://downloads.openwrt.org/snapshots/targets/ipq40xx/generic/openwrt-sdk-ipq40xx-generic_gcc-11.3.0_musl_eabi.Linux-x86_64.tar.xz" 280 | - arch: "mipsel_24kc" 281 | sdk: "https://downloads.openwrt.org/snapshots/targets/ramips/mt7621/openwrt-sdk-ramips-mt7621_gcc-11.3.0_musl.Linux-x86_64.tar.xz" 282 | - arch: "mips_24kc" 283 | sdk: "https://archive.openwrt.org/releases/19.07.7/targets/ar71xx/nand/openwrt-sdk-19.07.7-ar71xx-nand_gcc-7.5.0_musl.Linux-x86_64.tar.xz" 284 | steps: 285 | - uses: actions/checkout@v2 286 | - name: Install build requirements 287 | run: | 288 | sudo apt-get update 289 | sudo apt-get install -y build-essential ccache ecj fastjar file g++ gawk \ 290 | gettext git java-propose-classpath libelf-dev libncurses5-dev \ 291 | libncursesw5-dev libssl-dev python python2.7-dev python3 unzip wget \ 292 | python3-distutils python3-setuptools python3-dev rsync subversion \ 293 | swig time xsltproc zlib1g-dev 294 | - name: Install OpenWrt SDK 295 | run: | 296 | wget -O openwrt-sdk.tar.xz ${{ matrix.target.sdk }} 297 | xz -q -d openwrt-sdk.tar.xz && tar -xvf openwrt-sdk.tar 298 | mv -f openwrt-sdk-* openwrt-sdk 299 | - name: Build Package 300 | run: | 301 | echo "src-link aliyundrive $GITHUB_WORKSPACE/openwrt" > openwrt-sdk/feeds.conf 302 | echo 'CONFIG_PACKAGE_aliyundrive-fuse=y 303 | CONFIG_PACKAGE_luci-app-aliyundrive-fuse=y 304 | ' >> openwrt-sdk/.config 305 | cd openwrt-sdk 306 | cat feeds.conf.default >> feeds.conf 307 | cat feeds.conf 308 | 309 | ./scripts/feeds update -a > /dev/null 310 | make defconfig 311 | 312 | ./scripts/feeds install -d y -f -a 313 | make package/aliyundrive-fuse/compile V=s 314 | make package/luci-app-aliyundrive-fuse/compile V=s 315 | tree bin/packages/ 316 | - name: Archive package 317 | uses: actions/upload-artifact@v2 318 | with: 319 | name: aliyundrive-fuse-openwrt-${{ matrix.target.arch }} 320 | path: openwrt-sdk/bin/packages/*/aliyundrive/aliyundrive-fuse*.ipk 321 | if-no-files-found: error 322 | - name: Archive luci packages 323 | uses: actions/upload-artifact@v2 324 | if: ${{ matrix.target.arch == 'aarch64_generic' }} 325 | with: 326 | name: aliyundrive-fuse-openwrt-${{ matrix.target.arch }} 327 | path: openwrt-sdk/bin/packages/*/aliyundrive/luci-*.ipk 328 | if-no-files-found: error 329 | - name: Upload package to GitHub Release 330 | uses: svenstaro/upload-release-action@v2 331 | if: "startsWith(github.ref, 'refs/tags/')" 332 | with: 333 | repo_token: ${{ secrets.GITHUB_TOKEN }} 334 | file_glob: true 335 | overwrite: true 336 | file: openwrt-sdk/bin/packages/*/aliyundrive/aliyundrive-fuse*.ipk 337 | tag: ${{ github.ref }} 338 | - name: Upload luci packages to GitHub Release 339 | uses: svenstaro/upload-release-action@v2 340 | if: ${{ startsWith(github.ref, 'refs/tags/') && matrix.target.arch == 'aarch64_generic' }} 341 | with: 342 | repo_token: ${{ secrets.GITHUB_TOKEN }} 343 | file_glob: true 344 | overwrite: true 345 | file: openwrt-sdk/bin/packages/*/aliyundrive/luci-*.ipk 346 | tag: ${{ github.ref }} 347 | 348 | docker: 349 | name: Build Docker Image 350 | runs-on: ubuntu-latest 351 | needs: [ linux ] 352 | environment: Docker Hub 353 | steps: 354 | - uses: actions/checkout@v2 355 | - uses: actions/download-artifact@v2 356 | with: 357 | name: x86_64-unknown-linux-musl-bin 358 | - run: | 359 | chmod a+x aliyundrive-fuse 360 | mv aliyundrive-fuse aliyundrive-fuse-amd64 361 | - uses: actions/download-artifact@v2 362 | with: 363 | name: i686-unknown-linux-musl-bin 364 | - run: | 365 | chmod a+x aliyundrive-fuse 366 | mv aliyundrive-fuse aliyundrive-fuse-386 367 | - uses: actions/download-artifact@v2 368 | with: 369 | name: aarch64-unknown-linux-musl-bin 370 | - run: | 371 | chmod a+x aliyundrive-fuse 372 | mv aliyundrive-fuse aliyundrive-fuse-arm64 373 | - uses: actions/download-artifact@v2 374 | with: 375 | name: armv7-unknown-linux-musleabihf-bin 376 | - run: | 377 | chmod a+x aliyundrive-fuse 378 | mv aliyundrive-fuse aliyundrive-fuse-armv7 379 | - uses: actions/download-artifact@v2 380 | with: 381 | name: arm-unknown-linux-musleabihf-bin 382 | - run: | 383 | chmod a+x aliyundrive-fuse 384 | mv aliyundrive-fuse aliyundrive-fuse-armv6 385 | - name: Docker meta 386 | id: meta 387 | uses: docker/metadata-action@v3 388 | with: 389 | images: | 390 | messense/aliyundrive-fuse 391 | ghcr.io/messense/aliyundrive-fuse 392 | tags: | 393 | type=schedule 394 | type=ref,event=branch 395 | type=ref,event=pr 396 | type=semver,pattern={{version}} 397 | type=semver,pattern={{major}}.{{minor}} 398 | type=semver,pattern={{major}} 399 | type=sha 400 | - name: Setup QEMU 401 | uses: dbhi/qus/action@main 402 | - name: Setup Docker Buildx 403 | uses: docker/setup-buildx-action@v1 404 | - name: Login to DockerHub 405 | if: github.event_name != 'pull_request' 406 | uses: docker/login-action@v1 407 | with: 408 | username: ${{ secrets.DOCKER_USERNAME }} 409 | password: ${{ secrets.DOCKER_TOKEN }} 410 | - name: Login to GitHub Container Registry 411 | if: github.event_name != 'pull_request' 412 | uses: docker/login-action@v2 413 | with: 414 | registry: ghcr.io 415 | username: ${{ github.repository_owner }} 416 | password: ${{ secrets.GITHUB_TOKEN }} 417 | - name: docker build 418 | uses: docker/build-push-action@v2 419 | with: 420 | context: . 421 | platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v7,linux/arm/v6 422 | push: ${{ github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) }} 423 | tags: ${{ steps.meta.outputs.tags }} 424 | labels: ${{ steps.meta.outputs.labels }} 425 | - name: Docker Hub Description 426 | if: github.event_name != 'pull_request' 427 | uses: peter-evans/dockerhub-description@v3 428 | with: 429 | username: ${{ secrets.DOCKER_USERNAME }} 430 | password: ${{ secrets.DOCKER_TOKEN }} 431 | repository: messense/aliyundrive-fuse 432 | 433 | 434 | release: 435 | name: Release 436 | runs-on: ubuntu-latest 437 | environment: 438 | name: PyPI 439 | url: https://pypi.org/project/aliyundrive-fuse/ 440 | if: "startsWith(github.ref, 'refs/tags/')" 441 | needs: [ linux, macos ] 442 | steps: 443 | - uses: actions/download-artifact@v2 444 | with: 445 | name: wheels 446 | - uses: actions/setup-python@v2 447 | with: 448 | python-version: 3.9 449 | - name: Publish to PyPI 450 | env: 451 | TWINE_USERNAME: __token__ 452 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 453 | run: | 454 | pip install --upgrade twine 455 | twine upload --skip-existing * 456 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /dist 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## v0.1.0 6 | 7 | * Initial implementation 8 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "aliyundrive-fuse" 13 | version = "0.1.14" 14 | dependencies = [ 15 | "anyhow", 16 | "bytes", 17 | "clap", 18 | "fuser", 19 | "libc", 20 | "oneshot", 21 | "openssl-probe", 22 | "parking_lot", 23 | "reqwest", 24 | "serde", 25 | "time", 26 | "tracing", 27 | "tracing-subscriber", 28 | "url", 29 | ] 30 | 31 | [[package]] 32 | name = "ansi_term" 33 | version = "0.12.1" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 36 | dependencies = [ 37 | "winapi", 38 | ] 39 | 40 | [[package]] 41 | name = "anyhow" 42 | version = "1.0.65" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" 45 | 46 | [[package]] 47 | name = "async-compression" 48 | version = "0.3.14" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695" 51 | dependencies = [ 52 | "flate2", 53 | "futures-core", 54 | "memchr", 55 | "pin-project-lite", 56 | "tokio", 57 | ] 58 | 59 | [[package]] 60 | name = "atty" 61 | version = "0.2.14" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 64 | dependencies = [ 65 | "hermit-abi", 66 | "libc", 67 | "winapi", 68 | ] 69 | 70 | [[package]] 71 | name = "autocfg" 72 | version = "1.1.0" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 75 | 76 | [[package]] 77 | name = "base64" 78 | version = "0.13.0" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 81 | 82 | [[package]] 83 | name = "bitflags" 84 | version = "1.3.2" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 87 | 88 | [[package]] 89 | name = "bumpalo" 90 | version = "3.11.0" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" 93 | 94 | [[package]] 95 | name = "byteorder" 96 | version = "1.4.3" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 99 | 100 | [[package]] 101 | name = "bytes" 102 | version = "1.2.1" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" 105 | 106 | [[package]] 107 | name = "cc" 108 | version = "1.0.73" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 111 | 112 | [[package]] 113 | name = "cfg-if" 114 | version = "1.0.0" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 117 | 118 | [[package]] 119 | name = "clap" 120 | version = "4.0.4" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "7f78ad8e84aa8e8aa3e821857be40eb4b925ff232de430d4dd2ae6aa058cbd92" 123 | dependencies = [ 124 | "atty", 125 | "bitflags", 126 | "clap_derive", 127 | "clap_lex", 128 | "once_cell", 129 | "strsim", 130 | "termcolor", 131 | "terminal_size", 132 | ] 133 | 134 | [[package]] 135 | name = "clap_derive" 136 | version = "4.0.1" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "ca689d7434ce44517a12a89456b2be4d1ea1cafcd8f581978c03d45f5a5c12a7" 139 | dependencies = [ 140 | "heck", 141 | "proc-macro-error", 142 | "proc-macro2", 143 | "quote", 144 | "syn", 145 | ] 146 | 147 | [[package]] 148 | name = "clap_lex" 149 | version = "0.3.0" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" 152 | dependencies = [ 153 | "os_str_bytes", 154 | ] 155 | 156 | [[package]] 157 | name = "core-foundation" 158 | version = "0.9.3" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 161 | dependencies = [ 162 | "core-foundation-sys", 163 | "libc", 164 | ] 165 | 166 | [[package]] 167 | name = "core-foundation-sys" 168 | version = "0.8.3" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 171 | 172 | [[package]] 173 | name = "crc32fast" 174 | version = "1.3.2" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 177 | dependencies = [ 178 | "cfg-if", 179 | ] 180 | 181 | [[package]] 182 | name = "either" 183 | version = "1.8.0" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" 186 | 187 | [[package]] 188 | name = "encoding_rs" 189 | version = "0.8.31" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" 192 | dependencies = [ 193 | "cfg-if", 194 | ] 195 | 196 | [[package]] 197 | name = "errno" 198 | version = "0.2.8" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" 201 | dependencies = [ 202 | "errno-dragonfly", 203 | "libc", 204 | "winapi", 205 | ] 206 | 207 | [[package]] 208 | name = "errno-dragonfly" 209 | version = "0.1.2" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 212 | dependencies = [ 213 | "cc", 214 | "libc", 215 | ] 216 | 217 | [[package]] 218 | name = "fastrand" 219 | version = "1.8.0" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" 222 | dependencies = [ 223 | "instant", 224 | ] 225 | 226 | [[package]] 227 | name = "flate2" 228 | version = "1.0.24" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" 231 | dependencies = [ 232 | "crc32fast", 233 | "miniz_oxide", 234 | ] 235 | 236 | [[package]] 237 | name = "fnv" 238 | version = "1.0.7" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 241 | 242 | [[package]] 243 | name = "foreign-types" 244 | version = "0.3.2" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 247 | dependencies = [ 248 | "foreign-types-shared", 249 | ] 250 | 251 | [[package]] 252 | name = "foreign-types-shared" 253 | version = "0.1.1" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 256 | 257 | [[package]] 258 | name = "form_urlencoded" 259 | version = "1.1.0" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" 262 | dependencies = [ 263 | "percent-encoding", 264 | ] 265 | 266 | [[package]] 267 | name = "fuser" 268 | version = "0.11.1" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "104ed58f182bc2975062cd3fab229e82b5762de420e26cf5645f661402694599" 271 | dependencies = [ 272 | "libc", 273 | "log", 274 | "memchr", 275 | "page_size", 276 | "pkg-config", 277 | "smallvec", 278 | "users", 279 | "zerocopy", 280 | ] 281 | 282 | [[package]] 283 | name = "futures-channel" 284 | version = "0.3.24" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" 287 | dependencies = [ 288 | "futures-core", 289 | ] 290 | 291 | [[package]] 292 | name = "futures-core" 293 | version = "0.3.24" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" 296 | 297 | [[package]] 298 | name = "futures-io" 299 | version = "0.3.24" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" 302 | 303 | [[package]] 304 | name = "futures-sink" 305 | version = "0.3.24" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" 308 | 309 | [[package]] 310 | name = "futures-task" 311 | version = "0.3.24" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" 314 | 315 | [[package]] 316 | name = "futures-util" 317 | version = "0.3.24" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" 320 | dependencies = [ 321 | "futures-core", 322 | "futures-io", 323 | "futures-task", 324 | "memchr", 325 | "pin-project-lite", 326 | "pin-utils", 327 | "slab", 328 | ] 329 | 330 | [[package]] 331 | name = "generator" 332 | version = "0.7.1" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "cc184cace1cea8335047a471cc1da80f18acf8a76f3bab2028d499e328948ec7" 335 | dependencies = [ 336 | "cc", 337 | "libc", 338 | "log", 339 | "rustversion", 340 | "windows", 341 | ] 342 | 343 | [[package]] 344 | name = "h2" 345 | version = "0.3.14" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" 348 | dependencies = [ 349 | "bytes", 350 | "fnv", 351 | "futures-core", 352 | "futures-sink", 353 | "futures-util", 354 | "http", 355 | "indexmap", 356 | "slab", 357 | "tokio", 358 | "tokio-util", 359 | "tracing", 360 | ] 361 | 362 | [[package]] 363 | name = "hashbrown" 364 | version = "0.12.3" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 367 | 368 | [[package]] 369 | name = "heck" 370 | version = "0.4.0" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 373 | 374 | [[package]] 375 | name = "hermit-abi" 376 | version = "0.1.19" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 379 | dependencies = [ 380 | "libc", 381 | ] 382 | 383 | [[package]] 384 | name = "http" 385 | version = "0.2.8" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" 388 | dependencies = [ 389 | "bytes", 390 | "fnv", 391 | "itoa", 392 | ] 393 | 394 | [[package]] 395 | name = "http-body" 396 | version = "0.4.5" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" 399 | dependencies = [ 400 | "bytes", 401 | "http", 402 | "pin-project-lite", 403 | ] 404 | 405 | [[package]] 406 | name = "httparse" 407 | version = "1.8.0" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 410 | 411 | [[package]] 412 | name = "httpdate" 413 | version = "1.0.2" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 416 | 417 | [[package]] 418 | name = "hyper" 419 | version = "0.14.20" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" 422 | dependencies = [ 423 | "bytes", 424 | "futures-channel", 425 | "futures-core", 426 | "futures-util", 427 | "h2", 428 | "http", 429 | "http-body", 430 | "httparse", 431 | "httpdate", 432 | "itoa", 433 | "pin-project-lite", 434 | "socket2", 435 | "tokio", 436 | "tower-service", 437 | "tracing", 438 | "want", 439 | ] 440 | 441 | [[package]] 442 | name = "hyper-rustls" 443 | version = "0.23.0" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" 446 | dependencies = [ 447 | "http", 448 | "hyper", 449 | "rustls", 450 | "tokio", 451 | "tokio-rustls", 452 | ] 453 | 454 | [[package]] 455 | name = "hyper-tls" 456 | version = "0.5.0" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" 459 | dependencies = [ 460 | "bytes", 461 | "hyper", 462 | "native-tls", 463 | "tokio", 464 | "tokio-native-tls", 465 | ] 466 | 467 | [[package]] 468 | name = "idna" 469 | version = "0.3.0" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" 472 | dependencies = [ 473 | "unicode-bidi", 474 | "unicode-normalization", 475 | ] 476 | 477 | [[package]] 478 | name = "indexmap" 479 | version = "1.9.1" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 482 | dependencies = [ 483 | "autocfg", 484 | "hashbrown", 485 | ] 486 | 487 | [[package]] 488 | name = "instant" 489 | version = "0.1.12" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 492 | dependencies = [ 493 | "cfg-if", 494 | ] 495 | 496 | [[package]] 497 | name = "io-lifetimes" 498 | version = "0.7.3" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "1ea37f355c05dde75b84bba2d767906ad522e97cd9e2eef2be7a4ab7fb442c06" 501 | 502 | [[package]] 503 | name = "ipnet" 504 | version = "2.5.0" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" 507 | 508 | [[package]] 509 | name = "itoa" 510 | version = "1.0.3" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" 513 | 514 | [[package]] 515 | name = "js-sys" 516 | version = "0.3.60" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" 519 | dependencies = [ 520 | "wasm-bindgen", 521 | ] 522 | 523 | [[package]] 524 | name = "lazy_static" 525 | version = "1.4.0" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 528 | 529 | [[package]] 530 | name = "libc" 531 | version = "0.2.134" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" 534 | 535 | [[package]] 536 | name = "linux-raw-sys" 537 | version = "0.0.46" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" 540 | 541 | [[package]] 542 | name = "lock_api" 543 | version = "0.4.9" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 546 | dependencies = [ 547 | "autocfg", 548 | "scopeguard", 549 | ] 550 | 551 | [[package]] 552 | name = "log" 553 | version = "0.4.17" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 556 | dependencies = [ 557 | "cfg-if", 558 | ] 559 | 560 | [[package]] 561 | name = "loom" 562 | version = "0.5.6" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" 565 | dependencies = [ 566 | "cfg-if", 567 | "generator", 568 | "pin-utils", 569 | "scoped-tls", 570 | "tracing", 571 | "tracing-subscriber", 572 | ] 573 | 574 | [[package]] 575 | name = "matchers" 576 | version = "0.1.0" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 579 | dependencies = [ 580 | "regex-automata", 581 | ] 582 | 583 | [[package]] 584 | name = "memchr" 585 | version = "2.5.0" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 588 | 589 | [[package]] 590 | name = "mime" 591 | version = "0.3.16" 592 | source = "registry+https://github.com/rust-lang/crates.io-index" 593 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 594 | 595 | [[package]] 596 | name = "miniz_oxide" 597 | version = "0.5.4" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" 600 | dependencies = [ 601 | "adler", 602 | ] 603 | 604 | [[package]] 605 | name = "mio" 606 | version = "0.8.4" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" 609 | dependencies = [ 610 | "libc", 611 | "log", 612 | "wasi", 613 | "windows-sys", 614 | ] 615 | 616 | [[package]] 617 | name = "native-tls" 618 | version = "0.2.10" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" 621 | dependencies = [ 622 | "lazy_static", 623 | "libc", 624 | "log", 625 | "openssl", 626 | "openssl-probe", 627 | "openssl-sys", 628 | "schannel", 629 | "security-framework", 630 | "security-framework-sys", 631 | "tempfile", 632 | ] 633 | 634 | [[package]] 635 | name = "num_cpus" 636 | version = "1.13.1" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 639 | dependencies = [ 640 | "hermit-abi", 641 | "libc", 642 | ] 643 | 644 | [[package]] 645 | name = "num_threads" 646 | version = "0.1.6" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" 649 | dependencies = [ 650 | "libc", 651 | ] 652 | 653 | [[package]] 654 | name = "once_cell" 655 | version = "1.15.0" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" 658 | 659 | [[package]] 660 | name = "oneshot" 661 | version = "0.1.5" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | checksum = "fc22d22931513428ea6cc089e942d38600e3d00976eef8c86de6b8a3aadec6eb" 664 | dependencies = [ 665 | "loom", 666 | ] 667 | 668 | [[package]] 669 | name = "openssl" 670 | version = "0.10.42" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" 673 | dependencies = [ 674 | "bitflags", 675 | "cfg-if", 676 | "foreign-types", 677 | "libc", 678 | "once_cell", 679 | "openssl-macros", 680 | "openssl-sys", 681 | ] 682 | 683 | [[package]] 684 | name = "openssl-macros" 685 | version = "0.1.0" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" 688 | dependencies = [ 689 | "proc-macro2", 690 | "quote", 691 | "syn", 692 | ] 693 | 694 | [[package]] 695 | name = "openssl-probe" 696 | version = "0.1.5" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 699 | 700 | [[package]] 701 | name = "openssl-src" 702 | version = "111.22.0+1.1.1q" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853" 705 | dependencies = [ 706 | "cc", 707 | ] 708 | 709 | [[package]] 710 | name = "openssl-sys" 711 | version = "0.9.76" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" 714 | dependencies = [ 715 | "autocfg", 716 | "cc", 717 | "libc", 718 | "openssl-src", 719 | "pkg-config", 720 | "vcpkg", 721 | ] 722 | 723 | [[package]] 724 | name = "os_str_bytes" 725 | version = "6.3.0" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" 728 | 729 | [[package]] 730 | name = "page_size" 731 | version = "0.4.2" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd" 734 | dependencies = [ 735 | "libc", 736 | "winapi", 737 | ] 738 | 739 | [[package]] 740 | name = "parking_lot" 741 | version = "0.12.1" 742 | source = "registry+https://github.com/rust-lang/crates.io-index" 743 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 744 | dependencies = [ 745 | "lock_api", 746 | "parking_lot_core", 747 | ] 748 | 749 | [[package]] 750 | name = "parking_lot_core" 751 | version = "0.9.3" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" 754 | dependencies = [ 755 | "cfg-if", 756 | "libc", 757 | "redox_syscall", 758 | "smallvec", 759 | "windows-sys", 760 | ] 761 | 762 | [[package]] 763 | name = "percent-encoding" 764 | version = "2.2.0" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 767 | 768 | [[package]] 769 | name = "pin-project-lite" 770 | version = "0.2.9" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 773 | 774 | [[package]] 775 | name = "pin-utils" 776 | version = "0.1.0" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 779 | 780 | [[package]] 781 | name = "pkg-config" 782 | version = "0.3.25" 783 | source = "registry+https://github.com/rust-lang/crates.io-index" 784 | checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" 785 | 786 | [[package]] 787 | name = "proc-macro-error" 788 | version = "1.0.4" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 791 | dependencies = [ 792 | "proc-macro-error-attr", 793 | "proc-macro2", 794 | "quote", 795 | "syn", 796 | "version_check", 797 | ] 798 | 799 | [[package]] 800 | name = "proc-macro-error-attr" 801 | version = "1.0.4" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 804 | dependencies = [ 805 | "proc-macro2", 806 | "quote", 807 | "version_check", 808 | ] 809 | 810 | [[package]] 811 | name = "proc-macro2" 812 | version = "1.0.46" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" 815 | dependencies = [ 816 | "unicode-ident", 817 | ] 818 | 819 | [[package]] 820 | name = "quote" 821 | version = "1.0.21" 822 | source = "registry+https://github.com/rust-lang/crates.io-index" 823 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 824 | dependencies = [ 825 | "proc-macro2", 826 | ] 827 | 828 | [[package]] 829 | name = "redox_syscall" 830 | version = "0.2.16" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 833 | dependencies = [ 834 | "bitflags", 835 | ] 836 | 837 | [[package]] 838 | name = "regex" 839 | version = "1.6.0" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" 842 | dependencies = [ 843 | "regex-syntax", 844 | ] 845 | 846 | [[package]] 847 | name = "regex-automata" 848 | version = "0.1.10" 849 | source = "registry+https://github.com/rust-lang/crates.io-index" 850 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 851 | dependencies = [ 852 | "regex-syntax", 853 | ] 854 | 855 | [[package]] 856 | name = "regex-syntax" 857 | version = "0.6.27" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" 860 | 861 | [[package]] 862 | name = "remove_dir_all" 863 | version = "0.5.3" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 866 | dependencies = [ 867 | "winapi", 868 | ] 869 | 870 | [[package]] 871 | name = "reqwest" 872 | version = "0.11.12" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" 875 | dependencies = [ 876 | "async-compression", 877 | "base64", 878 | "bytes", 879 | "encoding_rs", 880 | "futures-core", 881 | "futures-util", 882 | "h2", 883 | "http", 884 | "http-body", 885 | "hyper", 886 | "hyper-rustls", 887 | "hyper-tls", 888 | "ipnet", 889 | "js-sys", 890 | "log", 891 | "mime", 892 | "native-tls", 893 | "once_cell", 894 | "percent-encoding", 895 | "pin-project-lite", 896 | "rustls", 897 | "rustls-pemfile", 898 | "serde", 899 | "serde_json", 900 | "serde_urlencoded", 901 | "tokio", 902 | "tokio-native-tls", 903 | "tokio-rustls", 904 | "tokio-socks", 905 | "tokio-util", 906 | "tower-service", 907 | "url", 908 | "wasm-bindgen", 909 | "wasm-bindgen-futures", 910 | "web-sys", 911 | "webpki-roots", 912 | "winreg", 913 | ] 914 | 915 | [[package]] 916 | name = "ring" 917 | version = "0.16.20" 918 | source = "registry+https://github.com/rust-lang/crates.io-index" 919 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" 920 | dependencies = [ 921 | "cc", 922 | "libc", 923 | "once_cell", 924 | "spin", 925 | "untrusted", 926 | "web-sys", 927 | "winapi", 928 | ] 929 | 930 | [[package]] 931 | name = "rustix" 932 | version = "0.35.11" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | checksum = "fbb2fda4666def1433b1b05431ab402e42a1084285477222b72d6c564c417cef" 935 | dependencies = [ 936 | "bitflags", 937 | "errno", 938 | "io-lifetimes", 939 | "libc", 940 | "linux-raw-sys", 941 | "windows-sys", 942 | ] 943 | 944 | [[package]] 945 | name = "rustls" 946 | version = "0.20.6" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" 949 | dependencies = [ 950 | "log", 951 | "ring", 952 | "sct", 953 | "webpki", 954 | ] 955 | 956 | [[package]] 957 | name = "rustls-pemfile" 958 | version = "1.0.1" 959 | source = "registry+https://github.com/rust-lang/crates.io-index" 960 | checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" 961 | dependencies = [ 962 | "base64", 963 | ] 964 | 965 | [[package]] 966 | name = "rustversion" 967 | version = "1.0.9" 968 | source = "registry+https://github.com/rust-lang/crates.io-index" 969 | checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" 970 | 971 | [[package]] 972 | name = "ryu" 973 | version = "1.0.11" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 976 | 977 | [[package]] 978 | name = "schannel" 979 | version = "0.1.20" 980 | source = "registry+https://github.com/rust-lang/crates.io-index" 981 | checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" 982 | dependencies = [ 983 | "lazy_static", 984 | "windows-sys", 985 | ] 986 | 987 | [[package]] 988 | name = "scoped-tls" 989 | version = "1.0.0" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" 992 | 993 | [[package]] 994 | name = "scopeguard" 995 | version = "1.1.0" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 998 | 999 | [[package]] 1000 | name = "sct" 1001 | version = "0.7.0" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" 1004 | dependencies = [ 1005 | "ring", 1006 | "untrusted", 1007 | ] 1008 | 1009 | [[package]] 1010 | name = "security-framework" 1011 | version = "2.7.0" 1012 | source = "registry+https://github.com/rust-lang/crates.io-index" 1013 | checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" 1014 | dependencies = [ 1015 | "bitflags", 1016 | "core-foundation", 1017 | "core-foundation-sys", 1018 | "libc", 1019 | "security-framework-sys", 1020 | ] 1021 | 1022 | [[package]] 1023 | name = "security-framework-sys" 1024 | version = "2.6.1" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" 1027 | dependencies = [ 1028 | "core-foundation-sys", 1029 | "libc", 1030 | ] 1031 | 1032 | [[package]] 1033 | name = "serde" 1034 | version = "1.0.145" 1035 | source = "registry+https://github.com/rust-lang/crates.io-index" 1036 | checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" 1037 | dependencies = [ 1038 | "serde_derive", 1039 | ] 1040 | 1041 | [[package]] 1042 | name = "serde_derive" 1043 | version = "1.0.145" 1044 | source = "registry+https://github.com/rust-lang/crates.io-index" 1045 | checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" 1046 | dependencies = [ 1047 | "proc-macro2", 1048 | "quote", 1049 | "syn", 1050 | ] 1051 | 1052 | [[package]] 1053 | name = "serde_json" 1054 | version = "1.0.85" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" 1057 | dependencies = [ 1058 | "itoa", 1059 | "ryu", 1060 | "serde", 1061 | ] 1062 | 1063 | [[package]] 1064 | name = "serde_urlencoded" 1065 | version = "0.7.1" 1066 | source = "registry+https://github.com/rust-lang/crates.io-index" 1067 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1068 | dependencies = [ 1069 | "form_urlencoded", 1070 | "itoa", 1071 | "ryu", 1072 | "serde", 1073 | ] 1074 | 1075 | [[package]] 1076 | name = "sharded-slab" 1077 | version = "0.1.4" 1078 | source = "registry+https://github.com/rust-lang/crates.io-index" 1079 | checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" 1080 | dependencies = [ 1081 | "lazy_static", 1082 | ] 1083 | 1084 | [[package]] 1085 | name = "slab" 1086 | version = "0.4.7" 1087 | source = "registry+https://github.com/rust-lang/crates.io-index" 1088 | checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" 1089 | dependencies = [ 1090 | "autocfg", 1091 | ] 1092 | 1093 | [[package]] 1094 | name = "smallvec" 1095 | version = "1.9.0" 1096 | source = "registry+https://github.com/rust-lang/crates.io-index" 1097 | checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" 1098 | 1099 | [[package]] 1100 | name = "socket2" 1101 | version = "0.4.7" 1102 | source = "registry+https://github.com/rust-lang/crates.io-index" 1103 | checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" 1104 | dependencies = [ 1105 | "libc", 1106 | "winapi", 1107 | ] 1108 | 1109 | [[package]] 1110 | name = "spin" 1111 | version = "0.5.2" 1112 | source = "registry+https://github.com/rust-lang/crates.io-index" 1113 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 1114 | 1115 | [[package]] 1116 | name = "strsim" 1117 | version = "0.10.0" 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" 1119 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1120 | 1121 | [[package]] 1122 | name = "syn" 1123 | version = "1.0.101" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" 1126 | dependencies = [ 1127 | "proc-macro2", 1128 | "quote", 1129 | "unicode-ident", 1130 | ] 1131 | 1132 | [[package]] 1133 | name = "synstructure" 1134 | version = "0.12.6" 1135 | source = "registry+https://github.com/rust-lang/crates.io-index" 1136 | checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" 1137 | dependencies = [ 1138 | "proc-macro2", 1139 | "quote", 1140 | "syn", 1141 | "unicode-xid", 1142 | ] 1143 | 1144 | [[package]] 1145 | name = "tempfile" 1146 | version = "3.3.0" 1147 | source = "registry+https://github.com/rust-lang/crates.io-index" 1148 | checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 1149 | dependencies = [ 1150 | "cfg-if", 1151 | "fastrand", 1152 | "libc", 1153 | "redox_syscall", 1154 | "remove_dir_all", 1155 | "winapi", 1156 | ] 1157 | 1158 | [[package]] 1159 | name = "termcolor" 1160 | version = "1.1.3" 1161 | source = "registry+https://github.com/rust-lang/crates.io-index" 1162 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 1163 | dependencies = [ 1164 | "winapi-util", 1165 | ] 1166 | 1167 | [[package]] 1168 | name = "terminal_size" 1169 | version = "0.2.1" 1170 | source = "registry+https://github.com/rust-lang/crates.io-index" 1171 | checksum = "8440c860cf79def6164e4a0a983bcc2305d82419177a0e0c71930d049e3ac5a1" 1172 | dependencies = [ 1173 | "rustix", 1174 | "windows-sys", 1175 | ] 1176 | 1177 | [[package]] 1178 | name = "thiserror" 1179 | version = "1.0.37" 1180 | source = "registry+https://github.com/rust-lang/crates.io-index" 1181 | checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" 1182 | dependencies = [ 1183 | "thiserror-impl", 1184 | ] 1185 | 1186 | [[package]] 1187 | name = "thiserror-impl" 1188 | version = "1.0.37" 1189 | source = "registry+https://github.com/rust-lang/crates.io-index" 1190 | checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" 1191 | dependencies = [ 1192 | "proc-macro2", 1193 | "quote", 1194 | "syn", 1195 | ] 1196 | 1197 | [[package]] 1198 | name = "thread_local" 1199 | version = "1.1.4" 1200 | source = "registry+https://github.com/rust-lang/crates.io-index" 1201 | checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" 1202 | dependencies = [ 1203 | "once_cell", 1204 | ] 1205 | 1206 | [[package]] 1207 | name = "time" 1208 | version = "0.3.14" 1209 | source = "registry+https://github.com/rust-lang/crates.io-index" 1210 | checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b" 1211 | dependencies = [ 1212 | "itoa", 1213 | "libc", 1214 | "num_threads", 1215 | ] 1216 | 1217 | [[package]] 1218 | name = "tinyvec" 1219 | version = "1.6.0" 1220 | source = "registry+https://github.com/rust-lang/crates.io-index" 1221 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 1222 | dependencies = [ 1223 | "tinyvec_macros", 1224 | ] 1225 | 1226 | [[package]] 1227 | name = "tinyvec_macros" 1228 | version = "0.1.0" 1229 | source = "registry+https://github.com/rust-lang/crates.io-index" 1230 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1231 | 1232 | [[package]] 1233 | name = "tokio" 1234 | version = "1.21.2" 1235 | source = "registry+https://github.com/rust-lang/crates.io-index" 1236 | checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" 1237 | dependencies = [ 1238 | "autocfg", 1239 | "bytes", 1240 | "libc", 1241 | "memchr", 1242 | "mio", 1243 | "num_cpus", 1244 | "pin-project-lite", 1245 | "socket2", 1246 | "winapi", 1247 | ] 1248 | 1249 | [[package]] 1250 | name = "tokio-native-tls" 1251 | version = "0.3.0" 1252 | source = "registry+https://github.com/rust-lang/crates.io-index" 1253 | checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" 1254 | dependencies = [ 1255 | "native-tls", 1256 | "tokio", 1257 | ] 1258 | 1259 | [[package]] 1260 | name = "tokio-rustls" 1261 | version = "0.23.4" 1262 | source = "registry+https://github.com/rust-lang/crates.io-index" 1263 | checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" 1264 | dependencies = [ 1265 | "rustls", 1266 | "tokio", 1267 | "webpki", 1268 | ] 1269 | 1270 | [[package]] 1271 | name = "tokio-socks" 1272 | version = "0.5.1" 1273 | source = "registry+https://github.com/rust-lang/crates.io-index" 1274 | checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0" 1275 | dependencies = [ 1276 | "either", 1277 | "futures-util", 1278 | "thiserror", 1279 | "tokio", 1280 | ] 1281 | 1282 | [[package]] 1283 | name = "tokio-util" 1284 | version = "0.7.4" 1285 | source = "registry+https://github.com/rust-lang/crates.io-index" 1286 | checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" 1287 | dependencies = [ 1288 | "bytes", 1289 | "futures-core", 1290 | "futures-sink", 1291 | "pin-project-lite", 1292 | "tokio", 1293 | "tracing", 1294 | ] 1295 | 1296 | [[package]] 1297 | name = "tower-service" 1298 | version = "0.3.2" 1299 | source = "registry+https://github.com/rust-lang/crates.io-index" 1300 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 1301 | 1302 | [[package]] 1303 | name = "tracing" 1304 | version = "0.1.36" 1305 | source = "registry+https://github.com/rust-lang/crates.io-index" 1306 | checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" 1307 | dependencies = [ 1308 | "cfg-if", 1309 | "pin-project-lite", 1310 | "tracing-attributes", 1311 | "tracing-core", 1312 | ] 1313 | 1314 | [[package]] 1315 | name = "tracing-attributes" 1316 | version = "0.1.22" 1317 | source = "registry+https://github.com/rust-lang/crates.io-index" 1318 | checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" 1319 | dependencies = [ 1320 | "proc-macro2", 1321 | "quote", 1322 | "syn", 1323 | ] 1324 | 1325 | [[package]] 1326 | name = "tracing-core" 1327 | version = "0.1.29" 1328 | source = "registry+https://github.com/rust-lang/crates.io-index" 1329 | checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" 1330 | dependencies = [ 1331 | "once_cell", 1332 | "valuable", 1333 | ] 1334 | 1335 | [[package]] 1336 | name = "tracing-log" 1337 | version = "0.1.3" 1338 | source = "registry+https://github.com/rust-lang/crates.io-index" 1339 | checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" 1340 | dependencies = [ 1341 | "lazy_static", 1342 | "log", 1343 | "tracing-core", 1344 | ] 1345 | 1346 | [[package]] 1347 | name = "tracing-subscriber" 1348 | version = "0.3.15" 1349 | source = "registry+https://github.com/rust-lang/crates.io-index" 1350 | checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" 1351 | dependencies = [ 1352 | "ansi_term", 1353 | "matchers", 1354 | "once_cell", 1355 | "regex", 1356 | "sharded-slab", 1357 | "smallvec", 1358 | "thread_local", 1359 | "time", 1360 | "tracing", 1361 | "tracing-core", 1362 | "tracing-log", 1363 | ] 1364 | 1365 | [[package]] 1366 | name = "try-lock" 1367 | version = "0.2.3" 1368 | source = "registry+https://github.com/rust-lang/crates.io-index" 1369 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1370 | 1371 | [[package]] 1372 | name = "unicode-bidi" 1373 | version = "0.3.8" 1374 | source = "registry+https://github.com/rust-lang/crates.io-index" 1375 | checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" 1376 | 1377 | [[package]] 1378 | name = "unicode-ident" 1379 | version = "1.0.4" 1380 | source = "registry+https://github.com/rust-lang/crates.io-index" 1381 | checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" 1382 | 1383 | [[package]] 1384 | name = "unicode-normalization" 1385 | version = "0.1.22" 1386 | source = "registry+https://github.com/rust-lang/crates.io-index" 1387 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 1388 | dependencies = [ 1389 | "tinyvec", 1390 | ] 1391 | 1392 | [[package]] 1393 | name = "unicode-xid" 1394 | version = "0.2.4" 1395 | source = "registry+https://github.com/rust-lang/crates.io-index" 1396 | checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" 1397 | 1398 | [[package]] 1399 | name = "untrusted" 1400 | version = "0.7.1" 1401 | source = "registry+https://github.com/rust-lang/crates.io-index" 1402 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 1403 | 1404 | [[package]] 1405 | name = "url" 1406 | version = "2.3.1" 1407 | source = "registry+https://github.com/rust-lang/crates.io-index" 1408 | checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" 1409 | dependencies = [ 1410 | "form_urlencoded", 1411 | "idna", 1412 | "percent-encoding", 1413 | ] 1414 | 1415 | [[package]] 1416 | name = "users" 1417 | version = "0.11.0" 1418 | source = "registry+https://github.com/rust-lang/crates.io-index" 1419 | checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032" 1420 | dependencies = [ 1421 | "libc", 1422 | "log", 1423 | ] 1424 | 1425 | [[package]] 1426 | name = "valuable" 1427 | version = "0.1.0" 1428 | source = "registry+https://github.com/rust-lang/crates.io-index" 1429 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 1430 | 1431 | [[package]] 1432 | name = "vcpkg" 1433 | version = "0.2.15" 1434 | source = "registry+https://github.com/rust-lang/crates.io-index" 1435 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1436 | 1437 | [[package]] 1438 | name = "version_check" 1439 | version = "0.9.4" 1440 | source = "registry+https://github.com/rust-lang/crates.io-index" 1441 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1442 | 1443 | [[package]] 1444 | name = "want" 1445 | version = "0.3.0" 1446 | source = "registry+https://github.com/rust-lang/crates.io-index" 1447 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1448 | dependencies = [ 1449 | "log", 1450 | "try-lock", 1451 | ] 1452 | 1453 | [[package]] 1454 | name = "wasi" 1455 | version = "0.11.0+wasi-snapshot-preview1" 1456 | source = "registry+https://github.com/rust-lang/crates.io-index" 1457 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1458 | 1459 | [[package]] 1460 | name = "wasm-bindgen" 1461 | version = "0.2.83" 1462 | source = "registry+https://github.com/rust-lang/crates.io-index" 1463 | checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" 1464 | dependencies = [ 1465 | "cfg-if", 1466 | "wasm-bindgen-macro", 1467 | ] 1468 | 1469 | [[package]] 1470 | name = "wasm-bindgen-backend" 1471 | version = "0.2.83" 1472 | source = "registry+https://github.com/rust-lang/crates.io-index" 1473 | checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" 1474 | dependencies = [ 1475 | "bumpalo", 1476 | "log", 1477 | "once_cell", 1478 | "proc-macro2", 1479 | "quote", 1480 | "syn", 1481 | "wasm-bindgen-shared", 1482 | ] 1483 | 1484 | [[package]] 1485 | name = "wasm-bindgen-futures" 1486 | version = "0.4.33" 1487 | source = "registry+https://github.com/rust-lang/crates.io-index" 1488 | checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" 1489 | dependencies = [ 1490 | "cfg-if", 1491 | "js-sys", 1492 | "wasm-bindgen", 1493 | "web-sys", 1494 | ] 1495 | 1496 | [[package]] 1497 | name = "wasm-bindgen-macro" 1498 | version = "0.2.83" 1499 | source = "registry+https://github.com/rust-lang/crates.io-index" 1500 | checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" 1501 | dependencies = [ 1502 | "quote", 1503 | "wasm-bindgen-macro-support", 1504 | ] 1505 | 1506 | [[package]] 1507 | name = "wasm-bindgen-macro-support" 1508 | version = "0.2.83" 1509 | source = "registry+https://github.com/rust-lang/crates.io-index" 1510 | checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" 1511 | dependencies = [ 1512 | "proc-macro2", 1513 | "quote", 1514 | "syn", 1515 | "wasm-bindgen-backend", 1516 | "wasm-bindgen-shared", 1517 | ] 1518 | 1519 | [[package]] 1520 | name = "wasm-bindgen-shared" 1521 | version = "0.2.83" 1522 | source = "registry+https://github.com/rust-lang/crates.io-index" 1523 | checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" 1524 | 1525 | [[package]] 1526 | name = "web-sys" 1527 | version = "0.3.60" 1528 | source = "registry+https://github.com/rust-lang/crates.io-index" 1529 | checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" 1530 | dependencies = [ 1531 | "js-sys", 1532 | "wasm-bindgen", 1533 | ] 1534 | 1535 | [[package]] 1536 | name = "webpki" 1537 | version = "0.22.0" 1538 | source = "registry+https://github.com/rust-lang/crates.io-index" 1539 | checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" 1540 | dependencies = [ 1541 | "ring", 1542 | "untrusted", 1543 | ] 1544 | 1545 | [[package]] 1546 | name = "webpki-roots" 1547 | version = "0.22.5" 1548 | source = "registry+https://github.com/rust-lang/crates.io-index" 1549 | checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" 1550 | dependencies = [ 1551 | "webpki", 1552 | ] 1553 | 1554 | [[package]] 1555 | name = "winapi" 1556 | version = "0.3.9" 1557 | source = "registry+https://github.com/rust-lang/crates.io-index" 1558 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1559 | dependencies = [ 1560 | "winapi-i686-pc-windows-gnu", 1561 | "winapi-x86_64-pc-windows-gnu", 1562 | ] 1563 | 1564 | [[package]] 1565 | name = "winapi-i686-pc-windows-gnu" 1566 | version = "0.4.0" 1567 | source = "registry+https://github.com/rust-lang/crates.io-index" 1568 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1569 | 1570 | [[package]] 1571 | name = "winapi-util" 1572 | version = "0.1.5" 1573 | source = "registry+https://github.com/rust-lang/crates.io-index" 1574 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1575 | dependencies = [ 1576 | "winapi", 1577 | ] 1578 | 1579 | [[package]] 1580 | name = "winapi-x86_64-pc-windows-gnu" 1581 | version = "0.4.0" 1582 | source = "registry+https://github.com/rust-lang/crates.io-index" 1583 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1584 | 1585 | [[package]] 1586 | name = "windows" 1587 | version = "0.32.0" 1588 | source = "registry+https://github.com/rust-lang/crates.io-index" 1589 | checksum = "fbedf6db9096bc2364adce0ae0aa636dcd89f3c3f2cd67947062aaf0ca2a10ec" 1590 | dependencies = [ 1591 | "windows_aarch64_msvc 0.32.0", 1592 | "windows_i686_gnu 0.32.0", 1593 | "windows_i686_msvc 0.32.0", 1594 | "windows_x86_64_gnu 0.32.0", 1595 | "windows_x86_64_msvc 0.32.0", 1596 | ] 1597 | 1598 | [[package]] 1599 | name = "windows-sys" 1600 | version = "0.36.1" 1601 | source = "registry+https://github.com/rust-lang/crates.io-index" 1602 | checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" 1603 | dependencies = [ 1604 | "windows_aarch64_msvc 0.36.1", 1605 | "windows_i686_gnu 0.36.1", 1606 | "windows_i686_msvc 0.36.1", 1607 | "windows_x86_64_gnu 0.36.1", 1608 | "windows_x86_64_msvc 0.36.1", 1609 | ] 1610 | 1611 | [[package]] 1612 | name = "windows_aarch64_msvc" 1613 | version = "0.32.0" 1614 | source = "registry+https://github.com/rust-lang/crates.io-index" 1615 | checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" 1616 | 1617 | [[package]] 1618 | name = "windows_aarch64_msvc" 1619 | version = "0.36.1" 1620 | source = "registry+https://github.com/rust-lang/crates.io-index" 1621 | checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" 1622 | 1623 | [[package]] 1624 | name = "windows_i686_gnu" 1625 | version = "0.32.0" 1626 | source = "registry+https://github.com/rust-lang/crates.io-index" 1627 | checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" 1628 | 1629 | [[package]] 1630 | name = "windows_i686_gnu" 1631 | version = "0.36.1" 1632 | source = "registry+https://github.com/rust-lang/crates.io-index" 1633 | checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" 1634 | 1635 | [[package]] 1636 | name = "windows_i686_msvc" 1637 | version = "0.32.0" 1638 | source = "registry+https://github.com/rust-lang/crates.io-index" 1639 | checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" 1640 | 1641 | [[package]] 1642 | name = "windows_i686_msvc" 1643 | version = "0.36.1" 1644 | source = "registry+https://github.com/rust-lang/crates.io-index" 1645 | checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" 1646 | 1647 | [[package]] 1648 | name = "windows_x86_64_gnu" 1649 | version = "0.32.0" 1650 | source = "registry+https://github.com/rust-lang/crates.io-index" 1651 | checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" 1652 | 1653 | [[package]] 1654 | name = "windows_x86_64_gnu" 1655 | version = "0.36.1" 1656 | source = "registry+https://github.com/rust-lang/crates.io-index" 1657 | checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" 1658 | 1659 | [[package]] 1660 | name = "windows_x86_64_msvc" 1661 | version = "0.32.0" 1662 | source = "registry+https://github.com/rust-lang/crates.io-index" 1663 | checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" 1664 | 1665 | [[package]] 1666 | name = "windows_x86_64_msvc" 1667 | version = "0.36.1" 1668 | source = "registry+https://github.com/rust-lang/crates.io-index" 1669 | checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" 1670 | 1671 | [[package]] 1672 | name = "winreg" 1673 | version = "0.10.1" 1674 | source = "registry+https://github.com/rust-lang/crates.io-index" 1675 | checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" 1676 | dependencies = [ 1677 | "winapi", 1678 | ] 1679 | 1680 | [[package]] 1681 | name = "zerocopy" 1682 | version = "0.6.1" 1683 | source = "registry+https://github.com/rust-lang/crates.io-index" 1684 | checksum = "332f188cc1bcf1fe1064b8c58d150f497e697f49774aa846f2dc949d9a25f236" 1685 | dependencies = [ 1686 | "byteorder", 1687 | "zerocopy-derive", 1688 | ] 1689 | 1690 | [[package]] 1691 | name = "zerocopy-derive" 1692 | version = "0.3.1" 1693 | source = "registry+https://github.com/rust-lang/crates.io-index" 1694 | checksum = "a0fbc82b82efe24da867ee52e015e58178684bd9dd64c34e66bdf21da2582a9f" 1695 | dependencies = [ 1696 | "proc-macro2", 1697 | "syn", 1698 | "synstructure", 1699 | ] 1700 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aliyundrive-fuse" 3 | version = "0.1.14" 4 | edition = "2021" 5 | description = "FUSE for AliyunDrive" 6 | license = "MIT" 7 | homepage = "https://github.com/messense/aliyundrive-fuse" 8 | repository = "https://github.com/messense/aliyundrive-fuse.git" 9 | readme = "README.md" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | anyhow = "1.0" 15 | bytes = "1.0" 16 | clap = { version = "4.0", features = ["derive", "env", "wrap_help"] } 17 | fuser = { version = "0.11", default-features = false } 18 | libc = "0.2" 19 | oneshot = { version = "0.1", default-features = false, features = ["std"] } 20 | openssl-probe = { version = "0.1", optional = true } 21 | parking_lot = "0.12" 22 | reqwest = { version = "0.11", default-features = false, features = ["json", "gzip", "blocking", "socks"] } 23 | serde = { version = "1.0", features = ["derive"] } 24 | time = { version = "0.3", features = ["formatting", "parsing"] } 25 | tracing = "0.1" 26 | tracing-subscriber = { version = "0.3", features = ["env-filter", "local-time"] } 27 | url = "2.2" 28 | 29 | [features] 30 | default = ["rustls-tls", "libfuse"] 31 | rustls-tls = ["reqwest/rustls-tls"] 32 | native-tls = ["reqwest/native-tls"] 33 | native-tls-vendored = ["reqwest/native-tls-vendored", "openssl-probe"] 34 | libfuse = ["fuser/libfuse"] 35 | 36 | [profile.release] 37 | lto = true 38 | opt-level = "z" # Optimize for size 39 | 40 | [package.metadata.deb] 41 | maintainer = "messense " 42 | copyright = "2021-present, messense " 43 | license-file = ["LICENSE", "4"] 44 | extended-description = """\ 45 | 阿里云盘 FUSE 磁盘挂载""" 46 | depends = "fuse3" 47 | section = "utility" 48 | priority = "optional" 49 | assets = [ 50 | ["target/release/aliyundrive-fuse", "usr/bin/", "755"], 51 | ["systemd.service", "etc/systemd/system/aliyundrive-fuse.service", "644"], 52 | ] 53 | 54 | [package.metadata.generate-rpm] 55 | assets = [ 56 | { source = "target/release/aliyundrive-fuse", dest = "/usr/bin/aliyundrive-fuse", mode = "0755" }, 57 | { source = "LICENSE", dest = "/usr/share/doc/aliyundrive-fuse/LICENSE", doc = true, mode = "0644" }, 58 | { source = "systemd.service", dest = "/etc/systemd/system/aliyundrive-fuse.service", config = true, mode = "0644" }, 59 | ] 60 | 61 | [package.metadata.generate-rpm.requires] 62 | fuse3 = "*" 63 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | ARG TARGETARCH 3 | ARG TARGETVARIANT 4 | RUN apk --no-cache add ca-certificates tini fuse3 5 | RUN apk add tzdata && \ 6 | cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ 7 | echo "Asia/Shanghai" > /etc/timezone && \ 8 | apk del tzdata 9 | 10 | RUN mkdir -p /etc/aliyundrive-fuse /mnt/aliyundrive 11 | WORKDIR /root/ 12 | ADD aliyundrive-fuse-$TARGETARCH$TARGETVARIANT /usr/bin/aliyundrive-fuse 13 | 14 | ENTRYPOINT ["/sbin/tini", "--"] 15 | CMD ["/usr/bin/aliyundrive-fuse", "--workdir", "/etc/aliyundrive-fuse", "/mnt/aliyundrive"] 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021-present Messense Lv 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aliyundrive-fuse 2 | 3 | [![GitHub Actions](https://github.com/messense/aliyundrive-fuse/workflows/CI/badge.svg)](https://github.com/messense/aliyundrive-fuse/actions?query=workflow%3ACI) 4 | [![PyPI](https://img.shields.io/pypi/v/aliyundrive-fuse.svg)](https://pypi.org/project/aliyundrive-fuse) 5 | [![Docker Image](https://img.shields.io/docker/pulls/messense/aliyundrive-fuse.svg?maxAge=2592000)](https://hub.docker.com/r/messense/aliyundrive-fuse/) 6 | [![aliyundrive-fuse](https://snapcraft.io/aliyundrive-fuse/badge.svg)](https://snapcraft.io/aliyundrive-fuse) 7 | [![Crates.io](https://img.shields.io/crates/v/aliyundrive-fuse.svg)](https://crates.io/crates/aliyundrive-fuse) 8 | 9 | > 🚀 Help me to become a full-time open-source developer by [sponsoring me on GitHub](https://github.com/sponsors/messense) 10 | 11 | 阿里云盘 FUSE 磁盘挂载,主要用于配合 [Emby](https://emby.media) 或者 [Jellyfin](https://jellyfin.org) 观看阿里云盘内容,功能特性: 12 | 13 | 1. 目前只读,不支持写入 14 | 2. 支持 Linux 和 macOS,暂不支持 Windows 15 | 16 | [aliyundrive-webdav](https://github.com/messense/aliyundrive-webdav) 项目已经实现了通过 WebDAV 访问阿里云盘内容,但由于 Emby 和 Jellyfin 都不支持直接访问 WebDAV 资源, 17 | 需要配合 [rclone](https://rclone.org) 之类的软件将 WebDAV 挂载为本地磁盘,而本项目则直接通过 FUSE 实现将阿里云盘挂载为本地磁盘,省去使用 rclone 再做一层中转。 18 | 19 | ## 安装 20 | 21 | * macOS 需要先安装 [macfuse](https://osxfuse.github.io/) 22 | * Linux 需要先安装 fuse 23 | * Debian 系如 Ubuntu: `apt-get install -y fuse3` 24 | * RedHat 系如 CentOS: `yum install -y fuse3` 25 | 26 | 可以从 [GitHub Releases](https://github.com/messense/aliyundrive-fuse/releases) 页面下载预先构建的二进制包, 也可以使用 pip 从 PyPI 下载: 27 | 28 | ```bash 29 | pip install aliyundrive-fuse 30 | ``` 31 | 32 | 如果系统支持 [Snapcraft](https://snapcraft.io) 比如 Ubuntu、Debian 等,也可以使用 snap 安装: 33 | 34 | ```bash 35 | sudo snap install aliyundrive-fuse 36 | ``` 37 | 38 | ### OpenWrt 路由器 39 | 40 | [GitHub Releases](https://github.com/messense/aliyundrive-fuse/releases) 中有预编译的 ipk 文件, 目前提供了 41 | aarch64/arm/x86_64/i686 等架构的版本,可以下载后使用 opkg 安装,以 nanopi r4s 为例: 42 | 43 | ```bash 44 | wget https://github.com/messense/aliyundrive-fuse/releases/download/v0.1.14/aliyundrive-fuse_0.1.14-1_aarch64_generic.ipk 45 | wget https://github.com/messense/aliyundrive-fuse/releases/download/v0.1.14/luci-app-aliyundrive-fuse_0.1.14_all.ipk 46 | wget https://github.com/messense/aliyundrive-fuse/releases/download/v0.1.14/luci-i18n-aliyundrive-fuse-zh-cn_0.1.14-1_all.ipk 47 | opkg install aliyundrive-fuse_0.1.14-1_aarch64_generic.ipk 48 | opkg install luci-app-aliyundrive-fuse_0.1.14_all.ipk 49 | opkg install luci-i18n-aliyundrive-fuse-zh-cn_0.1.14-1_all.ipk 50 | ``` 51 | 52 | 其它 CPU 架构的路由器可在 [GitHub Releases](https://github.com/messense/aliyundrive-fuse/releases) 页面中查找对应的架构的主程序 ipk 文件下载安装。 53 | 54 | > Tips: 不清楚 CPU 架构类型可通过运行 `opkg print-architecture` 命令查询。 55 | 56 | ## 命令行用法 57 | 58 | ```bash 59 | USAGE: 60 | aliyundrive-fuse [OPTIONS] --refresh-token 61 | 62 | ARGS: 63 | Mount point 64 | 65 | OPTIONS: 66 | --allow-other Allow other users to access the drive 67 | -h, --help Print help information 68 | -r, --refresh-token Aliyun drive refresh token [env: REFRESH_TOKEN=] 69 | -S, --read-buffer-size Read/download buffer size in bytes, defaults to 10MB [default: 10485760] 70 | -V, --version Print version information 71 | -w, --workdir Working directory, refresh_token will be stored in there if specified 72 | ``` 73 | 74 | 比如将磁盘挂载到 `/mnt/aliyundrive` 目录: 75 | 76 | ```bash 77 | mkdir -p /mnt/aliyundrive /var/run/aliyundrive-fuse 78 | aliyundrive-fuse -r your-refresh-token -w /var/run/aliyundrive-fuse /mnt/aliyundrive 79 | ``` 80 | 81 | ## Emby/Jellyfin 82 | 83 | 如果是直接运行在系统上的 Emby/Jellyfin,则可以直接在其控制台添加媒体库的时候选择阿里云盘对应的挂载路径中的文件夹即可; 84 | 如果是 Docker 运行的 Emby/Jellyfin,则需要将阿里云盘挂载路径也挂载到 Docker 容器中,假设阿里云盘挂载路径为 `/mnt/aliyundrive`, 85 | 以 Jellyfin 为例(假设 Jellyfin 工作路径为 `/root/jellyfin`)将云盘挂载到容器 `/media` 路径: 86 | 87 | ```bash 88 | docker run -d --name jellyfin \ 89 | -v /root/jellyfin/config:/config \ 90 | -v /root/jellyfin/cache:/cache \ 91 | -v /mnt/aliyundrive:/media \ 92 | -p 8096:8096 \ 93 | --device=/dev/dri/renderD128 \ 94 | --device /dev/dri/card0:/dev/dri/card0 \ 95 | --restart unless-stopped \ 96 | jellyfin/jellyfin 97 | ``` 98 | 99 | ## License 100 | 101 | This work is released under the MIT license. A copy of the license is provided in the [LICENSE](./LICENSE) file. 102 | -------------------------------------------------------------------------------- /openwrt/aliyundrive-fuse/Makefile: -------------------------------------------------------------------------------- 1 | include $(TOPDIR)/rules.mk 2 | 3 | PKG_NAME:=aliyundrive-fuse 4 | PKG_VERSION:=0.1.14 5 | PKG_RELEASE:=1 6 | 7 | PKG_LICENSE:=MIT 8 | PKG_MAINTAINER:=messense 9 | 10 | PKG_LIBC:=musl 11 | ifeq ($(ARCH),arm) 12 | PKG_LIBC:=musleabi 13 | 14 | ARM_CPU_FEATURES:=$(word 2,$(subst +,$(space),$(call qstrip,$(CONFIG_CPU_TYPE)))) 15 | ifneq ($(filter $(ARM_CPU_FEATURES),vfp vfpv2),) 16 | PKG_LIBC:=musleabihf 17 | endif 18 | endif 19 | 20 | PKG_ARCH=$(ARCH) 21 | ifeq ($(ARCH),i386) 22 | PKG_ARCH:=i686 23 | endif 24 | 25 | PKG_SOURCE:=aliyundrive-fuse-v$(PKG_VERSION).$(PKG_ARCH)-unknown-linux-$(PKG_LIBC).tar.gz 26 | PKG_SOURCE_URL:=https://github.com/messense/aliyundrive-fuse/releases/download/v$(PKG_VERSION)/ 27 | PKG_HASH:=skip 28 | 29 | include $(INCLUDE_DIR)/package.mk 30 | 31 | define Package/aliyundrive-fuse 32 | SECTION:=multimedia 33 | CATEGORY:=Multimedia 34 | DEPENDS:=+fuse-utils 35 | TITLE:=FUSE for AliyunDrive 36 | URL:=https://github.com/messense/aliyundrive-fuse 37 | endef 38 | 39 | define Package/aliyundrive-fuse/description 40 | FUSE for AliyunDrive. 41 | endef 42 | 43 | define Package/aliyundrive-fuse/conffiles 44 | /etc/config/aliyundrive-fuse 45 | endef 46 | 47 | define Download/sha256sum 48 | FILE:=$(PKG_SOURCE).sha256 49 | URL_FILE:=$(FILE) 50 | URL:=$(PKG_SOURCE_URL) 51 | HASH:=skip 52 | endef 53 | $(eval $(call Download,sha256sum)) 54 | 55 | define Build/Prepare 56 | mv $(DL_DIR)/$(PKG_SOURCE).sha256 . 57 | cp $(DL_DIR)/$(PKG_SOURCE) . 58 | shasum -a 256 -c $(PKG_SOURCE).sha256 59 | rm $(PKG_SOURCE).sha256 $(PKG_SOURCE) 60 | 61 | tar -C $(PKG_BUILD_DIR)/ -zxf $(DL_DIR)/$(PKG_SOURCE) 62 | endef 63 | 64 | define Build/Compile 65 | echo "aliyundrive-fuse using precompiled binary." 66 | endef 67 | 68 | define Package/aliyundrive-fuse/install 69 | $(INSTALL_DIR) $(1)/usr/bin 70 | $(INSTALL_BIN) $(PKG_BUILD_DIR)/aliyundrive-fuse $(1)/usr/bin/aliyundrive-fuse 71 | $(INSTALL_DIR) $(1)/etc/init.d 72 | $(INSTALL_BIN) ./files/aliyundrive-fuse.init $(1)/etc/init.d/aliyundrive-fuse 73 | $(INSTALL_DIR) $(1)/etc/config 74 | $(INSTALL_CONF) ./files/aliyundrive-fuse.config $(1)/etc/config/aliyundrive-fuse 75 | endef 76 | 77 | $(eval $(call BuildPackage,aliyundrive-fuse)) 78 | -------------------------------------------------------------------------------- /openwrt/aliyundrive-fuse/files/aliyundrive-fuse.config: -------------------------------------------------------------------------------- 1 | config default 2 | option enable '0' 3 | option debug '0' 4 | option refresh_token '' 5 | option mount_point '/mnt/aliyundrive' 6 | option read_buffer_size '10485760' 7 | option allow_other '1' 8 | -------------------------------------------------------------------------------- /openwrt/aliyundrive-fuse/files/aliyundrive-fuse.init: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | 3 | USE_PROCD=1 4 | 5 | START=99 6 | STOP=15 7 | 8 | NAME=aliyundrive-fuse 9 | 10 | uci_get_by_type() { 11 | local ret=$(uci get $NAME.@$1[0].$2 2>/dev/null) 12 | echo ${ret:=$3} 13 | } 14 | 15 | start_service() { 16 | local enable=$(uci_get_by_type default enable) 17 | case "$enable" in 18 | 1|on|true|yes|enabled) 19 | local refresh_token=$(uci_get_by_type default refresh_token) 20 | local mount_point=$(uci_get_by_type default mount_point) 21 | local read_buf_size=$(uci_get_by_type default read_buffer_size 10485760) 22 | local allow_other=$(uci_get_by_type default allow_other 0) 23 | 24 | local extra_options="" 25 | 26 | if [ "$allow_other" = "1" ]; then 27 | extra_options="$extra_options --allow-other" 28 | fi 29 | 30 | mkdir -p "$mount_point" 31 | procd_open_instance 32 | procd_set_param command /bin/sh -c "/usr/bin/$NAME $extra_options -S $read_buf_size --workdir /var/run/$NAME $mount_point >>/var/log/$NAME.log 2>&1" 33 | procd_set_param pidfile /var/run/$NAME.pid 34 | procd_set_param env REFRESH_TOKEN="$refresh_token" 35 | case $(uci_get_by_type default debug) in 36 | 1|on|true|yes|enabled) 37 | procd_append_param env RUST_LOG="aliyundrive_fuse=debug" ;; 38 | *) ;; 39 | esac 40 | procd_close_instance ;; 41 | *) 42 | stop_service ;; 43 | esac 44 | } 45 | 46 | service_triggers() { 47 | procd_add_reload_trigger "aliyundrive-fuse" 48 | } 49 | -------------------------------------------------------------------------------- /openwrt/luci-app-aliyundrive-fuse/Makefile: -------------------------------------------------------------------------------- 1 | include $(TOPDIR)/rules.mk 2 | 3 | PKG_NAME:=luci-app-aliyundrive-fuse 4 | PKG_VERSION:=0.1.14 5 | PKG_RELEASE:=1 6 | PKG_PO_VERSION:=$(PKG_VERSION)-$(PKG_RELEASE) 7 | 8 | PKG_LICENSE:=MIT 9 | PKG_MAINTAINER:=messense 10 | 11 | LUCI_TITLE:=LuCI Support for aliyundrive-fuse 12 | LUCI_PKGARCH:=all 13 | LUCI_DEPENDS:=+aliyundrive-fuse 14 | 15 | include $(TOPDIR)/feeds/luci/luci.mk 16 | 17 | # call BuildPackage - OpenWrt buildroot signature 18 | -------------------------------------------------------------------------------- /openwrt/luci-app-aliyundrive-fuse/luasrc/controller/aliyundrive-fuse.lua: -------------------------------------------------------------------------------- 1 | module("luci.controller.aliyundrive-fuse", package.seeall) 2 | 3 | function index() 4 | if not nixio.fs.access("/etc/config/aliyundrive-fuse") then 5 | return 6 | end 7 | 8 | local page 9 | page = entry({"admin", "services", "aliyundrive-fuse"}, alias("admin", "services", "aliyundrive-fuse", "client"), _("AliyunDrive FUSE"), 10) -- 首页 10 | page.dependent = true 11 | page.acl_depends = { "luci-app-aliyundrive-fuse" } 12 | 13 | entry({"admin", "services", "aliyundrive-fuse", "client"}, cbi("aliyundrive-fuse/client"), _("Settings"), 10).leaf = true -- 客户端配置 14 | entry({"admin", "services", "aliyundrive-fuse", "log"}, form("aliyundrive-fuse/log"), _("Log"), 30).leaf = true -- 日志页面 15 | 16 | entry({"admin", "services", "aliyundrive-fuse", "status"}, call("action_status")).leaf = true 17 | entry({"admin", "services", "aliyundrive-fuse", "logtail"}, call("action_logtail")).leaf = true 18 | end 19 | 20 | function action_status() 21 | local e = {} 22 | e.running = luci.sys.call("pidof aliyundrive-fuse >/dev/null") == 0 23 | e.application = luci.sys.exec("aliyundrive-fuse --version") 24 | luci.http.prepare_content("application/json") 25 | luci.http.write_json(e) 26 | end 27 | 28 | function action_logtail() 29 | local fs = require "nixio.fs" 30 | local log_path = "/var/log/aliyundrive-fuse.log" 31 | local e = {} 32 | e.running = luci.sys.call("pidof aliyundrive-fuse >/dev/null") == 0 33 | if fs.access(log_path) then 34 | e.log = luci.sys.exec("tail -n 100 %s | sed 's/\\x1b\\[[0-9;]*m//g'" % log_path) 35 | else 36 | e.log = "" 37 | end 38 | luci.http.prepare_content("application/json") 39 | luci.http.write_json(e) 40 | end 41 | -------------------------------------------------------------------------------- /openwrt/luci-app-aliyundrive-fuse/luasrc/model/cbi/aliyundrive-fuse/client.lua: -------------------------------------------------------------------------------- 1 | m = Map("aliyundrive-fuse") 2 | m.title = translate("AliyunDrive FUSE") 3 | m.description = translate("Project GitHub URL") 4 | 5 | m:section(SimpleSection).template = "aliyundrive-fuse/aliyundrive-fuse_status" 6 | 7 | e = m:section(TypedSection, "default") 8 | e.anonymous = true 9 | 10 | enable = e:option(Flag, "enable", translate("Enable")) 11 | enable.rmempty = false 12 | 13 | refresh_token = e:option(Value, "refresh_token", translate("Refresh Token")) 14 | refresh_token.description = translate("How to get refresh token") 15 | 16 | mount_point = e:option(Value, "mount_point", translate("Mount Point")) 17 | mount_point.default = "/mnt/aliyundrive" 18 | 19 | read_buffer_size = e:option(Value, "read_buffer_size", translate("Read Buffer Size")) 20 | read_buffer_size.default = "10485760" 21 | read_buffer_size.datatype = "uinteger" 22 | 23 | allow_other = e:option(Flag, "allow_other", translate("Allow Other users Access")) 24 | allow_other.description = translate("Allow other users to access the drive, enable this if you share with samba") 25 | allow_other.rmempty = false 26 | 27 | debug = e:option(Flag, "debug", translate("Debug Mode")) 28 | debug.rmempty = false 29 | 30 | return m 31 | -------------------------------------------------------------------------------- /openwrt/luci-app-aliyundrive-fuse/luasrc/model/cbi/aliyundrive-fuse/log.lua: -------------------------------------------------------------------------------- 1 | log = SimpleForm("logview") 2 | log.submit = false 3 | log.reset = false 4 | 5 | t = log:field(DummyValue, '', '') 6 | t.rawhtml = true 7 | t.template = 'aliyundrive-fuse/aliyundrive-fuse_log' 8 | 9 | return log 10 | -------------------------------------------------------------------------------- /openwrt/luci-app-aliyundrive-fuse/luasrc/view/aliyundrive-fuse/aliyundrive-fuse_log.htm: -------------------------------------------------------------------------------- 1 | <%+cbi/valueheader%> 2 | 3 | 4 | 15 | <%+cbi/valuefooter%> 16 | -------------------------------------------------------------------------------- /openwrt/luci-app-aliyundrive-fuse/luasrc/view/aliyundrive-fuse/aliyundrive-fuse_status.htm: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 |

19 | <%:Collecting data...%> 20 |

21 |
22 | -------------------------------------------------------------------------------- /openwrt/luci-app-aliyundrive-fuse/po/zh-cn/aliyundrive-fuse.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "Content-Type: text/plain; charset=UTF-8\n" 3 | 4 | msgid "AliyunDrive" 5 | msgstr "阿里云盘" 6 | 7 | msgid "AliyunDrive FUSE" 8 | msgstr "阿里云盘 FUSE" 9 | 10 | msgid "Enable" 11 | msgstr "启用" 12 | 13 | msgid "Refresh Token" 14 | msgstr "Refresh Token" 15 | 16 | msgid "Mount Point" 17 | msgstr "挂载点" 18 | 19 | msgid "Read Buffer Size" 20 | msgstr "下载缓冲大小(bytes)" 21 | 22 | msgid "Collecting data..." 23 | msgstr "获取数据中..." 24 | 25 | msgid "RUNNING" 26 | msgstr "运行中" 27 | 28 | msgid "NOT RUNNING" 29 | msgstr "未运行" 30 | 31 | msgid "Settings" 32 | msgstr "设置" 33 | 34 | msgid "Log" 35 | msgstr "日志" 36 | 37 | msgid "Debug Mode" 38 | msgstr "调试模式" 39 | 40 | msgid "Project GitHub URL" 41 | msgstr "GitHub 项目地址" 42 | 43 | msgid "How to get refresh token" 44 | msgstr "查看获取 refresh token 的方法" 45 | 46 | msgid "Allow Other users Access" 47 | msgstr "允许其他用户访问" 48 | 49 | msgid "Allow other users to access the drive, enable this if you share with samba" 50 | msgstr "允许其他用户访问此驱动,如果你想用Samba分享请开启此开关" 51 | -------------------------------------------------------------------------------- /openwrt/luci-app-aliyundrive-fuse/po/zh_Hans: -------------------------------------------------------------------------------- 1 | zh-cn -------------------------------------------------------------------------------- /openwrt/luci-app-aliyundrive-fuse/root/etc/uci-defaults/luci-aliyundrive-fuse: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | uci -q batch <<-EOF >/dev/null 4 | delete ucitrack.@aliyundrive-fuse[-1] 5 | add ucitrack aliyundrive-fuse 6 | set ucitrack.@aliyundrive-fuse[-1].init=aliyundrive-fuse 7 | commit ucitrack 8 | EOF 9 | 10 | rm -f /tmp/luci-indexcache 11 | exit 0 12 | -------------------------------------------------------------------------------- /openwrt/luci-app-aliyundrive-fuse/root/usr/share/rpcd/acl.d/luci-app-aliyundrive-fuse.json: -------------------------------------------------------------------------------- 1 | { 2 | "luci-app-aliyundrive-fuse": { 3 | "description": "Grant UCI access for luci-app-aliyundrive-fuse", 4 | "read": { 5 | "uci": [ "aliyundrive-fuse" ] 6 | }, 7 | "write": { 8 | "uci": [ "aliyundrive-fuse" ] 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["maturin>=0.13,<0.14"] 3 | build-backend = "maturin" 4 | 5 | [tool.maturin] 6 | bindings = "bin" 7 | -------------------------------------------------------------------------------- /snap/snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: aliyundrive-fuse 2 | version: '0.1.14' 3 | summary: 阿里云盘 FUSE 4 | description: | 5 | 阿里云盘 FUSE 磁盘挂载 6 | 7 | grade: stable # must be 'stable' to release into candidate/stable channels 8 | confinement: strict # use 'strict' once you have the right plugs and slots 9 | 10 | base: core20 11 | 12 | build-packages: 13 | - pkg-config 14 | - libfuse3-dev 15 | 16 | architectures: 17 | - build-on: amd64 18 | - build-on: i386 19 | - build-on: arm64 20 | - build-on: armhf 21 | 22 | parts: 23 | aliyundrive-fuse: 24 | plugin: rust 25 | source: . 26 | stage-packages: 27 | - fuse3 28 | 29 | apps: 30 | aliyundrive-fuse: 31 | command: bin/aliyundrive-fuse 32 | plugs: [network] 33 | -------------------------------------------------------------------------------- /src/drive/mod.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::fs; 3 | use std::path::PathBuf; 4 | use std::sync::Arc; 5 | use std::thread; 6 | use std::time::Duration; 7 | 8 | use anyhow::{bail, Context, Result}; 9 | use bytes::Bytes; 10 | use parking_lot::RwLock; 11 | use reqwest::{ 12 | header::{HeaderMap, HeaderValue}, 13 | StatusCode, 14 | }; 15 | use serde::de::DeserializeOwned; 16 | use serde::Serialize; 17 | use tracing::{debug, error, info, warn}; 18 | 19 | mod model; 20 | 21 | use model::*; 22 | pub use model::{AliyunFile, DateTime, FileType}; 23 | 24 | const ORIGIN: &str = "https://www.aliyundrive.com"; 25 | const REFERER: &str = "https://www.aliyundrive.com/"; 26 | const UA: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"; 27 | 28 | #[derive(Debug, Clone)] 29 | pub struct DriveConfig { 30 | pub api_base_url: String, 31 | pub refresh_token_url: String, 32 | pub workdir: Option, 33 | pub app_id: Option, 34 | } 35 | 36 | #[derive(Debug, Clone)] 37 | struct Credentials { 38 | refresh_token: String, 39 | access_token: Option, 40 | } 41 | 42 | #[derive(Debug, Clone)] 43 | pub struct AliyunDrive { 44 | config: DriveConfig, 45 | client: reqwest::blocking::Client, 46 | credentials: Arc>, 47 | drive_id: Option, 48 | pub nick_name: Option, 49 | } 50 | 51 | impl AliyunDrive { 52 | pub fn new(config: DriveConfig, refresh_token: String) -> Result { 53 | let credentials = Credentials { 54 | refresh_token, 55 | access_token: None, 56 | }; 57 | let mut headers = HeaderMap::new(); 58 | headers.insert("Origin", HeaderValue::from_static(ORIGIN)); 59 | headers.insert("Referer", HeaderValue::from_static(REFERER)); 60 | let client = reqwest::blocking::Client::builder() 61 | .user_agent(UA) 62 | .default_headers(headers) 63 | // OSS closes idle connections after 60 seconds, 64 | // so we can close idle connections ahead of time to prevent re-using them. 65 | // See also https://github.com/hyperium/hyper/issues/2136 66 | .pool_idle_timeout(Duration::from_secs(50)) 67 | .connect_timeout(Duration::from_secs(10)) 68 | .timeout(Duration::from_secs(30)) 69 | .build()?; 70 | let mut drive = Self { 71 | config, 72 | client, 73 | credentials: Arc::new(RwLock::new(credentials)), 74 | drive_id: None, 75 | nick_name: None, 76 | }; 77 | 78 | let (tx, rx) = oneshot::channel(); 79 | // schedule update token task 80 | let client = drive.clone(); 81 | let refresh_token_from_file = if let Some(dir) = drive.config.workdir.as_ref() { 82 | fs::read_to_string(dir.join("refresh_token")).ok() 83 | } else { 84 | None 85 | }; 86 | thread::spawn(move || { 87 | let mut delay_seconds = 7000; 88 | match client.do_refresh_token_with_retry(refresh_token_from_file) { 89 | Ok(res) => { 90 | // token usually expires in 7200s, refresh earlier 91 | delay_seconds = res.expires_in - 200; 92 | if tx.send((res.default_drive_id, res.nick_name)).is_err() { 93 | error!("send default drive id failed"); 94 | } 95 | } 96 | Err(err) => { 97 | error!("refresh token failed: {}", err); 98 | tx.send((String::new(), String::new())).unwrap(); 99 | } 100 | } 101 | loop { 102 | thread::sleep(Duration::from_secs(delay_seconds)); 103 | if let Err(err) = client.do_refresh_token_with_retry(None) { 104 | error!("refresh token failed: {}", err); 105 | } 106 | } 107 | }); 108 | 109 | let (drive_id, nick_name) = rx.recv()?; 110 | if drive_id.is_empty() { 111 | bail!("get default drive id failed"); 112 | } 113 | info!(drive_id = %drive_id, "found default drive"); 114 | drive.drive_id = Some(drive_id); 115 | drive.nick_name = Some(nick_name); 116 | 117 | Ok(drive) 118 | } 119 | 120 | fn save_refresh_token(&self, refresh_token: &str) -> Result<()> { 121 | if let Some(dir) = self.config.workdir.as_ref() { 122 | fs::create_dir_all(dir)?; 123 | let refresh_token_file = dir.join("refresh_token"); 124 | fs::write(refresh_token_file, refresh_token)?; 125 | } 126 | Ok(()) 127 | } 128 | 129 | fn do_refresh_token(&self, refresh_token: &str) -> Result { 130 | let mut data = HashMap::new(); 131 | data.insert("refresh_token", refresh_token); 132 | data.insert("grant_type", "refresh_token"); 133 | if let Some(app_id) = self.config.app_id.as_ref() { 134 | data.insert("app_id", app_id); 135 | } 136 | let res = self 137 | .client 138 | .post(&self.config.refresh_token_url) 139 | .json(&data) 140 | .send()?; 141 | match res.error_for_status_ref() { 142 | Ok(_) => { 143 | let res = res.json::()?; 144 | info!( 145 | refresh_token = %res.refresh_token, 146 | nick_name = %res.nick_name, 147 | "refresh token succeed" 148 | ); 149 | Ok(res) 150 | } 151 | Err(err) => { 152 | let msg = res.text()?; 153 | let context = format!("{}: {}", err, msg); 154 | Err(err).context(context) 155 | } 156 | } 157 | } 158 | 159 | fn do_refresh_token_with_retry( 160 | &self, 161 | refresh_token_from_file: Option, 162 | ) -> Result { 163 | let mut last_err = None; 164 | let mut refresh_token = self.refresh_token(); 165 | for _ in 0..10 { 166 | match self.do_refresh_token(&refresh_token) { 167 | Ok(res) => { 168 | let mut cred = self.credentials.write(); 169 | cred.refresh_token = res.refresh_token.clone(); 170 | cred.access_token = Some(res.access_token.clone()); 171 | if let Err(err) = self.save_refresh_token(&res.refresh_token) { 172 | error!(error = %err, "save refresh token failed"); 173 | } 174 | return Ok(res); 175 | } 176 | Err(err) => { 177 | let mut should_warn = true; 178 | let mut should_retry = match err.downcast_ref::() { 179 | Some(e) => { 180 | e.is_connect() 181 | || e.is_timeout() 182 | || matches!(e.status(), Some(StatusCode::TOO_MANY_REQUESTS)) 183 | } 184 | None => false, 185 | }; 186 | // retry if command line refresh_token is invalid but we also have 187 | // refresh_token from file 188 | if let Some(refresh_token_from_file) = refresh_token_from_file.as_ref() { 189 | if !should_retry && &refresh_token != refresh_token_from_file { 190 | refresh_token = refresh_token_from_file.trim().to_string(); 191 | should_retry = true; 192 | // don't warn if we are gonna try refresh_token from file 193 | should_warn = false; 194 | } 195 | } 196 | if should_retry { 197 | if should_warn { 198 | warn!(error = %err, "refresh token failed, will wait and retry"); 199 | } 200 | last_err = Some(err); 201 | thread::sleep(Duration::from_secs(1)); 202 | continue; 203 | } else { 204 | last_err = Some(err); 205 | break; 206 | } 207 | } 208 | } 209 | } 210 | Err(last_err.unwrap()) 211 | } 212 | 213 | fn refresh_token(&self) -> String { 214 | let cred = self.credentials.read(); 215 | cred.refresh_token.clone() 216 | } 217 | 218 | fn access_token(&self) -> Result { 219 | let cred = self.credentials.read(); 220 | cred.access_token.clone().context("missing access_token") 221 | } 222 | 223 | fn drive_id(&self) -> Result<&str> { 224 | self.drive_id.as_deref().context("missing drive_id") 225 | } 226 | 227 | fn request(&self, url: String, req: &T) -> Result> 228 | where 229 | T: Serialize + ?Sized, 230 | U: DeserializeOwned, 231 | { 232 | let mut access_token = self.access_token()?; 233 | let url = reqwest::Url::parse(&url)?; 234 | let res = self 235 | .client 236 | .post(url.clone()) 237 | .bearer_auth(&access_token) 238 | .json(&req) 239 | .send()? 240 | .error_for_status(); 241 | match res { 242 | Ok(res) => { 243 | if res.status() == StatusCode::NO_CONTENT { 244 | return Ok(None); 245 | } 246 | let res = res.json::()?; 247 | Ok(Some(res)) 248 | } 249 | Err(err) => { 250 | match err.status() { 251 | Some( 252 | status_code 253 | @ 254 | // 4xx 255 | (StatusCode::UNAUTHORIZED 256 | | StatusCode::REQUEST_TIMEOUT 257 | | StatusCode::TOO_MANY_REQUESTS 258 | // 5xx 259 | | StatusCode::INTERNAL_SERVER_ERROR 260 | | StatusCode::BAD_GATEWAY 261 | | StatusCode::SERVICE_UNAVAILABLE 262 | | StatusCode::GATEWAY_TIMEOUT), 263 | ) => { 264 | if status_code == StatusCode::UNAUTHORIZED { 265 | // refresh token and retry 266 | let token_res = self.do_refresh_token_with_retry(None)?; 267 | access_token = token_res.access_token; 268 | } else { 269 | // wait for a while and retry 270 | thread::sleep(Duration::from_secs(1)); 271 | } 272 | let res = self 273 | .client 274 | .post(url) 275 | .bearer_auth(&access_token) 276 | .json(&req) 277 | .send() 278 | ? 279 | .error_for_status()?; 280 | if res.status() == StatusCode::NO_CONTENT { 281 | return Ok(None); 282 | } 283 | let res = res.json::()?; 284 | Ok(Some(res)) 285 | } 286 | _ => Err(err.into()), 287 | } 288 | } 289 | } 290 | } 291 | 292 | pub fn list_all(&self, parent_file_id: &str) -> Result> { 293 | let mut files = Vec::new(); 294 | let mut marker = None; 295 | loop { 296 | let res = self.list(parent_file_id, marker.as_deref())?; 297 | files.extend(res.items.into_iter()); 298 | if res.next_marker.is_empty() { 299 | break; 300 | } 301 | marker = Some(res.next_marker); 302 | } 303 | Ok(files) 304 | } 305 | 306 | pub fn list(&self, parent_file_id: &str, marker: Option<&str>) -> Result { 307 | let drive_id = self.drive_id()?; 308 | debug!(drive_id = %drive_id, parent_file_id = %parent_file_id, marker = ?marker, "list file"); 309 | let req = ListFileRequest { 310 | drive_id, 311 | parent_file_id, 312 | limit: 200, 313 | all: false, 314 | image_thumbnail_process: "image/resize,w_400/format,jpeg", 315 | image_url_process: "image/resize,w_1920/format,jpeg", 316 | video_thumbnail_process: "video/snapshot,t_0,f_jpg,ar_auto,w_300", 317 | fields: "*", 318 | order_by: "updated_at", 319 | order_direction: "DESC", 320 | marker, 321 | }; 322 | self.request(format!("{}/adrive/v3/file/list", self.config.api_base_url), &req) 323 | .and_then(|res| res.context("expect response")) 324 | } 325 | 326 | pub fn download(&self, url: &str, start_pos: u64, size: usize) -> Result { 327 | use reqwest::header::RANGE; 328 | 329 | let end_pos = start_pos + size as u64 - 1; 330 | debug!(url = %url, start = start_pos, end = end_pos, "download file"); 331 | let range = format!("bytes={}-{}", start_pos, end_pos); 332 | let res = self 333 | .client 334 | .get(url) 335 | .header(RANGE, range) 336 | .send()? 337 | .error_for_status()?; 338 | Ok(res.bytes()?) 339 | } 340 | 341 | pub fn get_download_url(&self, file_id: &str) -> Result { 342 | debug!(file_id = %file_id, "get download url"); 343 | let req = GetFileDownloadUrlRequest { 344 | drive_id: self.drive_id()?, 345 | file_id, 346 | }; 347 | let res: GetFileDownloadUrlResponse = self 348 | .request( 349 | format!("{}/v2/file/get_download_url", self.config.api_base_url), 350 | &req, 351 | )? 352 | .context("expect response")?; 353 | Ok(res.url) 354 | } 355 | 356 | pub fn get_quota(&self) -> Result<(u64, u64)> { 357 | let drive_id = self.drive_id()?; 358 | let mut data = HashMap::new(); 359 | data.insert("drive_id", drive_id); 360 | let res: GetDriveResponse = self 361 | .request(format!("{}/v2/drive/get", self.config.api_base_url), &data)? 362 | .context("expect response")?; 363 | Ok((res.used_size, res.total_size)) 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /src/drive/model.rs: -------------------------------------------------------------------------------- 1 | use std::ops; 2 | use std::time::SystemTime; 3 | 4 | use ::time::{format_description::well_known::Rfc3339, OffsetDateTime}; 5 | use serde::{Deserialize, Deserializer, Serialize}; 6 | 7 | #[derive(Debug, Clone, Deserialize)] 8 | pub struct RefreshTokenResponse { 9 | pub access_token: String, 10 | pub refresh_token: String, 11 | pub expires_in: u64, 12 | pub token_type: String, 13 | pub user_id: String, 14 | pub nick_name: String, 15 | pub default_drive_id: String, 16 | } 17 | 18 | #[derive(Debug, Clone, Serialize)] 19 | pub struct ListFileRequest<'a> { 20 | pub drive_id: &'a str, 21 | pub parent_file_id: &'a str, 22 | pub limit: u64, 23 | pub all: bool, 24 | pub image_thumbnail_process: &'a str, 25 | pub image_url_process: &'a str, 26 | pub video_thumbnail_process: &'a str, 27 | pub fields: &'a str, 28 | pub order_by: &'a str, 29 | pub order_direction: &'a str, 30 | pub marker: Option<&'a str>, 31 | } 32 | 33 | #[derive(Debug, Clone, Deserialize)] 34 | pub struct ListFileResponse { 35 | pub items: Vec, 36 | pub next_marker: String, 37 | } 38 | 39 | #[derive(Debug, Clone, Serialize)] 40 | pub struct GetFileDownloadUrlRequest<'a> { 41 | pub drive_id: &'a str, 42 | pub file_id: &'a str, 43 | } 44 | 45 | #[derive(Debug, Clone, Deserialize)] 46 | pub struct GetFileDownloadUrlResponse { 47 | pub url: String, 48 | pub size: u64, 49 | pub expiration: String, 50 | } 51 | 52 | #[derive(Debug, Clone, Deserialize)] 53 | pub struct GetDriveResponse { 54 | pub total_size: u64, 55 | pub used_size: u64, 56 | } 57 | 58 | #[derive(Debug, Clone)] 59 | pub struct DateTime(SystemTime); 60 | 61 | impl<'a> Deserialize<'a> for DateTime { 62 | fn deserialize>(deserializer: D) -> Result { 63 | let dt = OffsetDateTime::parse(<&str>::deserialize(deserializer)?, &Rfc3339) 64 | .map_err(serde::de::Error::custom)?; 65 | Ok(Self(dt.into())) 66 | } 67 | } 68 | 69 | impl ops::Deref for DateTime { 70 | type Target = SystemTime; 71 | 72 | fn deref(&self) -> &Self::Target { 73 | &self.0 74 | } 75 | } 76 | 77 | #[derive(Debug, Clone, Copy, Deserialize)] 78 | #[serde(rename_all = "lowercase")] 79 | pub enum FileType { 80 | Folder, 81 | File, 82 | } 83 | 84 | #[derive(Debug, Clone, Deserialize)] 85 | pub struct AliyunFile { 86 | pub name: String, 87 | #[serde(rename = "file_id")] 88 | pub id: String, 89 | pub r#type: FileType, 90 | pub created_at: DateTime, 91 | pub updated_at: DateTime, 92 | #[serde(default)] 93 | pub size: u64, 94 | } 95 | 96 | impl AliyunFile { 97 | pub fn new_root() -> Self { 98 | let now = SystemTime::now(); 99 | Self { 100 | name: "/".to_string(), 101 | id: "root".to_string(), 102 | r#type: FileType::Folder, 103 | created_at: DateTime(now), 104 | updated_at: DateTime(now), 105 | size: 0, 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use libc::c_int; 2 | 3 | #[derive(Debug, Clone, Copy)] 4 | pub enum Error { 5 | NoEntry, 6 | ParentNotFound, 7 | ChildNotFound, 8 | ApiCallFailed, 9 | } 10 | 11 | impl From for c_int { 12 | fn from(e: Error) -> Self { 13 | match e { 14 | Error::NoEntry => libc::ENOENT, 15 | Error::ParentNotFound => libc::ENOENT, 16 | Error::ChildNotFound => libc::ENOENT, 17 | Error::ApiCallFailed => libc::EIO, 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/file_cache.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use bytes::Bytes; 4 | use tracing::debug; 5 | 6 | use crate::error::Error; 7 | use crate::AliyunDrive; 8 | 9 | #[derive(Debug)] 10 | struct CachedFile { 11 | file_id: String, 12 | file_size: u64, 13 | start_pos: i64, 14 | buffer: Bytes, 15 | } 16 | 17 | #[derive(Debug)] 18 | pub struct FileCache { 19 | drive: AliyunDrive, 20 | read_buffer_size: usize, 21 | // file handle -> cached file 22 | cache: BTreeMap, 23 | } 24 | 25 | impl FileCache { 26 | pub fn new(drive: AliyunDrive, read_buffer_size: usize) -> Self { 27 | Self { 28 | drive, 29 | read_buffer_size, 30 | cache: BTreeMap::new(), 31 | } 32 | } 33 | 34 | fn read_chunk(&self, file_id: &str, file_size: u64, offset: i64) -> Result { 35 | let size = std::cmp::min( 36 | self.read_buffer_size, 37 | file_size.saturating_sub(offset as u64) as usize, 38 | ); 39 | let download_url = self 40 | .drive 41 | .get_download_url(file_id) 42 | .map_err(|_| Error::ApiCallFailed)?; 43 | let data = self 44 | .drive 45 | .download(&download_url, offset as _, size) 46 | .map_err(|_| Error::ApiCallFailed)?; 47 | Ok(data) 48 | } 49 | 50 | pub fn read(&mut self, fh: u64, offset: i64, size: u32) -> Result { 51 | let cached = self.cache.get(&fh).ok_or(Error::NoEntry)?; 52 | let start_pos = cached.start_pos; 53 | let end_pos = offset + i64::from(size); 54 | let buf_size = cached.buffer.len(); 55 | debug!( 56 | fh = fh, 57 | offset = offset, 58 | size = size, 59 | buffer_start = start_pos, 60 | buffer_size = buf_size, 61 | "read file cache" 62 | ); 63 | if offset >= start_pos && end_pos <= start_pos + buf_size as i64 { 64 | let buf_start = (offset - start_pos) as usize; 65 | let buf_end = buf_start + size as usize; 66 | let data = cached.buffer.slice(buf_start..buf_end); 67 | return Ok(data); 68 | } 69 | let chunk = self.read_chunk(&cached.file_id, cached.file_size, offset)?; 70 | let new_cached = CachedFile { 71 | file_id: cached.file_id.clone(), 72 | file_size: cached.file_size, 73 | start_pos: offset, 74 | buffer: chunk.clone(), 75 | }; 76 | self.cache.insert(fh, new_cached); 77 | 78 | // chunk size maybe less than size 79 | let size = if chunk.len() >= size as usize { 80 | size as usize 81 | } else { 82 | chunk.len() 83 | }; 84 | Ok(chunk.slice(..size as usize)) 85 | } 86 | 87 | pub fn open(&mut self, fh: u64, file_id: String, file_size: u64) { 88 | let file = CachedFile { 89 | file_id, 90 | file_size, 91 | start_pos: 0, 92 | buffer: Bytes::new(), 93 | }; 94 | self.cache.insert(fh, file); 95 | } 96 | 97 | pub fn release(&mut self, fh: u64) { 98 | self.cache.remove(&fh); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{env, io, path::PathBuf}; 2 | 3 | use clap::Parser; 4 | use fuser::MountOption; 5 | 6 | use drive::{AliyunDrive, DriveConfig}; 7 | use vfs::AliyunDriveFileSystem; 8 | 9 | mod drive; 10 | mod error; 11 | mod file_cache; 12 | mod vfs; 13 | 14 | #[derive(Parser, Debug)] 15 | #[command(name = "aliyundrive-fuse", about, version, author)] 16 | struct Opt { 17 | /// Mount point 18 | #[arg(long)] 19 | path: PathBuf, 20 | /// Aliyun drive refresh token 21 | #[arg(short, long, env = "REFRESH_TOKEN")] 22 | refresh_token: String, 23 | /// Working directory, refresh_token will be stored in there if specified 24 | #[arg(short = 'w', long)] 25 | workdir: Option, 26 | /// Allow other users to access the drive 27 | #[arg(long)] 28 | allow_other: bool, 29 | /// Read/download buffer size in bytes, defaults to 10MB 30 | #[arg(short = 'S', long, default_value = "10485760")] 31 | read_buffer_size: usize, 32 | } 33 | 34 | fn main() -> anyhow::Result<()> { 35 | #[cfg(feature = "native-tls-vendored")] 36 | openssl_probe::init_ssl_cert_env_vars(); 37 | 38 | if env::var("RUST_LOG").is_err() { 39 | env::set_var("RUST_LOG", "aliyundrive_fuse=info"); 40 | } 41 | tracing_subscriber::fmt::init(); 42 | 43 | let opt = Opt::parse(); 44 | let drive_config = DriveConfig { 45 | api_base_url: "https://api.aliyundrive.com".to_string(), 46 | refresh_token_url: "https://api.aliyundrive.com/token/refresh".to_string(), 47 | workdir: opt.workdir, 48 | app_id: None, 49 | }; 50 | let drive = AliyunDrive::new(drive_config, opt.refresh_token).map_err(|_| { 51 | io::Error::new(io::ErrorKind::Other, "initialize aliyundrive client failed") 52 | })?; 53 | 54 | let _nick_name = drive.nick_name.clone(); 55 | let vfs = AliyunDriveFileSystem::new(drive, opt.read_buffer_size); 56 | let mut mount_options = vec![MountOption::AutoUnmount, MountOption::NoAtime]; 57 | // read only for now 58 | mount_options.push(MountOption::RO); 59 | if opt.allow_other { 60 | mount_options.push(MountOption::AllowOther); 61 | } 62 | if cfg!(target_os = "macos") { 63 | mount_options.push(MountOption::CUSTOM("local".to_string())); 64 | mount_options.push(MountOption::CUSTOM("noappledouble".to_string())); 65 | let volname = if let Some(nick_name) = _nick_name { 66 | format!("volname=阿里云盘({})", nick_name) 67 | } else { 68 | "volname=阿里云盘".to_string() 69 | }; 70 | mount_options.push(MountOption::CUSTOM(volname)); 71 | } 72 | fuser::mount2(vfs, opt.path, &mount_options)?; 73 | Ok(()) 74 | } 75 | -------------------------------------------------------------------------------- /src/vfs.rs: -------------------------------------------------------------------------------- 1 | //! FUSE adaptor 2 | //! 3 | //! https://github.com/gz/btfs is used as a reference. 4 | use std::ffi::{OsStr, OsString}; 5 | use std::path::Path; 6 | use std::time::UNIX_EPOCH; 7 | use std::{collections::BTreeMap, time::Duration}; 8 | 9 | use bytes::Bytes; 10 | use fuser::{ 11 | FileAttr, FileType, Filesystem, ReplyAttr, ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry, 12 | ReplyOpen, Request, FUSE_ROOT_ID, 13 | }; 14 | use tracing::debug; 15 | 16 | use crate::drive::{AliyunDrive, AliyunFile}; 17 | use crate::error::Error; 18 | use crate::file_cache::FileCache; 19 | 20 | const TTL: Duration = Duration::from_secs(1); 21 | const BLOCK_SIZE: u64 = 4194304; 22 | 23 | #[derive(Debug, Clone)] 24 | pub struct Inode { 25 | children: BTreeMap, 26 | parent: u64, 27 | } 28 | 29 | impl Inode { 30 | fn new(parent: u64) -> Self { 31 | Self { 32 | children: BTreeMap::new(), 33 | parent, 34 | } 35 | } 36 | 37 | fn add_child(&mut self, name: OsString, inode: u64) { 38 | self.children.insert(name, inode); 39 | } 40 | } 41 | 42 | pub struct AliyunDriveFileSystem { 43 | drive: AliyunDrive, 44 | file_cache: FileCache, 45 | files: BTreeMap, 46 | inodes: BTreeMap, 47 | next_inode: u64, 48 | next_fh: u64, 49 | } 50 | 51 | impl AliyunDriveFileSystem { 52 | pub fn new(drive: AliyunDrive, read_buffer_size: usize) -> Self { 53 | let file_cache = FileCache::new(drive.clone(), read_buffer_size); 54 | Self { 55 | drive, 56 | file_cache, 57 | files: BTreeMap::new(), 58 | inodes: BTreeMap::new(), 59 | next_inode: 1, 60 | next_fh: 2, 61 | } 62 | } 63 | 64 | /// Next inode number 65 | fn next_inode(&mut self) -> u64 { 66 | self.next_inode = self.next_inode.wrapping_add(1); 67 | self.next_inode 68 | } 69 | 70 | /// Next file handler 71 | fn next_fh(&mut self) -> u64 { 72 | self.next_fh = self.next_fh.wrapping_add(1); 73 | self.next_fh 74 | } 75 | 76 | fn init(&mut self) -> Result<(), Error> { 77 | let mut root_file = AliyunFile::new_root(); 78 | let (used_size, _) = self.drive.get_quota().map_err(|_| Error::ApiCallFailed)?; 79 | root_file.size = used_size; 80 | let root_inode = Inode::new(0); 81 | self.inodes.insert(FUSE_ROOT_ID, root_inode); 82 | self.files.insert(FUSE_ROOT_ID, root_file); 83 | Ok(()) 84 | } 85 | 86 | fn lookup(&mut self, parent: u64, name: &OsStr) -> Result { 87 | let mut parent_inode = self 88 | .inodes 89 | .get(&parent) 90 | .ok_or(Error::ParentNotFound)? 91 | .clone(); 92 | if parent_inode.children.is_empty() { 93 | // Parent inode isn't loaded yet 94 | debug!(parent = parent, "readdir missing parent in lookup"); 95 | self.readdir(parent, 0)?; 96 | parent_inode = self 97 | .inodes 98 | .get(&parent) 99 | .ok_or(Error::ParentNotFound)? 100 | .clone(); 101 | } 102 | let inode = parent_inode 103 | .children 104 | .get(name) 105 | .ok_or(Error::ChildNotFound)?; 106 | let file = self.files.get(inode).ok_or(Error::NoEntry)?; 107 | Ok(file.to_file_attr(*inode)) 108 | } 109 | 110 | fn readdir(&mut self, ino: u64, offset: i64) -> Result, Error> { 111 | let mut entries = Vec::new(); 112 | let mut inode = self.inodes.get(&ino).ok_or(Error::NoEntry)?.clone(); 113 | 114 | if offset == 0 { 115 | entries.push((ino, FileType::Directory, ".".to_string())); 116 | entries.push((inode.parent, FileType::Directory, String::from(".."))); 117 | 118 | let file = self.files.get(&ino).ok_or(Error::NoEntry)?; 119 | let parent_file_id = &file.id; 120 | let files = self 121 | .drive 122 | .list_all(parent_file_id) 123 | .map_err(|_| Error::ApiCallFailed)?; 124 | debug!( 125 | inode = ino, 126 | "total {} files in directory {}", 127 | files.len(), 128 | file.name 129 | ); 130 | 131 | let mut to_remove = inode.children.keys().cloned().collect::>(); 132 | for file in &files { 133 | let name = OsString::from(file.name.clone()); 134 | if inode.children.contains_key(&name) { 135 | // file already exists 136 | to_remove.retain(|n| n != &name); 137 | } else { 138 | let new_inode = self.next_inode(); 139 | inode.add_child(name, new_inode); 140 | self.files.insert(new_inode, file.clone()); 141 | self.inodes 142 | .entry(new_inode) 143 | .or_insert_with(|| Inode::new(ino)); 144 | } 145 | } 146 | 147 | if !to_remove.is_empty() { 148 | for name in to_remove { 149 | if let Some(ino_remove) = inode.children.remove(&name) { 150 | debug!(inode = ino_remove, name = %Path::new(&name).display(), "remove outdated inode"); 151 | self.files.remove(&ino_remove); 152 | self.inodes.remove(&ino_remove); 153 | } 154 | } 155 | } 156 | self.inodes.insert(ino, inode.clone()); 157 | } 158 | 159 | for child_ino in inode.children.values().skip(offset as usize) { 160 | let file = self.files.get(child_ino).ok_or(Error::ChildNotFound)?; 161 | entries.push((*child_ino, file.r#type.into(), file.name.clone())); 162 | } 163 | Ok(entries) 164 | } 165 | 166 | fn read(&mut self, ino: u64, fh: u64, offset: i64, size: u32) -> Result { 167 | let file = self.files.get(&ino).ok_or(Error::NoEntry)?; 168 | debug!(inode = ino, name = %file.name, fh = fh, offset = offset, size = size, "read"); 169 | if offset >= file.size as i64 { 170 | return Ok(Bytes::new()); 171 | } 172 | let size = std::cmp::min(size, file.size.saturating_sub(offset as u64) as u32); 173 | self.file_cache.read(fh, offset, size) 174 | } 175 | } 176 | 177 | impl Filesystem for AliyunDriveFileSystem { 178 | fn init( 179 | &mut self, 180 | _req: &Request<'_>, 181 | _config: &mut fuser::KernelConfig, 182 | ) -> Result<(), libc::c_int> { 183 | if let Err(e) = self.init() { 184 | return Err(e.into()); 185 | } 186 | Ok(()) 187 | } 188 | 189 | fn lookup(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) { 190 | let dirname = Path::new(name); 191 | debug!(parent = parent, name = %dirname.display(), "lookup"); 192 | match self.lookup(parent, name) { 193 | Ok(attr) => reply.entry(&TTL, &attr, 0), 194 | Err(e) => reply.error(e.into()), 195 | } 196 | } 197 | 198 | fn getattr(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyAttr) { 199 | if let Some(file) = self.files.get(&ino) { 200 | debug!(inode = ino, name = %file.name, "getattr"); 201 | reply.attr(&TTL, &file.to_file_attr(ino)) 202 | } else { 203 | debug!(inode = ino, "getattr"); 204 | reply.error(libc::ENOENT); 205 | } 206 | } 207 | 208 | fn readdir( 209 | &mut self, 210 | _req: &Request<'_>, 211 | ino: u64, 212 | _fh: u64, 213 | offset: i64, 214 | mut reply: ReplyDirectory, 215 | ) { 216 | debug!(inode = ino, offset = offset, "readdir"); 217 | match self.readdir(ino, offset) { 218 | Ok(entries) => { 219 | // Offset of 0 means no offset. 220 | // Non-zero offset means the passed offset has already been seen, 221 | // and we should start after it. 222 | let offset_add = if offset == 0 { 0 } else { offset + 1 }; 223 | for (i, (ino, kind, name)) in entries.into_iter().enumerate() { 224 | let buffer_full = reply.add(ino, offset_add + i as i64, kind, name); 225 | if buffer_full { 226 | break; 227 | } 228 | } 229 | reply.ok(); 230 | } 231 | Err(e) => reply.error(e.into()), 232 | } 233 | } 234 | 235 | fn open(&mut self, _req: &Request<'_>, ino: u64, _flags: i32, reply: ReplyOpen) { 236 | if let Some((file_id, file_name, file_size)) = self 237 | .files 238 | .get(&ino) 239 | .map(|f| (f.id.clone(), f.name.clone(), f.size)) 240 | { 241 | debug!(inode = ino, name = %file_name, "open file"); 242 | let fh = self.next_fh(); 243 | self.file_cache.open(fh, file_id, file_size); 244 | reply.opened(fh, 0); 245 | } else { 246 | debug!(inode = ino, "open file"); 247 | reply.error(libc::ENOENT); 248 | } 249 | } 250 | 251 | fn release( 252 | &mut self, 253 | _req: &Request<'_>, 254 | ino: u64, 255 | fh: u64, 256 | _flags: i32, 257 | _lock_owner: Option, 258 | _flush: bool, 259 | reply: ReplyEmpty, 260 | ) { 261 | debug!(inode = ino, fh = fh, "release file"); 262 | self.file_cache.release(fh); 263 | reply.ok(); 264 | } 265 | 266 | fn read( 267 | &mut self, 268 | _req: &Request<'_>, 269 | ino: u64, 270 | fh: u64, 271 | offset: i64, 272 | size: u32, 273 | _flags: i32, 274 | _lock_owner: Option, 275 | reply: ReplyData, 276 | ) { 277 | match self.read(ino, fh, offset, size) { 278 | Ok(data) => reply.data(&data), 279 | Err(e) => reply.error(e.into()), 280 | } 281 | } 282 | } 283 | 284 | impl From for FileType { 285 | fn from(typ: crate::drive::FileType) -> Self { 286 | use crate::drive::FileType as AliyunFileType; 287 | 288 | match typ { 289 | AliyunFileType::Folder => FileType::Directory, 290 | AliyunFileType::File => FileType::RegularFile, 291 | } 292 | } 293 | } 294 | 295 | impl AliyunFile { 296 | fn to_file_attr(&self, ino: u64) -> FileAttr { 297 | let kind = self.r#type.into(); 298 | let perm = if matches!(kind, FileType::Directory) { 299 | 0o755 300 | } else { 301 | 0o644 302 | }; 303 | let nlink = if ino == FUSE_ROOT_ID { 2 } else { 1 }; 304 | let uid = unsafe { libc::getuid() }; 305 | let gid = unsafe { libc::getgid() }; 306 | let blksize = BLOCK_SIZE; 307 | let blocks = self.size / blksize + 1; 308 | FileAttr { 309 | ino, 310 | size: self.size, 311 | blocks, 312 | atime: UNIX_EPOCH, 313 | mtime: *self.updated_at, 314 | ctime: *self.created_at, 315 | crtime: *self.created_at, 316 | kind, 317 | perm, 318 | nlink, 319 | uid, 320 | gid, 321 | rdev: 0, 322 | blksize: blksize as u32, 323 | flags: 0, 324 | } 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /systemd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=AliyunDrive FUSE 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | PermissionsStartOnly=true 8 | ExecStartPre=/bin/mkdir -p /var/run/aliyundrive-fuse -p /mnt/aliyundrive 9 | Environment="REFRESH_TOKEN=" 10 | ExecStart=/usr/bin/aliyundrive-fuse --allow-other -w /var/run/aliyundrive-fuse /mnt/aliyundrive 11 | KillMode=process 12 | Restart=on-failure 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | --------------------------------------------------------------------------------