Compare commits

...

647 Commits

Author SHA1 Message Date
Andrew Gaul 5c1932f702
Upgrade CI to Ubuntu 24.04 LTS (#2456) 2024-05-12 11:12:25 +09:00
LiuBingrun ccdcccd44c
Fix DeadLock in FdManager::ChangeEntityToTempPath (#2455)
commit e3b50ad introduce smart pointer to manage FdEntity

But in ChangeEntityToTempPath, we should not destroy the entity.

We should move the entry to the temp ky

Signed-off-by: liubingrun <liubr1@chinatelecom.cn>
2024-05-11 11:29:40 +09:00
Andrew Gaul 3864f58c22
Upgrade CI to Fedora 40 (#2451) 2024-05-11 09:25:05 +09:00
Takeshi Nakatani c36827d1de
Fixed README.md for Github Action Badge URL (#2449) 2024-04-28 20:10:53 +09:00
Takeshi Nakatani e2cc36a37f
Updated COMPILATION.md about compilation on linux (#2445) 2024-04-28 14:31:01 +09:00
Takeshi Nakatani cf6102f91b
Changed due to s3fs-fuse logo change (#2448) 2024-04-28 14:28:24 +09:00
Sébastien Brochet dd6815b90f retry request on HTTP 429 error 2024-04-14 12:09:26 +09:00
Takeshi Nakatani 95026804e9 Support SSL client cert and added ssl_client_cert option 2024-04-14 10:21:48 +09:00
Takeshi Nakatani 9ab5a2ea73 Fixed configure error for GHA:sanitize_thread 2024-03-19 21:37:19 +09:00
Takeshi Nakatani a5cdd05c25 Added ipresolve option 2024-03-13 22:29:17 +09:00
Andrew Gaul 31676f6201
Convert thpoolman_param to value (#2430)
This simplifies memory management.
2024-03-13 21:27:12 +09:00
Andrew Gaul c97f7a2a13
Address clang-tidy 18 warnings (#2428) 2024-03-07 01:04:22 +09:00
Andrew Gaul be54c34ecb
Remove unneeded XML macros (#2427) 2024-03-07 00:45:34 +09:00
Andrew Gaul 79597c7960
Upgrade CI to Alpine 3.19 (#2429) 2024-03-07 00:23:00 +09:00
Andrew Gaul 70a30d6e26 Update ChangeLog and configure.ac for 1.94
Fixes #2420.
2024-02-25 13:08:43 +09:00
Takeshi Nakatani b97fd470a5 Abort for SSE-KMS encryption type and not SSL/TLS specified 2024-02-23 13:11:56 +09:00
Andrew Gaul 4d7fd60305
Call abort instead of exit in tests (#2416)
This can give useful core dumps.
2024-02-23 12:28:29 +09:00
Takeshi Nakatani da38dc73ad Gentoo + libxml2-2.12 requires inclusion of parser.h 2024-02-20 08:28:42 +09:00
Takeshi Nakatani e89adf6633 Fixed a bug that mounting with ksmid specified to fail 2024-02-18 21:18:50 +09:00
Takeshi Nakatani fa2bcfc60d Fixed a bug in multi head request parameter 2024-02-12 17:37:03 +09:00
Takeshi Nakatani ed1d431a1f Improved to output error details when bucket check fails 2024-02-12 17:36:47 +09:00
Takeshi Nakatani 67442cf054 Changed the level of messages by the get_base_exp function 2024-02-12 17:35:45 +09:00
Takeshi Nakatani a7186b6072 Updated actions/checkout from v3 to v4 2024-02-07 21:29:42 +09:00
Takeshi Nakatani 517574c40c Fixed a bug in fdatasync(fsync) 2024-02-06 14:11:37 +09:00
Jason Carpenter 5e6f21a9ff
fix: ListBucket edge cases (#2399) 2024-02-03 13:24:40 +09:00
Takeshi Nakatani 54aa278df0
Fixed errors reported by cppcheck 2.13.0 (#2400) 2024-01-25 00:46:45 +09:00
Takeshi Nakatani 2f9fb74a42
Corrected list_bucket to search in stat cache during creating new file (#2376) 2024-01-24 22:10:14 +09:00
Andrew Gaul b82632547c
Replace miscellaneous pointers with unique_ptr (#2388) 2023-12-23 13:06:41 +09:00
Andrew Gaul e3b50ad3e1
Convert FdEntity to std::unique_ptr (#2383) 2023-12-07 23:56:35 +09:00
Andrew Gaul b139507ae6
Simplify locking with C++11 atomics (#2382) 2023-11-27 01:12:49 +09:00
Andrew Gaul feb0845103
Use JDK 21 for Ubuntu 23.10 (#2380) 2023-11-27 00:55:35 +09:00
Andrew Gaul f041812939
Revert "Call C++11 get_time and put_time (#2375)" (#2381)
This reverts commit 10a72bfd0f.  These
commit is incompatible with older CentOS 7 libstdc++.
2023-11-27 00:51:17 +09:00
Andrew Gaul 2b57e74330
Use std::unique_ptr in threadpoolman (#2374) 2023-11-26 01:49:17 +09:00
Andrew Gaul b671fa7a9c
Pass std::unique_ptr by value (#2373)
This ensures that the parameter is moved.
2023-11-26 01:48:47 +09:00
Andrew Gaul 691669749e
Remove obsolete C++11 #ifdef (#2377) 2023-11-21 00:37:42 +09:00
Andrew Gaul 10a72bfd0f
Call C++11 get_time and put_time (#2375)
This removes workarounds and fixed-length buffers.
2023-11-20 18:45:27 +09:00
Andrew Gaul 43f81b76af
Enable clang-tidy CERT warnings (#2371) 2023-11-19 10:00:42 +09:00
Andrew Gaul 68bbfee8ea
Address clang-tidy modernize-deprecated-headers (#2370) 2023-11-19 10:00:16 +09:00
Takeshi Nakatani ec8caf64b8 Reverted the macos CI process(using macos-fuse-t) 2023-11-17 21:08:34 +09:00
Eryu Guan bcacca6599 s3fs: make dir size not zero
Directory has size 0, which looks weired and may confuse users. So fake
dir size as 4k.

Signed-off-by: Eryu Guan <eguan@linux.alibaba.com>
2023-11-17 17:55:57 +09:00
Takeshi Nakatani 4fdd2456d4 Fixed a script of not functioning environment in Github Actions 2023-11-15 23:17:26 +09:00
Takeshi Nakatani 3f6e8a8707 Fixed indent in integration-test-main.sh 2023-11-15 23:15:40 +09:00
Andrew Gaul 4845831f93
Convert some const to constexpr (#2342)
This guarantees that the function or value will resolve at compile-time.
2023-11-14 22:15:17 +09:00
Andrew Gaul 919575f312
Upgrade CI to Fedora 39 (#2365) 2023-11-12 16:01:09 +09:00
Andrew Gaul 0cd73e406d
Address clang-tidy 17 warnings (#2362) 2023-11-12 11:48:08 +09:00
Andrew Gaul 807ec1f6f7
Upgrade CI to Alpine 3.18 (#2332)
References #2328.
2023-11-12 11:08:44 +09:00
Takeshi Nakatani e2ac9b45e8 Re-improved updating of temporary stat cache when new file 2023-11-11 07:45:32 +09:00
Takeshi Nakatani b15ed13807 Force disk free space recovery in test(for only macos) 2023-11-10 15:51:22 +09:00
Takeshi Nakatani f9d3941d9d Fixed a bug in the re-upload part of Streamupload 2023-11-10 10:41:26 +09:00
Takeshi Nakatani 34c379babb Improved updating of temporary stat cache while creating a file 2023-11-10 10:23:15 +09:00
Takeshi Nakatani 7b5111c955 Suppress some message levels on macos 2023-11-09 14:16:43 +09:00
Andrew Gaul a3964b3fcd
Upgrade CI to Ubuntu 23.10 (#2355) 2023-10-22 22:18:29 +09:00
AdamQQQ 3856637cd2
s3fs: add option free_space_ratio to control cache size (#2351)
* Try to cleanup cache directory when initing without enough disk space

Also optimize log messages to print detailed errors to the user.

Signed-off-by: Qinqi Qu <quqinqi@linux.alibaba.com>

* s3fs: add option free_space_ratio to control cache size

Since the ensure_diskfree option is not convenient enough, we have added
a new option "-o free_space_ratio" to control the space used by the s3fs
cache based on the current disk size.

The value of this option can be between 0 and 100. It will control the
size of the cache according to this ratio to ensure that the idle ratio
of the disk is greater than this value.

For example, when the value is 10 and the disk space is 50GB, it will
ensure that the disk will reserve at least 50GB * 10% = 5GB of remaining
space.

Signed-off-by: Qinqi Qu <quqinqi@linux.alibaba.com>

---------

Signed-off-by: Qinqi Qu <quqinqi@linux.alibaba.com>
2023-10-20 18:11:47 +09:00
Jan Stastny 2871975d1e Set SSE headers when checking bucket 2023-10-18 21:45:47 +09:00
Andrew Gaul d5dd17644d
Add a helper script to compile all targets (#2337)
This is useful to compile different SSL libraries and 32-bit targets.
2023-10-15 11:54:52 +09:00
Takeshi Nakatani 5e5b4f0a49 Fixed ETag parsing at completing the Multipart upload part 2023-10-13 11:13:52 +09:00
Andrew Gaul e5b15bed7d
Pass by value to trim functions (#2345)
These already force a copy so passing by value has the same
performance but is simpler.  But this allows the compiler to perform
copy elision on temporaries and the caller to explicitly std::move in
others.
2023-10-12 22:21:33 +09:00
Takeshi Nakatani 2e4a6928c3 Changed argument name in ParallelMultipartUploadAll 2023-10-09 13:17:25 +09:00
Qinqi Qu 1aa77f6cda s3fs_cred: print detailed error message when stat file fails
Signed-off-by: Qinqi Qu <quqinqi@linux.alibaba.com>
2023-10-09 13:11:47 +09:00
Takeshi Nakatani d0c4b5c763 Fixed a bug in exclusive control of pagelist in FdEntity class 2023-09-26 21:26:17 +09:00
Andrew Gaul 361e10d09c
Add scope_guard for ad-hoc resource management (#2313)
References #2261.  Suggested by:
https://stackoverflow.com/questions/10270328/the-simplest-and-neatest-c11-scopeguard
2023-09-26 07:52:55 +09:00
Andrew Gaul 95cfbe30ed
Add error checking to test_concurrent_writes (#2299)
This reveals a situation where s3fs triggers an unexpected
EntityTooSmall error.
2023-09-26 07:32:02 +09:00
Andrew Gaul 87b8bafaea
Address unknown pragma warning with GCC (#2324) 2023-09-26 01:16:14 +09:00
Andrew Gaul 1a703e623a
Remove volatile qualifiers deprecated in C++23 (#2323)
These are protected by upload_list_lock.  Addresses warnings of the
form:

warning: ‘++’ expression of ‘volatile’-qualified type is deprecated
2023-09-26 00:15:05 +09:00
Andrew Gaul 1ebefca029
Reorder $CXXFLAGS to the end (#2322)
This allows overriding flags like -std=c++11.
2023-09-26 00:05:54 +09:00
Andrew Gaul ffff26e165
Add stat helper for user and group (#2320) 2023-09-26 00:04:24 +09:00
Andrew Gaul 61df7bf42c
Use std::unique_ptr for fclose (#2318)
References #2261.
2023-09-25 23:55:11 +09:00
Andrew Gaul c5fb42ff10
Use std::unique_ptr in libxml functions (#2317)
References #2261.
2023-09-25 23:46:52 +09:00
Takeshi Nakatani cbc33cd7ae Fixed a bug upload boundary calculation in StreamUpload 2023-09-25 09:28:37 +09:00
Takeshi Nakatani 645c10a3c3 Fixed test_not_existed_dir_obj test condition 2023-09-25 08:16:32 +09:00
Takeshi Nakatani 54293a66b3 Simplify the determination of the --cached option of the stat 2023-09-25 08:08:58 +09:00
Takeshi Nakatani 01b3caa38c Fixed errors of cppcheck 2.12.0 2023-09-24 19:55:02 +09:00
Andrew Gaul 64642e1d1b
Do not cache stat attributes (#2319)
This is a workaround for CI failures.
2023-09-24 18:32:07 +09:00
Iain Samuel McLean Elder 546cdd0d91 Improve docs on environment variables 2023-09-21 12:40:46 +09:00
Andrew Gaul a83f4a48d0
Add extra logging to debug test (#2316) 2023-09-15 21:50:01 +09:00
Andrew Gaul 99d3e68d59
Revert ls change (#2315)
echo does not split the words on newlines.
2023-09-15 21:48:04 +09:00
Andrew Gaul 01189e99fc
Store mvnode in vector instead of manual linked list (#2312)
This simplifies code and avoids manual memory management.  References #2261.
2023-09-13 22:32:15 +09:00
Andrew Gaul f493cb5846
Remove unnecessary uses of ls (#2311)
Other call sites need the call to readdir/getdents64.
2023-09-13 22:27:12 +09:00
Andrew Gaul e9814b4a4d Add Debian bookworm to CI 2023-09-11 00:59:23 +09:00
Andrew Gaul 3b12aaf2ab
Do not escape percent (#2310)
This addresses warnings of the form:

grep: warning: stray \ before %
2023-09-10 12:51:36 +09:00
Andrew Gaul 7e20278489
Address some Shellcheck SC2012 warnings (#2306) 2023-09-10 12:50:18 +09:00
Andrew Gaul 3d73d5a687
Delete unneeded constructors and assignment operators (#2309) 2023-09-06 23:52:10 +09:00
Andrew Gaul fa3a472c6b
Remove several calls to free (#2308) 2023-09-06 23:50:33 +09:00
Andrew Gaul 5f38301861
Emit unexpected file names in failed test_list (#2307) 2023-09-06 23:47:12 +09:00
Andrew Gaul 4d5632912a
Initialize variable before use (#2302)
clang-analyzer found a path where this could be used without
initialization.
2023-09-06 23:32:49 +09:00
Takeshi Nakatani a74034a012 Fixed a bug with setting the statvfs value 2023-09-05 09:03:11 -07:00
Andrew Gaul 3f64c72c24
Explicitly grep for ps args (#2301) 2023-09-03 22:03:45 +09:00
AdamQQQ 68c45ce791
s3fs: print unmounting hint when the mount point is stale (#2295)
When the error code returned by the stat information of the mount point
is ENOTCONN, print unmount command hint for user to fix.

Signed-off-by: Qinqi Qu <quqinqi@linux.alibaba.com>
2023-09-03 10:50:09 +09:00
Andrew Gaul e8cb6d6d34
Abort after failed MPU (#2298)
This reclaims storage after a failed MPU which caused OutOfMemory
issues in #2291.
2023-08-29 23:29:16 +09:00
Andrew Gaul a2f2f72aaf
Enable Valgrind in CI (#2297)
Using HTTP instead of HTTPS and
82107f4b6c improve test run-time so that
this is now feasible.
2023-08-29 23:11:26 +09:00
Andrew Gaul 7bb9609827
Return errors from AutoFdEntity::Open (#2296)
Found via pjdfstest which creates a PATH_MAX path that should return
NAMETOOLONG.
2023-08-29 22:57:30 +09:00
Andrew Gaul 82107f4b6c
Skip is_uid_include_group when GID available (#2292)
This can avoid an expensive computation which is 20% of test runtime.
2023-08-27 15:24:33 +09:00
Eryu Guan ee49ca4abf
s3fs: print fuse context in s3fs fuse operations (#2274)
Print fuse context like pid in fuse operations, so we know which process
is triggering this operation.

Signed-off-by: Eryu Guan <eguan@linux.alibaba.com>
2023-08-27 14:00:57 +09:00
Andrew Gaul 48548f0896
Remove unused functions (#2289) 2023-08-22 23:17:43 +09:00
Andrew Gaul 981e97ee76
Use default move constructor (#2288)
This is identical to the explicit one.
2023-08-22 23:14:09 +09:00
Andrew Gaul a568aa70fd
Replace uses of emplace with operator=(&&) (#2287)
emplace does not overwrite the value if the key already exists.  C++17 has
insert_or_assign but C++11 only has operator= which invokes the default
constructor.  Follows on to 6781ef5bd1.
2023-08-22 23:12:12 +09:00
Takeshi Nakatani 218adcb29b
Fixed errors in cache.cpp from cppcheck 2.11.1 (#2286) 2023-08-20 19:00:20 +09:00
Andrew Gaul 6c55bcfdd8
Own values in add_header (#2285)
Also fix up indentation.
2023-08-20 18:59:18 +09:00
Andrew Gaul 8d04ee3e01
Own values in stat_cache and symlink_cache (#2284)
This removes an unnecessary use of unique_ptr.
2023-08-20 12:10:47 +09:00
Takeshi Nakatani 6781ef5bd1 Reverted to direct array access instead of std::map emplace 2023-08-20 09:44:38 +09:00
Andrew Gaul 7e94b64ae7
Use unique_ptr in SSL functions (#2282)
References #2261.
2023-08-19 23:29:00 +09:00
Andrew Gaul 64a142723a
Document environment variable configuration (#2281) 2023-08-19 23:23:05 +09:00
Andrew Gaul 50f6c38c84
Replace xattr_value with std::string (#2280) 2023-08-19 11:12:43 +09:00
Andrew Gaul 9fb4c32c6a
Test filenames longer than POSIX maximum (#2277) 2023-08-18 08:58:44 +09:00
Takeshi Nakatani 280ed5d706
Additional fix for #2276(Convert BodyData to std::string) (#2278) 2023-08-18 00:35:50 +09:00
Andrew Gaul 2518ff3568
Convert BodyData to std::string (#2276)
This is simpler and avoids some copies.
2023-08-17 22:49:41 +09:00
Andrew Gaul c65ce8a42c
Add clang-tidy to CI (#2270) 2023-08-17 22:42:11 +09:00
Andrew Gaul e5986d0034
Run all tests with sanitizers (#2275) 2023-08-17 22:27:06 +09:00
Andrew Gaul b2bb12fd2c
Remove unneeded explicit std::string constructors (#2273)
std::string(const char*) implicitly constructs these.  The remaining call sites
requires string literals from C++14.
2023-08-17 22:12:28 +09:00
Andrew Gaul 7f30353fb9
Return std::unique_ptr from S3fsCurl callbacks (#2272)
References #2261.
2023-08-17 22:08:56 +09:00
Takeshi Nakatani 235bccced5 Added make check for src directory for Linux OS 2023-08-16 08:07:02 +09:00
Andrew Gaul 67e6b9e495
Simplify xattr_value with owned values (#2262)
References #2261.
2023-08-15 22:54:46 +09:00
Andrew Gaul ea42911530
Build s3fs in parallel like in CI (#2267) 2023-08-15 22:37:39 +09:00
Andrew Gaul 6823c5a7ec
Enable clang-tidy cppcoreguidelines (#2269) 2023-08-15 22:12:33 +09:00
Andrew Gaul d1272d296a
Tighten up CLI argument handling (#2268)
This ensures that each option is only handled once.
2023-08-15 21:45:38 +09:00
Andrew Gaul d2a571a868
Set exit code for Valgrind (#2265)
Otherwise errors can be ignored for successful tests with memory
errors.
2023-08-15 21:33:34 +09:00
Andrew Gaul d120e54284
Improve illegal bucket name error message (#2263)
This may help users debug situations like:

https://stackoverflow.com/questions/76359564/why-does-mounting-s3fs-bucket-on-centos-7-using-fstab-fail-but-mount-a-works
2023-08-15 21:31:05 +09:00
Andrew Gaul 3a6af38582
Tighten up Content-Type checking (#2258) 2023-08-15 21:23:59 +09:00
Andrew Gaul e157d811cb
Use std::string::compare and operator== where possible (#2256) 2023-08-15 21:22:36 +09:00
Andrew Gaul 56a4e67009
Replace more raw pointers with std::unique_ptr (#2255) 2023-08-14 00:03:10 +09:00
Takeshi Nakatani 5b93765802 Fixed a warning for compiling C/C++ codes 2023-08-13 20:49:39 +09:00
Takeshi Nakatani acea1d33f9 Fixed string test for s3fs_base64 2023-08-13 20:48:38 +09:00
Andrew Gaul 528a61718d
Convert manual memory allocations to std::unique_ptr (#2253) 2023-08-11 23:26:07 +09:00
Andrew Gaul c5a75a1fb2
Delete copy constructors and assignment operators (#2257)
One of these was buggy and others had the wrong parameters and return
types.
2023-08-11 13:12:03 +09:00
Andrew Gaul 3790a0f8b4
Calculate MD5 without using a temporary file (#2252)
This mirrors the SHA256 code.
2023-08-07 00:17:15 +09:00
Andrew Gaul 779afe5d62
Make help more consistent (#2251) 2023-08-06 22:25:10 +09:00
Andrew Gaul 26b5658d70
Wrap ps3fscred with std::unique_ptr (#2250)
This removes many manual memory deallocations.
2023-08-06 22:23:25 +09:00
Andrew Gaul c568a69452
Return std::string from base64 encoding function (#2248)
This is avoids manual memory allocations.
2023-08-06 22:22:02 +09:00
Andrew Gaul 13ad53eef7
Convert most std::list to std::vector (#2247)
This tends to be more efficient due to fewer allocations.  Also fix std::sort
comparator which should be strictly less than.
2023-08-05 10:05:32 +09:00
Andrew Gaul b14758baff
Fix junk_data for 32-bit platforms (#2245)
Previously this had a mismatch between size_t and unsigned long long.
2023-08-05 09:37:18 +09:00
Andrew Gaul b5c3fc0a08
Convert fixed-size allocations to C++11 std::array (#2242)
This is safer and more efficient.
2023-08-05 09:36:22 +09:00
Andrew Gaul b29f8d0f2b
Use C++ enum class for most enums (#2241)
This promotes type-safety.
2023-07-30 22:53:17 +09:00
Andrew Gaul 5699875e30
Use C++11 emplace where possible (#2240)
This is more concise and sometimes more efficient.
2023-07-30 22:51:20 +09:00
Andrew Gaul 3081e419e1
Simplify direct shellcheck download with jq (#2239) 2023-07-29 09:22:55 +09:00
Andrew Gaul a7b38a6940
Address stray warnings (#2237) 2023-07-29 09:19:18 +09:00
Andrew Gaul 1f04165a33
Convert most str callers to C++11 std::to_string (#2238)
Remaining ones handle timespec.
2023-07-28 18:21:55 +09:00
Andrew Gaul 36db898d01
Use C++11 std::map::erase return value (#2236) 2023-07-27 23:34:43 +09:00
Andrew Gaul 38a1ff42e5
Convert test binaries to C++ (#2235)
This ensures that they are compiled with a consistent compiler and set of flags
as the rest of s3fs.
2023-07-27 23:15:19 +09:00
Andrew Gaul a4a2841c05
Use C++11 nullptr instead of 0 or NULL (#2234)
This improves type-safety.
2023-07-27 21:56:58 +09:00
Andrew Gaul 0ece204393 Fix -Wshorten-64-to-32 warnings 2023-07-27 12:23:26 +09:00
Andrew Gaul 6344d74ae3
Replace some raw pointers with std::unique_ptr (#2195)
This simplifies code paths and makes memory leaks less likely.  It
also makes memory ownership more explicit by requiring std::move.
This commit requires C++11.  References #2179.
2023-07-27 09:12:28 +09:00
Takeshi Nakatani faec0d9d15 Refixed for cppcheck 2.1x 2023-07-26 07:55:33 +09:00
Andrew Gaul e14a2eb94b
Add AWS CLI config for use_sse=custom (#2230) 2023-07-25 23:31:20 +09:00
Takeshi Nakatani cb3dc28e6e Supported cppcheck 2.10(and changed std from c++03 to c++11 for RHEL7) 2023-07-25 08:08:06 +09:00
Takeshi Nakatani 38dc65180b Fixed checking cppcheck version in ci.yml 2023-07-24 11:07:31 +09:00
Andrew Gaul 2405706643
Insert SSE headers when appropriate (#2228)
References #2218.  References #2227.
2023-07-23 16:17:34 +09:00
Andrew Gaul 5371cd1468
Update ChangeLog and configure.ac for 1.93 (#2225)
Fixes #2213.
2023-07-19 22:31:43 +09:00
Eryu Guan 7978395083 Use smart pointer to manage pcfstat object
Previously pcfstat points to a raw pointer, and it may be leaked if
function returned before deleting it.

So use smart pointer to automatically release the object.

Note that currently s3fs only uses c++03, so we use auto_ptr here, not
unique_ptr, which requires c++11.

Fixes: 6ca5a24a7f ("Fix two inconsistency issues between stat cache and cache file (#2152)")
Signed-off-by: Eryu Guan <eguan@linux.alibaba.com>
2023-07-19 20:15:55 +09:00
Takeshi Nakatani d0a944fcaa Fixed data race about fuse_fill_dir_t function and data pointer 2023-07-14 22:32:12 +09:00
Andrew Gaul 537384e9b5
Guard filler calls with filled check (#2215)
Follows on to e650d8c55c.
2023-07-13 22:46:19 +09:00
Andrew Gaul e650d8c55c
Explicitly handle CommonPrefixes with nocompat_dir (#2212)
Previously the test missed listing implicit directories and another
test was incorrect.  This fixes a regression from 1.91.
2023-07-13 21:15:34 +09:00
Takeshi Nakatani 9663215bb4 Fixed data race at OPENSSL_sk_dup/free in libcurl 2023-07-12 22:51:57 +09:00
Takeshi Nakatani b2537052ef Fixed data race in threads found thread sanitizer 2023-07-09 20:53:27 +09:00
Eryu Guan 2e51908bec Check FdEntity::Open() status correctly
FdEntity::Open() returns -errno on error, but FdManager::Open() only
checks if its ret is -1. This may lead to use '-errno' as pseudo fd
in next read or write, which would fail due to '-errno' is not in
fent map.

Signed-off-by: Eryu Guan <eguan@linux.alibaba.com>
2023-07-05 08:50:12 +09:00
LiuBingrun a8edbd8622
fix streamuplod not working (#2204)
fuse_main will do fork to daemonize. if ThreadPool is created before
this, worker thread will not be placed in child process.

Move ThreadPool Init to s3fs_init and destory to s3fs_destory.

Signed-off-by: liubingrun <liubr1@chinatelecom.cn>
2023-07-02 10:56:59 +09:00
Andrew Gaul 06d0852e74
Use JDK 17 for Ubuntu CI (#2200)
Also remove stale 18.04 configuration.
2023-06-26 22:07:04 +09:00
Alex Fishman 03066ea29a
Fix extended attribute support when using FUSE-T (#2201)
* Add support for FUSE-T on macos

Signed-off-by: Alex Fishman <alex@fuse-t.org>
Signed-off-by: alex <alex@alex-NUC10.lan>

* Ignore value pointer when size is zero on setattrx

Signed-off-by: Alex Fishman <alex@fuse-t.org>

---------

Signed-off-by: Alex Fishman <alex@fuse-t.org>
Signed-off-by: alex <alex@alex-NUC10.lan>
2023-06-26 22:05:37 +09:00
Andrew Gaul e66c9a82a2
Add sanitize_thread to CI (#2199) 2023-06-25 23:13:49 +09:00
Andrew Gaul e86e6cf24f Add Ubuntu 23.04 and remove 18.04
18.04 is EOL:

https://ubuntu.com/blog/ubuntu-18-04-eol-for-devices

This removes a FUSE 3 blocker.  References #1159.
2023-06-25 22:53:59 +09:00
Andrew Gaul 7e8238abc0
Upgrade to JDK 17 (#2196)
Newer distros package this but some older ones do not.  Also remove
stale Ubuntu 16.04 configuration.
2023-06-25 18:05:38 +09:00
Andrew Gaul 6448c8f1a8
Protect FdEntity::physical_fd with fdent_lock (#2194)
* Protect FdEntity::physical_fd with fdent_lock
Found via ThreadSanitizer.
2023-06-25 16:43:15 +09:00
Andrew Gaul 3b6688253f
Address cppcheck 2.10 warnings (#2163)
Disable newer cppcheck until we can diagnose this further.
References #2162.
2023-06-25 16:04:16 +09:00
Alex Fishman 45e7cd085a Add support for FUSE-T on macos
Signed-off-by: Alex Fishman <alex@fuse-t.org>
Signed-off-by: alex <alex@alex-NUC10.lan>
2023-06-24 22:26:42 +09:00
Takeshi Nakatani 7c9cf84316 Added several memory checks to CI 2023-06-11 10:23:08 -07:00
Takeshi Nakatani 580775b47c Removed unnecessary debug output 2023-06-07 20:48:50 -07:00
Takeshi Nakatani eab26a1e01 Fixed test setting for macOS 2023-06-07 20:48:34 -07:00
Andrew Gaul 1910856c6c
Remove wait and check loop from mk_test_file (#2175)
This appears to be some kind of eventual consistency check.  This
should have no effect given S3Proxy and recent AWS strong consistency.
Also it is likely ineffective given the other test object creation
operations.
2023-06-07 23:24:31 +09:00
Andrew Gaul 4b3e715291
Always return nanoseconds from get_time helpers (#2174)
This makes Linux and macOS more consistent.
2023-05-30 18:52:55 +09:00
Eryu Guan 6ca5a24a7f
Fix two inconsistency issues between stat cache and cache file (#2152)
* Fix inconsistency between stat cache file and cache file

We unlock stat cache file too early in FdEntity::Open(), and would
truncate cache file and update stat cache file, so there's a window that
stat cache doesn't reflect cache file status.

Suggested-by: Takeshi Nakatani <ggtakec@gmail.com>
Signed-off-by: Eryu Guan <eguan@linux.alibaba.com>

* Mark pagelist as unloaded if cache file has been truncated

If cache file size doesn't match object size, the cache file might be
corrupted, so invalidate it and save new cache stat file.

Suggested-by: Takeshi Nakatani <ggtakec@gmail.com>
Signed-off-by: Eryu Guan <eguan@linux.alibaba.com>

---------

Signed-off-by: Eryu Guan <eguan@linux.alibaba.com>
2023-05-30 18:39:50 +09:00
Takeshi Nakatani 0d6b02090e
Revert "Update curl.cpp: reduce memory cache use (#2157)" (#2170)
This reverts commit 5b487f651a.
2023-05-27 20:33:43 +09:00
Tan Guofu 5b487f651a
Update curl.cpp: reduce memory cache use (#2157)
flush the file and clean the page cache when filepart download done
2023-05-27 19:15:47 +09:00
Andrew Gaul e7a364d610
Specify _FORTIFY_SOURCE=3 (#2168)
This can find more kinds of buffer overflows:

https://developers.redhat.com/articles/2022/09/17/gccs-new-fortification-level
2023-05-27 17:20:29 +09:00
Andrew Gaul 161be3ff33
Update ChangeLog and configure.ac for 1.92 (#2166) 2023-05-22 08:25:50 +09:00
Takeshi Nakatani 1fc0e52dc3 Fixed mount point stat flag to be thread safe 2023-05-21 13:46:06 +09:00
Andrew Gaul 7bf4ca1837
Add Fedora 38 and remove 36 from CI (#2160) 2023-05-14 13:41:12 +09:00
Andrew Gaul 59c3b26655
Fix clang-tidy 16 nits (#2158) 2023-05-14 01:59:26 +09:00
Takeshi Nakatani 8296fe32cb
Directly and simplify requests in mount point checks (#2155) 2023-05-14 01:29:24 +09:00
Takeshi Nakatani 4a15699669
Disabled to run github action for macos (#2154) 2023-05-08 20:29:20 +09:00
Takeshi Nakatani 24b990d899
Disabled to run github action for macos (#2154) 2023-05-08 20:28:41 +09:00
Takeshi Nakatani ca9a257eec
Allow mount points without directory objects by compat_dir (#2153) 2023-05-07 09:15:27 +09:00
Takeshi Nakatani 6d4bb59865
Corresponded to upload in case of calling release without flush (#2150) 2023-04-23 22:59:04 +09:00
Ottavia Balducci 9b75abfbe6
New option: bucket_size (#2148)
* Added bucket_size option
2023-04-23 14:04:38 +09:00
Takeshi Nakatani c4f95f14cb
Fixed a bug about attributes of mount point (#2147) 2023-04-15 14:34:39 +09:00
Takeshi Nakatani 9c74014443
Fixed a bug in handling file names containing CR(0x1D) (#2136) 2023-03-26 13:19:16 +09:00
Takeshi Nakatani a25cb9e07a
Fixed a bug when reading a reduced file without flushing (#2133) 2023-03-26 11:45:21 +09:00
Takeshi Nakatani 2cd7c94653
Fixed configure.ac by autoupdate (#2134) 2023-03-21 15:10:55 +09:00
Takeshi Nakatani 9648eba5bb
Changed to rename cache files when renaming large files (#2135) 2023-03-21 14:15:34 +09:00
Takeshi Nakatani 18495c44aa
Added cache clearing on upload error (#2127) 2023-03-13 21:22:13 +09:00
Leon 4f22354aae
Fix an error to calculate no data page bytes (#2122)
Co-authored-by: Takeshi Nakatani <ggtakec@gmail.com>
2023-03-11 17:23:56 +09:00
Garen Chan 9d00b8d4a8
Fixed segmentation fault caused by file write failure (#2123) 2023-03-11 16:45:56 +09:00
Takeshi Nakatani aeacd0a7d3
Updated README.md(feat #2109) (#2115) 2023-03-05 15:03:48 +09:00
Takeshi Nakatani 526700f2de
Updated files for support ALPINE v3.17 (#2116) 2023-03-05 13:30:59 +09:00
Takeshi Nakatani a25a9450a6
Fixed to fail when the mount point directory is not existed (#2114) 2023-03-05 13:03:48 +09:00
Takeshi Nakatani f8a825e9b9
multipart upload id is converted by url encode (#2097) 2023-02-18 11:40:25 +09:00
Takeshi Nakatani dbddd762a9
Fixed token update process using ext-cred library (#2101) 2023-02-18 09:49:05 +09:00
Takeshi Nakatani e715b77307
Added the function to update mtime/ctime of the parent directory (#2016) 2023-02-12 17:59:40 +09:00
Takeshi Nakatani d1388ff446 Added proxy and proxy_cred_file option 2023-02-08 21:53:34 +09:00
Takeshi Nakatani 38e8a830c9 Fixed a deadlock bug when specifying iam role(IAMv2) 2023-01-31 20:53:39 +09:00
Takeshi Nakatani 7605c2e8fb Updated help contents and man pages for streamupload and etc 2023-01-31 20:53:20 +09:00
Takeshi Nakatani 16bc44948e Retry BucketCheck containing directory paths 2023-01-09 21:18:47 +09:00
Takeshi Nakatani e4f85c1e08 Fixed renaming bug with SSE 2023-01-09 21:17:48 +09:00
Takeshi Nakatani 222110e153 Fixed a bug about multipart upload with SSE 2023-01-07 09:12:55 +05:30
Takeshi Nakatani a40004f9cc
Fixed a conflict between curl and curl-minimal on RockyLinux 9 (#2086) 2023-01-04 22:18:28 +09:00
Andrew Gaul 0ba49518e9
Make some methods and parameters const (#2078)
This requires making some locks mutable.
2023-01-04 20:23:39 +09:00
Elvira Gandelman 14eb1a7fd8 Update readme, mac instructions, on M1 2022-12-07 07:46:17 +09:00
Takeshi Nakatani 23a8124c51 Compatible with OpenSSL 3.0 2022-11-27 21:46:08 +09:00
Takeshi Nakatani f5af9dc4e2 Updated issue and pull request github templates 2022-11-23 21:58:21 +09:00
Takeshi Nakatani d8e4e34b74 Fixed a warning by compiling on fedora 37 2022-11-23 21:57:31 +09:00
Pavel Knoblokh d98fdf4b2e Fix man page inaccuracies 2022-11-22 22:47:11 +09:00
Takeshi Nakatani 4e5f17e907 Fixed test_posix_acl test 2022-11-22 22:31:02 +09:00
Takeshi Nakatani 38cdaeb191 Added Fedora:37 and RockyLinux:9 to Github Actions target OS 2022-11-22 22:30:30 +09:00
Andrew Gaul 956eb77369
Updates for clang-tidy 15 (#2058) 2022-11-21 23:29:35 +09:00
Takeshi Nakatani 465986e397
Fixed a bug when end of path for mount point is multi slash (#2057) 2022-11-19 15:07:27 +09:00
Takeshi Nakatani 23a5583a7f
Updated actions/checkout for gha from v2 to v3 (#2053) 2022-11-05 15:08:42 +09:00
Takeshi Nakatani aec7efc3af
Updated comments in curl.cpp (related to #1827) (#2052) 2022-11-05 11:45:28 +09:00
Andrew Gaul 30353f1a83
Remove unnecessary calls to dup (#1827)
These functions already use pread and keep track of an explicit offset
for the SHA-256 calculations.
2022-11-05 10:24:41 +09:00
iforiq 6f4bf55d5e fix: handle file names with ':' in them 2022-11-01 06:19:47 -07:00
Takeshi Nakatani 2e77920943
Added support for xattr as POSIX ACL (#2039) 2022-10-22 21:48:02 +09:00
Takeshi Nakatani 4a813aec42
Support the object under no directory object path by compat_dir (#2023) 2022-10-22 15:12:00 +09:00
Takeshi Nakatani 4304ec63bb
Added stat information to the mount point (#1964) 2022-10-22 11:46:13 +09:00
Takeshi Nakatani 6e89e69bba
Enabled to load shared library for Credential and Token (#1927) 2022-10-22 10:42:07 +09:00
Takeshi Nakatani 9a4282208e Improved fdcache_page performance 2022-09-25 17:14:35 +09:00
Takeshi Nakatani 238fc0799e Fixed statfs(free inodes) for macos 2022-09-25 17:10:37 +09:00
Takeshi Nakatani 5b95a0fcb6
Fixed errors reported by cppcheck 2.9 (#2038) 2022-09-24 15:45:13 +09:00
Bernhard M. Wiedemann 1946ac415a Use man-page date instead of build date
to allow for reproducible builds of s3fs.1

This PR was done while working on reproducible builds for openSUSE.
2022-09-02 07:29:10 +09:00
Andrew Gaul 2186317676 Include missing ctime header
This caused compilation errors on Fedora 36.
2022-09-01 22:40:16 +09:00
Takeshi Nakatani 5ab1037094 Strictly reviewed the exclusive control of PseudoFdInfo class(additional change) 2022-08-08 07:51:49 +09:00
Takeshi Nakatani f6d7ff1084 Strictly reviewed the exclusive control of PseudoFdInfo class 2022-08-07 20:41:35 +09:00
Andrew Gaul 3e242d0bad
Avoid C-style casts (#2015)
Prefer more precise C++-style casts.
2022-07-30 22:35:27 +09:00
Takeshi Nakatani c491fbeabc Replace uses of lock_already_held flag with AutoLock::Type 2022-07-30 16:20:43 +09:00
Takeshi Nakatani e654e8ec8a Removed const_cast in set_bucket 2022-07-30 16:18:52 +09:00
Andrew Gaul 48e9e51f4f
Remove more unneeded headers identified by IWYU (#2011) 2022-07-30 12:06:47 +09:00
Takeshi Nakatani 5a2172dc56 Fixed data race and memory leaks in PseudoFdInfo 2022-07-30 06:51:16 +09:00
Andrew Gaul 07535ec3ec
Address clang-tidy warnings (#2010) 2022-07-29 22:00:07 +09:00
Takeshi Nakatani b8dd466988 Fixed data race in ThreadPoolMan 2022-07-29 13:24:09 +09:00
Andrew Gaul 01a92476e6
Remove unneeded headers identified by IWYU (#2007) 2022-07-28 23:38:38 +09:00
Andrew Gaul 3928a7e359
Remove more uses of const_cast (#2006)
Follows on to #2004.
2022-07-28 23:37:15 +09:00
Andrew Gaul f9f614a474
Avoid mutating mybasename and mydirname parameters (#2004)
basename and dirname mutate their inputs but const_cast hid this
behavior.  Also shuffle helpers to avoid unnecessary std::string.
Follows on to 404c284440.
2022-07-28 23:34:01 +09:00
Takeshi Nakatani e30a5939d0 Fixed deadlock in S3fsCurl::DestroyCurlHandle 2022-07-28 13:47:27 +09:00
Takeshi Nakatani 4b2f3fecb5 Set mtime/ctime/atime of all objects as nanosecond 2022-07-28 13:47:03 +09:00
Takeshi Nakatani ccfc119e45 Improved to avoid unnecessary head request 2022-07-27 21:51:28 +09:00
Takeshi Nakatani 11adf11957 Changed the time(a/c/m) acquisition of stat to nanosecond string 2022-07-25 07:21:37 +09:00
Andrew Gaul 38b5018bab
Remove unneedd binary_function inheritance (#1998)
GCC 12 warns that C++17 removes this.
2022-07-24 22:41:58 +09:00
Takeshi Nakatani 404c284440 Fixed race condition in dirname and basename call 2022-07-24 18:14:58 +09:00
Andrew Gaul e0655008b3
Protect pending_status in UploadPending (#1992)
This requires avoiding double-locking in RowFlush.  References #1991.
2022-07-22 23:30:04 +09:00
Takeshi Nakatani 22f2392fca Fixed bugs about stream upload 2022-07-19 21:29:56 +09:00
Takeshi Nakatani 136c5ec653 Fixed a bug in the test_external_creation test 2022-07-18 22:34:53 +09:00
Takeshi Nakatani faddb4900f Merged the code corresponding to the mknod fix(f11eb7d) 2022-07-17 22:20:45 +09:00
Takeshi Nakatani 6ca7d5ec27 Fixed an error which reported by cppcheck 2.8 2022-07-17 22:20:45 +09:00
Takeshi Nakatani 6f679a9e78 Reflect the result of the review in the code again 2022-07-17 22:20:45 +09:00
Takeshi Nakatani b0eeaa6679 Reflected the result of the review in the code 2022-07-17 22:20:45 +09:00
Takeshi Nakatani d22e1dc018 Add the stream upload which starts uploading parts before Flush 2022-07-17 22:20:45 +09:00
Andrew Gaul 3a0799ec18
Re-re-re-fix propagating the return code (#1984)
This shell incantation is necessary to have errexit take effect for
test bodies.  Previous only the _last_ command's exit code was
propagated to the test runner, masking real failures.  Reverts
ea3c21f270.
2022-07-17 02:13:06 +09:00
Andrew Gaul 4e163b2888
Allow listing implicit directories (#1986)
This fixes an issue when using -o notsup_compat_dir flag, dating back
to its introduction 3ac39d61f8.  The new
default exposed this in my local testing but a test runner bug masked
a test failure in test_external_directory_creation.  References #927.
References #1984.
2022-07-17 01:54:29 +09:00
Andrew Gaul 86da2eed3a
Do not call exit in individual test functions (#1985)
This could prevent the test runner from reporting failures.
2022-07-17 01:33:50 +09:00
Andrew Gaul e7ed01b35f
Consume return code from get_object_attribute (#1976)
Found via clang-analyzer.
2022-07-09 16:45:23 +09:00
Andrew Gaul 4d303caa62
Add Valgrind to sanitizer script (#1978)
Theoretically msan can do this but practically it requires a custom
libc++.
2022-07-09 16:40:23 +09:00
Takeshi Nakatani 3f55c98a3f Fixed a bug when the pool of curl handler exceeds the upper limit 2022-07-08 21:14:29 +09:00
Andrew Gaul 2723e1049e
Add missing paragraph to help (#1974) 2022-07-03 12:35:02 +09:00
Takeshi Nakatani f11eb7d69b Fixed a bug that regular files could not be created by mknod 2022-06-29 16:56:19 +09:00
Takeshi Nakatani 73b49c1038 Fixed a bug that regular files could not be created by mknod 2022-06-29 16:56:19 +09:00
Andrew Gaul 4bec68713a
Add CSI for S3 to references (#1971) 2022-06-28 22:56:12 +09:00
Andrew Gaul 8b90cd6ba1
Enable notsup_compat_dir by default (#1970)
Few applications create the dir_$folder$ objects and users can enable
compat_dir if required.  This commit reduces readdir latency by 33%.
Also remove notsup_compat_dir from tests since these directories are
never created.  Fixes #927.  References #1643.
2022-06-28 07:56:06 +09:00
Andrew Gaul ac72bf34dd
Run test_external_directory_creation for all flags (#1969) 2022-06-28 07:50:24 +09:00
Andrew Gaul a282cb7a84
Update CI runners to Fedora 36 and Ubuntu 22.04 (#1965)
Also remove Debian stretch which is no longer supported by LTS.
2022-06-22 17:40:08 +09:00
Andrew Gaul b52f916af6
Avoid extended initializer lists in C++03 (#1960)
C++11 introduces these.
2022-06-12 23:52:16 +09:00
Takeshi Nakatani ec7810f08e Fixed a bug could not change the mode while the file was opened 2022-06-12 13:48:42 +09:00
Andrew Gaul 904682b856
Fix double-iteration in curl_slist_remove (#1951)
Also backfill tests.  Fixes #1948.
2022-05-27 22:56:20 +09:00
Takeshi Nakatani 92fd5bc3e1
Fixed errors reported by cppcheck 2.8 (#1949) 2022-05-26 01:48:53 +09:00
Takeshi Nakatani d75c6d6538
Changed the message level(ERR to INFO) of skip cleanup cache (#1946) 2022-05-22 14:36:56 +09:00
Takeshi Nakatani a30beded1c Removed unnecessary debug options for aws command 2022-04-22 22:36:06 +09:00
Takeshi Nakatani df7bbb28d5 Replace awscli version 1 to 2 2022-04-19 23:11:25 +09:00
Andrew Gaul dc40f16161
Upgrade to S3Proxy 2.0.0 (#1929)
Release notes:

https://github.com/gaul/s3proxy/releases/tag/s3proxy-2.0.0

This enables Java 17 compatibility.
2022-04-04 23:03:16 +09:00
Andrew Gaul 1abfb7e965
Reference docker-s3fs-client (#1925) 2022-03-21 20:44:07 +09:00
Andrew Gaul d2d75787d2
Specify bash strictness options (#1924) 2022-03-12 17:01:27 +09:00
Andrew Gaul 5c57e17b77
Enable noobj_cache by default (#1922)
This should improve performance in many situations.  s3fs already
enables the stat cache by default so memorizing noobj makes this more
consistent.  Fixes #1901.
2022-03-12 16:57:31 +09:00
Andrew Gaul deaa85c40e
Document glacier_ir storage class (#1921)
Announcement:
https://aws.amazon.com/about-aws/whats-new/2021/11/amazon-s3-glacier-instant-retrieval-storage-class/

This does not require code changes due to
bbcccd6e98.
2022-03-12 16:20:38 +09:00
Andrew Gaul 49d92c7022
Update ChangeLog and configure.ac for 1.91 (#1920)
Fixes #1876.
2022-03-08 07:48:15 +09:00
Takeshi Nakatani d842d45b2b Fixed a bug about truncation for shrinking file 2022-03-02 22:41:10 +09:00
Takeshi Nakatani 684ced5a41 Changed handling the credential in S3fsCred more robust 2022-03-02 22:39:15 +09:00
Carsten Grohmann afb0897553 Typos 2022-02-24 19:15:00 +09:00
Andrew Gaul 8a5c4306f5
Preserve sub-second precision where possible (#1915) 2022-02-23 23:58:51 +09:00
Andrew Gaul 01e24967b6
Add test for external object creation (#1900)
This test demonstrates the behavior before and after the stat cache
timeout when using noobj_cache.
2022-02-23 23:34:58 +09:00
Andrew Gaul 08adffd2fe
Fix typos (#1916) 2022-02-23 23:31:52 +09:00
Andrew Gaul 0842c5718f
Use more new file names for every test (#1902)
This makes the tests more robust.  Also fix filename to end in .txt.
2022-02-23 22:59:21 +09:00
Takeshi Nakatani 5452e9cb10 Dynamically generate dates for man page file 2022-02-23 21:51:47 +09:00
Andrew Gaul 232ff28cc7
Re-re-fix propagating the return code (#1903)
Previously the integration tests were exiting after the first failed
test instead of running all of them an reporting their statuses.
Follows on to dbf93c0152.
2022-02-23 14:27:29 +09:00
Andrew Gaul 81ed2bd91e
Propagate deferred exit status from main (#1912)
Previously s3fs always returned zero when the bucket did not mount.
Fixes #1911.
2022-02-23 10:09:12 +09:00
Andrew Gaul 305d660e39
Use custom CA bundle instead of ignoring errors (#1910)
Fixes #1846.
2022-02-23 10:04:05 +09:00
Carsten Grohmann a716c72d37 Update notsup_compat_dir in --help 2022-02-21 19:29:15 +09:00
Andrew Gaul 302150b4f5
Filter mountpoints via mount -t (#1905)
This is portable between Linux and macOS.
2022-02-20 20:49:35 +09:00
Andrew Gaul 3fa03d4e1e
Pass explicit -p option to ps (#1904)
This ensures that a pid follows.
2022-02-20 20:40:29 +09:00
Takeshi Nakatani e014d6e646 Changed Rocky Linux 8 instead of CentOS 8 2022-02-20 19:29:34 +09:00
Carsten Grohmann c2a49b7b1a Rephrase the description of notsup_compat_dir 2022-02-20 16:06:57 +09:00
Carsten Grohmann 265fa9e47a Add performance considerations section to man page 2022-02-20 16:06:57 +09:00
Takeshi Nakatani d31cbda7b6 Fixed a bug about checking credential 2022-02-19 23:22:15 +09:00
Takeshi Nakatani b64dc7749c Moved parameter analysis processing to S3fsCred class 2022-02-19 17:23:40 +09:00
Takeshi Nakatani b9e2be5c21 Fixed two typos in configure.ac 2022-02-19 17:22:43 +09:00
Takeshi Nakatani 839a33de49
Fixed not to call Flush even if the file size is increased (#1887)
Changed s3fs_truncate function.
This change reduces the number of file uploads if the file size is changed.

On macOS, I have found that the truncate call when "size=0" cannot reflect the file size.(This reason is not understood...)
To avoid this, only when "size=0", the flush method is called as before.

Other than that, I found a bug in FdEntity::Open() and fixed it.

Fixes #1875.
2022-02-15 21:29:07 +09:00
Petr Vaněk 4dfe2bfdd7 Include climits to support musl libc
PATH_MAX constant is not visible from any of currently included header
files in system with musl libc, where compilation fails with an error
below. The constant is defined in limits.h which is directly include via
climits header file.

fdcache.cpp: In static member function 'static FILE* FdManager::MakeTempFile()':
fdcache.cpp:381:14: error: 'PATH_MAX' was not declared in this scope
  381 |     char cfn[PATH_MAX];
      |              ^~~~~~~~

Fixes: d67b83e671 ("Allow configuration for temporary files directory")
2022-02-14 09:19:30 +09:00
Takeshi Nakatani 1678803566 Added S3fsCred class and moved Credential related processing in it 2022-02-13 21:38:30 +09:00
Takeshi Nakatani d7e929e0a8
Fixed some Github Actions errors. (#1886)
- Fix knownConditionTrueFalse cppcheck(2.7) error on MacOS
- Fixed package installing failure of appstream download on centos8
2022-02-13 14:23:35 +09:00
Takeshi Nakatani 94e8e23eef
Fixed test_external_directory_creation test when cache enabled (#1885) 2022-02-13 13:32:19 +09:00
Andrew Gaul dbf93c0152
Propagate return code properly (#1884)
Previously this did not propagate test failures.  A bad rebase
introduced this logic in 495d51113c.
2022-02-06 22:45:20 +09:00
Andrew Gaul 9224f792f0
Use CLOCK_REALTIME for UTIME_NOW (#1881)
Previously s3fs_utimens used CLOCK_MONOTONIC_COARSE which was not
1970-based.  Found via pjdfstest.  References #1589.
2022-01-30 22:19:15 +09:00
Andrew Gaul f6ed972926
Always flush open files with O_CREAT flag (#1879)
Previously s3fs only created files that had dirty data and not those
with zero-bytes.  Regression from
771bbfeac5.  References #1013.  Found
via pjdfstest.  References #1589.
2022-01-30 22:02:37 +09:00
Andrew Gaul 0c75a63184
Preserve sub-second precision with utimens (#1880)
Found via pjdfstest.  References #1589.
2022-01-30 21:45:51 +09:00
Takeshi Nakatani 30cf7a50bb Added stat check for subdir created with awscli 2022-01-30 18:31:36 +09:00
Takeshi Nakatani e452ef3940 Fixed the fault tolerance when time stamp getting fails 2022-01-30 18:31:36 +09:00
Andrew Gaul cd5a69b9eb
Handle UTIME_NOW and UTIME_OMIT special values (#1868)
FUSE 3 will require this behavior.  References #1159.
2022-01-29 11:35:37 +09:00
Andrew Gaul 74c11ef226
Check bucket before trying to create it (#1874)
This makes it easier to run tests against S3 other than S3Proxy.
2022-01-26 23:42:12 +09:00
Andrew Gaul 662882d2f0
Always call clock_gettime(2) (#1871)
e01ded9e27 introduced this compatibility
shim but macOS 10.12 (2016) added this:
https://stackoverflow.com/a/39801564 .  Also remove fallback to
time(3) which loses precision.
2022-01-25 08:36:27 +09:00
Andrew Gaul de0c87c801
Convert S3FS_LOW_LOGPRN from a macro to a function (#1869)
This shrinks the binary size from 770 to 540 KB and reduces compile
times.
2022-01-23 23:10:09 +09:00
Naoki Ikeguchi 41aaa4184f Avoid double setting values in statfs 2022-01-23 21:49:51 +09:00
Naoki Ikeguchi 451602e58d Remove unnecessary conditional for automake 2022-01-23 21:49:51 +09:00
Naoki Ikeguchi 581f5c0356 Move strptime polyfill to string_util 2022-01-23 21:49:51 +09:00
Naoki Ikeguchi e5f6f112db Fix typo 2022-01-23 21:49:51 +09:00
Naoki Ikeguchi b3cef944b2 Fix test_page_list_SOURCES has no if MSYS clause 2022-01-23 21:49:51 +09:00
Naoki Ikeguchi 6edb6067f3 Remove strcasestr polyfill 2022-01-23 21:49:51 +09:00
Naoki Ikeguchi b2c659c0a6 Disable compiling polyfills in not MSYS2 env 2022-01-23 21:49:51 +09:00
Naoki Ikeguchi 807ea52ba7 Remove duplicates in .gitignore 2022-01-23 21:49:51 +09:00
Naoki Ikeguchi 3ac9f571f5 Use std::get_time instead in strptime polyfill 2022-01-23 21:49:51 +09:00
Naoki Ikeguchi d95a612548 Revert "Run `autoupdate`"
This reverts commit 0b1d801598164c45e7c9e89ebd30ddde8251befa.
2022-01-23 21:49:51 +09:00
Naoki Ikeguchi 19303a546e Fix the statfs issue, using f_frsize instead 2022-01-23 21:49:51 +09:00
Naoki Ikeguchi 4d117fd0af Add instructions for Windows compilation 2022-01-23 21:49:51 +09:00
Naoki Ikeguchi 2bf84fc705 Ignore .exe files 2022-01-23 21:49:51 +09:00
Naoki Ikeguchi 70692ee770 Run `autoupdate` 2022-01-23 21:49:51 +09:00
Naoki Ikeguchi 6370e150dd Disable features that causes problems on Windows 2022-01-23 21:49:51 +09:00
Naoki Ikeguchi b14e39815b Use polyfills in MSYS2 environment 2022-01-23 21:49:51 +09:00
Naoki Ikeguchi 6aaf9433a5 Add polyfills for MSYS2 environment 2022-01-23 21:49:51 +09:00
Takeshi Nakatani 46014397d8 Added test by a shell script static analysis tool(ShellCheck) 2022-01-22 22:23:08 +09:00
Andrew Gaul 93d1c30d4d
Use XML parsing with PUT HTTP 200 responses (#1858)
This works around the missing strcasestr on win32.  References #728.
2022-01-14 16:10:22 +09:00
Andrew Gaul 6300859c80
Prefer = over == for older shell compatibility (#1857) 2022-01-14 12:40:55 +09:00
Andrew Gaul 2892d3b755
Call curl --fail to propagate exit code (#1856) 2022-01-14 11:52:52 +09:00
Andrew Gaul 25012f3839 Fix typo in -o enable_unsigned_payload 2022-01-12 22:50:49 +09:00
Andrew Gaul 3dfc1184ca Remove python2 from bullseye 2022-01-10 19:34:36 +09:00
Andrew Gaul 53d1b04cc2
Add new suppressions for clang-tidy 13 (#1847) 2022-01-09 21:42:36 +09:00
Andrew Gaul b67465b91d
Specify C++03 for CI (#1850) 2022-01-09 20:48:09 +09:00
Andrew Gaul cba65fc51a
Remove Python 2 (#1849) 2022-01-09 20:37:15 +09:00
Andrew Gaul 75b16c72aa
Build s3fs in parallel (#1848)
GitHub runners provide 2 Linux CPUs or 3 macOS CPUs:

https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
2022-01-09 20:22:49 +09:00
Andrew Gaul 577e2bc987
Generate S3Proxy SSL certificate during tests (#1845)
Also provide CA bundle to AWS CLI to work around CI failures instead
of ignoring errors.  Fixes #1812.
2022-01-09 15:13:36 +09:00
Andrew Gaul adb58af17b
Annotate local variables (#1844)
This prevents collisions with other globals.  Fixes #1843.
2022-01-09 13:03:36 +09:00
Andrew Gaul dd11de3a50
Add Debian Bullseye to CI (#1842)
Stretch is supported until June 2022:

https://wiki.debian.org/LTS
2022-01-09 12:11:00 +09:00
Andrew Gaul fc7543fa25
Make ut_test compatible with Python 3 (#1838)
This may allow removing Python 2 on newer distros.

Co-authored-by: Takeshi Nakatani <ggtakec@gmail.com>
2022-01-09 11:08:36 +09:00
Andrew Gaul a44ea7c12a
Replace write_multiple_offsets.py with write_multiblock (#1837)
This reduces the dependency on Python 2.
2022-01-09 10:51:17 +09:00
Andrew Gaul e734763002
Remove createbucket option (#1841)
AWS CLI can do this.  Fixes #1840.
2022-01-05 01:59:31 +09:00
Andrew Gaul 37af08bacf
Use JRE instead of JDK for Debian-based distros (#1839)
This reduces the dependencies installed.  It does not appear that
Fedora-based distros support this.

Co-authored-by: Takeshi Nakatani <ggtakec@gmail.com>
2022-01-05 01:29:06 +09:00
Andrew Gaul 616db5bf1c
Prefer curl over wget for fetching dependencies (#1836)
The former is lighter-weight and libcurl is already a dependency for
s3fs.
2022-01-05 00:43:36 +09:00
Serhii Polishchuk 9ad9c382f4 Update README.md
Fixed install error
2022-01-04 22:44:49 +09:00
Andrew Gaul 1d090aa7a3 Install default-jdk-headless instead of default-jdk
This reduces the dependencies installed.
2022-01-04 18:18:24 +09:00
Andrew Gaul 61e9029be4 Fix typos in CI scripts 2022-01-03 19:50:13 +09:00
Andrew Gaul 5de92e9788
Bump CI to Fedora 35 (#1806) 2021-12-02 23:45:19 +09:00
LiuBingrun 85ca2a3e45
fix mixupload return EntityTooSmall while a copypart is less than 5MB after split (#1809)
* fix  mixupload return EntityTooSmall while a copypart is less than 5MB after split
* fix possible part exceeds 5GB when multipart_copy_size is set to 5120MB
* Update curl.cpp
Co-authored-by: liubingrun <liubr1@chinatelecom.cn>
2021-11-27 16:53:26 +09:00
Andrew Gaul 07e2e3f72a
Remove sleep 1 from test_update_directory_time (#1803)
Reduces per-flag test run-time by 5 seconds.
2021-11-04 08:16:40 +09:00
Andrew Gaul 3cf00626a2
Add option to allow unsigned payloads (#1801)
This reduces CPU usage of sigv4.  This reduces test run-time by 7
seconds per flag.
2021-11-01 23:33:55 +09:00
Andrew Gaul e289915dcb
Remove require-root script (#1800)
Tests do not require this.
2021-10-31 10:48:15 +09:00
Andrew Gaul 06dec32965
Use AWS CLI to create explicit times in the past (#1797)
s3fs can also do this via utimensat but tests should not trust this.
Also break tests into individual functions.  This further reduces test
run-time 8 seconds per flag.
2021-10-30 10:54:18 +09:00
Andrew Gaul 86317dd185
Replace dd if=/dev/urandom with junk data generator (#1786)
This reduces test run time for a single flag from 73 to 60 seconds.
2021-10-28 22:54:25 +09:00
Andrew Gaul 473da56abf
Use default JDK instead of forcing Java 8 (#1796)
S3Proxy requires Java 8 or later, not 8 specifically.
2021-10-28 22:27:48 +09:00
Andrew Gaul 162ab14517
Bump Ubuntu CI to latest non-LTS version (#1794) 2021-10-28 22:10:20 +09:00
Andrew Gaul 40d2e0d1ad
Reduce sleep time to 1 (#1793)
This reduces test run-time 15 seconds per flag or 2.5 minutes when
testing all flags.
2021-10-27 23:47:08 +09:00
Takeshi Nakatani b6c5069ef7 Fixed the test is multi-block writing by one flush 2021-10-27 08:19:05 +09:00
Takeshi Nakatani 7273d561f5 Added exclusive control of statc variables in s3fs xml parser 2021-10-27 08:18:19 +09:00
Takeshi Nakatani 78126aea0b Added exclusive control of statc variables in s3fs xml parser 2021-10-27 08:18:19 +09:00
Takeshi Nakatani 7892eee207 Fixed a bug that copied without considering the length of xmlChar 2021-10-27 08:18:19 +09:00
Andrew Gaul 72a9f40f3f
Update to S3Proxy 1.9.0 (#1788)
Notably this fixes an issue with the transient provider reading parts
of large files.

Release notes:

https://github.com/gaul/s3proxy/releases/tag/s3proxy-1.9.0
2021-10-26 23:20:52 +09:00
Andrew Gaul 495d51113c
Remove unneeded sleeps from tests (#1784)
Also use a unique file name for every test.  This ensures that tests
like test_external_directory_creation and test_external_modification
do not collide.
2021-10-26 23:19:14 +09:00
Carsten Grohmann 0abeec9cae Simpily errexit modifications 2021-10-26 21:47:36 +09:00
Andrew Gaul ea3c21f270
Reduce errexit modifications (#1785)
This is less error prone but requires some magic && ||.
2021-10-25 23:53:45 +09:00
Takeshi Nakatani 23fe6f4dee Fixed parse_string function in write_multiblock.cc 2021-10-25 17:56:49 +09:00
Takeshi Nakatani 34ea2acd75 Add a test that is multi-block writing by one flush 2021-10-25 17:56:49 +09:00
Takeshi Nakatani ea64886469 Fixed a bug in test_(zero_)cache_file_stat test function 2021-10-24 18:24:12 +09:00
Takeshi Nakatani 023aaf7dff Fixed wrong stat of cache after new creation file
And added a test for stat of cache after new creation file
2021-10-17 16:10:14 +09:00
Takeshi Nakatani 2f412804e2 Fixed forgetting to clear the dirty flag for meta information
Addressed an error in macos cpp check
2021-10-15 22:54:55 +09:00
Takeshi Nakatani d6ffd389da Excluded ubuntu 16.04 from the CI build execution environment 2021-10-15 08:54:13 +09:00
Carsten Grohmann be0b17329a
Fix wrong function name in log message (#1774) 2021-10-10 11:08:32 +09:00
Kamil Jakrzewski b4edad86d6 remove Expect: 100-continue header when requesting an IMDSv2 access token 2021-09-09 08:12:36 +09:00
Noah Meyerhans 9d1552a54e fix IAM role retrieval from IMDSv2
AWS IMDSv2 support was added in #1462, but the implementation did not
cover the addional IMDS access that occurs with the iam_role=auto
configuration.  This change implements IMDSv2 support for the IMDS
call to determine the instance's role name.

See also
https://stackoverflow.com/questions/69031023/how-to-make-s3fs-use-imds-v2-when-mounting-s3-buckets-from-ec2-instance
2021-09-03 20:36:34 +09:00
Andrew Gaul 47ebfcc60a
Consume return value from curl_easy_setopt (#1759)
Found via Coverity.
2021-09-02 08:07:06 +09:00
Andrew Gaul beecf32dff
fclose(FILE*) instead of close(fileno(FILE*)) (#1758)
This is the same thing but confuses Coverity.
2021-09-01 19:41:55 +09:00
Andrew Gaul 57b2e4a4f1
Fix 32-bit compilation issues (#1757) 2021-08-31 19:36:02 +09:00
Andrew Gaul 48817d849f
Require explicit length in s3fs_decode64 (#1755)
This is available from std::string::size in callers.
2021-08-31 09:22:10 +09:00
VVoidV d9f2d17040
1. fix RowFlush can not upload last part smaller than 5MB using NoCacheMultipartPost; (#1753)
2. fix deadlock in UploadPendingMeta
2021-08-31 00:41:47 +09:00
Andrew Gaul cd98afdd7b
Do not NUL terminate base64 decoded output (#1752)
This is binary data and must use the explicit length.
2021-08-31 00:15:47 +09:00
Andrew Gaul dac6885fb0
Don't over-allocate in base64 encoding and decoding (#1751) 2021-08-30 00:03:10 +09:00
VVoidV fcd180891b
fix misuse of IsUploading (#1747)
Co-authored-by: liubingrun <liubr1@chinatelecom.cn>
2021-08-29 23:41:02 +09:00
Vincent Milum Jr d5d541c7f7 Adding FreeBSD example to README
S3FS has existing on FreeBSD since 2009, and should be reflected here that it is well supported.
2021-08-26 10:18:56 +09:00
Takeshi Nakatani a868c0656e Changed etaglist_t from string list to new structure etagpairs list 2021-08-16 09:27:12 +09:00
Andrew Gaul cd466ebdd4
Update ChangeLog and configure.ac for 1.90 (#1739)
Fixes #1737.
2021-08-08 14:01:19 +09:00
Andrew Gaul 15e89b78de
Add a partial page_list unit test (#1735) 2021-08-04 07:36:32 +09:00
Andrew Gaul 66006ba48d
Add dedicated upper- and lower-case hex functions (#1734)
This makes the call sites more readable than a boolean parameter.
2021-08-04 07:28:51 +09:00
Andrew Gaul 18e9c62087
Make string constants read-only const (#1733)
This removes some global constructors.  Also use a consistent ALL_CAPS
style.
2021-08-03 00:10:27 +09:00
Takeshi Nakatani 34f89e5936 Added fake_diskfree option to deceive free disk space for test 2021-08-02 11:04:42 +09:00
Carsten Grohmann e1f3b9d8c1
Add support for AWS-style environment variables (#1729)
Support AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN
in addition to the AWSACCESSKEYID, AWSSECRETACCESSKEY and
AWSSESSIONTOKEN.

The old environment variables are still supported, but they are
deprecated and no longer documented.

Close #1708
2021-07-26 23:29:45 +09:00
Andrew Gaul d3278f4886
Loosen CheckBucket to check only the bucket (#1728)
Previously it checked if there was an object present, assuming that
this was a directory object normally created for s3fs directories.
However most S3 clients do not create this object for virtual folders.
Fixes #1460.  Fixes #1687.
2021-07-25 12:29:00 +09:00
Andrew Gaul 77f0b75d2f
Add jitter to avoid thundering herd (#1723) 2021-07-25 10:22:19 +09:00
Andrew Gaul 012e6dd8a2
Fix README nit (#1726) 2021-07-22 13:54:20 +09:00
Takeshi Nakatani 199b3d4709 Fixed a bug in disk free space calculation 2021-07-18 14:52:11 +09:00
Andrew Gaul 7890989cbb
Address cppcheck 2.5 warnings (#1720)
This slightly simplifies memory management.
2021-07-14 22:18:09 +09:00
Takeshi Nakatani 945cc2ac54 Added UntreatedParts class instead of untreated upload info members in PseudoFdInfo 2021-07-12 21:45:29 +09:00
Takeshi Nakatani c30acbbf90 Splited some methods of FdEntity class by uploading mode
- Splited FdEntity::Write method by uploading mode
- Splited FdEntity::RowFlush method by uplading mode
2021-07-10 17:24:34 +09:00
Martynov Maxim 881025cc9e
Add initiator pid, uid and gid to a debug log (#1716) 2021-07-10 16:23:20 +09:00
Takeshi Nakatani 4cc210c5ab Prevents the etag string buffer from being destroyed during use
and absorbed MPPART_INFO structure into filepart structure
2021-07-03 22:42:18 +09:00
Takeshi Nakatani 2d5316a334 Fixed 1710 - Added a flag for check in Linux 2021-07-02 11:34:51 +09:00
Andrew Gaul 9cfa177af0
Add flag to run all tests (#1710)
This allows CI to run the full set of tests while making local
development faster.
2021-07-02 08:41:47 +09:00
Andrew Gaul fe44355d25
Report s3fs space as 18.4 EB (#1709)
256 TB probably seemed large in 2007 but in 2021 it is small enough to
confuse some users.
2021-07-02 07:56:05 +09:00
Andrew Gaul 487df27008
Return EREMOTE when reading Glacier objects (#1705)
Previously s3fs returned EIO which was not helpful to the caller.
Returning a more specific error code allows automatically restoring
the object via RestoreObject in a subsequent commit.
References #1466.
2021-06-30 09:25:36 +09:00
Andrew Gaul 1965916f7a
Convert storage class values to uppercase (#1704)
AWS requires uppercase values.  Fixes a regression from
bbcccd6e98.  References #1613.
2021-06-30 09:03:31 +09:00
Andrew Gaul 8948eded09
Address clang-tidy warnings (#1703) 2021-06-30 08:42:44 +09:00
Andrew Gaul 2f59cb5a0a
Remove unneeded volatile qualifiers (#1702)
We should use proper locking instead.  Specifically for
is_meta_pending this does not do what was intended due to the
read-modify-write of the member.
2021-06-30 08:20:44 +09:00
Andrew Gaul f505c8224e
Explicitly specify lock type (#1701)
This makes it more clear and type-safe if the caller already has the
lock.  Follows on to 84174c560d.
2021-06-27 15:15:48 +09:00
Andrew Gaul 2154e898bc
Fix typos (#1700) 2021-06-27 11:22:33 +09:00
Takeshi Nakatani f9e80f995d Fixed a bug about rename existing file of different sizes by mpcopy 2021-06-26 00:04:58 +09:00
Jan Stastny a5c1915772
Set CURLOPT_UNRESTRICTED_AUTH when authenticating (#1681)
This is necessary for authentication to work with AWS when it responds with 307 to the check bucket request when mounting. This happens to newly created buckets according to https://aws.amazon.com/premiumsupport/knowledge-center/s3-http-307-response/.

Prior to this, curl would follow the redirect, but would not include the `Authorization` header which would end up with `404`.

Co-authored-by: Takeshi Nakatani <ggtakec@gmail.com>
2021-06-25 20:26:12 +09:00
Takeshi Nakatani 6edf3d6427 Updated to clearly output message about the file descriptor type(pseudo/physical) 2021-06-25 19:18:08 +09:00
Andrew Gaul 944c1fd148
Clean up litter in ut_test (#1697) 2021-06-25 19:15:03 +09:00
Andrew Gaul 20281737b1
Set explicit Content-Length: 0 when initiating MPU (#1696)
This works around a GCS quirk that incorrectly requires this header.
Fixes #1661.
2021-06-25 18:36:40 +09:00
Takeshi Nakatani 0555c4216e Added test case for utimens is calling during multipart upload 2021-06-21 17:20:24 +09:00
Takeshi Nakatani 5b6684ca19 Fixed a bug utimens is calling before flush 2021-06-21 17:20:24 +09:00
Carsten Grohmann c4ac923b4c
Ensure NUL-terminated result after strncpy (#1694)
Long symlinks may cause that the result buffer is filled and not proper
terminated with a null byte.
2021-06-21 08:08:56 +09:00
Takeshi Nakatani cc022a68f4 Fixed a bug in s3fs_write 2021-06-20 23:13:44 +09:00
Andrew Gaul 1571379304
Set LC_ALL=en_US.UTF-8 in tests (#1691)
This allows tests to pass using the S3Proxy filesystem provider.
References #1665.
2021-06-20 19:27:25 +09:00
Takeshi Nakatani 858562ed53 Fixed comparison warning for size_t variable 2021-06-20 16:53:25 +09:00
Andrew Gaul b8724425d3
Require explicit base parameter in cvt_strtoofft (#1683)
Also convert most callers of cvt_strtoofft to base 10 which avoid the
magical behavior of interpreting a leading 0 as octal.
References #1682.
2021-06-20 11:00:15 +09:00
Dmitrii Vasilev b9ce0faee2 Set Content-MD5 header even for empty objects
The Content-MD5 header is required for any request to upload an object
with a retention period configured using Amazon S3 Object Lock
2021-06-19 16:10:58 +09:00
Carsten Grohmann 1e0e2752bf Improve warning for terminated threads
with non-zero return code.
2021-06-18 07:54:38 +09:00
Carsten Grohmann 7a488b93d0 Set decimal base for converting subseconds to int
The leading 0 triggers an automatic conversion as an octal value. This
fails because it is a decimal value.

Setting the base to 10 prevents this automatism and treats the value
as a decimal value.

Fixes: [WAN] string_util.cpp:cvt_strtoofft(96): something error is occurred in convert std::string(017080564) to off_t, thus return 0 as default.

Related to #1676
2021-06-17 06:27:02 +09:00
Carsten Grohmann d67b83e671 Allow configuration for temporary files directory 2021-06-16 21:29:58 +09:00
Andrew Gaul a100be9dce
Prefer std::string::empty over length == 0 (#1679)
This is more concise.
2021-06-13 20:03:10 +09:00
Andrew Gaul 4d39ea887e
Protect orgmeta with fdent_lock (#1678)
Fixes a crash seen with the CentOS 7 builder and when running with
AddressSanitizer.  Regression introduced by
ac578d188e.  Fixes #1677.
2021-06-13 16:14:24 +09:00
Andrew Gaul 7638b5b3e3
Prefer std::string::rbegin over operator[] (#1673)
This is more concise and safer due to not repeating the variable name.
We cannot use std::string::back since it is not available in C++03.
2021-06-13 13:26:38 +09:00
Andrew Gaul 600cee118d
Fix implicit narrowing conversions (#1672)
These do not appear to be problematic but rather just clean up warnings.
Found via clang -Wshorten-64-to-32.
2021-06-13 12:50:07 +09:00
Takeshi Nakatani c2c56d0263 Added info object about multipart uploading for each pseudo fd
(and fixed typo about method name)
2021-06-04 22:42:58 +09:00
Takeshi Nakatani ac578d188e Introduced pseudo fd and separated fd for each file opening 2021-06-04 22:42:58 +09:00
Andrew Gaul 53dfd48f59
Correct usage of istringstream (#1670)
Previously this looped one more time than necessary due to the eof
check:

https://isocpp.org/wiki/faq/input-output#istream-and-eof

Remove now redundant empty check.
2021-06-02 07:14:32 +09:00
Andrew Gaul f5701fa9ad
Correct typo in undefinedsanitizer config (#1669) 2021-05-29 22:07:33 +09:00
Andrew Gaul 84174c560d
Fix data races caused by incorrect locking (#1668)
Found via Threadsanitizer.  Fixes #1471.
2021-05-29 00:11:55 +09:00
Carsten Grohmann 9bf525ee7a Ensuring multipart size even when storage is low
When the temporary storage was full, the old implementation started an
upload even if there was not enough data to completely fill the minimum
multipart size or fill the user-selected multipart size.

The new implementation ensures the minimum multipart size by forcing
the user-selected multipart size.

Fixes #1591
2021-05-27 22:55:52 +09:00
Takeshi Nakatani 4b69d4b1bb Fixed a bug when the disk capacity was insufficient in RowFlush 2021-05-22 23:11:46 +09:00
Andrew Gaul bbcccd6e98
Make storage class a string (#1663)
This allows non-standard storage classes like Google Cloud Storage
Nearline.  Fixes #1613.
2021-05-21 23:34:31 +09:00
Carsten Grohmann c0bcb41175
Increase test robustness by adding LC_ALL=C (#1660)
Use C locale, because some tests check for English expressions

Fixes #1658
2021-05-20 21:19:50 +09:00
Takeshi Nakatani b5fef788da Additional bug fixing for not creating zero-byte object 2021-05-09 17:33:53 +09:00
Andrew Gaul 42f5965d8a
Fix off_t to int narrowing in PageList::GetSparseFilePages (#1654)
Found via clang-tidy.
2021-05-09 14:35:00 +09:00
Andrew Gaul d904d91252
Prefer std::map::insert over find and operator[] (#1653)
This avoids a duplicate lookups.
2021-05-09 14:11:35 +09:00
Andrew Gaul 4d81a4bf68
Use same time when creating directory objects (#1652)
This avoids mismatched times and is slightly faster.
2021-05-09 13:44:59 +09:00
Andrew Gaul 9abe3fa662
Use same time when creating directory objects (#1652)
This avoids mismatched times and is slightly faster.
2021-05-09 13:44:12 +09:00
Takeshi Nakatani 9d8f1b00f7 Fixed make_random_string test code being slow on macos 2021-05-08 13:55:39 +09:00
Andrew Gaul bb6d2b1b74
Replace snprintf with string and ostringstream (#1649)
These uses are probably safe from a buffer overflow perspective but
can cause data race issues in logging due to static buffers.
2021-05-08 02:48:47 +09:00
Andrew Gaul 096a230b70
Allow arbitrary size AWS secret keys (#1648)
Previously s3fs limited these to 123 characters.  Fixes #1626.
2021-05-06 22:24:38 +09:00
Andrew Gaul 8ef01d37a9
Fix a few nits (#1645)
Make some strings more const, initialize members, and abort if lock
initialization fails.  Partially found via clang-tidy.
2021-05-06 19:40:35 +09:00
Andrew Gaul cb9148f6cd
Update for clang-tidy 12 (#1644) 2021-05-03 09:43:18 +09:00
Carsten Grohmann c4fa53ec8b
Add section about storage consumption to manpage (#1639)
References s3fs-fuse#1591. References s3fs-fuse#1595.
2021-05-01 15:09:29 +09:00
Andrew Gaul 1e2df406ee
Add Ubuntu 21.04 to CI (#1642) 2021-04-30 23:51:55 +09:00
Andrew Gaul 1dabfbe1da
Update CI to latest Fedora version (#1641) 2021-04-30 20:29:28 +09:00
Andrew Gaul 771bbfeac5
Do not create zero-byte object when creating file (#1640)
Previously s3fs created this object to store metadata and overwrote it
when flushing.  This prevented use with object stores which do not
allow overwrites like HDS.  Instead only create an in-memory
representation which reduces the time to create small files.
Fixes #1013.
2021-04-30 19:56:33 +09:00
Andrew Gaul 3694786112
Propagate errno instead of EIO (#1638)
This improves error fidelity.  Follows on to
b70f8db037.  References #1523.
2021-04-30 07:09:00 +09:00
Andrew Gaul e477d7d186
Add note about Homebrew and FUSE (#1637) 2021-04-25 21:35:08 +09:00
Andrew Gaul fbf3c83019
Consider S3 errors in HTTP PUT 200 responses (#1635)
S3 can emit these in unusual situations.  Fixes #1317.
2021-04-25 13:18:11 +09:00
Andrew Gaul 8a51a26819 Allow truncation of open and modified files
Regression introduced in f5bf41cf11.
Fixes #1575.
2021-04-25 12:35:35 +09:00
Takeshi Nakatani 1838f52e19 Declare undefined symbols for fallocate function 2021-04-25 10:12:25 +09:00
Andrew Gaul e9eb248f2f
Fail CheckBucket when S3 returns PermanentRedirect (#1630)
Previously s3fs allowed mounting but all operations returned EIO.
References #693.

Co-authored-by: Takeshi Nakatani <ggtakec@gmail.com>
2021-04-24 18:46:24 +09:00
Ambika Nair 77581eda59 Change in ibm iam endpoint 2021-04-21 21:42:55 +09:00
Andrew Gaul 7f3e423bbe
Preserve sub-second time precision (#1624)
Found via pjdfstests.  References #897.  References #1589.
2021-04-18 13:11:12 +09:00
Andrew Gaul 706e3cbebd
Update ctime and mtime when opening with O_TRUNC (#1623)
Found via pjdfstests.  References #1589.
2021-04-15 07:08:49 +09:00
Takeshi Nakatani 2effffd8e2 Fixed codes for cppcheck 2.4.1 2021-04-13 21:12:35 +09:00
Takeshi Nakatani 2908878988 Fixed codes for cppcheck 2.4.1 2021-04-13 21:12:35 +09:00
Andrew Gaul 01a26a9011
Do not allow open with O_RDONLY and O_TRUNC (#1620)
Found via pjdfstests.  References #1589.
2021-04-13 07:28:10 +09:00
Andrew Gaul 23e1fbf7b9
Update ctime when truncating file (#1619)
Found via pjdfstests.  References #1589.
2021-04-13 07:08:06 +09:00
Andrew Gaul ed2e877bb6
Return EACESS when lacking required executable bit (#1617)
This makes the check consistent with read and write.  Found via
pjdfstests.  References #1551.  References #1589.
2021-04-11 22:39:27 +09:00
Andrew Gaul 3663082a01
Do not allow renaming to a non-empty directory (#1604)
Found via pjdfstests.  References #1589.
2021-03-28 19:10:52 +09:00
Andrew Gaul 9645d57c05
Translate KeyTooLongError to ENAMETOOLONG (#1602)
AWS does not support keys longer than 1024 characters.  Add special
handling for HeadObject which does not have a response body.
Found via pjdfstests.  References #1589.
2021-03-28 13:17:41 +09:00
Andrew Gaul f6fbd75320
Return EFBIG when file exceeds multipart limit (#1600)
Found via pjdfstests.  References #1589.
2021-03-28 12:33:01 +09:00
Andrew Gaul 20a6c9d35d
Update to S3Proxy 1.8.0 (#1598)
Notably this fixes an issue with the filesystem backend which returned
the incorrect Content-Type for some directories.  It also reduces
memory use with GetObject using range requests with large objects.
References #1543.

Release notes:

https://github.com/gaul/s3proxy/releases/tag/s3proxy-1.8.0
2021-03-28 11:56:10 +09:00
Morteza Ghasempour 0a90a40569 remove duplicate line from README.md 2021-03-27 19:03:15 +09:00
Carsten Grohmann ef079f4e94 Don't ignore nomultipart when storage is low
When the temporary storage filled up, the old implementation uploaded
all data with multipart uploads, even if "nomultipart" was set.

The new implementation emits a warning and returns -ENOSPC instead.

Fixes #1595
2021-03-10 08:23:54 +09:00
Takeshi Nakatani b589ebec23 Output version and command parameters at startup 2021-03-06 19:11:57 +09:00
Andrew Gaul 8c58ba8ac0
Update ChangeLog and configure.ac for 1.89 (#1584)
Also make formatting more consistent.  Fixes #1588.

Co-authored-by: Takeshi Nakatani <ggtakec@gmail.com>
2021-02-24 08:03:55 +09:00
Takeshi Nakatani a19d223434 Bypass an OSX test(no different from other OS) which takes very time consuming 2021-02-23 20:47:51 +09:00
Takeshi Nakatani fc06419549
Fixed aws command error by rsa on ubuntu16.04 (#1585) 2021-02-23 11:41:52 +09:00
Andrew Gaul 032fcf2a47
Allow optional issuing of ListObjectsV2 (#1583)
This allows use of s3fs on object stores that do not implement
the V1 API.  Fixes #1573.
2021-02-23 09:45:13 +09:00
Takeshi Nakatani 5b5bc3114a Fixed a bug that padded null bytes when changing xattr 2021-02-21 12:23:36 +09:00
Takeshi Nakatani 81e267d421 Fixed the same bug in chmod/chown/utimens and added tests 2021-02-20 23:30:50 +09:00
Takeshi Nakatani 4fc92d59f3 Fixed a bug that filling NULL bytes when changing the attribute after renaming 2021-02-20 23:30:50 +09:00
Takeshi Nakatani a0f347b10f Added no_time_stamp_ms option mainly for testing 2021-02-20 20:24:26 +09:00
Carsten Grohmann 5debf523b0 Allow changing temporary directory used for tests
Set temporary directory to $TMPDIR if set or use default /var/tmp
2021-02-19 13:59:23 +09:00
Carsten Grohmann 1c8aadafd1 Fix typo in an error message of the test suite 2021-02-19 13:59:23 +09:00
Takeshi Nakatani d33f252404 Fixed forgetting to clean up test files 2021-02-14 00:07:46 +09:00
Takeshi Nakatani 4da56acdcc Improved processing when HEAD response is 400 2021-02-13 17:00:13 +09:00
Takeshi Nakatani 493802a605 Fixed osx mistype declaration and typo warnings 2021-02-13 12:04:35 +09:00
林千里 22b0ae9d51 set IsExpireTime to true by default
fixes #1563
2021-02-12 12:20:47 +09:00
Andrew Gaul 134a54b32f
Allow configuring the multipart threshold (#1562)
Also change default which improves write performance for files >= 25
MB and <= 5 GB, particularly over lossy networks.

Co-authored-by: Takeshi Nakatani <ggtakec@gmail.com>
2021-02-11 23:35:46 +09:00
Andrew Gaul 7f6fbb0021
Correct singlepart_copy_limit unit (#1561)
This is documented to be in MB not KB.

Co-authored-by: Takeshi Nakatani <ggtakec@gmail.com>
2021-02-11 22:53:50 +09:00
Andrew Gaul ec8bae9827
Do not periodically flush with nomultipartupload (#1559)
s3fs periodically flushes data to S3 and hole-punches the written
data, marking it as clean.  Without multipart copy, s3fs would write
the now-zeroed data along with any new data.  s3fs should not attempt
to periodically flush dirty data at all without multipart copy
support.  Fixes #1542.
2021-02-11 22:24:39 +09:00
Andrew Gaul a4d916af13
Allow configuring multipart copy size (#1555)
Also align configuration with mixupload.
2021-02-08 20:32:12 +09:00
Takeshi Nakatani 32f096fa3f Use clock_gettime instead of gettimeofday 2021-02-08 10:19:28 +09:00
Andrew Gaul c692093921
Copy at most 5 GB per multipart copy part request (#1553)
Previously mixupload failed with larger objects due to the AWS
constraint on part sizes.  This symptom was seen more frequently due
to periodic flushing of dirty data.  Fixes #1547.
2021-02-07 23:10:07 +09:00
Andrew Gaul 1a6d0826b5
Allow -1 value to disable max_dirty_data (#1552)
This matches the man page.
2021-02-07 22:14:31 +09:00
Andrew Gaul f2c5e38724
Allow integration tests to use larger files (#1548)
Previously these failed due to dd only copying 32 MB from /dev/urandom
and exhausting the limited space in /tmp.  References #1543.

Co-authored-by: Takeshi Nakatani <ggtakec@gmail.com>
2021-02-07 12:50:02 +09:00
Andrew Gaul bf33fe7f55
Increase buffer size to avoid warning (#1550)
This can be up to 54 bytes.

Co-authored-by: Takeshi Nakatani <ggtakec@gmail.com>
2021-02-07 12:26:58 +09:00
Andrew Gaul ff9d6a75c7
Parse more specific AWS error codes (#1549)
Fixes #1545.
2021-02-07 11:29:08 +09:00
Carsten Grohmann 4c6690f5f0
Add timestamps to debug log and cache integrity check (#1540)
* Add UTC timestamp to debug output

for easier correlation with external events

Old output:
$ s3fs mybucket /bucket/  -o curldbg,dbglevel=debug -d -d -f
[CRT] s3fs_logger.cpp:LowSetLogLevel(201): change debug level from...
[INF]     s3fs.cpp:set_mountpoint_attribute(3989): PROC(uid=0, ...

New output:
$ s3fs mybucket /bucket/  -o curldbg,dbglevel=debug -d -d -f
2021-01-28T21:09:16.264Z [CRT] s3fs_logger.cpp:LowSetLogLevel(202):...
2021-01-28T21:09:16.264Z [INF]     s3fs.cpp:set_mountpoint_attrib...

* Add UTC timestamp to cache integrity check

for easier correlation with external events.

$ s3fs mybucket /mybucket  -oset_check_cache_sigusr1=/tmp/check.cache

Old output:
$ kill -s SIGUSR1 $(pgrep s3fs)
$ cat /tmp/check.cache
------------------------------------------------------------
Check cache file and its stats file consistency
------------------------------------------------------------
------------------------------------------------------------
Summary - Total files:                0
          Detected error files:       0
          Detected error directories: 0
------------------------------------------------------------

New output:
$ kill -s SIGUSR1 $(pgrep s3fs)
$ cat /tmp/check.cache
---------------------------------------------------------------------------
Check cache file and its stats file consistency at 2021-01-30T13:04:14.111Z
---------------------------------------------------------------------------
---------------------------------------------------------------------------
Summary - Total files:                0
          Detected error files:       0
          Detected error directories: 0
---------------------------------------------------------------------------

* Fix indentation of S3fsLog::GetCurrentTime()
2021-02-04 10:41:29 +09:00
Andrew Gaul 67b9381825 Remove limit for single-part objects
AWS enforces a 5 GB limit for single-part objects but other S3
implementations like GCS support larger objects.  Fixes #1542.
2021-02-04 09:19:37 +09:00
Andrew Gaul 4b53d4bf6b
Suppress spurious SHA256 error message (#1544) 2021-02-04 07:28:51 +09:00
Carsten Grohmann ed85b72bf5 Add warning for failing metadata updates of large files
Metadata updates fail for large files if "-o nocopyapi" or
"-o nomultipart" is set.

This change is related to #1528
2021-01-28 10:20:32 +09:00
Andrew Gaul 17fda89ae9
Handle s3fs_get_content_md5 and s3fs_sha256_hex_fd errors (#1534)
Follows on to 4d833a4fb9.
2021-01-25 19:08:14 +09:00
Andrew Gaul 1987bcbea3
Re-fix retry logic (#1533)
This logic has always been broken but happened to try one time due to
&& operator precedence.  7158e50ee2
broke this further when quoting && since the command was not
evaluated.
2021-01-25 18:32:40 +09:00
Andrew Gaul d019dda4f7
Simplify substr manipulations with erase (#1532)
This avoids creating a new std::string.
2021-01-25 18:02:32 +09:00
Andrew Gaul dc9255bc5f
Prefer static_cast where possible (#1531) 2021-01-25 08:15:17 +09:00
Andrew Gaul b0e8758b63
Use result instead of res for consistency (#1530) 2021-01-25 07:56:10 +09:00
Takeshi Nakatani 6d65e30dd5 Fixed brew install with cask in github actions workflow 2021-01-21 17:34:03 +09:00
Andrew Gaul b70f8db037
Propagate errno from get_local_fent (#1525)
Follows on to 4d833a4fb9.
References #1523.
2021-01-20 23:21:14 +09:00
Andrew Gaul 4d833a4fb9
Return more specific errno when available (#1520)
Previously s3fs threw away some function return values and returned
EIO instead.  This was due to not trusting the mix of -1 and errno
return codes.  Correct the obviously incorrect ones via visual
inspection.  Stronger typing may find more occurrences.  Fixes #1519.
2021-01-18 18:50:49 +09:00
Andrew Gaul a6563211af Update ChangeLog and configure.ac for 1.88 2021-01-11 17:59:28 +09:00
Andrew Gaul 8d66b0e4a8
Updated ChangeLog for release 1.88 (#1514)
References #1511.
2021-01-05 16:46:41 +09:00
Takeshi Nakatani 555c1dde3d Use Gtihub Actions instead of Travis CI 2021-01-04 23:46:54 +09:00
Andrew Gaul b04bca37a5
Add configuration for Chaos HTTP Proxy (#1508)
This can find errors in retry logic.  Chaos HTTP Proxy does not
support SSL bouncestorage/chaos-http-proxy#1 so users must set
s3proxy.endpoint and run via:

CHAOS_HTTP_PROXY=1 S3_URL=http://127.0.0.1:8080 make check -C test

It can also be helpful to increase retries and reduce sleep times.
References #1504.
2021-01-04 23:32:04 +09:00
fly3366 168e588ac7
fix: Add reset offset (#1503) 2021-01-04 22:57:56 +09:00
Andrew Gaul 7158e50ee2
Quote | and && to allow passing it to retry (#1506) 2021-01-04 22:28:40 +09:00
Andrew Gaul bd0fadbe5f
Remove authorization header when remaking handle (#1505)
This avoids including Authorization in SignedHeaders.  s3fs will
recreate the Authorization header before sending the request.
2021-01-04 21:37:34 +09:00
kontrollanten d1c638ab7a fix 404 error message 2020-12-30 14:10:17 +09:00
fly3366 51f65d7b14 fix: miss header when retry 2020-12-23 22:16:07 +09:00
Andrew Gaul a16d00d673
Simply curl_slist_sort_insert (#1494)
Some good taste from Linus:
https://github.com/mkirchner/linked-list-good-taste
Also avoid an allocation when replacing a value and tighten up tests.
2020-12-23 20:29:33 +09:00
Takeshi Nakatani 4d0daddad4
Fixed about Github Actions failure(centos8 and macos) (#1498) 2020-12-23 19:34:04 +09:00
Brian Myers d246b9e8bf Update README for change in brew arguments
* Homebrew changed the arguments required to install casks. Previously,
  `brew cask install <package>` was the correct syntax, but now it's
  `brew install --cask <package>`. This small commit updates the
  instructions for installing `osxfuse` to reflect this change
2020-12-22 11:14:24 +09:00
Adam Johnson ef3d4e506d Update README note on eventual consistency
AWS promise read-after-write consistency now, but presumably non-AWS S3-compatible services are still eventually consistent.
2020-12-15 08:50:53 +09:00
Takeshi Nakatani 462b37b0bb Added Github Actions workflow 2020-12-05 22:57:40 +09:00
Takeshi Nakatani 4341291cc2
Changed a condition of the atime test(fixed #1477) (#1487) 2020-12-03 02:45:05 +09:00
Takeshi Nakatani c589886ba5
Added a condition to the atime test (#1477) 2020-12-02 23:21:25 +09:00
Andrew Gaul d0363b118e
Add tests for curl_util (#1481) 2020-11-24 21:37:09 +09:00
Takeshi Nakatani 533322859d Added comment for nullPointerRedundantCheck/cppcheck2.2 2020-11-21 20:06:14 +09:00
Andrew Gaul f8d5b76edb
Simplify handling of returned ETag (#1479)
This works around lifetime warnings uncovered by cppcheck.
References #1478.
2020-11-21 06:56:05 +09:00
Andrew Gaul 834862f8a4
Add S3_ENDPOINT test variable (#1476) 2020-11-15 22:10:26 +09:00
Takeshi Nakatani d9f6469b7b Fixed flushing dirty data and compressed the cache size 2020-11-14 16:45:37 +09:00
Takeshi Nakatani 852e6ee4c6 Fixed .gitignore for src/test_string_util.cpp 2020-11-14 16:22:30 +09:00
Andrew Gaul ecb24c9c26
Disable preprocessor warnings w/o optimizations (#1473)
This removes some fortify source warnings.  Also disable failing
memory sanitizer.
2020-11-09 21:48:07 +09:00
Andrew Gaul 543231c9f2
Address warnings from clang-tidy 11 (#1470) 2020-11-09 21:15:20 +09:00
Noah Meyerhans d96a08d4ad fixup! Implement AWS IMDSv2 support 2020-11-07 14:48:52 +09:00
Noah Meyerhans f2f930300a Implement AWS IMDSv2 support
AWS IMDSv2 is a session oriented method for retrieving instance metadata,
including IAM credentials, in Amazon EC2.  It is enabled by default in
non-enforcing mode in AWS (meaning it retains backwards compatibility with
existing IMDSv1 clients), but can be switched to enforcing mode, in which
clients are required to return API tokens with requests.

With this change, we implement support for IMDSv2 and enable it by default when
IAM roles are our source for authentication credentials.  In the event that
s3fs is running in cloud environment offering an IMDSv1-compatible API, we
support graceful fallback to that mode.  It can also be selected explicitly via
the imdsv1only mount option.

More details on IMDSv2 are available at
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
and
https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/

Signed-off-by: Noah Meyerhans <nmeyerha@amazon.com>
2020-11-07 14:48:52 +09:00
yoshihiko 81ad3ce0ae Fix directory authority 2020-11-07 14:47:53 +09:00
Takeshi Nakatani 38e1eaa8a3 Added logfile option for non-syslog logging 2020-10-19 20:10:19 +09:00
Andrew Gaul 6aa786b886
Flush dirty data after a writing a number of bytes (#1448)
This allows s3fs to write large files without consuming a large amount
of temporary local storage but can slow uploads due to server-side
copies.  References #617.  Fixed #1056.  Fixes #1257.
2020-10-13 22:30:42 +09:00
Takeshi Nakatani 58750cc441 Fixed a bug that symlink could not be read after restarting s3fs 2020-10-11 22:14:36 +09:00
Takeshi Nakatani 2188fb067e
Merge pull request #1446 from gaul/use-cache/relative-path
Allow relative path with use_path
2020-10-10 23:13:07 +09:00
Andrew Gaul 910255745e Allow relative path with use_path
Fixes #1161.
2020-10-10 18:13:23 +09:00
Takeshi Nakatani cf86fa51b0
Merge pull request #1441 from gaul/coverity
Suppress false positive from Coverity
2020-10-04 23:44:51 +09:00
Takeshi Nakatani a4e4ce8aea
Merge pull request #1440 from gaul/test/undefined-behavior-sanitizer
Add memory and undefined behavior sanitizer
2020-10-04 23:19:36 +09:00
Andrew Gaul 7f43b7fa53 Add memory and undefined behavior sanitizer
Remove Valgrind which duplicates the sanitizers and is much slower.
2020-10-04 22:55:27 +09:00
Takeshi Nakatani 0492f75197
Merge pull request #1439 from gaul/run-once
Protect curl_warnings_once with a mutex
2020-10-04 22:43:56 +09:00
Takeshi Nakatani 493cf20f95
Merge pull request #1435 from gaul/no-exceptions
Remove exceptions from s3fs_strtoofft
2020-10-04 22:19:25 +09:00
Andrew Gaul 3553fb65a0 Suppress false positive from Coverity 2020-10-04 14:58:13 +09:00
Takeshi Nakatani 059cc57ba6 Added atime and Corrected atime/mtime/ctime operations 2020-10-04 13:54:01 +09:00
Andrew Gaul 4df4ffe06f Protect curl_warnings_once with a mutex
This allows ThreadSanitizer to run without warnings.
2020-10-03 21:09:35 +09:00
Takeshi Nakatani 462347256d Changed the message level in the AutoFdEntity::GetFdEntity 2020-10-03 20:41:17 +09:00
Takeshi Nakatani 133feb67c3
Merge pull request #1434 from gaul/const-string
Use const std::string& where possible
2020-10-03 10:02:03 +09:00
Takeshi Nakatani be308e9d11
Merge pull request #1436 from ggtakec/fix_code
Fixed signature error due to case of hex string
2020-10-03 03:44:49 +09:00
Takeshi Nakatani 2cf195741c Fixed signature error due to case of hex string 2020-10-02 18:09:13 +00:00
Takeshi Nakatani f61baada46
Merge pull request #1433 from gaul/hex
Further simplify and centralize hex conversion
2020-10-03 00:34:02 +09:00
Takeshi Nakatani 6e1e0d1d31
Merge pull request #1432 from gaul/v2-or-v4
Add sigv4 flag and fix v4 regression
2020-10-03 00:09:55 +09:00
Takeshi Nakatani 66419e7292
Merge pull request #1431 from gaul/string/find_first_of
Prefer string::find_first_of(char) where possible
2020-10-02 23:42:17 +09:00
Takeshi Nakatani 9e998877e9
Merge pull request #1430 from gaul/ostringstream
Simplify use of ostringstream
2020-10-02 23:26:41 +09:00
Takeshi Nakatani 5ebd4039e6
Merge pull request #1426 from gaul/is_prefix
Call is_prefix instead of compare and substr
2020-10-02 23:03:10 +09:00
Andrew Gaul 3628b9d1e2 Remove exceptions from s3fs_strtoofft
Explicit return codes are simpler and safer.
2020-10-02 22:53:12 +09:00
Andrew Gaul 8d2bd874d7 Use const std::string& where possible
This hints to callers on usage.
2020-10-02 08:48:43 +09:00
Andrew Gaul 7e27c6cf7d Further simplify and centralize hex conversion 2020-10-01 23:31:06 +09:00
Andrew Gaul b8ff4ede49 Sign GET requests with V4 signature correctly
This bug was previously masked by v2 fallback.  Fixes regression from
81805715bd.
2020-10-01 20:03:14 +09:00
Andrew Gaul 081d6c1245 Allow setting signature V4-only
Default to allowing V2 or V4.
2020-10-01 20:03:14 +09:00
Andrew Gaul eb8004c355 Prefer string::find_first_of(char) where possible 2020-10-01 18:24:45 +09:00
Andrew Gaul b3bf9f8f54 Simplify use of ostringstream
Resetting this does not re-use the underlying buffer so creating a new
ostringstream has similar efficiency.
2020-10-01 17:55:34 +09:00
Andrew Gaul 503c86bb8a Call is_prefix instead of compare and substr 2020-09-27 22:19:54 +09:00
Takeshi Nakatani 757f4caee8
Merge pull request #1425 from gaul/create-bucket/sse
Do not send SSE headers during bucket creation
2020-09-27 11:34:15 +09:00
Takeshi Nakatani 95fabd1f3a
Merge pull request #1423 from gaul/nettle
Fix MD5 when using gnutls with nettle
2020-09-27 11:17:55 +09:00
Takeshi Nakatani 0b42e08636
Merge pull request #1422 from gaul/c++/append-assign-at
Remove calls to append, assign, and at
2020-09-27 10:59:48 +09:00
Takeshi Nakatani d5e4f99e72
Merge pull request #1413 from liuyongqing/master
not call put headers if not exist pending meta
2020-09-27 10:37:22 +09:00
Takeshi Nakatani 781d4dd857
Merge pull request #1424 from gaul/travis/ppc64
Disable builder for linux-ppc64le
2020-09-27 10:18:52 +09:00
Andrew Gaul f35fe850c0 Do not send SSE headers during bucket creation
This allows integration tests to pass with use_sse.
2020-09-26 13:25:18 +09:00
Andrew Gaul 7102b9eb74 Disable builder for linux-ppc64le
This works around a travis-ci.com Java/S3Proxy incompatibility.  Also
fix vim formatting.  References #1415.
2020-09-25 23:25:36 +09:00
Andrew Gaul 9a55c9fd9f Fix MD5 when using gnutls with nettle
This matches the non-nettle definition of s3fs_md5_fd.
2020-09-25 23:07:50 +09:00
Neeraj Kumar Kashyap 041b4ec05c enforce exact key match in IBM IAM response 2020-09-25 22:41:19 +09:00
Andrew Gaul 2438066d52 Remove calls to append, assign, and at
operator+, operator=, and operator[] are more idiomatic and consistent
with the code base.
2020-09-25 12:19:51 +09:00
Takeshi Nakatani 3c5b35b3b9
Update README.md for migrating travis-ci.com 2020-09-21 18:59:45 +09:00
yongqingliu e98827ec6f not call put headers if not exist pending meta 2020-09-21 14:12:07 +08:00
Takeshi Nakatani 864941d4d5
Merge pull request #1414 from gaul/clang-tidy/misc
Fix clang-tidy warnings
2020-09-21 07:49:59 +09:00
Andrew Gaul 05863a3178 Fix clang-tidy warnings 2020-09-21 07:03:42 +09:00
Takeshi Nakatani 075d161bb1
Merge pull request #1408 from gaul/env
Ensure environment variable is set when using ECS
2020-09-20 13:01:17 +09:00
Takeshi Nakatani f8b5c911ed
Merge pull request #1407 from gaul/pid
Rework s3fs pid handling
2020-09-20 12:32:32 +09:00
Takeshi Nakatani 6f40503328
Merge pull request #1406 from gaul/strcmp
Remove unnecessary calls to strcmp(str.c_str())
2020-09-20 11:28:41 +09:00
Takeshi Nakatani 3440c3348c
Merge pull request #1405 from gaul/is-prefix
Rename STR2NCMP to is_prefix for clarity
2020-09-20 10:03:53 +09:00
Takeshi Nakatani 853be26612
Merge pull request #1404 from liuyongqing/master
fix dead lock in disk insufficient and optimize code
2020-09-20 01:34:02 +09:00
Andrew Gaul 89b1c32b24 Rework s3fs pid handling
Previously S3FS_PID was not set in the correct shell and thus
ps u $S3FS_PID showed all programs.  This caused the flag detection to
fail when users ran other instances of s3fs with different flags.
References #1402.
2020-09-18 22:05:37 +09:00
Andrew Gaul 44d2cc15f7 Ensure environment variable is set when using ECS
Previously s3fs crashed when AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
was not set.  References #1162.
2020-09-18 08:14:58 +09:00
Takeshi Nakatani 76d88f2291
Merge pull request #1403 from gaul/vim/expandtab
Fix expandtab settings for tests
2020-09-17 21:35:58 +09:00
Takeshi Nakatani 72340cfbd9
Merge pull request #1401 from gaul/test/hardlink
Add test for hardlink
2020-09-17 20:04:07 +09:00
Andrew Gaul 81805715bd Remove unnecessary calls to strcmp(str.c_str()) 2020-09-17 20:00:45 +09:00
Andrew Gaul ebcbb02d16 Rename STR2NCMP to is_prefix for clarity
This may prevent confusion seen in
39ec8e242e.
2020-09-17 19:45:54 +09:00
Takeshi Nakatani 8c29b60129
Merge pull request #1400 from gaul/test/concurrent-read
Add test for concurrent reads
2020-09-17 19:26:44 +09:00
Takeshi Nakatani 93cf67c65d
Merge pull request #1399 from gaul/md5-sha256
Use more descriptive names for digest functions
2020-09-17 17:11:19 +09:00
yongqingliu 211cc0f5f2 fix dead lock in disk insufficient and optimize code 2020-09-16 22:45:28 +08:00
Andrew Gaul 8fb70c5e4a Fix expandtab settings for tests
This matches the main source.
2020-09-15 22:11:14 +09:00
Takeshi Nakatani c58c91fc4f
Merge pull request #1398 from gaul/memset
Remove unneeded memset calls
2020-09-15 22:09:54 +09:00
Andrew Gaul 78e2345c19 Add test for hardlink 2020-09-15 22:07:25 +09:00
Andrew Gaul 3bc565b986 Add test for concurrent reads 2020-09-15 21:25:51 +09:00
Andrew Gaul b7187352e1 Use more descriptive names for digest functions
Previously they used hex to refer to binary data which is confusing
since other std::string return types are actually hex.  Also remove
unused s3fs_md5sum.
2020-09-15 21:18:58 +09:00
Takeshi Nakatani 1520ca6220
Merge pull request #1397 from gaul/hex
Centralize hex conversion
2020-09-15 21:14:23 +09:00
Takeshi Nakatani f7a63d5c97
Merge pull request #1396 from gaul/32-bit-read
Use off_t for read sizes
2020-09-15 20:05:19 +09:00
Takeshi Nakatani 3958450c05 Added a class for automating fdentity reference counts 2020-09-14 22:02:28 +09:00
Andrew Gaul 5121c73ed1 Remove unneeded memset calls
Also use constant for array lengths.
2020-09-14 19:51:36 +09:00
Andrew Gaul 44eaac8471 Centralize hex conversion 2020-09-14 19:28:59 +09:00
Andrew Gaul 77501c3600 Use off_t for read sizes
This fixes an issue with large multipart_size on 32-bit platforms like
Raspberry Pi.
2020-09-14 18:12:23 +09:00
Takeshi Nakatani 8205607716 Fixed incorrect Signal related message at s3fs initialization and termination 2020-09-13 18:54:10 +09:00
Takeshi Nakatani c7132b7f56
Merge pull request #1390 from gaul/namespace-std
Remove uses of implicit namespace std
2020-09-13 13:26:22 +09:00
Andrew Gaul 1043e08dfa Remove uses of implicit namespace std
Fixed via:

sed -i '/using namespace std/{N;d}' src/*.cpp
sed -i 's/ string/ std::string/g' src/*.cpp
sed -i 's/(string/(std::string/g' src/*.cpp
sed -i 's/\[string/\[std::string/g' src/*.cpp
sed -i 's/^string/std::string/g' src/*.cpp
sed -i 's/ ifstream/ std::ifstream/g' src/*.cpp
sed -i 's/ istringstream/ std::istringstream/g' src/*.cpp
sed -i 's/ ostringstream/ std::ostringstream/g' src/*.cpp
sed -i 's/ max(/ std::max(/g' src/*.cpp
sed -i 's/ min(/ std::min(/g' src/*.cpp
sed -i 's/ endl/ std::endl/g' src/*.cpp
2020-09-13 11:57:20 +09:00
Takeshi Nakatani e5f4f9b69e
Merge pull request #1393 from gaul/32-bit-casting
Fix casting warnings on 32-bit platforms
2020-09-13 10:40:56 +09:00
Takeshi Nakatani 1e3c10d803
Merge pull request #1392 from gaul/large-singlepart-32-bit
Use 64-bit off_t when computing digests
2020-09-13 10:39:34 +09:00
Takeshi Nakatani 6fa4477673
Merge pull request #1389 from gaul/flags
Remove incorrect string prefix comparisons
2020-09-13 10:17:33 +09:00
Andrew Gaul 6d1e704e34 Fix casting warnings on 32-bit platforms 2020-09-12 18:22:32 +09:00
Andrew Gaul ffc33a447f Use 64-bit off_t when computing digests
This allows 32-bit platforms like Raspberry Pi to upload single-part
objects larger than 2 GB.
2020-09-12 15:00:23 +09:00
Andrew Gaul 39ec8e242e Remove incorrect string prefix comparisons
Previously flags like -o nouafoobar were parsed as -o noua.  Found
via:

grep STR2NCMP src/s3fs.cpp | grep -v '="'
2020-09-11 17:38:20 +09:00
Ben Mares 0d4e39ad1c
return 0 after parsing "use_session_token" arg (#1388)
return 0 after parsing "use_session_token" arg

I hope this might fix #651.

There's clearly an open bug in #651 confirmed by several users related to the parsing of the `-o use_session_token`. Looking at the source, I noticed that there was a return value everywhere except here, so I suspect this may be responsible.
2020-09-11 08:09:36 +09:00
Takeshi Nakatani 6112eb6a49
Merge pull request #1385 from liuyongqing/master
close FdEntity to avoid leakage
2020-09-10 19:44:48 +09:00
yongqingliu 986fab8738 close FdEntity to avoid leakage 2020-09-07 20:48:16 +08:00
Takeshi Nakatani b5ffd419d8 Source file division and set 4 spaces and cleanup 2020-08-26 17:43:50 +09:00
Takeshi Nakatani c6e23212bb
Merge pull request #1375 from gaul/cppcheck
Add const where possible
2020-08-21 07:32:39 +09:00
Takeshi Nakatani e75c11956c
Merge pull request #1374 from gaul/deep-archive
Add support for deep archive storage class
2020-08-21 07:01:43 +09:00
Andrew Gaul 1ec8528502 Add const where possible
Found via cppcheck 2.1
2020-08-20 23:46:11 +09:00
Andrew Gaul 892e7129c5 Add support for deep archive storage class 2020-08-20 23:42:45 +09:00
Takeshi Nakatani bdea2ee5c8
Merge pull request #1373 from gaul/enum
Use scoped enums for acl_t, sse_type_t, and storage_class_t
2020-08-20 23:06:52 +09:00
Takeshi Nakatani a5186c73c2
Merge pull request #1365 from ggtakec/check_lseek
Dynamically determine whether lseek extended options are supported
2020-08-20 22:36:56 +09:00
Takeshi Nakatani 4580e6ff93
Merge pull request #1364 from ggtakec/test_nospace_mv
Added mv file test when disk space is insufficient
2020-08-20 22:08:09 +09:00
Andrew Gaul 60d456a993 Use scoped enums for acl_t, sse_type_t, and storage_class_t
This prevents some kinds of implicit conversions.  Also deduplicate
str/from_str logic.  References #1371.
2020-08-20 18:46:18 +09:00
Takeshi Nakatani bcf6838e86
Merge pull request #1370 from gaul/vim/modeline
Correct vim modeline to 2-space indentation
2020-08-19 22:44:40 +09:00
Takeshi Nakatani 41a66d9706
Merge pull request #1368 from gaul/curl-lock
Prefer named locks in curl
2020-08-19 22:17:32 +09:00
Andrew Gaul 958ad83a4b Correct vim modeline to 2-space indentation 2020-08-19 21:03:46 +09:00
Andrew Gaul ba61470bae Prefer named locks in curl
This removes the confusing and unsafe pthread_mutex_t array.
2020-08-18 22:20:41 +09:00
Takeshi Nakatani 0e895f60a0
Merge pull request #1366 from gaul/curl_handles_lock
Consistently lock curl_times and curl_progress
2020-08-17 19:27:21 +09:00
Andrew Gaul 8210a1b2f2 Consistently lock curl_times and curl_progress
References #1362.
2020-08-17 09:47:38 +09:00
Takeshi Nakatani 2feefeec47 Added mv file test when disk space is insufficient 2020-08-16 12:47:29 +00:00
Takeshi Nakatani 55cb8920d5 Dynamically determine whether lseek extended options are supported 2020-08-16 12:37:11 +00:00
Takeshi Nakatani 46acbf10ba
Merge pull request #1363 from gaul/is-modified-race
Lock fdent_data_lock before accessing pagelist
2020-08-16 20:25:24 +09:00
Takeshi Nakatani f28e3bd89e
Merge pull request #1362 from gaul/test/sanitizers
Add script to run tests using sanitizers
2020-08-16 19:15:05 +09:00
Takeshi Nakatani 2c0408b95a
Merge pull request #1357 from gaul/readdir
Call readdir instead of readdir_r
2020-08-16 18:50:38 +09:00
Takeshi Nakatani 057da86d87
Merge pull request #1356 from gaul/printf-size_t
Prefer %zd and %zu for ssize_t and size_t
2020-08-16 18:16:59 +09:00
Andrew Gaul 8de6cb3504 Lock fdent_data_lock before accessing pagelist
Found via ThreadSanitizer.  References #1353.  References #1362.
2020-08-16 17:44:03 +09:00
Takeshi Nakatani 2bb745cdd7 Fixed a bug about move file over limit of ensure space 2020-08-16 17:42:20 +09:00
Andrew Gaul 35090ba4d5 Call readdir instead of readdir_r
Only a single thread uses this directory stream.  Further, modern
implementations are thread-safe by default and deprecated this call:

https://man7.org/linux/man-pages/man3/readdir_r.3.html
2020-08-16 13:33:23 +09:00
Andrew Gaul 132a1bebbb Prefer %zd and %zu for ssize_t and size_t
This removes unnecessary casting.
2020-08-15 17:30:23 +09:00
Andrew Gaul c8e13300e1 Add script to run tests using sanitizers
These currently show several kinds of errors.
2020-08-15 17:28:35 +09:00
yongqingliu a23d02923c add storage providers 2020-08-13 15:09:33 +09:00
106 changed files with 32361 additions and 18160 deletions

View File

@ -1,8 +1,37 @@
WarningsAsErrors: '*'
Checks: '
-*,
bugprone-*,
-bugprone-assignment-in-if-condition,
-bugprone-branch-clone,
-bugprone-easily-swappable-parameters,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-macro-parentheses,
-bugprone-narrowing-conversions,
-bugprone-unhandled-self-assignment,
cert-*,
-cert-dcl50-cpp,
-cert-env33-c,
-cert-err33-c,
-cert-err58-cpp,
cppcoreguidelines-*,
-cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-do-while,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-init-variables,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-narrowing-conversions,
-cppcoreguidelines-no-malloc,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-bounds-constant-array-index,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-pro-type-const-cast,
-cppcoreguidelines-pro-type-member-init,
-cppcoreguidelines-pro-type-reinterpret-cast,
-cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-vararg,
google-*,
-google-build-using-namespace,
-google-readability-casting,
@ -11,23 +40,38 @@ Checks: '
-google-runtime-int,
-google-runtime-references,
misc-*,
-misc-const-correctness,
-misc-include-cleaner,
-misc-no-recursion,
-misc-redundant-expression,
-misc-unused-parameters,
-misc-use-anonymous-namespace,
modernize-*,
-modernize-avoid-c-arrays,
-modernize-deprecated-headers,
-modernize-loop-convert,
-modernize-make-unique,
-modernize-raw-string-literal,
-modernize-return-braced-init-list,
-modernize-use-auto,
-modernize-use-nullptr,
-modernize-use-default-member-init,
-modernize-use-trailing-return-type,
-modernize-use-using,
performance-*,
-performance-avoid-endl,
-performance-no-int-to-ptr,
portability-*,
readability-*,
-readability-avoid-nested-conditional-operator,
-readability-braces-around-statements,
-readability-else-after-return,
-readability-function-cognitive-complexity,
-readability-function-size,
-readability-identifier-length,
-readability-implicit-bool-conversion,
-readability-inconsistent-declaration-parameter-name,
-readability-isolate-declaration,
-readability-magic-numbers,
-readability-named-parameter,
-readability-simplify-boolean-expr'
-readability-redundant-declaration,
-readability-simplify-boolean-expr,
-readability-suspicious-call-argument'

View File

@ -1,28 +1,40 @@
<!-- --------------------------------------------------------------------------
The following information is very important in order to help us to help you.
Omission of the following details may delay your support request or receive no
attention at all.
Keep in mind that the commands we provide to retrieve information are oriented
to GNU/Linux Distributions, so you could need to use others if you use s3fs on
macOS or BSD.
--------------------------------------------------------------------------- -->
### Additional Information
_The following information is very important in order to help us to help you. Omission of the following details may delay your support request or receive no attention at all._
_Keep in mind that the commands we provide to retrieve information are oriented to GNU/Linux Distributions, so you could need to use others if you use s3fs on macOS or BSD_
#### Version of s3fs being used (s3fs --version)
_example: 1.00_
#### Version of s3fs being used (`s3fs --version`)
<!-- example: V1.91 (commit:b19262a) -->
#### Version of fuse being used (pkg-config --modversion fuse, rpm -qi fuse, dpkg -s fuse)
_example: 2.9.4_
#### Version of fuse being used (`pkg-config --modversion fuse`, `rpm -qi fuse` or `dpkg -s fuse`)
<!-- example: 2.9.2 -->
#### Kernel information (uname -r)
_command result: uname -r_
#### Kernel information (`uname -r`)
<!-- example: 5.10.96-90.460.amzn2.x86_64 -->
#### GNU/Linux Distribution, if applicable (cat /etc/os-release)
_command result: cat /etc/os-release_
#### GNU/Linux Distribution, if applicable (`cat /etc/os-release`)
<!-- command result -->
#### s3fs command line used, if applicable
#### How to run s3fs, if applicable
<!-- Describe the s3fs "command line" or "/etc/fstab" entry used. -->
[] command line
[] /etc/fstab
<!-- Executed command line or /etc/fastab entry -->
```
```
#### /etc/fstab entry, if applicable
```
```
#### s3fs syslog messages (grep s3fs /var/log/syslog, journalctl | grep s3fs, or s3fs outputs)
_if you execute s3fs with dbglevel, curldbg option, you can get detail debug messages_
#### s3fs syslog messages (`grep s3fs /var/log/syslog`, `journalctl | grep s3fs`, or `s3fs outputs`)
<!-- if you execute s3fs with dbglevel, curldbg option, you can get detail debug messages. -->
```
```
### Details about issue
<!-- Please describe the content of the issue in detail. -->

View File

@ -1,5 +1,11 @@
<!-- --------------------------------------------------------------------------
Please describe the purpose of the pull request(such as resolving the issue)
and what the fix/update is.
--------------------------------------------------------------------------- -->
### Relevant Issue (if applicable)
_If there are Issues related to this PullRequest, please list it._
<!-- If there are Issues related to this PullRequest, please list it. -->
### Details
_Please describe the details of PullRequest._
<!-- Please describe the details of PullRequest. -->

297
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,297 @@
#
# s3fs - FUSE-based file system backed by Amazon S3
#
# Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
name: s3fs-fuse CI
on:
push:
pull_request:
#
# CRON event is fire on every sunday(UTC).
#
schedule:
- cron: '0 0 * * 0'
#
# Jobs
#
jobs:
Linux:
runs-on: ubuntu-latest
#
# build matrix for containers
#
strategy:
#
# do not stop jobs automatically if any of the jobs fail
#
fail-fast: false
#
# matrix for containers
#
matrix:
container:
- ubuntu:24.04
- ubuntu:22.04
- ubuntu:20.04
- debian:bookworm
- debian:bullseye
- debian:buster
- rockylinux:9
- rockylinux:8
- centos:centos7
- fedora:40
- fedora:39
- opensuse/leap:15
- alpine:3.19
container:
image: ${{ matrix.container }}
options: "--privileged --cap-add SYS_ADMIN --device /dev/fuse"
env:
# [NOTE]
# Installation special environment variables for debian and ubuntu.
#
DEBIAN_FRONTEND: noninteractive
steps:
# [NOTE]
# On openSUSE, tar and gzip must be installed before action/checkout.
#
- name: Install packages before checkout
run: |
if [ "${{ matrix.container }}" = "opensuse/leap:15" ]; then zypper install -y tar gzip; fi
# [NOTE]
# actions/checkout@v3 uses nodejs v16 and will be deprecated.
# However, @v4 does not work on centos7 depending on the glibc version,
# so we will continue to use @v3.
#
- name: Checkout source code(other than centos7)
if: matrix.container != 'centos:centos7'
uses: actions/checkout@v4
- name: Checkout source code(only centos7)
if: matrix.container == 'centos:centos7'
uses: actions/checkout@v3
# [NOTE]
# Matters that depend on OS:VERSION are determined and executed in the following script.
# Please note that the option to configure (CONFIGURE_OPTIONS) is set in the environment variable.
#
- name: Install packages
run: |
.github/workflows/linux-ci-helper.sh ${{ matrix.container }}
- name: Build
run: |
./autogen.sh
/bin/sh -c "./configure ${CONFIGURE_OPTIONS}"
make --jobs=$(nproc)
- name: clang-tidy
run: |
# skip if clang-tidy does not exist, e.g., CentOS 7
if command -v clang-tidy; then
make -C src/ clang-tidy
make -C test/ clang-tidy
fi
- name: Cppcheck
run: |
# specify the version range to run cppcheck (cppcheck version number is x.y or x.y.z)
if cppcheck --version | sed -e 's/\./ /g' | awk '{if (($2 * 1000 + $3) <= 2004) { exit(1) } }'; then
make cppcheck
fi
- name: Shellcheck
run: |
if shellcheck --version | awk -F '[ .]' '/version:/ && ($2 * 1000 + $3 <= 7) { exit(1) }'; then
make shellcheck
fi
- name: Test suite
run: |
make check -C src
make ALL_TESTS=1 check -C test || (test/filter-suite-log.sh test/test-suite.log; exit 1)
# [NOTE]
# Using macos-fuse-t
# This product(package) is a workaround for osxfuse which required an OS reboot(macos 11 and later).
# see. https://github.com/macos-fuse-t/fuse-t
# About osxfuse
# This job doesn't work with Github Actions using macOS 11+ because "load_osxfuse" returns
# "exit code = 1".(requires OS reboot)
#
macos12:
runs-on: macos-12
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Brew tap
run: |
TAPS="$(brew --repository)/Library/Taps";
if [ -e "$TAPS/caskroom/homebrew-cask" ]; then rm -rf "$TAPS/caskroom/homebrew-cask"; fi;
HOMEBREW_NO_AUTO_UPDATE=1 brew tap homebrew/homebrew-cask
HOMEBREW_NO_AUTO_UPDATE=1 brew tap macos-fuse-t/homebrew-cask
- name: Install fuse-t
run: |
HOMEBREW_NO_AUTO_UPDATE=1 brew install fuse-t
- name: Install brew other packages
run: |
S3FS_BREW_PACKAGES='automake cppcheck python3 coreutils gnu-sed shellcheck jq';
for s3fs_brew_pkg in ${S3FS_BREW_PACKAGES}; do if brew list | grep -q ${s3fs_brew_pkg}; then if brew outdated | grep -q ${s3fs_brew_pkg}; then HOMEBREW_NO_AUTO_UPDATE=1 brew upgrade ${s3fs_brew_pkg}; fi; else HOMEBREW_NO_AUTO_UPDATE=1 brew install ${s3fs_brew_pkg}; fi; done;
- name: Install awscli2
run: |
cd /tmp
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /
- name: Build
run: |
./autogen.sh
PKG_CONFIG_PATH=/usr/local/opt/curl/lib/pkgconfig:/usr/local/opt/openssl/lib/pkgconfig ./configure CXXFLAGS='-std=c++11 -DS3FS_PTHREAD_ERRORCHECK=1'
make --jobs=$(sysctl -n hw.ncpu)
- name: Cppcheck
run: |
# specify the version range to run cppcheck (cppcheck version number is x.y or x.y.z)
if cppcheck --version | sed -e 's/\./ /g' | awk '{if (($2 * 1000 + $3) <= 2004) { exit(1) } }'; then
make cppcheck
fi
- name: Shellcheck
run: |
if shellcheck --version | awk -F '[ .]' '/version:/ && ($2 * 1000 + $3 <= 7) { exit(1) }'; then
make shellcheck
fi
- name: Test suite
run: |
make check -C src
make ALL_TESTS=1 check -C test || (test/filter-suite-log.sh test/test-suite.log; exit 1)
MemoryTest:
runs-on: ubuntu-latest
#
# build matrix for containers
#
strategy:
#
# do not stop jobs automatically if any of the jobs fail
#
fail-fast: false
#
# matrix for type of checking
#
# [NOTE]
# Currently following test is not supported:
# - sanitize_memory : Future support planned
#
matrix:
checktype:
- glibc_debug
- sanitize_address
- sanitize_others
- sanitize_thread
- valgrind
container:
image: fedora:40
options: "--privileged --cap-add SYS_ADMIN --device /dev/fuse"
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Install packages
run: |
.github/workflows/linux-ci-helper.sh fedora:40
- name: Install clang
run: |
dnf install -y clang
if [ "${{ matrix.checktype }}" = "valgrind" ]; then
dnf install -y valgrind
fi
#
# Set CXX/CXXFLAGS and Variables for test
#
- name: Set variables
run: |
COMMON_CXXFLAGS='-g -Wno-cpp -DS3FS_PTHREAD_ERRORCHECK=1'
if [ "${{ matrix.checktype }}" = "glibc_debug" ]; then
echo "CXXFLAGS=${COMMON_CXXFLAGS} -O0 -D_GLIBCXX_DEBUG" >> $GITHUB_ENV
elif [ "${{ matrix.checktype }}" = "sanitize_address" ]; then
echo 'CXX=clang++' >> $GITHUB_ENV
echo "CXXFLAGS=${COMMON_CXXFLAGS} -O0 -fsanitize=address -fsanitize-address-use-after-scope" >> $GITHUB_ENV
echo 'ASAN_OPTIONS=detect_leaks=1,detect_stack_use_after_return=1' >> $GITHUB_ENV
elif [ "${{ matrix.checktype }}" = "sanitize_memory" ]; then
echo 'CXX=clang++' >> $GITHUB_ENV
echo "CXXFLAGS=${COMMON_CXXFLAGS} -O0 -fsanitize=memory" >> $GITHUB_ENV
elif [ "${{ matrix.checktype }}" = "sanitize_thread" ]; then
echo 'CXX=clang++' >> $GITHUB_ENV
echo "CXXFLAGS=${COMMON_CXXFLAGS} -O0 -fsanitize=thread" >> $GITHUB_ENV
echo 'TSAN_OPTIONS=halt_on_error=1' >> $GITHUB_ENV
# [NOTE]
# Set this to avoid following error when running configure.
# "FATAL: ThreadSanitizer: unexpected memory mapping"
sysctl vm.mmap_rnd_bits=28
elif [ "${{ matrix.checktype }}" = "sanitize_others" ]; then
echo 'CXX=clang++' >> $GITHUB_ENV
echo "CXXFLAGS=${COMMON_CXXFLAGS} -O1 -fsanitize=undefined,implicit-conversion,local-bounds,unsigned-integer-overflow" >> $GITHUB_ENV
elif [ "${{ matrix.checktype }}" = "valgrind" ]; then
echo "CXXFLAGS=${COMMON_CXXFLAGS} -O1" >> $GITHUB_ENV
echo 'VALGRIND=--leak-check=full' >> $GITHUB_ENV
echo 'RETRIES=100' >> $GITHUB_ENV
echo 'S3_URL=http://127.0.0.1:8081' >> $GITHUB_ENV
fi
- name: Build
run: |
./autogen.sh
/bin/sh -c "CXX=${CXX} CXXFLAGS=\"${CXXFLAGS}\" ./configure --prefix=/usr --with-openssl"
make
- name: Test suite
run: |
/bin/sh -c "ALL_TESTS=1 ASAN_OPTIONS=${ASAN_OPTIONS} TSAN_OPTIONS=${TSAN_OPTIONS} VALGRIND=${VALGRIND} RETRIES=${RETRIES} make check -C test || (test/filter-suite-log.sh test/test-suite.log; exit 1)"
#
# Local variables:
# tab-width: 4
# c-basic-offset: 4
# End:
# vim600: expandtab sw=4 ts=4 fdm=marker
# vim<600: expandtab sw=4 ts=4
#

307
.github/workflows/linux-ci-helper.sh vendored Executable file
View File

@ -0,0 +1,307 @@
#!/bin/sh
#
# s3fs - FUSE-based file system backed by Amazon S3
#
# Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# [NOTE]
# Since bash is not present in some Runner containers, this script
# runs in sh.
# pipefail etc. are not native variables of sh. It exists in bash's
# sh compatibility mode, but doesn't work in sh compatibility mode
# of ash such as alpine.
# However, it's not fatal that pipefail doesn't work for this script.
#
set -o errexit
set -o nounset
#set -o pipefail
#-----------------------------------------------------------
# Common variables
#-----------------------------------------------------------
PRGNAME=$(basename "$0")
echo "${PRGNAME} [INFO] Start Linux helper for installing packages."
#-----------------------------------------------------------
# Parameter check
#-----------------------------------------------------------
#
# Usage: ${PRGNAME} "OS:VERSION"
#
if [ $# -ne 1 ]; then
echo "${PRGNAME} [ERROR] No container name options specified."
fi
#-----------------------------------------------------------
# Container OS variables
#-----------------------------------------------------------
CONTAINER_FULLNAME=$1
# shellcheck disable=SC2034
CONTAINER_OSNAME=$(echo "${CONTAINER_FULLNAME}" | sed 's/:/ /g' | awk '{print $1}')
# shellcheck disable=SC2034
CONTAINER_OSVERSION=$(echo "${CONTAINER_FULLNAME}" | sed 's/:/ /g' | awk '{print $2}')
#-----------------------------------------------------------
# Common variables for awscli2
#-----------------------------------------------------------
AWSCLI_URI="https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"
AWSCLI_ZIP_FILE="awscliv2.zip"
#-----------------------------------------------------------
# Parameters for configure(set environments)
#-----------------------------------------------------------
CXXFLAGS="-O -DS3FS_PTHREAD_ERRORCHECK=1"
CONFIGURE_OPTIONS="--prefix=/usr --with-openssl"
#-----------------------------------------------------------
# OS dependent variables
#-----------------------------------------------------------
#
# Default values
#
PACKAGE_ENABLE_REPO_OPTIONS=""
PACKAGE_INSTALL_ADDITIONAL_OPTIONS=""
SHELLCHECK_DIRECT_INSTALL=0
AWSCLI_DIRECT_INSTALL=1
if [ "${CONTAINER_FULLNAME}" = "ubuntu:24.04" ]; then
PACKAGE_MANAGER_BIN="apt-get"
PACKAGE_UPDATE_OPTIONS="update -y -qq"
PACKAGE_INSTALL_OPTIONS="install -y"
INSTALL_PACKAGES="autoconf autotools-dev clang-tidy openjdk-21-jre-headless fuse jq libfuse-dev libcurl4-openssl-dev libxml2-dev locales-all mailcap libtool pkg-config libssl-dev attr curl python3-pip unzip"
INSTALL_CHECKER_PKGS="cppcheck shellcheck"
INSTALL_CHECKER_PKG_OPTIONS=""
elif [ "${CONTAINER_FULLNAME}" = "ubuntu:22.04" ]; then
PACKAGE_MANAGER_BIN="apt-get"
PACKAGE_UPDATE_OPTIONS="update -y -qq"
PACKAGE_INSTALL_OPTIONS="install -y"
INSTALL_PACKAGES="autoconf autotools-dev clang-tidy openjdk-17-jre-headless fuse jq libfuse-dev libcurl4-openssl-dev libxml2-dev locales-all mime-support libtool pkg-config libssl-dev attr curl python3-pip unzip"
INSTALL_CHECKER_PKGS="cppcheck shellcheck"
INSTALL_CHECKER_PKG_OPTIONS=""
elif [ "${CONTAINER_FULLNAME}" = "ubuntu:20.04" ]; then
PACKAGE_MANAGER_BIN="apt-get"
PACKAGE_UPDATE_OPTIONS="update -y -qq"
PACKAGE_INSTALL_OPTIONS="install -y"
INSTALL_PACKAGES="autoconf autotools-dev clang-tidy openjdk-17-jre-headless fuse jq libfuse-dev libcurl4-openssl-dev libxml2-dev locales-all mime-support libtool pkg-config libssl-dev attr curl python3-pip unzip"
INSTALL_CHECKER_PKGS="cppcheck shellcheck"
INSTALL_CHECKER_PKG_OPTIONS=""
elif [ "${CONTAINER_FULLNAME}" = "debian:bookworm" ]; then
PACKAGE_MANAGER_BIN="apt-get"
PACKAGE_UPDATE_OPTIONS="update -y -qq"
PACKAGE_INSTALL_OPTIONS="install -y"
INSTALL_PACKAGES="autoconf autotools-dev clang-tidy openjdk-17-jre-headless fuse jq libfuse-dev libcurl4-openssl-dev libxml2-dev locales-all mime-support libtool pkg-config libssl-dev attr curl procps python3-pip unzip"
INSTALL_CHECKER_PKGS="cppcheck shellcheck"
INSTALL_CHECKER_PKG_OPTIONS=""
elif [ "${CONTAINER_FULLNAME}" = "debian:bullseye" ]; then
PACKAGE_MANAGER_BIN="apt-get"
PACKAGE_UPDATE_OPTIONS="update -y -qq"
PACKAGE_INSTALL_OPTIONS="install -y"
INSTALL_PACKAGES="autoconf autotools-dev clang-tidy openjdk-17-jre-headless fuse jq libfuse-dev libcurl4-openssl-dev libxml2-dev locales-all mime-support libtool pkg-config libssl-dev attr curl procps python3-pip unzip"
INSTALL_CHECKER_PKGS="cppcheck shellcheck"
INSTALL_CHECKER_PKG_OPTIONS=""
elif [ "${CONTAINER_FULLNAME}" = "debian:buster" ]; then
PACKAGE_MANAGER_BIN="apt-get"
PACKAGE_UPDATE_OPTIONS="update -y -qq"
PACKAGE_INSTALL_OPTIONS="install -y"
INSTALL_PACKAGES="autoconf autotools-dev clang-tidy default-jre-headless fuse jq libfuse-dev libcurl4-openssl-dev libxml2-dev locales-all mime-support libtool pkg-config libssl-dev attr curl procps python3-pip unzip"
INSTALL_CHECKER_PKGS="cppcheck shellcheck"
INSTALL_CHECKER_PKG_OPTIONS=""
elif [ "${CONTAINER_FULLNAME}" = "rockylinux:9" ]; then
PACKAGE_MANAGER_BIN="dnf"
PACKAGE_UPDATE_OPTIONS="update -y -qq"
PACKAGE_INSTALL_OPTIONS="install -y"
PACKAGE_ENABLE_REPO_OPTIONS="--enablerepo=crb"
# [NOTE]
# Rocky Linux 9 (or CentOS Stream 9) images may have curl installation issues that
# conflict with the curl-minimal package.
#
PACKAGE_INSTALL_ADDITIONAL_OPTIONS="--allowerasing"
INSTALL_PACKAGES="clang-tools-extra curl-devel fuse fuse-devel gcc libstdc++-devel gcc-c++ glibc-langpack-en java-17-openjdk-headless jq libxml2-devel mailcap git automake make openssl openssl-devel attr diffutils curl python3 procps unzip xz https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm"
INSTALL_CHECKER_PKGS="cppcheck"
INSTALL_CHECKER_PKG_OPTIONS="--enablerepo=epel"
# [NOTE]
# For RockyLinux, ShellCheck is downloaded from the github archive and installed.
#
SHELLCHECK_DIRECT_INSTALL=1
elif [ "${CONTAINER_FULLNAME}" = "rockylinux:8" ]; then
PACKAGE_MANAGER_BIN="dnf"
PACKAGE_UPDATE_OPTIONS="update -y -qq"
PACKAGE_INSTALL_OPTIONS="install -y"
INSTALL_PACKAGES="clang-tools-extra curl-devel fuse fuse-devel gcc libstdc++-devel gcc-c++ glibc-langpack-en java-17-openjdk-headless jq libxml2-devel mailcap git automake make openssl openssl-devel attr diffutils curl python3 unzip"
INSTALL_CHECKER_PKGS="cppcheck"
INSTALL_CHECKER_PKG_OPTIONS="--enablerepo=powertools"
# [NOTE]
# For RockyLinux, ShellCheck is downloaded from the github archive and installed.
#
SHELLCHECK_DIRECT_INSTALL=1
elif [ "${CONTAINER_FULLNAME}" = "centos:centos7" ]; then
PACKAGE_MANAGER_BIN="yum"
PACKAGE_UPDATE_OPTIONS="update -y"
PACKAGE_INSTALL_OPTIONS="install -y"
# [NOTE]
# ShellCheck version(0.3.8) is too low to check.
# And in this version, it cannot be passed due to following error.
# "shellcheck: ./test/integration-test-main.sh: hGetContents: invalid argument (invalid byte sequence)"
#
INSTALL_PACKAGES="curl-devel fuse fuse-devel gcc libstdc++-devel llvm-toolset-7-clang-tools-extra gcc-c++ glibc-langpack-en java-11-openjdk-headless libxml2-devel mailcap git automake make openssl openssl-devel attr curl python3 epel-release unzip"
INSTALL_CHECKER_PKGS="cppcheck jq"
INSTALL_CHECKER_PKG_OPTIONS="--enablerepo=epel"
elif [ "${CONTAINER_FULLNAME}" = "fedora:40" ]; then
PACKAGE_MANAGER_BIN="dnf"
PACKAGE_UPDATE_OPTIONS="update -y -qq"
PACKAGE_INSTALL_OPTIONS="install -y"
INSTALL_PACKAGES="clang-tools-extra curl-devel fuse fuse-devel gcc libstdc++-devel gcc-c++ glibc-langpack-en java-latest-openjdk-headless jq libxml2-devel mailcap git automake make openssl openssl-devel curl attr diffutils procps python3-pip unzip"
INSTALL_CHECKER_PKGS="cppcheck ShellCheck"
INSTALL_CHECKER_PKG_OPTIONS=""
elif [ "${CONTAINER_FULLNAME}" = "fedora:39" ]; then
PACKAGE_MANAGER_BIN="dnf"
PACKAGE_UPDATE_OPTIONS="update -y -qq"
PACKAGE_INSTALL_OPTIONS="install -y"
INSTALL_PACKAGES="clang-tools-extra curl-devel fuse fuse-devel gcc libstdc++-devel gcc-c++ glibc-langpack-en java-latest-openjdk-headless jq libxml2-devel mailcap git automake make openssl openssl-devel curl attr diffutils procps python3-pip unzip"
INSTALL_CHECKER_PKGS="cppcheck ShellCheck"
INSTALL_CHECKER_PKG_OPTIONS=""
elif [ "${CONTAINER_FULLNAME}" = "opensuse/leap:15" ]; then
PACKAGE_MANAGER_BIN="zypper"
PACKAGE_UPDATE_OPTIONS="refresh"
PACKAGE_INSTALL_OPTIONS="install -y"
INSTALL_PACKAGES="automake clang-tools curl-devel fuse fuse-devel gcc-c++ java-17-openjdk-headless jq libxml2-devel make openssl openssl-devel python3-pip curl attr ShellCheck unzip"
INSTALL_CHECKER_PKGS="cppcheck ShellCheck"
INSTALL_CHECKER_PKG_OPTIONS=""
elif [ "${CONTAINER_FULLNAME}" = "alpine:3.19" ]; then
PACKAGE_MANAGER_BIN="apk"
PACKAGE_UPDATE_OPTIONS="update --no-progress"
PACKAGE_INSTALL_OPTIONS="add --no-progress --no-cache"
INSTALL_PACKAGES="bash clang-extra-tools curl g++ make automake autoconf libtool git curl-dev fuse-dev jq libxml2-dev openssl coreutils procps attr sed mailcap openjdk17 aws-cli"
INSTALL_CHECKER_PKGS="cppcheck shellcheck"
INSTALL_CHECKER_PKG_OPTIONS=""
AWSCLI_DIRECT_INSTALL=0
else
echo "No container configured for: ${CONTAINER_FULLNAME}"
exit 1
fi
#-----------------------------------------------------------
# Install
#-----------------------------------------------------------
#
# Update packages (ex. apt-get update -y -qq)
#
echo "${PRGNAME} [INFO] Updates."
/bin/sh -c "${PACKAGE_MANAGER_BIN} ${PACKAGE_UPDATE_OPTIONS}"
#
# Install packages ( with cppcheck )
#
echo "${PRGNAME} [INFO] Install packages."
/bin/sh -c "${PACKAGE_MANAGER_BIN} ${PACKAGE_ENABLE_REPO_OPTIONS} ${PACKAGE_INSTALL_OPTIONS} ${PACKAGE_INSTALL_ADDITIONAL_OPTIONS} ${INSTALL_PACKAGES}"
echo "${PRGNAME} [INFO] Install cppcheck package."
/bin/sh -c "${PACKAGE_MANAGER_BIN} ${INSTALL_CHECKER_PKG_OPTIONS} ${PACKAGE_INSTALL_OPTIONS} ${INSTALL_CHECKER_PKGS}"
#
# Install ShellCheck manually
#
if [ "${SHELLCHECK_DIRECT_INSTALL}" -eq 1 ]; then
echo "${PRGNAME} [INFO] Install shellcheck package from github archive."
if ! LATEST_SHELLCHECK_DOWNLOAD_URL=$(curl --silent --show-error https://api.github.com/repos/koalaman/shellcheck/releases/latest | jq -r '.assets[].browser_download_url | select(contains("linux.x86_64"))'); then
echo "Could not get shellcheck package url"
exit 1
fi
if ! curl -s -S -L -o /tmp/shellcheck.tar.xz "${LATEST_SHELLCHECK_DOWNLOAD_URL}"; then
echo "Failed to download shellcheck package from ${LATEST_SHELLCHECK_DOWNLOAD_URL}"
exit 1
fi
if ! tar -C /usr/bin/ -xf /tmp/shellcheck.tar.xz --no-anchored 'shellcheck' --strip=1; then
echo "Failed to extract and install shellcheck."
rm -f /tmp/shellcheck.tar.xz
exit 1
fi
rm -f /tmp/shellcheck.tar.xz
fi
# Check Java version
java -version
#
# Install awscli
#
if [ "${AWSCLI_DIRECT_INSTALL}" -eq 1 ]; then
echo "${PRGNAME} [INFO] Install awscli2 package."
CURRENT_DIR=$(pwd)
cd /tmp || exit 1
curl "${AWSCLI_URI}" -o "${AWSCLI_ZIP_FILE}"
unzip "${AWSCLI_ZIP_FILE}"
./aws/install
cd "${CURRENT_DIR}" || exit 1
fi
#-----------------------------------------------------------
# Set environment for configure
#-----------------------------------------------------------
echo "${PRGNAME} [INFO] Set environment for configure options"
echo "CXXFLAGS=${CXXFLAGS}" >> "${GITHUB_ENV}"
echo "CONFIGURE_OPTIONS=${CONFIGURE_OPTIONS}" >> "${GITHUB_ENV}"
echo "${PRGNAME} [INFO] Finish Linux helper for installing packages."
exit 0
#
# Local variables:
# tab-width: 4
# c-basic-offset: 4
# End:
# vim600: expandtab sw=4 ts=4 fdm=marker
# vim<600: expandtab sw=4 ts=4
#

25
.gitignore vendored
View File

@ -42,6 +42,7 @@ config.status
config.sub
configure
configure.scan
configure.ac~
depcomp
install-sh
libtool
@ -56,6 +57,11 @@ test-driver
compile
missing
#
# man page
#
doc/man/s3fs.1
#
# object directories
#
@ -73,8 +79,25 @@ missing
*.trs
default_commit_hash
src/s3fs
src/test_*
src/test_curl_util
src/test_page_list
src/test_string_util
test/chaos-http-proxy-*
test/junk_data
test/s3proxy-*
test/write_multiblock
test/mknod_test
test/truncate_read_file
test/cr_filename
#
# Windows ports
#
*.dll
*.exe
fuse.pc
WinFsp/
bin/
#
# Local variables:

View File

@ -1,150 +0,0 @@
#
# s3fs - FUSE-based file system backed by Amazon S3
#
# Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
language: cpp
dist: xenial
os: linux
jobs:
include:
- os: linux
dist: trusty
cache: apt
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq attr cppcheck libfuse-dev openjdk-7-jdk
- sudo update-alternatives --set java /usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java
- sudo -H pip install --upgrade awscli
script:
- ./autogen.sh
- ./configure CPPFLAGS='-I/usr/local/opt/openssl/include' CXXFLAGS='-std=c++03 -DS3FS_PTHREAD_ERRORCHECK=1'
- make
- make cppcheck
- make check -C src
- modprobe fuse
- travis_wait 30 make check -C test
- test/filter-suite-log.sh test/test-suite.log
- os: osx
osx_image: xcode9.2
cache:
directories:
- $HOME/Library/Caches/Homebrew
- /usr/local/Homebrew
- $HOME/.osx_cache
before_cache:
- brew cleanup
- cd /usr/local/Homebrew; find . \! -regex ".+\.git.+" -delete
- mkdir -p $HOME/.osx_cache; touch $HOME/.osx_cache/cached
before_install:
- TAPS="$(brew --repository)/Library/Taps";
if [ -e "$TAPS/caskroom/homebrew-cask" ]; then
rm -rf "$TAPS/caskroom/homebrew-cask";
fi;
if [ ! -f $HOME/.osx_cache/cached ]; then
echo "==> [Not found cache] brew tap homebrew/homebrew-cask";
echo "[NOTE]";
echo "If brew is executed without HOMEBREW_NO_AUTO_UPDATE=1,";
echo "python3 cannot be installed, so this is added as a temporary workaround.";
echo "If it is xcode 9.4 or higher, clear this patch.";
HOMEBREW_NO_AUTO_UPDATE=1 brew tap homebrew/homebrew-cask;
else
echo "==> [Found cache] HOMEBREW_NO_AUTO_UPDATE=1 brew tap homebrew/homebrew-cask";
HOMEBREW_NO_AUTO_UPDATE=1 brew tap homebrew/homebrew-cask;
fi
- HOMEBREW_NO_AUTO_UPDATE=1 brew cask install osxfuse
- S3FS_BREW_PACKAGES='cppcheck python3';
for s3fs_brew_pkg in ${S3FS_BREW_PACKAGES}; do
if brew list | grep -q ${s3fs_brew_pkg}; then
if brew outdated | grep -q ${s3fs_brew_pkg}; then
echo "==> Try to upgrade ${s3fs_brew_pkg}";
HOMEBREW_NO_AUTO_UPDATE=1 brew upgrade ${s3fs_brew_pkg};
fi
else
echo "==> Try to install ${s3fs_brew_pkg}";
HOMEBREW_NO_AUTO_UPDATE=1 brew install ${s3fs_brew_pkg};
fi;
done
- if pip3 --version; then
echo "==> Try to install awscli by pip3";
sudo -H pip3 install awscli;
else
echo "==> Try to install awscli by pip";
curl https://bootstrap.pypa.io/get-pip.py | sudo python;
sudo -H pip install awscli --ignore-installed matplotlib;
fi
- if [ -f /Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs ]; then
sudo chmod +s /Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs;
elif [ -f /Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse ]; then
sudo chmod +s /Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse;
else
exit 1;
fi
- if [ ! -f /usr/local/bin/truncate ]; then
echo "==> Make symbolic link truncate to gtruncate";
sudo ln -s /usr/local/opt/coreutils/bin/gtruncate /usr/local/bin/truncate;
fi
- if [ ! -f /usr/local/bin/stdbuf ]; then
echo "==> Make symbolic link stdbuf to gstdbuf";
sudo ln -s /usr/local/opt/coreutils/bin/gstdbuf /usr/local/bin/stdbuf;
fi
script:
- ./autogen.sh
- PKG_CONFIG_PATH=/usr/local/opt/curl/lib/pkgconfig:/usr/local/opt/openssl/lib/pkgconfig ./configure CXXFLAGS='-std=c++03 -DS3FS_PTHREAD_ERRORCHECK=1'
- make
- make cppcheck
- make check -C src
- if [ -f /Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs ]; then
/Library/Filesystems/osxfusefs.fs/Support/load_osxfusefs;
elif [ -f /Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse ]; then
/Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse;
else
exit 1;
fi
- travis_wait 30 make check -C test
- test/filter-suite-log.sh test/test-suite.log
- os: linux-ppc64le
dist: trusty
cache: apt
before_install:
- sudo add-apt-repository -y ppa:openjdk-r/ppa
- sudo apt-get update -qq
- sudo apt-get install -qq attr cppcheck libfuse-dev openjdk-7-jdk
- sudo update-alternatives --set java /usr/lib/jvm/java-7-openjdk-ppc64el/jre/bin/java
- sudo -H pip install --upgrade awscli
script:
- ./autogen.sh
- ./configure CPPFLAGS='-I/usr/local/opt/openssl/include' CXXFLAGS='-std=c++03 -DS3FS_PTHREAD_ERRORCHECK=1'
- make
- make cppcheck
- make check -C src
- modprobe fuse
- travis_wait 30 make check -C test
- test/filter-suite-log.sh test/test-suite.log
#
# Local variables:
# tab-width: 4
# c-basic-offset: 4
# End:
# vim600: noet sw=4 ts=4 fdm=marker
# vim<600: noet sw=4 ts=4
#

View File

@ -6,7 +6,9 @@ If you want specific instructions for some distributions, check the [wiki](https
Keep in mind using the pre-built packages when available.
1. Ensure your system satisfies build and runtime dependencies for:
## Compilation on Linux
### Ensure your system satisfies build and runtime dependencies for:
* fuse >= 2.8.4
* automake
@ -14,7 +16,10 @@ Keep in mind using the pre-built packages when available.
* make
* libcurl
* libxml2
* openssl
* openssl/gnutls/nss
* Please prepare the library according to the OS on which you will compile.
* It is necessary to match the library used by libcurl.
* Install the OpenSSL, GnuTLS or NSS devel package.
* mime.types (the package providing depends on the OS)
* s3fs tries to detect `/etc/mime.types` as default regardless of the OS
* Else s3fs tries to detect `/etc/apache2/mime.types` if OS is macOS
@ -22,13 +27,90 @@ Keep in mind using the pre-built packages when available.
* Alternatively, you can set mime.types file path with `mime` option without detecting these default files
* pkg-config (or your OS equivalent)
2. Then compile from master via the following commands:
* NOTE
If you have any trouble about details on required packages, see `INSTALL_PACKAGES` in [linux-ci-helper.sh](https://github.com/s3fs-fuse/s3fs-fuse/blob/master/.github/workflows/linux-ci-helper.sh).
```
git clone https://github.com/s3fs-fuse/s3fs-fuse.git
cd s3fs-fuse
./autogen.sh
./configure
make
sudo make install
```
### Then compile from master via the following commands:
1. Clone the source code:
```sh
git clone https://github.com/s3fs-fuse/s3fs-fuse.git
```
2. Configuration:
```sh
cd s3fs-fuse
./autogen.sh
./configure
```
Depending on the TLS library (OpenSSL/GnuTLS/NSS), add `--with-openssl`, `--with-gnutls` or `--with-nss` when executing `configure`. (If omitted, it is equivalent to `--with-openssl`.)
3. Bulding:
```sh
make
```
4. Installing:
```sh
sudo make install
```
### NOTE - The required libraries/components required to run s3fs are:
* fuse >= 2.8.4
* libcurl
* libxml2
* openssl/gnutls/nss
* mime.types (the package providing depends on the OS)
## Compilation on Windows (using MSYS2)
On Windows, use [MSYS2](https://www.msys2.org/) to compile for itself.
1. Install [WinFsp](https://github.com/billziss-gh/winfsp) to your machine.
2. Install dependencies onto MSYS2:
```sh
pacman -S git autoconf automake gcc make pkg-config libopenssl-devel libcurl-devel libxml2-devel libzstd-devel
```
3. Clone this repository, then change directory into the cloned one.
4. Copy WinFsp files to the directory:
```sh
cp -r "/c/Program Files (x86)/WinFsp" "./WinFsp"
```
5. Write `fuse.pc` to resolve the package correctly:
```sh
cat > ./fuse.pc << 'EOS'
arch=x64
prefix=${pcfiledir}/WinFsp
incdir=${prefix}/inc/fuse
implib=${prefix}/bin/winfsp-${arch}.dll
Name: fuse
Description: WinFsp FUSE compatible API
Version: 2.8.4
URL: http://www.secfs.net/winfsp/
Libs: "${implib}"
Cflags: -I"${incdir}"
EOS
```
6. Compile using the command line:
```sh
./autogen.sh
PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$(pwd)" ./configure
make
```
7. Copy binary files to distribute at one place:
```sh
mkdir ./bin
cp ./src/s3fs.exe ./bin/
cp ./WinFsp/bin/winfsp-x64.dll ./bin/
cp /usr/bin/msys-*.dll ./bin/
```
8. Distribute these files.

View File

@ -1,8 +1,90 @@
ChangeLog for S3FS
------------------
Version 1.87 -- 10 Aug, 2020 (major changes only)
Version 1.94 -- 23 Feb, 2024 (major changes only)
#2409 - Fixed a bug that mounting with ksmid specified to fail
#2404 - Fixed ordering problem between fdatasync and flush
#2399 - Fixed ListBucket/IAM edge cases
#2376 - Corrected list_bucket to search in stat cache during creating new file
#2369 - Make dir size 4096 not 0
#2351 - Added option free_space_ratio to control cache size
#2325 - Fixed a bug upload boundary calculation in StreamUpload
#2298 - Abort MPU when MPU fails to avoid litter
#2261 - Use explicit ownership for memory
#2179 - Require C++11
Version 1.93 -- 19 Jul, 2023 (major changes only)
#2212 - Allow listing implicit directories
#2194 - #2209 - #2211 - #2214 - #2215 - Fix thread safety issues
#2191 - #2201 - Add support for FUSE-T on macOS
Version 1.92 -- 21 May, 2023 (major changes only)
#1802 - #2104 - New option: streamupload
#1922 - Enable noobj_cache by default
#1927 - #2101 - New option: credlib and credlib_ops
#1957 - Fixed a bug that regular files could not be created by mknod
#1964 - Added stat information to the mount point
#1970 - #1986 - Enable notsup_compat_dir by default
#2000 - #2001 - Set mtime/ctime/atime of all objects as nanosecond
#2065 - Compatible with OpenSSL 3.0
#2075 - Added proxy and proxy_cred_file option
#2135 - Changed to rename cache files when renaming large files
#2148 - New option: bucket_size
Version 1.91 -- 07 Mar, 2022 (major changes only)
#1753 - Fix RowFlush can not upload last part smaller than 5MB using NoCacheMultipartPost
#1760 - Fix IAM role retrieval from IMDSv2
#1801 - Add option to allow unsigned payloads
#1809 - Fix mixupload return EntityTooSmall while a copypart is less than 5MB after split
#1855 - Allow compilation on Windows via MSYS2
#1868 - Handle utimensat UTIME_NOW and UTIME_OMIT special values
#1871 - #1880 - Preserve sub-second precision in more situations
#1879 - Always flush open files with O_CREAT flag
#1887 - Fixed not to call Flush even if the file size is increased
#1888 - Include climits to support musl libc
Version 1.90 -- 07 Aug, 2021 (major changes only)
#1599 - Don't ignore nomultipart when storage is low
#1600 - #1602 - #1604 - #1617 - #1619 - #1620 - #1623 - #1624 - Fix POSIX compatibility issues found by pjdfstest
#1630 - Fail CheckBucket when S3 returns PermanentRedirect
#1640 - #1655 - Do not create zero-byte object when creating file
#1648 - Allow arbitrary size AWS secret keys
#1668 - #1678 - Fix race conditions
#1696 - Set explicit Content-Length: 0 when initiating MPU
#1681 - Set CURLOPT_UNRESTRICTED_AUTH when authenticating
#1723 - Add jitter to avoid thundering herd
#1728 - Loosen CheckBucket to check only the bucket
#1729 - Add support for AWS-style environment variables
Version 1.89 -- 22 Feb, 2021 (major changes only)
#1520 - #1525 - #1534 - #1549 - Propagate S3 errors to errno more accurately
#1546 - #1559 - Allow writing > 5 GB single-part objects supported by some non-AWS S3
#1553 - #1555 - Allow configuration of multipart copy size and limit to 5 GB
#1562 - Allow configuration of multipart upload threshold and reduce default to 25 MB
#1565 - Set default stat timeout to 900 seconds correctly
#1579 - #1582 - Fix data corruption while updating metadata with use_cache
Version 1.88 -- 4 Jan, 2021 (major changes only)
#1349 - Fixed a bug about move file over limit of ensure space
#1363 - #1366 - #1439 - Fix multiple race conditions
#1365 - Dynamically determine whether lseek extended options are supported
#1374 - Add support for deep archive storage class
#1385 - Plug FdEntity leaks
#1388 - Fix use_session_token option parsing
#1392 - Allow 32-bit platforms to upload single-part objects > 2 GB
#1404 - Fix dead lock in disk insufficient and optimize code
#1408 - Ensure environment variable is set when using ECS
#1413 - not call put headers if not exist pending meta
#1425 - Do not send SSE headers during bucket creation
#1432 - Add sigv4 only option
#1437 - Add atime and correct atime/mtime/ctime operations
#1447 - Fixed a bug that symlink could not be read after restarting s3fs
#1448 - #1467 - Periodically flush written data to reduce temporary local storage
#1449 - Added logfile option for non-syslog logging
#1462 - Add AWS IMDSv2 support
#1502 - #1503 - #1505 - Fix multiple issues when retrying requests
Version 1.87 -- 10 Aug, 2020 (major changes only)
#1244 - use correct content-type when complete multipart upload
#1265 - Fixed a bug of stats cache compression
#1271 - Fixed the truncation bug of stat file for cache file

View File

@ -17,6 +17,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
######################################################################
SUBDIRS=src test doc
EXTRA_DIST=doc default_commit_hash
@ -28,18 +29,59 @@ dist-hook:
release : dist ../utils/release.sh
../utils/release.sh $(DIST_ARCHIVES)
.PHONY: cppcheck shellcheck
cppcheck:
cppcheck --quiet --error-exitcode=1 \
--inline-suppr \
--std=c++03 \
--std=c++11 \
--xml \
-D HAVE_ATTR_XATTR_H \
-D HAVE_SYS_EXTATTR_H \
-D HAVE_MALLOC_TRIM \
-U CURLE_PEER_FAILED_VERIFICATION \
-U P_tmpdir \
-U ENOATTR \
--enable=warning,style,information,missingInclude \
--suppress=missingIncludeSystem \
--suppress=unmatchedSuppression \
--suppress=useStlAlgorithm \
--suppress=checkLevelNormal \
src/ test/
#
# ShellCheck
#
SHELLCHECK_CMD = shellcheck
SHELLCHECK_SH_OPT = --shell=sh
SHELLCHECK_BASH_OPT = --shell=bash
# [NOTE]
# To control error warnings as a whole, specify the "SC<number>" with the following variables.
#
SHELLCHECK_COMMON_IGN = --exclude=SC1091
SHELLCHECK_CUSTOM_IGN = --exclude=SC1091
shellcheck:
@if type shellcheck > /dev/null 2>&1; then \
echo "* ShellCheck version"; \
$(SHELLCHECK_CMD) --version; \
echo ""; \
echo "* Check all sh files with ShellCheck"; \
LC_ALL=C.UTF-8 $(SHELLCHECK_CMD) $(SHELLCHECK_SH_OPT) $(SHELLCHECK_COMMON_IGN) $$(grep '#![[:space:]]*/bin/sh' $$(find . -type f -name \*.sh) | sed -e 's|^\(.*\):#\!.*$$|\1|g') || exit 1; \
echo "-> No error was detected."; \
echo ""; \
echo "* Check all bash files with ShellCheck"; \
LC_ALL=C.UTF-8 $(SHELLCHECK_CMD) $(SHELLCHECK_BASH_OPT) $(SHELLCHECK_COMMON_IGN) $$(grep '#![[:space:]]*/bin/bash' $$(find . -type f -name \*.sh) | sed -e 's|^\(.*\):#\!.*$$|\1|g') || exit 1; \
echo "-> No error was detected."; \
else \
echo "* ShellCheck is not installed, so skip this."; \
fi
#
# Local variables:
# tab-width: 4
# c-basic-offset: 4
# End:
# vim600: expandtab sw=4 ts=4 fdm=marker
# vim<600: expandtab sw=4 ts=4
#

View File

@ -1,15 +1,18 @@
# s3fs
s3fs allows Linux and macOS to mount an S3 bucket via FUSE.
s3fs preserves the native object format for files, allowing use of other
tools like [AWS CLI](https://github.com/aws/aws-cli).
[![Build Status](https://travis-ci.org/s3fs-fuse/s3fs-fuse.svg?branch=master)](https://travis-ci.org/s3fs-fuse/s3fs-fuse)
s3fs allows Linux, macOS, and FreeBSD to mount an S3 bucket via [FUSE(Filesystem in Userspace)](https://github.com/libfuse/libfuse).
s3fs makes you operate files and directories in S3 bucket like a local file system.
s3fs preserves the native object format for files, allowing use of other tools like [AWS CLI](https://github.com/aws/aws-cli).
[![s3fs-fuse CI](https://github.com/s3fs-fuse/s3fs-fuse/actions/workflows/ci.yml/badge.svg)](https://github.com/s3fs-fuse/s3fs-fuse/actions/workflows/ci.yml)
[![Twitter Follow](https://img.shields.io/twitter/follow/s3fsfuse.svg?style=social&label=Follow)](https://twitter.com/s3fsfuse)
![s3fs-fuse](https://github.com/ggtakec/s3fs-fuse-images/blob/master/images/s3fslogo.png)
## Features
* large subset of POSIX including reading/writing files, directories, symlinks, mode, uid/gid, and extended attributes
* compatible with Amazon S3, Google Cloud Storage, and other S3-based object stores
* compatible with Amazon S3, and other [S3-based object stores](https://github.com/s3fs-fuse/s3fs-fuse/wiki/Non-Amazon-S3)
* allows random writes and appends
* large files via multi-part upload
* renames via server-side copy
@ -55,7 +58,7 @@ Many systems provide pre-built packages:
sudo emerge net-fs/s3fs
```
* RHEL and CentOS 7 or newer through via EPEL:
* RHEL and CentOS 7 or newer via EPEL:
```
sudo yum install epel-release
@ -68,13 +71,23 @@ Many systems provide pre-built packages:
sudo zypper install s3fs
```
* macOS via [Homebrew](https://brew.sh/):
* macOS 10.12 and newer via [Homebrew](https://brew.sh/):
```
brew cask install osxfuse
brew install s3fs
brew install --cask macfuse
brew install gromgit/fuse/s3fs-mac
```
* FreeBSD:
```
pkg install fusefs-s3fs
```
* Windows:
Windows has its own install, seening in [this link](COMPILATION.md)
Otherwise consult the [compilation instructions](COMPILATION.md).
## Examples
@ -82,6 +95,8 @@ Otherwise consult the [compilation instructions](COMPILATION.md).
s3fs supports the standard
[AWS credentials file](https://docs.aws.amazon.com/cli/latest/userguide/cli-config-files.html)
stored in `${HOME}/.aws/credentials`. Alternatively, s3fs supports a custom passwd file.
Finally s3fs recognizes the `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN`
environment variables.
The default location for the s3fs password file can be created:
@ -114,12 +129,6 @@ You can also mount on boot by entering the following line to `/etc/fstab`:
mybucket /path/to/mountpoint fuse.s3fs _netdev,allow_other 0 0
```
or
```
mybucket /path/to/mountpoint fuse.s3fs _netdev,allow_other 0 0
```
If you use s3fs with a non-Amazon S3 implementation, specify the URL and path-style requests:
```
@ -147,7 +156,7 @@ Generally S3 cannot offer the same performance or semantics as a local file syst
* random writes or appends to files require rewriting the entire object, optimized with multi-part upload copy
* metadata operations such as listing directories have poor performance due to network latency
* [eventual consistency](https://en.wikipedia.org/wiki/Eventual_consistency) can temporarily yield stale data([Amazon S3 Data Consistency Model](https://docs.aws.amazon.com/AmazonS3/latest/dev/Introduction.html#ConsistencyModel))
* non-AWS providers may have [eventual consistency](https://en.wikipedia.org/wiki/Eventual_consistency) so reads can temporarily yield stale data (AWS offers read-after-write consistency [since Dec 2020](https://aws.amazon.com/about-aws/whats-new/2020/12/amazon-s3-now-delivers-strong-read-after-write-consistency-automatically-for-all-applications/))
* no atomic renames of files or directories
* no coordination between multiple clients mounting the same bucket
* no hard links
@ -155,6 +164,8 @@ Generally S3 cannot offer the same performance or semantics as a local file syst
## References
* [CSI for S3](https://github.com/ctrox/csi-s3) - Kubernetes CSI driver
* [docker-s3fs-client](https://github.com/efrecon/docker-s3fs-client) - Docker image containing s3fs
* [goofys](https://github.com/kahing/goofys) - similar to s3fs but has better performance and less POSIX compatibility
* [s3backer](https://github.com/archiecobbs/s3backer) - mount an S3 bucket as a single file
* [S3Proxy](https://github.com/gaul/s3proxy) - combine with s3fs to mount Backblaze B2, EMC Atmos, Microsoft Azure, and OpenStack Swift buckets
@ -172,4 +183,3 @@ Generally S3 cannot offer the same performance or semantics as a local file syst
Copyright (C) 2010 Randy Rizun <rrizun@gmail.com>
Licensed under the GNU GPL version 2

View File

@ -1,5 +1,5 @@
#! /bin/sh
#!/bin/sh
#
# This file is part of S3FS.
#
# Copyright 2009, 2010 Free Software Foundation, Inc.
@ -22,20 +22,19 @@
echo "--- Make commit hash file -------"
SHORTHASH="unknown"
type git > /dev/null 2>&1
if [ $? -eq 0 -a -d .git ]; then
RESULT=`git rev-parse --short HEAD`
if [ $? -eq 0 ]; then
SHORTHASH=${RESULT}
if command -v git > /dev/null 2>&1 && test -d .git; then
if RESULT=$(git rev-parse --short HEAD); then
SHORTHASH="${RESULT}"
fi
fi
echo ${SHORTHASH} > default_commit_hash
echo "${SHORTHASH}" > default_commit_hash
echo "--- Finished commit hash file ---"
echo "--- Start autotools -------------"
aclocal \
autoupdate \
&& aclocal \
&& autoheader \
&& automake --add-missing \
&& autoconf
@ -44,3 +43,11 @@ echo "--- Finished autotools ----------"
exit 0
#
# Local variables:
# tab-width: 4
# c-basic-offset: 4
# End:
# vim600: expandtab sw=4 ts=4 fdm=marker
# vim<600: expandtab sw=4 ts=4
#

View File

@ -19,11 +19,11 @@
######################################################################
dnl Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(s3fs, 1.87)
AC_PREREQ([2.69])
AC_INIT([s3fs],[1.94])
AC_CONFIG_HEADER([config.h])
AC_CANONICAL_SYSTEM
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([foreign])
AC_PROG_CXX
@ -32,8 +32,9 @@ AC_PROG_CC
AC_CHECK_HEADERS([sys/xattr.h])
AC_CHECK_HEADERS([attr/xattr.h])
AC_CHECK_HEADERS([sys/extattr.h])
AC_CHECK_FUNCS([fallocate])
CXXFLAGS="$CXXFLAGS -Wall -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2"
CXXFLAGS="-Wall -fno-exceptions -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=3 -std=c++11 $CXXFLAGS"
dnl ----------------------------------------------
dnl For macOS
@ -47,6 +48,7 @@ case "$target" in
*-darwin* )
# Do something specific for mac
min_fuse_version=2.7.3
min_fuse_t_version=1.0.20
;;
*)
# Default Case
@ -55,11 +57,24 @@ case "$target" in
;;
esac
dnl ----------------------------------------------
dnl Checking the FUSE library
dnl ----------------------------------------------
dnl Distinguish between Linux (libfuse) and macOS (FUSE-T).
dnl
found_fuse_t=no
PKG_CHECK_MODULES([FUSE_T], [fuse-t >= ${min_fuse_t_version}], [found_fuse_t=yes], [found_fuse_t=no])
AS_IF([test "$found_fuse_t" = "yes"],
[PKG_CHECK_MODULES([fuse_library_checking], [fuse-t >= ${min_fuse_t_version}])],
[PKG_CHECK_MODULES([fuse_library_checking], [fuse >= ${min_fuse_version}])])
dnl ----------------------------------------------
dnl Choice SSL library
dnl ----------------------------------------------
auth_lib=na
nettle_lib=no
use_openssl_30=no
dnl
dnl nettle library
@ -179,15 +194,24 @@ AS_IF(
dnl
dnl For PKG_CONFIG before checking nss/gnutls.
dnl this is redundant checking, but we need checking before following.
dnl
PKG_CHECK_MODULES([common_lib_checking], [fuse >= ${min_fuse_version} libcurl >= 7.0 libxml-2.0 >= 2.6 ])
AC_MSG_CHECKING([compile s3fs with])
case "${auth_lib}" in
openssl)
AC_MSG_RESULT(OpenSSL)
PKG_CHECK_MODULES([DEPS], [fuse >= ${min_fuse_version} libcurl >= 7.0 libxml-2.0 >= 2.6 libcrypto >= 0.9 ])
AS_IF([test "$found_fuse_t" = "yes"],
[PKG_CHECK_MODULES([DEPS], [fuse-t >= ${min_fuse_t_version} libcurl >= 7.0 libxml-2.0 >= 2.6 libcrypto >= 0.9 ])],
[PKG_CHECK_MODULES([DEPS], [fuse >= ${min_fuse_version} libcurl >= 7.0 libxml-2.0 >= 2.6 libcrypto >= 0.9 ])])
AC_MSG_CHECKING([openssl 3.0 or later])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([[#include <openssl/opensslv.h>
#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x30000000L
#error "found openssl is 3.0 or later(so compiling is stopped with error)"
#endif]], [[]])],
[AC_MSG_RESULT(no)],
[AC_MSG_RESULT(yes); use_openssl_30=yes])
;;
gnutls)
AC_MSG_RESULT(GnuTLS-gcrypt)
@ -196,7 +220,9 @@ gnutls)
AS_IF([test "$gnutls_nettle" = ""], [AC_CHECK_LIB(gcrypt, gcry_control, [gnutls_nettle=0])])
AS_IF([test $gnutls_nettle = 0],
[
PKG_CHECK_MODULES([DEPS], [fuse >= ${min_fuse_version} libcurl >= 7.0 libxml-2.0 >= 2.6 gnutls >= 2.12.0 ])
AS_IF([test "$found_fuse_t" = "yes"],
[PKG_CHECK_MODULES([DEPS], [fuse-t >= ${min_fuse_t_version} libcurl >= 7.0 libxml-2.0 >= 2.6 gnutls >= 2.12.0 ])],
[PKG_CHECK_MODULES([DEPS], [fuse >= ${min_fuse_version} libcurl >= 7.0 libxml-2.0 >= 2.6 gnutls >= 2.12.0 ])])
LIBS="-lgnutls -lgcrypt $LIBS"
AC_MSG_CHECKING([gnutls is build with])
AC_MSG_RESULT(gcrypt)
@ -210,7 +236,9 @@ nettle)
AS_IF([test "$gnutls_nettle" = ""], [AC_CHECK_LIB(nettle, nettle_MD5Init, [gnutls_nettle=1])])
AS_IF([test $gnutls_nettle = 1],
[
PKG_CHECK_MODULES([DEPS], [fuse >= ${min_fuse_version} libcurl >= 7.0 libxml-2.0 >= 2.6 nettle >= 2.7.1 ])
AS_IF([test "$found_fuse_t" = "yes"],
[PKG_CHECK_MODULES([DEPS], [fuse-t >= ${min_fuse_t_version} libcurl >= 7.0 libxml-2.0 >= 2.6 nettle >= 2.7.1 ])],
[PKG_CHECK_MODULES([DEPS], [fuse >= ${min_fuse_version} libcurl >= 7.0 libxml-2.0 >= 2.6 nettle >= 2.7.1 ])])
LIBS="-lgnutls -lnettle $LIBS"
AC_MSG_CHECKING([gnutls is build with])
AC_MSG_RESULT(nettle)
@ -219,7 +247,9 @@ nettle)
;;
nss)
AC_MSG_RESULT(NSS)
PKG_CHECK_MODULES([DEPS], [fuse >= ${min_fuse_version} libcurl >= 7.0 libxml-2.0 >= 2.6 nss >= 3.15.0 ])
AS_IF([test "$found_fuse_t" = "yes"],
[PKG_CHECK_MODULES([DEPS], [fuse-t >= ${min_fuse_t_version} libcurl >= 7.0 libxml-2.0 >= 2.6 nss >= 3.15.0 ])],
[PKG_CHECK_MODULES([DEPS], [fuse >= ${min_fuse_version} libcurl >= 7.0 libxml-2.0 >= 2.6 nss >= 3.15.0 ])])
;;
*)
AC_MSG_ERROR([unknown ssl library type.])
@ -227,6 +257,7 @@ nss)
esac
AM_CONDITIONAL([USE_SSL_OPENSSL], [test "$auth_lib" = openssl])
AM_CONDITIONAL([USE_SSL_OPENSSL_30], [test "$use_openssl_30" = yes])
AM_CONDITIONAL([USE_SSL_GNUTLS], [test "$auth_lib" = gnutls -o "$auth_lib" = nettle])
AM_CONDITIONAL([USE_GNUTLS_NETTLE], [test "$auth_lib" = nettle])
AM_CONDITIONAL([USE_SSL_NSS], [test "$auth_lib" = nss])
@ -309,15 +340,29 @@ AC_COMPILE_IFELSE(
]
)
dnl ----------------------------------------------
dnl dl library
dnl ----------------------------------------------
AC_CHECK_LIB([dl], [dlopen, dlclose, dlerror, dlsym], [], [AC_MSG_ERROR([Could not found dlopen, dlclose, dlerror and dlsym])])
dnl ----------------------------------------------
dnl build date
dnl ----------------------------------------------
AC_SUBST([MAN_PAGE_DATE], [$(date -r doc/man/s3fs.1.in +"%B %Y")])
dnl ----------------------------------------------
dnl output files
dnl ----------------------------------------------
AC_CONFIG_FILES(Makefile src/Makefile test/Makefile doc/Makefile)
AC_CONFIG_FILES(Makefile
src/Makefile
test/Makefile
doc/Makefile
doc/man/s3fs.1)
dnl ----------------------------------------------
dnl short commit hash
dnl ----------------------------------------------
AC_CHECK_PROG([GITCMD], [git —version], [yes], [no])
AC_CHECK_PROG([GITCMD], [git --version], [yes], [no])
AS_IF([test -d .git], [DOTGITDIR=yes], [DOTGITDIR=no])
AC_MSG_CHECKING([github short commit hash])
@ -341,3 +386,11 @@ dnl ----------------------------------------------
dnl end configuration
dnl ----------------------------------------------
#
# Local variables:
# tab-width: 4
# c-basic-offset: 4
# End:
# vim600: expandtab sw=4 ts=4 fdm=marker
# vim<600: expandtab sw=4 ts=4
#

View File

@ -1,4 +1,4 @@
.TH S3FS "1" "February 2011" "S3FS" "User Commands"
.TH S3FS "1" "@MAN_PAGE_DATE@" "S3FS" "User Commands"
.SH NAME
S3FS \- FUSE-based file system backed by Amazon S3
.SH SYNOPSIS
@ -40,7 +40,7 @@ Password files can be stored in two locations:
\fB$HOME/.passwd-s3fs\fP [0600]
.RE
.PP
s3fs also recognizes the \fBAWSACCESSKEYID\fP and \fBAWSSECRETACCESSKEY\fP environment variables.
s3fs also recognizes the \fBAWS_ACCESS_KEY_ID\fP and \fBAWS_SECRET_ACCESS_KEY\fP environment variables.
.SH OPTIONS
.SS "general options"
.TP
@ -65,11 +65,14 @@ if it is not specified bucket name (and path) in command line, must specify this
.TP
\fB\-o\fR default_acl (default="private")
the default canned acl to apply to all written s3 objects, e.g., "private", "public-read".
see https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl for the full list of canned acls.
see https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl for the full list of canned ACLs.
.TP
\fB\-o\fR retries (default="5")
number of times to retry a failed S3 transaction.
.TP
\fB\-o\fR tmpdir (default="/tmp")
local folder for temporary files.
.TP
\fB\-o\fR use_cache (default="" which means disabled)
local folder to use for local file cache.
.TP
@ -82,8 +85,7 @@ delete local file cache when s3fs starts and exits.
.TP
\fB\-o\fR storage_class (default="standard")
store object with specified storage class.
this option replaces the old option use_rrs.
Possible values: standard, standard_ia, onezone_ia, reduced_redundancy, intelligent_tiering, and glacier.
Possible values: standard, standard_ia, onezone_ia, reduced_redundancy, intelligent_tiering, glacier, glacier_ir, and deep_archive.
.TP
\fB\-o\fR use_rrs (default is disable)
use Amazon's Reduced Redundancy Storage.
@ -108,6 +110,7 @@ You can use "k" for short "kmsid".
If you san specify SSE-KMS type with your <kms id> in AWS KMS, you can set it after "kmsid:" (or "k:").
If you specify only "kmsid" ("k"), you need to set AWSSSEKMSID environment which value is <kms id>.
You must be careful about that you can not use the KMS id which is not same EC2 region.
Additionally, if you specify SSE-KMS, your endpoints must use Secure Sockets Layer(SSL) or Transport Layer Security(TLS).
.TP
\fB\-o\fR load_sse_c - specify SSE-C keys
Specify the custom-provided encryption keys file path for decrypting at downloading.
@ -165,11 +168,10 @@ specify expire time (seconds) for entries in the stat cache and symbolic link ca
specify expire time (seconds) for entries in the stat cache and symbolic link cache. This expire time is based on the time from the last access time of those cache.
This option is exclusive with stat_cache_expire, and is left for compatibility with older versions.
.TP
\fB\-o\fR enable_noobj_cache (default is disable)
enable cache entries for the object which does not exist.
s3fs always has to check whether file (or sub directory) exists under object (path) when s3fs does some command, since s3fs has recognized a directory which does not exist and has files or sub directories under itself.
It increases ListBucket request and makes performance bad.
You can specify this option for performance, s3fs memorizes in stat cache that the object (file or directory) does not exist.
\fB\-o\fR disable_noobj_cache (default is enable)
By default s3fs memorizes when an object does not exist up until the stat cache timeout.
This caching can cause staleness for applications.
If disabled, s3fs will not memorize objects and may cause extra HeadObject requests and reduce performance.
.TP
\fB\-o\fR no_check_certificate (by default this option is disabled)
server certificate won't be checked against the available certificate authorities.
@ -177,6 +179,18 @@ server certificate won't be checked against the available certificate authoritie
\fB\-o\fR ssl_verify_hostname (default="2")
When 0, do not verify the SSL certificate against the hostname.
.TP
\fB\-o\fR ssl_client_cert (default="")
Specify an SSL client certificate.
Specify this optional parameter in the following format:
"<SSL Cert>[:<Cert Type>[:<Private Key>[:<Key Type>
[:<Password>]]]]"
<SSL Cert>: Client certificate.
Specify the file path or NickName(for NSS, etc.).
<Cert Type>: Type of certificate, default is "PEM"(optional).
<Private Key>: Certificate's private key file(optional).
<Key Type>: Type of private key, default is "PEM"(optional).
<Password>: Passphrase of the private key(optional). It is also possible to omit this value and specify it using the environment variable "S3FS_SSL_PRIVKEY_PASSWORD".
.TP
\fB\-o\fR nodnscache - disable DNS cache.
s3fs is always using DNS cache, this option make DNS cache disable.
.TP
@ -188,7 +202,7 @@ maximum number of parallel request for listing objects.
.TP
\fB\-o\fR parallel_count (default="5")
number of parallel request for uploading big objects.
s3fs uploads large object (over 20MB) by multipart post request, and sends parallel requests.
s3fs uploads large object (over 25MB by default) by multipart post request, and sends parallel requests.
This option limits parallel request count which s3fs requests at once.
It is necessary to set this value depending on a CPU and a network band.
.TP
@ -196,10 +210,45 @@ It is necessary to set this value depending on a CPU and a network band.
part size, in MB, for each multipart request.
The minimum value is 5 MB and the maximum value is 5 GB.
.TP
\fB\-o\fR multipart_copy_size (default="512")
part size, in MB, for each multipart copy request, used for
renames and mixupload.
The minimum value is 5 MB and the maximum value is 5 GB.
Must be at least 512 MB to copy the maximum 5 TB object size
but lower values may improve performance.
.TP
\fB\-o\fR max_dirty_data (default="5120")
Flush dirty data to S3 after a certain number of MB written.
The minimum value is 50 MB. -1 value means disable.
Cannot be used with nomixupload.
.TP
\fB\-o\fR bucket_size (default=maximum long unsigned integer value)
The size of the bucket with which the corresponding
elements of the statvfs structure will be filled. The option
argument is an integer optionally followed by a
multiplicative suffix (GB, GiB, TB, TiB, PB, PiB,
EB, EiB) (no spaces in between). If no suffix is supplied,
bytes are assumed; eg: 20000000, 30GB, 45TiB. Note that
s3fs does not compute the actual volume size (too
expensive): by default it will assume the maximum possible
size; however, since this may confuse other software which
uses s3fs, the advertised bucket size can be set with this
option.
.TP
\fB\-o\fR ensure_diskfree (default 0)
sets MB to ensure disk free space. This option means the threshold of free space size on disk which is used for the cache file by s3fs.
s3fs makes file for downloading, uploading and caching files.
If the disk free space is smaller than this value, s3fs do not use diskspace as possible in exchange for the performance.
If the disk free space is smaller than this value, s3fs do not use disk space as possible in exchange for the performance.
.TP
\fB\-o\fR free_space_ratio (default="10")
sets min free space ratio of the disk. The value of this option can be between 0 and 100. It will control
the size of the cache according to this ratio to ensure that the idle ratio of the disk is greater than this value.
For example, when the disk space is 50GB, the default value will
ensure that the disk will reserve at least 50GB * 10%% = 5GB of remaining space.
.TP
\fB\-o\fR multipart_threshold (default="25")
threshold, in MB, to use multipart upload instead of
single-part. Must be at least 5 MB.
.TP
\fB\-o\fR singlepart_copy_limit (default="512")
maximum size, in MB, of a single-part copy before trying
@ -222,8 +271,11 @@ If the s3fs could not connect to the region specified by this option, s3fs could
But if you do not specify this option, and if you can not connect with the default region, s3fs will retry to automatically connect to the other region.
So s3fs can know the correct region name, because s3fs can find it in an error from the S3 server.
.TP
\fB\-o\fR sigv2 (default is signature version 4)
sets signing AWS requests by using Signature Version 2.
\fB\-o\fR sigv2 (default is signature version 4 falling back to version 2)
sets signing AWS requests by using only signature version 2.
.TP
\fB\-o\fR sigv4 (default is signature version 4 falling back to version 2)
sets signing AWS requests by using only signature version 4.
.TP
\fB\-o\fR mp_umask (default is "0000")
sets umask for the mount point directory.
@ -232,28 +284,58 @@ In the opposite case s3fs allows access to all users as the default.
But if you set the allow_other with this option, you can control the permissions of the mount point by this option like umask.
.TP
\fB\-o\fR umask (default is "0000")
sets umask for files under the mountpoint. This can allow
sets umask for files under the mountpoint. This can allow
users other than the mounting user to read and write to files
that they did not create.
.TP
\fB\-o\fR nomultipart - disable multipart uploads
.TP
\fB\-o\fR streamupload (default is disable)
Enable stream upload.
If this option is enabled, a sequential upload will be performed in parallel with the write from the part that has been written during a multipart upload.
This is expected to give better performance than other upload functions.
Note that this option is still experimental and may change in the future.
.TP
\fB\-o\fR max_thread_count (default is "5")
Specifies the number of threads waiting for stream uploads.
Note that this option and Streamm Upload are still experimental and subject to change in the future.
This option will be merged with "parallel_count" in the future.
.TP
\fB\-o\fR enable_content_md5 (default is disable)
Allow S3 server to check data integrity of uploads via the Content-MD5 header.
This can add CPU overhead to transfers.
.TP
\fB\-o\fR enable_unsigned_payload (default is disable)
Do not calculate Content-SHA256 for PutObject and UploadPart
payloads. This can reduce CPU overhead to transfers.
.TP
\fB\-o\fR ecs (default is disable)
This option instructs s3fs to query the ECS container credential metadata address instead of the instance metadata address.
.TP
\fB\-o\fR iam_role (default is no IAM role)
This option requires the IAM role name or "auto". If you specify "auto", s3fs will automatically use the IAM role names that are set to an instance. If you specify this option without any argument, it is the same as that you have specified the "auto".
.TP
\fB\-o\fR imdsv1only (default is to use IMDSv2 with fallback to v1)
AWS instance metadata service, used with IAM role authentication,
supports the use of an API token. If you're using an IAM role in an
environment that does not support IMDSv2, setting this flag will skip
retrieval and usage of the API token when retrieving IAM credentials.
.TP
\fB\-o\fR ibm_iam_auth (default is not using IBM IAM authentication)
This option instructs s3fs to use IBM IAM authentication. In this mode, the AWSAccessKey and AWSSecretKey will be used as IBM's Service-Instance-ID and APIKey, respectively.
.TP
\fB\-o\fR ibm_iam_endpoint (default is https://iam.bluemix.net)
\fB\-o\fR ibm_iam_endpoint (default is https://iam.cloud.ibm.com)
Sets the URL to use for IBM IAM authentication.
.TP
\fB\-o\fR credlib (default=\"\" which means disabled)
Specifies the shared library that handles the credentials containing the authentication token.
If this option is specified, the specified credential and token processing provided by the shared library ant will be performed instead of the built-in credential processing.
This option cannot be specified with passwd_file, profile, use_session_token, ecs, ibm_iam_auth, ibm_iam_endpoint, imdsv1only and iam_role option.
.TP
\fB\-o\fR credlib_opts (default=\"\" which means disabled)
Specifies the options to pass when the shared library specified in credlib is loaded and then initialized.
For the string specified in this option, specify the string defined by the shared library.
.TP
\fB\-o\fR use_xattr (default is not handling the extended attribute)
Enable to handle the extended attribute (xattrs).
If you set this option, you can use the extended attribute.
@ -281,6 +363,10 @@ If this option is specified with nocopyapi, then s3fs ignores it.
\fB\-o\fR use_path_request_style (use legacy API calling style)
Enable compatibility with S3-like APIs which do not support the virtual-host request style, by using the older path request style.
.TP
\fB\-o\fR listobjectsv2 (use ListObjectsV2)
Issue ListObjectsV2 instead of ListObjects, useful on object
stores without ListObjects support.
.TP
\fB\-o\fR noua (suppress User-Agent header)
Usually s3fs outputs of the User-Agent in "s3fs/<version> (commit hash <hash>; <using ssl library name>)" format.
If this option is specified, s3fs suppresses the output of the User-Agent.
@ -298,16 +384,16 @@ This name will be added to logging messages and user agent headers sent by s3fs.
s3fs complements lack of information about file/directory mode if a file or a directory object does not have x-amz-meta-mode header.
As default, s3fs does not complements stat information for a object, then the object will not be able to be allowed to list/modify.
.TP
\fB\-o\fR notsup_compat_dir (not support compatibility directory types)
As a default, s3fs supports objects of the directory type as much as possible and recognizes them as directories.
Objects that can be recognized as directory objects are "dir/", "dir", "dir_$folder$", and there is a file object that does not have a directory object but contains that directory path.
s3fs needs redundant communication to support all these directory types.
The object as the directory created by s3fs is "dir/".
By restricting s3fs to recognize only "dir/" as a directory, communication traffic can be reduced.
This option is used to give this restriction to s3fs.
However, if there is a directory object other than "dir/" in the bucket, specifying this option is not recommended.
s3fs may not be able to recognize the object correctly if an object created by s3fs exists in the bucket.
Please use this option when the directory in the bucket is only "dir/" object.
\fB\-o\fR compat_dir (enable support of alternative directory names)
.RS
s3fs supports two different naming schemas "dir/" and "dir" to map directory names to S3 objects and vice versa by default. As a third variant, directories can be determined indirectly if there is a file object with a path (e.g. "/dir/file") but without the parent directory. This option enables a fourth variant, "dir_$folder$", created by older applications.
.TP
S3fs uses only the first schema "dir/" to create S3 objects for directories.
.TP
The support for these different naming schemas causes an increased communication effort.
.TP
If you do not have access permissions to the bucket and specify a directory path created by a client other than s3fs for the mount point, you cannot start because the mount point directory cannot be found by s3fs. But by specifying this option, you can avoid this error.
.RE
.TP
\fB\-o\fR use_wtf8 - support arbitrary file system encoding.
S3 requires all object names to be valid UTF-8. But some
@ -319,7 +405,7 @@ Useful on clients not using UTF-8 as their file system encoding.
.TP
\fB\-o\fR use_session_token - indicate that session token should be provided.
If credentials are provided by environment variables this switch
forces presence check of AWSSESSIONTOKEN variable.
forces presence check of AWS_SESSION_TOKEN variable.
Otherwise an error is returned.
.TP
\fB\-o\fR requester_pays (default is disable)
@ -330,10 +416,35 @@ Specify the path of the mime.types file.
If this option is not specified, the existence of "/etc/mime.types" is checked, and that file is loaded as mime information.
If this file does not exist on macOS, then "/etc/apache2/mime.types" is checked as well.
.TP
\fB\-o\fR proxy (default="")
This option specifies a proxy to S3 server.
Specify the proxy with '[<scheme://]hostname(fqdn)[:<port>]' formatted.
'<schema>://' can be omitted, and 'http://' is used when omitted.
Also, ':<port>' can also be omitted. If omitted, port 443 is used for HTTPS schema, and port 1080 is used otherwise.
This option is the same as the curl command's '--proxy(-x)' option and libcurl's 'CURLOPT_PROXY' flag.
This option is equivalent to and takes precedence over the environment variables 'http_proxy', 'all_proxy', etc.
.TP
\fB\-o\fR proxy_cred_file (default="")
This option specifies the file that describes the username and passphrase for authentication of the proxy when the HTTP schema proxy is specified by the 'proxy' option.
Username and passphrase are valid only for HTTP schema.
If the HTTP proxy does not require authentication, this option is not required.
Separate the username and passphrase with a ':' character and specify each as a URL-encoded string.
.TP
\fB\-o\fR ipresolve (default="whatever")
Select what type of IP addresses to use when establishing a connection.
Default('whatever') can use addresses of all IP versions(IPv4 and IPv6) that your system allows.
If you specify 'IPv4', only IPv4 addresses are used.
And when 'IPv6' is specified, only IPv6 addresses will be used.
.TP
\fB\-o\fR logfile - specify the log output file.
s3fs outputs the log file to syslog. Alternatively, if s3fs is started with the "-f" option specified, the log will be output to the stdout/stderr.
You can use this option to specify the log file that s3fs outputs.
If you specify a log file with this option, it will reopen the log file when s3fs receives a SIGHUP signal. You can use the SIGHUP signal for log rotation.
.TP
\fB\-o\fR dbglevel (default="crit")
Set the debug message level. set value as crit (critical), err (error), warn (warning), info (information) to debug level. default debug level is critical.
If s3fs run with "-d" option, the debug level is set information.
When s3fs catch the signal SIGUSR2, the debug level is bumpup.
When s3fs catch the signal SIGUSR2, the debug level is bump up.
.TP
\fB\-o\fR curldbg - put curl debug message
Put the debug message from libcurl when this option is specified.
@ -341,11 +452,20 @@ Specify "normal" or "body" for the parameter.
If the parameter is omitted, it is the same as "normal".
If "body" is specified, some API communication body data will be output in addition to the debug message output as "normal".
.TP
\fB\-o\fR no_time_stamp_msg - no time stamp in debug message
The time stamp is output to the debug message by default.
If this option is specified, the time stamp will not be output in the debug message.
It is the same even if the environment variable "S3FS_MSGTIMESTAMP" is set to "no".
.TP
\fB\-o\fR set_check_cache_sigusr1 (default is stdout)
If the cache is enabled, you can check the integrity of the cache file and the cache file's stats info file.
This option is specified and when sending the SIGUSR1 signal to the s3fs process checks the cache status at that time.
This option can take a file path as parameter to output the check result to that file.
The file path parameter can be omitted. If omitted, the result will be output to stdout or syslog.
.TP
\fB\-o\fR update_parent_dir_stat (default is disable)
The parent directory's mtime and ctime are updated when a file or directory is created or deleted (when the parent directory's inode is updated).
By default, parent directory statistics are not updated.
.SS "utility mode options"
.TP
\fB\-u\fR or \fB\-\-incomplete\-mpu\-list\fR
@ -360,19 +480,74 @@ It can be specified as year, month, day, hour, minute, second, and it is express
For example, "1Y6M10D12h30m30s".
.SH FUSE/MOUNT OPTIONS
.TP
Most of the generic mount options described in 'man mount' are supported (ro, rw, suid, nosuid, dev, nodev, exec, noexec, atime, noatime, sync async, dirsync). Filesystems are mounted with '\-onodev,nosuid' by default, which can only be overridden by a privileged user.
Most of the generic mount options described in 'man mount' are supported (ro, rw, suid, nosuid, dev, nodev, exec, noexec, atime, noatime, sync, async, dirsync). Filesystems are mounted with '\-onodev,nosuid' by default, which can only be overridden by a privileged user.
.TP
There are many FUSE specific mount options that can be specified. e.g. allow_other. See the FUSE README for the full set.
.SH SERVER URL/REQUEST STYLE
Be careful when specifying the server endpoint(URL).
.TP
If your bucket name contains dots("."), you should use the path request style(using "use_path_request_style" option).
.TP
Also, if you are using a server other than Amazon S3, you need to specify the endpoint with the "url" option. At that time, depending on the server you are using, you may have to specify the path request style("use_path_request_style" option).
.SH LOCAL STORAGE CONSUMPTION
.TP
s3fs requires local caching for operation. You can enable a local cache with "\-o use_cache" or s3fs uses temporary files to cache pending requests to s3.
.TP
Apart from the requirements discussed below, it is recommended to keep enough cache resp. temporary storage to allow one copy each of all files open for reading and writing at any one time.
.TP
.SS Local cache with \[dq]\-o use_cache\[dq]
.TP
s3fs automatically maintains a local cache of files. The cache folder is specified by the parameter of "\-o use_cache". It is only a local cache that can be deleted at any time. s3fs rebuilds it if necessary.
.TP
Whenever s3fs needs to read or write a file on S3, it first creates the file in the cache directory and operates on it.
.TP
The amount of local cache storage used can be indirectly controlled with "\-o ensure_diskfree".
.TP
.SS Without local cache
.TP
Since s3fs always requires some storage space for operation, it creates temporary files to store incoming write requests until the required s3 request size is reached and the segment has been uploaded. After that, this data is truncated in the temporary file to free up storage space.
.TP
Per file you need at least twice the part size (default 5MB or "-o multipart_size") for writing multipart requests or space for the whole file if single requests are enabled ("\-o nomultipart").
.SH PERFORMANCE CONSIDERATIONS
.TP
This section discusses settings to improve s3fs performance.
.TP
In most cases, backend performance cannot be controlled and is therefore not part of this discussion.
.TP
Details of the local storage usage is discussed in "LOCAL STORAGE CONSUMPTION".
.TP
.SS CPU and Memory Consumption
.TP
s3fs is a multi-threaded application. Depending on the workload it may use multiple CPUs and a certain amount of memory. You can monitor the CPU and memory consumption with the "top" utility.
.TP
.SS Performance of S3 requests
.TP
s3fs provides several options (e.g. "\-o multipart_size", "\-o parallel_count") to control behaviour and thus indirectly the performance. The possible combinations of these options in conjunction with the various S3 backends are so varied that there is no individual recommendation other than the default values. Improved individual settings can be found by testing and measuring.
.TP
The two options "Enable no object cache" ("\-o enable_noobj_cache") and "Disable support of alternative directory names" ("\-o notsup_compat_dir") can be used to control shared access to the same bucket by different applications:
.TP
.IP \[bu]
Enable no object cache ("\-o enable_noobj_cache")
.RS
.TP
If a bucket is used exclusively by an s3fs instance, you can enable the cache for non-existent files and directories with "\-o enable_noobj_cache". This eliminates repeated requests to check the existence of an object, saving time and possibly money.
.RE
.IP \[bu]
Enable support of alternative directory names ("\-o compat_dir")
.RS
.TP
s3fs recognizes "dir/" objects as directories. Clients other than s3fs may use "dir", "dir_$folder$" objects as directories, or directory objects may not exist. In order for s3fs to recognize these as directories, you can specify the "compat_dir" option.
.RE
.IP \[bu]
Completion of file and directory information ("\-o complement_stat")
.RS
.TP
s3fs uses the "x-amz-meta-mode header" to determine if an object is a file or a directory. For this reason, objects that do not have the "x-amz-meta-mode header" may not produce the expected results(The directory cannot be displayed, etc.). By specifying the "complement_stat" option, s3fs can automatically complete this missing attribute information, and you can get the expected results.
.RE
.SH NOTES
.TP
The maximum size of objects that s3fs can handle depends on Amazon S3. For example, up to 5 GB when using single PUT API. And up to 5 TB is supported when Multipart Upload API is used.
.TP
If enabled via the "use_cache" option, s3fs automatically maintains a local cache of files in the folder specified by use_cache. Whenever s3fs needs to read or write a file on S3, it first downloads the entire file locally to the folder specified by use_cache and operates on it. When fuse_release() is called, s3fs will re-upload the file to S3 if it has been changed. s3fs uses MD5 checksums to minimize downloads from S3.
.TP
The folder specified by use_cache is just a local cache. It can be deleted at any time. s3fs rebuilds it on demand.
.TP
Local file caching works by calculating and comparing MD5 checksums (ETag HTTP header).
.TP
s3fs leverages /etc/mime.types to "guess" the "correct" content-type based on file name extension. This means that you can copy a website to S3 and serve it up directly from S3 with correct content-types!
.SH SEE ALSO
fuse(8), mount(8), fusermount(1), fstab(5)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -21,36 +21,94 @@ bin_PROGRAMS=s3fs
AM_CPPFLAGS = $(DEPS_CFLAGS)
if USE_GNUTLS_NETTLE
AM_CPPFLAGS += -DUSE_GNUTLS_NETTLE
AM_CPPFLAGS += -DUSE_GNUTLS_NETTLE
endif
if USE_SSL_OPENSSL_30
AM_CPPFLAGS += -DUSE_OPENSSL_30
endif
s3fs_SOURCES = \
s3fs.cpp \
curl.cpp \
cache.cpp \
string_util.cpp \
s3fs_util.cpp \
fdcache.cpp \
common_auth.cpp \
addhead.cpp \
sighandlers.cpp
s3fs.cpp \
s3fs_global.cpp \
s3fs_help.cpp \
s3fs_logger.cpp \
s3fs_xml.cpp \
metaheader.cpp \
mpu_util.cpp \
curl.cpp \
curl_handlerpool.cpp \
curl_multi.cpp \
curl_util.cpp \
s3objlist.cpp \
cache.cpp \
string_util.cpp \
s3fs_cred.cpp \
s3fs_util.cpp \
fdcache.cpp \
fdcache_entity.cpp \
fdcache_page.cpp \
fdcache_stat.cpp \
fdcache_auto.cpp \
fdcache_fdinfo.cpp \
fdcache_pseudofd.cpp \
fdcache_untreated.cpp \
addhead.cpp \
sighandlers.cpp \
autolock.cpp \
threadpoolman.cpp \
common_auth.cpp
if USE_SSL_OPENSSL
s3fs_SOURCES += openssl_auth.cpp
s3fs_SOURCES += openssl_auth.cpp
endif
if USE_SSL_GNUTLS
s3fs_SOURCES += gnutls_auth.cpp
s3fs_SOURCES += gnutls_auth.cpp
endif
if USE_SSL_NSS
s3fs_SOURCES += nss_auth.cpp
s3fs_SOURCES += nss_auth.cpp
endif
s3fs_LDADD = $(DEPS_LIBS)
noinst_PROGRAMS = test_string_util
noinst_PROGRAMS = \
test_curl_util \
test_page_list \
test_string_util
test_string_util_SOURCES = string_util.cpp test_string_util.cpp
test_curl_util_SOURCES = common_auth.cpp curl_util.cpp string_util.cpp test_curl_util.cpp s3fs_global.cpp s3fs_logger.cpp
if USE_SSL_OPENSSL
test_curl_util_SOURCES += openssl_auth.cpp
endif
if USE_SSL_GNUTLS
test_curl_util_SOURCES += gnutls_auth.cpp
endif
if USE_SSL_NSS
test_curl_util_SOURCES += nss_auth.cpp
endif
TESTS = test_string_util
test_curl_util_LDADD = $(DEPS_LIBS)
test_page_list_SOURCES = \
fdcache_page.cpp \
s3fs_global.cpp \
s3fs_logger.cpp \
string_util.cpp \
test_page_list.cpp
test_string_util_SOURCES = string_util.cpp test_string_util.cpp s3fs_logger.cpp
TESTS = \
test_curl_util \
test_page_list \
test_string_util
clang-tidy:
clang-tidy $(s3fs_SOURCES) -- $(DEPS_CFLAGS) $(CPPFLAGS)
#
# Local variables:
# tab-width: 4
# c-basic-offset: 4
# End:
# vim600: noexpandtab sw=4 ts=4 fdm=marker
# vim<600: noexpandtab sw=4 ts=4
#

View File

@ -21,26 +21,20 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <syslog.h>
#include <curl/curl.h>
#include <sstream>
#include <fstream>
#include <string>
#include <map>
#include <list>
#include <strings.h>
#include <vector>
#include "common.h"
#include "addhead.h"
#include "curl.h"
#include "s3fs.h"
using namespace std;
#include "addhead.h"
#include "curl_util.h"
#include "s3fs_logger.h"
//-------------------------------------------------------------------
// Symbols
//-------------------------------------------------------------------
#define ADD_HEAD_REGEX "reg:"
static constexpr char ADD_HEAD_REGEX[] = "reg:";
//-------------------------------------------------------------------
// Class AdditionalHeader
@ -52,229 +46,196 @@ AdditionalHeader AdditionalHeader::singleton;
//-------------------------------------------------------------------
AdditionalHeader::AdditionalHeader()
{
if(this == AdditionalHeader::get()){
is_enable = false;
}else{
abort();
}
if(this == AdditionalHeader::get()){
is_enable = false;
}else{
abort();
}
}
AdditionalHeader::~AdditionalHeader()
{
if(this == AdditionalHeader::get()){
Unload();
}else{
abort();
}
if(this == AdditionalHeader::get()){
Unload();
}else{
abort();
}
}
bool AdditionalHeader::Load(const char* file)
{
if(!file){
S3FS_PRN_WARN("file is NULL.");
return false;
}
Unload();
if(!file){
S3FS_PRN_WARN("file is nullptr.");
return false;
}
Unload();
ifstream AH(file);
if(!AH.good()){
S3FS_PRN_WARN("Could not open file(%s).", file);
return false;
}
// read file
string line;
ADDHEAD *paddhead;
while(getline(AH, line)){
if('#' == line[0]){
continue;
}
if(line.empty()){
continue;
}
// load a line
istringstream ss(line);
string key; // suffix(key)
string head; // additional HTTP header
string value; // header value
if(0 == isblank(line[0])){
ss >> key;
}
if(ss){
ss >> head;
if(ss && static_cast<size_t>(ss.tellg()) < line.size()){
value = line.substr(static_cast<int>(ss.tellg()) + 1);
}
std::ifstream AH(file);
if(!AH.good()){
S3FS_PRN_WARN("Could not open file(%s).", file);
return false;
}
// check it
if(head.empty()){
if(key.empty()){
continue;
}
S3FS_PRN_ERR("file format error: %s key(suffix) is no HTTP header value.", key.c_str());
Unload();
return false;
// read file
std::string line;
while(getline(AH, line)){
if(line.empty()){
continue;
}
if('#' == line[0]){
continue;
}
// load a line
std::istringstream ss(line);
std::string key; // suffix(key)
std::string head; // additional HTTP header
std::string value; // header value
if(0 == isblank(line[0])){
ss >> key;
}
if(ss){
ss >> head;
if(ss && static_cast<size_t>(ss.tellg()) < line.size()){
value = line.substr(static_cast<int>(ss.tellg()) + 1);
}
}
// check it
if(head.empty()){
if(key.empty()){
continue;
}
S3FS_PRN_ERR("file format error: %s key(suffix) is no HTTP header value.", key.c_str());
Unload();
return false;
}
if(0 == strncasecmp(key.c_str(), ADD_HEAD_REGEX, strlen(ADD_HEAD_REGEX))){
// regex
if(key.size() <= strlen(ADD_HEAD_REGEX)){
S3FS_PRN_ERR("file format error: %s key(suffix) does not have key std::string.", key.c_str());
continue;
}
key.erase(0, strlen(ADD_HEAD_REGEX));
// compile
std::unique_ptr<regex_t> preg(new regex_t);
int result;
if(0 != (result = regcomp(preg.get(), key.c_str(), REG_EXTENDED | REG_NOSUB))){ // we do not need matching info
char errbuf[256];
regerror(result, preg.get(), errbuf, sizeof(errbuf));
S3FS_PRN_ERR("failed to compile regex from %s key by %s.", key.c_str(), errbuf);
continue;
}
addheadlist.emplace_back(std::move(preg), key, head, value);
}else{
// not regex, directly comparing
addheadlist.emplace_back(nullptr, key, head, value);
}
// set flag
is_enable = true;
}
paddhead = new ADDHEAD;
if(0 == strncasecmp(key.c_str(), ADD_HEAD_REGEX, strlen(ADD_HEAD_REGEX))){
// regex
if(key.size() <= strlen(ADD_HEAD_REGEX)){
S3FS_PRN_ERR("file format error: %s key(suffix) does not have key string.", key.c_str());
delete paddhead;
continue;
}
key = key.substr(strlen(ADD_HEAD_REGEX));
// compile
regex_t* preg = new regex_t;
int result;
if(0 != (result = regcomp(preg, key.c_str(), REG_EXTENDED | REG_NOSUB))){ // we do not need matching info
char errbuf[256];
regerror(result, preg, errbuf, sizeof(errbuf));
S3FS_PRN_ERR("failed to compile regex from %s key by %s.", key.c_str(), errbuf);
delete preg;
delete paddhead;
continue;
}
// set
paddhead->pregex = preg;
paddhead->basestring = key;
paddhead->headkey = head;
paddhead->headvalue = value;
}else{
// not regex, directly comparing
paddhead->pregex = NULL;
paddhead->basestring = key;
paddhead->headkey = head;
paddhead->headvalue = value;
}
// add list
addheadlist.push_back(paddhead);
// set flag
if(!is_enable){
is_enable = true;
}
}
return true;
return true;
}
void AdditionalHeader::Unload()
{
is_enable = false;
is_enable = false;
for(addheadlist_t::iterator iter = addheadlist.begin(); iter != addheadlist.end(); ++iter){
ADDHEAD *paddhead = *iter;
if(paddhead){
if(paddhead->pregex){
regfree(paddhead->pregex);
delete paddhead->pregex;
}
delete paddhead;
}
}
addheadlist.clear();
addheadlist.clear();
}
bool AdditionalHeader::AddHeader(headers_t& meta, const char* path) const
{
if(!is_enable){
return true;
}
if(!path){
S3FS_PRN_WARN("path is NULL.");
return false;
}
size_t pathlength = strlen(path);
// loop
//
// [NOTE]
// Because to allow duplicate key, and then scanning the entire table.
//
for(addheadlist_t::const_iterator iter = addheadlist.begin(); iter != addheadlist.end(); ++iter){
const ADDHEAD *paddhead = *iter;
if(!paddhead){
continue;
if(!is_enable){
return true;
}
if(!path){
S3FS_PRN_WARN("path is nullptr.");
return false;
}
if(paddhead->pregex){
// regex
regmatch_t match; // not use
if(0 == regexec(paddhead->pregex, path, 1, &match, 0)){
// match -> adding header
meta[paddhead->headkey] = paddhead->headvalue;
}
}else{
// directly comparing
if(paddhead->basestring.length() < pathlength){
if(0 == paddhead->basestring.length() || 0 == strcmp(&path[pathlength - paddhead->basestring.length()], paddhead->basestring.c_str())){
// match -> adding header
meta[paddhead->headkey] = paddhead->headvalue;
size_t pathlength = strlen(path);
// loop
//
// [NOTE]
// Because to allow duplicate key, and then scanning the entire table.
//
for(addheadlist_t::const_iterator iter = addheadlist.begin(); iter != addheadlist.end(); ++iter){
const add_header *paddhead = &*iter;
if(paddhead->pregex){
// regex
regmatch_t match; // not use
if(0 == regexec(paddhead->pregex.get(), path, 1, &match, 0)){
// match -> adding header
meta[paddhead->headkey] = paddhead->headvalue;
}
}else{
// directly comparing
if(paddhead->basestring.length() < pathlength){
if(paddhead->basestring.empty() || paddhead->basestring == &path[pathlength - paddhead->basestring.length()]){
// match -> adding header
meta[paddhead->headkey] = paddhead->headvalue;
}
}
}
}
}
}
return true;
return true;
}
struct curl_slist* AdditionalHeader::AddHeader(struct curl_slist* list, const char* path) const
{
headers_t meta;
headers_t meta;
if(!AddHeader(meta, path)){
if(!AddHeader(meta, path)){
return list;
}
for(headers_t::iterator iter = meta.begin(); iter != meta.end(); ++iter){
// Adding header
list = curl_slist_sort_insert(list, iter->first.c_str(), iter->second.c_str());
}
meta.clear();
S3FS_MALLOCTRIM(0);
return list;
}
for(headers_t::iterator iter = meta.begin(); iter != meta.end(); ++iter){
// Adding header
list = curl_slist_sort_insert(list, iter->first.c_str(), iter->second.c_str());
}
meta.clear();
S3FS_MALLOCTRIM(0);
return list;
}
bool AdditionalHeader::Dump() const
{
if(!IS_S3FS_LOG_DBG()){
return true;
}
ostringstream ssdbg;
int cnt = 1;
ssdbg << "Additional Header list[" << addheadlist.size() << "] = {" << endl;
for(addheadlist_t::const_iterator iter = addheadlist.begin(); iter != addheadlist.end(); ++iter, ++cnt){
const ADDHEAD *paddhead = *iter;
ssdbg << " [" << cnt << "] = {" << endl;
if(paddhead){
if(paddhead->pregex){
ssdbg << " type\t\t--->\tregex" << endl;
}else{
ssdbg << " type\t\t--->\tsuffix matching" << endl;
}
ssdbg << " base string\t--->\t" << paddhead->basestring << endl;
ssdbg << " add header\t--->\t" << paddhead->headkey << ": " << paddhead->headvalue << endl;
if(!S3fsLog::IsS3fsLogDbg()){
return true;
}
std::ostringstream ssdbg;
int cnt = 1;
ssdbg << "Additional Header list[" << addheadlist.size() << "] = {" << std::endl;
for(addheadlist_t::const_iterator iter = addheadlist.begin(); iter != addheadlist.end(); ++iter, ++cnt){
const add_header *paddhead = &*iter;
ssdbg << " [" << cnt << "] = {" << std::endl;
if(paddhead->pregex){
ssdbg << " type\t\t--->\tregex" << std::endl;
}else{
ssdbg << " type\t\t--->\tsuffix matching" << std::endl;
}
ssdbg << " base std::string\t--->\t" << paddhead->basestring << std::endl;
ssdbg << " add header\t--->\t" << paddhead->headkey << ": " << paddhead->headvalue << std::endl;
ssdbg << " }" << std::endl;
}
ssdbg << " }" << endl;
}
ssdbg << "}" << endl;
ssdbg << "}" << std::endl;
// print all
S3FS_PRN_DBG("%s", ssdbg.str().c_str());
// print all
S3FS_PRN_DBG("%s", ssdbg.str().c_str());
return true;
return true;
}
/*
@ -282,6 +243,6 @@ bool AdditionalHeader::Dump() const
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

View File

@ -21,41 +21,69 @@
#ifndef S3FS_ADDHEAD_H_
#define S3FS_ADDHEAD_H_
#include <memory>
#include <regex.h>
#include <vector>
#include "metaheader.h"
//----------------------------------------------
// class AdditionalHeader
// Structure / Typedef
//----------------------------------------------
typedef struct add_header{
regex_t* pregex; // not NULL means using regex, NULL means comparing suffix directly.
std::string basestring;
std::string headkey;
std::string headvalue;
}ADDHEAD;
struct add_header{
add_header(std::unique_ptr<regex_t> pregex, std::string basestring, std::string headkey, std::string headvalue)
: pregex(std::move(pregex))
, basestring(std::move(basestring))
, headkey(std::move(headkey))
, headvalue(std::move(headvalue))
{}
~add_header() {
if(pregex){
regfree(pregex.get());
}
}
typedef std::vector<ADDHEAD *> addheadlist_t;
add_header(const add_header&) = delete;
add_header(add_header&& val) = default;
add_header& operator=(const add_header&) = delete;
add_header& operator=(add_header&&) = delete;
std::unique_ptr<regex_t> pregex; // not nullptr means using regex, nullptr means comparing suffix directly.
std::string basestring;
std::string headkey;
std::string headvalue;
};
typedef std::vector<add_header> addheadlist_t;
//----------------------------------------------
// Class AdditionalHeader
//----------------------------------------------
class AdditionalHeader
{
private:
static AdditionalHeader singleton;
bool is_enable;
addheadlist_t addheadlist;
private:
static AdditionalHeader singleton;
bool is_enable;
addheadlist_t addheadlist;
protected:
AdditionalHeader();
~AdditionalHeader();
protected:
AdditionalHeader();
~AdditionalHeader();
AdditionalHeader(const AdditionalHeader&) = delete;
AdditionalHeader(AdditionalHeader&&) = delete;
AdditionalHeader& operator=(const AdditionalHeader&) = delete;
AdditionalHeader& operator=(AdditionalHeader&&) = delete;
public:
// Reference singleton
static AdditionalHeader* get(void) { return &singleton; }
public:
// Reference singleton
static AdditionalHeader* get() { return &singleton; }
bool Load(const char* file);
void Unload(void);
bool Load(const char* file);
void Unload();
bool AddHeader(headers_t& meta, const char* path) const;
struct curl_slist* AddHeader(struct curl_slist* list, const char* path) const;
bool Dump(void) const;
bool AddHeader(headers_t& meta, const char* path) const;
struct curl_slist* AddHeader(struct curl_slist* list, const char* path) const;
bool Dump() const;
};
#endif // S3FS_ADDHEAD_H_
@ -65,6 +93,6 @@ class AdditionalHeader
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

78
src/autolock.cpp Normal file
View File

@ -0,0 +1,78 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cstdlib>
#include <cerrno>
#include "autolock.h"
#include "s3fs_logger.h"
//-------------------------------------------------------------------
// Class AutoLock
//-------------------------------------------------------------------
AutoLock::AutoLock(pthread_mutex_t* pmutex, Type type) : auto_mutex(pmutex)
{
if (type == ALREADY_LOCKED) {
is_lock_acquired = false;
} else if (type == NO_WAIT) {
int result = pthread_mutex_trylock(auto_mutex);
if(result == 0){
is_lock_acquired = true;
}else if(result == EBUSY){
is_lock_acquired = false;
}else{
S3FS_PRN_CRIT("pthread_mutex_trylock returned: %d", result);
abort();
}
} else {
int result = pthread_mutex_lock(auto_mutex);
if(result == 0){
is_lock_acquired = true;
}else{
S3FS_PRN_CRIT("pthread_mutex_lock returned: %d", result);
abort();
}
}
}
bool AutoLock::isLockAcquired() const
{
return is_lock_acquired;
}
AutoLock::~AutoLock()
{
if (is_lock_acquired) {
int result = pthread_mutex_unlock(auto_mutex);
if(result != 0){
S3FS_PRN_CRIT("pthread_mutex_unlock returned: %d", result);
abort();
}
}
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

63
src/autolock.h Normal file
View File

@ -0,0 +1,63 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_AUTOLOCK_H_
#define S3FS_AUTOLOCK_H_
#include <pthread.h>
//-------------------------------------------------------------------
// AutoLock Class
//-------------------------------------------------------------------
class AutoLock
{
public:
enum Type {
NO_WAIT = 1,
ALREADY_LOCKED = 2,
NONE = 0
};
private:
pthread_mutex_t* const auto_mutex;
bool is_lock_acquired;
private:
AutoLock(const AutoLock&) = delete;
AutoLock(AutoLock&&) = delete;
AutoLock& operator=(const AutoLock&) = delete;
AutoLock& operator=(AutoLock&&) = delete;
public:
explicit AutoLock(pthread_mutex_t* pmutex, Type type = NONE);
~AutoLock();
bool isLockAcquired() const;
};
#endif // S3FS_AUTOLOCK_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

File diff suppressed because it is too large Load Diff

View File

@ -21,49 +21,63 @@
#ifndef S3FS_CACHE_H_
#define S3FS_CACHE_H_
#include "common.h"
#include <cstring>
#include "autolock.h"
#include "metaheader.h"
//-------------------------------------------------------------------
// Structure
//-------------------------------------------------------------------
//
// Struct for stats cache
//
struct stat_cache_entry {
struct stat stbuf;
unsigned long hit_count;
struct timespec cache_date;
headers_t meta;
bool isforce;
bool noobjcache; // Flag: cache is no object for no listing.
unsigned long notruncate; // 0<: not remove automatically at checking truncate
struct stat stbuf;
unsigned long hit_count;
struct timespec cache_date;
headers_t meta;
bool isforce;
bool noobjcache; // Flag: cache is no object for no listing.
unsigned long notruncate; // 0<: not remove automatically at checking truncate
stat_cache_entry() : hit_count(0), isforce(false), noobjcache(false), notruncate(0L) {
memset(&stbuf, 0, sizeof(struct stat));
cache_date.tv_sec = 0;
cache_date.tv_nsec = 0;
meta.clear();
}
stat_cache_entry() : hit_count(0), isforce(false), noobjcache(false), notruncate(0L)
{
memset(&stbuf, 0, sizeof(struct stat));
cache_date.tv_sec = 0;
cache_date.tv_nsec = 0;
meta.clear();
}
};
typedef std::map<std::string, stat_cache_entry*> stat_cache_t; // key=path
typedef std::map<std::string, stat_cache_entry> stat_cache_t; // key=path
//
// Struct for symbolic link cache
//
struct symlink_cache_entry {
std::string link;
unsigned long hit_count;
struct timespec cache_date; // The function that operates timespec uses the same as Stats
std::string link;
unsigned long hit_count;
struct timespec cache_date; // The function that operates timespec uses the same as Stats
symlink_cache_entry() : link(""), hit_count(0) {
cache_date.tv_sec = 0;
cache_date.tv_nsec = 0;
}
symlink_cache_entry() : link(""), hit_count(0)
{
cache_date.tv_sec = 0;
cache_date.tv_nsec = 0;
}
};
typedef std::map<std::string, symlink_cache_entry*> symlink_cache_t;
typedef std::map<std::string, symlink_cache_entry> symlink_cache_t;
//
// Class
// Typedefs for No truncate file name cache
//
typedef std::vector<std::string> notruncate_filelist_t; // untruncated file name list in dir
typedef std::map<std::string, notruncate_filelist_t> notruncate_dir_map_t; // key is parent dir path
//-------------------------------------------------------------------
// Class StatCache
//-------------------------------------------------------------------
// [NOTE] About Symbolic link cache
// The Stats cache class now also has a symbolic link cache.
// It is possible to take out the Symbolic link cache in another class,
@ -75,94 +89,118 @@ typedef std::map<std::string, symlink_cache_entry*> symlink_cache_t;
//
class StatCache
{
private:
static StatCache singleton;
static pthread_mutex_t stat_cache_lock;
stat_cache_t stat_cache;
bool IsExpireTime;
bool IsExpireIntervalType; // if this flag is true, cache data is updated at last access time.
time_t ExpireTime;
unsigned long CacheSize;
bool IsCacheNoObject;
symlink_cache_t symlink_cache;
private:
static StatCache singleton;
static pthread_mutex_t stat_cache_lock;
stat_cache_t stat_cache;
bool IsExpireTime;
bool IsExpireIntervalType; // if this flag is true, cache data is updated at last access time.
time_t ExpireTime;
unsigned long CacheSize;
bool IsCacheNoObject;
symlink_cache_t symlink_cache;
notruncate_dir_map_t notruncate_file_cache;
private:
StatCache();
~StatCache();
private:
StatCache();
~StatCache();
void Clear(void);
bool GetStat(const std::string& key, struct stat* pst, headers_t* meta, bool overcheck, const char* petag, bool* pisforce);
// Truncate stat cache
bool TruncateCache(void);
// Truncate symbolic link cache
bool TruncateSymlink(void);
void Clear();
bool GetStat(const std::string& key, struct stat* pst, headers_t* meta, bool overcheck, const char* petag, bool* pisforce);
// Truncate stat cache
bool TruncateCache(AutoLock::Type locktype = AutoLock::NONE);
// Truncate symbolic link cache
bool TruncateSymlink(AutoLock::Type locktype = AutoLock::NONE);
public:
// Reference singleton
static StatCache* getStatCacheData(void) {
return &singleton;
}
bool AddNotruncateCache(const std::string& key);
bool DelNotruncateCache(const std::string& key);
// Attribute
unsigned long GetCacheSize(void) const;
unsigned long SetCacheSize(unsigned long size);
time_t GetExpireTime(void) const;
time_t SetExpireTime(time_t expire, bool is_interval = false);
time_t UnsetExpireTime(void);
bool SetCacheNoObject(bool flag);
bool EnableCacheNoObject(void) {
return SetCacheNoObject(true);
}
bool DisableCacheNoObject(void) {
return SetCacheNoObject(false);
}
bool GetCacheNoObject(void) const {
return IsCacheNoObject;
}
public:
// Reference singleton
static StatCache* getStatCacheData()
{
return &singleton;
}
// Get stat cache
bool GetStat(const std::string& key, struct stat* pst, headers_t* meta, bool overcheck = true, bool* pisforce = NULL) {
return GetStat(key, pst, meta, overcheck, NULL, pisforce);
}
bool GetStat(const std::string& key, struct stat* pst, bool overcheck = true) {
return GetStat(key, pst, NULL, overcheck, NULL, NULL);
}
bool GetStat(const std::string& key, headers_t* meta, bool overcheck = true) {
return GetStat(key, NULL, meta, overcheck, NULL, NULL);
}
bool HasStat(const std::string& key, bool overcheck = true) {
return GetStat(key, NULL, NULL, overcheck, NULL, NULL);
}
bool HasStat(const std::string& key, const char* etag, bool overcheck = true) {
return GetStat(key, NULL, NULL, overcheck, etag, NULL);
}
// Attribute
unsigned long GetCacheSize() const;
unsigned long SetCacheSize(unsigned long size);
time_t GetExpireTime() const;
time_t SetExpireTime(time_t expire, bool is_interval = false);
time_t UnsetExpireTime();
bool SetCacheNoObject(bool flag);
bool EnableCacheNoObject()
{
return SetCacheNoObject(true);
}
bool DisableCacheNoObject()
{
return SetCacheNoObject(false);
}
bool GetCacheNoObject() const
{
return IsCacheNoObject;
}
// Cache For no object
bool IsNoObjectCache(const std::string& key, bool overcheck = true);
bool AddNoObjectCache(const std::string& key);
// Get stat cache
bool GetStat(const std::string& key, struct stat* pst, headers_t* meta, bool overcheck = true, bool* pisforce = nullptr)
{
return GetStat(key, pst, meta, overcheck, nullptr, pisforce);
}
bool GetStat(const std::string& key, struct stat* pst, bool overcheck = true)
{
return GetStat(key, pst, nullptr, overcheck, nullptr, nullptr);
}
bool GetStat(const std::string& key, headers_t* meta, bool overcheck = true)
{
return GetStat(key, nullptr, meta, overcheck, nullptr, nullptr);
}
bool HasStat(const std::string& key, bool overcheck = true)
{
return GetStat(key, nullptr, nullptr, overcheck, nullptr, nullptr);
}
bool HasStat(const std::string& key, const char* etag, bool overcheck = true)
{
return GetStat(key, nullptr, nullptr, overcheck, etag, nullptr);
}
bool HasStat(const std::string& key, struct stat* pst, const char* etag)
{
return GetStat(key, pst, nullptr, true, etag, nullptr);
}
// Add stat cache
bool AddStat(const std::string& key, headers_t& meta, bool forcedir = false, bool no_truncate = false);
// Cache For no object
bool IsNoObjectCache(const std::string& key, bool overcheck = true);
bool AddNoObjectCache(const std::string& key);
// Change no truncate flag
void ChangeNoTruncateFlag(const std::string& key, bool no_truncate);
// Add stat cache
bool AddStat(const std::string& key, const headers_t& meta, bool forcedir = false, bool no_truncate = false);
// Delete stat cache
bool DelStat(const char* key, bool lock_already_held = false);
bool DelStat(std::string& key, bool lock_already_held = false) {
return DelStat(key.c_str(), lock_already_held);
}
// Update meta stats
bool UpdateMetaStats(const std::string& key, const headers_t& meta);
// Cache for symbolic link
bool GetSymlink(const std::string& key, std::string& value);
bool AddSymlink(const std::string& key, const std::string& value);
bool DelSymlink(const char* key, bool lock_already_held = false);
// Change no truncate flag
void ChangeNoTruncateFlag(const std::string& key, bool no_truncate);
// Delete stat cache
bool DelStat(const char* key, AutoLock::Type locktype = AutoLock::NONE);
bool DelStat(const std::string& key, AutoLock::Type locktype = AutoLock::NONE)
{
return DelStat(key.c_str(), locktype);
}
// Cache for symbolic link
bool GetSymlink(const std::string& key, std::string& value);
bool AddSymlink(const std::string& key, const std::string& value);
bool DelSymlink(const char* key, AutoLock::Type locktype = AutoLock::NONE);
// Cache for Notruncate file
bool GetNotruncateCache(const std::string& parentdir, notruncate_filelist_t& list);
};
//
//-------------------------------------------------------------------
// Functions
//
bool convert_header_to_stat(const char* path, headers_t& meta, struct stat* pst, bool forcedir = false);
//-------------------------------------------------------------------
bool convert_header_to_stat(const char* path, const headers_t& meta, struct stat* pst, bool forcedir = false);
#endif // S3FS_CACHE_H_
@ -171,6 +209,6 @@ bool convert_header_to_stat(const char* path, headers_t& meta, struct stat* pst,
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

View File

@ -21,184 +21,35 @@
#ifndef S3FS_COMMON_H_
#define S3FS_COMMON_H_
#include <stdlib.h>
#include <sys/types.h>
#include "../config.h"
#include "types.h"
//
// Extended attribute
//
#ifdef HAVE_SYS_EXTATTR_H
#include <sys/extattr.h>
#elif HAVE_ATTR_XATTR_H
#include <attr/xattr.h>
#elif HAVE_SYS_XATTR_H
#include <sys/xattr.h>
#endif
//
// Macro
//
static inline const char *SAFESTRPTR(const char *strptr) { return strptr ? strptr : ""; }
//
// Debug level
//
enum s3fs_log_level{
S3FS_LOG_CRIT = 0, // LOG_CRIT
S3FS_LOG_ERR = 1, // LOG_ERR
S3FS_LOG_WARN = 3, // LOG_WARNING
S3FS_LOG_INFO = 7, // LOG_INFO
S3FS_LOG_DBG = 15 // LOG_DEBUG
};
//
// Debug macros
//
#define IS_S3FS_LOG_CRIT() (S3FS_LOG_CRIT == debug_level)
#define IS_S3FS_LOG_ERR() (S3FS_LOG_ERR == (debug_level & S3FS_LOG_DBG))
#define IS_S3FS_LOG_WARN() (S3FS_LOG_WARN == (debug_level & S3FS_LOG_DBG))
#define IS_S3FS_LOG_INFO() (S3FS_LOG_INFO == (debug_level & S3FS_LOG_DBG))
#define IS_S3FS_LOG_DBG() (S3FS_LOG_DBG == (debug_level & S3FS_LOG_DBG))
#define S3FS_LOG_LEVEL_TO_SYSLOG(level) \
( S3FS_LOG_DBG == (level & S3FS_LOG_DBG) ? LOG_DEBUG : \
S3FS_LOG_INFO == (level & S3FS_LOG_DBG) ? LOG_INFO : \
S3FS_LOG_WARN == (level & S3FS_LOG_DBG) ? LOG_WARNING : \
S3FS_LOG_ERR == (level & S3FS_LOG_DBG) ? LOG_ERR : LOG_CRIT )
#define S3FS_LOG_LEVEL_STRING(level) \
( S3FS_LOG_DBG == (level & S3FS_LOG_DBG) ? "[DBG] " : \
S3FS_LOG_INFO == (level & S3FS_LOG_DBG) ? "[INF] " : \
S3FS_LOG_WARN == (level & S3FS_LOG_DBG) ? "[WAN] " : \
S3FS_LOG_ERR == (level & S3FS_LOG_DBG) ? "[ERR] " : "[CRT] " )
#define S3FS_LOG_NEST_MAX 4
#define S3FS_LOG_NEST(nest) (nest < S3FS_LOG_NEST_MAX ? s3fs_log_nest[nest] : s3fs_log_nest[S3FS_LOG_NEST_MAX - 1])
#define S3FS_LOW_LOGPRN(level, fmt, ...) \
do{ \
if(S3FS_LOG_CRIT == level || (S3FS_LOG_CRIT != debug_level && level == (debug_level & level))){ \
if(foreground){ \
fprintf(stdout, "%s%s:%s(%d): " fmt "%s\n", S3FS_LOG_LEVEL_STRING(level), __FILE__, __func__, __LINE__, __VA_ARGS__); \
}else{ \
syslog(S3FS_LOG_LEVEL_TO_SYSLOG(level), "%s%s:%s(%d): " fmt "%s", instance_name.c_str(), __FILE__, __func__, __LINE__, __VA_ARGS__); \
} \
} \
}while(0)
#define S3FS_LOW_LOGPRN2(level, nest, fmt, ...) \
do{ \
if(S3FS_LOG_CRIT == level || (S3FS_LOG_CRIT != debug_level && level == (debug_level & level))){ \
if(foreground){ \
fprintf(stdout, "%s%s%s:%s(%d): " fmt "%s\n", S3FS_LOG_LEVEL_STRING(level), S3FS_LOG_NEST(nest), __FILE__, __func__, __LINE__, __VA_ARGS__); \
}else{ \
syslog(S3FS_LOG_LEVEL_TO_SYSLOG(level), "%s%s" fmt "%s", instance_name.c_str(), S3FS_LOG_NEST(nest), __VA_ARGS__); \
} \
} \
}while(0)
#define S3FS_LOW_CURLDBG(fmt, ...) \
do{ \
if(foreground){ \
fprintf(stdout, "[CURL DBG] " fmt "%s\n", __VA_ARGS__); \
}else{ \
syslog(S3FS_LOG_LEVEL_TO_SYSLOG(S3FS_LOG_CRIT), "%s" fmt "%s", instance_name.c_str(), __VA_ARGS__); \
} \
}while(0)
#define S3FS_LOW_LOGPRN_EXIT(fmt, ...) \
do{ \
if(foreground){ \
fprintf(stderr, "s3fs: " fmt "%s\n", __VA_ARGS__); \
}else{ \
fprintf(stderr, "s3fs: " fmt "%s\n", __VA_ARGS__); \
syslog(S3FS_LOG_LEVEL_TO_SYSLOG(S3FS_LOG_CRIT), "%ss3fs: " fmt "%s", instance_name.c_str(), __VA_ARGS__); \
} \
}while(0)
// Special macro for init message
#define S3FS_PRN_INIT_INFO(fmt, ...) \
do{ \
if(foreground){ \
fprintf(stdout, "%s%s%s:%s(%d): " fmt "%s\n", S3FS_LOG_LEVEL_STRING(S3FS_LOG_INFO), S3FS_LOG_NEST(0), __FILE__, __func__, __LINE__, __VA_ARGS__, ""); \
}else{ \
syslog(S3FS_LOG_LEVEL_TO_SYSLOG(S3FS_LOG_INFO), "%s%s" fmt "%s", instance_name.c_str(), S3FS_LOG_NEST(0), __VA_ARGS__, ""); \
} \
}while(0)
// Special macro for checking cache files
#define S3FS_LOW_CACHE(fp, fmt, ...) \
do{ \
if(foreground){ \
fprintf(fp, fmt "%s\n", __VA_ARGS__); \
}else{ \
syslog(S3FS_LOG_LEVEL_TO_SYSLOG(S3FS_LOG_INFO), "%s: " fmt "%s", instance_name.c_str(), __VA_ARGS__); \
} \
}while(0)
// [NOTE]
// small trick for VA_ARGS
//
#define S3FS_PRN_EXIT(fmt, ...) S3FS_LOW_LOGPRN_EXIT(fmt, ##__VA_ARGS__, "")
#define S3FS_PRN_CRIT(fmt, ...) S3FS_LOW_LOGPRN(S3FS_LOG_CRIT, fmt, ##__VA_ARGS__, "")
#define S3FS_PRN_ERR(fmt, ...) S3FS_LOW_LOGPRN(S3FS_LOG_ERR, fmt, ##__VA_ARGS__, "")
#define S3FS_PRN_WARN(fmt, ...) S3FS_LOW_LOGPRN(S3FS_LOG_WARN, fmt, ##__VA_ARGS__, "")
#define S3FS_PRN_DBG(fmt, ...) S3FS_LOW_LOGPRN(S3FS_LOG_DBG, fmt, ##__VA_ARGS__, "")
#define S3FS_PRN_INFO(fmt, ...) S3FS_LOW_LOGPRN2(S3FS_LOG_INFO, 0, fmt, ##__VA_ARGS__, "")
#define S3FS_PRN_INFO0(fmt, ...) S3FS_LOG_INFO(fmt, __VA_ARGS__)
#define S3FS_PRN_INFO1(fmt, ...) S3FS_LOW_LOGPRN2(S3FS_LOG_INFO, 1, fmt, ##__VA_ARGS__, "")
#define S3FS_PRN_INFO2(fmt, ...) S3FS_LOW_LOGPRN2(S3FS_LOG_INFO, 2, fmt, ##__VA_ARGS__, "")
#define S3FS_PRN_INFO3(fmt, ...) S3FS_LOW_LOGPRN2(S3FS_LOG_INFO, 3, fmt, ##__VA_ARGS__, "")
#define S3FS_PRN_CURL(fmt, ...) S3FS_LOW_CURLDBG(fmt, ##__VA_ARGS__, "")
#define S3FS_PRN_CACHE(fp, ...) S3FS_LOW_CACHE(fp, ##__VA_ARGS__, "")
//
// Typedef
//
struct header_nocase_cmp : public std::binary_function<std::string, std::string, bool>{
bool operator()(const std::string &strleft, const std::string &strright) const
{
return (strcasecmp(strleft.c_str(), strright.c_str()) < 0);
}
};
typedef std::map<std::string, std::string, header_nocase_cmp> headers_t;
//
// Header "x-amz-meta-xattr" is for extended attributes.
// This header is url encoded string which is json formatted.
// x-amz-meta-xattr:urlencode({"xattr-1":"base64(value-1)","xattr-2":"base64(value-2)","xattr-3":"base64(value-3)"})
//
typedef struct xattr_value{
unsigned char* pvalue;
size_t length;
explicit xattr_value(unsigned char* pval = NULL, size_t len = 0) : pvalue(pval), length(len) {}
~xattr_value()
{
delete[] pvalue;
}
}XATTRVAL, *PXATTRVAL;
typedef std::map<std::string, PXATTRVAL> xattrs_t;
//
//-------------------------------------------------------------------
// Global variables
//
//-------------------------------------------------------------------
// TODO: namespace these
static constexpr int64_t FIVE_GB = 5LL * 1024LL * 1024LL * 1024LL;
static constexpr off_t MIN_MULTIPART_SIZE = 5 * 1024 * 1024;
extern bool foreground;
extern bool nomultipart;
extern bool pathrequeststyle;
extern bool complement_stat;
extern bool noxmlns;
extern std::string program_name;
extern std::string service_path;
extern std::string host;
extern std::string bucket;
extern std::string s3host;
extern std::string mount_prefix;
extern std::string endpoint;
extern std::string cipher_suites;
extern std::string instance_name;
extern s3fs_log_level debug_level;
extern const char* s3fs_log_nest[S3FS_LOG_NEST_MAX];
//-------------------------------------------------------------------
// For weak attribute
//-------------------------------------------------------------------
#define S3FS_FUNCATTR_WEAK __attribute__ ((weak,unused))
#endif // S3FS_COMMON_H_
@ -207,6 +58,6 @@ extern const char* s3fs_log_nest[S3FS_LOG_NEST_MAX];
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

View File

@ -18,72 +18,37 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include "s3fs_auth.h"
#include "string_util.h"
using namespace std;
//-------------------------------------------------------------------
// Utility Function
//-------------------------------------------------------------------
string s3fs_get_content_md5(int fd)
std::string s3fs_get_content_md5(int fd)
{
unsigned char* md5hex;
char* base64;
string Signature;
if(NULL == (md5hex = s3fs_md5hexsum(fd, 0, -1))){
return string("");
}
if(NULL == (base64 = s3fs_base64(md5hex, get_md5_digest_length()))){
return string(""); // ENOMEM
}
delete[] md5hex;
Signature = base64;
delete[] base64;
return Signature;
md5_t md5;
if(!s3fs_md5_fd(fd, 0, -1, &md5)){
// TODO: better return value?
return "";
}
return s3fs_base64(md5.data(), md5.size());
}
string s3fs_md5sum(int fd, off_t start, ssize_t size)
std::string s3fs_sha256_hex_fd(int fd, off_t start, off_t size)
{
size_t digestlen = get_md5_digest_length();
unsigned char* md5hex;
sha256_t sha256;
if(NULL == (md5hex = s3fs_md5hexsum(fd, start, size))){
return string("");
}
if(!s3fs_sha256_fd(fd, start, size, &sha256)){
// TODO: better return value?
return "";
}
std::string md5 = s3fs_hex(md5hex, digestlen);
delete[] md5hex;
std::string sha256hex = s3fs_hex_lower(sha256.data(), sha256.size());
return md5;
}
string s3fs_sha256sum(int fd, off_t start, ssize_t size)
{
size_t digestlen = get_sha256_digest_length();
char sha256[2 * digestlen + 1];
unsigned char* sha256hex;
if(NULL == (sha256hex = s3fs_sha256hexsum(fd, start, size))){
return string("");
}
memset(sha256, 0, 2 * digestlen + 1);
for(size_t pos = 0; pos < digestlen; pos++){
snprintf(sha256 + 2 * pos, 3, "%02x", sha256hex[pos]);
}
delete[] sha256hex;
return string(sha256);
return sha256hex;
}
/*
@ -91,6 +56,6 @@ string s3fs_sha256sum(int fd, off_t start, ssize_t size)
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

File diff suppressed because it is too large Load Diff

View File

@ -21,9 +21,14 @@
#ifndef S3FS_CURL_H_
#define S3FS_CURL_H_
#include <cassert>
#include <curl/curl.h>
#include <map>
#include <memory>
#include <vector>
#include "psemaphore.h"
#include "autolock.h"
#include "metaheader.h"
#include "fdcache_page.h"
//----------------------------------------------
// Avoid dependency on libcurl version
@ -34,573 +39,372 @@
// CURLOPT_SSL_ENABLE_ALPN 7.36.0 and later
// CURLOPT_KEEP_SENDING_ON_ERROR 7.51.0 and later
//
// s3fs uses these, if you build s3fs with the old libcurl,
// s3fs uses these, if you build s3fs with the old libcurl,
// substitute the following symbols to avoid errors.
// If the version of libcurl linked at runtime is old,
// curl_easy_setopt results in an error(CURLE_UNKNOWN_OPTION) and
// a message is output.
//
#if defined(HAVE_CURLOPT_TCP_KEEPALIVE) && (HAVE_CURLOPT_TCP_KEEPALIVE == 1)
#define S3FS_CURLOPT_TCP_KEEPALIVE CURLOPT_TCP_KEEPALIVE
#define S3FS_CURLOPT_TCP_KEEPALIVE CURLOPT_TCP_KEEPALIVE
#else
#define S3FS_CURLOPT_TCP_KEEPALIVE static_cast<CURLoption>(213)
#define S3FS_CURLOPT_TCP_KEEPALIVE static_cast<CURLoption>(213)
#endif
#if defined(HAVE_CURLOPT_SSL_ENABLE_ALPN) && (HAVE_CURLOPT_SSL_ENABLE_ALPN == 1)
#define S3FS_CURLOPT_SSL_ENABLE_ALPN CURLOPT_SSL_ENABLE_ALPN
#define S3FS_CURLOPT_SSL_ENABLE_ALPN CURLOPT_SSL_ENABLE_ALPN
#else
#define S3FS_CURLOPT_SSL_ENABLE_ALPN static_cast<CURLoption>(226)
#define S3FS_CURLOPT_SSL_ENABLE_ALPN static_cast<CURLoption>(226)
#endif
#if defined(HAVE_CURLOPT_KEEP_SENDING_ON_ERROR) && (HAVE_CURLOPT_KEEP_SENDING_ON_ERROR == 1)
#define S3FS_CURLOPT_KEEP_SENDING_ON_ERROR CURLOPT_KEEP_SENDING_ON_ERROR
#define S3FS_CURLOPT_KEEP_SENDING_ON_ERROR CURLOPT_KEEP_SENDING_ON_ERROR
#else
#define S3FS_CURLOPT_KEEP_SENDING_ON_ERROR static_cast<CURLoption>(245)
#define S3FS_CURLOPT_KEEP_SENDING_ON_ERROR static_cast<CURLoption>(245)
#endif
//----------------------------------------------
// Symbols
// Structure / Typedefs
//----------------------------------------------
static const int MIN_MULTIPART_SIZE = 5 * 1024 * 1024;
//----------------------------------------------
// class BodyData
//----------------------------------------------
// memory class for curl write memory callback
//
class BodyData
{
private:
char* text;
size_t lastpos;
size_t bufsize;
private:
bool IsSafeSize(size_t addbytes) const {
return ((lastpos + addbytes + 1) > bufsize ? false : true);
}
bool Resize(size_t addbytes);
public:
BodyData() : text(NULL), lastpos(0), bufsize(0) {}
~BodyData() {
Clear();
}
void Clear(void);
bool Append(void* ptr, size_t bytes);
bool Append(void* ptr, size_t blockSize, size_t numBlocks) {
return Append(ptr, (blockSize * numBlocks));
}
const char* str() const;
size_t size() const {
return lastpos;
}
};
//----------------------------------------------
// Utility structs & typedefs
//----------------------------------------------
typedef std::vector<std::string> etaglist_t;
// Each part information for Multipart upload
struct filepart
{
bool uploaded; // does finish uploading
std::string etag; // expected etag value
int fd; // base file(temporary full file) descriptor
off_t startpos; // seek fd point for uploading
off_t size; // uploading size
etaglist_t* etaglist; // use only parallel upload
int etagpos; // use only parallel upload
filepart() : uploaded(false), fd(-1), startpos(0), size(-1), etaglist(NULL), etagpos(-1) {}
~filepart()
{
clear();
}
void clear(void)
{
uploaded = false;
etag = "";
fd = -1;
startpos = 0;
size = -1;
etaglist = NULL;
etagpos = - 1;
}
void add_etag_list(etaglist_t* list)
{
if(list){
list->push_back(std::string(""));
etaglist = list;
etagpos = list->size() - 1;
}else{
etaglist = NULL;
etagpos = - 1;
}
}
};
// for progress
struct case_insensitive_compare_func
{
bool operator()(const std::string& a, const std::string& b) const {
return strcasecmp(a.c_str(), b.c_str()) < 0;
}
};
typedef std::map<std::string, std::string, case_insensitive_compare_func> mimes_t;
typedef std::pair<double, double> progress_t;
typedef std::map<CURL*, time_t> curltime_t;
typedef std::map<CURL*, progress_t> curlprogress_t;
class S3fsMultiCurl;
//----------------------------------------------
// class CurlHandlerPool
//----------------------------------------------
typedef std::list<CURL*> hcurllist_t;
class CurlHandlerPool
{
public:
explicit CurlHandlerPool(int maxHandlers) : mMaxHandlers(maxHandlers)
{
assert(maxHandlers > 0);
}
bool Init();
bool Destroy();
CURL* GetHandler(bool only_pool);
void ReturnHandler(CURL* hCurl, bool restore_pool);
private:
int mMaxHandlers;
pthread_mutex_t mLock;
hcurllist_t mPool;
};
//----------------------------------------------
// class S3fsCurl
//----------------------------------------------
#include "fdcache.h" // for fdpage_list_t
class CurlHandlerPool;
class S3fsCred;
class S3fsCurl;
class Semaphore;
// Prototype function for lazy setup options for curl handle
typedef bool (*s3fscurl_lazy_setup)(S3fsCurl* s3fscurl);
typedef std::map<std::string, std::string> iamcredmap_t;
typedef std::map<std::string, std::string> sseckeymap_t;
typedef std::list<sseckeymap_t> sseckeylist_t;
// storage class(rrs)
enum storage_class_t {
STANDARD,
STANDARD_IA,
ONEZONE_IA,
REDUCED_REDUNDANCY,
INTELLIGENT_TIERING,
GLACIER
};
enum acl_t {
PRIVATE,
PUBLIC_READ,
PUBLIC_READ_WRITE,
AWS_EXEC_READ,
AUTHENTICATED_READ,
BUCKET_OWNER_READ,
BUCKET_OWNER_FULL_CONTROL,
LOG_DELIVERY_WRITE,
INVALID_ACL
};
// sse type
enum sse_type_t {
SSE_DISABLE = 0, // not use server side encrypting
SSE_S3, // server side encrypting by S3 key
SSE_C, // server side encrypting by custom key
SSE_KMS // server side encrypting by kms id
};
// share
enum {
SHARE_MUTEX_DNS = 0,
SHARE_MUTEX_SSL_SESSION = 1,
SHARE_MUTEX_MAX = 2,
};
typedef std::vector<sseckeymap_t> sseckeylist_t;
// Class for lapping curl
//
class S3fsCurl
{
friend class S3fsMultiCurl;
friend class S3fsMultiCurl;
private:
enum REQTYPE {
REQTYPE_UNSET = -1,
REQTYPE_DELETE = 0,
REQTYPE_HEAD,
REQTYPE_PUTHEAD,
REQTYPE_PUT,
REQTYPE_GET,
REQTYPE_CHKBUCKET,
REQTYPE_LISTBUCKET,
REQTYPE_PREMULTIPOST,
REQTYPE_COMPLETEMULTIPOST,
REQTYPE_UPLOADMULTIPOST,
REQTYPE_COPYMULTIPOST,
REQTYPE_MULTILIST,
REQTYPE_IAMCRED,
REQTYPE_ABORTMULTIUPLOAD,
REQTYPE_IAMROLE
};
private:
enum class REQTYPE {
UNSET = -1,
DELETE = 0,
HEAD,
PUTHEAD,
PUT,
GET,
CHKBUCKET,
LISTBUCKET,
PREMULTIPOST,
COMPLETEMULTIPOST,
UPLOADMULTIPOST,
COPYMULTIPOST,
MULTILIST,
IAMCRED,
ABORTMULTIUPLOAD,
IAMROLE
};
// class variables
static pthread_mutex_t curl_handles_lock;
static pthread_mutex_t curl_share_lock[SHARE_MUTEX_MAX];
static bool is_initglobal_done;
static CurlHandlerPool* sCurlPool;
static int sCurlPoolSize;
static CURLSH* hCurlShare;
static bool is_cert_check;
static bool is_dns_cache;
static bool is_ssl_session_cache;
static long connect_timeout;
static time_t readwrite_timeout;
static int retries;
static bool is_public_bucket;
static acl_t default_acl;
static storage_class_t storage_class;
static sseckeylist_t sseckeys;
static std::string ssekmsid;
static sse_type_t ssetype;
static bool is_content_md5;
static bool is_verbose;
static bool is_dump_body;
static std::string AWSAccessKeyId;
static std::string AWSSecretAccessKey;
static std::string AWSAccessToken;
static time_t AWSAccessTokenExpire;
static bool is_ecs;
static bool is_use_session_token;
static bool is_ibm_iam_auth;
static std::string IAM_cred_url;
static size_t IAM_field_count;
static std::string IAM_token_field;
static std::string IAM_expiry_field;
static std::string IAM_role;
static long ssl_verify_hostname;
static curltime_t curl_times;
static curlprogress_t curl_progress;
static std::string curl_ca_bundle;
static mimes_t mimeTypes;
static std::string userAgent;
static int max_parallel_cnt;
static int max_multireq;
static off_t multipart_size;
static bool is_sigv4;
static bool is_ua; // User-Agent
static bool requester_pays;
// Environment name
static constexpr char S3FS_SSL_PRIVKEY_PASSWORD[] = "S3FS_SSL_PRIVKEY_PASSWORD";
// variables
CURL* hCurl;
REQTYPE type; // type of request
std::string path; // target object path
std::string base_path; // base path (for multi curl head request)
std::string saved_path; // saved path = cache key (for multi curl head request)
std::string url; // target object path(url)
struct curl_slist* requestHeaders;
headers_t responseHeaders; // header data by HeaderCallback
BodyData bodydata; // body data by WriteMemoryCallback
BodyData headdata; // header data by WriteMemoryCallback
volatile long LastResponseCode;
const unsigned char* postdata; // use by post method and read callback function.
int postdata_remaining; // use by post method and read callback function.
filepart partdata; // use by multipart upload/get object callback
bool is_use_ahbe; // additional header by extension
int retry_count; // retry count for multipart
FILE* b_infile; // backup for retrying
const unsigned char* b_postdata; // backup for retrying
int b_postdata_remaining; // backup for retrying
off_t b_partdata_startpos; // backup for retrying
ssize_t b_partdata_size; // backup for retrying
int b_ssekey_pos; // backup for retrying
std::string b_ssevalue; // backup for retrying
sse_type_t b_ssetype; // backup for retrying
std::string b_from; // backup for retrying(for copy request)
headers_t b_meta; // backup for retrying(for copy request)
std::string op; // the HTTP verb of the request ("PUT", "GET", etc.)
std::string query_string; // request query string
Semaphore *sem;
pthread_mutex_t *completed_tids_lock;
std::vector<pthread_t> *completed_tids;
s3fscurl_lazy_setup fpLazySetup; // curl options for lazy setting function
// class variables
static pthread_mutex_t curl_warnings_lock;
static bool curl_warnings_once; // emit older curl warnings only once
static pthread_mutex_t curl_handles_lock;
static struct callback_locks_t {
pthread_mutex_t dns;
pthread_mutex_t ssl_session;
} callback_locks;
static bool is_initglobal_done;
static CurlHandlerPool* sCurlPool;
static int sCurlPoolSize;
static CURLSH* hCurlShare;
static bool is_cert_check;
static bool is_dns_cache;
static bool is_ssl_session_cache;
static long connect_timeout;
static time_t readwrite_timeout;
static int retries;
static bool is_public_bucket;
static acl_t default_acl;
static std::string storage_class;
static sseckeylist_t sseckeys;
static std::string ssekmsid;
static sse_type_t ssetype;
static bool is_content_md5;
static bool is_verbose;
static bool is_dump_body;
static S3fsCred* ps3fscred;
static long ssl_verify_hostname;
static std::string client_cert;
static std::string client_cert_type;
static std::string client_priv_key;
static std::string client_priv_key_type;
static std::string client_key_password;
static curltime_t curl_times;
static curlprogress_t curl_progress;
static std::string curl_ca_bundle;
static mimes_t mimeTypes;
static std::string userAgent;
static int max_parallel_cnt;
static int max_multireq;
static off_t multipart_size;
static off_t multipart_copy_size;
static signature_type_t signature_type;
static bool is_unsigned_payload;
static bool is_ua; // User-Agent
static bool listobjectsv2;
static bool requester_pays;
static std::string proxy_url;
static bool proxy_http;
static std::string proxy_userpwd; // load from file(<username>:<passphrase>)
static long ipresolve_type; // this value is a libcurl symbol.
public:
// constructor/destructor
explicit S3fsCurl(bool ahbe = false);
~S3fsCurl();
// variables
CURL* hCurl;
REQTYPE type; // type of request
std::string path; // target object path
std::string base_path; // base path (for multi curl head request)
std::string saved_path; // saved path = cache key (for multi curl head request)
std::string url; // target object path(url)
struct curl_slist* requestHeaders;
headers_t responseHeaders; // header data by HeaderCallback
std::string bodydata; // body data by WriteMemoryCallback
std::string headdata; // header data by WriteMemoryCallback
long LastResponseCode;
const unsigned char* postdata; // use by post method and read callback function.
off_t postdata_remaining; // use by post method and read callback function.
filepart partdata; // use by multipart upload/get object callback
bool is_use_ahbe; // additional header by extension
int retry_count; // retry count for multipart
FILE* b_infile; // backup for retrying
const unsigned char* b_postdata; // backup for retrying
off_t b_postdata_remaining; // backup for retrying
off_t b_partdata_startpos; // backup for retrying
off_t b_partdata_size; // backup for retrying
size_t b_ssekey_pos; // backup for retrying
std::string b_ssevalue; // backup for retrying
sse_type_t b_ssetype; // backup for retrying
std::string b_from; // backup for retrying(for copy request)
headers_t b_meta; // backup for retrying(for copy request)
std::string op; // the HTTP verb of the request ("PUT", "GET", etc.)
std::string query_string; // request query string
Semaphore *sem;
pthread_mutex_t *completed_tids_lock;
std::vector<pthread_t> *completed_tids;
s3fscurl_lazy_setup fpLazySetup; // curl options for lazy setting function
CURLcode curlCode; // handle curl return
private:
// class methods
static bool InitGlobalCurl(void);
static bool DestroyGlobalCurl(void);
static bool InitShareCurl(void);
static bool DestroyShareCurl(void);
static void LockCurlShare(CURL* handle, curl_lock_data nLockData, curl_lock_access laccess, void* useptr);
static void UnlockCurlShare(CURL* handle, curl_lock_data nLockData, void* useptr);
static bool InitCryptMutex(void);
static bool DestroyCryptMutex(void);
static int CurlProgress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow);
public:
static constexpr long S3FSCURL_RESPONSECODE_NOTSET = -1;
static constexpr long S3FSCURL_RESPONSECODE_FATAL_ERROR = -2;
static constexpr int S3FSCURL_PERFORM_RESULT_NOTSET = 1;
static bool LocateBundle(void);
static size_t HeaderCallback(void *data, size_t blockSize, size_t numBlocks, void *userPtr);
static size_t WriteMemoryCallback(void *ptr, size_t blockSize, size_t numBlocks, void *data);
static size_t ReadCallback(void *ptr, size_t size, size_t nmemb, void *userp);
static size_t UploadReadCallback(void *ptr, size_t size, size_t nmemb, void *userp);
static size_t DownloadWriteCallback(void* ptr, size_t size, size_t nmemb, void* userp);
public:
// constructor/destructor
explicit S3fsCurl(bool ahbe = false);
~S3fsCurl();
S3fsCurl(const S3fsCurl&) = delete;
S3fsCurl(S3fsCurl&&) = delete;
S3fsCurl& operator=(const S3fsCurl&) = delete;
S3fsCurl& operator=(S3fsCurl&&) = delete;
static bool UploadMultipartPostCallback(S3fsCurl* s3fscurl);
static bool CopyMultipartPostCallback(S3fsCurl* s3fscurl);
static bool MixMultipartPostCallback(S3fsCurl* s3fscurl);
static S3fsCurl* UploadMultipartPostRetryCallback(S3fsCurl* s3fscurl);
static S3fsCurl* CopyMultipartPostRetryCallback(S3fsCurl* s3fscurl);
static S3fsCurl* MixMultipartPostRetryCallback(S3fsCurl* s3fscurl);
static S3fsCurl* ParallelGetObjectRetryCallback(S3fsCurl* s3fscurl);
private:
// class methods
static bool InitGlobalCurl();
static bool DestroyGlobalCurl();
static bool InitShareCurl();
static bool DestroyShareCurl();
static void LockCurlShare(CURL* handle, curl_lock_data nLockData, curl_lock_access laccess, void* useptr);
static void UnlockCurlShare(CURL* handle, curl_lock_data nLockData, void* useptr);
static bool InitCryptMutex();
static bool DestroyCryptMutex();
static int CurlProgress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow);
// lazy functions for set curl options
static bool UploadMultipartPostSetCurlOpts(S3fsCurl* s3fscurl);
static bool CopyMultipartPostSetCurlOpts(S3fsCurl* s3fscurl);
static bool PreGetObjectRequestSetCurlOpts(S3fsCurl* s3fscurl);
static bool PreHeadRequestSetCurlOpts(S3fsCurl* s3fscurl);
static bool LocateBundle();
static size_t HeaderCallback(void *data, size_t blockSize, size_t numBlocks, void *userPtr);
static size_t WriteMemoryCallback(void *ptr, size_t blockSize, size_t numBlocks, void *data);
static size_t ReadCallback(void *ptr, size_t size, size_t nmemb, void *userp);
static size_t UploadReadCallback(void *ptr, size_t size, size_t nmemb, void *userp);
static size_t DownloadWriteCallback(void* ptr, size_t size, size_t nmemb, void* userp);
static bool ParseIAMCredentialResponse(const char* response, iamcredmap_t& keyval);
static bool SetIAMCredentials(const char* response);
static bool ParseIAMRoleFromMetaDataResponse(const char* response, std::string& rolename);
static bool SetIAMRoleFromMetaData(const char* response);
static bool LoadEnvSseCKeys(void);
static bool LoadEnvSseKmsid(void);
static bool PushbackSseKeys(std::string& onekey);
static bool AddUserAgent(CURL* hCurl);
static bool UploadMultipartPostCallback(S3fsCurl* s3fscurl, void* param);
static bool CopyMultipartPostCallback(S3fsCurl* s3fscurl, void* param);
static bool MixMultipartPostCallback(S3fsCurl* s3fscurl, void* param);
static std::unique_ptr<S3fsCurl> UploadMultipartPostRetryCallback(S3fsCurl* s3fscurl);
static std::unique_ptr<S3fsCurl> CopyMultipartPostRetryCallback(S3fsCurl* s3fscurl);
static std::unique_ptr<S3fsCurl> MixMultipartPostRetryCallback(S3fsCurl* s3fscurl);
static std::unique_ptr<S3fsCurl> ParallelGetObjectRetryCallback(S3fsCurl* s3fscurl);
static int CurlDebugFunc(CURL* hcurl, curl_infotype type, char* data, size_t size, void* userptr);
static int CurlDebugBodyInFunc(CURL* hcurl, curl_infotype type, char* data, size_t size, void* userptr);
static int CurlDebugBodyOutFunc(CURL* hcurl, curl_infotype type, char* data, size_t size, void* userptr);
static int RawCurlDebugFunc(CURL* hcurl, curl_infotype type, char* data, size_t size, void* userptr, curl_infotype datatype);
// lazy functions for set curl options
static bool CopyMultipartPostSetCurlOpts(S3fsCurl* s3fscurl);
static bool PreGetObjectRequestSetCurlOpts(S3fsCurl* s3fscurl);
static bool PreHeadRequestSetCurlOpts(S3fsCurl* s3fscurl);
// methods
bool ResetHandle(void);
bool RemakeHandle(void);
bool ClearInternalData(void);
void insertV4Headers();
void insertV2Headers();
void insertIBMIAMHeaders();
void insertAuthHeaders();
std::string CalcSignatureV2(const std::string& method, const std::string& strMD5, const std::string& content_type, const std::string& date, const std::string& resource);
std::string CalcSignature(const std::string& method, const std::string& canonical_uri, const std::string& query_string, const std::string& strdate, const std::string& payload_hash, const std::string& date8601);
int GetIAMCredentials(void);
static bool LoadEnvSseCKeys();
static bool LoadEnvSseKmsid();
static bool PushbackSseKeys(const std::string& onekey);
static bool AddUserAgent(CURL* hCurl);
int UploadMultipartPostSetup(const char* tpath, int part_num, const std::string& upload_id);
int CopyMultipartPostSetup(const char* from, const char* to, int part_num, const std::string& upload_id, headers_t& meta);
bool UploadMultipartPostComplete();
bool CopyMultipartPostComplete();
bool MixMultipartPostComplete();
static int CurlDebugFunc(const CURL* hcurl, curl_infotype type, char* data, size_t size, void* userptr);
static int CurlDebugBodyInFunc(const CURL* hcurl, curl_infotype type, char* data, size_t size, void* userptr);
static int CurlDebugBodyOutFunc(const CURL* hcurl, curl_infotype type, char* data, size_t size, void* userptr);
static int RawCurlDebugFunc(const CURL* hcurl, curl_infotype type, char* data, size_t size, void* userptr, curl_infotype datatype);
public:
// class methods
static bool InitS3fsCurl(void);
static bool InitMimeType(const std::string& strFile);
static bool DestroyS3fsCurl(void);
static int ParallelMultipartUploadRequest(const char* tpath, headers_t& meta, int fd);
static int ParallelMixMultipartUploadRequest(const char* tpath, headers_t& meta, int fd, const fdpage_list_t& mixuppages);
static int ParallelGetObjectRequest(const char* tpath, int fd, off_t start, ssize_t size);
static bool CheckIAMCredentialUpdate(void);
// methods
bool ResetHandle(AutoLock::Type locktype = AutoLock::NONE);
bool RemakeHandle();
bool ClearInternalData();
void insertV4Headers(const std::string& access_key_id, const std::string& secret_access_key, const std::string& access_token);
void insertV2Headers(const std::string& access_key_id, const std::string& secret_access_key, const std::string& access_token);
void insertIBMIAMHeaders(const std::string& access_key_id, const std::string& access_token);
void insertAuthHeaders();
bool AddSseRequestHead(sse_type_t ssetype, const std::string& ssevalue, bool is_copy);
std::string CalcSignatureV2(const std::string& method, const std::string& strMD5, const std::string& content_type, const std::string& date, const std::string& resource, const std::string& secret_access_key, const std::string& access_token);
std::string CalcSignature(const std::string& method, const std::string& canonical_uri, const std::string& query_string, const std::string& strdate, const std::string& payload_hash, const std::string& date8601, const std::string& secret_access_key, const std::string& access_token);
int UploadMultipartPostSetup(const char* tpath, int part_num, const std::string& upload_id);
int CopyMultipartPostSetup(const char* from, const char* to, int part_num, const std::string& upload_id, headers_t& meta);
bool UploadMultipartPostComplete();
bool CopyMultipartPostComplete();
int MapPutErrorResponse(int result);
// class methods(variables)
static std::string LookupMimeType(const std::string& name);
static bool SetCheckCertificate(bool isCertCheck);
static bool SetDnsCache(bool isCache);
static bool SetSslSessionCache(bool isCache);
static long SetConnectTimeout(long timeout);
static time_t SetReadwriteTimeout(time_t timeout);
static time_t GetReadwriteTimeout(void) { return S3fsCurl::readwrite_timeout; }
static int SetRetries(int count);
static bool SetPublicBucket(bool flag);
static bool IsPublicBucket(void) { return S3fsCurl::is_public_bucket; }
static acl_t SetDefaultAcl(acl_t acl);
static acl_t GetDefaultAcl();
static storage_class_t SetStorageClass(storage_class_t storage_class);
static storage_class_t GetStorageClass() { return S3fsCurl::storage_class; }
static bool LoadEnvSse(void) { return (S3fsCurl::LoadEnvSseCKeys() && S3fsCurl::LoadEnvSseKmsid()); }
static sse_type_t SetSseType(sse_type_t type);
static sse_type_t GetSseType(void) { return S3fsCurl::ssetype; }
static bool IsSseDisable(void) { return (SSE_DISABLE == S3fsCurl::ssetype); }
static bool IsSseS3Type(void) { return (SSE_S3 == S3fsCurl::ssetype); }
static bool IsSseCType(void) { return (SSE_C == S3fsCurl::ssetype); }
static bool IsSseKmsType(void) { return (SSE_KMS == S3fsCurl::ssetype); }
static bool FinalCheckSse(void);
static bool SetSseCKeys(const char* filepath);
static bool SetSseKmsid(const char* kmsid);
static bool IsSetSseKmsId(void) { return !S3fsCurl::ssekmsid.empty(); }
static const char* GetSseKmsId(void) { return S3fsCurl::ssekmsid.c_str(); }
static bool GetSseKey(std::string& md5, std::string& ssekey);
static bool GetSseKeyMd5(int pos, std::string& md5);
static int GetSseKeyCount(void);
static bool SetContentMd5(bool flag);
static bool SetVerbose(bool flag);
static bool GetVerbose(void) { return S3fsCurl::is_verbose; }
static bool SetDumpBody(bool flag);
static bool IsDumpBody(void) { return S3fsCurl::is_dump_body; }
static bool SetAccessKey(const char* AccessKeyId, const char* SecretAccessKey);
static bool SetAccessKeyWithSessionToken(const char* AccessKeyId, const char* SecretAccessKey, const char * SessionToken);
static bool IsSetAccessKeyID(void){
return (0 < S3fsCurl::AWSAccessKeyId.size());
}
static bool IsSetAccessKeys(void){
return (0 < S3fsCurl::IAM_role.size() || ((0 < S3fsCurl::AWSAccessKeyId.size() || S3fsCurl::is_ibm_iam_auth) && 0 < S3fsCurl::AWSSecretAccessKey.size()));
}
static long SetSslVerifyHostname(long value);
static long GetSslVerifyHostname(void) { return S3fsCurl::ssl_verify_hostname; }
// maximum parallel GET and PUT requests
static int SetMaxParallelCount(int value);
static int GetMaxParallelCount(void) { return S3fsCurl::max_parallel_cnt; }
// maximum parallel HEAD requests
static int SetMaxMultiRequest(int max);
static int GetMaxMultiRequest(void) { return S3fsCurl::max_multireq; }
static bool SetIsECS(bool flag);
static bool SetIsIBMIAMAuth(bool flag);
static size_t SetIAMFieldCount(size_t field_count);
static std::string SetIAMCredentialsURL(const char* url);
static std::string SetIAMTokenField(const char* token_field);
static std::string SetIAMExpiryField(const char* expiry_field);
static std::string SetIAMRole(const char* role);
static const char* GetIAMRole(void) { return S3fsCurl::IAM_role.c_str(); }
static bool SetMultipartSize(off_t size);
static off_t GetMultipartSize(void) { return S3fsCurl::multipart_size; }
static bool SetSignatureV4(bool isset) { bool bresult = S3fsCurl::is_sigv4; S3fsCurl::is_sigv4 = isset; return bresult; }
static bool IsSignatureV4(void) { return S3fsCurl::is_sigv4; }
static bool SetUserAgentFlag(bool isset) { bool bresult = S3fsCurl::is_ua; S3fsCurl::is_ua = isset; return bresult; }
static bool IsUserAgentFlag(void) { return S3fsCurl::is_ua; }
static void InitUserAgent(void);
static bool SetRequesterPays(bool flag) { bool old_flag = S3fsCurl::requester_pays; S3fsCurl::requester_pays = flag; return old_flag; }
static bool IsRequesterPays(void) { return S3fsCurl::requester_pays; }
public:
// class methods
static bool InitS3fsCurl();
static bool InitCredentialObject(S3fsCred* pcredobj);
static bool InitMimeType(const std::string& strFile);
static bool DestroyS3fsCurl();
static std::unique_ptr<S3fsCurl> CreateParallelS3fsCurl(const char* tpath, int fd, off_t start, off_t size, int part_num, bool is_copy, etagpair* petag, const std::string& upload_id, int& result);
static int ParallelMultipartUploadRequest(const char* tpath, headers_t& meta, int fd);
static int ParallelMixMultipartUploadRequest(const char* tpath, headers_t& meta, int fd, const fdpage_list_t& mixuppages);
static int ParallelGetObjectRequest(const char* tpath, int fd, off_t start, off_t size);
// methods
bool CreateCurlHandle(bool only_pool = false, bool remake = false);
bool DestroyCurlHandle(bool restore_pool = true, bool clear_internal_data = true);
// lazy functions for set curl options(public)
static bool UploadMultipartPostSetCurlOpts(S3fsCurl* s3fscurl);
bool LoadIAMRoleFromMetaData(void);
bool AddSseRequestHead(sse_type_t ssetype, std::string& ssevalue, bool is_only_c, bool is_copy);
bool GetResponseCode(long& responseCode, bool from_curl_handle = true);
int RequestPerform(bool dontAddAuthHeaders=false);
int DeleteRequest(const char* tpath);
bool PreHeadRequest(const char* tpath, const char* bpath = NULL, const char* savedpath = NULL, int ssekey_pos = -1);
bool PreHeadRequest(std::string& tpath, std::string& bpath, std::string& savedpath, int ssekey_pos = -1) {
return PreHeadRequest(tpath.c_str(), bpath.c_str(), savedpath.c_str(), ssekey_pos);
}
int HeadRequest(const char* tpath, headers_t& meta);
int PutHeadRequest(const char* tpath, headers_t& meta, bool is_copy);
int PutRequest(const char* tpath, headers_t& meta, int fd);
int PreGetObjectRequest(const char* tpath, int fd, off_t start, ssize_t size, sse_type_t ssetype, std::string& ssevalue);
int GetObjectRequest(const char* tpath, int fd, off_t start = -1, ssize_t size = -1);
int CheckBucket(void);
int ListBucketRequest(const char* tpath, const char* query);
int PreMultipartPostRequest(const char* tpath, headers_t& meta, std::string& upload_id, bool is_copy);
int CompleteMultipartPostRequest(const char* tpath, const std::string& upload_id, etaglist_t& parts);
int UploadMultipartPostRequest(const char* tpath, int part_num, const std::string& upload_id);
int MultipartListRequest(std::string& body);
int AbortMultipartUpload(const char* tpath, const std::string& upload_id);
int MultipartHeadRequest(const char* tpath, off_t size, headers_t& meta, bool is_copy);
int MultipartUploadRequest(const char* tpath, headers_t& meta, int fd, bool is_copy);
int MultipartUploadRequest(const std::string& upload_id, const char* tpath, int fd, off_t offset, off_t size, etaglist_t& list);
int MultipartRenameRequest(const char* from, const char* to, headers_t& meta, off_t size);
// class methods(variables)
static std::string LookupMimeType(const std::string& name);
static bool SetCheckCertificate(bool isCertCheck);
static bool SetDnsCache(bool isCache);
static bool SetSslSessionCache(bool isCache);
static long SetConnectTimeout(long timeout);
static time_t SetReadwriteTimeout(time_t timeout);
static time_t GetReadwriteTimeout() { return S3fsCurl::readwrite_timeout; }
static int SetRetries(int count);
static bool SetPublicBucket(bool flag);
static bool IsPublicBucket() { return S3fsCurl::is_public_bucket; }
static acl_t SetDefaultAcl(acl_t acl);
static acl_t GetDefaultAcl();
static std::string SetStorageClass(const std::string& storage_class);
static std::string GetStorageClass() { return S3fsCurl::storage_class; }
static bool LoadEnvSse() { return (S3fsCurl::LoadEnvSseCKeys() && S3fsCurl::LoadEnvSseKmsid()); }
static sse_type_t SetSseType(sse_type_t type);
static sse_type_t GetSseType() { return S3fsCurl::ssetype; }
static bool IsSseDisable() { return (sse_type_t::SSE_DISABLE == S3fsCurl::ssetype); }
static bool IsSseS3Type() { return (sse_type_t::SSE_S3 == S3fsCurl::ssetype); }
static bool IsSseCType() { return (sse_type_t::SSE_C == S3fsCurl::ssetype); }
static bool IsSseKmsType() { return (sse_type_t::SSE_KMS == S3fsCurl::ssetype); }
static bool FinalCheckSse();
static bool SetSseCKeys(const char* filepath);
static bool SetSseKmsid(const char* kmsid);
static bool IsSetSseKmsId() { return !S3fsCurl::ssekmsid.empty(); }
static const char* GetSseKmsId() { return S3fsCurl::ssekmsid.c_str(); }
static bool GetSseKey(std::string& md5, std::string& ssekey);
static bool GetSseKeyMd5(size_t pos, std::string& md5);
static size_t GetSseKeyCount();
static bool SetContentMd5(bool flag);
static bool SetVerbose(bool flag);
static bool GetVerbose() { return S3fsCurl::is_verbose; }
static bool SetDumpBody(bool flag);
static bool IsDumpBody() { return S3fsCurl::is_dump_body; }
static long SetSslVerifyHostname(long value);
static long GetSslVerifyHostname() { return S3fsCurl::ssl_verify_hostname; }
static bool SetSSLClientCertOptions(const std::string& values);
static void ResetOffset(S3fsCurl* pCurl);
// maximum parallel GET and PUT requests
static int SetMaxParallelCount(int value);
static int GetMaxParallelCount() { return S3fsCurl::max_parallel_cnt; }
// maximum parallel HEAD requests
static int SetMaxMultiRequest(int max);
static int GetMaxMultiRequest() { return S3fsCurl::max_multireq; }
static bool SetMultipartSize(off_t size);
static off_t GetMultipartSize() { return S3fsCurl::multipart_size; }
static bool SetMultipartCopySize(off_t size);
static off_t GetMultipartCopySize() { return S3fsCurl::multipart_copy_size; }
static signature_type_t SetSignatureType(signature_type_t signature_type) { signature_type_t bresult = S3fsCurl::signature_type; S3fsCurl::signature_type = signature_type; return bresult; }
static signature_type_t GetSignatureType() { return S3fsCurl::signature_type; }
static bool SetUnsignedPayload(bool issset) { bool bresult = S3fsCurl::is_unsigned_payload; S3fsCurl::is_unsigned_payload = issset; return bresult; }
static bool GetUnsignedPayload() { return S3fsCurl::is_unsigned_payload; }
static bool SetUserAgentFlag(bool isset) { bool bresult = S3fsCurl::is_ua; S3fsCurl::is_ua = isset; return bresult; }
static bool IsUserAgentFlag() { return S3fsCurl::is_ua; }
static void InitUserAgent();
static bool SetListObjectsV2(bool isset) { bool bresult = S3fsCurl::listobjectsv2; S3fsCurl::listobjectsv2 = isset; return bresult; }
static bool IsListObjectsV2() { return S3fsCurl::listobjectsv2; }
static bool SetRequesterPays(bool flag) { bool old_flag = S3fsCurl::requester_pays; S3fsCurl::requester_pays = flag; return old_flag; }
static bool IsRequesterPays() { return S3fsCurl::requester_pays; }
static bool SetProxy(const char* url);
static bool SetProxyUserPwd(const char* userpwd);
static bool SetIPResolveType(const char* value);
// methods(variables)
CURL* GetCurlHandle(void) const { return hCurl; }
std::string GetPath(void) const { return path; }
std::string GetBasePath(void) const { return base_path; }
std::string GetSpacialSavedPath(void) const { return saved_path; }
std::string GetUrl(void) const { return url; }
std::string GetOp(void) const { return op; }
headers_t* GetResponseHeaders(void) { return &responseHeaders; }
BodyData* GetBodyData(void) { return &bodydata; }
BodyData* GetHeadData(void) { return &headdata; }
long GetLastResponseCode(void) const { return LastResponseCode; }
bool SetUseAhbe(bool ahbe);
bool EnableUseAhbe(void) { return SetUseAhbe(true); }
bool DisableUseAhbe(void) { return SetUseAhbe(false); }
bool IsUseAhbe(void) const { return is_use_ahbe; }
int GetMultipartRetryCount(void) const { return retry_count; }
void SetMultipartRetryCount(int retrycnt) { retry_count = retrycnt; }
bool IsOverMultipartRetryCount(void) const { return (retry_count >= S3fsCurl::retries); }
int GetLastPreHeadSeecKeyPos(void) const { return b_ssekey_pos; }
// methods
bool CreateCurlHandle(bool only_pool = false, bool remake = false);
bool DestroyCurlHandle(bool restore_pool = true, bool clear_internal_data = true, AutoLock::Type locktype = AutoLock::NONE);
bool GetIAMCredentials(const char* cred_url, const char* iam_v2_token, const char* ibm_secret_access_key, std::string& response);
bool GetIAMRoleFromMetaData(const char* cred_url, const char* iam_v2_token, std::string& token);
bool GetResponseCode(long& responseCode, bool from_curl_handle = true) const;
int RequestPerform(bool dontAddAuthHeaders=false);
int DeleteRequest(const char* tpath);
int GetIAMv2ApiToken(const char* token_url, int token_ttl, const char* token_ttl_hdr, std::string& response);
bool PreHeadRequest(const char* tpath, const char* bpath = nullptr, const char* savedpath = nullptr, size_t ssekey_pos = -1);
bool PreHeadRequest(const std::string& tpath, const std::string& bpath, const std::string& savedpath, size_t ssekey_pos = -1) {
return PreHeadRequest(tpath.c_str(), bpath.c_str(), savedpath.c_str(), ssekey_pos);
}
int HeadRequest(const char* tpath, headers_t& meta);
int PutHeadRequest(const char* tpath, headers_t& meta, bool is_copy);
int PutRequest(const char* tpath, headers_t& meta, int fd);
int PreGetObjectRequest(const char* tpath, int fd, off_t start, off_t size, sse_type_t ssetype, const std::string& ssevalue);
int GetObjectRequest(const char* tpath, int fd, off_t start = -1, off_t size = -1);
int CheckBucket(const char* check_path, bool compat_dir, bool force_no_sse);
int ListBucketRequest(const char* tpath, const char* query);
int PreMultipartPostRequest(const char* tpath, headers_t& meta, std::string& upload_id, bool is_copy);
int CompleteMultipartPostRequest(const char* tpath, const std::string& upload_id, etaglist_t& parts);
int UploadMultipartPostRequest(const char* tpath, int part_num, const std::string& upload_id);
bool MixMultipartPostComplete();
int MultipartListRequest(std::string& body);
int AbortMultipartUpload(const char* tpath, const std::string& upload_id);
int MultipartHeadRequest(const char* tpath, off_t size, headers_t& meta, bool is_copy);
int MultipartUploadRequest(const std::string& upload_id, const char* tpath, int fd, off_t offset, off_t size, etagpair* petagpair);
int MultipartRenameRequest(const char* from, const char* to, headers_t& meta, off_t size);
// methods(variables)
CURL* GetCurlHandle() const { return hCurl; }
std::string GetPath() const { return path; }
std::string GetBasePath() const { return base_path; }
std::string GetSpecialSavedPath() const { return saved_path; }
std::string GetUrl() const { return url; }
std::string GetOp() const { return op; }
const headers_t* GetResponseHeaders() const { return &responseHeaders; }
const std::string* GetBodyData() const { return &bodydata; }
const std::string* GetHeadData() const { return &headdata; }
CURLcode GetCurlCode() const { return curlCode; }
long GetLastResponseCode() const { return LastResponseCode; }
bool SetUseAhbe(bool ahbe);
bool EnableUseAhbe() { return SetUseAhbe(true); }
bool DisableUseAhbe() { return SetUseAhbe(false); }
bool IsUseAhbe() const { return is_use_ahbe; }
int GetMultipartRetryCount() const { return retry_count; }
void SetMultipartRetryCount(int retrycnt) { retry_count = retrycnt; }
bool IsOverMultipartRetryCount() const { return (retry_count >= S3fsCurl::retries); }
size_t GetLastPreHeadSeecKeyPos() const { return b_ssekey_pos; }
};
//----------------------------------------------
// class S3fsMultiCurl
//----------------------------------------------
// Class for lapping multi curl
//
typedef std::vector<S3fsCurl*> s3fscurllist_t;
typedef bool (*S3fsMultiSuccessCallback)(S3fsCurl* s3fscurl); // callback for succeed multi request
typedef S3fsCurl* (*S3fsMultiRetryCallback)(S3fsCurl* s3fscurl); // callback for failure and retrying
class S3fsMultiCurl
{
private:
const int maxParallelism;
s3fscurllist_t clist_all; // all of curl requests
s3fscurllist_t clist_req; // curl requests are sent
S3fsMultiSuccessCallback SuccessCallback;
S3fsMultiRetryCallback RetryCallback;
pthread_mutex_t completed_tids_lock;
std::vector<pthread_t> completed_tids;
private:
bool ClearEx(bool is_all);
int MultiPerform(void);
int MultiRead(void);
static void* RequestPerformWrapper(void* arg);
public:
explicit S3fsMultiCurl(int maxParallelism);
~S3fsMultiCurl();
int GetMaxParallelism() { return maxParallelism; }
S3fsMultiSuccessCallback SetSuccessCallback(S3fsMultiSuccessCallback function);
S3fsMultiRetryCallback SetRetryCallback(S3fsMultiRetryCallback function);
bool Clear(void) { return ClearEx(true); }
bool SetS3fsCurlObject(S3fsCurl* s3fscurl);
int Request(void);
};
//----------------------------------------------
// Utility Functions
//----------------------------------------------
std::string GetContentMD5(int fd);
unsigned char* md5hexsum(int fd, off_t start, ssize_t size);
std::string md5sum(int fd, off_t start, ssize_t size);
struct curl_slist* curl_slist_sort_insert(struct curl_slist* list, const char* data);
struct curl_slist* curl_slist_sort_insert(struct curl_slist* list, const char* key, const char* value);
std::string get_sorted_header_keys(const struct curl_slist* list);
std::string get_canonical_headers(const struct curl_slist* list, bool only_amz = false);
std::string get_header_value(const struct curl_slist* list, const std::string &key);
bool MakeUrlResource(const char* realpath, std::string& resourcepath, std::string& url);
std::string prepare_url(const char* url);
bool get_object_sse_type(const char* path, sse_type_t& ssetype, std::string& ssevalue); // implement in s3fs.cpp
const char *acl_to_string(acl_t acl);
acl_t string_to_acl(const char *acl);
#endif // S3FS_CURL_H_
/*
@ -608,6 +412,6 @@ acl_t string_to_acl(const char *acl);
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

137
src/curl_handlerpool.cpp Normal file
View File

@ -0,0 +1,137 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cstdio>
#include "s3fs_logger.h"
#include "curl_handlerpool.h"
#include "autolock.h"
//-------------------------------------------------------------------
// Class CurlHandlerPool
//-------------------------------------------------------------------
bool CurlHandlerPool::Init()
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
#if S3FS_PTHREAD_ERRORCHECK
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
#endif
if (0 != pthread_mutex_init(&mLock, &attr)) {
S3FS_PRN_ERR("Init curl handlers lock failed");
return false;
}
for(int cnt = 0; cnt < mMaxHandlers; ++cnt){
CURL* hCurl = curl_easy_init();
if(!hCurl){
S3FS_PRN_ERR("Init curl handlers pool failed");
Destroy();
return false;
}
mPool.push_back(hCurl);
}
return true;
}
bool CurlHandlerPool::Destroy()
{
{
AutoLock lock(&mLock);
while(!mPool.empty()){
CURL* hCurl = mPool.back();
mPool.pop_back();
if(hCurl){
curl_easy_cleanup(hCurl);
}
}
}
if (0 != pthread_mutex_destroy(&mLock)) {
S3FS_PRN_ERR("Destroy curl handlers lock failed");
return false;
}
return true;
}
CURL* CurlHandlerPool::GetHandler(bool only_pool)
{
AutoLock lock(&mLock);
CURL* hCurl = nullptr;
if(!mPool.empty()){
hCurl = mPool.back();
mPool.pop_back();
S3FS_PRN_DBG("Get handler from pool: rest = %d", static_cast<int>(mPool.size()));
}
if(only_pool){
return hCurl;
}
if(!hCurl){
S3FS_PRN_INFO("Pool empty: force to create new handler");
hCurl = curl_easy_init();
}
return hCurl;
}
void CurlHandlerPool::ReturnHandler(CURL* hCurl, bool restore_pool)
{
if(!hCurl){
return;
}
AutoLock lock(&mLock);
if(restore_pool){
S3FS_PRN_DBG("Return handler to pool");
mPool.push_back(hCurl);
while(mMaxHandlers < static_cast<int>(mPool.size())){
CURL* hOldCurl = mPool.front();
mPool.pop_front();
if(hOldCurl){
S3FS_PRN_INFO("Pool full: destroy the oldest handler");
curl_easy_cleanup(hOldCurl);
}
}
}else{
S3FS_PRN_INFO("Pool full: destroy the handler");
curl_easy_cleanup(hCurl);
}
}
void CurlHandlerPool::ResetHandler(CURL* hCurl)
{
if(!hCurl){
return;
}
AutoLock lock(&mLock);
curl_easy_reset(hCurl);
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

70
src/curl_handlerpool.h Normal file
View File

@ -0,0 +1,70 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_CURL_HANDLERPOOL_H_
#define S3FS_CURL_HANDLERPOOL_H_
#include <cassert>
#include <curl/curl.h>
#include <list>
//----------------------------------------------
// Typedefs
//----------------------------------------------
typedef std::list<CURL*> hcurllist_t;
//----------------------------------------------
// class CurlHandlerPool
//----------------------------------------------
class CurlHandlerPool
{
public:
explicit CurlHandlerPool(int maxHandlers) : mMaxHandlers(maxHandlers)
{
assert(maxHandlers > 0);
}
CurlHandlerPool(const CurlHandlerPool&) = delete;
CurlHandlerPool(CurlHandlerPool&&) = delete;
CurlHandlerPool& operator=(const CurlHandlerPool&) = delete;
CurlHandlerPool& operator=(CurlHandlerPool&&) = delete;
bool Init();
bool Destroy();
CURL* GetHandler(bool only_pool);
void ReturnHandler(CURL* hCurl, bool restore_pool);
void ResetHandler(CURL* hCurl);
private:
int mMaxHandlers;
pthread_mutex_t mLock;
hcurllist_t mPool;
};
#endif // S3FS_CURL_HANDLERPOOL_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

394
src/curl_multi.cpp Normal file
View File

@ -0,0 +1,394 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <vector>
#include "s3fs.h"
#include "s3fs_logger.h"
#include "curl_multi.h"
#include "curl.h"
#include "autolock.h"
#include "psemaphore.h"
//-------------------------------------------------------------------
// Class S3fsMultiCurl
//-------------------------------------------------------------------
S3fsMultiCurl::S3fsMultiCurl(int maxParallelism, bool not_abort) : maxParallelism(maxParallelism), not_abort(not_abort), SuccessCallback(nullptr), NotFoundCallback(nullptr), RetryCallback(nullptr), pSuccessCallbackParam(nullptr), pNotFoundCallbackParam(nullptr)
{
int result;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
#if S3FS_PTHREAD_ERRORCHECK
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
#endif
if (0 != (result = pthread_mutex_init(&completed_tids_lock, &attr))) {
S3FS_PRN_ERR("could not initialize completed_tids_lock: %i", result);
abort();
}
}
S3fsMultiCurl::~S3fsMultiCurl()
{
Clear();
int result;
if(0 != (result = pthread_mutex_destroy(&completed_tids_lock))){
S3FS_PRN_ERR("could not destroy completed_tids_lock: %i", result);
}
}
bool S3fsMultiCurl::ClearEx(bool is_all)
{
s3fscurllist_t::iterator iter;
for(iter = clist_req.begin(); iter != clist_req.end(); ++iter){
S3fsCurl* s3fscurl = iter->get();
if(s3fscurl){
s3fscurl->DestroyCurlHandle();
}
}
clist_req.clear();
if(is_all){
for(iter = clist_all.begin(); iter != clist_all.end(); ++iter){
S3fsCurl* s3fscurl = iter->get();
s3fscurl->DestroyCurlHandle();
}
clist_all.clear();
}
S3FS_MALLOCTRIM(0);
return true;
}
S3fsMultiSuccessCallback S3fsMultiCurl::SetSuccessCallback(S3fsMultiSuccessCallback function)
{
S3fsMultiSuccessCallback old = SuccessCallback;
SuccessCallback = function;
return old;
}
S3fsMultiNotFoundCallback S3fsMultiCurl::SetNotFoundCallback(S3fsMultiNotFoundCallback function)
{
S3fsMultiNotFoundCallback old = NotFoundCallback;
NotFoundCallback = function;
return old;
}
S3fsMultiRetryCallback S3fsMultiCurl::SetRetryCallback(S3fsMultiRetryCallback function)
{
S3fsMultiRetryCallback old = RetryCallback;
RetryCallback = function;
return old;
}
void* S3fsMultiCurl::SetSuccessCallbackParam(void* param)
{
void* old = pSuccessCallbackParam;
pSuccessCallbackParam = param;
return old;
}
void* S3fsMultiCurl::SetNotFoundCallbackParam(void* param)
{
void* old = pNotFoundCallbackParam;
pNotFoundCallbackParam = param;
return old;
}
bool S3fsMultiCurl::SetS3fsCurlObject(std::unique_ptr<S3fsCurl> s3fscurl)
{
if(!s3fscurl){
return false;
}
clist_all.push_back(std::move(s3fscurl));
return true;
}
int S3fsMultiCurl::MultiPerform()
{
std::vector<pthread_t> threads;
bool success = true;
bool isMultiHead = false;
Semaphore sem(GetMaxParallelism());
int rc;
for(s3fscurllist_t::iterator iter = clist_req.begin(); iter != clist_req.end(); ++iter) {
pthread_t thread;
S3fsCurl* s3fscurl = iter->get();
if(!s3fscurl){
continue;
}
sem.wait();
{
AutoLock lock(&completed_tids_lock);
for(std::vector<pthread_t>::iterator it = completed_tids.begin(); it != completed_tids.end(); ++it){
void* retval;
rc = pthread_join(*it, &retval);
if (rc) {
success = false;
S3FS_PRN_ERR("failed pthread_join - rc(%d) %s", rc, strerror(rc));
} else {
long int_retval = reinterpret_cast<long>(retval);
if (int_retval && !(int_retval == -ENOENT && isMultiHead)) {
S3FS_PRN_WARN("thread terminated with non-zero return code: %ld", int_retval);
}
}
}
completed_tids.clear();
}
s3fscurl->sem = &sem;
s3fscurl->completed_tids_lock = &completed_tids_lock;
s3fscurl->completed_tids = &completed_tids;
isMultiHead |= s3fscurl->GetOp() == "HEAD";
rc = pthread_create(&thread, nullptr, S3fsMultiCurl::RequestPerformWrapper, static_cast<void*>(s3fscurl));
if (rc != 0) {
success = false;
S3FS_PRN_ERR("failed pthread_create - rc(%d)", rc);
break;
}
threads.push_back(thread);
}
for(int i = 0; i < sem.get_value(); ++i){
sem.wait();
}
AutoLock lock(&completed_tids_lock);
for (std::vector<pthread_t>::iterator titer = completed_tids.begin(); titer != completed_tids.end(); ++titer) {
void* retval;
rc = pthread_join(*titer, &retval);
if (rc) {
success = false;
S3FS_PRN_ERR("failed pthread_join - rc(%d)", rc);
} else {
long int_retval = reinterpret_cast<long>(retval);
if (int_retval && !(int_retval == -ENOENT && isMultiHead)) {
S3FS_PRN_WARN("thread terminated with non-zero return code: %ld", int_retval);
}
}
}
completed_tids.clear();
return success ? 0 : -EIO;
}
int S3fsMultiCurl::MultiRead()
{
int result = 0;
for(s3fscurllist_t::iterator iter = clist_req.begin(); iter != clist_req.end(); ){
std::unique_ptr<S3fsCurl> s3fscurl(std::move(*iter));
bool isRetry = false;
bool isPostpone = false;
bool isNeedResetOffset = true;
long responseCode = S3fsCurl::S3FSCURL_RESPONSECODE_NOTSET;
CURLcode curlCode = s3fscurl->GetCurlCode();
if(s3fscurl->GetResponseCode(responseCode, false) && curlCode == CURLE_OK){
if(S3fsCurl::S3FSCURL_RESPONSECODE_NOTSET == responseCode){
// This is a case where the processing result has not yet been updated (should be very rare).
isPostpone = true;
}else if(400 > responseCode){
// add into stat cache
// cppcheck-suppress unmatchedSuppression
// cppcheck-suppress knownPointerToBool
if(SuccessCallback && !SuccessCallback(s3fscurl.get(), pSuccessCallbackParam)){
S3FS_PRN_WARN("error from success callback function(%s).", s3fscurl->url.c_str());
}
}else if(400 == responseCode){
// as possibly in multipart
S3FS_PRN_WARN("failed a request(%ld: %s)", responseCode, s3fscurl->url.c_str());
isRetry = true;
}else if(404 == responseCode){
// not found
// HEAD requests on readdir_multi_head can return 404
if(s3fscurl->GetOp() != "HEAD"){
S3FS_PRN_WARN("failed a request(%ld: %s)", responseCode, s3fscurl->url.c_str());
}
// Call callback function
// cppcheck-suppress unmatchedSuppression
// cppcheck-suppress knownPointerToBool
if(NotFoundCallback && !NotFoundCallback(s3fscurl.get(), pNotFoundCallbackParam)){
S3FS_PRN_WARN("error from not found callback function(%s).", s3fscurl->url.c_str());
}
}else if(500 == responseCode){
// case of all other result, do retry.(11/13/2013)
// because it was found that s3fs got 500 error from S3, but could success
// to retry it.
S3FS_PRN_WARN("failed a request(%ld: %s)", responseCode, s3fscurl->url.c_str());
isRetry = true;
}else{
// Retry in other case.
S3FS_PRN_WARN("failed a request(%ld: %s)", responseCode, s3fscurl->url.c_str());
isRetry = true;
}
}else{
S3FS_PRN_ERR("failed a request(Unknown response code: %s)", s3fscurl->url.c_str());
// Reuse partical file
switch(curlCode){
case CURLE_OPERATION_TIMEDOUT:
isRetry = true;
isNeedResetOffset = false;
break;
case CURLE_PARTIAL_FILE:
isRetry = true;
isNeedResetOffset = false;
break;
default:
S3FS_PRN_ERR("###curlCode: %d msg: %s", curlCode, curl_easy_strerror(curlCode));
isRetry = true;
break;
}
}
if(isPostpone){
clist_req.erase(iter);
clist_req.push_back(std::move(s3fscurl)); // Re-evaluate at the end
iter = clist_req.begin();
}else{
if(!isRetry || (!not_abort && 0 != result)){
// If an EIO error has already occurred, it will be terminated
// immediately even if retry processing is required.
s3fscurl->DestroyCurlHandle();
}else{
// Reset offset
if(isNeedResetOffset){
S3fsCurl::ResetOffset(s3fscurl.get());
}
// For retry
std::unique_ptr<S3fsCurl> retrycurl;
const S3fsCurl* retrycurl_ptr = retrycurl.get(); // save this due to std::move below
if(RetryCallback){
retrycurl = RetryCallback(s3fscurl.get());
if(nullptr != retrycurl){
clist_all.push_back(std::move(retrycurl));
}else{
// set EIO and wait for other parts.
result = -EIO;
}
}
// cppcheck-suppress mismatchingContainers
if(s3fscurl.get() != retrycurl_ptr){
s3fscurl->DestroyCurlHandle();
}
}
iter = clist_req.erase(iter);
}
}
clist_req.clear();
if(!not_abort && 0 != result){
// If an EIO error has already occurred, clear all retry objects.
for(s3fscurllist_t::iterator iter = clist_all.begin(); iter != clist_all.end(); ++iter){
S3fsCurl* s3fscurl = iter->get();
s3fscurl->DestroyCurlHandle();
}
clist_all.clear();
}
return result;
}
int S3fsMultiCurl::Request()
{
S3FS_PRN_INFO3("[count=%zu]", clist_all.size());
// Make request list.
//
// Send multi request loop( with retry )
// (When many request is sends, sometimes gets "Couldn't connect to server")
//
while(!clist_all.empty()){
// set curl handle to multi handle
int result;
s3fscurllist_t::iterator iter;
for(iter = clist_all.begin(); iter != clist_all.end(); ++iter){
clist_req.push_back(std::move(*iter));
}
clist_all.clear();
// Send multi request.
if(0 != (result = MultiPerform())){
Clear();
return result;
}
// Read the result
if(0 != (result = MultiRead())){
Clear();
return result;
}
// Cleanup curl handle in multi handle
ClearEx(false);
}
return 0;
}
//
// thread function for performing an S3fsCurl request
//
void* S3fsMultiCurl::RequestPerformWrapper(void* arg)
{
S3fsCurl* s3fscurl= static_cast<S3fsCurl*>(arg);
void* result = nullptr;
if(!s3fscurl){
return reinterpret_cast<void*>(static_cast<intptr_t>(-EIO));
}
if(s3fscurl->fpLazySetup){
if(!s3fscurl->fpLazySetup(s3fscurl)){
S3FS_PRN_ERR("Failed to lazy setup, then respond EIO.");
result = reinterpret_cast<void*>(static_cast<intptr_t>(-EIO));
}
}
if(!result){
result = reinterpret_cast<void*>(static_cast<intptr_t>(s3fscurl->RequestPerform()));
s3fscurl->DestroyCurlHandle(true, false);
}
AutoLock lock(s3fscurl->completed_tids_lock);
s3fscurl->completed_tids->push_back(pthread_self());
s3fscurl->sem->post();
return result;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

90
src/curl_multi.h Normal file
View File

@ -0,0 +1,90 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_CURL_MULTI_H_
#define S3FS_CURL_MULTI_H_
#include <memory>
#include <vector>
//----------------------------------------------
// Typedef
//----------------------------------------------
class S3fsCurl;
typedef std::vector<std::unique_ptr<S3fsCurl>> s3fscurllist_t;
typedef bool (*S3fsMultiSuccessCallback)(S3fsCurl* s3fscurl, void* param); // callback for succeed multi request
typedef bool (*S3fsMultiNotFoundCallback)(S3fsCurl* s3fscurl, void* param); // callback for succeed multi request
typedef std::unique_ptr<S3fsCurl> (*S3fsMultiRetryCallback)(S3fsCurl* s3fscurl); // callback for failure and retrying
//----------------------------------------------
// class S3fsMultiCurl
//----------------------------------------------
class S3fsMultiCurl
{
private:
const int maxParallelism;
s3fscurllist_t clist_all; // all of curl requests
s3fscurllist_t clist_req; // curl requests are sent
bool not_abort; // complete all requests without aborting on errors
S3fsMultiSuccessCallback SuccessCallback;
S3fsMultiNotFoundCallback NotFoundCallback;
S3fsMultiRetryCallback RetryCallback;
void* pSuccessCallbackParam;
void* pNotFoundCallbackParam;
pthread_mutex_t completed_tids_lock;
std::vector<pthread_t> completed_tids;
private:
bool ClearEx(bool is_all);
int MultiPerform();
int MultiRead();
static void* RequestPerformWrapper(void* arg);
public:
explicit S3fsMultiCurl(int maxParallelism, bool not_abort = false);
~S3fsMultiCurl();
int GetMaxParallelism() const { return maxParallelism; }
S3fsMultiSuccessCallback SetSuccessCallback(S3fsMultiSuccessCallback function);
S3fsMultiNotFoundCallback SetNotFoundCallback(S3fsMultiNotFoundCallback function);
S3fsMultiRetryCallback SetRetryCallback(S3fsMultiRetryCallback function);
void* SetSuccessCallbackParam(void* param);
void* SetNotFoundCallbackParam(void* param);
bool Clear() { return ClearEx(true); }
bool SetS3fsCurlObject(std::unique_ptr<S3fsCurl> s3fscurl);
int Request();
};
#endif // S3FS_CURL_MULTI_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

334
src/curl_util.cpp Normal file
View File

@ -0,0 +1,334 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cstdio>
#include <cstdlib>
#include <curl/curl.h>
#include "common.h"
#include "s3fs_logger.h"
#include "curl_util.h"
#include "string_util.h"
#include "s3fs_auth.h"
#include "s3fs_cred.h"
//-------------------------------------------------------------------
// Utility Functions
//-------------------------------------------------------------------
//
// curl_slist_sort_insert
// This function is like curl_slist_append function, but this adds data by a-sorting.
// Because AWS signature needs sorted header.
//
struct curl_slist* curl_slist_sort_insert(struct curl_slist* list, const char* key, const char* value)
{
if(!key){
return list;
}
// key & value are trimmed and lower (only key)
std::string strkey = trim(key);
std::string strval = value ? trim(value) : "";
std::string strnew = key + std::string(": ") + strval;
char* data;
if(nullptr == (data = strdup(strnew.c_str()))){
return list;
}
struct curl_slist **p = &list;
for(;*p; p = &(*p)->next){
std::string strcur = (*p)->data;
size_t pos;
if(std::string::npos != (pos = strcur.find(':', 0))){
strcur.erase(pos);
}
int result = strcasecmp(strkey.c_str(), strcur.c_str());
if(0 == result){
free((*p)->data);
(*p)->data = data;
return list;
}else if(result < 0){
break;
}
}
struct curl_slist* new_item;
// Must use malloc since curl_slist_free_all calls free.
if(nullptr == (new_item = static_cast<struct curl_slist*>(malloc(sizeof(*new_item))))){
free(data);
return list;
}
struct curl_slist* before = *p;
*p = new_item;
new_item->data = data;
new_item->next = before;
return list;
}
struct curl_slist* curl_slist_remove(struct curl_slist* list, const char* key)
{
if(!key){
return list;
}
std::string strkey = trim(key);
struct curl_slist **p = &list;
while(*p){
std::string strcur = (*p)->data;
size_t pos;
if(std::string::npos != (pos = strcur.find(':', 0))){
strcur.erase(pos);
}
int result = strcasecmp(strkey.c_str(), strcur.c_str());
if(0 == result){
free((*p)->data);
struct curl_slist *tmp = *p;
*p = (*p)->next;
free(tmp);
}else{
p = &(*p)->next;
}
}
return list;
}
std::string get_sorted_header_keys(const struct curl_slist* list)
{
std::string sorted_headers;
if(!list){
return sorted_headers;
}
for( ; list; list = list->next){
std::string strkey = list->data;
size_t pos;
if(std::string::npos != (pos = strkey.find(':', 0))){
if (trim(strkey.substr(pos + 1)).empty()) {
// skip empty-value headers (as they are discarded by libcurl)
continue;
}
strkey.erase(pos);
}
if(!sorted_headers.empty()){
sorted_headers += ";";
}
sorted_headers += lower(strkey);
}
return sorted_headers;
}
std::string get_header_value(const struct curl_slist* list, const std::string &key)
{
if(!list){
return "";
}
for( ; list; list = list->next){
std::string strkey = list->data;
size_t pos;
if(std::string::npos != (pos = strkey.find(':', 0))){
if(0 == strcasecmp(trim(strkey.substr(0, pos)).c_str(), key.c_str())){
return trim(strkey.substr(pos+1));
}
}
}
return "";
}
std::string get_canonical_headers(const struct curl_slist* list, bool only_amz)
{
std::string canonical_headers;
if(!list){
canonical_headers = "\n";
return canonical_headers;
}
for( ; list; list = list->next){
std::string strhead = list->data;
size_t pos;
if(std::string::npos != (pos = strhead.find(':', 0))){
std::string strkey = trim(lower(strhead.substr(0, pos)));
std::string strval = trim(strhead.substr(pos + 1));
if (strval.empty()) {
// skip empty-value headers (as they are discarded by libcurl)
continue;
}
strhead = strkey;
strhead += ":";
strhead += strval;
}else{
strhead = trim(lower(strhead));
}
if(only_amz && strhead.substr(0, 5) != "x-amz"){
continue;
}
canonical_headers += strhead;
canonical_headers += "\n";
}
return canonical_headers;
}
// function for using global values
bool MakeUrlResource(const char* realpath, std::string& resourcepath, std::string& url)
{
if(!realpath){
return false;
}
resourcepath = urlEncodePath(service_path + S3fsCred::GetBucket() + realpath);
url = s3host + resourcepath;
return true;
}
std::string prepare_url(const char* url)
{
S3FS_PRN_INFO3("URL is %s", url);
std::string uri;
std::string hostname;
std::string path;
std::string url_str = url;
std::string token = "/" + S3fsCred::GetBucket();
size_t bucket_pos;
size_t bucket_length = token.size();
size_t uri_length = 0;
if(!strncasecmp(url_str.c_str(), "https://", 8)){
uri_length = 8;
} else if(!strncasecmp(url_str.c_str(), "http://", 7)) {
uri_length = 7;
}
uri = url_str.substr(0, uri_length);
bucket_pos = url_str.find(token, uri_length);
if(!pathrequeststyle){
hostname = S3fsCred::GetBucket() + "." + url_str.substr(uri_length, bucket_pos - uri_length);
path = url_str.substr((bucket_pos + bucket_length));
}else{
hostname = url_str.substr(uri_length, bucket_pos - uri_length);
std::string part = url_str.substr((bucket_pos + bucket_length));
if('/' != part[0]){
part = "/" + part;
}
path = "/" + S3fsCred::GetBucket() + part;
}
url_str = uri + hostname + path;
S3FS_PRN_INFO3("URL changed is %s", url_str.c_str());
return url_str;
}
bool make_md5_from_binary(const char* pstr, size_t length, std::string& md5)
{
if(!pstr || '\0' == pstr[0]){
S3FS_PRN_ERR("Parameter is wrong.");
return false;
}
md5_t binary;
if(!s3fs_md5(reinterpret_cast<const unsigned char*>(pstr), length, &binary)){
return false;
}
md5 = s3fs_base64(binary.data(), binary.size());
return true;
}
std::string url_to_host(const std::string &url)
{
S3FS_PRN_INFO3("url is %s", url.c_str());
static constexpr char HTTP[] = "http://";
static constexpr char HTTPS[] = "https://";
std::string hostname;
if (is_prefix(url.c_str(), HTTP)) {
hostname = url.substr(sizeof(HTTP) - 1);
} else if (is_prefix(url.c_str(), HTTPS)) {
hostname = url.substr(sizeof(HTTPS) - 1);
} else {
S3FS_PRN_EXIT("url does not begin with http:// or https://");
abort();
}
size_t idx;
if ((idx = hostname.find('/')) != std::string::npos) {
return hostname.substr(0, idx);
} else {
return hostname;
}
}
std::string get_bucket_host()
{
if(!pathrequeststyle){
return S3fsCred::GetBucket() + "." + url_to_host(s3host);
}
return url_to_host(s3host);
}
const char* getCurlDebugHead(curl_infotype type)
{
const char* unknown = "";
const char* dataIn = "BODY <";
const char* dataOut = "BODY >";
const char* headIn = "<";
const char* headOut = ">";
switch(type){
case CURLINFO_DATA_IN:
return dataIn;
case CURLINFO_DATA_OUT:
return dataOut;
case CURLINFO_HEADER_IN:
return headIn;
case CURLINFO_HEADER_OUT:
return headOut;
default:
break;
}
return unknown;
}
//
// compare ETag ignoring quotes and case
//
bool etag_equals(const std::string& s1, const std::string& s2)
{
return 0 == strcasecmp(peeloff(s1).c_str(), peeloff(s2).c_str());
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

56
src/curl_util.h Normal file
View File

@ -0,0 +1,56 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_CURL_UTIL_H_
#define S3FS_CURL_UTIL_H_
#include <curl/curl.h>
enum class sse_type_t;
//----------------------------------------------
// Functions
//----------------------------------------------
struct curl_slist* curl_slist_sort_insert(struct curl_slist* list, const char* key, const char* value);
struct curl_slist* curl_slist_remove(struct curl_slist* list, const char* key);
std::string get_sorted_header_keys(const struct curl_slist* list);
std::string get_canonical_headers(const struct curl_slist* list, bool only_amz = false);
std::string get_header_value(const struct curl_slist* list, const std::string &key);
bool MakeUrlResource(const char* realpath, std::string& resourcepath, std::string& url);
std::string prepare_url(const char* url);
bool get_object_sse_type(const char* path, sse_type_t& ssetype, std::string& ssevalue); // implement in s3fs.cpp
bool make_md5_from_binary(const char* pstr, size_t length, std::string& md5);
std::string url_to_host(const std::string &url);
std::string get_bucket_host();
const char* getCurlDebugHead(curl_infotype type);
bool etag_equals(const std::string& s1, const std::string& s2);
#endif // S3FS_CURL_UTIL_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

File diff suppressed because it is too large Load Diff

View File

@ -17,199 +17,11 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef FD_CACHE_H_
#define FD_CACHE_H_
#include <sys/statvfs.h>
#ifndef S3FS_FDCACHE_H_
#define S3FS_FDCACHE_H_
//------------------------------------------------
// CacheFileStat
//------------------------------------------------
class CacheFileStat
{
private:
std::string path;
int fd;
private:
static bool MakeCacheFileStatPath(const char* path, std::string& sfile_path, bool is_create_dir = true);
bool RawOpen(bool readonly);
public:
static std::string GetCacheFileStatTopDir(void);
static bool DeleteCacheFileStat(const char* path);
static bool CheckCacheFileStatTopDir(void);
static bool DeleteCacheFileStatDirectory(void);
static bool RenameCacheFileStat(const char* oldpath, const char* newpath);
explicit CacheFileStat(const char* tpath = NULL);
~CacheFileStat();
bool Open(void);
bool ReadOnlyOpen(void);
bool Release(void);
bool SetPath(const char* tpath, bool is_open = true);
int GetFd(void) const { return fd; }
};
//------------------------------------------------
// fdpage & PageList
//------------------------------------------------
// page block information
struct fdpage
{
off_t offset;
off_t bytes;
bool loaded;
bool modified;
fdpage(off_t start = 0, off_t size = 0, bool is_loaded = false, bool is_modified = false)
: offset(start), bytes(size), loaded(is_loaded), modified(is_modified) {}
off_t next(void) const { return (offset + bytes); }
off_t end(void) const { return (0 < bytes ? offset + bytes - 1 : 0); }
};
typedef std::list<struct fdpage> fdpage_list_t;
class FdEntity;
//
// Management of loading area/modifying
//
// cppcheck-suppress copyCtorAndEqOperator
class PageList
{
friend class FdEntity; // only one method access directly pages.
private:
fdpage_list_t pages;
public:
enum page_status{
PAGE_NOT_LOAD_MODIFIED = 0,
PAGE_LOADED,
PAGE_MODIFIED,
PAGE_LOAD_MODIFIED
};
private:
static bool GetSparseFilePages(int fd, size_t file_size, fdpage_list_t& sparse_list);
static bool CheckZeroAreaInFile(int fd, off_t start, size_t bytes);
static bool CheckAreaInSparseFile(const struct fdpage& checkpage, const fdpage_list_t& sparse_list, int fd, fdpage_list_t& err_area_list, fdpage_list_t& warn_area_list);
void Clear(void);
bool Compress();
bool Parse(off_t new_pos);
public:
static void FreeList(fdpage_list_t& list);
explicit PageList(off_t size = 0, bool is_loaded = false, bool is_modified = false);
explicit PageList(const PageList& other);
~PageList();
bool Init(off_t size, bool is_loaded, bool is_modified);
off_t Size(void) const;
bool Resize(off_t size, bool is_loaded, bool is_modified);
bool IsPageLoaded(off_t start = 0, off_t size = 0) const; // size=0 is checking to end of list
bool SetPageLoadedStatus(off_t start, off_t size, PageList::page_status pstatus = PAGE_LOADED, bool is_compress = true);
bool FindUnloadedPage(off_t start, off_t& resstart, off_t& ressize) const;
off_t GetTotalUnloadedPageSize(off_t start = 0, off_t size = 0) const; // size=0 is checking to end of list
int GetUnloadedPages(fdpage_list_t& unloaded_list, off_t start = 0, off_t size = 0) const; // size=0 is checking to end of list
bool GetPageListsForMultipartUpload(fdpage_list_t& dlpages, fdpage_list_t& mixuppages, off_t max_partsize);
bool IsModified(void) const;
bool ClearAllModified(void);
bool Serialize(CacheFileStat& file, bool is_output, ino_t inode);
void Dump(void) const;
bool CompareSparseFile(int fd, size_t file_size, fdpage_list_t& err_area_list, fdpage_list_t& warn_area_list);
};
//------------------------------------------------
// class FdEntity
//------------------------------------------------
class FdEntity
{
private:
static bool mixmultipart; // whether multipart uploading can use copy api.
pthread_mutex_t fdent_lock;
bool is_lock_init;
int refcnt; // reference count
std::string path; // object path
int fd; // file descriptor(tmp file or cache file)
FILE* pfile; // file pointer(tmp file or cache file)
ino_t inode; // inode number for cache file
headers_t orgmeta; // original headers at opening
off_t size_orgmeta; // original file size in original headers
pthread_mutex_t fdent_data_lock;// protects the following members
PageList pagelist;
std::string upload_id; // for no cached multipart uploading when no disk space
etaglist_t etaglist; // for no cached multipart uploading when no disk space
off_t mp_start; // start position for no cached multipart(write method only)
off_t mp_size; // size for no cached multipart(write method only)
std::string cachepath; // local cache file path
// (if this is empty, does not load/save pagelist.)
std::string mirrorpath; // mirror file path to local cache file path
private:
static int FillFile(int fd, unsigned char byte, off_t size, off_t start);
static ino_t GetInode(int fd);
void Clear(void);
ino_t GetInode(void);
int OpenMirrorFile(void);
bool SetAllStatus(bool is_loaded); // [NOTE] not locking
//bool SetAllStatusLoaded(void) { return SetAllStatus(true); }
bool SetAllStatusUnloaded(void) { return SetAllStatus(false); }
public:
static bool SetNoMixMultipart(void);
explicit FdEntity(const char* tpath = NULL, const char* cpath = NULL);
~FdEntity();
void Close(void);
bool IsOpen(void) const { return (-1 != fd); }
int Open(headers_t* pmeta = NULL, off_t size = -1, time_t time = -1, bool no_fd_lock_wait = false);
bool OpenAndLoadAll(headers_t* pmeta = NULL, off_t* size = NULL, bool force_load = false);
int Dup(bool lock_already_held = false);
const char* GetPath(void) const { return path.c_str(); }
bool RenamePath(const std::string& newpath, std::string& fentmapkey);
int GetFd(void) const { return fd; }
bool IsModified(void) const { return pagelist.IsModified(); }
bool GetStats(struct stat& st, bool lock_already_held = false);
int SetCtime(time_t time);
int SetMtime(time_t time, bool lock_already_held = false);
bool UpdateCtime(void);
bool UpdateMtime(void);
bool GetSize(off_t& size);
bool SetMode(mode_t mode);
bool SetUId(uid_t uid);
bool SetGId(gid_t gid);
bool SetContentType(const char* path);
int Load(off_t start = 0, off_t size = 0, bool lock_already_held = false, bool is_modified_flag = false); // size=0 means loading to end
int NoCacheLoadAndPost(off_t start = 0, off_t size = 0); // size=0 means loading to end
int NoCachePreMultipartPost(void);
int NoCacheMultipartPost(int tgfd, off_t start, off_t size);
int NoCacheCompleteMultipartPost(void);
int RowFlush(const char* tpath, bool force_sync = false);
int Flush(bool force_sync = false) { return RowFlush(NULL, force_sync); }
ssize_t Read(char* bytes, off_t start, size_t size, bool force_load = false);
ssize_t Write(const char* bytes, off_t start, size_t size);
bool ReserveDiskSpace(off_t size);
};
typedef std::map<std::string, class FdEntity*> fdent_map_t; // key=path, value=FdEntity*
#include "fdcache_entity.h"
//------------------------------------------------
// class FdManager
@ -217,68 +29,86 @@ typedef std::map<std::string, class FdEntity*> fdent_map_t; // key=path, value
class FdManager
{
private:
static FdManager singleton;
static pthread_mutex_t fd_manager_lock;
static pthread_mutex_t cache_cleanup_lock;
static pthread_mutex_t reserved_diskspace_lock;
static bool is_lock_init;
static std::string cache_dir;
static bool check_cache_dir_exist;
static off_t free_disk_space; // limit free disk space
static std::string check_cache_output;
static FdManager singleton;
static pthread_mutex_t fd_manager_lock;
static pthread_mutex_t cache_cleanup_lock;
static pthread_mutex_t reserved_diskspace_lock;
static bool is_lock_init;
static std::string cache_dir;
static bool check_cache_dir_exist;
static off_t free_disk_space; // limit free disk space
static off_t fake_used_disk_space; // difference between fake free disk space and actual at startup(for test/debug)
static std::string check_cache_output;
static bool checked_lseek;
static bool have_lseek_hole;
static std::string tmp_dir;
fdent_map_t fent;
fdent_map_t fent;
private:
static off_t GetFreeDiskSpace(const char* path);
void CleanupCacheDirInternal(const std::string &path = "");
bool RawCheckAllCache(FILE* fp, const char* cache_stat_top_dir, const char* sub_path, int& total_file_cnt, int& err_file_cnt, int& err_dir_cnt);
static off_t GetFreeDiskSpace(const char* path);
static off_t GetTotalDiskSpace(const char* path);
static bool IsDir(const std::string* dir);
static int GetVfsStat(const char* path, struct statvfs* vfsbuf);
int GetPseudoFdCount(const char* path);
void CleanupCacheDirInternal(const std::string &path = "");
bool RawCheckAllCache(FILE* fp, const char* cache_stat_top_dir, const char* sub_path, int& total_file_cnt, int& err_file_cnt, int& err_dir_cnt);
public:
FdManager();
~FdManager();
FdManager();
~FdManager();
// Reference singleton
static FdManager* get(void) { return &singleton; }
// Reference singleton
static FdManager* get() { return &singleton; }
static bool DeleteCacheDirectory(void);
static int DeleteCacheFile(const char* path);
static bool SetCacheDir(const char* dir);
static bool IsCacheDir(void) { return !FdManager::cache_dir.empty(); }
static const char* GetCacheDir(void) { return FdManager::cache_dir.c_str(); }
static bool SetCacheCheckOutput(const char* path);
static const char* GetCacheCheckOutput(void) { return FdManager::check_cache_output.c_str(); }
static bool MakeCachePath(const char* path, std::string& cache_path, bool is_create_dir = true, bool is_mirror_path = false);
static bool CheckCacheTopDir(void);
static bool MakeRandomTempPath(const char* path, std::string& tmppath);
static bool SetCheckCacheDirExist(bool is_check);
static bool CheckCacheDirExist(void);
static bool DeleteCacheDirectory();
static int DeleteCacheFile(const char* path);
static bool SetCacheDir(const char* dir);
static bool IsCacheDir() { return !FdManager::cache_dir.empty(); }
static const char* GetCacheDir() { return FdManager::cache_dir.c_str(); }
static bool SetCacheCheckOutput(const char* path);
static const char* GetCacheCheckOutput() { return FdManager::check_cache_output.c_str(); }
static bool MakeCachePath(const char* path, std::string& cache_path, bool is_create_dir = true, bool is_mirror_path = false);
static bool CheckCacheTopDir();
static bool MakeRandomTempPath(const char* path, std::string& tmppath);
static bool SetCheckCacheDirExist(bool is_check);
static bool CheckCacheDirExist();
static bool HasOpenEntityFd(const char* path);
static int GetOpenFdCount(const char* path);
static off_t GetEnsureFreeDiskSpace();
static off_t SetEnsureFreeDiskSpace(off_t size);
static bool InitFakeUsedDiskSize(off_t fake_freesize);
static bool IsSafeDiskSpace(const char* path, off_t size);
static bool IsSafeDiskSpaceWithLog(const char* path, off_t size);
static void FreeReservedDiskSpace(off_t size);
static bool ReserveDiskSpace(off_t size);
static bool HaveLseekHole();
static bool SetTmpDir(const char* dir);
static bool CheckTmpDirExist();
static FILE* MakeTempFile();
static off_t GetTotalDiskSpaceByRatio(int ratio);
static off_t GetEnsureFreeDiskSpace();
static off_t SetEnsureFreeDiskSpace(off_t size);
static bool IsSafeDiskSpace(const char* path, off_t size);
static void FreeReservedDiskSpace(off_t size);
static bool ReserveDiskSpace(off_t size);
// Return FdEntity associated with path, returning nullptr on error. This operation increments the reference count; callers must decrement via Close after use.
FdEntity* GetFdEntity(const char* path, int& existfd, bool newfd = true, AutoLock::Type locktype = AutoLock::NONE);
FdEntity* Open(int& fd, const char* path, const headers_t* pmeta, off_t size, const struct timespec& ts_mctime, int flags, bool force_tmpfile, bool is_create, bool ignore_modify, AutoLock::Type type);
FdEntity* GetExistFdEntity(const char* path, int existfd = -1);
FdEntity* OpenExistFdEntity(const char* path, int& fd, int flags = O_RDONLY);
void Rename(const std::string &from, const std::string &to);
bool Close(FdEntity* ent, int fd);
bool ChangeEntityToTempPath(const FdEntity* ent, const char* path);
void CleanupCacheDir();
// Return FdEntity associated with path, returning NULL on error. This operation increments the reference count; callers must decrement via Close after use.
FdEntity* GetFdEntity(const char* path, int existfd = -1);
FdEntity* Open(const char* path, headers_t* pmeta = NULL, off_t size = -1, time_t time = -1, bool force_tmpfile = false, bool is_create = true, bool no_fd_lock_wait = false);
FdEntity* ExistOpen(const char* path, int existfd = -1, bool ignore_existfd = false);
void Rename(const std::string &from, const std::string &to);
bool Close(FdEntity* ent);
bool ChangeEntityToTempPath(FdEntity* ent, const char* path);
void CleanupCacheDir();
bool CheckAllCache(void);
bool CheckAllCache();
};
#endif // FD_CACHE_H_
#endif // S3FS_FDCACHE_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

126
src/fdcache_auto.cpp Normal file
View File

@ -0,0 +1,126 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cstdio>
#include "s3fs_logger.h"
#include "fdcache_auto.h"
#include "fdcache.h"
//------------------------------------------------
// AutoFdEntity methods
//------------------------------------------------
AutoFdEntity::AutoFdEntity() : pFdEntity(nullptr), pseudo_fd(-1)
{
}
AutoFdEntity::~AutoFdEntity()
{
Close();
}
bool AutoFdEntity::Close()
{
if(pFdEntity){
if(!FdManager::get()->Close(pFdEntity, pseudo_fd)){
S3FS_PRN_ERR("Failed to close fdentity.");
return false;
}
pFdEntity = nullptr;
pseudo_fd = -1;
}
return true;
}
// [NOTE]
// This method touches the internal fdentity with.
// This is used to keep the file open.
//
int AutoFdEntity::Detach()
{
if(!pFdEntity){
S3FS_PRN_ERR("Does not have a associated FdEntity.");
return -1;
}
int fd = pseudo_fd;
pseudo_fd = -1;
pFdEntity = nullptr;
return fd;
}
FdEntity* AutoFdEntity::Attach(const char* path, int existfd)
{
Close();
if(nullptr == (pFdEntity = FdManager::get()->GetFdEntity(path, existfd, false))){
S3FS_PRN_DBG("Could not find fd entity object(file=%s, pseudo_fd=%d)", path, existfd);
return nullptr;
}
pseudo_fd = existfd;
return pFdEntity;
}
FdEntity* AutoFdEntity::Open(const char* path, const headers_t* pmeta, off_t size, const struct timespec& ts_mctime, int flags, bool force_tmpfile, bool is_create, bool ignore_modify, AutoLock::Type type, int* error)
{
Close();
if(nullptr == (pFdEntity = FdManager::get()->Open(pseudo_fd, path, pmeta, size, ts_mctime, flags, force_tmpfile, is_create, ignore_modify, type))){
if(error){
*error = pseudo_fd;
}
pseudo_fd = -1;
return nullptr;
}
return pFdEntity;
}
// [NOTE]
// the fd obtained by this method is not a newly created pseudo fd.
//
FdEntity* AutoFdEntity::GetExistFdEntity(const char* path, int existfd)
{
Close();
FdEntity* ent;
if(nullptr == (ent = FdManager::get()->GetExistFdEntity(path, existfd))){
return nullptr;
}
return ent;
}
FdEntity* AutoFdEntity::OpenExistFdEntity(const char* path, int flags)
{
Close();
if(nullptr == (pFdEntity = FdManager::get()->OpenExistFdEntity(path, pseudo_fd, flags))){
return nullptr;
}
return pFdEntity;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

74
src/fdcache_auto.h Normal file
View File

@ -0,0 +1,74 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_FDCACHE_AUTO_H_
#define S3FS_FDCACHE_AUTO_H_
#include <fcntl.h>
#include "autolock.h"
#include "metaheader.h"
class FdEntity;
//------------------------------------------------
// class AutoFdEntity
//------------------------------------------------
// A class that opens fdentry and closes it automatically.
// This class object is used to prevent inconsistencies in
// the number of references in fdentry.
// The methods are wrappers to the method of the FdManager class.
//
class AutoFdEntity
{
private:
FdEntity* pFdEntity;
int pseudo_fd;
private:
AutoFdEntity(const AutoFdEntity&) = delete;
AutoFdEntity(AutoFdEntity&&) = delete;
AutoFdEntity& operator=(const AutoFdEntity&) = delete;
AutoFdEntity& operator=(AutoFdEntity&&) = delete;
public:
AutoFdEntity();
~AutoFdEntity();
bool Close();
int Detach();
FdEntity* Attach(const char* path, int existfd);
int GetPseudoFd() const { return pseudo_fd; }
FdEntity* Open(const char* path, const headers_t* pmeta, off_t size, const struct timespec& ts_mctime, int flags, bool force_tmpfile, bool is_create, bool ignore_modify, AutoLock::Type type, int* error = nullptr);
FdEntity* GetExistFdEntity(const char* path, int existfd = -1);
FdEntity* OpenExistFdEntity(const char* path, int flags = O_RDONLY);
};
#endif // S3FS_FDCACHE_AUTO_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

2658
src/fdcache_entity.cpp Normal file

File diff suppressed because it is too large Load Diff

178
src/fdcache_entity.h Normal file
View File

@ -0,0 +1,178 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_FDCACHE_ENTITY_H_
#define S3FS_FDCACHE_ENTITY_H_
#include <fcntl.h>
#include <memory>
#include "autolock.h"
#include "fdcache_page.h"
#include "fdcache_fdinfo.h"
#include "fdcache_untreated.h"
#include "metaheader.h"
//------------------------------------------------
// class FdEntity
//------------------------------------------------
class FdEntity
{
private:
// [NOTE]
// Distinguish between meta pending and new file creation pending,
// because the processing(request) at these updates is different.
// Therefore, the pending state is expressed by this enum type.
//
enum class pending_status_t {
NO_UPDATE_PENDING = 0,
UPDATE_META_PENDING, // pending meta header
CREATE_FILE_PENDING // pending file creation and meta header
};
static bool mixmultipart; // whether multipart uploading can use copy api.
static bool streamupload; // whether stream uploading.
mutable pthread_mutex_t fdent_lock;
bool is_lock_init;
std::string path; // object path
int physical_fd; // physical file(cache or temporary file) descriptor
UntreatedParts untreated_list; // list of untreated parts that have been written and not yet uploaded(for streamupload)
fdinfo_map_t pseudo_fd_map; // pseudo file descriptor information map
FILE* pfile; // file pointer(tmp file or cache file)
ino_t inode; // inode number for cache file
headers_t orgmeta; // original headers at opening
off_t size_orgmeta; // original file size in original headers
mutable pthread_mutex_t fdent_data_lock;// protects the following members
PageList pagelist;
std::string cachepath; // local cache file path
// (if this is empty, does not load/save pagelist.)
std::string mirrorpath; // mirror file path to local cache file path
pending_status_t pending_status;// status for new file creation and meta update
struct timespec holding_mtime; // if mtime is updated while the file is open, it is set time_t value
private:
static int FillFile(int fd, unsigned char byte, off_t size, off_t start);
static ino_t GetInode(int fd);
void Clear();
ino_t GetInode() const;
int OpenMirrorFile();
int NoCacheLoadAndPost(PseudoFdInfo* pseudo_obj, off_t start = 0, off_t size = 0); // size=0 means loading to end
PseudoFdInfo* CheckPseudoFdFlags(int fd, bool writable, AutoLock::Type locktype = AutoLock::NONE);
bool IsUploading(AutoLock::Type locktype = AutoLock::NONE);
bool SetAllStatus(bool is_loaded); // [NOTE] not locking
bool SetAllStatusUnloaded() { return SetAllStatus(false); }
int NoCachePreMultipartPost(PseudoFdInfo* pseudo_obj);
int NoCacheMultipartPost(PseudoFdInfo* pseudo_obj, int tgfd, off_t start, off_t size);
int NoCacheCompleteMultipartPost(PseudoFdInfo* pseudo_obj);
int RowFlushNoMultipart(const PseudoFdInfo* pseudo_obj, const char* tpath);
int RowFlushMultipart(PseudoFdInfo* pseudo_obj, const char* tpath);
int RowFlushMixMultipart(PseudoFdInfo* pseudo_obj, const char* tpath);
int RowFlushStreamMultipart(PseudoFdInfo* pseudo_obj, const char* tpath);
ssize_t WriteNoMultipart(const PseudoFdInfo* pseudo_obj, const char* bytes, off_t start, size_t size);
ssize_t WriteMultipart(PseudoFdInfo* pseudo_obj, const char* bytes, off_t start, size_t size);
ssize_t WriteMixMultipart(PseudoFdInfo* pseudo_obj, const char* bytes, off_t start, size_t size);
ssize_t WriteStreamUpload(PseudoFdInfo* pseudo_obj, const char* bytes, off_t start, size_t size);
bool ReserveDiskSpace(off_t size);
bool AddUntreated(off_t start, off_t size);
bool IsDirtyMetadata() const;
public:
static bool GetNoMixMultipart() { return mixmultipart; }
static bool SetNoMixMultipart();
static bool GetStreamUpload() { return streamupload; }
static bool SetStreamUpload(bool isstream);
explicit FdEntity(const char* tpath = nullptr, const char* cpath = nullptr);
~FdEntity();
FdEntity(const FdEntity&) = delete;
FdEntity(FdEntity&&) = delete;
FdEntity& operator=(const FdEntity&) = delete;
FdEntity& operator=(FdEntity&&) = delete;
void Close(int fd);
bool IsOpen() const { return (-1 != physical_fd); }
bool FindPseudoFd(int fd, AutoLock::Type locktype = AutoLock::NONE) const;
int Open(const headers_t* pmeta, off_t size, const struct timespec& ts_mctime, int flags, AutoLock::Type type);
bool LoadAll(int fd, headers_t* pmeta = nullptr, off_t* size = nullptr, bool force_load = false);
int Dup(int fd, AutoLock::Type locktype = AutoLock::NONE);
int OpenPseudoFd(int flags = O_RDONLY, AutoLock::Type locktype = AutoLock::NONE);
int GetOpenCount(AutoLock::Type locktype = AutoLock::NONE) const;
const std::string& GetPath() const { return path; }
bool RenamePath(const std::string& newpath, std::string& fentmapkey);
int GetPhysicalFd() const { return physical_fd; }
bool IsModified() const;
bool MergeOrgMeta(headers_t& updatemeta);
int UploadPending(int fd, AutoLock::Type type);
bool GetStats(struct stat& st, AutoLock::Type locktype = AutoLock::NONE) const;
int SetCtime(struct timespec time, AutoLock::Type locktype = AutoLock::NONE);
int SetAtime(struct timespec time, AutoLock::Type locktype = AutoLock::NONE);
int SetMCtime(struct timespec mtime, struct timespec ctime, AutoLock::Type locktype = AutoLock::NONE);
bool UpdateCtime();
bool UpdateAtime();
bool UpdateMtime(bool clear_holding_mtime = false);
bool UpdateMCtime();
bool SetHoldingMtime(struct timespec mtime, AutoLock::Type locktype = AutoLock::NONE);
bool ClearHoldingMtime(AutoLock::Type locktype = AutoLock::NONE);
bool GetSize(off_t& size) const;
bool GetXattr(std::string& xattr) const;
bool SetXattr(const std::string& xattr);
bool SetMode(mode_t mode);
bool SetUId(uid_t uid);
bool SetGId(gid_t gid);
bool SetContentType(const char* path);
int Load(off_t start, off_t size, AutoLock::Type type, bool is_modified_flag = false); // size=0 means loading to end
off_t BytesModified();
int RowFlush(int fd, const char* tpath, AutoLock::Type type, bool force_sync = false);
int Flush(int fd, AutoLock::Type type, bool force_sync = false) { return RowFlush(fd, nullptr, type, force_sync); }
ssize_t Read(int fd, char* bytes, off_t start, size_t size, bool force_load = false);
ssize_t Write(int fd, const char* bytes, off_t start, size_t size);
bool PunchHole(off_t start = 0, size_t size = 0);
void MarkDirtyNewFile();
bool IsDirtyNewFile() const;
void MarkDirtyMetadata();
bool GetLastUpdateUntreatedPart(off_t& start, off_t& size) const;
bool ReplaceLastUpdateUntreatedPart(off_t front_start, off_t front_size, off_t behind_start, off_t behind_size);
};
typedef std::map<std::string, std::unique_ptr<FdEntity>> fdent_map_t; // key=path, value=FdEntity*
#endif // S3FS_FDCACHE_ENTITY_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

1049
src/fdcache_fdinfo.cpp Normal file

File diff suppressed because it is too large Load Diff

133
src/fdcache_fdinfo.h Normal file
View File

@ -0,0 +1,133 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_FDCACHE_FDINFO_H_
#define S3FS_FDCACHE_FDINFO_H_
#include <memory>
#include "psemaphore.h"
#include "metaheader.h"
#include "autolock.h"
#include "types.h"
class FdEntity;
class UntreatedParts;
//------------------------------------------------
// Structure of parameters to pass to thread
//------------------------------------------------
class PseudoFdInfo;
struct pseudofdinfo_thparam
{
PseudoFdInfo* ppseudofdinfo;
std::string path;
std::string upload_id;
int upload_fd;
off_t start;
off_t size;
bool is_copy;
int part_num;
etagpair* petag;
pseudofdinfo_thparam() : ppseudofdinfo(nullptr), path(""), upload_id(""), upload_fd(-1), start(0), size(0), is_copy(false), part_num(-1), petag(nullptr) {}
};
//------------------------------------------------
// Class PseudoFdInfo
//------------------------------------------------
class PseudoFdInfo
{
private:
static int max_threads;
static int opt_max_threads; // for option value
int pseudo_fd;
int physical_fd;
int flags; // flags at open
std::string upload_id;
int upload_fd; // duplicated fd for uploading
filepart_list_t upload_list;
petagpool etag_entities; // list of etag string and part number entities(to maintain the etag entity even if MPPART_INFO is destroyed)
bool is_lock_init;
mutable pthread_mutex_t upload_list_lock; // protects upload_id and upload_list
Semaphore uploaded_sem; // use a semaphore to trigger an upload completion like event flag
int instruct_count; // number of instructions for processing by threads
int completed_count; // number of completed processes by thread
int last_result; // the result of thread processing
private:
static void* MultipartUploadThreadWorker(void* arg);
bool Clear();
void CloseUploadFd();
bool OpenUploadFd(AutoLock::Type type = AutoLock::NONE);
bool ResetUploadInfo(AutoLock::Type type);
bool RowInitialUploadInfo(const std::string& id, bool is_cancel_mp, AutoLock::Type type);
bool CompleteInstruction(int result, AutoLock::Type type = AutoLock::NONE);
bool ParallelMultipartUpload(const char* path, const mp_part_list_t& mplist, bool is_copy, AutoLock::Type type = AutoLock::NONE);
bool InsertUploadPart(off_t start, off_t size, int part_num, bool is_copy, etagpair** ppetag, AutoLock::Type type = AutoLock::NONE);
bool CancelAllThreads();
bool ExtractUploadPartsFromUntreatedArea(const off_t& untreated_start, const off_t& untreated_size, mp_part_list_t& to_upload_list, filepart_list_t& cancel_upload_list, off_t max_mp_size);
public:
explicit PseudoFdInfo(int fd = -1, int open_flags = 0);
~PseudoFdInfo();
PseudoFdInfo(const PseudoFdInfo&) = delete;
PseudoFdInfo(PseudoFdInfo&&) = delete;
PseudoFdInfo& operator=(const PseudoFdInfo&) = delete;
PseudoFdInfo& operator=(PseudoFdInfo&&) = delete;
int GetPhysicalFd() const { return physical_fd; }
int GetPseudoFd() const { return pseudo_fd; }
int GetFlags() const { return flags; }
bool Writable() const;
bool Readable() const;
bool Set(int fd, int open_flags);
bool ClearUploadInfo(bool is_cancel_mp = false);
bool InitialUploadInfo(const std::string& id){ return RowInitialUploadInfo(id, true, AutoLock::NONE); }
bool IsUploading() const { return !upload_id.empty(); }
bool GetUploadId(std::string& id) const;
bool GetEtaglist(etaglist_t& list) const;
bool AppendUploadPart(off_t start, off_t size, bool is_copy = false, etagpair** ppetag = nullptr);
bool ParallelMultipartUploadAll(const char* path, const mp_part_list_t& to_upload_list, const mp_part_list_t& copy_list, int& result);
int WaitAllThreadsExit();
ssize_t UploadBoundaryLastUntreatedArea(const char* path, headers_t& meta, FdEntity* pfdent);
bool ExtractUploadPartsFromAllArea(UntreatedParts& untreated_list, mp_part_list_t& to_upload_list, mp_part_list_t& to_copy_list, mp_part_list_t& to_download_list, filepart_list_t& cancel_upload_list, bool& wait_upload_complete, off_t max_mp_size, off_t file_size, bool use_copy);
};
typedef std::map<int, std::unique_ptr<PseudoFdInfo>> fdinfo_map_t;
#endif // S3FS_FDCACHE_FDINFO_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

1035
src/fdcache_page.cpp Normal file

File diff suppressed because it is too large Load Diff

136
src/fdcache_page.h Normal file
View File

@ -0,0 +1,136 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_FDCACHE_PAGE_H_
#define S3FS_FDCACHE_PAGE_H_
#include <sys/types.h>
#include <vector>
//------------------------------------------------
// Symbols
//------------------------------------------------
// [NOTE]
// If the following symbols in lseek whence are undefined, define them.
// If it is not supported by lseek, s3fs judges by the processing result of lseek.
//
#ifndef SEEK_DATA
#define SEEK_DATA 3
#endif
#ifndef SEEK_HOLE
#define SEEK_HOLE 4
#endif
//------------------------------------------------
// Structure fdpage
//------------------------------------------------
// page block information
struct fdpage
{
off_t offset;
off_t bytes;
bool loaded;
bool modified;
explicit fdpage(off_t start = 0, off_t size = 0, bool is_loaded = false, bool is_modified = false) :
offset(start), bytes(size), loaded(is_loaded), modified(is_modified) {}
off_t next() const
{
return (offset + bytes);
}
off_t end() const
{
return (0 < bytes ? offset + bytes - 1 : 0);
}
};
typedef std::vector<struct fdpage> fdpage_list_t;
//------------------------------------------------
// Class PageList
//------------------------------------------------
class CacheFileStat;
class FdEntity;
// cppcheck-suppress copyCtorAndEqOperator
class PageList
{
friend class FdEntity; // only one method access directly pages.
private:
fdpage_list_t pages;
bool is_shrink; // [NOTE] true if it has been shrinked even once
public:
enum class page_status{
NOT_LOAD_MODIFIED = 0,
LOADED,
MODIFIED,
LOAD_MODIFIED
};
private:
static bool GetSparseFilePages(int fd, size_t file_size, fdpage_list_t& sparse_list);
static bool CheckZeroAreaInFile(int fd, off_t start, size_t bytes);
static bool CheckAreaInSparseFile(const struct fdpage& checkpage, const fdpage_list_t& sparse_list, int fd, fdpage_list_t& err_area_list, fdpage_list_t& warn_area_list);
void Clear();
bool Parse(off_t new_pos);
public:
static void FreeList(fdpage_list_t& list);
explicit PageList(off_t size = 0, bool is_loaded = false, bool is_modified = false, bool shrinked = false);
PageList(const PageList&) = delete;
PageList& operator=(const PageList&) = delete;
~PageList();
bool Init(off_t size, bool is_loaded, bool is_modified);
off_t Size() const;
bool Resize(off_t size, bool is_loaded, bool is_modified);
bool IsPageLoaded(off_t start = 0, off_t size = 0) const; // size=0 is checking to end of list
bool SetPageLoadedStatus(off_t start, off_t size, PageList::page_status pstatus = page_status::LOADED, bool is_compress = true);
bool FindUnloadedPage(off_t start, off_t& resstart, off_t& ressize) const;
off_t GetTotalUnloadedPageSize(off_t start = 0, off_t size = 0, off_t limit_size = 0) const; // size=0 is checking to end of list
size_t GetUnloadedPages(fdpage_list_t& unloaded_list, off_t start = 0, off_t size = 0) const; // size=0 is checking to end of list
bool GetPageListsForMultipartUpload(fdpage_list_t& dlpages, fdpage_list_t& mixuppages, off_t max_partsize);
bool GetNoDataPageLists(fdpage_list_t& nodata_pages, off_t start = 0, size_t size = 0);
off_t BytesModified() const;
bool IsModified() const;
bool ClearAllModified();
bool Compress();
bool Serialize(CacheFileStat& file, bool is_output, ino_t inode);
void Dump() const;
bool CompareSparseFile(int fd, size_t file_size, fdpage_list_t& err_area_list, fdpage_list_t& warn_area_list);
};
#endif // S3FS_FDCACHE_PAGE_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

133
src/fdcache_pseudofd.cpp Normal file
View File

@ -0,0 +1,133 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <algorithm>
#include <cstdlib>
#include <vector>
#include "s3fs_logger.h"
#include "fdcache_pseudofd.h"
#include "autolock.h"
//------------------------------------------------
// Symbols
//------------------------------------------------
// [NOTE]
// The minimum pseudo fd value starts 2.
// This is to avoid mistakes for 0(stdout) and 1(stderr), which are usually used.
//
static constexpr int MIN_PSEUDOFD_NUMBER = 2;
//------------------------------------------------
// PseudoFdManager class methods
//------------------------------------------------
PseudoFdManager& PseudoFdManager::GetManager()
{
static PseudoFdManager singleton;
return singleton;
}
int PseudoFdManager::Get()
{
return (PseudoFdManager::GetManager()).CreatePseudoFd();
}
bool PseudoFdManager::Release(int fd)
{
return (PseudoFdManager::GetManager()).ReleasePseudoFd(fd);
}
//------------------------------------------------
// PseudoFdManager methods
//------------------------------------------------
PseudoFdManager::PseudoFdManager() : is_lock_init(false)
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
#if S3FS_PTHREAD_ERRORCHECK
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
#endif
int result;
if(0 != (result = pthread_mutex_init(&pseudofd_list_lock, &attr))){
S3FS_PRN_CRIT("failed to init pseudofd_list_lock: %d", result);
abort();
}
is_lock_init = true;
}
PseudoFdManager::~PseudoFdManager()
{
if(is_lock_init){
int result;
if(0 != (result = pthread_mutex_destroy(&pseudofd_list_lock))){
S3FS_PRN_CRIT("failed to destroy pseudofd_list_lock: %d", result);
abort();
}
is_lock_init = false;
}
}
int PseudoFdManager::GetUnusedMinPseudoFd() const
{
int min_fd = MIN_PSEUDOFD_NUMBER;
// Look for the first discontinuous value.
for(pseudofd_list_t::const_iterator iter = pseudofd_list.begin(); iter != pseudofd_list.end(); ++iter){
if(min_fd == (*iter)){
++min_fd;
}else if(min_fd < (*iter)){
break;
}
}
return min_fd;
}
int PseudoFdManager::CreatePseudoFd()
{
AutoLock auto_lock(&pseudofd_list_lock);
int new_fd = PseudoFdManager::GetUnusedMinPseudoFd();
pseudofd_list.push_back(new_fd);
std::sort(pseudofd_list.begin(), pseudofd_list.end());
return new_fd;
}
bool PseudoFdManager::ReleasePseudoFd(int fd)
{
AutoLock auto_lock(&pseudofd_list_lock);
for(pseudofd_list_t::iterator iter = pseudofd_list.begin(); iter != pseudofd_list.end(); ++iter){
if(fd == (*iter)){
pseudofd_list.erase(iter);
return true;
}
}
return false;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

71
src/fdcache_pseudofd.h Normal file
View File

@ -0,0 +1,71 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_FDCACHE_PSEUDOFD_H_
#define S3FS_FDCACHE_PSEUDOFD_H_
#include <vector>
//------------------------------------------------
// Typdefs
//------------------------------------------------
// List of pseudo fd in use
//
typedef std::vector<int> pseudofd_list_t;
//------------------------------------------------
// Class PseudoFdManager
//------------------------------------------------
class PseudoFdManager
{
private:
pseudofd_list_t pseudofd_list;
bool is_lock_init;
pthread_mutex_t pseudofd_list_lock; // protects pseudofd_list
private:
static PseudoFdManager& GetManager();
PseudoFdManager();
~PseudoFdManager();
PseudoFdManager(const PseudoFdManager&) = delete;
PseudoFdManager(PseudoFdManager&&) = delete;
PseudoFdManager& operator=(const PseudoFdManager&) = delete;
PseudoFdManager& operator=(PseudoFdManager&&) = delete;
int GetUnusedMinPseudoFd() const;
int CreatePseudoFd();
bool ReleasePseudoFd(int fd);
public:
static int Get();
static bool Release(int fd);
};
#endif // S3FS_FDCACHE_PSEUDOFD_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

282
src/fdcache_stat.cpp Normal file
View File

@ -0,0 +1,282 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cerrno>
#include <unistd.h>
#include <sys/file.h>
#include <sys/stat.h>
#include "s3fs_logger.h"
#include "fdcache_stat.h"
#include "fdcache.h"
#include "s3fs_util.h"
#include "s3fs_cred.h"
#include "string_util.h"
//------------------------------------------------
// CacheFileStat class methods
//------------------------------------------------
std::string CacheFileStat::GetCacheFileStatTopDir()
{
std::string top_path;
if(!FdManager::IsCacheDir() || S3fsCred::GetBucket().empty()){
return top_path;
}
// stat top dir( "/<cache_dir>/.<bucket_name>.stat" )
top_path += FdManager::GetCacheDir();
top_path += "/.";
top_path += S3fsCred::GetBucket();
top_path += ".stat";
return top_path;
}
int CacheFileStat::MakeCacheFileStatPath(const char* path, std::string& sfile_path, bool is_create_dir)
{
std::string top_path = CacheFileStat::GetCacheFileStatTopDir();
if(top_path.empty()){
S3FS_PRN_ERR("The path to cache top dir is empty.");
return -EIO;
}
if(is_create_dir){
int result;
if(0 != (result = mkdirp(top_path + mydirname(path), 0777))){
S3FS_PRN_ERR("failed to create dir(%s) by errno(%d).", path, result);
return result;
}
}
if(!path || '\0' == path[0]){
sfile_path = top_path;
}else{
sfile_path = top_path + SAFESTRPTR(path);
}
return 0;
}
bool CacheFileStat::CheckCacheFileStatTopDir()
{
std::string top_path = CacheFileStat::GetCacheFileStatTopDir();
if(top_path.empty()){
S3FS_PRN_INFO("The path to cache top dir is empty, thus not need to check permission.");
return true;
}
return check_exist_dir_permission(top_path.c_str());
}
int CacheFileStat::DeleteCacheFileStat(const char* path)
{
if(!path || '\0' == path[0]){
return -EINVAL;
}
// stat path
std::string sfile_path;
int result;
if(0 != (result = CacheFileStat::MakeCacheFileStatPath(path, sfile_path, false))){
S3FS_PRN_ERR("failed to create cache stat file path(%s)", path);
return result;
}
if(0 != unlink(sfile_path.c_str())){
result = -errno;
if(-ENOENT == result){
S3FS_PRN_DBG("failed to delete file(%s): errno=%d", path, result);
}else{
S3FS_PRN_ERR("failed to delete file(%s): errno=%d", path, result);
}
return result;
}
return 0;
}
// [NOTE]
// If remove stat file directory, it should do before removing
// file cache directory.
//
bool CacheFileStat::DeleteCacheFileStatDirectory()
{
std::string top_path = CacheFileStat::GetCacheFileStatTopDir();
if(top_path.empty()){
S3FS_PRN_INFO("The path to cache top dir is empty, thus not need to remove it.");
return true;
}
return delete_files_in_dir(top_path.c_str(), true);
}
bool CacheFileStat::RenameCacheFileStat(const char* oldpath, const char* newpath)
{
if(!oldpath || '\0' == oldpath[0] || !newpath || '\0' == newpath[0]){
return false;
}
// stat path
std::string old_filestat;
std::string new_filestat;
if(0 != CacheFileStat::MakeCacheFileStatPath(oldpath, old_filestat, false) || 0 != CacheFileStat::MakeCacheFileStatPath(newpath, new_filestat, false)){
return false;
}
// check new stat path
struct stat st;
if(0 == stat(new_filestat.c_str(), &st)){
// new stat path is existed, then unlink it.
if(-1 == unlink(new_filestat.c_str())){
S3FS_PRN_ERR("failed to unlink new cache file stat path(%s) by errno(%d).", new_filestat.c_str(), errno);
return false;
}
}
// check old stat path
if(0 != stat(old_filestat.c_str(), &st)){
// old stat path is not existed, then nothing to do any more.
return true;
}
// link and unlink
if(-1 == link(old_filestat.c_str(), new_filestat.c_str())){
S3FS_PRN_ERR("failed to link old cache file stat path(%s) to new cache file stat path(%s) by errno(%d).", old_filestat.c_str(), new_filestat.c_str(), errno);
return false;
}
if(-1 == unlink(old_filestat.c_str())){
S3FS_PRN_ERR("failed to unlink old cache file stat path(%s) by errno(%d).", old_filestat.c_str(), errno);
return false;
}
return true;
}
//------------------------------------------------
// CacheFileStat methods
//------------------------------------------------
CacheFileStat::CacheFileStat(const char* tpath) : fd(-1)
{
if(tpath && '\0' != tpath[0]){
SetPath(tpath, true);
}
}
CacheFileStat::~CacheFileStat()
{
Release();
}
bool CacheFileStat::SetPath(const char* tpath, bool is_open)
{
if(!tpath || '\0' == tpath[0]){
return false;
}
if(!Release()){
// could not close old stat file.
return false;
}
path = tpath;
if(!is_open){
return true;
}
return Open();
}
bool CacheFileStat::RawOpen(bool readonly)
{
if(path.empty()){
return false;
}
if(-1 != fd){
// already opened
return true;
}
// stat path
std::string sfile_path;
if(0 != CacheFileStat::MakeCacheFileStatPath(path.c_str(), sfile_path, true)){
S3FS_PRN_ERR("failed to create cache stat file path(%s)", path.c_str());
return false;
}
// open
int tmpfd;
if(readonly){
if(-1 == (tmpfd = open(sfile_path.c_str(), O_RDONLY))){
S3FS_PRN_ERR("failed to read only open cache stat file path(%s) - errno(%d)", path.c_str(), errno);
return false;
}
}else{
if(-1 == (tmpfd = open(sfile_path.c_str(), O_CREAT|O_RDWR, 0600))){
S3FS_PRN_ERR("failed to open cache stat file path(%s) - errno(%d)", path.c_str(), errno);
return false;
}
}
scope_guard guard([&]() { close(tmpfd); });
// lock
if(-1 == flock(tmpfd, LOCK_EX)){
S3FS_PRN_ERR("failed to lock cache stat file(%s) - errno(%d)", path.c_str(), errno);
return false;
}
// seek top
if(0 != lseek(tmpfd, 0, SEEK_SET)){
S3FS_PRN_ERR("failed to lseek cache stat file(%s) - errno(%d)", path.c_str(), errno);
flock(tmpfd, LOCK_UN);
return false;
}
S3FS_PRN_DBG("file locked(%s - %s)", path.c_str(), sfile_path.c_str());
guard.dismiss();
fd = tmpfd;
return true;
}
bool CacheFileStat::Open()
{
return RawOpen(false);
}
bool CacheFileStat::ReadOnlyOpen()
{
return RawOpen(true);
}
bool CacheFileStat::Release()
{
if(-1 == fd){
// already release
return true;
}
// unlock
if(-1 == flock(fd, LOCK_UN)){
S3FS_PRN_ERR("failed to unlock cache stat file(%s) - errno(%d)", path.c_str(), errno);
return false;
}
S3FS_PRN_DBG("file unlocked(%s)", path.c_str());
if(-1 == close(fd)){
S3FS_PRN_ERR("failed to close cache stat file(%s) - errno(%d)", path.c_str(), errno);
return false;
}
fd = -1;
return true;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

66
src/fdcache_stat.h Normal file
View File

@ -0,0 +1,66 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_FDCACHE_STAT_H_
#define S3FS_FDCACHE_STAT_H_
#include <string>
//------------------------------------------------
// CacheFileStat
//------------------------------------------------
class CacheFileStat
{
private:
std::string path;
int fd;
private:
static int MakeCacheFileStatPath(const char* path, std::string& sfile_path, bool is_create_dir = true);
bool RawOpen(bool readonly);
public:
static std::string GetCacheFileStatTopDir();
static int DeleteCacheFileStat(const char* path);
static bool CheckCacheFileStatTopDir();
static bool DeleteCacheFileStatDirectory();
static bool RenameCacheFileStat(const char* oldpath, const char* newpath);
explicit CacheFileStat(const char* tpath = nullptr);
~CacheFileStat();
bool Open();
bool ReadOnlyOpen();
bool Release();
bool SetPath(const char* tpath, bool is_open = true);
int GetFd() const { return fd; }
};
#endif // S3FS_FDCACHE_STAT_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

277
src/fdcache_untreated.cpp Normal file
View File

@ -0,0 +1,277 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cstdlib>
#include "s3fs_logger.h"
#include "fdcache_untreated.h"
#include "autolock.h"
//------------------------------------------------
// UntreatedParts methods
//------------------------------------------------
UntreatedParts::UntreatedParts() : last_tag(0) //, is_lock_init(false)
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
#if S3FS_PTHREAD_ERRORCHECK
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
#endif
int result;
if(0 != (result = pthread_mutex_init(&untreated_list_lock, &attr))){
S3FS_PRN_CRIT("failed to init untreated_list_lock: %d", result);
abort();
}
is_lock_init = true;
}
UntreatedParts::~UntreatedParts()
{
if(is_lock_init){
int result;
if(0 != (result = pthread_mutex_destroy(&untreated_list_lock))){
S3FS_PRN_CRIT("failed to destroy untreated_list_lock: %d", result);
abort();
}
is_lock_init = false;
}
}
bool UntreatedParts::empty()
{
AutoLock auto_lock(&untreated_list_lock);
return untreated_list.empty();
}
bool UntreatedParts::AddPart(off_t start, off_t size)
{
if(start < 0 || size <= 0){
S3FS_PRN_ERR("Paramter are wrong(start=%lld, size=%lld).", static_cast<long long int>(start), static_cast<long long int>(size));
return false;
}
AutoLock auto_lock(&untreated_list_lock);
++last_tag;
// Check the overlap with the existing part and add the part.
for(untreated_list_t::iterator iter = untreated_list.begin(); iter != untreated_list.end(); ++iter){
if(iter->stretch(start, size, last_tag)){
// the part was stretched, thus check if it overlaps with next parts
untreated_list_t::iterator niter = iter;
for(++niter; niter != untreated_list.end(); ){
if(!iter->stretch(niter->start, niter->size, last_tag)){
// This next part does not overlap with the current part
break;
}
// Since the parts overlap and the current part is stretched, delete this next part.
niter = untreated_list.erase(niter);
}
// success to stretch and compress existed parts
return true;
}else if((start + size) < iter->start){
// The part to add should be inserted before the current part.
untreated_list.insert(iter, untreatedpart(start, size, last_tag));
// success to stretch and compress existed parts
return true;
}
}
// There are no overlapping parts in the untreated_list, then add the part at end of list
untreated_list.emplace_back(start, size, last_tag);
return true;
}
bool UntreatedParts::RowGetPart(off_t& start, off_t& size, off_t max_size, off_t min_size, bool lastpart) const
{
if(max_size <= 0 || min_size < 0 || max_size < min_size){
S3FS_PRN_ERR("Paramter are wrong(max_size=%lld, min_size=%lld).", static_cast<long long int>(max_size), static_cast<long long int>(min_size));
return false;
}
AutoLock auto_lock(&untreated_list_lock);
// Check the overlap with the existing part and add the part.
for(untreated_list_t::const_iterator iter = untreated_list.begin(); iter != untreated_list.end(); ++iter){
if(!lastpart || iter->untreated_tag == last_tag){
if(min_size <= iter->size){
if(iter->size <= max_size){
// whole part( min <= part size <= max )
start = iter->start;
size = iter->size;
}else{
// Partially take out part( max < part size )
start = iter->start;
size = max_size;
}
return true;
}else{
if(lastpart){
return false;
}
}
}
}
return false;
}
// [NOTE]
// If size is specified as 0, all areas(parts) after start will be deleted.
//
bool UntreatedParts::ClearParts(off_t start, off_t size)
{
if(start < 0 || size < 0){
S3FS_PRN_ERR("Paramter are wrong(start=%lld, size=%lld).", static_cast<long long int>(start), static_cast<long long int>(size));
return false;
}
AutoLock auto_lock(&untreated_list_lock);
if(untreated_list.empty()){
return true;
}
// Check the overlap with the existing part.
for(untreated_list_t::iterator iter = untreated_list.begin(); iter != untreated_list.end(); ){
if(0 != size && (start + size) <= iter->start){
// clear area is in front of iter area, no more to do.
break;
}else if(start <= iter->start){
if(0 != size && (start + size) <= (iter->start + iter->size)){
// clear area overlaps with iter area(on the start side)
iter->size = (iter->start + iter->size) - (start + size);
iter->start = start + size;
if(0 == iter->size){
iter = untreated_list.erase(iter);
}
}else{
// clear area overlaps with all of iter area
iter = untreated_list.erase(iter);
}
}else if(start < (iter->start + iter->size)){
// clear area overlaps with iter area(on the end side)
if(0 == size || (iter->start + iter->size) <= (start + size)){
// start to iter->end is clear
iter->size = start - iter->start;
}else{
// parse current part
iter->size = start - iter->start;
// add new part
off_t next_start = start + size;
off_t next_size = (iter->start + iter->size) - (start + size);
long next_tag = iter->untreated_tag;
++iter;
iter = untreated_list.insert(iter, untreatedpart(next_start, next_size, next_tag));
++iter;
}
}else{
// clear area is in behind of iter area
++iter;
}
}
return true;
}
//
// Update the last updated Untreated part
//
bool UntreatedParts::GetLastUpdatePart(off_t& start, off_t& size) const
{
AutoLock auto_lock(&untreated_list_lock);
for(untreated_list_t::const_iterator iter = untreated_list.begin(); iter != untreated_list.end(); ++iter){
if(iter->untreated_tag == last_tag){
start = iter->start;
size = iter->size;
return true;
}
}
return false;
}
//
// Replaces the last updated Untreated part.
//
// [NOTE]
// If size <= 0, delete that part
//
bool UntreatedParts::ReplaceLastUpdatePart(off_t start, off_t size)
{
AutoLock auto_lock(&untreated_list_lock);
for(untreated_list_t::iterator iter = untreated_list.begin(); iter != untreated_list.end(); ++iter){
if(iter->untreated_tag == last_tag){
if(0 < size){
iter->start = start;
iter->size = size;
}else{
iter = untreated_list.erase(iter);
}
return true;
}
}
return false;
}
//
// Remove the last updated Untreated part.
//
bool UntreatedParts::RemoveLastUpdatePart()
{
AutoLock auto_lock(&untreated_list_lock);
for(untreated_list_t::iterator iter = untreated_list.begin(); iter != untreated_list.end(); ++iter){
if(iter->untreated_tag == last_tag){
untreated_list.erase(iter);
return true;
}
}
return false;
}
//
// Duplicate the internally untreated_list.
//
bool UntreatedParts::Duplicate(untreated_list_t& list)
{
AutoLock auto_lock(&untreated_list_lock);
list = untreated_list;
return true;
}
void UntreatedParts::Dump()
{
AutoLock auto_lock(&untreated_list_lock);
S3FS_PRN_DBG("untreated list = [");
for(untreated_list_t::const_iterator iter = untreated_list.begin(); iter != untreated_list.end(); ++iter){
S3FS_PRN_DBG(" {%014lld - %014lld : tag=%ld}", static_cast<long long int>(iter->start), static_cast<long long int>(iter->size), iter->untreated_tag);
}
S3FS_PRN_DBG("]");
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

76
src/fdcache_untreated.h Normal file
View File

@ -0,0 +1,76 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_FDCACHE_UNTREATED_H_
#define S3FS_FDCACHE_UNTREATED_H_
#include "common.h"
#include "types.h"
//------------------------------------------------
// Class UntreatedParts
//------------------------------------------------
class UntreatedParts
{
private:
mutable pthread_mutex_t untreated_list_lock; // protects untreated_list
bool is_lock_init;
untreated_list_t untreated_list;
long last_tag; // [NOTE] Use this to identify the latest updated part.
private:
bool RowGetPart(off_t& start, off_t& size, off_t max_size, off_t min_size, bool lastpart) const;
public:
UntreatedParts();
~UntreatedParts();
UntreatedParts(const UntreatedParts&) = delete;
UntreatedParts(UntreatedParts&&) = delete;
UntreatedParts& operator=(const UntreatedParts&) = delete;
UntreatedParts& operator=(UntreatedParts&&) = delete;
bool empty();
bool AddPart(off_t start, off_t size);
bool GetLastUpdatedPart(off_t& start, off_t& size, off_t max_size, off_t min_size = MIN_MULTIPART_SIZE) const { return RowGetPart(start, size, max_size, min_size, true); }
bool ClearParts(off_t start, off_t size);
bool ClearAll() { return ClearParts(0, 0); }
bool GetLastUpdatePart(off_t& start, off_t& size) const;
bool ReplaceLastUpdatePart(off_t start, off_t size);
bool RemoveLastUpdatePart();
bool Duplicate(untreated_list_t& list);
void Dump();
};
#endif // S3FS_FDCACHE_UNTREATED_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

View File

@ -18,19 +18,19 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <pthread.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <gcrypt.h>
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#ifdef USE_GNUTLS_NETTLE
#ifdef USE_GNUTLS_NETTLE
#include <nettle/md5.h>
#include <nettle/sha1.h>
#include <nettle/hmac.h>
@ -39,53 +39,53 @@
#include <map>
#include "common.h"
#include "s3fs.h"
#include "s3fs_auth.h"
using namespace std;
#include "s3fs_logger.h"
//-------------------------------------------------------------------
// Utility Function for version
//-------------------------------------------------------------------
#ifdef USE_GNUTLS_NETTLE
#ifdef USE_GNUTLS_NETTLE
const char* s3fs_crypt_lib_name(void)
{
static const char version[] = "GnuTLS(nettle)";
static constexpr char version[] = "GnuTLS(nettle)";
return version;
return version;
}
#else // USE_GNUTLS_NETTLE
#else // USE_GNUTLS_NETTLE
const char* s3fs_crypt_lib_name()
{
static const char version[] = "GnuTLS(gcrypt)";
static constexpr char version[] = "GnuTLS(gcrypt)";
return version;
return version;
}
#endif // USE_GNUTLS_NETTLE
#endif // USE_GNUTLS_NETTLE
//-------------------------------------------------------------------
// Utility Function for global init
//-------------------------------------------------------------------
bool s3fs_init_global_ssl()
{
if(GNUTLS_E_SUCCESS != gnutls_global_init()){
return false;
}
if(GNUTLS_E_SUCCESS != gnutls_global_init()){
return false;
}
#ifndef USE_GNUTLS_NETTLE
if(NULL == gcry_check_version(NULL)){
return false;
}
#endif // USE_GNUTLS_NETTLE
return true;
if(nullptr == gcry_check_version(nullptr)){
return false;
}
#endif // USE_GNUTLS_NETTLE
return true;
}
bool s3fs_destroy_global_ssl()
{
gnutls_global_deinit();
return true;
gnutls_global_deinit();
return true;
}
//-------------------------------------------------------------------
@ -93,304 +93,303 @@ bool s3fs_destroy_global_ssl()
//-------------------------------------------------------------------
bool s3fs_init_crypt_mutex()
{
return true;
return true;
}
bool s3fs_destroy_crypt_mutex()
{
return true;
return true;
}
//-------------------------------------------------------------------
// Utility Function for HMAC
//-------------------------------------------------------------------
#ifdef USE_GNUTLS_NETTLE
#ifdef USE_GNUTLS_NETTLE
bool s3fs_HMAC(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned char** digest, unsigned int* digestlen)
std::unique_ptr<unsigned char[]> s3fs_HMAC(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned int* digestlen)
{
if(!key || !data || !digest || !digestlen){
return false;
}
if(!key || !data || !digestlen){
return nullptr;
}
*digest = new unsigned char[SHA1_DIGEST_SIZE];
std::unique_ptr<unsigned char[]> digest(new unsigned char[SHA1_DIGEST_SIZE]);
struct hmac_sha1_ctx ctx_hmac;
hmac_sha1_set_key(&ctx_hmac, keylen, reinterpret_cast<const uint8_t*>(key));
hmac_sha1_update(&ctx_hmac, datalen, reinterpret_cast<const uint8_t*>(data));
hmac_sha1_digest(&ctx_hmac, SHA1_DIGEST_SIZE, reinterpret_cast<uint8_t*>(*digest));
*digestlen = SHA1_DIGEST_SIZE;
struct hmac_sha1_ctx ctx_hmac;
hmac_sha1_set_key(&ctx_hmac, keylen, reinterpret_cast<const uint8_t*>(key));
hmac_sha1_update(&ctx_hmac, datalen, reinterpret_cast<const uint8_t*>(data));
hmac_sha1_digest(&ctx_hmac, SHA1_DIGEST_SIZE, reinterpret_cast<uint8_t*>(digest.get()));
*digestlen = SHA1_DIGEST_SIZE;
return true;
return digest;
}
bool s3fs_HMAC256(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned char** digest, unsigned int* digestlen)
std::unique_ptr<unsigned char[]> s3fs_HMAC256(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned int* digestlen)
{
if(!key || !data || !digest || !digestlen){
return false;
}
if(!key || !data || !digestlen){
return nullptr;
}
*digest = new unsigned char[SHA256_DIGEST_SIZE];
std::unique_ptr<unsigned char[]> digest(new unsigned char[SHA256_DIGEST_SIZE]);
struct hmac_sha256_ctx ctx_hmac;
hmac_sha256_set_key(&ctx_hmac, keylen, reinterpret_cast<const uint8_t*>(key));
hmac_sha256_update(&ctx_hmac, datalen, reinterpret_cast<const uint8_t*>(data));
hmac_sha256_digest(&ctx_hmac, SHA256_DIGEST_SIZE, reinterpret_cast<uint8_t*>(*digest));
*digestlen = SHA256_DIGEST_SIZE;
struct hmac_sha256_ctx ctx_hmac;
hmac_sha256_set_key(&ctx_hmac, keylen, reinterpret_cast<const uint8_t*>(key));
hmac_sha256_update(&ctx_hmac, datalen, reinterpret_cast<const uint8_t*>(data));
hmac_sha256_digest(&ctx_hmac, SHA256_DIGEST_SIZE, reinterpret_cast<uint8_t*>(digest.get()));
*digestlen = SHA256_DIGEST_SIZE;
return true;
return digest;
}
#else // USE_GNUTLS_NETTLE
#else // USE_GNUTLS_NETTLE
bool s3fs_HMAC(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned char** digest, unsigned int* digestlen)
std::unique_ptr<unsigned char[]> s3fs_HMAC(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned int* digestlen)
{
if(!key || !data || !digest || !digestlen){
return false;
}
if(!key || !data || !digestlen){
return nullptr;
}
if(0 == (*digestlen = gnutls_hmac_get_len(GNUTLS_MAC_SHA1))){
return false;
}
*digest = new unsigned char[*digestlen + 1];
if(0 > gnutls_hmac_fast(GNUTLS_MAC_SHA1, key, keylen, data, datalen, *digest)){
delete[] *digest;
*digest = NULL;
return false;
}
return true;
if(0 == (*digestlen = gnutls_hmac_get_len(GNUTLS_MAC_SHA1))){
return nullptr;
}
std::unique_ptr<unsigned char[]> digest(new unsigned char[*digestlen + 1]);
if(0 > gnutls_hmac_fast(GNUTLS_MAC_SHA1, key, keylen, data, datalen, digest.get())){
return nullptr;
}
return digest;
}
bool s3fs_HMAC256(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned char** digest, unsigned int* digestlen)
std::unique_ptr<unsigned char[]> s3fs_HMAC256(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned int* digestlen)
{
if(!key || !data || !digest || !digestlen){
return false;
}
if(!key || !data || !digestlen){
return nullptr;
}
if(0 == (*digestlen = gnutls_hmac_get_len(GNUTLS_MAC_SHA256))){
return false;
}
*digest = new unsigned char[*digestlen + 1];
if(0 > gnutls_hmac_fast(GNUTLS_MAC_SHA256, key, keylen, data, datalen, *digest)){
delete[] *digest;
*digest = NULL;
return false;
}
return true;
if(0 == (*digestlen = gnutls_hmac_get_len(GNUTLS_MAC_SHA256))){
return nullptr;
}
std::unique_ptr<unsigned char[]> digest(new unsigned char[*digestlen + 1]);
if(0 > gnutls_hmac_fast(GNUTLS_MAC_SHA256, key, keylen, data, datalen, digest.get())){
return nullptr;
}
return digest;
}
#endif // USE_GNUTLS_NETTLE
#endif // USE_GNUTLS_NETTLE
//-------------------------------------------------------------------
// Utility Function for MD5
//-------------------------------------------------------------------
size_t get_md5_digest_length()
#ifdef USE_GNUTLS_NETTLE
bool s3fs_md5(const unsigned char* data, size_t datalen, md5_t* result)
{
return 16;
struct md5_ctx ctx_md5;
md5_init(&ctx_md5);
md5_update(&ctx_md5, datalen, data);
md5_digest(&ctx_md5, result->size(), result->data());
return true;
}
#ifdef USE_GNUTLS_NETTLE
unsigned char* s3fs_md5hexsum(int fd, off_t start, ssize_t size)
bool s3fs_md5_fd(int fd, off_t start, off_t size, md5_t* result)
{
struct md5_ctx ctx_md5;
unsigned char buf[512];
ssize_t bytes;
unsigned char* result;
struct md5_ctx ctx_md5;
off_t bytes;
memset(buf, 0, 512);
md5_init(&ctx_md5);
for(ssize_t total = 0; total < size; total += bytes){
bytes = 512 < (size - total) ? 512 : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
return NULL;
if(-1 == size){
struct stat st;
if(-1 == fstat(fd, &st)){
return false;
}
size = st.st_size;
}
md5_update(&ctx_md5, bytes, buf);
memset(buf, 0, 512);
}
result = new unsigned char[get_md5_digest_length()];
md5_digest(&ctx_md5, get_md5_digest_length(), result);
return result;
md5_init(&ctx_md5);
for(off_t total = 0; total < size; total += bytes){
off_t len = 512;
unsigned char buf[len];
bytes = len < (size - total) ? len : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
return false;
}
md5_update(&ctx_md5, bytes, buf);
}
md5_digest(&ctx_md5, result->size(), result->data());
return true;
}
#else // USE_GNUTLS_NETTLE
#else // USE_GNUTLS_NETTLE
unsigned char* s3fs_md5hexsum(int fd, off_t start, ssize_t size)
bool s3fs_md5(const unsigned char* data, size_t datalen, md5_t* digest)
{
gcry_md_hd_t ctx_md5;
gcry_error_t err;
char buf[512];
ssize_t bytes;
unsigned char* result;
if(-1 == size){
struct stat st;
if(-1 == fstat(fd, &st)){
return NULL;
gcry_md_hd_t ctx_md5;
gcry_error_t err;
if(GPG_ERR_NO_ERROR != (err = gcry_md_open(&ctx_md5, GCRY_MD_MD5, 0))){
S3FS_PRN_ERR("MD5 context creation failure: %s/%s", gcry_strsource(err), gcry_strerror(err));
return false;
}
size = static_cast<ssize_t>(st.st_size);
}
gcry_md_write(ctx_md5, digest->data(), digest->size());
gcry_md_close(ctx_md5);
memset(buf, 0, 512);
if(GPG_ERR_NO_ERROR != (err = gcry_md_open(&ctx_md5, GCRY_MD_MD5, 0))){
S3FS_PRN_ERR("MD5 context creation failure: %s/%s", gcry_strsource(err), gcry_strerror(err));
return NULL;
}
for(ssize_t total = 0; total < size; total += bytes){
bytes = 512 < (size - total) ? 512 : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
gcry_md_close(ctx_md5);
return NULL;
}
gcry_md_write(ctx_md5, buf, bytes);
memset(buf, 0, 512);
}
result = new unsigned char[get_md5_digest_length()];
memcpy(result, gcry_md_read(ctx_md5, 0), get_md5_digest_length());
gcry_md_close(ctx_md5);
return result;
return true;
}
#endif // USE_GNUTLS_NETTLE
bool s3fs_md5_fd(int fd, off_t start, off_t size, md5_t* result)
{
gcry_md_hd_t ctx_md5;
gcry_error_t err;
off_t bytes;
if(-1 == size){
struct stat st;
if(-1 == fstat(fd, &st)){
return false;
}
size = st.st_size;
}
if(GPG_ERR_NO_ERROR != (err = gcry_md_open(&ctx_md5, GCRY_MD_MD5, 0))){
S3FS_PRN_ERR("MD5 context creation failure: %s/%s", gcry_strsource(err), gcry_strerror(err));
return false;
}
for(off_t total = 0; total < size; total += bytes){
off_t len = 512;
char buf[len];
bytes = len < (size - total) ? len : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
gcry_md_close(ctx_md5);
return false;
}
gcry_md_write(ctx_md5, buf, bytes);
}
memcpy(result->data(), gcry_md_read(ctx_md5, 0), result->size());
gcry_md_close(ctx_md5);
return true;
}
#endif // USE_GNUTLS_NETTLE
//-------------------------------------------------------------------
// Utility Function for SHA256
//-------------------------------------------------------------------
size_t get_sha256_digest_length()
#ifdef USE_GNUTLS_NETTLE
bool s3fs_sha256(const unsigned char* data, size_t datalen, sha256_t* digest)
{
return 32;
struct sha256_ctx ctx_sha256;
sha256_init(&ctx_sha256);
sha256_update(&ctx_sha256, datalen, data);
sha256_digest(&ctx_sha256, digest->size(), digest->data());
return true;
}
#ifdef USE_GNUTLS_NETTLE
bool s3fs_sha256(const unsigned char* data, unsigned int datalen, unsigned char** digest, unsigned int* digestlen)
bool s3fs_sha256_fd(int fd, off_t start, off_t size, sha256_t* result)
{
(*digestlen) = static_cast<unsigned int>(get_sha256_digest_length());
*digest = new unsigned char[*digestlen];
struct sha256_ctx ctx_sha256;
off_t bytes;
struct sha256_ctx ctx_sha256;
sha256_init(&ctx_sha256);
sha256_update(&ctx_sha256, datalen, data);
sha256_digest(&ctx_sha256, *digestlen, *digest);
sha256_init(&ctx_sha256);
return true;
}
unsigned char* s3fs_sha256hexsum(int fd, off_t start, ssize_t size)
{
struct sha256_ctx ctx_sha256;
unsigned char buf[512];
ssize_t bytes;
unsigned char* result;
memset(buf, 0, 512);
sha256_init(&ctx_sha256);
for(ssize_t total = 0; total < size; total += bytes){
bytes = 512 < (size - total) ? 512 : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
return NULL;
for(off_t total = 0; total < size; total += bytes){
off_t len = 512;
unsigned char buf[len];
bytes = len < (size - total) ? len : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
return false;
}
sha256_update(&ctx_sha256, bytes, buf);
}
sha256_update(&ctx_sha256, bytes, buf);
memset(buf, 0, 512);
}
result = new unsigned char[get_sha256_digest_length()];
sha256_digest(&ctx_sha256, get_sha256_digest_length(), result);
sha256_digest(&ctx_sha256, result->size(), result->data());
return result;
return true;
}
#else // USE_GNUTLS_NETTLE
#else // USE_GNUTLS_NETTLE
bool s3fs_sha256(const unsigned char* data, unsigned int datalen, unsigned char** digest, unsigned int* digestlen)
bool s3fs_sha256(const unsigned char* data, size_t datalen, sha256_t* digest)
{
size_t len = (*digestlen) = static_cast<unsigned int>(get_sha256_digest_length());
*digest = new unsigned char[len];
gcry_md_hd_t ctx_sha256;
gcry_error_t err;
if(GPG_ERR_NO_ERROR != (err = gcry_md_open(&ctx_sha256, GCRY_MD_SHA256, 0))){
S3FS_PRN_ERR("SHA256 context creation failure: %s/%s", gcry_strsource(err), gcry_strerror(err));
return false;
}
gcry_md_write(ctx_sha256, data, datalen);
memcpy(digest->data(), gcry_md_read(ctx_sha256, 0), digest->size());
gcry_md_close(ctx_sha256);
gcry_md_hd_t ctx_sha256;
gcry_error_t err;
if(GPG_ERR_NO_ERROR != (err = gcry_md_open(&ctx_sha256, GCRY_MD_SHA256, 0))){
S3FS_PRN_ERR("SHA256 context creation failure: %s/%s", gcry_strsource(err), gcry_strerror(err));
delete[] *digest;
return false;
}
gcry_md_write(ctx_sha256, data, datalen);
memcpy(*digest, gcry_md_read(ctx_sha256, 0), *digestlen);
gcry_md_close(ctx_sha256);
return true;
return true;
}
unsigned char* s3fs_sha256hexsum(int fd, off_t start, ssize_t size)
bool s3fs_sha256_fd(int fd, off_t start, off_t size, sha256_t* result)
{
gcry_md_hd_t ctx_sha256;
gcry_error_t err;
char buf[512];
ssize_t bytes;
unsigned char* result;
gcry_md_hd_t ctx_sha256;
gcry_error_t err;
off_t bytes;
if(-1 == size){
struct stat st;
if(-1 == fstat(fd, &st)){
return NULL;
if(-1 == size){
struct stat st;
if(-1 == fstat(fd, &st)){
return false;
}
size = st.st_size;
}
size = static_cast<ssize_t>(st.st_size);
}
memset(buf, 0, 512);
if(GPG_ERR_NO_ERROR != (err = gcry_md_open(&ctx_sha256, GCRY_MD_SHA256, 0))){
S3FS_PRN_ERR("SHA256 context creation failure: %s/%s", gcry_strsource(err), gcry_strerror(err));
return NULL;
}
for(ssize_t total = 0; total < size; total += bytes){
bytes = 512 < (size - total) ? 512 : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
gcry_md_close(ctx_sha256);
return NULL;
if(GPG_ERR_NO_ERROR != (err = gcry_md_open(&ctx_sha256, GCRY_MD_SHA256, 0))){
S3FS_PRN_ERR("SHA256 context creation failure: %s/%s", gcry_strsource(err), gcry_strerror(err));
return false;
}
gcry_md_write(ctx_sha256, buf, bytes);
memset(buf, 0, 512);
}
result = new unsigned char[get_sha256_digest_length()];
memcpy(result, gcry_md_read(ctx_sha256, 0), get_sha256_digest_length());
gcry_md_close(ctx_sha256);
return result;
for(off_t total = 0; total < size; total += bytes){
off_t len = 512;
char buf[len];
bytes = len < (size - total) ? len : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
gcry_md_close(ctx_sha256);
return false;
}
gcry_md_write(ctx_sha256, buf, bytes);
}
memcpy(result->data(), gcry_md_read(ctx_sha256, 0), result->size());
gcry_md_close(ctx_sha256);
return true;
}
#endif // USE_GNUTLS_NETTLE
#endif // USE_GNUTLS_NETTLE
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

341
src/metaheader.cpp Normal file
View File

@ -0,0 +1,341 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <ctime>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "common.h"
#include "metaheader.h"
#include "string_util.h"
static constexpr struct timespec DEFAULT_TIMESPEC = {-1, 0};
//-------------------------------------------------------------------
// Utility functions for convert
//-------------------------------------------------------------------
static struct timespec cvt_string_to_time(const char *str)
{
// [NOTE]
// In rclone, there are cases where ns is set to x-amz-meta-mtime
// with floating point number. s3fs uses x-amz-meta-mtime by
// truncating the floating point or less (in seconds or less) to
// correspond to this.
//
std::string strmtime;
long nsec = 0;
if(str && '\0' != *str){
strmtime = str;
std::string::size_type pos = strmtime.find('.', 0);
if(std::string::npos != pos){
nsec = cvt_strtoofft(strmtime.substr(pos + 1).c_str(), /*base=*/ 10);
strmtime.erase(pos);
}
}
struct timespec ts = {static_cast<time_t>(cvt_strtoofft(strmtime.c_str(), /*base=*/ 10)), nsec};
return ts;
}
static struct timespec get_time(const headers_t& meta, const char *header)
{
headers_t::const_iterator iter;
if(meta.end() == (iter = meta.find(header))){
return DEFAULT_TIMESPEC;
}
return cvt_string_to_time((*iter).second.c_str());
}
struct timespec get_mtime(const headers_t& meta, bool overcheck)
{
struct timespec t = get_time(meta, "x-amz-meta-mtime");
if(0 < t.tv_sec){
return t;
}
t = get_time(meta, "x-amz-meta-goog-reserved-file-mtime");
if(0 < t.tv_sec){
return t;
}
if(overcheck){
struct timespec ts = {get_lastmodified(meta), 0};
return ts;
}
return DEFAULT_TIMESPEC;
}
struct timespec get_ctime(const headers_t& meta, bool overcheck)
{
struct timespec t = get_time(meta, "x-amz-meta-ctime");
if(0 < t.tv_sec){
return t;
}
if(overcheck){
struct timespec ts = {get_lastmodified(meta), 0};
return ts;
}
return DEFAULT_TIMESPEC;
}
struct timespec get_atime(const headers_t& meta, bool overcheck)
{
struct timespec t = get_time(meta, "x-amz-meta-atime");
if(0 < t.tv_sec){
return t;
}
if(overcheck){
struct timespec ts = {get_lastmodified(meta), 0};
return ts;
}
return DEFAULT_TIMESPEC;
}
off_t get_size(const char *s)
{
return cvt_strtoofft(s, /*base=*/ 10);
}
off_t get_size(const headers_t& meta)
{
headers_t::const_iterator iter = meta.find("Content-Length");
if(meta.end() == iter){
return 0;
}
return get_size((*iter).second.c_str());
}
mode_t get_mode(const char *s, int base)
{
return static_cast<mode_t>(cvt_strtoofft(s, base));
}
mode_t get_mode(const headers_t& meta, const std::string& strpath, bool checkdir, bool forcedir)
{
mode_t mode = 0;
bool isS3sync = false;
headers_t::const_iterator iter;
if(meta.end() != (iter = meta.find("x-amz-meta-mode"))){
mode = get_mode((*iter).second.c_str());
}else if(meta.end() != (iter = meta.find("x-amz-meta-permissions"))){ // for s3sync
mode = get_mode((*iter).second.c_str());
isS3sync = true;
}else if(meta.end() != (iter = meta.find("x-amz-meta-goog-reserved-posix-mode"))){ // for GCS
mode = get_mode((*iter).second.c_str(), 8);
}else{
// If another tool creates an object without permissions, default to owner
// read-write and group readable.
mode = (!strpath.empty() && '/' == *strpath.rbegin()) ? 0750 : 0640;
}
// Checking the bitmask, if the last 3 bits are all zero then process as a regular
// file type (S_IFDIR or S_IFREG), otherwise return mode unmodified so that S_IFIFO,
// S_IFSOCK, S_IFCHR, S_IFLNK and S_IFBLK devices can be processed properly by fuse.
if(!(mode & S_IFMT)){
if(!isS3sync){
if(checkdir){
if(forcedir){
mode |= S_IFDIR;
}else{
if(meta.end() != (iter = meta.find("Content-Type"))){
std::string strConType = (*iter).second;
// Leave just the mime type, remove any optional parameters (eg charset)
std::string::size_type pos = strConType.find(';');
if(std::string::npos != pos){
strConType.erase(pos);
}
if(strConType == "application/x-directory" || strConType == "httpd/unix-directory"){
// Nextcloud uses this MIME type for directory objects when mounting bucket as external Storage
mode |= S_IFDIR;
}else if(!strpath.empty() && '/' == *strpath.rbegin()){
if(strConType == "binary/octet-stream" || strConType == "application/octet-stream"){
mode |= S_IFDIR;
}else{
if(complement_stat){
// If complement lack stat mode, when the object has '/' character at end of name
// and content type is text/plain and the object's size is 0 or 1, it should be
// directory.
off_t size = get_size(meta);
if(strConType == "text/plain" && (0 == size || 1 == size)){
mode |= S_IFDIR;
}else{
mode |= S_IFREG;
}
}else{
mode |= S_IFREG;
}
}
}else{
mode |= S_IFREG;
}
}else{
mode |= S_IFREG;
}
}
}
// If complement lack stat mode, when it's mode is not set any permission,
// the object is added minimal mode only for read permission.
if(complement_stat && 0 == (mode & (S_IRWXU | S_IRWXG | S_IRWXO))){
mode |= (S_IRUSR | (0 == (mode & S_IFDIR) ? 0 : S_IXUSR));
}
}else{
if(!checkdir){
// cut dir/reg flag.
mode &= ~S_IFDIR;
mode &= ~S_IFREG;
}
}
}
return mode;
}
uid_t get_uid(const char *s)
{
return static_cast<uid_t>(cvt_strtoofft(s, /*base=*/ 0));
}
uid_t get_uid(const headers_t& meta)
{
headers_t::const_iterator iter;
if(meta.end() != (iter = meta.find("x-amz-meta-uid"))){
return get_uid((*iter).second.c_str());
}else if(meta.end() != (iter = meta.find("x-amz-meta-owner"))){ // for s3sync
return get_uid((*iter).second.c_str());
}else if(meta.end() != (iter = meta.find("x-amz-meta-goog-reserved-posix-uid"))){ // for GCS
return get_uid((*iter).second.c_str());
}else{
return geteuid();
}
}
gid_t get_gid(const char *s)
{
return static_cast<gid_t>(cvt_strtoofft(s, /*base=*/ 0));
}
gid_t get_gid(const headers_t& meta)
{
headers_t::const_iterator iter;
if(meta.end() != (iter = meta.find("x-amz-meta-gid"))){
return get_gid((*iter).second.c_str());
}else if(meta.end() != (iter = meta.find("x-amz-meta-group"))){ // for s3sync
return get_gid((*iter).second.c_str());
}else if(meta.end() != (iter = meta.find("x-amz-meta-goog-reserved-posix-gid"))){ // for GCS
return get_gid((*iter).second.c_str());
}else{
return getegid();
}
}
blkcnt_t get_blocks(off_t size)
{
return (size / 512) + (0 == (size % 512) ? 0 : 1);
}
time_t cvtIAMExpireStringToTime(const char* s)
{
struct tm tm;
if(!s){
return 0L;
}
memset(&tm, 0, sizeof(struct tm));
strptime(s, "%Y-%m-%dT%H:%M:%S", &tm);
return timegm(&tm); // GMT
}
time_t get_lastmodified(const char* s)
{
struct tm tm;
if(!s){
return -1;
}
memset(&tm, 0, sizeof(struct tm));
strptime(s, "%a, %d %b %Y %H:%M:%S %Z", &tm);
return timegm(&tm); // GMT
}
time_t get_lastmodified(const headers_t& meta)
{
headers_t::const_iterator iter = meta.find("Last-Modified");
if(meta.end() == iter){
return -1;
}
return get_lastmodified((*iter).second.c_str());
}
//
// Returns it whether it is an object with need checking in detail.
// If this function returns true, the object is possible to be directory
// and is needed checking detail(searching sub object).
//
bool is_need_check_obj_detail(const headers_t& meta)
{
headers_t::const_iterator iter;
// directory object is Content-Length as 0.
if(0 != get_size(meta)){
return false;
}
// if the object has x-amz-meta information, checking is no more.
if(meta.end() != meta.find("x-amz-meta-mode") ||
meta.end() != meta.find("x-amz-meta-mtime") ||
meta.end() != meta.find("x-amz-meta-ctime") ||
meta.end() != meta.find("x-amz-meta-atime") ||
meta.end() != meta.find("x-amz-meta-uid") ||
meta.end() != meta.find("x-amz-meta-gid") ||
meta.end() != meta.find("x-amz-meta-owner") ||
meta.end() != meta.find("x-amz-meta-group") ||
meta.end() != meta.find("x-amz-meta-permissions") )
{
return false;
}
// if there is not Content-Type, or Content-Type is "x-directory",
// checking is no more.
if(meta.end() == (iter = meta.find("Content-Type"))){
return false;
}
if("application/x-directory" == (*iter).second){
return false;
}
return true;
}
// [NOTE]
// If add_noexist is false and the key does not exist, it will not be added.
//
bool merge_headers(headers_t& base, const headers_t& additional, bool add_noexist)
{
bool added = false;
for(headers_t::const_iterator iter = additional.begin(); iter != additional.end(); ++iter){
if(add_noexist || base.find(iter->first) != base.end()){
base[iter->first] = iter->second;
added = true;
}
}
return added;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

71
src/metaheader.h Normal file
View File

@ -0,0 +1,71 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_METAHEADER_H_
#define S3FS_METAHEADER_H_
#include <string>
#include <strings.h>
#include <map>
//-------------------------------------------------------------------
// headers_t
//-------------------------------------------------------------------
struct header_nocase_cmp
{
bool operator()(const std::string &strleft, const std::string &strright) const
{
return (strcasecmp(strleft.c_str(), strright.c_str()) < 0);
}
};
typedef std::map<std::string, std::string, header_nocase_cmp> headers_t;
//-------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------
struct timespec get_mtime(const headers_t& meta, bool overcheck = true);
struct timespec get_ctime(const headers_t& meta, bool overcheck = true);
struct timespec get_atime(const headers_t& meta, bool overcheck = true);
off_t get_size(const char *s);
off_t get_size(const headers_t& meta);
mode_t get_mode(const char *s, int base = 0);
mode_t get_mode(const headers_t& meta, const std::string& strpath, bool checkdir = false, bool forcedir = false);
uid_t get_uid(const char *s);
uid_t get_uid(const headers_t& meta);
gid_t get_gid(const char *s);
gid_t get_gid(const headers_t& meta);
blkcnt_t get_blocks(off_t size);
time_t cvtIAMExpireStringToTime(const char* s);
time_t get_lastmodified(const char* s);
time_t get_lastmodified(const headers_t& meta);
bool is_need_check_obj_detail(const headers_t& meta);
bool merge_headers(headers_t& base, const headers_t& additional, bool add_noexist);
bool simple_parse_xml(const char* data, size_t len, const char* key, std::string& value);
#endif // S3FS_METAHEADER_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

158
src/mpu_util.cpp Normal file
View File

@ -0,0 +1,158 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cstdio>
#include <cstdlib>
#include "s3fs.h"
#include "s3fs_logger.h"
#include "mpu_util.h"
#include "curl.h"
#include "s3fs_xml.h"
#include "s3fs_auth.h"
#include "string_util.h"
//-------------------------------------------------------------------
// Global variables
//-------------------------------------------------------------------
utility_incomp_type utility_mode = utility_incomp_type::NO_UTILITY_MODE;
//-------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------
static void print_incomp_mpu_list(const incomp_mpu_list_t& list)
{
printf("\n");
printf("Lists the parts that have been uploaded for a specific multipart upload.\n");
printf("\n");
if(!list.empty()){
printf("---------------------------------------------------------------\n");
int cnt = 0;
for(incomp_mpu_list_t::const_iterator iter = list.begin(); iter != list.end(); ++iter, ++cnt){
printf(" Path : %s\n", (*iter).key.c_str());
printf(" UploadId : %s\n", (*iter).id.c_str());
printf(" Date : %s\n", (*iter).date.c_str());
printf("\n");
}
printf("---------------------------------------------------------------\n");
}else{
printf("There is no list.\n");
}
}
static bool abort_incomp_mpu_list(const incomp_mpu_list_t& list, time_t abort_time)
{
if(list.empty()){
return true;
}
time_t now_time = time(nullptr);
// do removing.
S3fsCurl s3fscurl;
bool result = true;
for(incomp_mpu_list_t::const_iterator iter = list.begin(); iter != list.end(); ++iter){
const char* tpath = (*iter).key.c_str();
std::string upload_id = (*iter).id;
if(0 != abort_time){ // abort_time is 0, it means all.
time_t date = 0;
if(!get_unixtime_from_iso8601((*iter).date.c_str(), date)){
S3FS_PRN_DBG("date format is not ISO 8601 for %s multipart uploading object, skip this.", tpath);
continue;
}
if(now_time <= (date + abort_time)){
continue;
}
}
if(0 != s3fscurl.AbortMultipartUpload(tpath, upload_id)){
S3FS_PRN_EXIT("Failed to remove %s multipart uploading object.", tpath);
result = false;
}else{
printf("Succeed to remove %s multipart uploading object.\n", tpath);
}
// reset(initialize) curl object
s3fscurl.DestroyCurlHandle();
}
return result;
}
int s3fs_utility_processing(time_t abort_time)
{
if(utility_incomp_type::NO_UTILITY_MODE == utility_mode){
return EXIT_FAILURE;
}
printf("\n*** s3fs run as utility mode.\n\n");
S3fsCurl s3fscurl;
std::string body;
int result = EXIT_SUCCESS;
if(0 != s3fscurl.MultipartListRequest(body)){
S3FS_PRN_EXIT("Could not get list multipart upload.\nThere is no incomplete multipart uploaded object in bucket.\n");
result = EXIT_FAILURE;
}else{
// parse result(incomplete multipart upload information)
S3FS_PRN_DBG("response body = {\n%s\n}", body.c_str());
std::unique_ptr<xmlDoc, decltype(&xmlFreeDoc)> doc(xmlReadMemory(body.c_str(), static_cast<int>(body.size()), "", nullptr, 0), xmlFreeDoc);
if(nullptr == doc){
S3FS_PRN_DBG("xmlReadMemory exited with error.");
result = EXIT_FAILURE;
}else{
// make incomplete uploads list
incomp_mpu_list_t list;
if(!get_incomp_mpu_list(doc.get(), list)){
S3FS_PRN_DBG("get_incomp_mpu_list exited with error.");
result = EXIT_FAILURE;
}else{
if(utility_incomp_type::INCOMP_TYPE_LIST == utility_mode){
// print list
print_incomp_mpu_list(list);
}else if(utility_incomp_type::INCOMP_TYPE_ABORT == utility_mode){
// remove
if(!abort_incomp_mpu_list(list, abort_time)){
S3FS_PRN_DBG("an error occurred during removal process.");
result = EXIT_FAILURE;
}
}
}
}
}
// ssl
s3fs_destroy_global_ssl();
return result;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

64
src/mpu_util.h Normal file
View File

@ -0,0 +1,64 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_MPU_UTIL_H_
#define S3FS_MPU_UTIL_H_
#include <string>
#include <vector>
//-------------------------------------------------------------------
// Structure / Typedef
//-------------------------------------------------------------------
typedef struct incomplete_multipart_upload_info
{
std::string key;
std::string id;
std::string date;
}INCOMP_MPU_INFO;
typedef std::vector<INCOMP_MPU_INFO> incomp_mpu_list_t;
//-------------------------------------------------------------------
// enum for utility process mode
//-------------------------------------------------------------------
enum class utility_incomp_type{
NO_UTILITY_MODE = 0, // not utility mode
INCOMP_TYPE_LIST, // list of incomplete mpu
INCOMP_TYPE_ABORT // delete incomplete mpu
};
extern utility_incomp_type utility_mode;
//-------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------
int s3fs_utility_processing(time_t abort_time);
#endif // S3FS_MPU_UTIL_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

View File

@ -18,15 +18,15 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <pthread.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <nss.h>
#include <pk11pub.h>
#include <hasht.h>
@ -35,18 +35,18 @@
#include <map>
#include "common.h"
#include "s3fs.h"
#include "s3fs_auth.h"
using namespace std;
#include "s3fs_logger.h"
//-------------------------------------------------------------------
// Utility Function for version
//-------------------------------------------------------------------
const char* s3fs_crypt_lib_name()
{
static const char version[] = "NSS";
static constexpr char version[] = "NSS";
return version;
return version;
}
//-------------------------------------------------------------------
@ -54,21 +54,21 @@ const char* s3fs_crypt_lib_name()
//-------------------------------------------------------------------
bool s3fs_init_global_ssl()
{
PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
if(SECSuccess != NSS_NoDB_Init(NULL)){
S3FS_PRN_ERR("Failed NSS_NoDB_Init call.");
return false;
}
return true;
if(SECSuccess != NSS_NoDB_Init(nullptr)){
S3FS_PRN_ERR("Failed NSS_NoDB_Init call.");
return false;
}
return true;
}
bool s3fs_destroy_global_ssl()
{
NSS_Shutdown();
PL_ArenaFinish();
PR_Cleanup();
return true;
NSS_Shutdown();
PL_ArenaFinish();
PR_Cleanup();
return true;
}
//-------------------------------------------------------------------
@ -76,186 +76,179 @@ bool s3fs_destroy_global_ssl()
//-------------------------------------------------------------------
bool s3fs_init_crypt_mutex()
{
return true;
return true;
}
bool s3fs_destroy_crypt_mutex()
{
return true;
return true;
}
//-------------------------------------------------------------------
// Utility Function for HMAC
//-------------------------------------------------------------------
static bool s3fs_HMAC_RAW(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned char** digest, unsigned int* digestlen, bool is_sha256)
static std::unique_ptr<unsigned char[]> s3fs_HMAC_RAW(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned int* digestlen, bool is_sha256)
{
if(!key || !data || !digest || !digestlen){
return false;
}
if(!key || !data || !digestlen){
return nullptr;
}
PK11SlotInfo* Slot;
PK11SymKey* pKey;
PK11Context* Context;
unsigned char tmpdigest[64];
SECItem KeySecItem = {siBuffer, reinterpret_cast<unsigned char*>(const_cast<void*>(key)), static_cast<unsigned int>(keylen)};
SECItem NullSecItem = {siBuffer, NULL, 0};
PK11SlotInfo* Slot;
PK11SymKey* pKey;
PK11Context* Context;
unsigned char tmpdigest[64];
SECItem KeySecItem = {siBuffer, reinterpret_cast<unsigned char*>(const_cast<void*>(key)), static_cast<unsigned int>(keylen)};
SECItem NullSecItem = {siBuffer, nullptr, 0};
if(NULL == (Slot = PK11_GetInternalKeySlot())){
return false;
}
if(NULL == (pKey = PK11_ImportSymKey(Slot, (is_sha256 ? CKM_SHA256_HMAC : CKM_SHA_1_HMAC), PK11_OriginUnwrap, CKA_SIGN, &KeySecItem, NULL))){
PK11_FreeSlot(Slot);
return false;
}
if(NULL == (Context = PK11_CreateContextBySymKey((is_sha256 ? CKM_SHA256_HMAC : CKM_SHA_1_HMAC), CKA_SIGN, pKey, &NullSecItem))){
PK11_FreeSymKey(pKey);
PK11_FreeSlot(Slot);
return false;
}
if(nullptr == (Slot = PK11_GetInternalKeySlot())){
return nullptr;
}
if(nullptr == (pKey = PK11_ImportSymKey(Slot, (is_sha256 ? CKM_SHA256_HMAC : CKM_SHA_1_HMAC), PK11_OriginUnwrap, CKA_SIGN, &KeySecItem, nullptr))){
PK11_FreeSlot(Slot);
return nullptr;
}
if(nullptr == (Context = PK11_CreateContextBySymKey((is_sha256 ? CKM_SHA256_HMAC : CKM_SHA_1_HMAC), CKA_SIGN, pKey, &NullSecItem))){
PK11_FreeSymKey(pKey);
PK11_FreeSlot(Slot);
return nullptr;
}
*digestlen = 0;
if(SECSuccess != PK11_DigestBegin(Context) ||
SECSuccess != PK11_DigestOp(Context, data, datalen) ||
SECSuccess != PK11_DigestFinal(Context, tmpdigest, digestlen, sizeof(tmpdigest)) )
{
*digestlen = 0;
if(SECSuccess != PK11_DigestBegin(Context) ||
SECSuccess != PK11_DigestOp(Context, data, datalen) ||
SECSuccess != PK11_DigestFinal(Context, tmpdigest, digestlen, sizeof(tmpdigest)) )
{
PK11_DestroyContext(Context, PR_TRUE);
PK11_FreeSymKey(pKey);
PK11_FreeSlot(Slot);
return nullptr;
}
PK11_DestroyContext(Context, PR_TRUE);
PK11_FreeSymKey(pKey);
PK11_FreeSlot(Slot);
return false;
}
PK11_DestroyContext(Context, PR_TRUE);
PK11_FreeSymKey(pKey);
PK11_FreeSlot(Slot);
*digest = new unsigned char[*digestlen];
memcpy(*digest, tmpdigest, *digestlen);
std::unique_ptr<unsigned char[]> digest(new unsigned char[*digestlen]);
memcpy(digest.get(), tmpdigest, *digestlen);
return true;
return digest;
}
bool s3fs_HMAC(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned char** digest, unsigned int* digestlen)
std::unique_ptr<unsigned char[]> s3fs_HMAC(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned int* digestlen)
{
return s3fs_HMAC_RAW(key, keylen, data, datalen, digest, digestlen, false);
return s3fs_HMAC_RAW(key, keylen, data, datalen, digestlen, false);
}
bool s3fs_HMAC256(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned char** digest, unsigned int* digestlen)
std::unique_ptr<unsigned char[]> s3fs_HMAC256(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned int* digestlen)
{
return s3fs_HMAC_RAW(key, keylen, data, datalen, digest, digestlen, true);
return s3fs_HMAC_RAW(key, keylen, data, datalen, digestlen, true);
}
//-------------------------------------------------------------------
// Utility Function for MD5
//-------------------------------------------------------------------
size_t get_md5_digest_length()
bool s3fs_md5(const unsigned char* data, size_t datalen, md5_t* result)
{
return MD5_LENGTH;
PK11Context* md5ctx;
unsigned int md5outlen;
md5ctx = PK11_CreateDigestContext(SEC_OID_MD5);
PK11_DigestOp(md5ctx, data, datalen);
PK11_DigestFinal(md5ctx, result->data(), &md5outlen, result->size());
PK11_DestroyContext(md5ctx, PR_TRUE);
return true;
}
unsigned char* s3fs_md5hexsum(int fd, off_t start, ssize_t size)
bool s3fs_md5_fd(int fd, off_t start, off_t size, md5_t* result)
{
PK11Context* md5ctx;
unsigned char buf[512];
ssize_t bytes;
unsigned char* result;
unsigned int md5outlen;
PK11Context* md5ctx;
off_t bytes;
unsigned int md5outlen;
if(-1 == size){
struct stat st;
if(-1 == fstat(fd, &st)){
return NULL;
if(-1 == size){
struct stat st;
if(-1 == fstat(fd, &st)){
return false;
}
size = st.st_size;
}
size = static_cast<ssize_t>(st.st_size);
}
memset(buf, 0, 512);
md5ctx = PK11_CreateDigestContext(SEC_OID_MD5);
md5ctx = PK11_CreateDigestContext(SEC_OID_MD5);
for(ssize_t total = 0; total < size; total += bytes){
bytes = 512 < (size - total) ? 512 : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
PK11_DestroyContext(md5ctx, PR_TRUE);
return NULL;
for(off_t total = 0; total < size; total += bytes){
off_t len = 512;
unsigned char buf[len];
bytes = len < (size - total) ? len : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
PK11_DestroyContext(md5ctx, PR_TRUE);
return false;
}
PK11_DigestOp(md5ctx, buf, bytes);
}
PK11_DigestOp(md5ctx, buf, bytes);
memset(buf, 0, 512);
}
result = new unsigned char[get_md5_digest_length()];
PK11_DigestFinal(md5ctx, result, &md5outlen, get_md5_digest_length());
PK11_DestroyContext(md5ctx, PR_TRUE);
PK11_DigestFinal(md5ctx, result->data(), &md5outlen, result->size());
PK11_DestroyContext(md5ctx, PR_TRUE);
return result;
return false;
}
//-------------------------------------------------------------------
// Utility Function for SHA256
//-------------------------------------------------------------------
size_t get_sha256_digest_length()
bool s3fs_sha256(const unsigned char* data, size_t datalen, sha256_t* digest)
{
return SHA256_LENGTH;
PK11Context* sha256ctx;
unsigned int sha256outlen;
sha256ctx = PK11_CreateDigestContext(SEC_OID_SHA256);
PK11_DigestOp(sha256ctx, data, datalen);
PK11_DigestFinal(sha256ctx, digest->data(), &sha256outlen, digest->size());
PK11_DestroyContext(sha256ctx, PR_TRUE);
return true;
}
bool s3fs_sha256(const unsigned char* data, unsigned int datalen, unsigned char** digest, unsigned int* digestlen)
bool s3fs_sha256_fd(int fd, off_t start, off_t size, sha256_t* result)
{
(*digestlen) = static_cast<unsigned int>(get_sha256_digest_length());
*digest = new unsigned char[*digestlen];
PK11Context* sha256ctx;
off_t bytes;
unsigned int sha256outlen;
PK11Context* sha256ctx;
unsigned int sha256outlen;
sha256ctx = PK11_CreateDigestContext(SEC_OID_SHA256);
PK11_DigestOp(sha256ctx, data, datalen);
PK11_DigestFinal(sha256ctx, *digest, &sha256outlen, *digestlen);
PK11_DestroyContext(sha256ctx, PR_TRUE);
*digestlen = sha256outlen;
return true;
}
unsigned char* s3fs_sha256hexsum(int fd, off_t start, ssize_t size)
{
PK11Context* sha256ctx;
unsigned char buf[512];
ssize_t bytes;
unsigned char* result;
unsigned int sha256outlen;
if(-1 == size){
struct stat st;
if(-1 == fstat(fd, &st)){
return NULL;
if(-1 == size){
struct stat st;
if(-1 == fstat(fd, &st)){
return false;
}
size = st.st_size;
}
size = static_cast<ssize_t>(st.st_size);
}
memset(buf, 0, 512);
sha256ctx = PK11_CreateDigestContext(SEC_OID_SHA256);
sha256ctx = PK11_CreateDigestContext(SEC_OID_SHA256);
for(ssize_t total = 0; total < size; total += bytes){
bytes = 512 < (size - total) ? 512 : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
PK11_DestroyContext(sha256ctx, PR_TRUE);
return NULL;
for(off_t total = 0; total < size; total += bytes){
off_t len = 512;
unsigned char buf[len];
bytes = len < (size - total) ? len : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
PK11_DestroyContext(sha256ctx, PR_TRUE);
return false;
}
PK11_DigestOp(sha256ctx, buf, bytes);
}
PK11_DigestOp(sha256ctx, buf, bytes);
memset(buf, 0, 512);
}
result = new unsigned char[get_sha256_digest_length()];
PK11_DigestFinal(sha256ctx, result, &sha256outlen, get_sha256_digest_length());
PK11_DestroyContext(sha256ctx, PR_TRUE);
PK11_DigestFinal(sha256ctx, result->data(), &sha256outlen, result->size());
PK11_DestroyContext(sha256ctx, PR_TRUE);
return result;
return true;
}
/*
@ -263,6 +256,6 @@ unsigned char* s3fs_sha256hexsum(int fd, off_t start, ssize_t size)
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

View File

@ -18,17 +18,16 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef __clang__
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <pthread.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <cstring>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/md5.h>
@ -36,21 +35,18 @@
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <string>
#include <map>
#include "common.h"
#include "s3fs_auth.h"
using namespace std;
#include "s3fs_logger.h"
//-------------------------------------------------------------------
// Utility Function for version
//-------------------------------------------------------------------
const char* s3fs_crypt_lib_name()
{
static const char version[] = "OpenSSL";
static constexpr char version[] = "OpenSSL";
return version;
return version;
}
//-------------------------------------------------------------------
@ -58,17 +54,24 @@ const char* s3fs_crypt_lib_name()
//-------------------------------------------------------------------
bool s3fs_init_global_ssl()
{
ERR_load_crypto_strings();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();
return true;
ERR_load_crypto_strings();
// [NOTE]
// OpenSSL 3.0 loads error strings automatically so these functions are not needed.
//
#ifndef USE_OPENSSL_30
ERR_load_BIO_strings();
#endif
OpenSSL_add_all_algorithms();
return true;
}
bool s3fs_destroy_global_ssl()
{
EVP_cleanup();
ERR_free_strings();
return true;
EVP_cleanup();
ERR_free_strings();
return true;
}
//-------------------------------------------------------------------
@ -77,285 +80,358 @@ bool s3fs_destroy_global_ssl()
// internal use struct for openssl
struct CRYPTO_dynlock_value
{
pthread_mutex_t dyn_mutex;
pthread_mutex_t dyn_mutex;
};
static pthread_mutex_t* s3fs_crypt_mutex = NULL;
static pthread_mutex_t* s3fs_crypt_mutex = nullptr;
static void s3fs_crypt_mutex_lock(int mode, int pos, const char* file, int line) __attribute__ ((unused));
static void s3fs_crypt_mutex_lock(int mode, int pos, const char* file, int line)
{
if(s3fs_crypt_mutex){
int res;
if(mode & CRYPTO_LOCK){
if(0 != (res = pthread_mutex_lock(&s3fs_crypt_mutex[pos]))){
S3FS_PRN_CRIT("pthread_mutex_lock returned: %d", res);
abort();
}
}else{
if(0 != (res = pthread_mutex_unlock(&s3fs_crypt_mutex[pos]))){
S3FS_PRN_CRIT("pthread_mutex_unlock returned: %d", res);
abort();
}
if(s3fs_crypt_mutex){
int result;
if(mode & CRYPTO_LOCK){
if(0 != (result = pthread_mutex_lock(&s3fs_crypt_mutex[pos]))){
S3FS_PRN_CRIT("pthread_mutex_lock returned: %d", result);
abort();
}
}else{
if(0 != (result = pthread_mutex_unlock(&s3fs_crypt_mutex[pos]))){
S3FS_PRN_CRIT("pthread_mutex_unlock returned: %d", result);
abort();
}
}
}
}
}
static unsigned long s3fs_crypt_get_threadid() __attribute__ ((unused));
static unsigned long s3fs_crypt_get_threadid()
{
// For FreeBSD etc, some system's pthread_t is structure pointer.
// Then we use cast like C style(not C++) instead of ifdef.
return (unsigned long)(pthread_self());
// For FreeBSD etc, some system's pthread_t is structure pointer.
// Then we use cast like C style(not C++) instead of ifdef.
return (unsigned long)(pthread_self());
}
static struct CRYPTO_dynlock_value* s3fs_dyn_crypt_mutex(const char* file, int line) __attribute__ ((unused));
static struct CRYPTO_dynlock_value* s3fs_dyn_crypt_mutex(const char* file, int line)
{
struct CRYPTO_dynlock_value* dyndata = new CRYPTO_dynlock_value();
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
struct CRYPTO_dynlock_value* dyndata = new CRYPTO_dynlock_value();
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
#if S3FS_PTHREAD_ERRORCHECK
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
#endif
int res;
if(0 != (res = pthread_mutex_init(&(dyndata->dyn_mutex), &attr))){
S3FS_PRN_CRIT("pthread_mutex_init returned: %d", res);
return NULL;
}
return dyndata;
int result;
if(0 != (result = pthread_mutex_init(&(dyndata->dyn_mutex), &attr))){
S3FS_PRN_CRIT("pthread_mutex_init returned: %d", result);
return nullptr;
}
return dyndata;
}
static void s3fs_dyn_crypt_mutex_lock(int mode, struct CRYPTO_dynlock_value* dyndata, const char* file, int line) __attribute__ ((unused));
static void s3fs_dyn_crypt_mutex_lock(int mode, struct CRYPTO_dynlock_value* dyndata, const char* file, int line)
{
if(dyndata){
int res;
if(mode & CRYPTO_LOCK){
if(0 != (res = pthread_mutex_lock(&(dyndata->dyn_mutex)))){
S3FS_PRN_CRIT("pthread_mutex_lock returned: %d", res);
abort();
}
}else{
if(0 != (res = pthread_mutex_unlock(&(dyndata->dyn_mutex)))){
S3FS_PRN_CRIT("pthread_mutex_unlock returned: %d", res);
abort();
}
if(dyndata){
int result;
if(mode & CRYPTO_LOCK){
if(0 != (result = pthread_mutex_lock(&(dyndata->dyn_mutex)))){
S3FS_PRN_CRIT("pthread_mutex_lock returned: %d", result);
abort();
}
}else{
if(0 != (result = pthread_mutex_unlock(&(dyndata->dyn_mutex)))){
S3FS_PRN_CRIT("pthread_mutex_unlock returned: %d", result);
abort();
}
}
}
}
}
static void s3fs_destroy_dyn_crypt_mutex(struct CRYPTO_dynlock_value* dyndata, const char* file, int line) __attribute__ ((unused));
static void s3fs_destroy_dyn_crypt_mutex(struct CRYPTO_dynlock_value* dyndata, const char* file, int line)
{
if(dyndata){
int res = pthread_mutex_destroy(&(dyndata->dyn_mutex));
if(res != 0){
S3FS_PRN_CRIT("failed to destroy dyn_mutex");
abort();
if(dyndata){
int result = pthread_mutex_destroy(&(dyndata->dyn_mutex));
if(result != 0){
S3FS_PRN_CRIT("failed to destroy dyn_mutex");
abort();
}
delete dyndata;
}
delete dyndata;
}
}
bool s3fs_init_crypt_mutex()
{
if(s3fs_crypt_mutex){
S3FS_PRN_DBG("s3fs_crypt_mutex is not NULL, destroy it.");
if(!s3fs_destroy_crypt_mutex()){
S3FS_PRN_ERR("Failed to s3fs_crypt_mutex");
return false;
}
}
s3fs_crypt_mutex = new pthread_mutex_t[CRYPTO_num_locks()];
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
#if S3FS_PTHREAD_ERRORCHECK
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
#endif
for(int cnt = 0; cnt < CRYPTO_num_locks(); cnt++){
int res = pthread_mutex_init(&s3fs_crypt_mutex[cnt], &attr);
if(res != 0){
S3FS_PRN_CRIT("pthread_mutex_init returned: %d", res);
return false;
}
}
// static lock
CRYPTO_set_locking_callback(s3fs_crypt_mutex_lock);
CRYPTO_set_id_callback(s3fs_crypt_get_threadid);
// dynamic lock
CRYPTO_set_dynlock_create_callback(s3fs_dyn_crypt_mutex);
CRYPTO_set_dynlock_lock_callback(s3fs_dyn_crypt_mutex_lock);
CRYPTO_set_dynlock_destroy_callback(s3fs_destroy_dyn_crypt_mutex);
if(s3fs_crypt_mutex){
S3FS_PRN_DBG("s3fs_crypt_mutex is not nullptr, destroy it.");
return true;
// cppcheck-suppress unmatchedSuppression
// cppcheck-suppress knownConditionTrueFalse
if(!s3fs_destroy_crypt_mutex()){
S3FS_PRN_ERR("Failed to s3fs_crypt_mutex");
return false;
}
}
s3fs_crypt_mutex = new pthread_mutex_t[CRYPTO_num_locks()];
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
#if S3FS_PTHREAD_ERRORCHECK
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
#endif
for(int cnt = 0; cnt < CRYPTO_num_locks(); cnt++){
int result = pthread_mutex_init(&s3fs_crypt_mutex[cnt], &attr);
if(result != 0){
S3FS_PRN_CRIT("pthread_mutex_init returned: %d", result);
return false;
}
}
// static lock
CRYPTO_set_locking_callback(s3fs_crypt_mutex_lock);
CRYPTO_set_id_callback(s3fs_crypt_get_threadid);
// dynamic lock
CRYPTO_set_dynlock_create_callback(s3fs_dyn_crypt_mutex);
CRYPTO_set_dynlock_lock_callback(s3fs_dyn_crypt_mutex_lock);
CRYPTO_set_dynlock_destroy_callback(s3fs_destroy_dyn_crypt_mutex);
return true;
}
bool s3fs_destroy_crypt_mutex()
{
if(!s3fs_crypt_mutex){
return true;
}
CRYPTO_set_dynlock_destroy_callback(NULL);
CRYPTO_set_dynlock_lock_callback(NULL);
CRYPTO_set_dynlock_create_callback(NULL);
CRYPTO_set_id_callback(NULL);
CRYPTO_set_locking_callback(NULL);
for(int cnt = 0; cnt < CRYPTO_num_locks(); cnt++){
int res = pthread_mutex_destroy(&s3fs_crypt_mutex[cnt]);
if(res != 0){
S3FS_PRN_CRIT("failed to destroy s3fs_crypt_mutex[%d]", cnt);
abort();
if(!s3fs_crypt_mutex){
return true;
}
}
CRYPTO_cleanup_all_ex_data();
delete[] s3fs_crypt_mutex;
s3fs_crypt_mutex = NULL;
return true;
CRYPTO_set_dynlock_destroy_callback(nullptr);
CRYPTO_set_dynlock_lock_callback(nullptr);
CRYPTO_set_dynlock_create_callback(nullptr);
CRYPTO_set_id_callback(nullptr);
CRYPTO_set_locking_callback(nullptr);
for(int cnt = 0; cnt < CRYPTO_num_locks(); cnt++){
int result = pthread_mutex_destroy(&s3fs_crypt_mutex[cnt]);
if(result != 0){
S3FS_PRN_CRIT("failed to destroy s3fs_crypt_mutex[%d]", cnt);
abort();
}
}
CRYPTO_cleanup_all_ex_data();
delete[] s3fs_crypt_mutex;
s3fs_crypt_mutex = nullptr;
return true;
}
//-------------------------------------------------------------------
// Utility Function for HMAC
//-------------------------------------------------------------------
static bool s3fs_HMAC_RAW(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned char** digest, unsigned int* digestlen, bool is_sha256)
static std::unique_ptr<unsigned char[]> s3fs_HMAC_RAW(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned int* digestlen, bool is_sha256)
{
if(!key || !data || !digest || !digestlen){
return false;
}
(*digestlen) = EVP_MAX_MD_SIZE * sizeof(unsigned char);
*digest = new unsigned char[*digestlen];
if(is_sha256){
HMAC(EVP_sha256(), key, keylen, data, datalen, *digest, digestlen);
}else{
HMAC(EVP_sha1(), key, keylen, data, datalen, *digest, digestlen);
}
return true;
}
bool s3fs_HMAC(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned char** digest, unsigned int* digestlen)
{
return s3fs_HMAC_RAW(key, keylen, data, datalen, digest, digestlen, false);
}
bool s3fs_HMAC256(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned char** digest, unsigned int* digestlen)
{
return s3fs_HMAC_RAW(key, keylen, data, datalen, digest, digestlen, true);
}
//-------------------------------------------------------------------
// Utility Function for MD5
//-------------------------------------------------------------------
size_t get_md5_digest_length()
{
return MD5_DIGEST_LENGTH;
}
unsigned char* s3fs_md5hexsum(int fd, off_t start, ssize_t size)
{
MD5_CTX md5ctx;
char buf[512];
ssize_t bytes;
unsigned char* result;
if(-1 == size){
struct stat st;
if(-1 == fstat(fd, &st)){
return NULL;
if(!key || !data || !digestlen){
return nullptr;
}
size = static_cast<ssize_t>(st.st_size);
}
memset(buf, 0, 512);
MD5_Init(&md5ctx);
for(ssize_t total = 0; total < size; total += bytes){
bytes = 512 < (size - total) ? 512 : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
return NULL;
(*digestlen) = EVP_MAX_MD_SIZE * sizeof(unsigned char);
std::unique_ptr<unsigned char[]> digest(new unsigned char[*digestlen]);
if(is_sha256){
HMAC(EVP_sha256(), key, static_cast<int>(keylen), data, datalen, digest.get(), digestlen);
}else{
HMAC(EVP_sha1(), key, static_cast<int>(keylen), data, datalen, digest.get(), digestlen);
}
MD5_Update(&md5ctx, buf, bytes);
memset(buf, 0, 512);
}
result = new unsigned char[get_md5_digest_length()];
MD5_Final(result, &md5ctx);
return result;
return digest;
}
std::unique_ptr<unsigned char[]> s3fs_HMAC(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned int* digestlen)
{
return s3fs_HMAC_RAW(key, keylen, data, datalen, digestlen, false);
}
std::unique_ptr<unsigned char[]> s3fs_HMAC256(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned int* digestlen)
{
return s3fs_HMAC_RAW(key, keylen, data, datalen, digestlen, true);
}
#ifdef USE_OPENSSL_30
//-------------------------------------------------------------------
// Utility Function for MD5 (OpenSSL >= 3.0)
//-------------------------------------------------------------------
// [NOTE]
// OpenSSL 3.0 deprecated the MD5_*** low-level encryption functions,
// so we should use the high-level EVP API instead.
//
bool s3fs_md5(const unsigned char* data, size_t datalen, md5_t* digest)
{
unsigned int digestlen = static_cast<unsigned int>(digest->size());
const EVP_MD* md = EVP_get_digestbyname("md5");
EVP_MD_CTX* mdctx = EVP_MD_CTX_create();
EVP_DigestInit_ex(mdctx, md, nullptr);
EVP_DigestUpdate(mdctx, data, datalen);
EVP_DigestFinal_ex(mdctx, digest->data(), &digestlen);
EVP_MD_CTX_destroy(mdctx);
return true;
}
bool s3fs_md5_fd(int fd, off_t start, off_t size, md5_t* result)
{
EVP_MD_CTX* mdctx;
unsigned int md5_digest_len = static_cast<unsigned int>(result->size());
off_t bytes;
if(-1 == size){
struct stat st;
if(-1 == fstat(fd, &st)){
return false;
}
size = st.st_size;
}
// instead of MD5_Init
mdctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(mdctx, EVP_md5(), nullptr);
for(off_t total = 0; total < size; total += bytes){
const off_t len = 512;
char buf[len];
bytes = len < (size - total) ? len : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
EVP_MD_CTX_free(mdctx);
return false;
}
// instead of MD5_Update
EVP_DigestUpdate(mdctx, buf, bytes);
}
// instead of MD5_Final
EVP_DigestFinal_ex(mdctx, result->data(), &md5_digest_len);
EVP_MD_CTX_free(mdctx);
return true;
}
#else
//-------------------------------------------------------------------
// Utility Function for MD5 (OpenSSL < 3.0)
//-------------------------------------------------------------------
// TODO: Does this fail on OpenSSL < 3.0 and we need to use MD5_CTX functions?
bool s3fs_md5(const unsigned char* data, size_t datalen, md5_t* digest)
{
unsigned int digestlen = digest->size();
const EVP_MD* md = EVP_get_digestbyname("md5");
EVP_MD_CTX* mdctx = EVP_MD_CTX_create();
EVP_DigestInit_ex(mdctx, md, nullptr);
EVP_DigestUpdate(mdctx, data, datalen);
EVP_DigestFinal_ex(mdctx, digest->data(), &digestlen);
EVP_MD_CTX_destroy(mdctx);
return true;
}
bool s3fs_md5_fd(int fd, off_t start, off_t size, md5_t* result)
{
MD5_CTX md5ctx;
off_t bytes;
if(-1 == size){
struct stat st;
if(-1 == fstat(fd, &st)){
return false;
}
size = st.st_size;
}
MD5_Init(&md5ctx);
for(off_t total = 0; total < size; total += bytes){
const off_t len = 512;
char buf[len];
bytes = len < (size - total) ? len : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
return false;
}
MD5_Update(&md5ctx, buf, bytes);
}
MD5_Final(result->data(), &md5ctx);
return true;
}
#endif
//-------------------------------------------------------------------
// Utility Function for SHA256
//-------------------------------------------------------------------
size_t get_sha256_digest_length()
bool s3fs_sha256(const unsigned char* data, size_t datalen, sha256_t* digest)
{
return SHA256_DIGEST_LENGTH;
const EVP_MD* md = EVP_get_digestbyname("sha256");
EVP_MD_CTX* mdctx = EVP_MD_CTX_create();
EVP_DigestInit_ex(mdctx, md, nullptr);
EVP_DigestUpdate(mdctx, data, datalen);
unsigned int digestlen = static_cast<unsigned int>(digest->size());
EVP_DigestFinal_ex(mdctx, digest->data(), &digestlen);
EVP_MD_CTX_destroy(mdctx);
return true;
}
bool s3fs_sha256(const unsigned char* data, unsigned int datalen, unsigned char** digest, unsigned int* digestlen)
bool s3fs_sha256_fd(int fd, off_t start, off_t size, sha256_t* result)
{
(*digestlen) = EVP_MAX_MD_SIZE * sizeof(unsigned char);
*digest = new unsigned char[*digestlen];
const EVP_MD* md = EVP_get_digestbyname("sha256");
EVP_MD_CTX* sha256ctx;
off_t bytes;
const EVP_MD* md = EVP_get_digestbyname("sha256");
EVP_MD_CTX* mdctx = EVP_MD_CTX_create();
EVP_DigestInit_ex(mdctx, md, NULL);
EVP_DigestUpdate(mdctx, data, datalen);
EVP_DigestFinal_ex(mdctx, *digest, digestlen);
EVP_MD_CTX_destroy(mdctx);
return true;
}
unsigned char* s3fs_sha256hexsum(int fd, off_t start, ssize_t size)
{
const EVP_MD* md = EVP_get_digestbyname("sha256");
EVP_MD_CTX* sha256ctx;
char buf[512];
ssize_t bytes;
unsigned char* result;
if(-1 == size){
struct stat st;
if(-1 == fstat(fd, &st)){
return NULL;
if(-1 == fd){
return false;
}
size = static_cast<ssize_t>(st.st_size);
}
sha256ctx = EVP_MD_CTX_create();
EVP_DigestInit_ex(sha256ctx, md, NULL);
memset(buf, 0, 512);
for(ssize_t total = 0; total < size; total += bytes){
bytes = 512 < (size - total) ? 512 : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
EVP_MD_CTX_destroy(sha256ctx);
return NULL;
if(-1 == size){
struct stat st;
if(-1 == fstat(fd, &st)){
S3FS_PRN_ERR("fstat error(%d)", errno);
return false;
}
size = st.st_size;
}
EVP_DigestUpdate(sha256ctx, buf, bytes);
memset(buf, 0, 512);
}
result = new unsigned char[get_sha256_digest_length()];
EVP_DigestFinal_ex(sha256ctx, result, NULL);
EVP_MD_CTX_destroy(sha256ctx);
return result;
sha256ctx = EVP_MD_CTX_create();
EVP_DigestInit_ex(sha256ctx, md, nullptr);
for(off_t total = 0; total < size; total += bytes){
const off_t len = 512;
char buf[len];
bytes = len < (size - total) ? len : (size - total);
bytes = pread(fd, buf, bytes, start + total);
if(0 == bytes){
// end of file
break;
}else if(-1 == bytes){
// error
S3FS_PRN_ERR("file read error(%d)", errno);
EVP_MD_CTX_destroy(sha256ctx);
return false;
}
EVP_DigestUpdate(sha256ctx, buf, bytes);
}
EVP_DigestFinal_ex(sha256ctx, result->data(), nullptr);
EVP_MD_CTX_destroy(sha256ctx);
return true;
}
/*
@ -363,6 +439,6 @@ unsigned char* s3fs_sha256hexsum(int fd, off_t start, ssize_t size)
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

View File

@ -21,29 +21,46 @@
#ifndef S3FS_SEMAPHORE_H_
#define S3FS_SEMAPHORE_H_
//-------------------------------------------------------------------
// Class Semaphore
//-------------------------------------------------------------------
// portability wrapper for sem_t since macOS does not implement it
#ifdef __APPLE__
#include <dispatch/dispatch.h>
class Semaphore
{
public:
explicit Semaphore(int value) : value(value), sem(dispatch_semaphore_create(value)) {}
~Semaphore() {
// macOS cannot destroy a semaphore with posts less than the initializer
for(int i = 0; i < get_value(); ++i){
post();
}
dispatch_release(sem);
}
void wait() { dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); }
void post() { dispatch_semaphore_signal(sem); }
int get_value() const { return value; }
private:
const int value;
dispatch_semaphore_t sem;
public:
explicit Semaphore(int value) : value(value), sem(dispatch_semaphore_create(value)) {}
~Semaphore()
{
// macOS cannot destroy a semaphore with posts less than the initializer
for(int i = 0; i < get_value(); ++i){
post();
}
dispatch_release(sem);
}
Semaphore(const Semaphore&) = delete;
Semaphore(Semaphore&&) = delete;
Semaphore& operator=(const Semaphore&) = delete;
Semaphore& operator=(Semaphore&&) = delete;
void wait() { dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); }
bool try_wait()
{
if(0 == dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW)){
return true;
}else{
return false;
}
}
void post() { dispatch_semaphore_signal(sem); }
int get_value() const { return value; }
private:
const int value;
dispatch_semaphore_t sem;
};
#else
@ -53,23 +70,42 @@ class Semaphore
class Semaphore
{
public:
explicit Semaphore(int value) : value(value) { sem_init(&mutex, 0, value); }
~Semaphore() { sem_destroy(&mutex); }
void wait()
{
int r;
do {
r = sem_wait(&mutex);
} while (r == -1 && errno == EINTR);
}
void post() { sem_post(&mutex); }
int get_value() const { return value; }
private:
const int value;
sem_t mutex;
public:
explicit Semaphore(int value) : value(value) { sem_init(&mutex, 0, value); }
~Semaphore() { sem_destroy(&mutex); }
void wait()
{
int r;
do {
r = sem_wait(&mutex);
} while (r == -1 && errno == EINTR);
}
bool try_wait()
{
int result;
do{
result = sem_trywait(&mutex);
}while(result == -1 && errno == EINTR);
return (0 == result);
}
void post() { sem_post(&mutex); }
int get_value() const { return value; }
private:
const int value;
sem_t mutex;
};
#endif
#endif // S3FS_SEMAPHORE_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

File diff suppressed because it is too large Load Diff

View File

@ -17,22 +17,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_S3_H_
#define S3FS_S3_H_
#ifndef S3FS_S3FS_H_
#define S3FS_S3FS_H_
#define FUSE_USE_VERSION 26
static const int64_t FIVE_GB = 5LL * 1024LL * 1024LL * 1024LL;
#include <fuse.h>
#define S3FS_FUSE_EXIT() \
do{ \
struct fuse_context* pcxt = fuse_get_context(); \
if(pcxt){ \
fuse_exit(pcxt->fuse); \
} \
}while(0)
do{ \
struct fuse_context* pcxt = fuse_get_context(); \
if(pcxt){ \
fuse_exit(pcxt->fuse); \
} \
}while(0)
// [NOTE]
// s3fs use many small allocated chunk in heap area for stats
@ -60,34 +59,13 @@ do{ \
#define S3FS_MALLOCTRIM(pad)
#endif // S3FS_MALLOC_TRIM
#define S3FS_XMLFREEDOC(doc) \
do{ \
xmlFreeDoc(doc); \
S3FS_MALLOCTRIM(0); \
}while(0)
#define S3FS_XMLFREE(ptr) \
do{ \
xmlFree(ptr); \
S3FS_MALLOCTRIM(0); \
}while(0)
#define S3FS_XMLXPATHFREECONTEXT(ctx) \
do{ \
xmlXPathFreeContext(ctx); \
S3FS_MALLOCTRIM(0); \
}while(0)
#define S3FS_XMLXPATHFREEOBJECT(obj) \
do{ \
xmlXPathFreeObject(obj); \
S3FS_MALLOCTRIM(0); \
}while(0)
#endif // S3FS_S3_H_
#endif // S3FS_S3FS_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

View File

@ -17,12 +17,18 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_AUTH_H_
#define S3FS_AUTH_H_
#include <array>
#include <memory>
#include <string>
#include <sys/types.h>
typedef std::array<unsigned char, 16> md5_t;
typedef std::array<unsigned char, 32> sha256_t;
//-------------------------------------------------------------------
// Utility functions for Authentication
//-------------------------------------------------------------------
@ -30,24 +36,22 @@
// in common_auth.cpp
//
std::string s3fs_get_content_md5(int fd);
std::string s3fs_md5sum(int fd, off_t start, ssize_t size);
std::string s3fs_sha256sum(int fd, off_t start, ssize_t size);
std::string s3fs_sha256_hex_fd(int fd, off_t start, off_t size);
//
// in xxxxxx_auth.cpp
//
const char* s3fs_crypt_lib_name(void);
bool s3fs_init_global_ssl(void);
bool s3fs_destroy_global_ssl(void);
bool s3fs_init_crypt_mutex(void);
bool s3fs_destroy_crypt_mutex(void);
bool s3fs_HMAC(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned char** digest, unsigned int* digestlen);
bool s3fs_HMAC256(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned char** digest, unsigned int* digestlen);
size_t get_md5_digest_length(void);
unsigned char* s3fs_md5hexsum(int fd, off_t start, ssize_t size);
bool s3fs_sha256(const unsigned char* data, unsigned int datalen, unsigned char** digest, unsigned int* digestlen);
size_t get_sha256_digest_length(void);
unsigned char* s3fs_sha256hexsum(int fd, off_t start, ssize_t size);
const char* s3fs_crypt_lib_name();
bool s3fs_init_global_ssl();
bool s3fs_destroy_global_ssl();
bool s3fs_init_crypt_mutex();
bool s3fs_destroy_crypt_mutex();
std::unique_ptr<unsigned char[]> s3fs_HMAC(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned int* digestlen);
std::unique_ptr<unsigned char[]> s3fs_HMAC256(const void* key, size_t keylen, const unsigned char* data, size_t datalen, unsigned int* digestlen);
bool s3fs_md5(const unsigned char* data, size_t datalen, md5_t* result);
bool s3fs_md5_fd(int fd, off_t start, off_t size, md5_t* result);
bool s3fs_sha256(const unsigned char* data, size_t datalen, sha256_t* digest);
bool s3fs_sha256_fd(int fd, off_t start, off_t size, sha256_t* result);
#endif // S3FS_AUTH_H_
@ -56,6 +60,6 @@ unsigned char* s3fs_sha256hexsum(int fd, off_t start, ssize_t size);
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

1628
src/s3fs_cred.cpp Normal file

File diff suppressed because it is too large Load Diff

187
src/s3fs_cred.h Normal file
View File

@ -0,0 +1,187 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_CRED_H_
#define S3FS_CRED_H_
#include "autolock.h"
#include "s3fs_extcred.h"
//----------------------------------------------
// Typedefs
//----------------------------------------------
typedef std::map<std::string, std::string> iamcredmap_t;
//------------------------------------------------
// class S3fsCred
//------------------------------------------------
// This is a class for operating and managing Credentials(accesskey,
// secret key, tokens, etc.) used by S3fs.
// Operations related to Credentials are aggregated in this class.
//
// cppcheck-suppress ctuOneDefinitionRuleViolation ; for stub in test_curl_util.cpp
class S3fsCred
{
private:
static constexpr char ALLBUCKET_FIELDS_TYPE[] = ""; // special key for mapping(This name is absolutely not used as a bucket name)
static constexpr char KEYVAL_FIELDS_TYPE[] = "\t"; // special key for mapping(This name is absolutely not used as a bucket name)
static constexpr char AWS_ACCESSKEYID[] = "AWSAccessKeyId";
static constexpr char AWS_SECRETKEY[] = "AWSSecretKey";
static constexpr int IAM_EXPIRE_MERGIN = 20 * 60; // update timing
static constexpr char ECS_IAM_ENV_VAR[] = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI";
static constexpr char IAMCRED_ACCESSKEYID[] = "AccessKeyId";
static constexpr char IAMCRED_SECRETACCESSKEY[] = "SecretAccessKey";
static constexpr char IAMCRED_ROLEARN[] = "RoleArn";
static std::string bucket_name;
mutable pthread_mutex_t token_lock;
bool is_lock_init;
std::string passwd_file;
std::string aws_profile;
bool load_iamrole;
std::string AWSAccessKeyId; // Protect exclusively
std::string AWSSecretAccessKey; // Protect exclusively
std::string AWSAccessToken; // Protect exclusively
time_t AWSAccessTokenExpire; // Protect exclusively
bool is_ecs;
bool is_use_session_token;
bool is_ibm_iam_auth;
std::string IAM_cred_url;
int IAM_api_version; // Protect exclusively
std::string IAMv2_api_token; // Protect exclusively
size_t IAM_field_count;
std::string IAM_token_field;
std::string IAM_expiry_field;
std::string IAM_role; // Protect exclusively
bool set_builtin_cred_opts; // true if options other than "credlib" is set
std::string credlib; // credlib(name or path)
std::string credlib_opts; // options for credlib
void* hExtCredLib;
fp_VersionS3fsCredential pFuncCredVersion;
fp_InitS3fsCredential pFuncCredInit;
fp_FreeS3fsCredential pFuncCredFree;
fp_UpdateS3fsCredential pFuncCredUpdate;
public:
static constexpr char IAMv2_token_url[] = "http://169.254.169.254/latest/api/token";
static constexpr int IAMv2_token_ttl = 21600;
static constexpr char IAMv2_token_ttl_hdr[] = "X-aws-ec2-metadata-token-ttl-seconds";
static constexpr char IAMv2_token_hdr[] = "X-aws-ec2-metadata-token";
private:
static bool ParseIAMRoleFromMetaDataResponse(const char* response, std::string& rolename);
bool SetS3fsPasswdFile(const char* file);
bool IsSetPasswdFile() const;
bool SetAwsProfileName(const char* profile_name);
bool SetIAMRoleMetadataType(bool flag);
bool SetAccessKey(const char* AccessKeyId, const char* SecretAccessKey, AutoLock::Type type);
bool SetAccessKeyWithSessionToken(const char* AccessKeyId, const char* SecretAccessKey, const char * SessionToken, AutoLock::Type type);
bool IsSetAccessKeys(AutoLock::Type type) const;
bool SetIsECS(bool flag);
bool SetIsUseSessionToken(bool flag);
bool SetIsIBMIAMAuth(bool flag);
int SetIMDSVersion(int version, AutoLock::Type type);
int GetIMDSVersion(AutoLock::Type type) const;
bool SetIAMv2APIToken(const std::string& token, AutoLock::Type type);
std::string GetIAMv2APIToken(AutoLock::Type type) const;
bool SetIAMRole(const char* role, AutoLock::Type type);
std::string GetIAMRole(AutoLock::Type type) const;
bool IsSetIAMRole(AutoLock::Type type) const;
size_t SetIAMFieldCount(size_t field_count);
std::string SetIAMCredentialsURL(const char* url);
std::string SetIAMTokenField(const char* token_field);
std::string SetIAMExpiryField(const char* expiry_field);
bool IsReadableS3fsPasswdFile() const;
bool CheckS3fsPasswdFilePerms();
bool ParseS3fsPasswdFile(bucketkvmap_t& resmap);
bool ReadS3fsPasswdFile(AutoLock::Type type);
static int CheckS3fsCredentialAwsFormat(const kvmap_t& kvmap, std::string& access_key_id, std::string& secret_access_key);
bool ReadAwsCredentialFile(const std::string &filename, AutoLock::Type type);
bool InitialS3fsCredentials();
bool ParseIAMCredentialResponse(const char* response, iamcredmap_t& keyval);
bool GetIAMCredentialsURL(std::string& url, bool check_iam_role, AutoLock::Type type);
bool LoadIAMCredentials(AutoLock::Type type);
bool SetIAMCredentials(const char* response, AutoLock::Type type);
bool SetIAMRoleFromMetaData(const char* response, AutoLock::Type type);
bool SetExtCredLib(const char* arg);
bool IsSetExtCredLib() const;
bool SetExtCredLibOpts(const char* args);
bool IsSetExtCredLibOpts() const;
bool InitExtCredLib();
bool LoadExtCredLib();
bool UnloadExtCredLib();
bool UpdateExtCredentials(AutoLock::Type type);
static bool CheckForbiddenBucketParams();
public:
static bool SetBucket(const char* bucket);
static const std::string& GetBucket();
S3fsCred();
~S3fsCred();
S3fsCred(const S3fsCred&) = delete;
S3fsCred(S3fsCred&&) = delete;
S3fsCred& operator=(const S3fsCred&) = delete;
S3fsCred& operator=(S3fsCred&&) = delete;
bool IsIBMIAMAuth() const { return is_ibm_iam_auth; }
bool LoadIAMRoleFromMetaData();
bool CheckIAMCredentialUpdate(std::string* access_key_id = nullptr, std::string* secret_access_key = nullptr, std::string* access_token = nullptr);
const char* GetCredFuncVersion(bool detail) const;
int DetectParam(const char* arg);
bool CheckAllParams();
};
#endif // S3FS_CRED_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

144
src/s3fs_extcred.h Normal file
View File

@ -0,0 +1,144 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Takeshi Nakatani <ggtakec@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_EXTCRED_H_
#define S3FS_EXTCRED_H_
//-------------------------------------------------------------------
// Attributes(weak) : use only in s3fs-fuse internally
//-------------------------------------------------------------------
// [NOTE]
// This macro is only used inside s3fs-fuse.
// External projects that utilize this header file substitute empty
//values as follows:
//
#ifndef S3FS_FUNCATTR_WEAK
#define S3FS_FUNCATTR_WEAK
#endif
extern "C" {
//-------------------------------------------------------------------
// Prototype for External Credential 4 functions
//-------------------------------------------------------------------
//
// [Required] VersionS3fsCredential
//
// Returns the library name and version as a string.
//
extern const char* VersionS3fsCredential(bool detail) S3FS_FUNCATTR_WEAK;
//
// [Optional] InitS3fsCredential
//
// A function that does the necessary initialization after the library is
// loaded. This function is called only once immediately after loading the
// library.
// If there is a required initialization inside the library, implement it.
// Implementation of this function is optional and not required. If not
// implemented, it will not be called.
//
// const char* popts : String passed with the credlib_opts option. If the
// credlib_opts option is not specified, nullptr will be
// passed.
// char** pperrstr : pperrstr is used to pass the error message to the
// caller when an error occurs.
// If this pointer is not nullptr, you can allocate memory
// and set an error message to it. The allocated memory
// area is freed by the caller.
//
extern bool InitS3fsCredential(const char* popts, char** pperrstr) S3FS_FUNCATTR_WEAK;
//
// [Optional] FreeS3fsCredential
//
// A function that is called only once just before the library is unloaded.
// If there is a required discard process in the library, implement it.
// Implementation of this feature is optional and not required.
// If not implemented, it will not be called.
//
// char** pperrstr : pperrstr is used to pass the error message to the
// caller when an error occurs.
// If this pointer is not nullptr, you can allocate memory
// and set an error message to it. The allocated memory
// area is freed by the caller.
//
extern bool FreeS3fsCredential(char** pperrstr) S3FS_FUNCATTR_WEAK;
//
// [Required] UpdateS3fsCredential
//
// A function that updates the token.
//
// char** ppaccess_key_id : Allocate and set "Access Key ID" string
// area to *ppaccess_key_id.
// char** ppserect_access_key : Allocate and set "Access Secret Key ID"
// string area to *ppserect_access_key.
// char** ppaccess_token : Allocate and set "Token" string area to
// *ppaccess_token.
// long long* ptoken_expire : Set token expire time(time_t) value to
// *ptoken_expire.
// This is essentially a time_t* variable.
// To avoid system differences about time_t
// size, long long* is used.
// When setting the value, cast from time_t
// to long long to set the value.
// char** pperrstr : pperrstr is used to pass the error message to the
// caller when an error occurs.
//
// For all argument of the character string pointer(char **) set the
// allocated string area. The allocated area is freed by the caller.
//
extern bool UpdateS3fsCredential(char** ppaccess_key_id, char** ppserect_access_key, char** ppaccess_token, long long* ptoken_expire, char** pperrstr) S3FS_FUNCATTR_WEAK;
//---------------------------------------------------------
// Typedef Prototype function
//---------------------------------------------------------
//
// const char* VersionS3fsCredential()
//
typedef const char* (*fp_VersionS3fsCredential)(bool detail);
//
// bool InitS3fsCredential(char** pperrstr)
//
typedef bool (*fp_InitS3fsCredential)(const char* popts, char** pperrstr);
//
// bool FreeS3fsCredential(char** pperrstr)
//
typedef bool (*fp_FreeS3fsCredential)(char** pperrstr);
//
// bool UpdateS3fsCredential(char** ppaccess_key_id, char** ppserect_access_key, char** ppaccess_token, long long* ptoken_expire, char** pperrstr)
//
typedef bool (*fp_UpdateS3fsCredential)(char** ppaccess_key_id, char** ppserect_access_key, char** ppaccess_token, long long* ptoken_expire, char** pperrstr);
} // extern "C"
#endif // S3FS_EXTCRED_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

45
src/s3fs_global.cpp Normal file
View File

@ -0,0 +1,45 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <string>
//-------------------------------------------------------------------
// Global variables
//-------------------------------------------------------------------
bool foreground = false;
bool nomultipart = false;
bool pathrequeststyle = false;
bool complement_stat = false;
bool noxmlns = false;
std::string program_name;
std::string service_path = "/";
std::string s3host = "https://s3.amazonaws.com";
std::string endpoint = "us-east-1";
std::string cipher_suites;
std::string instance_name;
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

677
src/s3fs_help.cpp Normal file
View File

@ -0,0 +1,677 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cstdio>
#include <string>
#include "common.h"
#include "s3fs_help.h"
#include "s3fs_auth.h"
//-------------------------------------------------------------------
// Contents
//-------------------------------------------------------------------
static constexpr char help_string[] =
"\n"
"Mount an Amazon S3 bucket as a file system.\n"
"\n"
"Usage:\n"
" mounting\n"
" s3fs bucket[:/path] mountpoint [options]\n"
" s3fs mountpoint [options (must specify bucket= option)]\n"
"\n"
" unmounting\n"
" umount mountpoint\n"
"\n"
" General forms for s3fs and FUSE/mount options:\n"
" -o opt[,opt...]\n"
" -o opt [-o opt] ...\n"
"\n"
" utility mode (remove interrupted multipart uploading objects)\n"
" s3fs --incomplete-mpu-list (-u) bucket\n"
" s3fs --incomplete-mpu-abort[=all | =<date format>] bucket\n"
"\n"
"s3fs Options:\n"
"\n"
" Most s3fs options are given in the form where \"opt\" is:\n"
"\n"
" <option_name>=<option_value>\n"
"\n"
" bucket\n"
" - if it is not specified bucket name (and path) in command line,\n"
" must specify this option after -o option for bucket name.\n"
"\n"
" default_acl (default=\"private\")\n"
" - the default canned acl to apply to all written s3 objects,\n"
" e.g., private, public-read. see\n"
" https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl\n"
" for the full list of canned ACLs\n"
"\n"
" retries (default=\"5\")\n"
" - number of times to retry a failed S3 transaction\n"
"\n"
" tmpdir (default=\"/tmp\")\n"
" - local folder for temporary files.\n"
"\n"
" use_cache (default=\"\" which means disabled)\n"
" - local folder to use for local file cache\n"
"\n"
" check_cache_dir_exist (default is disable)\n"
" - if use_cache is set, check if the cache directory exists.\n"
" If this option is not specified, it will be created at runtime\n"
" when the cache directory does not exist.\n"
"\n"
" del_cache (delete local file cache)\n"
" - delete local file cache when s3fs starts and exits.\n"
"\n"
" storage_class (default=\"standard\")\n"
" - store object with specified storage class. Possible values:\n"
" standard, standard_ia, onezone_ia, reduced_redundancy,\n"
" intelligent_tiering, glacier, glacier_ir, and deep_archive.\n"
"\n"
" use_rrs (default is disable)\n"
" - use Amazon's Reduced Redundancy Storage.\n"
" this option can not be specified with use_sse.\n"
" (can specify use_rrs=1 for old version)\n"
" this option has been replaced by new storage_class option.\n"
"\n"
" use_sse (default is disable)\n"
" - Specify three type Amazon's Server-Site Encryption: SSE-S3,\n"
" SSE-C or SSE-KMS. SSE-S3 uses Amazon S3-managed encryption\n"
" keys, SSE-C uses customer-provided encryption keys, and\n"
" SSE-KMS uses the master key which you manage in AWS KMS.\n"
" You can specify \"use_sse\" or \"use_sse=1\" enables SSE-S3\n"
" type (use_sse=1 is old type parameter).\n"
" Case of setting SSE-C, you can specify \"use_sse=custom\",\n"
" \"use_sse=custom:<custom key file path>\" or\n"
" \"use_sse=<custom key file path>\" (only <custom key file path>\n"
" specified is old type parameter). You can use \"c\" for\n"
" short \"custom\".\n"
" The custom key file must be 600 permission. The file can\n"
" have some lines, each line is one SSE-C key. The first line\n"
" in file is used as Customer-Provided Encryption Keys for\n"
" uploading and changing headers etc. If there are some keys\n"
" after first line, those are used downloading object which\n"
" are encrypted by not first key. So that, you can keep all\n"
" SSE-C keys in file, that is SSE-C key history.\n"
" If you specify \"custom\" (\"c\") without file path, you\n"
" need to set custom key by load_sse_c option or AWSSSECKEYS\n"
" environment. (AWSSSECKEYS environment has some SSE-C keys\n"
" with \":\" separator.) This option is used to decide the\n"
" SSE type. So that if you do not want to encrypt a object\n"
" object at uploading, but you need to decrypt encrypted\n"
" object at downloading, you can use load_sse_c option instead\n"
" of this option.\n"
" For setting SSE-KMS, specify \"use_sse=kmsid\" or\n"
" \"use_sse=kmsid:<kms id>\". You can use \"k\" for short \"kmsid\".\n"
" If you san specify SSE-KMS type with your <kms id> in AWS\n"
" KMS, you can set it after \"kmsid:\" (or \"k:\"). If you\n"
" specify only \"kmsid\" (\"k\"), you need to set AWSSSEKMSID\n"
" environment which value is <kms id>. You must be careful\n"
" about that you can not use the KMS id which is not same EC2\n"
" region.\n"
" Additionally, if you specify SSE-KMS, your endpoints must use\n"
" Secure Sockets Layer(SSL) or Transport Layer Security(TLS).\n"
"\n"
" load_sse_c - specify SSE-C keys\n"
" Specify the custom-provided encryption keys file path for decrypting\n"
" at downloading.\n"
" If you use the custom-provided encryption key at uploading, you\n"
" specify with \"use_sse=custom\". The file has many lines, one line\n"
" means one custom key. So that you can keep all SSE-C keys in file,\n"
" that is SSE-C key history. AWSSSECKEYS environment is as same as this\n"
" file contents.\n"
"\n"
" public_bucket (default=\"\" which means disabled)\n"
" - anonymously mount a public bucket when set to 1, ignores the \n"
" $HOME/.passwd-s3fs and /etc/passwd-s3fs files.\n"
" S3 does not allow copy object api for anonymous users, then\n"
" s3fs sets nocopyapi option automatically when public_bucket=1\n"
" option is specified.\n"
"\n"
" passwd_file (default=\"\")\n"
" - specify which s3fs password file to use\n"
"\n"
" ahbe_conf (default=\"\" which means disabled)\n"
" - This option specifies the configuration file path which\n"
" file is the additional HTTP header by file (object) extension.\n"
" The configuration file format is below:\n"
" -----------\n"
" line = [file suffix or regex] HTTP-header [HTTP-values]\n"
" file suffix = file (object) suffix, if this field is empty,\n"
" it means \"reg:(.*)\".(=all object).\n"
" regex = regular expression to match the file (object) path.\n"
" this type starts with \"reg:\" prefix.\n"
" HTTP-header = additional HTTP header name\n"
" HTTP-values = additional HTTP header value\n"
" -----------\n"
" Sample:\n"
" -----------\n"
" .gz Content-Encoding gzip\n"
" .Z Content-Encoding compress\n"
" reg:^/MYDIR/(.*)[.]t2$ Content-Encoding text2\n"
" -----------\n"
" A sample configuration file is uploaded in \"test\" directory.\n"
" If you specify this option for set \"Content-Encoding\" HTTP \n"
" header, please take care for RFC 2616.\n"
"\n"
" profile (default=\"default\")\n"
" - Choose a profile from ${HOME}/.aws/credentials to authenticate\n"
" against S3. Note that this format matches the AWS CLI format and\n"
" differs from the s3fs passwd format.\n"
"\n"
" connect_timeout (default=\"300\" seconds)\n"
" - time to wait for connection before giving up\n"
"\n"
" readwrite_timeout (default=\"120\" seconds)\n"
" - time to wait between read/write activity before giving up\n"
"\n"
" list_object_max_keys (default=\"1000\")\n"
" - specify the maximum number of keys returned by S3 list object\n"
" API. The default is 1000. you can set this value to 1000 or more.\n"
"\n"
" max_stat_cache_size (default=\"100,000\" entries (about 40MB))\n"
" - maximum number of entries in the stat cache, and this maximum is\n"
" also treated as the number of symbolic link cache.\n"
"\n"
" stat_cache_expire (default is 900))\n"
" - specify expire time (seconds) for entries in the stat cache.\n"
" This expire time indicates the time since stat cached. and this\n"
" is also set to the expire time of the symbolic link cache.\n"
"\n"
" stat_cache_interval_expire (default is 900)\n"
" - specify expire time (seconds) for entries in the stat cache(and\n"
" symbolic link cache).\n"
" This expire time is based on the time from the last access time\n"
" of the stat cache. This option is exclusive with stat_cache_expire,\n"
" and is left for compatibility with older versions.\n"
"\n"
" disable_noobj_cache (default is enable)\n"
" - By default s3fs memorizes when an object does not exist up until\n"
" the stat cache timeout. This caching can cause staleness for\n"
" applications. If disabled, s3fs will not memorize objects and may\n"
" cause extra HeadObject requests and reduce performance.\n"
"\n"
" no_check_certificate\n"
" - server certificate won't be checked against the available \n"
" certificate authorities.\n"
"\n"
" ssl_verify_hostname (default=\"2\")\n"
" - When 0, do not verify the SSL certificate against the hostname.\n"
"\n"
" ssl_client_cert (default=\"\")\n"
" - Specify an SSL client certificate.\n"
" Specify this optional parameter in the following format:\n"
" \"<SSL Cert>[:<Cert Type>[:<Private Key>[:<Key Type>\n"
" [:<Password>]]]]\"\n"
" <SSL Cert>: Client certificate.\n"
" Specify the file path or NickName(for NSS, etc.).\n"
" <Cert Type>: Type of certificate, default is \"PEM\"(optional).\n"
" <Private Key>: Certificate's private key file(optional).\n"
" <Key Type>: Type of private key, default is \"PEM\"(optional).\n"
" <Password>: Passphrase of the private key(optional).\n"
" It is also possible to omit this value and specify\n"
" it using the environment variable\n"
" \"S3FS_SSL_PRIVKEY_PASSWORD\".\n"
"\n"
" nodnscache (disable DNS cache)\n"
" - s3fs is always using DNS cache, this option make DNS cache disable.\n"
"\n"
" nosscache (disable SSL session cache)\n"
" - s3fs is always using SSL session cache, this option make SSL \n"
" session cache disable.\n"
"\n"
" multireq_max (default=\"20\")\n"
" - maximum number of parallel request for listing objects.\n"
"\n"
" parallel_count (default=\"5\")\n"
" - number of parallel request for uploading big objects.\n"
" s3fs uploads large object (over 20MB) by multipart post request, \n"
" and sends parallel requests.\n"
" This option limits parallel request count which s3fs requests \n"
" at once. It is necessary to set this value depending on a CPU \n"
" and a network band.\n"
"\n"
" multipart_size (default=\"10\")\n"
" - part size, in MB, for each multipart request.\n"
" The minimum value is 5 MB and the maximum value is 5 GB.\n"
"\n"
" multipart_copy_size (default=\"512\")\n"
" - part size, in MB, for each multipart copy request, used for\n"
" renames and mixupload.\n"
" The minimum value is 5 MB and the maximum value is 5 GB.\n"
" Must be at least 512 MB to copy the maximum 5 TB object size\n"
" but lower values may improve performance.\n"
"\n"
" max_dirty_data (default=\"5120\")\n"
" - flush dirty data to S3 after a certain number of MB written.\n"
" The minimum value is 50 MB. -1 value means disable.\n"
" Cannot be used with nomixupload.\n"
"\n"
" bucket_size (default=maximum long unsigned integer value)\n"
" - The size of the bucket with which the corresponding\n"
" elements of the statvfs structure will be filled. The option\n"
" argument is an integer optionally followed by a\n"
" multiplicative suffix (GB, GiB, TB, TiB, PB, PiB,\n"
" EB, EiB) (no spaces in between). If no suffix is supplied,\n"
" bytes are assumed; eg: 20000000, 30GB, 45TiB. Note that\n"
" s3fs does not compute the actual volume size (too\n"
" expensive): by default it will assume the maximum possible\n"
" size; however, since this may confuse other software which\n"
" uses s3fs, the advertised bucket size can be set with this\n"
" option.\n"
"\n"
" ensure_diskfree (default 0)\n"
" - sets MB to ensure disk free space. This option means the\n"
" threshold of free space size on disk which is used for the\n"
" cache file by s3fs. s3fs makes file for\n"
" downloading, uploading and caching files. If the disk free\n"
" space is smaller than this value, s3fs do not use disk space\n"
" as possible in exchange for the performance.\n"
"\n"
" free_space_ratio (default=\"10\")\n"
" - sets min free space ratio of the disk.\n"
" The value of this option can be between 0 and 100. It will control\n"
" the size of the cache according to this ratio to ensure that the\n"
" idle ratio of the disk is greater than this value.\n"
" For example, when the disk space is 50GB, the default value will\n"
" ensure that the disk will reserve at least 50GB * 10%% = 5GB of\n"
" remaining space.\n"
"\n"
" multipart_threshold (default=\"25\")\n"
" - threshold, in MB, to use multipart upload instead of\n"
" single-part. Must be at least 5 MB.\n"
"\n"
" singlepart_copy_limit (default=\"512\")\n"
" - maximum size, in MB, of a single-part copy before trying \n"
" multipart copy.\n"
"\n"
" host (default=\"https://s3.amazonaws.com\")\n"
" - Set a non-Amazon host, e.g., https://example.com.\n"
"\n"
" servicepath (default=\"/\")\n"
" - Set a service path when the non-Amazon host requires a prefix.\n"
"\n"
" url (default=\"https://s3.amazonaws.com\")\n"
" - sets the url to use to access Amazon S3. If you want to use HTTP,\n"
" then you can set \"url=http://s3.amazonaws.com\".\n"
" If you do not use https, please specify the URL with the url\n"
" option.\n"
"\n"
" endpoint (default=\"us-east-1\")\n"
" - sets the endpoint to use on signature version 4\n"
" If this option is not specified, s3fs uses \"us-east-1\" region as\n"
" the default. If the s3fs could not connect to the region specified\n"
" by this option, s3fs could not run. But if you do not specify this\n"
" option, and if you can not connect with the default region, s3fs\n"
" will retry to automatically connect to the other region. So s3fs\n"
" can know the correct region name, because s3fs can find it in an\n"
" error from the S3 server.\n"
"\n"
" sigv2 (default is signature version 4 falling back to version 2)\n"
" - sets signing AWS requests by using only signature version 2\n"
"\n"
" sigv4 (default is signature version 4 falling back to version 2)\n"
" - sets signing AWS requests by using only signature version 4\n"
"\n"
" mp_umask (default is \"0000\")\n"
" - sets umask for the mount point directory.\n"
" If allow_other option is not set, s3fs allows access to the mount\n"
" point only to the owner. In the opposite case s3fs allows access\n"
" to all users as the default. But if you set the allow_other with\n"
" this option, you can control the permissions of the\n"
" mount point by this option like umask.\n"
"\n"
" umask (default is \"0000\")\n"
" - sets umask for files under the mountpoint. This can allow\n"
" users other than the mounting user to read and write to files\n"
" that they did not create.\n"
"\n"
" nomultipart (disable multipart uploads)\n"
"\n"
" streamupload (default is disable)\n"
" - Enable stream upload.\n"
" If this option is enabled, a sequential upload will be performed\n"
" in parallel with the write from the part that has been written\n"
" during a multipart upload.\n"
" This is expected to give better performance than other upload\n"
" functions.\n"
" Note that this option is still experimental and may change in the\n"
" future.\n"
"\n"
" max_thread_count (default is \"5\")\n"
" - Specifies the number of threads waiting for stream uploads.\n"
" Note that this option and Streamm Upload are still experimental\n"
" and subject to change in the future.\n"
" This option will be merged with \"parallel_count\" in the future.\n"
"\n"
" enable_content_md5 (default is disable)\n"
" - Allow S3 server to check data integrity of uploads via the\n"
" Content-MD5 header. This can add CPU overhead to transfers.\n"
"\n"
" enable_unsigned_payload (default is disable)\n"
" - Do not calculate Content-SHA256 for PutObject and UploadPart\n"
" payloads. This can reduce CPU overhead to transfers.\n"
"\n"
" ecs (default is disable)\n"
" - This option instructs s3fs to query the ECS container credential\n"
" metadata address instead of the instance metadata address.\n"
"\n"
" iam_role (default is no IAM role)\n"
" - This option requires the IAM role name or \"auto\". If you specify\n"
" \"auto\", s3fs will automatically use the IAM role names that are set\n"
" to an instance. If you specify this option without any argument, it\n"
" is the same as that you have specified the \"auto\".\n"
"\n"
" imdsv1only (default is to use IMDSv2 with fallback to v1)\n"
" - AWS instance metadata service, used with IAM role authentication,\n"
" supports the use of an API token. If you're using an IAM role\n"
" in an environment that does not support IMDSv2, setting this flag\n"
" will skip retrieval and usage of the API token when retrieving\n"
" IAM credentials.\n"
"\n"
" ibm_iam_auth (default is not using IBM IAM authentication)\n"
" - This option instructs s3fs to use IBM IAM authentication.\n"
" In this mode, the AWSAccessKey and AWSSecretKey will be used as\n"
" IBM's Service-Instance-ID and APIKey, respectively.\n"
"\n"
" ibm_iam_endpoint (default is https://iam.cloud.ibm.com)\n"
" - sets the URL to use for IBM IAM authentication.\n"
"\n"
" credlib (default=\"\" which means disabled)\n"
" - Specifies the shared library that handles the credentials\n"
" containing the authentication token.\n"
" If this option is specified, the specified credential and token\n"
" processing provided by the shared library ant will be performed\n"
" instead of the built-in credential processing.\n"
" This option cannot be specified with passwd_file, profile,\n"
" use_session_token, ecs, ibm_iam_auth, ibm_iam_endpoint, imdsv1only\n"
" and iam_role option.\n"
"\n"
" credlib_opts (default=\"\" which means disabled)\n"
" - Specifies the options to pass when the shared library specified\n"
" in credlib is loaded and then initialized.\n"
" For the string specified in this option, specify the string defined\n"
" by the shared library.\n"
"\n"
" use_xattr (default is not handling the extended attribute)\n"
" Enable to handle the extended attribute (xattrs).\n"
" If you set this option, you can use the extended attribute.\n"
" For example, encfs and ecryptfs need to support the extended attribute.\n"
" Notice: if s3fs handles the extended attribute, s3fs can not work to\n"
" copy command with preserve=mode.\n"
"\n"
" noxmlns (disable registering xml name space)\n"
" disable registering xml name space for response of \n"
" ListBucketResult and ListVersionsResult etc. Default name \n"
" space is looked up from \"http://s3.amazonaws.com/doc/2006-03-01\".\n"
" This option should not be specified now, because s3fs looks up\n"
" xmlns automatically after v1.66.\n"
"\n"
" nomixupload (disable copy in multipart uploads)\n"
" Disable to use PUT (copy api) when multipart uploading large size objects.\n"
" By default, when doing multipart upload, the range of unchanged data\n"
" will use PUT (copy api) whenever possible.\n"
" When nocopyapi or norenameapi is specified, use of PUT (copy api) is\n"
" invalidated even if this option is not specified.\n"
"\n"
" nocopyapi (for other incomplete compatibility object storage)\n"
" Enable compatibility with S3-like APIs which do not support\n"
" PUT (copy api).\n"
" If you set this option, s3fs do not use PUT with \n"
" \"x-amz-copy-source\" (copy api). Because traffic is increased\n"
" 2-3 times by this option, we do not recommend this.\n"
"\n"
" norenameapi (for other incomplete compatibility object storage)\n"
" Enable compatibility with S3-like APIs which do not support\n"
" PUT (copy api).\n"
" This option is a subset of nocopyapi option. The nocopyapi\n"
" option does not use copy-api for all command (ex. chmod, chown,\n"
" touch, mv, etc), but this option does not use copy-api for\n"
" only rename command (ex. mv). If this option is specified with\n"
" nocopyapi, then s3fs ignores it.\n"
"\n"
" use_path_request_style (use legacy API calling style)\n"
" Enable compatibility with S3-like APIs which do not support\n"
" the virtual-host request style, by using the older path request\n"
" style.\n"
"\n"
" listobjectsv2 (use ListObjectsV2)\n"
" Issue ListObjectsV2 instead of ListObjects, useful on object\n"
" stores without ListObjects support.\n"
"\n"
" noua (suppress User-Agent header)\n"
" Usually s3fs outputs of the User-Agent in \"s3fs/<version> (commit\n"
" hash <hash>; <using ssl library name>)\" format.\n"
" If this option is specified, s3fs suppresses the output of the\n"
" User-Agent.\n"
"\n"
" cipher_suites\n"
" Customize the list of TLS cipher suites.\n"
" Expects a colon separated list of cipher suite names.\n"
" A list of available cipher suites, depending on your TLS engine,\n"
" can be found on the CURL library documentation:\n"
" https://curl.haxx.se/docs/ssl-ciphers.html\n"
"\n"
" instance_name - The instance name of the current s3fs mountpoint.\n"
" This name will be added to logging messages and user agent headers sent by s3fs.\n"
"\n"
" complement_stat (complement lack of file/directory mode)\n"
" s3fs complements lack of information about file/directory mode\n"
" if a file or a directory object does not have x-amz-meta-mode\n"
" header. As default, s3fs does not complements stat information\n"
" for a object, then the object will not be able to be allowed to\n"
" list/modify.\n"
"\n"
" compat_dir (enable support of alternative directory names)\n"
" s3fs supports two different naming schemas \"dir/\" and\n"
" \"dir\" to map directory names to S3 objects and\n"
" vice versa by default. As a third variant, directories can be\n"
" determined indirectly if there is a file object with a path (e.g.\n"
" \"/dir/file\") but without the parent directory.\n"
" This option enables a fourth variant, \"dir_$folder$\", created by\n"
" older applications.\n"
" \n"
" S3fs uses only the first schema \"dir/\" to create S3 objects for\n"
" directories."
" \n"
" The support for these different naming schemas causes an increased\n"
" communication effort.\n"
"\n"
" use_wtf8 - support arbitrary file system encoding.\n"
" S3 requires all object names to be valid UTF-8. But some\n"
" clients, notably Windows NFS clients, use their own encoding.\n"
" This option re-encodes invalid UTF-8 object names into valid\n"
" UTF-8 by mapping offending codes into a 'private' codepage of the\n"
" Unicode set.\n"
" Useful on clients not using UTF-8 as their file system encoding.\n"
"\n"
" use_session_token - indicate that session token should be provided.\n"
" If credentials are provided by environment variables this switch\n"
" forces presence check of AWSSESSIONTOKEN variable.\n"
" Otherwise an error is returned.\n"
"\n"
" requester_pays (default is disable)\n"
" This option instructs s3fs to enable requests involving\n"
" Requester Pays buckets.\n"
" It includes the 'x-amz-request-payer=requester' entry in the\n"
" request header.\n"
"\n"
" mime (default is \"/etc/mime.types\")\n"
" Specify the path of the mime.types file.\n"
" If this option is not specified, the existence of \"/etc/mime.types\"\n"
" is checked, and that file is loaded as mime information.\n"
" If this file does not exist on macOS, then \"/etc/apache2/mime.types\"\n"
" is checked as well.\n"
"\n"
" proxy (default=\"\")\n"
" This option specifies a proxy to S3 server.\n"
" Specify the proxy with '[<scheme://]hostname(fqdn)[:<port>]' formatted.\n"
" '<schema>://' can be omitted, and 'http://' is used when omitted.\n"
" Also, ':<port>' can also be omitted. If omitted, port 443 is used for\n"
" HTTPS schema, and port 1080 is used otherwise.\n"
" This option is the same as the curl command's '--proxy(-x)' option and\n"
" libcurl's 'CURLOPT_PROXY' flag.\n"
" This option is equivalent to and takes precedence over the environment\n"
" variables 'http_proxy', 'all_proxy', etc.\n"
"\n"
" proxy_cred_file (default=\"\")\n"
" This option specifies the file that describes the username and\n"
" passphrase for authentication of the proxy when the HTTP schema\n"
" proxy is specified by the 'proxy' option.\n"
" Username and passphrase are valid only for HTTP schema. If the HTTP\n"
" proxy does not require authentication, this option is not required.\n"
" Separate the username and passphrase with a ':' character and\n"
" specify each as a URL-encoded string.\n"
"\n"
" ipresolve (default=\"whatever\")\n"
" Select what type of IP addresses to use when establishing a\n"
" connection.\n"
" Default('whatever') can use addresses of all IP versions(IPv4 and\n"
" IPv6) that your system allows. If you specify 'IPv4', only IPv4\n"
" addresses are used. And when 'IPv6'is specified, only IPv6 addresses\n"
" will be used.\n"
"\n"
" logfile - specify the log output file.\n"
" s3fs outputs the log file to syslog. Alternatively, if s3fs is\n"
" started with the \"-f\" option specified, the log will be output\n"
" to the stdout/stderr.\n"
" You can use this option to specify the log file that s3fs outputs.\n"
" If you specify a log file with this option, it will reopen the log\n"
" file when s3fs receives a SIGHUP signal. You can use the SIGHUP\n"
" signal for log rotation.\n"
"\n"
" dbglevel (default=\"crit\")\n"
" Set the debug message level. set value as crit (critical), err\n"
" (error), warn (warning), info (information) to debug level.\n"
" default debug level is critical. If s3fs run with \"-d\" option,\n"
" the debug level is set information. When s3fs catch the signal\n"
" SIGUSR2, the debug level is bump up.\n"
"\n"
" curldbg - put curl debug message\n"
" Put the debug message from libcurl when this option is specified.\n"
" Specify \"normal\" or \"body\" for the parameter.\n"
" If the parameter is omitted, it is the same as \"normal\".\n"
" If \"body\" is specified, some API communication body data will be\n"
" output in addition to the debug message output as \"normal\".\n"
"\n"
" no_time_stamp_msg - no time stamp in debug message\n"
" The time stamp is output to the debug message by default.\n"
" If this option is specified, the time stamp will not be output\n"
" in the debug message.\n"
" It is the same even if the environment variable \"S3FS_MSGTIMESTAMP\"\n"
" is set to \"no\".\n"
"\n"
" set_check_cache_sigusr1 (default is stdout)\n"
" If the cache is enabled, you can check the integrity of the\n"
" cache file and the cache file's stats info file.\n"
" This option is specified and when sending the SIGUSR1 signal\n"
" to the s3fs process checks the cache status at that time.\n"
" This option can take a file path as parameter to output the\n"
" check result to that file. The file path parameter can be omitted.\n"
" If omitted, the result will be output to stdout or syslog.\n"
"\n"
" update_parent_dir_stat (default is disable)\n"
" The parent directory's mtime and ctime are updated when a file or\n"
" directory is created or deleted (when the parent directory's inode is\n"
" updated).\n"
" By default, parent directory statistics are not updated.\n"
"\n"
"FUSE/mount Options:\n"
"\n"
" Most of the generic mount options described in 'man mount' are\n"
" supported (ro, rw, suid, nosuid, dev, nodev, exec, noexec, atime,\n"
" noatime, sync async, dirsync). Filesystems are mounted with\n"
" '-onodev,nosuid' by default, which can only be overridden by a\n"
" privileged user.\n"
" \n"
" There are many FUSE specific mount options that can be specified.\n"
" e.g. allow_other See the FUSE's README for the full set.\n"
"\n"
"Utility mode Options:\n"
"\n"
" -u, --incomplete-mpu-list\n"
" Lists multipart incomplete objects uploaded to the specified\n"
" bucket.\n"
" --incomplete-mpu-abort (=all or =<date format>)\n"
" Delete the multipart incomplete object uploaded to the specified\n"
" bucket.\n"
" If \"all\" is specified for this option, all multipart incomplete\n"
" objects will be deleted. If you specify no argument as an option,\n"
" objects older than 24 hours (24H) will be deleted (This is the\n"
" default value). You can specify an optional date format. It can\n"
" be specified as year, month, day, hour, minute, second, and it is\n"
" expressed as \"Y\", \"M\", \"D\", \"h\", \"m\", \"s\" respectively.\n"
" For example, \"1Y6M10D12h30m30s\".\n"
"\n"
"Miscellaneous Options:\n"
"\n"
" -h, --help Output this help.\n"
" --version Output version info.\n"
" -d --debug Turn on DEBUG messages to syslog. Specifying -d\n"
" twice turns on FUSE debug messages to STDOUT.\n"
" -f FUSE foreground option - do not run as daemon.\n"
" -s FUSE single-threaded option\n"
" disable multi-threaded operation\n"
"\n"
"\n"
"s3fs home page: <https://github.com/s3fs-fuse/s3fs-fuse>\n"
;
//-------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------
void show_usage()
{
printf("Usage: %s BUCKET:[PATH] MOUNTPOINT [OPTION]...\n", program_name.c_str());
}
void show_help()
{
show_usage();
printf(help_string);
}
void show_version()
{
printf(
"Amazon Simple Storage Service File System V%s (commit:%s) with %s\n"
"Copyright (C) 2010 Randy Rizun <rrizun@gmail.com>\n"
"License GPL2: GNU GPL version 2 <https://gnu.org/licenses/gpl.html>\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n",
VERSION, COMMIT_HASH_VAL, s3fs_crypt_lib_name());
}
const char* short_version()
{
static constexpr char short_ver[] = "s3fs version " VERSION "(" COMMIT_HASH_VAL ")";
return short_ver;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

41
src/s3fs_help.h Normal file
View File

@ -0,0 +1,41 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_S3FS_HELP_H_
#define S3FS_S3FS_HELP_H_
//-------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------
void show_usage();
void show_help();
void show_version();
const char* short_version();
#endif // S3FS_S3FS_HELP_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

306
src/s3fs_logger.cpp Normal file
View File

@ -0,0 +1,306 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cstdlib>
#include <iomanip>
#include <memory>
#include <sstream>
#include <string>
#include "common.h"
#include "s3fs_logger.h"
//-------------------------------------------------------------------
// S3fsLog class : variables
//-------------------------------------------------------------------
constexpr char S3fsLog::LOGFILEENV[];
constexpr const char* S3fsLog::nest_spaces[];
constexpr char S3fsLog::MSGTIMESTAMP[];
S3fsLog* S3fsLog::pSingleton = nullptr;
S3fsLog::s3fs_log_level S3fsLog::debug_level = S3fsLog::LEVEL_CRIT;
FILE* S3fsLog::logfp = nullptr;
std::string S3fsLog::logfile;
bool S3fsLog::time_stamp = true;
//-------------------------------------------------------------------
// S3fsLog class : class methods
//-------------------------------------------------------------------
bool S3fsLog::IsS3fsLogLevel(s3fs_log_level level)
{
return (level == (S3fsLog::debug_level & level));
}
std::string S3fsLog::GetCurrentTime()
{
std::ostringstream current_time;
if(time_stamp){
struct timeval now;
struct timespec tsnow;
struct tm res;
char tmp[32];
if(-1 == clock_gettime(S3FS_CLOCK_MONOTONIC, &tsnow)){
now.tv_sec = tsnow.tv_sec;
now.tv_usec = (tsnow.tv_nsec / 1000);
}else{
gettimeofday(&now, nullptr);
}
strftime(tmp, sizeof(tmp), "%Y-%m-%dT%H:%M:%S", gmtime_r(&now.tv_sec, &res));
current_time << tmp << "." << std::setfill('0') << std::setw(3) << (now.tv_usec / 1000) << "Z ";
}
return current_time.str();
}
bool S3fsLog::SetLogfile(const char* pfile)
{
if(!S3fsLog::pSingleton){
S3FS_PRN_CRIT("S3fsLog::pSingleton is nullptr.");
return false;
}
return S3fsLog::pSingleton->LowSetLogfile(pfile);
}
bool S3fsLog::ReopenLogfile()
{
if(!S3fsLog::pSingleton){
S3FS_PRN_CRIT("S3fsLog::pSingleton is nullptr.");
return false;
}
if(!S3fsLog::logfp){
S3FS_PRN_INFO("Currently the log file is output to stdout/stderr.");
return true;
}
if(!S3fsLog::logfile.empty()){
S3FS_PRN_ERR("There is a problem with the path to the log file being empty.");
return false;
}
std::string tmp = S3fsLog::logfile;
return S3fsLog::pSingleton->LowSetLogfile(tmp.c_str());
}
S3fsLog::s3fs_log_level S3fsLog::SetLogLevel(s3fs_log_level level)
{
if(!S3fsLog::pSingleton){
S3FS_PRN_CRIT("S3fsLog::pSingleton is nullptr.");
return S3fsLog::debug_level; // Although it is an error, it returns the current value.
}
return S3fsLog::pSingleton->LowSetLogLevel(level);
}
S3fsLog::s3fs_log_level S3fsLog::BumpupLogLevel()
{
if(!S3fsLog::pSingleton){
S3FS_PRN_CRIT("S3fsLog::pSingleton is nullptr.");
return S3fsLog::debug_level; // Although it is an error, it returns the current value.
}
return S3fsLog::pSingleton->LowBumpupLogLevel();
}
bool S3fsLog::SetTimeStamp(bool value)
{
bool old = S3fsLog::time_stamp;
S3fsLog::time_stamp = value;
return old;
}
//-------------------------------------------------------------------
// S3fsLog class : methods
//-------------------------------------------------------------------
S3fsLog::S3fsLog()
{
if(!S3fsLog::pSingleton){
S3fsLog::pSingleton = this;
// init syslog(default CRIT)
openlog("s3fs", LOG_PID | LOG_ODELAY | LOG_NOWAIT, LOG_USER);
LowLoadEnv();
}else{
S3FS_PRN_ERR("Already set singleton object for S3fsLog.");
}
}
S3fsLog::~S3fsLog()
{
if(S3fsLog::pSingleton == this){
FILE* oldfp = S3fsLog::logfp;
S3fsLog::logfp = nullptr;
if(oldfp && 0 != fclose(oldfp)){
S3FS_PRN_ERR("Could not close old log file(%s), but continue...", (S3fsLog::logfile.empty() ? S3fsLog::logfile.c_str() : "null"));
}
S3fsLog::logfile.clear();
S3fsLog::pSingleton = nullptr;
S3fsLog::debug_level = S3fsLog::LEVEL_CRIT;
closelog();
}else{
S3FS_PRN_ERR("This object is not singleton S3fsLog object.");
}
}
bool S3fsLog::LowLoadEnv()
{
if(S3fsLog::pSingleton != this){
S3FS_PRN_ERR("This object is not as same as S3fsLog::pSingleton.");
return false;
}
char* pEnvVal;
if(nullptr != (pEnvVal = getenv(S3fsLog::LOGFILEENV))){
if(!SetLogfile(pEnvVal)){
return false;
}
}
if(nullptr != (pEnvVal = getenv(S3fsLog::MSGTIMESTAMP))){
if(0 == strcasecmp(pEnvVal, "true") || 0 == strcasecmp(pEnvVal, "yes") || 0 == strcasecmp(pEnvVal, "1")){
S3fsLog::time_stamp = true;
}else if(0 == strcasecmp(pEnvVal, "false") || 0 == strcasecmp(pEnvVal, "no") || 0 == strcasecmp(pEnvVal, "0")){
S3fsLog::time_stamp = false;
}else{
S3FS_PRN_WARN("Unknown %s environment value(%s) is specified, skip to set time stamp mode.", S3fsLog::MSGTIMESTAMP, pEnvVal);
}
}
return true;
}
bool S3fsLog::LowSetLogfile(const char* pfile)
{
if(S3fsLog::pSingleton != this){
S3FS_PRN_ERR("This object is not as same as S3fsLog::pSingleton.");
return false;
}
if(!pfile){
// close log file if it is opened
if(S3fsLog::logfp && 0 != fclose(S3fsLog::logfp)){
S3FS_PRN_ERR("Could not close log file(%s).", (S3fsLog::logfile.empty() ? S3fsLog::logfile.c_str() : "null"));
return false;
}
S3fsLog::logfp = nullptr;
S3fsLog::logfile.clear();
}else{
// open new log file
//
// [NOTE]
// It will reopen even if it is the same file.
//
FILE* newfp;
if(nullptr == (newfp = fopen(pfile, "a+"))){
S3FS_PRN_ERR("Could not open log file(%s).", pfile);
return false;
}
// switch new log file and close old log file if it is opened
FILE* oldfp = S3fsLog::logfp;
if(oldfp && 0 != fclose(oldfp)){
S3FS_PRN_ERR("Could not close old log file(%s).", (!S3fsLog::logfile.empty() ? S3fsLog::logfile.c_str() : "null"));
fclose(newfp);
return false;
}
S3fsLog::logfp = newfp;
S3fsLog::logfile = pfile;
}
return true;
}
S3fsLog::s3fs_log_level S3fsLog::LowSetLogLevel(s3fs_log_level level)
{
if(S3fsLog::pSingleton != this){
S3FS_PRN_ERR("This object is not as same as S3fsLog::pSingleton.");
return S3fsLog::debug_level; // Although it is an error, it returns the current value.
}
if(level == S3fsLog::debug_level){
return S3fsLog::debug_level;
}
s3fs_log_level old = S3fsLog::debug_level;
S3fsLog::debug_level = level;
setlogmask(LOG_UPTO(GetSyslogLevel(S3fsLog::debug_level)));
S3FS_PRN_CRIT("change debug level from %sto %s", GetLevelString(old), GetLevelString(S3fsLog::debug_level));
return old;
}
S3fsLog::s3fs_log_level S3fsLog::LowBumpupLogLevel()
{
if(S3fsLog::pSingleton != this){
S3FS_PRN_ERR("This object is not as same as S3fsLog::pSingleton.");
return S3fsLog::debug_level; // Although it is an error, it returns the current value.
}
s3fs_log_level old = S3fsLog::debug_level;
S3fsLog::debug_level = ( LEVEL_CRIT == S3fsLog::debug_level ? LEVEL_ERR :
LEVEL_ERR == S3fsLog::debug_level ? LEVEL_WARN :
LEVEL_WARN == S3fsLog::debug_level ? LEVEL_INFO :
LEVEL_INFO == S3fsLog::debug_level ? LEVEL_DBG : LEVEL_CRIT );
setlogmask(LOG_UPTO(GetSyslogLevel(S3fsLog::debug_level)));
S3FS_PRN_CRIT("change debug level from %sto %s", GetLevelString(old), GetLevelString(S3fsLog::debug_level));
return old;
}
void s3fs_low_logprn(S3fsLog::s3fs_log_level level, const char* file, const char *func, int line, const char *fmt, ...)
{
if(S3fsLog::IsS3fsLogLevel(level)){
va_list va;
va_start(va, fmt);
size_t len = vsnprintf(nullptr, 0, fmt, va) + 1;
va_end(va);
std::unique_ptr<char[]> message(new char[len]);
va_start(va, fmt);
vsnprintf(message.get(), len, fmt, va);
va_end(va);
if(foreground || S3fsLog::IsSetLogFile()){
S3fsLog::SeekEnd();
fprintf(S3fsLog::GetOutputLogFile(), "%s%s%s:%s(%d): %s\n", S3fsLog::GetCurrentTime().c_str(), S3fsLog::GetLevelString(level), file, func, line, message.get());
S3fsLog::Flush();
}else{
// TODO: why does this differ from s3fs_low_logprn2?
syslog(S3fsLog::GetSyslogLevel(level), "%s%s:%s(%d): %s", instance_name.c_str(), file, func, line, message.get());
}
}
}
void s3fs_low_logprn2(S3fsLog::s3fs_log_level level, int nest, const char* file, const char *func, int line, const char *fmt, ...)
{
if(S3fsLog::IsS3fsLogLevel(level)){
va_list va;
va_start(va, fmt);
size_t len = vsnprintf(nullptr, 0, fmt, va) + 1;
va_end(va);
std::unique_ptr<char[]> message(new char[len]);
va_start(va, fmt);
vsnprintf(message.get(), len, fmt, va);
va_end(va);
if(foreground || S3fsLog::IsSetLogFile()){
S3fsLog::SeekEnd();
fprintf(S3fsLog::GetOutputLogFile(), "%s%s%s%s:%s(%d): %s\n", S3fsLog::GetCurrentTime().c_str(), S3fsLog::GetLevelString(level), S3fsLog::GetS3fsLogNest(nest), file, func, line, message.get());
S3fsLog::Flush();
}else{
syslog(S3fsLog::GetSyslogLevel(level), "%s%s%s", instance_name.c_str(), S3fsLog::GetS3fsLogNest(nest), message.get());
}
}
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

270
src/s3fs_logger.h Normal file
View File

@ -0,0 +1,270 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_LOGGER_H_
#define S3FS_LOGGER_H_
#include <cstdarg>
#include <cstdio>
#include <string>
#include <syslog.h>
#include <sys/time.h>
#include "common.h"
#ifdef CLOCK_MONOTONIC_COARSE
#define S3FS_CLOCK_MONOTONIC CLOCK_MONOTONIC_COARSE
#else
// case of OSX
#define S3FS_CLOCK_MONOTONIC CLOCK_MONOTONIC
#endif
//-------------------------------------------------------------------
// S3fsLog class
//-------------------------------------------------------------------
class S3fsLog
{
public:
enum s3fs_log_level{
LEVEL_CRIT = 0, // LEVEL_CRIT
LEVEL_ERR = 1, // LEVEL_ERR
LEVEL_WARN = 3, // LEVEL_WARNING
LEVEL_INFO = 7, // LEVEL_INFO
LEVEL_DBG = 15 // LEVEL_DEBUG
};
protected:
static constexpr int NEST_MAX = 4;
static constexpr const char* nest_spaces[NEST_MAX] = {"", " ", " ", " "};
static constexpr char LOGFILEENV[] = "S3FS_LOGFILE";
static constexpr char MSGTIMESTAMP[] = "S3FS_MSGTIMESTAMP";
static S3fsLog* pSingleton;
static s3fs_log_level debug_level;
static FILE* logfp;
static std::string logfile;
static bool time_stamp;
protected:
bool LowLoadEnv();
bool LowSetLogfile(const char* pfile);
s3fs_log_level LowSetLogLevel(s3fs_log_level level);
s3fs_log_level LowBumpupLogLevel();
public:
static bool IsS3fsLogLevel(s3fs_log_level level);
static bool IsS3fsLogCrit() { return IsS3fsLogLevel(LEVEL_CRIT); }
static bool IsS3fsLogErr() { return IsS3fsLogLevel(LEVEL_ERR); }
static bool IsS3fsLogWarn() { return IsS3fsLogLevel(LEVEL_WARN); }
static bool IsS3fsLogInfo() { return IsS3fsLogLevel(LEVEL_INFO); }
static bool IsS3fsLogDbg() { return IsS3fsLogLevel(LEVEL_DBG); }
static constexpr int GetSyslogLevel(s3fs_log_level level)
{
return ( LEVEL_DBG == (level & LEVEL_DBG) ? LOG_DEBUG :
LEVEL_INFO == (level & LEVEL_DBG) ? LOG_INFO :
LEVEL_WARN == (level & LEVEL_DBG) ? LOG_WARNING :
LEVEL_ERR == (level & LEVEL_DBG) ? LOG_ERR : LOG_CRIT );
}
static std::string GetCurrentTime();
static constexpr const char* GetLevelString(s3fs_log_level level)
{
return ( LEVEL_DBG == (level & LEVEL_DBG) ? "[DBG] " :
LEVEL_INFO == (level & LEVEL_DBG) ? "[INF] " :
LEVEL_WARN == (level & LEVEL_DBG) ? "[WAN] " :
LEVEL_ERR == (level & LEVEL_DBG) ? "[ERR] " : "[CRT] " );
}
static constexpr const char* GetS3fsLogNest(int nest)
{
return nest_spaces[nest < NEST_MAX ? nest : NEST_MAX - 1];
}
static bool IsSetLogFile()
{
return (nullptr != logfp);
}
static FILE* GetOutputLogFile()
{
return (logfp ? logfp : stdout);
}
static FILE* GetErrorLogFile()
{
return (logfp ? logfp : stderr);
}
static void SeekEnd()
{
if(logfp){
fseek(logfp, 0, SEEK_END);
}
}
static void Flush()
{
if(logfp){
fflush(logfp);
}
}
static bool SetLogfile(const char* pfile);
static bool ReopenLogfile();
static s3fs_log_level SetLogLevel(s3fs_log_level level);
static s3fs_log_level BumpupLogLevel();
static bool SetTimeStamp(bool value);
explicit S3fsLog();
~S3fsLog();
S3fsLog(const S3fsLog&) = delete;
S3fsLog(S3fsLog&&) = delete;
S3fsLog& operator=(const S3fsLog&) = delete;
S3fsLog& operator=(S3fsLog&&) = delete;
};
//-------------------------------------------------------------------
// Debug macros
//-------------------------------------------------------------------
void s3fs_low_logprn(S3fsLog::s3fs_log_level level, const char* file, const char *func, int line, const char *fmt, ...) __attribute__ ((format (printf, 5, 6)));
#define S3FS_LOW_LOGPRN(level, fmt, ...) \
do{ \
s3fs_low_logprn(level, __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__); \
}while(0)
void s3fs_low_logprn2(S3fsLog::s3fs_log_level level, int nest, const char* file, const char *func, int line, const char *fmt, ...) __attribute__ ((format (printf, 6, 7)));
#define S3FS_LOW_LOGPRN2(level, nest, fmt, ...) \
do{ \
s3fs_low_logprn2(level, nest, __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__); \
}while(0)
#define S3FS_LOW_CURLDBG(fmt, ...) \
do{ \
if(foreground || S3fsLog::IsSetLogFile()){ \
S3fsLog::SeekEnd(); \
fprintf(S3fsLog::GetOutputLogFile(), "%s[CURL DBG] " fmt "%s\n", S3fsLog::GetCurrentTime().c_str(), __VA_ARGS__); \
S3fsLog::Flush(); \
}else{ \
syslog(S3fsLog::GetSyslogLevel(S3fsLog::LEVEL_CRIT), "%s" fmt "%s", instance_name.c_str(), __VA_ARGS__); \
} \
}while(0)
#define S3FS_LOW_LOGPRN_EXIT(fmt, ...) \
do{ \
if(foreground || S3fsLog::IsSetLogFile()){ \
S3fsLog::SeekEnd(); \
fprintf(S3fsLog::GetErrorLogFile(), "s3fs: " fmt "%s\n", __VA_ARGS__); \
S3fsLog::Flush(); \
}else{ \
fprintf(S3fsLog::GetErrorLogFile(), "s3fs: " fmt "%s\n", __VA_ARGS__); \
syslog(S3fsLog::GetSyslogLevel(S3fsLog::LEVEL_CRIT), "%ss3fs: " fmt "%s", instance_name.c_str(), __VA_ARGS__); \
} \
}while(0)
// Special macro for init message
#define S3FS_PRN_INIT_INFO(fmt, ...) \
do{ \
if(foreground || S3fsLog::IsSetLogFile()){ \
S3fsLog::SeekEnd(); \
fprintf(S3fsLog::GetOutputLogFile(), "%s%s%s%s:%s(%d): " fmt "%s\n", S3fsLog::GetCurrentTime().c_str(), S3fsLog::GetLevelString(S3fsLog::LEVEL_INFO), S3fsLog::GetS3fsLogNest(0), __FILE__, __func__, __LINE__, __VA_ARGS__, ""); \
S3fsLog::Flush(); \
}else{ \
syslog(S3fsLog::GetSyslogLevel(S3fsLog::LEVEL_INFO), "%s%s" fmt "%s", instance_name.c_str(), S3fsLog::GetS3fsLogNest(0), __VA_ARGS__, ""); \
} \
}while(0)
#define S3FS_PRN_LAUNCH_INFO(fmt, ...) \
do{ \
if(foreground || S3fsLog::IsSetLogFile()){ \
S3fsLog::SeekEnd(); \
fprintf(S3fsLog::GetOutputLogFile(), "%s%s" fmt "%s\n", S3fsLog::GetCurrentTime().c_str(), S3fsLog::GetLevelString(S3fsLog::LEVEL_INFO), __VA_ARGS__, ""); \
S3fsLog::Flush(); \
}else{ \
syslog(S3fsLog::GetSyslogLevel(S3fsLog::LEVEL_INFO), "%s" fmt "%s", instance_name.c_str(), __VA_ARGS__, ""); \
} \
}while(0)
// Special macro for checking cache files
#define S3FS_LOW_CACHE(fp, fmt, ...) \
do{ \
if(foreground || S3fsLog::IsSetLogFile()){ \
S3fsLog::SeekEnd(); \
fprintf(fp, fmt "%s\n", __VA_ARGS__); \
S3fsLog::Flush(); \
}else{ \
syslog(S3fsLog::GetSyslogLevel(S3fsLog::LEVEL_INFO), "%s: " fmt "%s", instance_name.c_str(), __VA_ARGS__); \
} \
}while(0)
// [NOTE]
// small trick for VA_ARGS
//
#define S3FS_PRN_EXIT(fmt, ...) S3FS_LOW_LOGPRN_EXIT(fmt, ##__VA_ARGS__, "")
#define S3FS_PRN_CRIT(fmt, ...) S3FS_LOW_LOGPRN(S3fsLog::LEVEL_CRIT, fmt, ##__VA_ARGS__)
#define S3FS_PRN_ERR(fmt, ...) S3FS_LOW_LOGPRN(S3fsLog::LEVEL_ERR, fmt, ##__VA_ARGS__)
#define S3FS_PRN_WARN(fmt, ...) S3FS_LOW_LOGPRN(S3fsLog::LEVEL_WARN, fmt, ##__VA_ARGS__)
#define S3FS_PRN_DBG(fmt, ...) S3FS_LOW_LOGPRN(S3fsLog::LEVEL_DBG, fmt, ##__VA_ARGS__)
#define S3FS_PRN_INFO(fmt, ...) S3FS_LOW_LOGPRN2(S3fsLog::LEVEL_INFO, 0, fmt, ##__VA_ARGS__)
#define S3FS_PRN_INFO1(fmt, ...) S3FS_LOW_LOGPRN2(S3fsLog::LEVEL_INFO, 1, fmt, ##__VA_ARGS__)
#define S3FS_PRN_INFO2(fmt, ...) S3FS_LOW_LOGPRN2(S3fsLog::LEVEL_INFO, 2, fmt, ##__VA_ARGS__)
#define S3FS_PRN_INFO3(fmt, ...) S3FS_LOW_LOGPRN2(S3fsLog::LEVEL_INFO, 3, fmt, ##__VA_ARGS__)
#define S3FS_PRN_CURL(fmt, ...) S3FS_LOW_CURLDBG(fmt, ##__VA_ARGS__, "")
#define S3FS_PRN_CACHE(fp, ...) S3FS_LOW_CACHE(fp, ##__VA_ARGS__, "")
// Macros to print log with fuse context
#define PRINT_FUSE_CTX(level, indent, fmt, ...) do { \
if(S3fsLog::IsS3fsLogLevel(level)){ \
struct fuse_context *ctx = fuse_get_context(); \
if(ctx == NULL){ \
S3FS_LOW_LOGPRN2(level, indent, fmt, ##__VA_ARGS__); \
}else{ \
S3FS_LOW_LOGPRN2(level, indent, fmt"[pid=%u,uid=%u,gid=%u]",\
##__VA_ARGS__, \
(unsigned int)(ctx->pid), \
(unsigned int)(ctx->uid), \
(unsigned int)(ctx->gid)); \
} \
} \
} while (0)
#define FUSE_CTX_INFO(fmt, ...) do { \
PRINT_FUSE_CTX(S3fsLog::LEVEL_INFO, 0, fmt, ##__VA_ARGS__); \
} while (0)
#define FUSE_CTX_INFO1(fmt, ...) do { \
PRINT_FUSE_CTX(S3fsLog::LEVEL_INFO, 1, fmt, ##__VA_ARGS__); \
} while (0)
#define FUSE_CTX_DBG(fmt, ...) do { \
PRINT_FUSE_CTX(S3fsLog::LEVEL_DBG, 0, fmt, ##__VA_ARGS__); \
} while (0)
#endif // S3FS_LOGGER_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

File diff suppressed because it is too large Load Diff

View File

@ -17,107 +17,38 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_S3FS_UTIL_H_
#define S3FS_S3FS_UTIL_H_
//-------------------------------------------------------------------
// Typedef
//-------------------------------------------------------------------
//
// Struct
//
struct s3obj_entry{
std::string normalname; // normalized name: if empty, object is normalized name.
std::string orgname; // original name: if empty, object is original name.
std::string etag;
bool is_dir;
#include <functional>
s3obj_entry() : is_dir(false) {}
};
typedef std::map<std::string, struct s3obj_entry> s3obj_t;
typedef std::list<std::string> s3obj_list_t;
//
// Class
//
class S3ObjList
{
private:
s3obj_t objects;
private:
bool insert_normalized(const char* name, const char* normalized, bool is_dir);
const s3obj_entry* GetS3Obj(const char* name) const;
s3obj_t::const_iterator begin(void) const {
return objects.begin();
}
s3obj_t::const_iterator end(void) const {
return objects.end();
}
public:
S3ObjList() {}
~S3ObjList() {}
bool IsEmpty(void) const {
return objects.empty();
}
bool insert(const char* name, const char* etag = NULL, bool is_dir = false);
std::string GetOrgName(const char* name) const;
std::string GetNormalizedName(const char* name) const;
std::string GetETag(const char* name) const;
bool IsDir(const char* name) const;
bool GetNameList(s3obj_list_t& list, bool OnlyNormalized = true, bool CutSlash = true) const;
bool GetLastName(std::string& lastname) const;
static bool MakeHierarchizedList(s3obj_list_t& list, bool haveSlash);
};
typedef struct mvnode {
char *old_path;
char *new_path;
bool is_dir;
bool is_normdir;
struct mvnode *prev;
struct mvnode *next;
} MVNODE;
class AutoLock
{
public:
enum Type {
NO_WAIT = 1,
ALREADY_LOCKED = 2,
NONE = 0
};
explicit AutoLock(pthread_mutex_t* pmutex, Type type = NONE);
bool isLockAcquired() const;
~AutoLock();
private:
pthread_mutex_t* const auto_mutex;
bool is_lock_acquired;
};
#ifndef CLOCK_REALTIME
#define CLOCK_REALTIME 0
#endif
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC CLOCK_REALTIME
#endif
#ifndef CLOCK_MONOTONIC_COARSE
#define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC
#endif
//-------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------
std::string get_realpath(const char *path);
MVNODE *create_mvnode(const char *old_path, const char *new_path, bool is_dir, bool normdir = false);
MVNODE *add_mvnode(MVNODE** head, MVNODE** tail, const char *old_path, const char *new_path, bool is_dir, bool normdir = false);
void free_mvnodes(MVNODE *head);
void init_sysconf_vars();
std::string get_username(uid_t uid);
int is_uid_include_group(uid_t uid, gid_t gid);
bool init_basename_lock();
bool destroy_basename_lock();
std::string mydirname(const char* path);
std::string mydirname(const std::string& path);
std::string mybasename(const char* path);
std::string mybasename(const std::string& path);
int mkdirp(const std::string& path, mode_t mode);
std::string get_exist_directory_path(const std::string& path);
bool check_exist_dir_permission(const char* dirpath);
@ -125,27 +56,58 @@ bool delete_files_in_dir(const char* dir, bool is_remove_own);
bool compare_sysname(const char* target);
time_t get_mtime(const char *s);
time_t get_mtime(headers_t& meta, bool overcheck = true);
time_t get_ctime(headers_t& meta, bool overcheck = true);
off_t get_size(const char *s);
off_t get_size(headers_t& meta);
mode_t get_mode(const char *s, int base = 0);
mode_t get_mode(headers_t& meta, const char* path = NULL, bool checkdir = false, bool forcedir = false);
uid_t get_uid(const char *s);
uid_t get_uid(headers_t& meta);
gid_t get_gid(const char *s);
gid_t get_gid(headers_t& meta);
blkcnt_t get_blocks(off_t size);
time_t cvtIAMExpireStringToTime(const char* s);
time_t get_lastmodified(const char* s);
time_t get_lastmodified(headers_t& meta);
bool is_need_check_obj_detail(headers_t& meta);
bool simple_parse_xml(const char* data, size_t len, const char* key, std::string& value);
void print_launch_message(int argc, char** argv);
void show_usage(void);
void show_help(void);
void show_version(void);
//
// Utility for nanosecond time(timespec)
//
enum class stat_time_type{
ATIME,
MTIME,
CTIME
};
//-------------------------------------------------------------------
// Utility for nanosecond time(timespec)
//-------------------------------------------------------------------
static constexpr struct timespec S3FS_OMIT_TS = {0, UTIME_OMIT};
int compare_timespec(const struct timespec& ts1, const struct timespec& ts2);
int compare_timespec(const struct stat& st, stat_time_type type, const struct timespec& ts);
void set_timespec_to_stat(struct stat& st, stat_time_type type, const struct timespec& ts);
struct timespec* set_stat_to_timespec(const struct stat& st, stat_time_type type, struct timespec& ts);
std::string str_stat_time(const struct stat& st, stat_time_type type);
struct timespec* s3fs_realtime(struct timespec& ts);
std::string s3fs_str_realtime();
// Wrap fclose since it is illegal to take the address of a stdlib function
int s3fs_fclose(FILE* fp);
class scope_guard {
public:
template<class Callable>
explicit scope_guard(Callable&& undo_func)
: func(std::forward<Callable>(undo_func))
{}
~scope_guard() {
if(func != nullptr) {
func();
}
}
void dismiss() {
func = nullptr;
}
scope_guard(const scope_guard&) = delete;
scope_guard(scope_guard&& other) = delete;
scope_guard& operator=(const scope_guard&) = delete;
scope_guard& operator=(scope_guard&&) = delete;
private:
std::function<void()> func;
};
#endif // S3FS_S3FS_UTIL_H_
@ -154,6 +116,6 @@ void show_version(void);
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

530
src/s3fs_xml.cpp Normal file
View File

@ -0,0 +1,530 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cstdio>
#include <cstdlib>
#include <libxml/xpathInternals.h>
#include "common.h"
#include "s3fs.h"
#include "s3fs_logger.h"
#include "s3fs_xml.h"
#include "s3fs_util.h"
#include "s3objlist.h"
#include "autolock.h"
#include "string_util.h"
//-------------------------------------------------------------------
// Variables
//-------------------------------------------------------------------
static constexpr char c_strErrorObjectName[] = "FILE or SUBDIR in DIR";
// [NOTE]
// mutex for static variables in GetXmlNsUrl
//
static pthread_mutex_t* pxml_parser_mutex = nullptr;
//-------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------
static bool GetXmlNsUrl(xmlDocPtr doc, std::string& nsurl)
{
bool result = false;
if(!pxml_parser_mutex || !doc){
return result;
}
std::string tmpNs;
{
static time_t tmLast = 0; // cache for 60 sec.
static std::string strNs;
AutoLock lock(pxml_parser_mutex);
if((tmLast + 60) < time(nullptr)){
// refresh
tmLast = time(nullptr);
strNs = "";
xmlNodePtr pRootNode = xmlDocGetRootElement(doc);
if(pRootNode){
std::unique_ptr<xmlNsPtr, decltype(xmlFree)> nslist(xmlGetNsList(doc, pRootNode), xmlFree);
if(nslist){
if(*nslist && (*nslist)[0].href){
int len = xmlStrlen((*nslist)[0].href);
if(0 < len){
strNs = std::string(reinterpret_cast<const char*>((*nslist)[0].href), len);
}
}
}
}
}
tmpNs = strNs;
}
if(!tmpNs.empty()){
nsurl = tmpNs;
result = true;
}
return result;
}
static unique_ptr_xmlChar get_base_exp(xmlDocPtr doc, const char* exp)
{
std::string xmlnsurl;
std::string exp_string;
if(!doc){
return {nullptr, xmlFree};
}
unique_ptr_xmlXPathContext ctx(xmlXPathNewContext(doc), xmlXPathFreeContext);
if(!noxmlns && GetXmlNsUrl(doc, xmlnsurl)){
xmlXPathRegisterNs(ctx.get(), reinterpret_cast<const xmlChar*>("s3"), reinterpret_cast<const xmlChar*>(xmlnsurl.c_str()));
exp_string = "/s3:ListBucketResult/s3:";
} else {
exp_string = "/ListBucketResult/";
}
exp_string += exp;
unique_ptr_xmlXPathObject marker_xp(xmlXPathEvalExpression(reinterpret_cast<const xmlChar*>(exp_string.c_str()), ctx.get()), xmlXPathFreeObject);
if(nullptr == marker_xp){
return {nullptr, xmlFree};
}
if(xmlXPathNodeSetIsEmpty(marker_xp->nodesetval)){
S3FS_PRN_INFO("marker_xp->nodesetval is empty.");
return {nullptr, xmlFree};
}
xmlNodeSetPtr nodes = marker_xp->nodesetval;
unique_ptr_xmlChar result(xmlNodeListGetString(doc, nodes->nodeTab[0]->xmlChildrenNode, 1), xmlFree);
return result;
}
static unique_ptr_xmlChar get_prefix(xmlDocPtr doc)
{
return get_base_exp(doc, "Prefix");
}
unique_ptr_xmlChar get_next_continuation_token(xmlDocPtr doc)
{
return get_base_exp(doc, "NextContinuationToken");
}
unique_ptr_xmlChar get_next_marker(xmlDocPtr doc)
{
return get_base_exp(doc, "NextMarker");
}
// return: the pointer to object name on allocated memory.
// the pointer to "c_strErrorObjectName".(not allocated)
// nullptr(a case of something error occurred)
static char* get_object_name(xmlDocPtr doc, xmlNodePtr node, const char* path)
{
// Get full path
unique_ptr_xmlChar fullpath(xmlNodeListGetString(doc, node, 1), xmlFree);
if(!fullpath){
S3FS_PRN_ERR("could not get object full path name..");
return nullptr;
}
// basepath(path) is as same as fullpath.
if(0 == strcmp(reinterpret_cast<char*>(fullpath.get()), path)){
return const_cast<char*>(c_strErrorObjectName);
}
// Make dir path and filename
std::string strdirpath = mydirname(reinterpret_cast<const char*>(fullpath.get()));
std::string strmybpath = mybasename(reinterpret_cast<const char*>(fullpath.get()));
const char* dirpath = strdirpath.c_str();
const char* mybname = strmybpath.c_str();
const char* basepath= (path && '/' == path[0]) ? &path[1] : path;
if('\0' == mybname[0]){
return nullptr;
}
// check subdir & file in subdir
if(0 < strlen(dirpath)){
// case of "/"
if(0 == strcmp(mybname, "/") && 0 == strcmp(dirpath, "/")){
return const_cast<char*>(c_strErrorObjectName);
}
// case of "."
if(0 == strcmp(mybname, ".") && 0 == strcmp(dirpath, ".")){
return const_cast<char *>(c_strErrorObjectName);
}
// case of ".."
if(0 == strcmp(mybname, "..") && 0 == strcmp(dirpath, ".")){
return const_cast<char *>(c_strErrorObjectName);
}
// case of "name"
if(0 == strcmp(dirpath, ".")){
// OK
return strdup(mybname);
}else{
if(basepath && 0 == strcmp(dirpath, basepath)){
// OK
return strdup(mybname);
}else if(basepath && 0 < strlen(basepath) && '/' == basepath[strlen(basepath) - 1] && 0 == strncmp(dirpath, basepath, strlen(basepath) - 1)){
std::string withdirname;
if(strlen(dirpath) > strlen(basepath)){
withdirname = &dirpath[strlen(basepath)];
}
// cppcheck-suppress unmatchedSuppression
// cppcheck-suppress knownConditionTrueFalse
if(!withdirname.empty() && '/' != *withdirname.rbegin()){
withdirname += "/";
}
withdirname += mybname;
return strdup(withdirname.c_str());
}
}
}
// case of something wrong
return const_cast<char*>(c_strErrorObjectName);
}
static unique_ptr_xmlChar get_exp_value_xml(xmlDocPtr doc, xmlXPathContextPtr ctx, const char* exp_key)
{
if(!doc || !ctx || !exp_key){
return {nullptr, xmlFree};
}
xmlNodeSetPtr exp_nodes;
// search exp_key tag
unique_ptr_xmlXPathObject exp(xmlXPathEvalExpression(reinterpret_cast<const xmlChar*>(exp_key), ctx), xmlXPathFreeObject);
if(nullptr == exp){
S3FS_PRN_ERR("Could not find key(%s).", exp_key);
return {nullptr, xmlFree};
}
if(xmlXPathNodeSetIsEmpty(exp->nodesetval)){
S3FS_PRN_ERR("Key(%s) node is empty.", exp_key);
return {nullptr, xmlFree};
}
// get exp_key value & set in struct
exp_nodes = exp->nodesetval;
unique_ptr_xmlChar exp_value(xmlNodeListGetString(doc, exp_nodes->nodeTab[0]->xmlChildrenNode, 1), xmlFree);
if(nullptr == exp_value){
S3FS_PRN_ERR("Key(%s) value is empty.", exp_key);
return {nullptr, xmlFree};
}
return exp_value;
}
bool get_incomp_mpu_list(xmlDocPtr doc, incomp_mpu_list_t& list)
{
if(!doc){
return false;
}
unique_ptr_xmlXPathContext ctx(xmlXPathNewContext(doc), xmlXPathFreeContext);
std::string xmlnsurl;
std::string ex_upload = "//";
std::string ex_key;
std::string ex_id;
std::string ex_date;
if(!noxmlns && GetXmlNsUrl(doc, xmlnsurl)){
xmlXPathRegisterNs(ctx.get(), reinterpret_cast<const xmlChar*>("s3"), reinterpret_cast<const xmlChar*>(xmlnsurl.c_str()));
ex_upload += "s3:";
ex_key += "s3:";
ex_id += "s3:";
ex_date += "s3:";
}
ex_upload += "Upload";
ex_key += "Key";
ex_id += "UploadId";
ex_date += "Initiated";
// get "Upload" Tags
unique_ptr_xmlXPathObject upload_xp(xmlXPathEvalExpression(reinterpret_cast<const xmlChar*>(ex_upload.c_str()), ctx.get()), xmlXPathFreeObject);
if(nullptr == upload_xp){
S3FS_PRN_ERR("xmlXPathEvalExpression returns null.");
return false;
}
if(xmlXPathNodeSetIsEmpty(upload_xp->nodesetval)){
S3FS_PRN_INFO("upload_xp->nodesetval is empty.");
return true;
}
// Make list
int cnt;
xmlNodeSetPtr upload_nodes;
list.clear();
for(cnt = 0, upload_nodes = upload_xp->nodesetval; cnt < upload_nodes->nodeNr; cnt++){
ctx->node = upload_nodes->nodeTab[cnt];
INCOMP_MPU_INFO part;
// search "Key" tag
unique_ptr_xmlChar ex_value(get_exp_value_xml(doc, ctx.get(), ex_key.c_str()));
if(nullptr == ex_value){
continue;
}
if('/' != *(reinterpret_cast<char*>(ex_value.get()))){
part.key = "/";
}else{
part.key = "";
}
part.key += reinterpret_cast<char*>(ex_value.get());
// search "UploadId" tag
if(nullptr == (ex_value = get_exp_value_xml(doc, ctx.get(), ex_id.c_str()))){
continue;
}
part.id = reinterpret_cast<char*>(ex_value.get());
// search "Initiated" tag
if(nullptr == (ex_value = get_exp_value_xml(doc, ctx.get(), ex_date.c_str()))){
continue;
}
part.date = reinterpret_cast<char*>(ex_value.get());
list.push_back(part);
}
return true;
}
bool is_truncated(xmlDocPtr doc)
{
unique_ptr_xmlChar strTruncate(get_base_exp(doc, "IsTruncated"));
if(!strTruncate){
return false;
}
return 0 == strcasecmp(reinterpret_cast<const char*>(strTruncate.get()), "true");
}
int append_objects_from_xml_ex(const char* path, xmlDocPtr doc, xmlXPathContextPtr ctx, const char* ex_contents, const char* ex_key, const char* ex_etag, int isCPrefix, S3ObjList& head, bool prefix)
{
xmlNodeSetPtr content_nodes;
unique_ptr_xmlXPathObject contents_xp(xmlXPathEvalExpression(reinterpret_cast<const xmlChar*>(ex_contents), ctx), xmlXPathFreeObject);
if(nullptr == contents_xp){
S3FS_PRN_ERR("xmlXPathEvalExpression returns null.");
return -1;
}
if(xmlXPathNodeSetIsEmpty(contents_xp->nodesetval)){
S3FS_PRN_DBG("contents_xp->nodesetval is empty.");
return 0;
}
content_nodes = contents_xp->nodesetval;
bool is_dir;
std::string stretag;
int i;
for(i = 0; i < content_nodes->nodeNr; i++){
ctx->node = content_nodes->nodeTab[i];
// object name
unique_ptr_xmlXPathObject key(xmlXPathEvalExpression(reinterpret_cast<const xmlChar*>(ex_key), ctx), xmlXPathFreeObject);
if(nullptr == key){
S3FS_PRN_WARN("key is null. but continue.");
continue;
}
if(xmlXPathNodeSetIsEmpty(key->nodesetval)){
S3FS_PRN_WARN("node is empty. but continue.");
continue;
}
xmlNodeSetPtr key_nodes = key->nodesetval;
char* name = get_object_name(doc, key_nodes->nodeTab[0]->xmlChildrenNode, path);
if(!name){
S3FS_PRN_WARN("name is something wrong. but continue.");
}else if(reinterpret_cast<const char*>(name) != c_strErrorObjectName){
is_dir = isCPrefix ? true : false;
stretag = "";
if(!isCPrefix && ex_etag){
// Get ETag
unique_ptr_xmlXPathObject ETag(xmlXPathEvalExpression(reinterpret_cast<const xmlChar*>(ex_etag), ctx), xmlXPathFreeObject);
if(nullptr != ETag){
if(xmlXPathNodeSetIsEmpty(ETag->nodesetval)){
S3FS_PRN_INFO("ETag->nodesetval is empty.");
}else{
xmlNodeSetPtr etag_nodes = ETag->nodesetval;
unique_ptr_xmlChar petag(xmlNodeListGetString(doc, etag_nodes->nodeTab[0]->xmlChildrenNode, 1), xmlFree);
if(petag){
stretag = reinterpret_cast<const char*>(petag.get());
}
}
}
}
// [NOTE]
// The XML data passed to this function is CR code(\r) encoded.
// The function below decodes that encoded CR code.
//
std::string decname = get_decoded_cr_code(name);
free(name);
if(prefix){
head.common_prefixes.push_back(decname);
}
if(!head.insert(decname.c_str(), (!stretag.empty() ? stretag.c_str() : nullptr), is_dir)){
S3FS_PRN_ERR("insert_object returns with error.");
return -1;
}
}else{
S3FS_PRN_DBG("name is file or subdir in dir. but continue.");
}
}
return 0;
}
int append_objects_from_xml(const char* path, xmlDocPtr doc, S3ObjList& head)
{
std::string xmlnsurl;
std::string ex_contents = "//";
std::string ex_key;
std::string ex_cprefix = "//";
std::string ex_prefix;
std::string ex_etag;
if(!doc){
return -1;
}
// If there is not <Prefix>, use path instead of it.
auto pprefix = get_prefix(doc);
std::string prefix = (pprefix ? reinterpret_cast<char*>(pprefix.get()) : path ? path : "");
unique_ptr_xmlXPathContext ctx(xmlXPathNewContext(doc), xmlXPathFreeContext);
if(!noxmlns && GetXmlNsUrl(doc, xmlnsurl)){
xmlXPathRegisterNs(ctx.get(), reinterpret_cast<const xmlChar*>("s3"), reinterpret_cast<const xmlChar*>(xmlnsurl.c_str()));
ex_contents+= "s3:";
ex_key += "s3:";
ex_cprefix += "s3:";
ex_prefix += "s3:";
ex_etag += "s3:";
}
ex_contents+= "Contents";
ex_key += "Key";
ex_cprefix += "CommonPrefixes";
ex_prefix += "Prefix";
ex_etag += "ETag";
if(-1 == append_objects_from_xml_ex(prefix.c_str(), doc, ctx.get(), ex_contents.c_str(), ex_key.c_str(), ex_etag.c_str(), 0, head, /*prefix=*/ false) ||
-1 == append_objects_from_xml_ex(prefix.c_str(), doc, ctx.get(), ex_cprefix.c_str(), ex_prefix.c_str(), nullptr, 1, head, /*prefix=*/ true) )
{
S3FS_PRN_ERR("append_objects_from_xml_ex returns with error.");
return -1;
}
return 0;
}
//-------------------------------------------------------------------
// Utility functions
//-------------------------------------------------------------------
bool simple_parse_xml(const char* data, size_t len, const char* key, std::string& value)
{
bool result = false;
if(!data || !key){
return false;
}
value.clear();
std::unique_ptr<xmlDoc, decltype(&xmlFreeDoc)> doc(xmlReadMemory(data, static_cast<int>(len), "", nullptr, 0), xmlFreeDoc);
if(nullptr == doc){
return false;
}
if(nullptr == doc->children){
return false;
}
for(xmlNodePtr cur_node = doc->children->children; nullptr != cur_node; cur_node = cur_node->next){
// For DEBUG
// std::string cur_node_name(reinterpret_cast<const char *>(cur_node->name));
// printf("cur_node_name: %s\n", cur_node_name.c_str());
if(XML_ELEMENT_NODE == cur_node->type){
std::string elementName = reinterpret_cast<const char*>(cur_node->name);
// For DEBUG
// printf("elementName: %s\n", elementName.c_str());
if(cur_node->children){
if(XML_TEXT_NODE == cur_node->children->type){
if(elementName == key) {
value = reinterpret_cast<const char *>(cur_node->children->content);
result = true;
break;
}
}
}
}
}
return result;
}
//-------------------------------------------------------------------
// Utility for lock
//-------------------------------------------------------------------
bool init_parser_xml_lock()
{
if(pxml_parser_mutex){
return false;
}
pxml_parser_mutex = new pthread_mutex_t;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
#if S3FS_PTHREAD_ERRORCHECK
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
#endif
if(0 != pthread_mutex_init(pxml_parser_mutex, &attr)){
delete pxml_parser_mutex;
pxml_parser_mutex = nullptr;
return false;
}
return true;
}
bool destroy_parser_xml_lock()
{
if(!pxml_parser_mutex){
return false;
}
if(0 != pthread_mutex_destroy(pxml_parser_mutex)){
return false;
}
delete pxml_parser_mutex;
pxml_parser_mutex = nullptr;
return true;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

62
src/s3fs_xml.h Normal file
View File

@ -0,0 +1,62 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_S3FS_XML_H_
#define S3FS_S3FS_XML_H_
#include <libxml/xpath.h>
#include <libxml/parser.h> // [NOTE] nessetially include this header in some environments
#include <memory>
#include <string>
#include "mpu_util.h"
class S3ObjList;
typedef std::unique_ptr<xmlChar, decltype(xmlFree)> unique_ptr_xmlChar;
typedef std::unique_ptr<xmlXPathObject, decltype(&xmlXPathFreeObject)> unique_ptr_xmlXPathObject;
typedef std::unique_ptr<xmlXPathContext, decltype(&xmlXPathFreeContext)> unique_ptr_xmlXPathContext;
typedef std::unique_ptr<xmlDoc, decltype(&xmlFreeDoc)> unique_ptr_xmlDoc;
//-------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------
bool is_truncated(xmlDocPtr doc);
int append_objects_from_xml_ex(const char* path, xmlDocPtr doc, xmlXPathContextPtr ctx, const char* ex_contents, const char* ex_key, const char* ex_etag, int isCPrefix, S3ObjList& head, bool prefix);
int append_objects_from_xml(const char* path, xmlDocPtr doc, S3ObjList& head);
unique_ptr_xmlChar get_next_continuation_token(xmlDocPtr doc);
unique_ptr_xmlChar get_next_marker(xmlDocPtr doc);
bool get_incomp_mpu_list(xmlDocPtr doc, incomp_mpu_list_t& list);
bool simple_parse_xml(const char* data, size_t len, const char* key, std::string& value);
bool init_parser_xml_lock();
bool destroy_parser_xml_lock();
#endif // S3FS_S3FS_XML_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

282
src/s3objlist.cpp Normal file
View File

@ -0,0 +1,282 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cstdio>
#include <cstring>
#include "s3objlist.h"
//-------------------------------------------------------------------
// Class S3ObjList
//-------------------------------------------------------------------
// New class S3ObjList is base on old s3_object struct.
// This class is for S3 compatible clients.
//
// If name is terminated by "/", it is forced dir type.
// If name is terminated by "_$folder$", it is forced dir type.
// If is_dir is true and name is not terminated by "/", the name is added "/".
//
bool S3ObjList::insert(const char* name, const char* etag, bool is_dir)
{
if(!name || '\0' == name[0]){
return false;
}
s3obj_t::iterator iter;
std::string newname;
std::string orgname = name;
// Normalization
std::string::size_type pos = orgname.find("_$folder$");
if(std::string::npos != pos){
newname = orgname.substr(0, pos);
is_dir = true;
}else{
newname = orgname;
}
if(is_dir){
if('/' != *newname.rbegin()){
newname += "/";
}
}else{
if('/' == *newname.rbegin()){
is_dir = true;
}
}
// Check derived name object.
if(is_dir){
std::string chkname = newname.substr(0, newname.length() - 1);
if(objects.end() != (iter = objects.find(chkname))){
// found "dir" object --> remove it.
objects.erase(iter);
}
}else{
std::string chkname = newname + "/";
if(objects.end() != (iter = objects.find(chkname))){
// found "dir/" object --> not add new object.
// and add normalization
return insert_normalized(orgname.c_str(), chkname.c_str(), true);
}
}
// Add object
if(objects.end() != (iter = objects.find(newname))){
// Found same object --> update information.
(*iter).second.normalname.erase();
(*iter).second.orgname = orgname;
(*iter).second.is_dir = is_dir;
if(etag){
(*iter).second.etag = etag; // over write
}
}else{
// add new object
s3obj_entry newobject;
newobject.orgname = orgname;
newobject.is_dir = is_dir;
if(etag){
newobject.etag = etag;
}
objects[newname] = newobject;
}
// add normalization
return insert_normalized(orgname.c_str(), newname.c_str(), is_dir);
}
bool S3ObjList::insert_normalized(const char* name, const char* normalized, bool is_dir)
{
if(!name || '\0' == name[0] || !normalized || '\0' == normalized[0]){
return false;
}
if(0 == strcmp(name, normalized)){
return true;
}
s3obj_t::iterator iter;
if(objects.end() != (iter = objects.find(name))){
// found name --> over write
iter->second.orgname.erase();
iter->second.etag.erase();
iter->second.normalname = normalized;
iter->second.is_dir = is_dir;
}else{
// not found --> add new object
s3obj_entry newobject;
newobject.normalname = normalized;
newobject.is_dir = is_dir;
objects[name] = newobject;
}
return true;
}
const s3obj_entry* S3ObjList::GetS3Obj(const char* name) const
{
s3obj_t::const_iterator iter;
if(!name || '\0' == name[0]){
return nullptr;
}
if(objects.end() == (iter = objects.find(name))){
return nullptr;
}
return &((*iter).second);
}
std::string S3ObjList::GetOrgName(const char* name) const
{
const s3obj_entry* ps3obj;
if(!name || '\0' == name[0]){
return "";
}
if(nullptr == (ps3obj = GetS3Obj(name))){
return "";
}
return ps3obj->orgname;
}
std::string S3ObjList::GetNormalizedName(const char* name) const
{
const s3obj_entry* ps3obj;
if(!name || '\0' == name[0]){
return "";
}
if(nullptr == (ps3obj = GetS3Obj(name))){
return "";
}
if(ps3obj->normalname.empty()){
return name;
}
return ps3obj->normalname;
}
std::string S3ObjList::GetETag(const char* name) const
{
const s3obj_entry* ps3obj;
if(!name || '\0' == name[0]){
return "";
}
if(nullptr == (ps3obj = GetS3Obj(name))){
return "";
}
return ps3obj->etag;
}
bool S3ObjList::IsDir(const char* name) const
{
const s3obj_entry* ps3obj;
if(nullptr == (ps3obj = GetS3Obj(name))){
return false;
}
return ps3obj->is_dir;
}
bool S3ObjList::GetLastName(std::string& lastname) const
{
bool result = false;
lastname = "";
for(s3obj_t::const_iterator iter = objects.begin(); iter != objects.end(); ++iter){
if(!iter->second.orgname.empty()){
if(lastname.compare(iter->second.orgname) < 0){
lastname = (*iter).second.orgname;
result = true;
}
}else{
if(lastname.compare(iter->second.normalname) < 0){
lastname = (*iter).second.normalname;
result = true;
}
}
}
return result;
}
bool S3ObjList::GetNameList(s3obj_list_t& list, bool OnlyNormalized, bool CutSlash) const
{
s3obj_t::const_iterator iter;
for(iter = objects.begin(); objects.end() != iter; ++iter){
if(OnlyNormalized && !iter->second.normalname.empty()){
continue;
}
std::string name = (*iter).first;
if(CutSlash && 1 < name.length() && '/' == *name.rbegin()){
// only "/" std::string is skipped this.
name.erase(name.length() - 1);
}
list.push_back(name);
}
return true;
}
typedef std::map<std::string, bool> s3obj_h_t;
bool S3ObjList::MakeHierarchizedList(s3obj_list_t& list, bool haveSlash)
{
s3obj_h_t h_map;
s3obj_h_t::iterator hiter;
s3obj_list_t::const_iterator liter;
for(liter = list.begin(); list.end() != liter; ++liter){
std::string strtmp = (*liter);
if(1 < strtmp.length() && '/' == *strtmp.rbegin()){
strtmp.erase(strtmp.length() - 1);
}
h_map[strtmp] = true;
// check hierarchized directory
for(std::string::size_type pos = strtmp.find_last_of('/'); std::string::npos != pos; pos = strtmp.find_last_of('/')){
strtmp.erase(pos);
if(strtmp.empty() || "/" == strtmp){
break;
}
if(h_map.end() == h_map.find(strtmp)){
// not found
h_map[strtmp] = false;
}
}
}
// check map and add lost hierarchized directory.
for(hiter = h_map.begin(); hiter != h_map.end(); ++hiter){
if(false == (*hiter).second){
// add hierarchized directory.
std::string strtmp = (*hiter).first;
if(haveSlash){
strtmp += "/";
}
list.push_back(strtmp);
}
}
return true;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

85
src/s3objlist.h Normal file
View File

@ -0,0 +1,85 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_S3OBJLIST_H_
#define S3FS_S3OBJLIST_H_
#include <map>
#include <string>
#include <vector>
//-------------------------------------------------------------------
// Structure / Typedef
//-------------------------------------------------------------------
struct s3obj_entry{
std::string normalname; // normalized name: if empty, object is normalized name.
std::string orgname; // original name: if empty, object is original name.
std::string etag;
bool is_dir;
s3obj_entry() : is_dir(false) {}
};
typedef std::map<std::string, struct s3obj_entry> s3obj_t;
typedef std::vector<std::string> s3obj_list_t;
//-------------------------------------------------------------------
// Class S3ObjList
//-------------------------------------------------------------------
class S3ObjList
{
private:
s3obj_t objects;
public:
std::vector<std::string> common_prefixes;
private:
bool insert_normalized(const char* name, const char* normalized, bool is_dir);
const s3obj_entry* GetS3Obj(const char* name) const;
s3obj_t::const_iterator begin() const { return objects.begin(); }
s3obj_t::const_iterator end() const { return objects.end(); }
public:
S3ObjList() {}
~S3ObjList() {}
bool IsEmpty() const { return objects.empty(); }
bool insert(const char* name, const char* etag = nullptr, bool is_dir = false);
std::string GetOrgName(const char* name) const;
std::string GetNormalizedName(const char* name) const;
std::string GetETag(const char* name) const;
bool IsDir(const char* name) const;
bool GetNameList(s3obj_list_t& list, bool OnlyNormalized = true, bool CutSlash = true) const;
bool GetLastName(std::string& lastname) const;
static bool MakeHierarchizedList(s3obj_list_t& list, bool haveSlash);
};
#endif // S3FS_S3OBJLIST_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

View File

@ -19,38 +19,17 @@
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <syslog.h>
#include <pthread.h>
#include <curl/curl.h>
#include <csignal>
#include <pthread.h>
#include <algorithm>
#include <map>
#include <string>
#include <list>
#include <vector>
#include "common.h"
#include "s3fs_logger.h"
#include "sighandlers.h"
#include "curl.h"
#include "fdcache.h"
#include "psemaphore.h"
using namespace std;
//-------------------------------------------------------------------
// Global variables
//-------------------------------------------------------------------
s3fs_log_level debug_level = S3FS_LOG_CRIT;
const char* s3fs_log_nest[S3FS_LOG_NEST_MAX] = {"", " ", " ", " "};
//-------------------------------------------------------------------
// Class S3fsSignals
//-------------------------------------------------------------------
S3fsSignals* S3fsSignals::pSingleton = NULL;
std::unique_ptr<S3fsSignals> S3fsSignals::pSingleton;
bool S3fsSignals::enableUsr1 = false;
//-------------------------------------------------------------------
@ -58,222 +37,224 @@ bool S3fsSignals::enableUsr1 = false;
//-------------------------------------------------------------------
bool S3fsSignals::Initialize()
{
if(!S3fsSignals::pSingleton){
S3fsSignals::pSingleton = new S3fsSignals;
}
return true;
if(!S3fsSignals::pSingleton){
S3fsSignals::pSingleton.reset(new S3fsSignals);
}
return true;
}
bool S3fsSignals::Destroy()
{
if(S3fsSignals::pSingleton){
delete S3fsSignals::pSingleton;
}
return true;
S3fsSignals::pSingleton.reset();
return true;
}
void S3fsSignals::HandlerUSR1(int sig)
{
if(SIGUSR1 != sig){
S3FS_PRN_ERR("The handler for SIGUSR1 received signal(%d)", sig);
return;
}
if(SIGUSR1 != sig){
S3FS_PRN_ERR("The handler for SIGUSR1 received signal(%d)", sig);
return;
}
S3fsSignals* pSigobj = S3fsSignals::get();
if(!pSigobj){
S3FS_PRN_ERR("S3fsSignals object is not initialized.");
return;
}
S3fsSignals* pSigobj = S3fsSignals::get();
if(!pSigobj){
S3FS_PRN_ERR("S3fsSignals object is not initialized.");
return;
}
if(!pSigobj->WakeupUsr1Thread()){
S3FS_PRN_ERR("Failed to wakeup the thread for SIGUSR1.");
return;
}
if(!pSigobj->WakeupUsr1Thread()){
S3FS_PRN_ERR("Failed to wakeup the thread for SIGUSR1.");
return;
}
}
bool S3fsSignals::SetUsr1Handler(const char* path)
{
// set output file
if(!FdManager::SetCacheCheckOutput(path)){
S3FS_PRN_ERR("Could not set output file(%s) for checking cache.", path ? path : "null(stdout)");
return false;
}
if(!FdManager::HaveLseekHole()){
S3FS_PRN_ERR("Could not set SIGUSR1 for checking cache, because this system does not support SEEK_DATA/SEEK_HOLE in lseek function.");
return false;
}
S3fsSignals::enableUsr1 = true;
// set output file
if(!FdManager::SetCacheCheckOutput(path)){
S3FS_PRN_ERR("Could not set output file(%s) for checking cache.", path ? path : "null(stdout)");
return false;
}
return true;
S3fsSignals::enableUsr1 = true;
return true;
}
void* S3fsSignals::CheckCacheWorker(void* arg)
{
Semaphore* pSem = static_cast<Semaphore*>(arg);
if(!pSem){
pthread_exit(NULL);
}
if(!S3fsSignals::enableUsr1){
pthread_exit(NULL);
}
// wait and loop
while(S3fsSignals::enableUsr1){
// wait
pSem->wait();
Semaphore* pSem = static_cast<Semaphore*>(arg);
if(!pSem){
pthread_exit(nullptr);
}
if(!S3fsSignals::enableUsr1){
break; // assap
pthread_exit(nullptr);
}
// check all cache
if(!FdManager::get()->CheckAllCache()){
S3FS_PRN_ERR("Processing failed due to some problem.");
}
// wait and loop
while(S3fsSignals::enableUsr1){
// wait
pSem->wait();
// do not allow request queuing
for(int value = pSem->get_value(); 0 < value; value = pSem->get_value()){
pSem->wait();
// cppcheck-suppress unmatchedSuppression
// cppcheck-suppress knownConditionTrueFalse
if(!S3fsSignals::enableUsr1){
break; // assap
}
// check all cache
if(!FdManager::get()->CheckAllCache()){
S3FS_PRN_ERR("Processing failed due to some problem.");
}
// do not allow request queuing
for(int value = pSem->get_value(); 0 < value; value = pSem->get_value()){
pSem->wait();
}
}
}
return NULL;
return nullptr;
}
void S3fsSignals::HandlerUSR2(int sig)
{
if(SIGUSR2 == sig){
S3fsSignals::BumpupLogLevel();
}else{
S3FS_PRN_ERR("The handler for SIGUSR2 received signal(%d)", sig);
}
if(SIGUSR2 == sig){
S3fsLog::BumpupLogLevel();
}else{
S3FS_PRN_ERR("The handler for SIGUSR2 received signal(%d)", sig);
}
}
bool S3fsSignals::InitUsr2Handler()
{
struct sigaction sa;
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = S3fsSignals::HandlerUSR2;
sa.sa_flags = SA_RESTART;
if(0 != sigaction(SIGUSR2, &sa, NULL)){
return false;
}
return true;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = S3fsSignals::HandlerUSR2;
sa.sa_flags = SA_RESTART;
if(0 != sigaction(SIGUSR2, &sa, nullptr)){
return false;
}
return true;
}
s3fs_log_level S3fsSignals::SetLogLevel(s3fs_log_level level)
void S3fsSignals::HandlerHUP(int sig)
{
if(level == debug_level){
return debug_level;
}
s3fs_log_level old = debug_level;
debug_level = level;
setlogmask(LOG_UPTO(S3FS_LOG_LEVEL_TO_SYSLOG(debug_level)));
S3FS_PRN_CRIT("change debug level from %sto %s", S3FS_LOG_LEVEL_STRING(old), S3FS_LOG_LEVEL_STRING(debug_level));
return old;
if(SIGHUP == sig){
S3fsLog::ReopenLogfile();
}else{
S3FS_PRN_ERR("The handler for SIGHUP received signal(%d)", sig);
}
}
s3fs_log_level S3fsSignals::BumpupLogLevel()
bool S3fsSignals::InitHupHandler()
{
s3fs_log_level old = debug_level;
debug_level = ( S3FS_LOG_CRIT == debug_level ? S3FS_LOG_ERR :
S3FS_LOG_ERR == debug_level ? S3FS_LOG_WARN :
S3FS_LOG_WARN == debug_level ? S3FS_LOG_INFO :
S3FS_LOG_INFO == debug_level ? S3FS_LOG_DBG :
S3FS_LOG_CRIT );
setlogmask(LOG_UPTO(S3FS_LOG_LEVEL_TO_SYSLOG(debug_level)));
S3FS_PRN_CRIT("change debug level from %sto %s", S3FS_LOG_LEVEL_STRING(old), S3FS_LOG_LEVEL_STRING(debug_level));
return old;
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = S3fsSignals::HandlerHUP;
sa.sa_flags = SA_RESTART;
if(0 != sigaction(SIGHUP, &sa, nullptr)){
return false;
}
return true;
}
//-------------------------------------------------------------------
// Methods
//-------------------------------------------------------------------
S3fsSignals::S3fsSignals() : pThreadUsr1(NULL), pSemUsr1(NULL)
S3fsSignals::S3fsSignals()
{
if(S3fsSignals::enableUsr1){
if(!InitUsr1Handler()){
S3FS_PRN_ERR("failed creating thread for SIGUSR1 handler, but continue...");
if(S3fsSignals::enableUsr1){
if(!InitUsr1Handler()){
S3FS_PRN_ERR("failed creating thread for SIGUSR1 handler, but continue...");
}
}
if(!S3fsSignals::InitUsr2Handler()){
S3FS_PRN_ERR("failed to initialize SIGUSR2 handler for bumping log level, but continue...");
}
if(!S3fsSignals::InitHupHandler()){
S3FS_PRN_ERR("failed to initialize SIGHUP handler for reopen log file, but continue...");
}
}
if(!S3fsSignals::InitUsr2Handler()){
S3FS_PRN_ERR("failed to initialize SIGUSR2 handler for bumping log level, but continue...");
}
}
S3fsSignals::~S3fsSignals()
{
if(S3fsSignals::enableUsr1){
if(!DestroyUsr1Handler()){
S3FS_PRN_ERR("failed stopping thread for SIGUSR1 handler, but continue...");
if(S3fsSignals::enableUsr1){
if(!DestroyUsr1Handler()){
S3FS_PRN_ERR("failed stopping thread for SIGUSR1 handler, but continue...");
}
}
}
}
bool S3fsSignals::InitUsr1Handler()
{
if(pThreadUsr1 || pSemUsr1){
S3FS_PRN_ERR("Already run thread for SIGUSR1");
return false;
}
if(pThreadUsr1 || pSemUsr1){
S3FS_PRN_ERR("Already run thread for SIGUSR1");
return false;
}
// create thread
int result;
pSemUsr1 = new Semaphore(0);
pThreadUsr1 = new pthread_t;
if(0 != (result = pthread_create(pThreadUsr1, NULL, S3fsSignals::CheckCacheWorker, static_cast<void*>(pSemUsr1)))){
S3FS_PRN_ERR("Could not create thread for SIGUSR1 by %d", result);
delete pSemUsr1;
delete pThreadUsr1;
pSemUsr1 = NULL;
pThreadUsr1 = NULL;
return false;
}
// create thread
int result;
std::unique_ptr<Semaphore> pSemUsr1_tmp(new Semaphore(0));
std::unique_ptr<pthread_t> pThreadUsr1_tmp(new pthread_t);
if(0 != (result = pthread_create(pThreadUsr1.get(), nullptr, S3fsSignals::CheckCacheWorker, static_cast<void*>(pSemUsr1_tmp.get())))){
S3FS_PRN_ERR("Could not create thread for SIGUSR1 by %d", result);
return false;
}
pSemUsr1 = std::move(pSemUsr1_tmp);
pThreadUsr1 = std::move(pThreadUsr1_tmp);
// set handler
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = S3fsSignals::HandlerUSR1;
sa.sa_flags = SA_RESTART;
if(0 != sigaction(SIGUSR1, &sa, NULL)){
S3FS_PRN_ERR("Could not set signal handler for SIGUSR1");
DestroyUsr1Handler();
return false;
}
// set handler
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = S3fsSignals::HandlerUSR1;
sa.sa_flags = SA_RESTART;
if(0 != sigaction(SIGUSR1, &sa, nullptr)){
S3FS_PRN_ERR("Could not set signal handler for SIGUSR1");
DestroyUsr1Handler();
return false;
}
return true;
return true;
}
bool S3fsSignals::DestroyUsr1Handler()
{
if(!pThreadUsr1 || !pSemUsr1){
return false;
}
// for thread exit
S3fsSignals::enableUsr1 = false;
if(!pThreadUsr1 || !pSemUsr1){
return false;
}
// for thread exit
S3fsSignals::enableUsr1 = false;
// wakeup thread
pSemUsr1->post();
// wakeup thread
pSemUsr1->post();
// wait for thread exiting
void* retval = NULL;
int result;
if(0 != (result = pthread_join(*pThreadUsr1, &retval))){
S3FS_PRN_ERR("Could not stop thread for SIGUSR1 by %d", result);
return false;
}
delete pSemUsr1;
delete pThreadUsr1;
pSemUsr1 = NULL;
pThreadUsr1 = NULL;
// wait for thread exiting
void* retval = nullptr;
int result;
if(0 != (result = pthread_join(*pThreadUsr1, &retval))){
S3FS_PRN_ERR("Could not stop thread for SIGUSR1 by %d", result);
return false;
}
pSemUsr1.reset();
pThreadUsr1.reset();
return true;
return true;
}
bool S3fsSignals::WakeupUsr1Thread()
{
if(!pThreadUsr1 || !pSemUsr1){
S3FS_PRN_ERR("The thread for SIGUSR1 is not setup.");
return false;
}
pSemUsr1->post();
return true;
if(!pThreadUsr1 || !pSemUsr1){
S3FS_PRN_ERR("The thread for SIGUSR1 is not setup.");
return false;
}
pSemUsr1->post();
return true;
}
/*
@ -281,6 +262,6 @@ bool S3fsSignals::WakeupUsr1Thread()
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

View File

@ -21,44 +21,50 @@
#ifndef S3FS_SIGHANDLERS_H_
#define S3FS_SIGHANDLERS_H_
#include "psemaphore.h"
#include <memory>
class Semaphore;
//----------------------------------------------
// class S3fsSignals
//----------------------------------------------
class S3fsSignals
{
private:
static S3fsSignals* pSingleton;
static bool enableUsr1;
private:
static std::unique_ptr<S3fsSignals> pSingleton;
static bool enableUsr1;
pthread_t* pThreadUsr1;
Semaphore* pSemUsr1;
std::unique_ptr<pthread_t> pThreadUsr1;
std::unique_ptr<Semaphore> pSemUsr1;
protected:
static S3fsSignals* get(void) { return pSingleton; }
protected:
static S3fsSignals* get() { return pSingleton.get(); }
static void HandlerUSR1(int sig);
static void* CheckCacheWorker(void* arg);
static void HandlerUSR1(int sig);
static void* CheckCacheWorker(void* arg);
static void HandlerUSR2(int sig);
static bool InitUsr2Handler(void);
static void HandlerUSR2(int sig);
static bool InitUsr2Handler();
S3fsSignals();
~S3fsSignals();
static void HandlerHUP(int sig);
static bool InitHupHandler();
bool InitUsr1Handler(void);
bool DestroyUsr1Handler(void);
bool WakeupUsr1Thread(void);
S3fsSignals();
S3fsSignals(const S3fsSignals&) = delete;
S3fsSignals(S3fsSignals&&) = delete;
S3fsSignals& operator=(const S3fsSignals&) = delete;
S3fsSignals& operator=(S3fsSignals&&) = delete;
public:
static bool Initialize(void);
static bool Destroy(void);
bool InitUsr1Handler();
bool DestroyUsr1Handler();
bool WakeupUsr1Thread();
static bool SetUsr1Handler(const char* path);
public:
~S3fsSignals();
static bool Initialize();
static bool Destroy();
static s3fs_log_level SetLogLevel(s3fs_log_level level);
static s3fs_log_level BumpupLogLevel(void);
static bool SetUsr1Handler(const char* path);
};
#endif // S3FS_SIGHANDLERS_H_
@ -68,6 +74,6 @@ class S3fsSignals
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

File diff suppressed because it is too large Load Diff

View File

@ -17,54 +17,113 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_STRING_UTIL_H_
#define S3FS_STRING_UTIL_H_
/*
* A collection of string utilities for manipulating URLs and HTTP responses.
*/
#include <string.h>
#include <syslog.h>
#include <sys/types.h>
#include <cstring>
#include <string>
static const std::string SPACES = " \t\r\n";
//
// A collection of string utilities for manipulating URLs and HTTP responses.
//
//-------------------------------------------------------------------
// Global variables
//-------------------------------------------------------------------
static constexpr char SPACES[] = " \t\r\n";
static inline int STR2NCMP(const char *str1, const char *str2) { return strncmp(str1, str2, strlen(str2)); }
//-------------------------------------------------------------------
// Inline functions
//-------------------------------------------------------------------
static inline int is_prefix(const char *str, const char *prefix) { return strncmp(str, prefix, strlen(prefix)) == 0; }
static inline const char* SAFESTRPTR(const char *strptr) { return strptr ? strptr : ""; }
template <class T> std::string str(T value);
//-------------------------------------------------------------------
// Macros(WTF8)
//-------------------------------------------------------------------
#define WTF8_ENCODE(ARG) \
std::string ARG##_buf; \
const char * ARG = _##ARG; \
if (use_wtf8 && s3fs_wtf8_encode( _##ARG, 0 )) { \
s3fs_wtf8_encode( _##ARG, &ARG##_buf); \
ARG = ARG##_buf.c_str(); \
}
// Convert string to off_t. Throws std::invalid_argument and std::out_of_range on bad input.
off_t s3fs_strtoofft(const char* str, int base = 0);
bool try_strtoofft(const char* str, off_t& value, int base = 0);
off_t cvt_strtoofft(const char* str, int base = 0);
//-------------------------------------------------------------------
// Utilities
//-------------------------------------------------------------------
// TODO: rename to to_string?
std::string str(const struct timespec value);
std::string trim_left(const std::string &s, const std::string &t = SPACES);
std::string trim_right(const std::string &s, const std::string &t = SPACES);
std::string trim(const std::string &s, const std::string &t = SPACES);
#ifdef __MSYS__
//
// Polyfill for strptime function.
//
char* strptime(const char* s, const char* f, struct tm* tm);
#endif
//
// Convert string to off_t. Returns false on bad input.
// Replacement for C++11 std::stoll.
//
bool s3fs_strtoofft(off_t* value, const char* str, int base = 0);
//
// This function returns 0 if a value that cannot be converted is specified.
// Only call if 0 is considered an error and the operation can continue.
//
off_t cvt_strtoofft(const char* str, int base);
//
// String Manipulation
//
std::string trim_left(std::string s, const char *t = SPACES);
std::string trim_right(std::string s, const char *t = SPACES);
std::string trim(std::string s, const char *t = SPACES);
std::string lower(std::string s);
std::string get_date_rfc850(void);
std::string peeloff(const std::string& s);
//
// Date string
//
std::string get_date_rfc850();
void get_date_sigv3(std::string& date, std::string& date8601);
std::string get_date_string(time_t tm);
std::string get_date_iso8601(time_t tm);
bool get_unixtime_from_iso8601(const char* pdate, time_t& unixtime);
bool convert_unixtime_from_option_arg(const char* argv, time_t& unixtime);
std::string urlEncode(const std::string &s);
std::string urlEncode2(const std::string &s);
//
// For encoding
//
std::string urlEncodeGeneral(const std::string &s);
std::string urlEncodePath(const std::string &s);
std::string urlEncodeQuery(const std::string &s);
std::string urlDecode(const std::string& s);
bool takeout_str_dquart(std::string& str);
bool get_keyword_value(std::string& target, const char* keyword, std::string& value);
bool get_keyword_value(const std::string& target, const char* keyword, std::string& value);
std::string s3fs_hex(const unsigned char* input, size_t length);
char* s3fs_base64(const unsigned char* input, size_t length);
unsigned char* s3fs_decode64(const char* input, size_t* plength);
//
// For binary string
//
std::string s3fs_hex_lower(const unsigned char* input, size_t length);
std::string s3fs_hex_upper(const unsigned char* input, size_t length);
std::string s3fs_base64(const unsigned char* input, size_t length);
std::string s3fs_decode64(const char* input, size_t input_len);
//
// WTF8
//
bool s3fs_wtf8_encode(const char *s, std::string *result);
std::string s3fs_wtf8_encode(const std::string &s);
bool s3fs_wtf8_decode(const char *s, std::string *result);
std::string s3fs_wtf8_decode(const std::string &s);
//
// For CR in XML
//
std::string get_encoded_cr_code(const char* pbase);
std::string get_decoded_cr_code(const char* pencode);
#endif // S3FS_STRING_UTIL_H_
/*
@ -72,6 +131,6 @@ std::string s3fs_wtf8_decode(const std::string &s);
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

167
src/test_curl_util.cpp Normal file
View File

@ -0,0 +1,167 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2020 Andrew Gaul <andrew@gaul.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <string>
#include <cstring>
#include "curl_util.h"
#include "test_util.h"
//---------------------------------------------------------
// S3fsCred Stub
//
// [NOTE]
// This test program links curl_util.cpp just to use the
// curl_slist_sort_insert function.
// This file has a call to S3fsCred::GetBucket(), which
// results in a link error. That method is not used in
// this test file, so define a stub class. Linking all
// implementation of the S3fsCred class or making all
// stubs is not practical, so this is the best answer.
//
class S3fsCred
{
private:
static std::string bucket_name;
public:
static const std::string& GetBucket();
};
std::string S3fsCred::bucket_name;
const std::string& S3fsCred::GetBucket()
{
return S3fsCred::bucket_name;
}
//---------------------------------------------------------
#define ASSERT_IS_SORTED(x) assert_is_sorted((x), __FILE__, __LINE__)
void assert_is_sorted(const struct curl_slist* list, const char *file, int line)
{
for(; list != nullptr; list = list->next){
std::string key1 = list->data;
key1.erase(key1.find(':'));
std::string key2 = list->data;
key2.erase(key2.find(':'));
std::cerr << "key1: " << key1 << " key2: " << key2 << std::endl;
if(strcasecmp(key1.c_str(), key2.c_str()) > 0){
std::cerr << "not sorted: " << key1 << " " << key2 << " at " << file << ":" << line << std::endl;
std::exit(1);
}
}
std::cerr << std::endl;
}
size_t curl_slist_length(const struct curl_slist* list)
{
size_t len = 0;
for(; list != nullptr; list = list->next){
++len;
}
return len;
}
void test_sort_insert()
{
struct curl_slist* list = nullptr;
ASSERT_IS_SORTED(list);
// add to head
list = curl_slist_sort_insert(list, "2", "val");
ASSERT_IS_SORTED(list);
// add to tail
list = curl_slist_sort_insert(list, "4", "val");
ASSERT_IS_SORTED(list);
// add in between
list = curl_slist_sort_insert(list, "3", "val");
ASSERT_IS_SORTED(list);
// add to head
list = curl_slist_sort_insert(list, "1", "val");
ASSERT_IS_SORTED(list);
ASSERT_STREQUALS("1: val", list->data);
// replace head
list = curl_slist_sort_insert(list, "1", "val2");
ASSERT_IS_SORTED(list);
ASSERT_EQUALS(static_cast<size_t>(4), curl_slist_length(list));
ASSERT_STREQUALS("1: val2", list->data);
curl_slist_free_all(list);
}
void test_slist_remove()
{
struct curl_slist* list = nullptr;
// remove no elements
ASSERT_EQUALS(static_cast<size_t>(0), curl_slist_length(list));
list = curl_slist_remove(list, "1");
ASSERT_EQUALS(static_cast<size_t>(0), curl_slist_length(list));
// remove only element
list = nullptr;
list = curl_slist_sort_insert(list, "1", "val");
ASSERT_EQUALS(static_cast<size_t>(1), curl_slist_length(list));
list = curl_slist_remove(list, "1");
ASSERT_EQUALS(static_cast<size_t>(0), curl_slist_length(list));
// remove head element
list = nullptr;
list = curl_slist_sort_insert(list, "1", "val");
list = curl_slist_sort_insert(list, "2", "val");
ASSERT_EQUALS(static_cast<size_t>(2), curl_slist_length(list));
list = curl_slist_remove(list, "1");
ASSERT_EQUALS(static_cast<size_t>(1), curl_slist_length(list));
curl_slist_free_all(list);
// remove tail element
list = nullptr;
list = curl_slist_sort_insert(list, "1", "val");
list = curl_slist_sort_insert(list, "2", "val");
ASSERT_EQUALS(static_cast<size_t>(2), curl_slist_length(list));
list = curl_slist_remove(list, "2");
ASSERT_EQUALS(static_cast<size_t>(1), curl_slist_length(list));
curl_slist_free_all(list);
// remove middle element
list = nullptr;
list = curl_slist_sort_insert(list, "1", "val");
list = curl_slist_sort_insert(list, "2", "val");
list = curl_slist_sort_insert(list, "3", "val");
ASSERT_EQUALS(static_cast<size_t>(3), curl_slist_length(list));
list = curl_slist_remove(list, "2");
ASSERT_EQUALS(static_cast<size_t>(2), curl_slist_length(list));
curl_slist_free_all(list);
}
int main(int argc, char *argv[])
{
test_sort_insert();
test_slist_remove();
return 0;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

79
src/test_page_list.cpp Normal file
View File

@ -0,0 +1,79 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2021 Andrew Gaul <andrew@gaul.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "fdcache_page.h"
#include "fdcache_stat.h"
#include "test_util.h"
bool CacheFileStat::Open() { return false; }
void test_compress()
{
PageList list;
ASSERT_EQUALS(off_t(0), list.Size());
list.Init(42, /*is_loaded=*/ false, /*is_modified=*/ false);
ASSERT_EQUALS(off_t(42), list.Size());
ASSERT_FALSE(list.IsPageLoaded(0, 1));
list.SetPageLoadedStatus(0, 1, /*pstatus=*/ PageList::page_status::LOADED);
ASSERT_TRUE(list.IsPageLoaded(0, 1));
ASSERT_FALSE(list.IsPageLoaded(0, 2));
off_t start = 0;
off_t size = 0;
ASSERT_TRUE(list.FindUnloadedPage(0, start, size));
ASSERT_EQUALS(off_t(1), start);
ASSERT_EQUALS(off_t(41), size);
// test adding subsequent page then compressing
list.SetPageLoadedStatus(1, 3, /*pstatus=*/ PageList::page_status::LOADED);
list.Compress();
ASSERT_TRUE(list.IsPageLoaded(0, 3));
ASSERT_TRUE(list.FindUnloadedPage(0, start, size));
ASSERT_EQUALS(off_t(4), start);
ASSERT_EQUALS(off_t(38), size);
// test adding non-contiguous page then compressing
list.SetPageLoadedStatus(5, 1, /*pstatus=*/ PageList::page_status::LOADED);
list.Compress();
ASSERT_TRUE(list.FindUnloadedPage(0, start, size));
ASSERT_EQUALS(off_t(4), start);
ASSERT_EQUALS(off_t(1), size);
list.Dump();
printf("\n");
// test adding page between two pages then compressing
list.SetPageLoadedStatus(4, 1, /*pstatus=*/ PageList::page_status::LOADED);
list.Compress();
list.Dump();
ASSERT_TRUE(list.FindUnloadedPage(0, start, size));
ASSERT_EQUALS(off_t(6), start);
ASSERT_EQUALS(off_t(36), size);
}
int main(int argc, char *argv[])
{
test_compress();
return 0;
}

View File

@ -18,13 +18,12 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <stdint.h>
#include <strings.h>
#include <string>
#include <map>
#include "common.h"
#include "s3fs_logger.h"
#include "string_util.h"
#include "test_util.h"
@ -32,105 +31,190 @@
// Global variables for test_string_util
//-------------------------------------------------------------------
bool foreground = false;
s3fs_log_level debug_level = S3FS_LOG_CRIT;
std::string instance_name;
void test_trim()
{
ASSERT_EQUALS(std::string("1234"), trim(" 1234 "));
ASSERT_EQUALS(std::string("1234"), trim("1234 "));
ASSERT_EQUALS(std::string("1234"), trim(" 1234"));
ASSERT_EQUALS(std::string("1234"), trim("1234"));
ASSERT_EQUALS(std::string("1234"), trim(" 1234 "));
ASSERT_EQUALS(std::string("1234"), trim("1234 "));
ASSERT_EQUALS(std::string("1234"), trim(" 1234"));
ASSERT_EQUALS(std::string("1234"), trim("1234"));
ASSERT_EQUALS(std::string("1234 "), trim_left(" 1234 "));
ASSERT_EQUALS(std::string("1234 "), trim_left("1234 "));
ASSERT_EQUALS(std::string("1234"), trim_left(" 1234"));
ASSERT_EQUALS(std::string("1234"), trim_left("1234"));
ASSERT_EQUALS(std::string("1234 "), trim_left(" 1234 "));
ASSERT_EQUALS(std::string("1234 "), trim_left("1234 "));
ASSERT_EQUALS(std::string("1234"), trim_left(" 1234"));
ASSERT_EQUALS(std::string("1234"), trim_left("1234"));
ASSERT_EQUALS(std::string(" 1234"), trim_right(" 1234 "));
ASSERT_EQUALS(std::string("1234"), trim_right("1234 "));
ASSERT_EQUALS(std::string(" 1234"), trim_right(" 1234"));
ASSERT_EQUALS(std::string("1234"), trim_right("1234"));
ASSERT_EQUALS(std::string(" 1234"), trim_right(" 1234 "));
ASSERT_EQUALS(std::string("1234"), trim_right("1234 "));
ASSERT_EQUALS(std::string(" 1234"), trim_right(" 1234"));
ASSERT_EQUALS(std::string("1234"), trim_right("1234"));
ASSERT_EQUALS(std::string("0"), str(0));
ASSERT_EQUALS(std::string("1"), str(1));
ASSERT_EQUALS(std::string("-1"), str(-1));
ASSERT_EQUALS(std::string("9223372036854775807"), str(std::numeric_limits<int64_t>::max()));
ASSERT_EQUALS(std::string("-9223372036854775808"), str(std::numeric_limits<int64_t>::min()));
ASSERT_EQUALS(std::string("0"), str(std::numeric_limits<uint64_t>::min()));
ASSERT_EQUALS(std::string("18446744073709551615"), str(std::numeric_limits<uint64_t>::max()));
ASSERT_EQUALS(std::string("1234"), peeloff("\"1234\"")); // "1234" -> 1234
ASSERT_EQUALS(std::string("\"1234\""), peeloff("\"\"1234\"\"")); // ""1234"" -> "1234"
ASSERT_EQUALS(std::string("\"1234"), peeloff("\"\"1234\"")); // ""1234" -> "1234
ASSERT_EQUALS(std::string("1234\""), peeloff("\"1234\"\"")); // "1234"" -> 1234"
ASSERT_EQUALS(std::string("\"1234"), peeloff("\"1234")); // "1234 -> "1234
ASSERT_EQUALS(std::string("1234\""), peeloff("1234\"")); // 1234" -> 1234"
ASSERT_EQUALS(std::string(" \"1234\""), peeloff(" \"1234\"")); // _"1234" -> _"1234"
ASSERT_EQUALS(std::string("\"1234\" "), peeloff("\"1234\" ")); // "1234"_ -> "1234"_
}
void test_base64()
{
size_t len;
ASSERT_STREQUALS(s3fs_base64(NULL, 0), NULL);
ASSERT_STREQUALS(reinterpret_cast<const char *>(s3fs_decode64(NULL, &len)), NULL);
ASSERT_STREQUALS(s3fs_base64(reinterpret_cast<const unsigned char *>(""), 0), NULL);
ASSERT_STREQUALS(reinterpret_cast<const char *>(s3fs_decode64("", &len)), NULL);
std::string buf;
char tmpbuf = '\0';
ASSERT_STREQUALS(s3fs_base64(reinterpret_cast<const unsigned char *>("1"), 1), "MQ==");
ASSERT_STREQUALS(reinterpret_cast<const char *>(s3fs_decode64("MQ==", &len)), "1");
ASSERT_EQUALS(len, static_cast<size_t>(1));
ASSERT_STREQUALS(s3fs_base64(reinterpret_cast<const unsigned char *>("12"), 2), "MTI=");
ASSERT_STREQUALS(reinterpret_cast<const char *>(s3fs_decode64("MTI=", &len)), "12");
ASSERT_EQUALS(len, static_cast<size_t>(2));
ASSERT_STREQUALS(s3fs_base64(reinterpret_cast<const unsigned char *>("123"), 3), "MTIz");
ASSERT_STREQUALS(reinterpret_cast<const char *>(s3fs_decode64("MTIz", &len)), "123");
ASSERT_EQUALS(len, static_cast<size_t>(3));
ASSERT_STREQUALS(s3fs_base64(reinterpret_cast<const unsigned char *>("1234"), 4), "MTIzNA==");
ASSERT_STREQUALS(reinterpret_cast<const char *>(s3fs_decode64("MTIzNA==", &len)), "1234");
ASSERT_EQUALS(len, static_cast<size_t>(4));
ASSERT_EQUALS(s3fs_base64(nullptr, 0), std::string(""));
buf = s3fs_decode64(nullptr, 0);
ASSERT_BUFEQUALS(buf.c_str(), buf.length(), &tmpbuf, 0);
// TODO: invalid input
ASSERT_EQUALS(s3fs_base64(reinterpret_cast<const unsigned char *>(""), 0), std::string(""));
buf = s3fs_decode64("", 0);
ASSERT_BUFEQUALS(buf.c_str(), buf.length(), &tmpbuf, 0);
ASSERT_EQUALS(s3fs_base64(reinterpret_cast<const unsigned char *>("1"), 1), std::string("MQ=="));
buf = s3fs_decode64("MQ==", 4);
ASSERT_BUFEQUALS(buf.c_str(), buf.length(), "1", 1);
ASSERT_EQUALS(buf.length(), static_cast<size_t>(1));
ASSERT_EQUALS(s3fs_base64(reinterpret_cast<const unsigned char *>("12"), 2), std::string("MTI="));
buf = s3fs_decode64("MTI=", 4);
ASSERT_BUFEQUALS(buf.c_str(), buf.length(), "12", 2);
ASSERT_EQUALS(buf.length(), static_cast<size_t>(2));
ASSERT_EQUALS(s3fs_base64(reinterpret_cast<const unsigned char *>("123"), 3), std::string("MTIz"));
buf = s3fs_decode64("MTIz", 4);
ASSERT_BUFEQUALS(buf.c_str(), buf.length(), "123", 3);
ASSERT_EQUALS(buf.length(), static_cast<size_t>(3));
ASSERT_EQUALS(s3fs_base64(reinterpret_cast<const unsigned char *>("1234"), 4), std::string("MTIzNA=="));
buf = s3fs_decode64("MTIzNA==", 8);
ASSERT_BUFEQUALS(buf.c_str(), buf.length(), "1234", 4);
ASSERT_EQUALS(buf.length(), static_cast<size_t>(4));
// TODO: invalid input
}
void test_strtoofft()
{
ASSERT_EQUALS(s3fs_strtoofft("0"), static_cast<off_t>(0L));
ASSERT_EQUALS(s3fs_strtoofft("9"), static_cast<off_t>(9L));
try{
s3fs_strtoofft("A");
abort();
}catch(std::exception &e){
// expected
}
ASSERT_EQUALS(s3fs_strtoofft("A", /*base=*/ 16), static_cast<off_t>(10L));
ASSERT_EQUALS(s3fs_strtoofft("F", /*base=*/ 16), static_cast<off_t>(15L));
ASSERT_EQUALS(s3fs_strtoofft("a", /*base=*/ 16), static_cast<off_t>(10L));
ASSERT_EQUALS(s3fs_strtoofft("f", /*base=*/ 16), static_cast<off_t>(15L));
ASSERT_EQUALS(s3fs_strtoofft("deadbeef", /*base=*/ 16), static_cast<off_t>(3735928559L));
off_t value;
ASSERT_TRUE(s3fs_strtoofft(&value, "0"));
ASSERT_EQUALS(value, static_cast<off_t>(0L));
ASSERT_TRUE(s3fs_strtoofft(&value, "9"));
ASSERT_EQUALS(value, static_cast<off_t>(9L));
ASSERT_FALSE(s3fs_strtoofft(&value, "A"));
ASSERT_TRUE(s3fs_strtoofft(&value, "A", /*base=*/ 16));
ASSERT_EQUALS(value, static_cast<off_t>(10L));
ASSERT_TRUE(s3fs_strtoofft(&value, "F", /*base=*/ 16));
ASSERT_EQUALS(value, static_cast<off_t>(15L));
ASSERT_TRUE(s3fs_strtoofft(&value, "a", /*base=*/ 16));
ASSERT_EQUALS(value, static_cast<off_t>(10L));
ASSERT_TRUE(s3fs_strtoofft(&value, "f", /*base=*/ 16));
ASSERT_EQUALS(value, static_cast<off_t>(15L));
ASSERT_TRUE(s3fs_strtoofft(&value, "deadbeef", /*base=*/ 16));
ASSERT_EQUALS(value, static_cast<off_t>(3735928559L));
}
void test_wtf8_encoding()
{
std::string ascii("normal string");
std::string utf8("Hyld\xc3\xbdpi \xc3\xbej\xc3\xb3\xc3\xb0""f\xc3\xa9lagsins vex \xc3\xbar k\xc3\xa6rkomnu b\xc3\xb6li \xc3\xad \xc3\xa1st");
std::string cp1252("Hyld\xfdpi \xfej\xf3\xf0""f\xe9lagsins vex \xfar k\xe6rkomnu b\xf6li \xed \xe1st");
std::string broken = utf8;
broken[14] = 0x97;
std::string mixed = ascii + utf8 + cp1252;
std::string ascii("normal std::string");
std::string utf8("Hyld\xc3\xbdpi \xc3\xbej\xc3\xb3\xc3\xb0""f\xc3\xa9lagsins vex \xc3\xbar k\xc3\xa6rkomnu b\xc3\xb6li \xc3\xad \xc3\xa1st");
std::string cp1252("Hyld\xfdpi \xfej\xf3\xf0""f\xe9lagsins vex \xfar k\xe6rkomnu b\xf6li \xed \xe1st");
std::string broken = utf8;
broken[14] = 0x97;
std::string mixed = ascii + utf8 + cp1252;
ASSERT_EQUALS(s3fs_wtf8_encode(ascii), ascii);
ASSERT_EQUALS(s3fs_wtf8_decode(ascii), ascii);
ASSERT_EQUALS(s3fs_wtf8_encode(utf8), utf8);
ASSERT_EQUALS(s3fs_wtf8_decode(utf8), utf8);
ASSERT_EQUALS(s3fs_wtf8_encode(ascii), ascii);
ASSERT_EQUALS(s3fs_wtf8_decode(ascii), ascii);
ASSERT_EQUALS(s3fs_wtf8_encode(utf8), utf8);
ASSERT_EQUALS(s3fs_wtf8_decode(utf8), utf8);
ASSERT_NEQUALS(s3fs_wtf8_encode(cp1252), cp1252);
ASSERT_EQUALS(s3fs_wtf8_decode(s3fs_wtf8_encode(cp1252)), cp1252);
ASSERT_NEQUALS(s3fs_wtf8_encode(cp1252), cp1252);
ASSERT_EQUALS(s3fs_wtf8_decode(s3fs_wtf8_encode(cp1252)), cp1252);
ASSERT_NEQUALS(s3fs_wtf8_encode(broken), broken);
ASSERT_EQUALS(s3fs_wtf8_decode(s3fs_wtf8_encode(broken)), broken);
ASSERT_NEQUALS(s3fs_wtf8_encode(broken), broken);
ASSERT_EQUALS(s3fs_wtf8_decode(s3fs_wtf8_encode(broken)), broken);
ASSERT_NEQUALS(s3fs_wtf8_encode(mixed), mixed);
ASSERT_EQUALS(s3fs_wtf8_decode(s3fs_wtf8_encode(mixed)), mixed);
ASSERT_NEQUALS(s3fs_wtf8_encode(mixed), mixed);
ASSERT_EQUALS(s3fs_wtf8_decode(s3fs_wtf8_encode(mixed)), mixed);
}
void test_cr_encoding()
{
// bse strings
std::string base_no("STR");
std::string base_end_cr1("STR\r");
std::string base_mid_cr1("STR\rSTR");
std::string base_end_cr2("STR\r\r");
std::string base_mid_cr2("STR\r\rSTR");
std::string base_end_per1("STR%");
std::string base_mid_per1("STR%STR");
std::string base_end_per2("STR%%");
std::string base_mid_per2("STR%%STR");
std::string base_end_crlf1("STR\r\n");
std::string base_mid_crlf1("STR\r\nSTR");
std::string base_end_crlf2("STR\r\n\r\n");
std::string base_mid_crlf2("STR\r\n\r\nSTR");
std::string base_end_crper1("STR%\r");
std::string base_mid_crper1("STR%\rSTR");
std::string base_end_crper2("STR%\r%\r");
std::string base_mid_crper2("STR%\r%\rSTR");
// encode->decode->compare
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_no.c_str()).c_str()), base_no);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_end_cr1.c_str()).c_str()), base_end_cr1);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_mid_cr1.c_str()).c_str()), base_mid_cr1);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_end_cr2.c_str()).c_str()), base_end_cr2);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_mid_cr2.c_str()).c_str()), base_mid_cr2);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_end_per1.c_str()).c_str()), base_end_per1);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_mid_per1.c_str()).c_str()), base_mid_per1);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_end_per2.c_str()).c_str()), base_end_per2);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_mid_per2.c_str()).c_str()), base_mid_per2);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_end_crlf1.c_str()).c_str()), base_end_crlf1);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_mid_crlf1.c_str()).c_str()), base_mid_crlf1);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_end_crlf2.c_str()).c_str()), base_end_crlf2);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_mid_crlf2.c_str()).c_str()), base_mid_crlf2);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_end_crper1.c_str()).c_str()), base_end_crper1);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_mid_crper1.c_str()).c_str()), base_mid_crper1);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_end_crper2.c_str()).c_str()), base_end_crper2);
ASSERT_EQUALS(get_decoded_cr_code(get_encoded_cr_code(base_mid_crper2.c_str()).c_str()), base_mid_crper2);
}
int main(int argc, char *argv[])
{
test_trim();
test_base64();
test_strtoofft();
test_wtf8_encoding();
return 0;
S3fsLog singletonLog;
test_trim();
test_base64();
test_strtoofft();
test_wtf8_encoding();
test_cr_encoding();
return 0;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

View File

@ -18,72 +18,89 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_TEST_UTIL_H_
#define S3FS_TEST_UTIL_H_
#include <cstdlib>
#include <iostream>
#include <stdio.h>
#include "string_util.h"
template <typename T> void assert_equals(const T &x, const T &y, const char *file, int line)
{
if (x != y) {
std::cerr << x << " != " << y << " at " << file << ":" << line << std::endl;
std::cerr << std::endl;
std::exit(1);
}
if (x != y) {
std::cerr << x << " != " << y << " at " << file << ":" << line << std::endl;
std::cerr << std::endl;
abort();
}
}
template <> void assert_equals(const std::string &x, const std::string &y, const char *file, int line)
{
if (x != y) {
std::cerr << x << " != " << y << " at " << file << ":" << line << std::endl;
for (unsigned i=0; i<x.length(); i++)
fprintf(stderr, "%02x ", (unsigned char)x[i]);
std::cerr << std::endl;
for (unsigned i=0; i<y.length(); i++)
fprintf(stderr, "%02x ", (unsigned char)y[i]);
std::cerr << std::endl;
std::exit(1);
}
if (x != y) {
std::cerr << x << " != " << y << " at " << file << ":" << line << std::endl;
std::cerr << s3fs_hex_lower(reinterpret_cast<const unsigned char *>(x.c_str()), x.size()) << std::endl;
std::cerr << s3fs_hex_lower(reinterpret_cast<const unsigned char *>(y.c_str()), y.size()) << std::endl;
abort();
}
}
template <typename T> void assert_nequals(const T &x, const T &y, const char *file, int line)
{
if (x == y) {
std::cerr << x << " == " << y << " at " << file << ":" << line << std::endl;
std::exit(1);
}
if (x == y) {
std::cerr << x << " == " << y << " at " << file << ":" << line << std::endl;
abort();
}
}
template <> void assert_nequals(const std::string &x, const std::string &y, const char *file, int line)
{
if (x == y) {
std::cerr << x << " == " << y << " at " << file << ":" << line << std::endl;
for (unsigned i=0; i<x.length(); i++)
fprintf(stderr, "%02x ", (unsigned char)x[i]);
std::cerr << std::endl;
for (unsigned i=0; i<y.length(); i++)
fprintf(stderr, "%02x ", (unsigned char)y[i]);
std::cerr << std::endl;
std::exit(1);
}
if (x == y) {
std::cerr << x << " == " << y << " at " << file << ":" << line << std::endl;
std::cerr << s3fs_hex_lower(reinterpret_cast<const unsigned char *>(x.c_str()), x.size()) << std::endl;
std::cerr << s3fs_hex_lower(reinterpret_cast<const unsigned char *>(y.c_str()), y.size()) << std::endl;
abort();
}
}
void assert_strequals(const char *x, const char *y, const char *file, int line)
{
if(x == NULL && y == NULL){
return;
if(x == nullptr && y == nullptr){
return;
// cppcheck-suppress nullPointerRedundantCheck
} else if(x == NULL || y == NULL || strcmp(x, y) != 0){
std::cerr << (x ? x : "null") << " != " << (y ? y : "null") << " at " << file << ":" << line << std::endl;
std::exit(1);
} else if(x == nullptr || y == nullptr || strcmp(x, y) != 0){
std::cerr << (x ? x : "null") << " != " << (y ? y : "null") << " at " << file << ":" << line << std::endl;
abort();
}
}
#define ASSERT_EQUALS(x, y) \
assert_equals((x), (y), __FILE__, __LINE__)
void assert_bufequals(const char *x, size_t len1, const char *y, size_t len2, const char *file, int line)
{
if(x == nullptr && y == nullptr){
return;
// cppcheck-suppress nullPointerRedundantCheck
} else if(x == nullptr || y == nullptr || len1 != len2 || memcmp(x, y, len1) != 0){
std::cerr << (x ? std::string(x, len1) : "null") << " != " << (y ? std::string(y, len2) : "null") << " at " << file << ":" << line << std::endl;
abort();
}
}
#define ASSERT_NEQUALS(x, y) \
assert_nequals((x), (y), __FILE__, __LINE__)
#define ASSERT_TRUE(x) assert_equals((x), true, __FILE__, __LINE__)
#define ASSERT_FALSE(x) assert_equals((x), false, __FILE__, __LINE__)
#define ASSERT_EQUALS(x, y) assert_equals((x), (y), __FILE__, __LINE__)
#define ASSERT_NEQUALS(x, y) assert_nequals((x), (y), __FILE__, __LINE__)
#define ASSERT_STREQUALS(x, y) assert_strequals((x), (y), __FILE__, __LINE__)
#define ASSERT_BUFEQUALS(x, len1, y, len2) assert_bufequals((x), (len1), (y), (len2), __FILE__, __LINE__)
#define ASSERT_STREQUALS(x, y) \
assert_strequals((x), (y), __FILE__, __LINE__)
#endif // S3FS_TEST_UTIL_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

253
src/threadpoolman.cpp Normal file
View File

@ -0,0 +1,253 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cerrno>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include "s3fs_logger.h"
#include "threadpoolman.h"
#include "autolock.h"
//------------------------------------------------
// ThreadPoolMan class variables
//------------------------------------------------
ThreadPoolMan* ThreadPoolMan::singleton = nullptr;
//------------------------------------------------
// ThreadPoolMan class methods
//------------------------------------------------
bool ThreadPoolMan::Initialize(int count)
{
if(ThreadPoolMan::singleton){
S3FS_PRN_WARN("Already singleton for Thread Manager is existed, then re-create it.");
ThreadPoolMan::Destroy();
}
ThreadPoolMan::singleton = new ThreadPoolMan(count);
return true;
}
void ThreadPoolMan::Destroy()
{
if(ThreadPoolMan::singleton){
delete ThreadPoolMan::singleton;
ThreadPoolMan::singleton = nullptr;
}
}
bool ThreadPoolMan::Instruct(const thpoolman_param& param)
{
if(!ThreadPoolMan::singleton){
S3FS_PRN_WARN("The singleton object is not initialized yet.");
return false;
}
ThreadPoolMan::singleton->SetInstruction(param);
return true;
}
//
// Thread worker
//
void* ThreadPoolMan::Worker(void* arg)
{
ThreadPoolMan* psingleton = static_cast<ThreadPoolMan*>(arg);
if(!psingleton){
S3FS_PRN_ERR("The parameter for worker thread is invalid.");
return reinterpret_cast<void*>(-EIO);
}
S3FS_PRN_INFO3("Start worker thread in ThreadPoolMan.");
while(!psingleton->IsExit()){
// wait
psingleton->thpoolman_sem.wait();
if(psingleton->IsExit()){
break;
}
// get instruction
thpoolman_param param;
{
AutoLock auto_lock(&(psingleton->thread_list_lock));
if(psingleton->instruction_list.empty()){
S3FS_PRN_DBG("Got a semaphore, but the instruction is empty.");
continue;
}else{
param = psingleton->instruction_list.front();
psingleton->instruction_list.pop_front();
}
}
void* retval = param.pfunc(param.args);
if(nullptr != retval){
S3FS_PRN_WARN("The instruction function returned with somthign error code(%ld).", reinterpret_cast<long>(retval));
}
if(param.psem){
param.psem->post();
}
}
return nullptr;
}
//------------------------------------------------
// ThreadPoolMan methods
//------------------------------------------------
ThreadPoolMan::ThreadPoolMan(int count) : is_exit(false), thpoolman_sem(0), is_lock_init(false)
{
if(count < 1){
S3FS_PRN_CRIT("Failed to creating singleton for Thread Manager, because thread count(%d) is under 1.", count);
abort();
}
if(ThreadPoolMan::singleton){
S3FS_PRN_CRIT("Already singleton for Thread Manager is existed.");
abort();
}
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
#if S3FS_PTHREAD_ERRORCHECK
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
#endif
int result;
if(0 != (result = pthread_mutex_init(&thread_list_lock, &attr))){
S3FS_PRN_CRIT("failed to init thread_list_lock: %d", result);
abort();
}
is_lock_init = true;
// create threads
if(!StartThreads(count)){
S3FS_PRN_ERR("Failed starting threads at initializing.");
abort();
}
}
ThreadPoolMan::~ThreadPoolMan()
{
StopThreads();
if(is_lock_init){
int result;
if(0 != (result = pthread_mutex_destroy(&thread_list_lock))){
S3FS_PRN_CRIT("failed to destroy thread_list_lock: %d", result);
abort();
}
is_lock_init = false;
}
}
bool ThreadPoolMan::IsExit() const
{
return is_exit;
}
void ThreadPoolMan::SetExitFlag(bool exit_flag)
{
is_exit = exit_flag;
}
bool ThreadPoolMan::StopThreads()
{
if(thread_list.empty()){
S3FS_PRN_INFO("Any threads are running now, then nothing to do.");
return true;
}
// all threads to exit
SetExitFlag(true);
for(size_t waitcnt = thread_list.size(); 0 < waitcnt; --waitcnt){
thpoolman_sem.post();
}
// wait for threads exiting
for(thread_list_t::const_iterator iter = thread_list.begin(); iter != thread_list.end(); ++iter){
void* retval = nullptr;
int result = pthread_join(*iter, &retval);
if(result){
S3FS_PRN_ERR("failed pthread_join - result(%d)", result);
}else{
S3FS_PRN_DBG("succeed pthread_join - return code(%ld)", reinterpret_cast<long>(retval));
}
}
thread_list.clear();
// reset semaphore(to zero)
while(thpoolman_sem.try_wait()){
}
return true;
}
bool ThreadPoolMan::StartThreads(int count)
{
if(count < 1){
S3FS_PRN_ERR("Failed to creating threads, because thread count(%d) is under 1.", count);
return false;
}
// stop all thread if they are running.
// cppcheck-suppress unmatchedSuppression
// cppcheck-suppress knownConditionTrueFalse
if(!StopThreads()){
S3FS_PRN_ERR("Failed to stop existed threads.");
return false;
}
// create all threads
SetExitFlag(false);
for(int cnt = 0; cnt < count; ++cnt){
// run thread
pthread_t thread;
int result;
if(0 != (result = pthread_create(&thread, nullptr, ThreadPoolMan::Worker, static_cast<void*>(this)))){
S3FS_PRN_ERR("failed pthread_create with return code(%d)", result);
StopThreads(); // if possible, stop all threads
return false;
}
thread_list.push_back(thread);
}
return true;
}
void ThreadPoolMan::SetInstruction(const thpoolman_param& param)
{
// set parameter to list
{
AutoLock auto_lock(&thread_list_lock);
instruction_list.push_back(param);
}
// run thread
thpoolman_sem.post();
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

108
src/threadpoolman.h Normal file
View File

@ -0,0 +1,108 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_THREADPOOLMAN_H_
#define S3FS_THREADPOOLMAN_H_
#include <atomic>
#include <list>
#include <vector>
#include "psemaphore.h"
//------------------------------------------------
// Typedefs for functions and structures
//------------------------------------------------
//
// Prototype function
//
typedef void* (*thpoolman_worker)(void*); // same as start_routine for pthread_create function
//
// Parameter structure
//
// [NOTE]
// The args member is a value that is an argument of the worker function.
// The psem member is allowed nullptr. If it is not nullptr, the post() method is
// called when finishing the function.
//
struct thpoolman_param
{
void* args;
Semaphore* psem;
thpoolman_worker pfunc;
thpoolman_param() : args(nullptr), psem(nullptr), pfunc(nullptr) {}
};
typedef std::list<thpoolman_param> thpoolman_params_t;
typedef std::vector<pthread_t> thread_list_t;
//------------------------------------------------
// Class ThreadPoolMan
//------------------------------------------------
class ThreadPoolMan
{
private:
static ThreadPoolMan* singleton;
std::atomic<bool> is_exit;
Semaphore thpoolman_sem;
bool is_lock_init;
pthread_mutex_t thread_list_lock;
thread_list_t thread_list;
thpoolman_params_t instruction_list;
private:
static void* Worker(void* arg);
explicit ThreadPoolMan(int count = 1);
~ThreadPoolMan();
ThreadPoolMan(const ThreadPoolMan&) = delete;
ThreadPoolMan(ThreadPoolMan&&) = delete;
ThreadPoolMan& operator=(const ThreadPoolMan&) = delete;
ThreadPoolMan& operator=(ThreadPoolMan&&) = delete;
bool IsExit() const;
void SetExitFlag(bool exit_flag);
bool StopThreads();
bool StartThreads(int count);
void SetInstruction(const thpoolman_param& pparam);
public:
static bool Initialize(int count);
static void Destroy();
static bool Instruct(const thpoolman_param& pparam);
};
#endif // S3FS_THREADPOOLMAN_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

363
src/types.h Normal file
View File

@ -0,0 +1,363 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef S3FS_TYPES_H_
#define S3FS_TYPES_H_
#include <cstdlib>
#include <cstring>
#include <string>
#include <map>
#include <list>
#include <vector>
//
// For extended attribute
// (HAVE_XXX symbols are defined in config.h)
//
#ifdef HAVE_SYS_EXTATTR_H
#include <sys/extattr.h>
#elif HAVE_ATTR_XATTR_H
#include <attr/xattr.h>
#elif HAVE_SYS_XATTR_H
#include <sys/xattr.h>
#endif
//-------------------------------------------------------------------
// xattrs_t
//-------------------------------------------------------------------
//
// Header "x-amz-meta-xattr" is for extended attributes.
// This header is url encoded string which is json formatted.
// x-amz-meta-xattr:urlencode({"xattr-1":"base64(value-1)","xattr-2":"base64(value-2)","xattr-3":"base64(value-3)"})
//
typedef std::map<std::string, std::string> xattrs_t;
//-------------------------------------------------------------------
// acl_t
//-------------------------------------------------------------------
enum class acl_t{
PRIVATE,
PUBLIC_READ,
PUBLIC_READ_WRITE,
AWS_EXEC_READ,
AUTHENTICATED_READ,
BUCKET_OWNER_READ,
BUCKET_OWNER_FULL_CONTROL,
LOG_DELIVERY_WRITE,
UNKNOWN
};
inline const char* str(acl_t value)
{
switch(value){
case acl_t::PRIVATE:
return "private";
case acl_t::PUBLIC_READ:
return "public-read";
case acl_t::PUBLIC_READ_WRITE:
return "public-read-write";
case acl_t::AWS_EXEC_READ:
return "aws-exec-read";
case acl_t::AUTHENTICATED_READ:
return "authenticated-read";
case acl_t::BUCKET_OWNER_READ:
return "bucket-owner-read";
case acl_t::BUCKET_OWNER_FULL_CONTROL:
return "bucket-owner-full-control";
case acl_t::LOG_DELIVERY_WRITE:
return "log-delivery-write";
case acl_t::UNKNOWN:
return nullptr;
}
abort();
}
inline acl_t to_acl(const char *acl)
{
if(0 == strcmp(acl, "private")){
return acl_t::PRIVATE;
}else if(0 == strcmp(acl, "public-read")){
return acl_t::PUBLIC_READ;
}else if(0 == strcmp(acl, "public-read-write")){
return acl_t::PUBLIC_READ_WRITE;
}else if(0 == strcmp(acl, "aws-exec-read")){
return acl_t::AWS_EXEC_READ;
}else if(0 == strcmp(acl, "authenticated-read")){
return acl_t::AUTHENTICATED_READ;
}else if(0 == strcmp(acl, "bucket-owner-read")){
return acl_t::BUCKET_OWNER_READ;
}else if(0 == strcmp(acl, "bucket-owner-full-control")){
return acl_t::BUCKET_OWNER_FULL_CONTROL;
}else if(0 == strcmp(acl, "log-delivery-write")){
return acl_t::LOG_DELIVERY_WRITE;
}else{
return acl_t::UNKNOWN;
}
}
//-------------------------------------------------------------------
// sse_type_t
//-------------------------------------------------------------------
enum class sse_type_t{
SSE_DISABLE = 0, // not use server side encrypting
SSE_S3, // server side encrypting by S3 key
SSE_C, // server side encrypting by custom key
SSE_KMS // server side encrypting by kms id
};
enum class signature_type_t {
V2_ONLY,
V4_ONLY,
V2_OR_V4
};
//----------------------------------------------
// etaglist_t / filepart / untreatedpart
//----------------------------------------------
//
// Etag string and part number pair
//
struct etagpair
{
std::string etag; // expected etag value
int part_num; // part number
explicit etagpair(const char* petag = nullptr, int part = -1) : etag(petag ? petag : ""), part_num(part) {}
~etagpair()
{
clear();
}
void clear()
{
etag.erase();
part_num = -1;
}
};
// Requires pointer stability and thus must be a list not a vector
typedef std::list<etagpair> etaglist_t;
struct petagpool
{
// Requires pointer stability and thus must be a list not a vector
std::list<etagpair> petaglist;
~petagpool()
{
clear();
}
void clear()
{
petaglist.clear();
}
etagpair* add(const etagpair& etag_entity)
{
petaglist.push_back(etag_entity);
return &petaglist.back();
}
};
//
// Each part information for Multipart upload
//
struct filepart
{
bool uploaded; // does finish uploading
std::string etag; // expected etag value
int fd; // base file(temporary full file) descriptor
off_t startpos; // seek fd point for uploading
off_t size; // uploading size
bool is_copy; // whether is copy multipart
etagpair* petag; // use only parallel upload
explicit filepart(bool is_uploaded = false, int _fd = -1, off_t part_start = 0, off_t part_size = -1, bool is_copy_part = false, etagpair* petagpair = nullptr) : uploaded(false), fd(_fd), startpos(part_start), size(part_size), is_copy(is_copy_part), petag(petagpair) {}
~filepart()
{
clear();
}
void clear()
{
uploaded = false;
etag = "";
fd = -1;
startpos = 0;
size = -1;
is_copy = false;
petag = nullptr;
}
void add_etag_list(etaglist_t& list, int partnum = -1)
{
if(-1 == partnum){
partnum = static_cast<int>(list.size()) + 1;
}
list.push_back(etagpair(nullptr, partnum));
petag = &list.back();
}
void set_etag(etagpair* petagobj)
{
petag = petagobj;
}
int get_part_number() const
{
if(!petag){
return -1;
}
return petag->part_num;
}
};
typedef std::vector<filepart> filepart_list_t;
//
// Each part information for Untreated parts
//
struct untreatedpart
{
off_t start; // untreated start position
off_t size; // number of untreated bytes
long untreated_tag; // untreated part tag
explicit untreatedpart(off_t part_start = 0, off_t part_size = 0, long part_untreated_tag = 0) : start(part_start), size(part_size), untreated_tag(part_untreated_tag)
{
if(part_start < 0 || part_size <= 0){
clear(); // wrong parameter, so clear value.
}
}
~untreatedpart()
{
clear();
}
void clear()
{
start = 0;
size = 0;
untreated_tag = 0;
}
// [NOTE]
// Check if the areas overlap
// However, even if the areas do not overlap, this method returns true if areas are adjacent.
//
bool check_overlap(off_t chk_start, off_t chk_size)
{
if(chk_start < 0 || chk_size <= 0 || start < 0 || size <= 0 || (chk_start + chk_size) < start || (start + size) < chk_start){
return false;
}
return true;
}
bool stretch(off_t add_start, off_t add_size, long tag)
{
if(!check_overlap(add_start, add_size)){
return false;
}
off_t new_start = std::min(start, add_start);
off_t new_next_start = std::max((start + size), (add_start + add_size));
start = new_start;
size = new_next_start - new_start;
untreated_tag = tag;
return true;
}
};
typedef std::vector<untreatedpart> untreated_list_t;
//
// Information on each part of multipart upload
//
struct mp_part
{
off_t start;
off_t size;
int part_num; // Set only for information to upload
explicit mp_part(off_t set_start = 0, off_t set_size = 0, int part = 0) : start(set_start), size(set_size), part_num(part) {}
};
typedef std::vector<struct mp_part> mp_part_list_t;
inline off_t total_mp_part_list(const mp_part_list_t& mplist)
{
off_t size = 0;
for(mp_part_list_t::const_iterator iter = mplist.begin(); iter != mplist.end(); ++iter){
size += iter->size;
}
return size;
}
//
// Rename directory struct
//
struct mvnode
{
mvnode(std::string old_path, std::string new_path, bool is_dir, bool is_normdir)
: old_path(std::move(old_path))
, new_path(std::move(new_path))
, is_dir(is_dir)
, is_normdir(is_normdir)
{}
std::string old_path;
std::string new_path;
bool is_dir;
bool is_normdir;
};
//-------------------------------------------------------------------
// mimes_t
//-------------------------------------------------------------------
struct case_insensitive_compare_func
{
bool operator()(const std::string& a, const std::string& b) const {
return strcasecmp(a.c_str(), b.c_str()) < 0;
}
};
typedef std::map<std::string, std::string, case_insensitive_compare_func> mimes_t;
//-------------------------------------------------------------------
// Typedefs specialized for use
//-------------------------------------------------------------------
typedef std::vector<std::string> readline_t;
typedef std::map<std::string, std::string> kvmap_t;
typedef std::map<std::string, kvmap_t> bucketkvmap_t;
#endif // S3FS_TYPES_H_
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

View File

@ -17,14 +17,45 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
######################################################################
TESTS=small-integration-test.sh
EXTRA_DIST = \
integration-test-common.sh \
require-root.sh \
small-integration-test.sh \
mergedir.sh \
sample_delcache.sh \
sample_ahbe.conf
integration-test-common.sh \
small-integration-test.sh \
mergedir.sh \
sample_delcache.sh \
sample_ahbe.conf
testdir = test
noinst_PROGRAMS = \
junk_data \
write_multiblock \
mknod_test \
truncate_read_file \
cr_filename
junk_data_SOURCES = junk_data.cc
write_multiblock_SOURCES = write_multiblock.cc
mknod_test_SOURCES = mknod_test.cc
truncate_read_file_SOURCES = truncate_read_file.cc
cr_filename_SOURCES = cr_filename.cc
clang-tidy:
clang-tidy \
$(junk_data_SOURCES) \
$(write_multiblock_SOURCES) \
$(mknod_test_SOURCES) \
$(truncate_read_file_SOURCES) \
$(cr_filename_SOURCES) \
-- $(DEPS_CFLAGS) $(CPPFLAGS)
#
# Local variables:
# tab-width: 4
# c-basic-offset: 4
# End:
# vim600: expandtab sw=4 ts=4 fdm=marker
# vim<600: expandtab sw=4 ts=4
#

View File

@ -0,0 +1,2 @@
com.bouncestorage.chaoshttpproxy.http_503=1
com.bouncestorage.chaoshttpproxy.success=9

63
test/compile_all_targets.sh Executable file
View File

@ -0,0 +1,63 @@
#!/bin/bash
#
# s3fs - FUSE-based file system backed by Amazon S3
#
# Copyright 2007-2008 Randy Rizun <rrizun@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
set -o errexit
set -o nounset
set -o pipefail
COMMON_FLAGS='-O -Wall -Werror'
make clean
CXXFLAGS="$COMMON_FLAGS" ./configure --with-gnutls
make --jobs "$(nproc)"
make clean
CXXFLAGS="$COMMON_FLAGS" ./configure --with-gnutls --with-nettle
make --jobs "$(nproc)"
make clean
CXXFLAGS="$COMMON_FLAGS" ./configure --with-nss
make --jobs "$(nproc)"
make clean
CXXFLAGS="$COMMON_FLAGS" ./configure --with-openssl
make --jobs "$(nproc)"
make clean
CXXFLAGS="$COMMON_FLAGS -std=c++23" ./configure
make --jobs "$(nproc)"
make clean
CXXFLAGS="$COMMON_FLAGS -m32" ./configure
make --jobs "$(nproc)"
make clean
CXX=clang++ CXXFLAGS="$COMMON_FLAGS -Wshorten-64-to-32" ./configure
make --jobs "$(nproc)"
#
# Local variables:
# tab-width: 4
# c-basic-offset: 4
# End:
# vim600: expandtab sw=4 ts=4 fdm=marker
# vim<600: expandtab sw=4 ts=4
#

77
test/cr_filename.cc Normal file
View File

@ -0,0 +1,77 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2021 Andrew Gaul <andrew@gaul.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
// [NOTE]
// This is a program used for file size inspection.
// File size checking should be done by the caller of this program.
// This program truncates the file and reads the file in another process
// between truncate and flush(close file).
//
int main(int argc, const char *argv[])
{
if(argc != 2){
fprintf(stderr, "[ERROR] Wrong paraemters\n");
fprintf(stdout, "[Usage] cr_filename <base file path>\n");
exit(EXIT_FAILURE);
}
int fd;
char filepath[4096];
snprintf(filepath, sizeof(filepath), "%s\r", argv[1]);
filepath[sizeof(filepath) - 1] = '\0'; // for safety
// create empty file
if(-1 == (fd = open(filepath, O_CREAT|O_RDWR, 0644))){
fprintf(stderr, "[ERROR] Could not open file(%s)\n", filepath);
exit(EXIT_FAILURE);
}
close(fd);
// stat
struct stat buf;
if(0 != stat(filepath, &buf)){
fprintf(stderr, "[ERROR] Could not get stat for file(%s)\n", filepath);
exit(EXIT_FAILURE);
}
// remove file
if(0 != unlink(filepath)){
fprintf(stderr, "[ERROR] Could not remove file(%s)\n", filepath);
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

View File

@ -28,25 +28,25 @@ func_usage()
echo ""
}
PRGNAME=`basename $0`
SCRIPTDIR=`dirname $0`
S3FSDIR=`cd ${SCRIPTDIR}/..; pwd`
TOPDIR=`cd ${S3FSDIR}/test; pwd`
PRGNAME=$(basename "$0")
SCRIPTDIR=$(dirname "$0")
S3FSDIR=$(cd "${SCRIPTDIR}"/.. || exit 1; pwd)
TOPDIR=$(cd "${S3FSDIR}"/test || exit 1; pwd)
SUITELOG="${TOPDIR}/test-suite.log"
TMP_LINENO_FILE="/tmp/.lineno.tmp"
while [ $# -ne 0 ]; do
if [ "X$1" = "X" ]; then
break
elif [ "X$1" = "X-h" -o "X$1" = "X-H" -o "X$1" = "X--help" -o "X$1" = "X--HELP" ]; then
func_usage ${PRGNAME}
elif [ "X$1" = "X-h" ] || [ "X$1" = "X-H" ] || [ "X$1" = "X--help" ] || [ "X$1" = "X--HELP" ]; then
func_usage "${PRGNAME}"
exit 0
else
SUITELOG=$1
fi
shift
done
if [ ! -f ${SUITELOG} ]; then
if [ ! -f "${SUITELOG}" ]; then
echo "[ERROR] not found ${SUITELOG} log file."
exit 1
fi
@ -59,75 +59,77 @@ fi
# 2 : passed line of end of one small test(specified in test-utils.sh)
# 3 : failed line of end of one small test(specified in test-utils.sh)
#
grep -n -e 'test_.*: ".*"' -o -e 'test_.* passed' -o -e 'test_.* failed' ${SUITELOG} 2>/dev/null | sed 's/:test_.*: ".*"/ 1/g' | sed 's/:test_.* passed/ 2/g' | sed 's/:test_.* failed/ 3/g' > ${TMP_LINENO_FILE}
grep -n -e 'test_.*: ".*"' -o -e 'test_.* passed' -o -e 'test_.* failed' "${SUITELOG}" 2>/dev/null | sed 's/:test_.*: ".*"/ 1/g' | sed 's/:test_.* passed/ 2/g' | sed 's/:test_.* failed/ 3/g' > "${TMP_LINENO_FILE}"
#
# Loop for printing result
#
prev_line_type=0
prev_line_number=1
while read line; do
while read -r line; do
# line is "<line number> <line type>"
number_type=($line)
#
# shellcheck disable=SC2206
number_type=(${line})
head_line_cnt=`expr ${number_type[0]} - 1`
tail_line_cnt=`expr ${number_type[0]} - ${prev_line_number}`
head_line_cnt=$((number_type[0] - 1))
tail_line_cnt=$((number_type[0] - prev_line_number))
if [ ${number_type[1]} -eq 2 ]; then
if [ "${number_type[1]}" -eq 2 ]; then
echo ""
fi
if [ ${prev_line_type} -eq 1 ]; then
if [ ${number_type[1]} -eq 2 ]; then
if [ "${prev_line_type}" -eq 1 ]; then
if [ "${number_type[1]}" -eq 2 ]; then
# if passed, cut s3fs information messages
head -${head_line_cnt} ${SUITELOG} | tail -${tail_line_cnt} | grep -v -e '[0-9]\+\%' | grep -v -e '^s3fs: ' -a -e '\[INF\]'
elif [ ${number_type[1]} -eq 3 ]; then
head "-${head_line_cnt}" "${SUITELOG}" | tail "-${tail_line_cnt}" | grep -v -e '[0-9]\+%' | grep -v -e '^s3fs: ' -a -e '\[INF\]'
elif [ "${number_type[1]}" -eq 3 ]; then
# if failed, print all
head -${head_line_cnt} ${SUITELOG} | tail -${tail_line_cnt} | grep -v -e '[0-9]\+\%'
head "-${head_line_cnt}" "${SUITELOG}" | tail "-${tail_line_cnt}" | grep -v -e '[0-9]\+%'
else
# there is start keyword but not end keyword, so print all
head -${head_line_cnt} ${SUITELOG} | tail -${tail_line_cnt} | grep -v -e '[0-9]\+\%'
head "-${head_line_cnt}" "${SUITELOG}" | tail "-${tail_line_cnt}" | grep -v -e '[0-9]\+%'
fi
elif [ ${prev_line_type} -eq 2 -o ${prev_line_type} -eq 3 ]; then
if [ ${number_type[1]} -eq 2 -o ${number_type[1]} -eq 3 ]; then
elif [ "${prev_line_type}" -eq 2 ] || [ "${prev_line_type}" -eq 3 ]; then
if [ "${number_type[1]}" -eq 2 ] || [ "${number_type[1]}" -eq 3 ]; then
# previous is end of chmpx, but this type is end of chmpx without start keyword. then print all
head -${head_line_cnt} ${SUITELOG} | tail -${tail_line_cnt} | grep -v -e '[0-9]\+\%'
head "-${head_line_cnt}" "${SUITELOG}" | tail "-${tail_line_cnt}" | grep -v -e '[0-9]\+%'
else
# this area is not from start to end, cut s3fs information messages
head -${head_line_cnt} ${SUITELOG} | tail -${tail_line_cnt} | grep -v -e '[0-9]\+\%' | grep -v -e '^s3fs: ' -a -e '\[INF\]'
head "-${head_line_cnt}" "${SUITELOG}" | tail "-${tail_line_cnt}" | grep -v -e '[0-9]\+%' | grep -v -e '^s3fs: ' -a -e '\[INF\]'
fi
else
if [ ${number_type[1]} -eq 2 -o ${number_type[1]} -eq 3 ]; then
if [ "${number_type[1]}" -eq 2 ] || [ "${number_type[1]}" -eq 3 ]; then
# previous is normal, but this type is end of chmpx without start keyword. then print all
head -${head_line_cnt} ${SUITELOG} | tail -${tail_line_cnt} | grep -v -e '[0-9]\+\%'
head "-${head_line_cnt}" "${SUITELOG}" | tail "-${tail_line_cnt}" | grep -v -e '[0-9]\+%'
else
# this area is normal, cut s3fs information messages
head -${head_line_cnt} ${SUITELOG} | tail -${tail_line_cnt} | grep -v -e '[0-9]\+\%' | grep -v -e '^s3fs: ' -a -e '\[INF\]'
head "-${head_line_cnt}" "${SUITELOG}" | tail "-${tail_line_cnt}" | grep -v -e '[0-9]\+%' | grep -v -e '^s3fs: ' -a -e '\[INF\]'
fi
fi
if [ ${number_type[1]} -eq 3 ]; then
if [ "${number_type[1]}" -eq 3 ]; then
echo ""
fi
prev_line_type=${number_type[1]}
prev_line_number=${number_type[0]}
prev_line_type="${number_type[1]}"
prev_line_number="${number_type[0]}"
done < ${TMP_LINENO_FILE}
done < "${TMP_LINENO_FILE}"
#
# Print rest lines
#
file_line_cnt=`wc -l ${SUITELOG} | awk '{print $1}'`
tail_line_cnt=`expr ${file_line_cnt} - ${prev_line_number}`
file_line_cnt=$(wc -l "${SUITELOG}" | awk '{print $1}')
tail_line_cnt=$((file_line_cnt - prev_line_number))
if [ ${prev_line_type} -eq 1 ]; then
tail -${tail_line_cnt} ${SUITELOG} | grep -v -e '[0-9]\+\%'
if [ "${prev_line_type}" -eq 1 ]; then
tail "-${tail_line_cnt}" "${SUITELOG}" | grep -v -e '[0-9]\+%'
else
tail -${tail_line_cnt} ${SUITELOG} | grep -v -e '[0-9]\+\%' | grep -v -e '^s3fs: ' -a -e '\[INF\]'
tail "-${tail_line_cnt}" "${SUITELOG}" | grep -v -e '[0-9]\+%' | grep -v -e '^s3fs: ' -a -e '\[INF\]'
fi
#
# Remove temp file
#
rm -f ${TMP_LINENO_FILE}
rm -f "${TMP_LINENO_FILE}"
exit 0
@ -136,6 +138,6 @@ exit 0
# tab-width: 4
# c-basic-offset: 4
# End:
# vim600: noet sw=4 ts=4 fdm=marker
# vim<600: noet sw=4 ts=4
# vim600: expandtab sw=4 ts=4 fdm=marker
# vim<600: expandtab sw=4 ts=4
#

View File

@ -1,4 +1,23 @@
#!/bin/bash
#
# s3fs - FUSE-based file system backed by Amazon S3
#
# Copyright 2007-2008 Randy Rizun <rrizun@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
# Common code for starting an s3fs-fuse mountpoint and an S3Proxy instance
@ -12,8 +31,13 @@
# TEST_BUCKET_1=bucketname Name of bucket to use
# S3PROXY_BINARY="" Specify empty string to skip S3Proxy start
# S3_URL="https://s3.amazonaws.com" Specify Amazon AWS as the S3 provider
# S3_ENDPOINT="us-east-1" Specify region
# TMPDIR="/var/tmp" Set to use a temporary directory different
# from /var/tmp
# CHAOS_HTTP_PROXY=1 Test proxy(environment) by CHAOS HTTP PROXY
# CHAOS_HTTP_PROXY_OPT=1 Test proxy(option) by CHAOS HTTP PROXY
#
# Example of running against Amazon S3 using a bucket named "bucket:
# Example of running against Amazon S3 using a bucket named "bucket":
#
# S3FS_CREDENTIALS_FILE=keyfile TEST_BUCKET_1=bucket S3PROXY_BINARY="" S3_URL="https://s3.amazonaws.com" ./small-integration-test.sh
#
@ -44,57 +68,73 @@ set -o pipefail
S3FS=../src/s3fs
# Allow these defaulted values to be overridden
: ${S3_URL:="https://127.0.0.1:8080"}
: ${S3FS_CREDENTIALS_FILE:="passwd-s3fs"}
: ${TEST_BUCKET_1:="s3fs-integration-test"}
#
# [NOTE]
# CHAOS HTTP PROXY does not support HTTPS.
#
if [ -z "${CHAOS_HTTP_PROXY}" ] && [ -z "${CHAOS_HTTP_PROXY_OPT}" ]; then
: "${S3_URL:="https://127.0.0.1:8080"}"
else
: "${S3_URL:="http://127.0.0.1:8080"}"
fi
: "${S3_ENDPOINT:="us-east-1"}"
: "${S3FS_CREDENTIALS_FILE:="passwd-s3fs"}"
: "${TEST_BUCKET_1:="s3fs-integration-test"}"
export TEST_BUCKET_1
export S3_URL
export TEST_SCRIPT_DIR=`pwd`
export S3_ENDPOINT
TEST_SCRIPT_DIR=$(pwd)
export TEST_SCRIPT_DIR
export TEST_BUCKET_MOUNT_POINT_1=${TEST_BUCKET_1}
S3PROXY_VERSION="1.7.1"
S3PROXY_BINARY=${S3PROXY_BINARY-"s3proxy-${S3PROXY_VERSION}"}
S3PROXY_VERSION="2.0.0"
S3PROXY_BINARY="${S3PROXY_BINARY-"s3proxy-${S3PROXY_VERSION}"}"
CHAOS_HTTP_PROXY_VERSION="1.1.0"
CHAOS_HTTP_PROXY_BINARY="chaos-http-proxy-${CHAOS_HTTP_PROXY_VERSION}"
if [ ! -f "$S3FS_CREDENTIALS_FILE" ]
then
echo "Missing credentials file: $S3FS_CREDENTIALS_FILE"
echo "Missing credentials file: ${S3FS_CREDENTIALS_FILE}"
exit 1
fi
chmod 600 "$S3FS_CREDENTIALS_FILE"
chmod 600 "${S3FS_CREDENTIALS_FILE}"
if [ -z "${S3FS_PROFILE}" ]; then
export AWS_ACCESS_KEY_ID=$(cut -d: -f1 ${S3FS_CREDENTIALS_FILE})
export AWS_SECRET_ACCESS_KEY=$(cut -d: -f2 ${S3FS_CREDENTIALS_FILE})
AWS_ACCESS_KEY_ID=$(cut -d: -f1 "${S3FS_CREDENTIALS_FILE}")
export AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY=$(cut -d: -f2 "${S3FS_CREDENTIALS_FILE}")
export AWS_SECRET_ACCESS_KEY
fi
if [ ! -d $TEST_BUCKET_MOUNT_POINT_1 ]
then
mkdir -p $TEST_BUCKET_MOUNT_POINT_1
if [ ! -d "${TEST_BUCKET_MOUNT_POINT_1}" ]; then
mkdir -p "${TEST_BUCKET_MOUNT_POINT_1}"
fi
# This function execute the function parameters $1 times
# before giving up, with 1 second delays.
function retry {
set +o errexit
N=$1; shift;
status=0
for i in $(seq $N); do
local N="$1"
shift
rc=0
for _ in $(seq "${N}"); do
echo "Trying: $*"
"$@"
status=$?
if [ $status == 0 ]; then
# shellcheck disable=SC2068,SC2294
eval $@
rc=$?
if [ "${rc}" -eq 0 ]; then
break
fi
sleep 1
echo "Retrying: $*"
done
if [ $status != 0 ]; then
if [ "${rc}" -ne 0 ]; then
echo "timeout waiting for $*"
fi
set -o errexit
return $status
return "${rc}"
}
# Proxy is not started if S3PROXY_BINARY is an empty string
@ -103,40 +143,68 @@ function retry {
#
function start_s3proxy {
if [ -n "${PUBLIC}" ]; then
S3PROXY_CONFIG="s3proxy-noauth.conf"
local S3PROXY_CONFIG="s3proxy-noauth.conf"
else
S3PROXY_CONFIG="s3proxy.conf"
if [ -z "${CHAOS_HTTP_PROXY}" ] && [ -z "${CHAOS_HTTP_PROXY_OPT}" ]; then
local S3PROXY_CONFIG="s3proxy.conf"
else
local S3PROXY_CONFIG="s3proxy_http.conf"
fi
fi
if [ -n "${S3PROXY_BINARY}" ]
then
if [ ! -e "${S3PROXY_BINARY}" ]; then
wget "https://github.com/andrewgaul/s3proxy/releases/download/s3proxy-${S3PROXY_VERSION}/s3proxy" \
--quiet -O "${S3PROXY_BINARY}"
curl "https://github.com/gaul/s3proxy/releases/download/s3proxy-${S3PROXY_VERSION}/s3proxy" \
--fail --location --silent --output "${S3PROXY_BINARY}"
chmod +x "${S3PROXY_BINARY}"
fi
stdbuf -oL -eL java -jar "$S3PROXY_BINARY" --properties $S3PROXY_CONFIG &
# generate self-signed SSL certificate
#
# [NOTE]
# The PROXY test is HTTP only, so do not create CA certificates.
#
if [ -z "${CHAOS_HTTP_PROXY}" ] && [ -z "${CHAOS_HTTP_PROXY_OPT}" ]; then
S3PROXY_CACERT_FILE="/tmp/keystore.pem"
rm -f /tmp/keystore.jks "${S3PROXY_CACERT_FILE}"
printf 'password\npassword\n\n\n\n\n\n\ny' | keytool -genkey -keystore /tmp/keystore.jks -keyalg RSA -keysize 2048 -validity 365 -ext SAN=IP:127.0.0.1
echo password | keytool -exportcert -keystore /tmp/keystore.jks -rfc -file "${S3PROXY_CACERT_FILE}"
else
S3PROXY_CACERT_FILE=""
fi
"${STDBUF_BIN}" -oL -eL java -jar "${S3PROXY_BINARY}" --properties "${S3PROXY_CONFIG}" &
S3PROXY_PID=$!
# wait for S3Proxy to start
for i in $(seq 30);
do
if exec 3<>"/dev/tcp/127.0.0.1/8080";
then
exec 3<&- # Close for read
exec 3>&- # Close for write
break
fi
sleep 1
done
wait_for_port 8080
fi
if [ -n "${CHAOS_HTTP_PROXY}" ] || [ -n "${CHAOS_HTTP_PROXY_OPT}" ]; then
if [ ! -e "${CHAOS_HTTP_PROXY_BINARY}" ]; then
curl "https://github.com/bouncestorage/chaos-http-proxy/releases/download/chaos-http-proxy-${CHAOS_HTTP_PROXY_VERSION}/chaos-http-proxy" \
--fail --location --silent --output "${CHAOS_HTTP_PROXY_BINARY}"
chmod +x "${CHAOS_HTTP_PROXY_BINARY}"
fi
"${STDBUF_BIN}" -oL -eL java -jar "${CHAOS_HTTP_PROXY_BINARY}" --properties chaos-http-proxy.conf &
CHAOS_HTTP_PROXY_PID=$!
# wait for Chaos HTTP Proxy to start
wait_for_port 1080
fi
}
function stop_s3proxy {
if [ -n "${S3PROXY_PID}" ]
then
kill $S3PROXY_PID
kill "${S3PROXY_PID}"
fi
if [ -n "${CHAOS_HTTP_PROXY_PID}" ]
then
kill "${CHAOS_HTTP_PROXY_PID}"
fi
}
@ -145,11 +213,11 @@ function stop_s3proxy {
function start_s3fs {
# Public bucket if PUBLIC is set
if [ -n "${PUBLIC}" ]; then
AUTH_OPT="-o public_bucket=1"
local AUTH_OPT="-o public_bucket=1"
elif [ -n "${S3FS_PROFILE}" ]; then
AUTH_OPT="-o profile=${S3FS_PROFILE}"
local AUTH_OPT="-o profile=${S3FS_PROFILE}"
else
AUTH_OPT="-o passwd_file=${S3FS_CREDENTIALS_FILE}"
local AUTH_OPT="-o passwd_file=${S3FS_CREDENTIALS_FILE}"
fi
# If VALGRIND is set, pass it as options to valgrind.
@ -161,10 +229,50 @@ function start_s3fs {
fi
# On OSX only, we need to specify the direct_io and auto_cache flag.
if [ `uname` = "Darwin" ]; then
DIRECT_IO_OPT="-o direct_io -o auto_cache"
#
# And Turn off creation and reference of spotlight index.
# (Leaving spotlight ON will result in a lot of wasted requests,
# which will affect test execution time)
#
if [ "$(uname)" = "Darwin" ]; then
local DIRECT_IO_OPT="-o direct_io -o auto_cache"
# disable spotlight
sudo mdutil -a -i off
else
DIRECT_IO_OPT=""
local DIRECT_IO_OPT=""
fi
# Set environment variables or options for proxy.
# And the PROXY test is HTTP only and does not set CA certificates.
#
if [ -n "${CHAOS_HTTP_PROXY}" ]; then
export http_proxy="127.0.0.1:1080"
S3FS_HTTP_PROXY_OPT=""
elif [ -n "${CHAOS_HTTP_PROXY_OPT}" ]; then
S3FS_HTTP_PROXY_OPT="-o proxy=http://127.0.0.1:1080"
else
S3FS_HTTP_PROXY_OPT=""
fi
# [NOTE]
# For macos fuse-t, we need to specify the "noattrcache" option to
# disable NFS caching.
#
if [ "$(uname)" = "Darwin" ]; then
local FUSE_T_ATTRCACHE_OPT="-o noattrcache"
else
local FUSE_T_ATTRCACHE_OPT=""
fi
# [NOTE]
# On macOS we may get a VERIFY error for the self-signed certificate used by s3proxy.
# We can specify NO_CHECK_CERT=1 to avoid this.
#
if [ -n "${NO_CHECK_CERT}" ] && [ "${NO_CHECK_CERT}" -eq 1 ]; then
local NO_CHECK_CERT_OPT="-o no_check_certificate"
else
local NO_CHECK_CERT_OPT=""
fi
# Common s3fs options:
@ -173,9 +281,6 @@ function start_s3fs {
#
# use_path_request_style
# The test env doesn't have virtual hosts
# createbucket
# S3Proxy always starts with no buckets, this tests the s3fs-fuse
# automatic bucket creation path.
# $AUTH_OPT
# Will be either "-o public_bucket=1"
# or
@ -187,51 +292,60 @@ function start_s3fs {
#
# subshell with set -x to log exact invocation of s3fs-fuse
# shellcheck disable=SC2086
(
set -x
stdbuf -oL -eL \
${VALGRIND_EXEC} ${S3FS} \
$TEST_BUCKET_1 \
$TEST_BUCKET_MOUNT_POINT_1 \
CURL_CA_BUNDLE="${S3PROXY_CACERT_FILE}" \
${STDBUF_BIN} -oL -eL \
${VALGRIND_EXEC} \
${S3FS} \
${TEST_BUCKET_1} \
${TEST_BUCKET_MOUNT_POINT_1} \
-o use_path_request_style \
-o url=${S3_URL} \
-o no_check_certificate \
-o ssl_verify_hostname=0 \
-o url="${S3_URL}" \
-o endpoint="${S3_ENDPOINT}" \
-o use_xattr=1 \
-o createbucket \
-o enable_unsigned_payload \
${AUTH_OPT} \
${DIRECT_IO_OPT} \
${S3FS_HTTP_PROXY_OPT} \
${NO_CHECK_CERT_OPT} \
${FUSE_T_ATTRCACHE_OPT} \
-o stat_cache_expire=1 \
-o stat_cache_interval_expire=1 \
-o dbglevel=${DBGLEVEL:=info} \
-o dbglevel="${DBGLEVEL:=info}" \
-o no_time_stamp_msg \
-o retries=3 \
-f \
"${@}" | stdbuf -oL -eL sed $SED_BUFFER_FLAG "s/^/s3fs: /" &
S3FS_PID=$!
)
"${@}" &
echo $! >&3
) 3>pid | "${STDBUF_BIN}" -oL -eL "${SED_BIN}" "${SED_BUFFER_FLAG}" "s/^/s3fs: /" &
sleep 1
S3FS_PID=$(<pid)
export S3FS_PID
rm -f pid
if [ `uname` = "Darwin" ]; then
set +o errexit
TRYCOUNT=0
while [ $TRYCOUNT -le 20 ]; do
df | grep -q $TEST_BUCKET_MOUNT_POINT_1
if [ $? -eq 0 ]; then
if [ "$(uname)" = "Darwin" ]; then
local TRYCOUNT=0
while [ "${TRYCOUNT}" -le "${RETRIES:=20}" ]; do
_DF_RESULT=$(df 2>/dev/null)
if echo "${_DF_RESULT}" | grep -q "${TEST_BUCKET_MOUNT_POINT_1}"; then
break;
fi
sleep 1
TRYCOUNT=`expr ${TRYCOUNT} + 1`
TRYCOUNT=$((TRYCOUNT + 1))
done
if [ $? -ne 0 ]; then
if [ "${TRYCOUNT}" -gt "${RETRIES}" ]; then
echo "Waited ${TRYCOUNT} seconds, but it could not be mounted."
exit 1
fi
set -o errexit
else
retry 20 grep -q $TEST_BUCKET_MOUNT_POINT_1 /proc/mounts || exit 1
retry "${RETRIES:=20}" grep -q "${TEST_BUCKET_MOUNT_POINT_1}" /proc/mounts || exit 1
fi
# Quick way to start system up for manual testing with options under test
if [[ -n ${INTERACT} ]]; then
echo "Mountpoint $TEST_BUCKET_MOUNT_POINT_1 is ready"
if [[ -n "${INTERACT}" ]]; then
echo "Mountpoint ${TEST_BUCKET_MOUNT_POINT_1} is ready"
echo "control-C to quit"
sleep infinity
exit 0
@ -240,13 +354,13 @@ function start_s3fs {
function stop_s3fs {
# Retry in case file system is in use
if [ `uname` = "Darwin" ]; then
if df | grep -q $TEST_BUCKET_MOUNT_POINT_1; then
retry 10 df | grep -q $TEST_BUCKET_MOUNT_POINT_1 && umount $TEST_BUCKET_MOUNT_POINT_1
if [ "$(uname)" = "Darwin" ]; then
if df | grep -q "${TEST_BUCKET_MOUNT_POINT_1}"; then
retry 10 df "|" grep -q "${TEST_BUCKET_MOUNT_POINT_1}" "&&" umount "${TEST_BUCKET_MOUNT_POINT_1}"
fi
else
if grep -q $TEST_BUCKET_MOUNT_POINT_1 /proc/mounts; then
retry 10 grep -q $TEST_BUCKET_MOUNT_POINT_1 /proc/mounts && fusermount -u $TEST_BUCKET_MOUNT_POINT_1
if grep -q "${TEST_BUCKET_MOUNT_POINT_1}" /proc/mounts; then
retry 10 grep -q "${TEST_BUCKET_MOUNT_POINT_1}" /proc/mounts "&&" fusermount -u "${TEST_BUCKET_MOUNT_POINT_1}"
fi
fi
}
@ -257,3 +371,12 @@ function common_exit_handler {
stop_s3proxy
}
trap common_exit_handler EXIT
#
# Local variables:
# tab-width: 4
# c-basic-offset: 4
# End:
# vim600: expandtab sw=4 ts=4 fdm=marker
# vim<600: expandtab sw=4 ts=4
#

File diff suppressed because it is too large Load Diff

50
test/junk_data.cc Normal file
View File

@ -0,0 +1,50 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2021 Andrew Gaul <andrew@gaul.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// Generate junk data at high speed. An alternative to dd if=/dev/urandom.
#include <cstdint>
#include <cstdio>
#include <cstdlib>
int main(int argc, const char *argv[])
{
if (argc != 2) {
return 1;
}
uint64_t count = strtoull(argv[1], nullptr, 10);
char buf[128 * 1024];
for (uint64_t i = 0; i < count; i += sizeof(buf)) {
for (uint64_t j = 0; j < sizeof(buf) / sizeof(i); ++j) {
*(reinterpret_cast<uint64_t *>(buf) + j) = i / sizeof(i) + j;
}
fwrite(buf, 1, sizeof(buf) > count - i ? count - i : sizeof(buf), stdout);
}
return 0;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

Binary file not shown.

View File

@ -1,4 +1,24 @@
#!/bin/sh
#
# s3fs - FUSE-based file system backed by Amazon S3
#
# Copyright 2007-2008 Randy Rizun <rrizun@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
# Merge old directory object to new.
# For s3fs after v1.64
@ -20,25 +40,25 @@ UsageFunction()
}
### Check parameters
WHOAMI=`whoami`
OWNNAME=`basename $0`
WHOAMI=$(whoami)
OWNNAME=$(basename "$0")
AUTOYES="no"
ALLYES="no"
DIRPARAM=""
while [ "$1" != "" ]; do
if [ "X$1" = "X-help" -o "X$1" = "X-h" -o "X$1" = "X-H" ]; then
UsageFunction $OWNNAME
if [ "X$1" = "X-help" ] || [ "X$1" = "X-h" ] || [ "X$1" = "X-H" ]; then
UsageFunction "${OWNNAME}"
exit 0
elif [ "X$1" = "X-y" -o "X$1" = "X-Y" ]; then
elif [ "X$1" = "X-y" ] || [ "X$1" = "X-Y" ]; then
AUTOYES="yes"
elif [ "X$1" = "X-all" -o "X$1" = "X-ALL" ]; then
elif [ "X$1" = "X-all" ] || [ "X$1" = "X-ALL" ]; then
ALLYES="yes"
else
if [ "X$DIRPARAM" != "X" ]; then
echo "*** Input error."
echo ""
UsageFunction $OWNNAME
UsageFunction "${OWNNAME}"
exit 1
fi
DIRPARAM=$1
@ -48,7 +68,7 @@ done
if [ "X$DIRPARAM" = "X" ]; then
echo "*** Input error."
echo ""
UsageFunction $OWNNAME
UsageFunction "${OWNNAME}"
exit 1
fi
@ -68,18 +88,17 @@ echo "Please execute this program by responsibility of your own."
echo "#############################################################################"
echo ""
DATE=`date +'%Y%m%d-%H%M%S'`
LOGFILE="$OWNNAME-$DATE.log"
DATE=$(date +'%Y%m%d-%H%M%S')
LOGFILE="${OWNNAME}-${DATE}.log"
echo -n "Start to merge directory object... [$DIRPARAM]"
echo "# Start to merge directory object... [$DIRPARAM]" >> $LOGFILE
echo -n "# DATE : " >> $LOGFILE
echo `date` >> $LOGFILE
echo -n "# BASEDIR : " >> $LOGFILE
echo `pwd` >> $LOGFILE
echo -n "# TARGET PATH : " >> $LOGFILE
echo $DIRPARAM >> $LOGFILE
echo "" >> $LOGFILE
echo "Start to merge directory object... [${DIRPARAM}]"
{
echo "# Start to merge directory object... [${DIRPARAM}]"
echo "# DATE : $(date)"
echo "# BASEDIR : $(pwd)"
echo "# TARGET PATH : ${DIRPARAM}"
echo ""
} > "${LOGFILE}"
if [ "$AUTOYES" = "yes" ]; then
echo "(no confirmation)"
@ -89,81 +108,90 @@ fi
echo ""
### Get Directory list
DIRLIST=`find $DIRPARAM -type d -print | grep -v ^\.$`
DIRLIST=$(find "${DIRPARAM}" -type d -print | grep -v ^\.$)
#
# Main loop
#
for DIR in $DIRLIST; do
### Skip "." and ".." directories
BASENAME=`basename $DIR`
if [ "$BASENAME" = "." -o "$BASENAME" = ".." ]; then
BASENAME=$(basename "${DIR}")
if [ "${BASENAME}" = "." ] || [ "${BASENAME}" = ".." ]; then
continue
fi
if [ "$ALLYES" = "no" ]; then
if [ "${ALLYES}" = "no" ]; then
### Skip "d---------" directories.
### Other clients make directory object "dir/" which don't have
### "x-amz-meta-mode" attribute.
### Then these directories is "d---------", it is target directory.
DIRPERMIT=`ls -ld --time-style=+'%Y%m%d%H%M' $DIR | awk '{print $1}'`
if [ "$DIRPERMIT" != "d---------" ]; then
# shellcheck disable=SC2012
DIRPERMIT=$(ls -ld --time-style=+'%Y%m%d%H%M' "${DIR}" | awk '{print $1}')
if [ "${DIRPERMIT}" != "d---------" ]; then
continue
fi
fi
### Confirm
ANSWER=""
if [ "$AUTOYES" = "yes" ]; then
if [ "${AUTOYES}" = "yes" ]; then
ANSWER="y"
fi
while [ "X$ANSWER" != "XY" -a "X$ANSWER" != "Xy" -a "X$ANSWER" != "XN" -a "X$ANSWER" != "Xn" ]; do
echo -n "Do you merge $DIR? (y/n): "
read ANSWER
while [ "X${ANSWER}" != "XY" ] && [ "X${ANSWER}" != "Xy" ] && [ "X${ANSWER}" != "XN" ] && [ "X${ANSWER}" != "Xn" ]; do
printf "%s" "Do you merge ${DIR} ? (y/n): "
read -r ANSWER
done
if [ "X$ANSWER" != "XY" -a "X$ANSWER" != "Xy" ]; then
if [ "X${ANSWER}" != "XY" ] && [ "X${ANSWER}" != "Xy" ]; then
continue
fi
### Do
CHOWN=`ls -ld --time-style=+'%Y%m%d%H%M' $DIR | awk '{print $3":"$4" "$7}'`
CHMOD=`ls -ld --time-style=+'%Y%m%d%H%M' $DIR | awk '{print $7}'`
TOUCH=`ls -ld --time-style=+'%Y%m%d%H%M' $DIR | awk '{print $6" "$7}'`
# shellcheck disable=SC2012
CHOWN=$(ls -ld --time-style=+'%Y%m%d%H%M' "${DIR}" | awk '{print $3":"$4" "$7}')
# shellcheck disable=SC2012
CHMOD=$(ls -ld --time-style=+'%Y%m%d%H%M' "${DIR}" | awk '{print $7}')
# shellcheck disable=SC2012
TOUCH=$(ls -ld --time-style=+'%Y%m%d%H%M' "${DIR}" | awk '{print $6" "$7}')
echo -n "*** Merge $DIR : "
echo -n " $DIR : " >> $LOGFILE
printf "%s" "*** Merge ${DIR} : "
printf "%s" " ${DIR} : " >> "${LOGFILE}"
chmod 755 $CHMOD > /dev/null 2>&1
chmod 755 "${CHMOD}" > /dev/null 2>&1
RESULT=$?
if [ $RESULT -ne 0 ]; then
if [ "${RESULT}" -ne 0 ]; then
echo "Failed(chmod)"
echo "Failed(chmod)" >> $LOGFILE
echo "Failed(chmod)" >> "${LOGFILE}"
continue
fi
chown $CHOWN > /dev/null 2>&1
chown "${CHOWN}" > /dev/null 2>&1
RESULT=$?
if [ $RESULT -ne 0 ]; then
if [ "${RESULT}" -ne 0 ]; then
echo "Failed(chown)"
echo "Failed(chown)" >> $LOGFILE
echo "Failed(chown)" >> "${LOGFILE}"
continue
fi
touch -t $TOUCH > /dev/null 2>&1
touch -t "${TOUCH}" > /dev/null 2>&1
RESULT=$?
if [ $RESULT -ne 0 ]; then
if [ "${RESULT}" -ne 0 ]; then
echo "Failed(touch)"
echo "Failed(touch)" >> $LOGFILE
echo "Failed(touch)" >> "${LOGFILE}"
continue
fi
echo "Succeed"
echo "Succeed" >> $LOGFILE
echo "Succeed" >> "${LOGFILE}"
done
echo ""
echo "" >> $LOGFILE
echo "" >> "${LOGFILE}"
echo "Finished."
echo -n "# Finished : " >> $LOGFILE
echo `date` >> $LOGFILE
echo "# Finished : $(date)" >> "${LOGFILE}"
#
# END
# Local variables:
# tab-width: 4
# c-basic-offset: 4
# End:
# vim600: expandtab sw=4 ts=4 fdm=marker
# vim<600: expandtab sw=4 ts=4
#

184
test/mknod_test.cc Normal file
View File

@ -0,0 +1,184 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2021 Andrew Gaul <andrew@gaul.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#ifndef __APPLE__
#include <sys/sysmacros.h>
#endif
//---------------------------------------------------------
// Const
//---------------------------------------------------------
static constexpr char usage_string[] = "Usage : \"mknod_test <base file path>\"";
static constexpr char str_mode_reg[] = "REGULAR";
static constexpr char str_mode_chr[] = "CHARACTER";
static constexpr char str_mode_blk[] = "BLOCK";
static constexpr char str_mode_fifo[] = "FIFO";
static constexpr char str_mode_sock[] = "SOCK";
static constexpr char str_ext_reg[] = "reg";
static constexpr char str_ext_chr[] = "chr";
static constexpr char str_ext_blk[] = "blk";
static constexpr char str_ext_fifo[] = "fifo";
static constexpr char str_ext_sock[] = "sock";
// [NOTE]
// It would be nice if PATH_MAX could be used as is, but since there are
// issues using on Linux and we also must support for macos, this simple
// test program defines a fixed value for simplicity.
//
static constexpr size_t S3FS_TEST_PATH_MAX = 255;
static constexpr size_t MAX_BASE_PATH_LENGTH = S3FS_TEST_PATH_MAX - 5;
//---------------------------------------------------------
// Test function
//---------------------------------------------------------
bool TestMknod(const char* basepath, mode_t mode)
{
if(!basepath){
fprintf(stderr, "[ERROR] Called function with wrong basepath argument.\n");
return false;
}
const char* str_mode;
dev_t dev;
char filepath[S3FS_TEST_PATH_MAX];
switch(mode){
case S_IFREG:
str_mode = str_mode_reg;
dev = 0;
snprintf(filepath, sizeof(filepath), "%s.%s", basepath, str_ext_reg);
filepath[S3FS_TEST_PATH_MAX - 1] = '\0'; // for safety
break;
case S_IFCHR:
str_mode = str_mode_chr;
dev = makedev(0, 0);
snprintf(filepath, sizeof(filepath), "%s.%s", basepath, str_ext_chr);
filepath[S3FS_TEST_PATH_MAX - 1] = '\0'; // for safety
break;
case S_IFBLK:
str_mode = str_mode_blk;
dev = makedev((unsigned int)(259), 0); // temporary value
snprintf(filepath, sizeof(filepath), "%s.%s", basepath, str_ext_blk);
filepath[S3FS_TEST_PATH_MAX - 1] = '\0'; // for safety
break;
case S_IFIFO:
str_mode = str_mode_fifo;
dev = 0;
snprintf(filepath, sizeof(filepath), "%s.%s", basepath, str_ext_fifo);
filepath[S3FS_TEST_PATH_MAX - 1] = '\0'; // for safety
break;
case S_IFSOCK:
str_mode = str_mode_sock;
dev = 0;
snprintf(filepath, sizeof(filepath), "%s.%s", basepath, str_ext_sock);
filepath[S3FS_TEST_PATH_MAX - 1] = '\0'; // for safety
break;
default:
fprintf(stderr, "[ERROR] Called function with wrong mode argument.\n");
return false;
}
//
// Create
//
if(0 != mknod(filepath, mode | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, dev)){
fprintf(stderr, "[ERROR] Could not create %s file(%s) : errno = %d\n", str_mode, filepath, errno);
return false;
}
//
// Check
//
struct stat st;
if(0 != stat(filepath, &st)){
fprintf(stderr, "[ERROR] Could not get stat from %s file(%s) : errno = %d\n", str_mode, filepath, errno);
return false;
}
if(mode != (st.st_mode & S_IFMT)){
fprintf(stderr, "[ERROR] Created %s file(%s) does not have 0%o stat\n", str_mode, filepath, mode);
return false;
}
//
// Remove
//
if(0 != unlink(filepath)){
fprintf(stderr, "[WARNING] Could not remove %s file(%s) : errno = %d\n", str_mode, filepath, mode);
}
return true;
}
//---------------------------------------------------------
// Main
//---------------------------------------------------------
int main(int argc, const char *argv[])
{
// Parse parameters
if(2 != argc){
fprintf(stderr, "[ERROR] No parameter is specified.\n");
fprintf(stderr, "%s\n", usage_string);
exit(EXIT_FAILURE);
}
if(0 == strcmp("-h", argv[1]) || 0 == strcmp("--help", argv[1])){
fprintf(stdout, "%s\n", usage_string);
exit(EXIT_SUCCESS);
}
if(MAX_BASE_PATH_LENGTH < strlen(argv[1])){
fprintf(stderr, "[ERROR] Base file path is too long, it must be less than %zu\n", MAX_BASE_PATH_LENGTH);
exit(EXIT_FAILURE);
}
// Test
//
// [NOTE]
// Privilege is required to execute S_IFBLK.
//
if(0 != geteuid()){
fprintf(stderr, "[WARNING] Skipping mknod(S_IFBLK) due to missing root privileges.\n");
}
if(!TestMknod(argv[1], S_IFREG) ||
!TestMknod(argv[1], S_IFCHR) ||
!TestMknod(argv[1], S_IFIFO) ||
!TestMknod(argv[1], S_IFSOCK) ||
(0 == geteuid() && !TestMknod(argv[1], S_IFBLK)))
{
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: expandtab sw=4 ts=4 fdm=marker
* vim<600: expandtab sw=4 ts=4
*/

View File

@ -1,7 +0,0 @@
#!/bin/bash -e
if [[ $EUID -ne 0 ]]
then
echo "This test script must be run as root" 1>&2
exit 1
fi

View File

@ -0,0 +1,73 @@
#!/bin/bash
#
# s3fs - FUSE-based file system backed by Amazon S3
#
# Copyright 2007-2008 Randy Rizun <rrizun@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
set -o errexit
set -o nounset
set -o pipefail
# Disable preprocessor warnings from _FORTIFY_SOURCE and -O0
COMMON_FLAGS="-g -O0 -Wno-cpp"
# run tests with libstc++ debug mode, https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html
make clean
./configure CXXFLAGS="$COMMON_FLAGS -D_GLIBCXX_DEBUG"
make --jobs="$(nproc)"
ALL_TESTS=1 make check -C test/
# run tests under AddressSanitizer, https://clang.llvm.org/docs/AddressSanitizer.html
make clean
./configure CXX=clang++ CXXFLAGS="$COMMON_FLAGS -fsanitize=address -fsanitize-address-use-after-scope"
make --jobs="$(nproc)"
ALL_TESTS=1 ASAN_OPTIONS='detect_leaks=1,detect_stack_use_after_return=1' make check -C test/
# run tests under MemorySanitizer, https://clang.llvm.org/docs/MemorySanitizer.html
# TODO: this requires a custom libc++
#make clean
#./configure CXX=clang++ CXXFLAGS="$COMMON_FLAGS -fsanitize=memory"
#make --jobs="$(nproc)"
#ALL_TESTS=1 make check -C test/
# run tests under ThreadSanitizer, https://clang.llvm.org/docs/ThreadSanitizer.html
make clean
./configure CXX=clang++ CXXFLAGS="$COMMON_FLAGS -fsanitize=thread"
make --jobs="$(nproc)"
ALL_TESTS=1 TSAN_OPTIONS='halt_on_error=1' make check -C test/
# run tests under UndefinedBehaviorSanitizer, https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
make clean
./configure CXX=clang++ CXXFLAGS="$COMMON_FLAGS -fsanitize=undefined,implicit-conversion,local-bounds,unsigned-integer-overflow"
make --jobs="$(nproc)"
ALL_TESTS=1 make check -C test/
# run tests with Valgrind
make clean
./configure CXXFLAGS="$COMMON_FLAGS"
make --jobs="$(nproc)"
ALL_TESTS=1 RETRIES=100 VALGRIND="--leak-check=full --error-exitcode=1" S3_URL=http://127.0.0.1:8081 make check -C test/
#
# Local variables:
# tab-width: 4
# c-basic-offset: 4
# End:
# vim600: expandtab sw=4 ts=4 fdm=marker
# vim<600: expandtab sw=4 ts=4
#

View File

@ -1,8 +1,9 @@
s3proxy.endpoint=http://127.0.0.1:8081
s3proxy.secure-endpoint=https://127.0.0.1:8080
s3proxy.authorization=aws-v2-or-v4
s3proxy.identity=local-identity
s3proxy.credential=local-credential
s3proxy.keystore-path=keystore.jks
s3proxy.keystore-path=/tmp/keystore.jks
s3proxy.keystore-password=password
jclouds.provider=transient

8
test/s3proxy_http.conf Normal file
View File

@ -0,0 +1,8 @@
s3proxy.endpoint=http://127.0.0.1:8080
s3proxy.authorization=aws-v2-or-v4
s3proxy.identity=local-identity
s3proxy.credential=local-credential
jclouds.provider=transient
jclouds.identity=remote-identity
jclouds.credential=remote-credential

View File

@ -1,4 +1,24 @@
#!/bin/sh
#
# s3fs - FUSE-based file system backed by Amazon S3
#
# Copyright 2007-2008 Randy Rizun <rrizun@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
# This is unsupport sample deleting cache files script.
# So s3fs's local cache files(stats and objects) grow up,
@ -12,49 +32,49 @@
func_usage()
{
echo ""
echo "Usage: $1 <bucket name> <cache path> <limit size> [-silent]"
echo " $1 -h"
echo "Sample: $1 mybucket /tmp/s3fs/cache 1073741824"
echo ""
echo " bucket name = bucket name which specified s3fs option"
echo " cache path = cache directory path which specified by"
echo " use_cache s3fs option."
echo " limit size = limit for total cache files size."
echo " specify by BYTE"
echo " -silent = silent mode"
echo ""
echo ""
echo "Usage: $1 <bucket name> <cache path> <limit size> [-silent]"
echo " $1 -h"
echo "Sample: $1 mybucket /tmp/s3fs/cache 1073741824"
echo ""
echo " bucket name = bucket name which specified s3fs option"
echo " cache path = cache directory path which specified by"
echo " use_cache s3fs option."
echo " limit size = limit for total cache files size."
echo " specify by BYTE"
echo " -silent = silent mode"
echo ""
}
PRGNAME=`basename $0`
PRGNAME=$(basename "$0")
if [ "X$1" = "X-h" -o "X$1" = "X-H" ]; then
func_usage $PRGNAME
exit 0
if [ "X$1" = "X-h" ] || [ "X$1" = "X-H" ]; then
func_usage "${PRGNAME}"
exit 0
fi
if [ "X$1" = "X" -o "X$2" = "X" -o "X$3" = "X" ]; then
func_usage $PRGNAME
exit 1
if [ "X$1" = "X" ] || [ "X$2" = "X" ] || [ "X$3" = "X" ]; then
func_usage "${PRGNAME}"
exit 1
fi
BUCKET=$1
BUCKET="$1"
CDIR="$2"
LIMIT=$3
LIMIT="$3"
SILENT=0
if [ "X$4" = "X-silent" ]; then
SILENT=1
SILENT=1
fi
FILES_CDIR="${CDIR}/${BUCKET}"
STATS_CDIR="${CDIR}/.${BUCKET}.stat"
CURRENT_CACHE_SIZE=`du -sb "$FILES_CDIR" | awk '{print $1}'`
CURRENT_CACHE_SIZE=$(du -sb "${FILES_CDIR}" | awk '{print $1}')
#
# Check total size
#
if [ $LIMIT -ge $CURRENT_CACHE_SIZE ]; then
if [ $SILENT -ne 1 ]; then
echo "$FILES_CDIR ($CURRENT_CACHE_SIZE) is below allowed $LIMIT"
fi
exit 0
if [ "${LIMIT}" -ge "${CURRENT_CACHE_SIZE}" ]; then
if [ $SILENT -ne 1 ]; then
echo "${FILES_CDIR} (${CURRENT_CACHE_SIZE}) is below allowed ${LIMIT}"
fi
exit 0
fi
#
@ -66,41 +86,45 @@ TMP_CFILE=""
#
# Make file list by sorted access time
#
find "$STATS_CDIR" -type f -exec stat -c "%X:%n" "{}" \; | sort | while read part
find "${STATS_CDIR}" -type f -exec stat -c "%X:%n" "{}" \; | sort | while read -r part
do
echo Looking at $part
TMP_ATIME=`echo "$part" | cut -d: -f1`
TMP_STATS="`echo "$part" | cut -d: -f2`"
TMP_CFILE=`echo "$TMP_STATS" | sed s/\.$BUCKET\.stat/$BUCKET/`
if [ `stat -c %X "$TMP_STATS"` -eq $TMP_ATIME ]; then
rm -f "$TMP_STATS" "$TMP_CFILE" > /dev/null 2>&1
if [ $? -ne 0 ]; then
if [ $SILENT -ne 1 ]; then
echo "ERROR: Could not remove files($TMP_STATS,$TMP_CFILE)"
fi
exit 1
else
if [ $SILENT -ne 1 ]; then
echo "remove file: $TMP_CFILE $TMP_STATS"
fi
echo "Looking at ${part}"
TMP_ATIME=$(echo "${part}" | cut -d: -f1)
TMP_STATS=$(echo "${part}" | cut -d: -f2-)
TMP_CFILE=$(echo "${TMP_STATS}" | sed -e "s/\\.${BUCKET}\\.stat/${BUCKET}/")
if [ "$(stat -c %X "${TMP_STATS}")" -eq "${TMP_ATIME}" ]; then
if ! rm "${TMP_STATS}" "${TMP_CFILE}" > /dev/null 2>&1; then
if [ "${SILENT}" -ne 1 ]; then
echo "ERROR: Could not remove files(${TMP_STATS},${TMP_CFILE})"
fi
exit 1
else
if [ "${SILENT}" -ne 1 ]; then
echo "remove file: ${TMP_CFILE} ${TMP_STATS}"
fi
fi
fi
fi
if [ $LIMIT -ge `du -sb "$FILES_CDIR" | awk '{print $1}'` ]; then
if [ $SILENT -ne 1 ]; then
echo "finish removing files"
if [ "${LIMIT}" -ge "$(du -sb "${FILES_CDIR}" | awk '{print $1}')" ]; then
if [ "${SILENT}" -ne 1 ]; then
echo "finish removing files"
fi
break
fi
break
fi
done
if [ $SILENT -ne 1 ]; then
TOTAL_SIZE=`du -sb "$FILES_CDIR" | awk '{print $1}'`
echo "Finish: $FILES_CDIR total size is $TOTAL_SIZE"
if [ "${SILENT}" -ne 1 ]; then
TOTAL_SIZE=$(du -sb "${FILES_CDIR}" | awk '{print $1}')
echo "Finish: ${FILES_CDIR} total size is ${TOTAL_SIZE}"
fi
exit 0
#
# End
# Local variables:
# tab-width: 4
# c-basic-offset: 4
# End:
# vim600: expandtab sw=4 ts=4 fdm=marker
# vim<600: expandtab sw=4 ts=4
#

Some files were not shown because too many files have changed in this diff Show More