diff options
| author | Joel Klinghed <the_jk@spawned.biz> | 2024-08-19 00:34:03 +0200 |
|---|---|---|
| committer | Joel Klinghed <the_jk@spawned.biz> | 2024-08-19 00:34:03 +0200 |
| commit | d372cdcea3b3a0ba4b49180695c4e6b0e2d074a5 (patch) | |
| tree | f3442d2a56afd362d172cef096c878a5e7311066 /libs/samba/src/main/cpp | |
| parent | b0d90f32974f6473552d8b1bf5387f9fc4995970 (diff) | |
Increase the samba implemetation
With the exception of openDir, largely untested.
Diffstat (limited to 'libs/samba/src/main/cpp')
| -rw-r--r-- | libs/samba/src/main/cpp/jni.cpp | 10 | ||||
| -rw-r--r-- | libs/samba/src/main/cpp/jni.hpp | 43 | ||||
| -rw-r--r-- | libs/samba/src/main/cpp/samba.cpp | 128 |
3 files changed, 173 insertions, 8 deletions
diff --git a/libs/samba/src/main/cpp/jni.cpp b/libs/samba/src/main/cpp/jni.cpp index be13df7..30f03a9 100644 --- a/libs/samba/src/main/cpp/jni.cpp +++ b/libs/samba/src/main/cpp/jni.cpp @@ -97,4 +97,14 @@ LocalRef<jstring> UTF8ToString(JNIEnv* env, const std::string& str) { return {env, env->NewStringUTF(str.c_str())}; } +LocalRef<jobjectArray> CreateArray(JNIEnv* env, const Ref<jclass>& element_class, std::vector<LocalRef<jobject>> objects) { + auto ret = LocalRef<jobjectArray>(env, env->NewObjectArray(static_cast<jsize>(objects.size()), element_class.get(), nullptr)); + ABORT_IF_NULL(env, ret); + jsize i = 0; + for (auto&& obj : objects) { + env->SetObjectArrayElement(ret.get(), i++, obj.release()); + } + return ret; +} + } // namespace jni diff --git a/libs/samba/src/main/cpp/jni.hpp b/libs/samba/src/main/cpp/jni.hpp index 90ca011..450fa71 100644 --- a/libs/samba/src/main/cpp/jni.hpp +++ b/libs/samba/src/main/cpp/jni.hpp @@ -3,6 +3,7 @@ #include <jni.h> #include <string> +#include <vector> #define ABORT_IF_NOT_OK(x) (::jni::internal::_abort_if_not_ok(__FILE__, __LINE__, (x))) #define ABORT_IF_NULL(env, x) (::jni::internal::_abort_if_null(__FILE__, __LINE__, (env), (x))) @@ -21,8 +22,8 @@ class Ref { public: constexpr Ref() : env_(nullptr), ptr_(0) {} Ref(const Ref<T>&) = delete; - Ref<T>& operator=(const Ref<T>&) = delete; + [[nodiscard]] JNIEnv* env() const { return env_; } [[nodiscard]] T get() const { return ptr_; } [[nodiscard]] T release() { auto ret = release_to_local(); @@ -44,7 +45,7 @@ class Ref { virtual T release_to_local() = 0; virtual void del() = 0; - JNIEnv* const env_; + JNIEnv* env_; T ptr_; }; @@ -54,6 +55,10 @@ class LocalRef : public Ref<T> { LocalRef(JNIEnv* env, T ptr): Ref<T>(env, ptr) {} ~LocalRef() override { free(); } + LocalRef(LocalRef<T> &&ref) noexcept : Ref<T>(ref.env_, ref.release()) {} + + LocalRef& operator=(const Ref<T>& other) = delete; + protected: T release_to_local() override { return this->ptr_; } void del() override { free(); } @@ -71,6 +76,8 @@ class ParamRef : public Ref<T> { ParamRef(JNIEnv* env, T ptr) : Ref<T>(env, ptr) {} ~ParamRef() override = default; + ParamRef& operator=(const Ref<T>& other) = delete; + protected: T release_to_local() override { if (this->ptr_) @@ -83,11 +90,18 @@ class ParamRef : public Ref<T> { template<class T> class GlobalRef : public Ref<T> { public: - GlobalRef(JNIEnv* env, T ptr) : Ref<T>(env, ptr ? env->NewGlobalRef(ptr) : 0) {} - explicit GlobalRef(const Ref<T>& other) : Ref<T>(other.env_, other ? other.env_->NewGlobalRef(other.ptr_) : 0) {} + GlobalRef(JNIEnv* env, T ptr) : Ref<T>(env, ptr ? static_cast<T>(env->NewGlobalRef(static_cast<jobject>(ptr))) : 0) {} + explicit GlobalRef(const Ref<T>& other) : Ref<T>(other.env(), other ? static_cast<T>(other.env()->NewGlobalRef(static_cast<jobject>(other.get()))) : 0) {} ~GlobalRef() override { free(); } + GlobalRef& operator=(const Ref<T>& other) { + free(); + this->env_ = other.env(); + this->ptr_ = other ? static_cast<T>(this->env_->NewGlobalRef(static_cast<jobject>(other.get()))) : 0; + return *this; + } + protected: T release_to_local() override { if (this->ptr_) { @@ -117,19 +131,32 @@ LocalRef<jclass> FindClass(JNIEnv *env, const char *name); LocalRef<jthrowable> ExceptionOccurred(JNIEnv* env); -template<typename Out, typename In> -LocalRef<Out> CallObjectMethod(JNIEnv* env, const Ref<In>& object, jmethodID method) { - return {env, static_cast<Out>(env->CallObjectMethod(object.get(), method))}; +template<typename Out, typename In, typename... Params> +LocalRef<Out> CallObjectMethod(JNIEnv* env, const Ref<In>& object, jmethodID method, Params... params) { + return {env, static_cast<Out>(env->CallObjectMethod(object.get(), method, params...))}; +} + +template<typename Out, typename... Params> +LocalRef<Out> CallStaticObjectMethod(JNIEnv* env, const Ref<jclass>& clazz, jmethodID method, Params... params) { + return {env, static_cast<Out>(env->CallStaticObjectMethod(clazz.get(), method, params...))}; } std::string StringToUTF8(JNIEnv* env, const Ref<jstring>& str); LocalRef<jstring> UTF8ToString(JNIEnv* env, const std::string& str); +LocalRef<jobjectArray> CreateArray(JNIEnv* env, const Ref<jclass>& element_class, std::vector<LocalRef<jobject>> objects); + namespace internal { template<typename T> -void _abort_if_null(const char* file, int line, JNIEnv* env, const jni::Ref<T>& ref) { +inline void _abort_if_null(const char* file, int line, JNIEnv* env, const jni::Ref<T>& ref) { + if (ref) [[likely]] return; + + _abort_with_exception(file, line, env); +} + +inline void _abort_if_null(const char* file, int line, JNIEnv* env, jmethodID ref) { if (ref) [[likely]] return; _abort_with_exception(file, line, env); diff --git a/libs/samba/src/main/cpp/samba.cpp b/libs/samba/src/main/cpp/samba.cpp index e3b689d..7b83eb4 100644 --- a/libs/samba/src/main/cpp/samba.cpp +++ b/libs/samba/src/main/cpp/samba.cpp @@ -11,6 +11,8 @@ namespace { +jni::LocalRef<jobject> CreateDirEntry(JNIEnv* env, const std::string& name, const smb2_stat_64& stat); + class Dir { public: Dir(std::shared_ptr<smb2_context> context, smb2dir* dir) : context_(std::move(context)), dir_(dir) { @@ -24,6 +26,20 @@ class Dir { Dir(const Dir&) = delete; Dir& operator=(const Dir&) = delete; + jni::LocalRef<jobjectArray> List(JNIEnv* env, const jni::Ref<jclass>& dir_entry_clazz) { + smb2_rewinddir(context_.get(), dir_); + std::vector<jni::LocalRef<jobject>> tmp; + while (auto* ent = smb2_readdir(context_.get(), dir_)) { + // Skip . and .. entries. + if (ent->name[0] == '.' && (ent->name[1] == '\0' || (ent->name[1] == '.' && ent->name[2] == '\0'))) continue; + auto obj = CreateDirEntry(env, ent->name, ent->st); + if (obj) { + tmp.push_back(std::move(obj)); + } + } + return jni::CreateArray(env, dir_entry_clazz, std::move(tmp)); + } + private: std::shared_ptr<smb2_context> context_; smb2dir* const dir_; @@ -81,6 +97,50 @@ class Context { return ptr ? std::make_unique<Dir>(context_, ptr) : nullptr; } + [[nodiscard]] jni::LocalRef<jobject> Entry(JNIEnv* env, const std::string& path) { + struct smb2_stat_64 stat; // NOLINT(*-pro-type-member-init) + auto ret = smb2_stat(context_.get(), path.c_str(), &stat); + if (ret) return {env, nullptr}; + auto slash = path.find_last_of('/'); + return CreateDirEntry(env, slash == std::string::npos ? path : path.substr(slash + 1), stat); + } + + bool MakeDir(const std::string& path) { + return smb2_mkdir(context_.get(), path.c_str()) == 0; + } + + bool RemoveDir(const std::string& path) { + return smb2_rmdir(context_.get(), path.c_str()) == 0; + } + + bool Unlink(const std::string& path) { + return smb2_unlink(context_.get(), path.c_str()) == 0; + } + + std::optional<std::string> ReadLink(const std::string& path) { + // Good to start with a fairly small size as current implementation + // of smb2_readlink uses strncpy, which pads the whole unused buffer + // with zeros. + uint32_t bufsize = 256; + std::vector<char> buf; + while (true) { + buf.resize(bufsize); + auto ret = smb2_readlink(context_.get(), path.c_str(), buf.data(), bufsize); + if (ret != 0) + return std::nullopt; + // smb2_readlink uses strncpy, so if actual path was larger than bufsize + // there will be no terminating zero. + auto it = std::find(buf.begin(), buf.end(), '\0'); + if (it != buf.end()) + return std::string(buf.begin(), it); + const auto previous = bufsize; + bufsize *= 2; + // Check for bufsize (a uint32_t) overflow. + if (bufsize <= previous) + return std::nullopt; + } + } + private: struct ContextDeleter { void operator()(smb2_context* context) { @@ -121,6 +181,29 @@ jlong nativeContextOpenDir(JNIEnv* env, jclass clazz, jlong ptr, jstring path) { return reinterpret_cast<jlong>(reinterpret_cast<Context*>(ptr)->OpenDir(jni::StringToUTF8(env, jni::ParamRef<jstring>(env, path))).release()); } +jobject nativeContextEntry(JNIEnv* env, jclass clazz, jlong ptr, jstring path) { + return reinterpret_cast<Context*>(ptr)->Entry(env, jni::StringToUTF8(env, jni::ParamRef<jstring>(env, path))).release(); +} + +jboolean nativeContextMakeDir(JNIEnv* env, jclass clazz, jlong ptr, jstring path) { + return reinterpret_cast<Context*>(ptr)->MakeDir(jni::StringToUTF8(env, jni::ParamRef<jstring>(env, path))) ? JNI_TRUE : JNI_FALSE; +} + +jboolean nativeContextRemoveDir(JNIEnv* env, jclass clazz, jlong ptr, jstring path) { + return reinterpret_cast<Context*>(ptr)->RemoveDir(jni::StringToUTF8(env, jni::ParamRef<jstring>(env, path))) ? JNI_TRUE : JNI_FALSE; +} + +jboolean nativeContextUnlink(JNIEnv* env, jclass clazz, jlong ptr, jstring path) { + return reinterpret_cast<Context*>(ptr)->Unlink(jni::StringToUTF8(env, jni::ParamRef<jstring>(env, path))) ? JNI_TRUE : JNI_FALSE; +} + +jstring nativeContextReadLink(JNIEnv* env, jclass clazz, jlong ptr, jstring path) { + auto ret = reinterpret_cast<Context*>(ptr)->ReadLink(jni::StringToUTF8(env, jni::ParamRef<jstring>(env, path))); + if (ret.has_value()) + return jni::UTF8ToString(env, ret.value()).release(); + return nullptr; +} + void nativeUrlDestroy(JNIEnv* env, jclass clazz, jlong ptr) { delete reinterpret_cast<Url*>(ptr); } @@ -133,9 +216,20 @@ void nativeDirDestroy(JNIEnv* env, jclass clazz, jlong ptr) { delete reinterpret_cast<Dir*>(ptr); } +jni::GlobalRef<jclass> g_DirEntryClass(nullptr, nullptr); + +jobjectArray nativeDirList(JNIEnv* env, jclass clazz, jlong ptr) { + return reinterpret_cast<Dir*>(ptr)->List(env, g_DirEntryClass).release(); +} + +jni::GlobalRef<jclass> g_NativeSambaClass(nullptr, nullptr); +jmethodID g_CreateDirEntry; + void RegisterSamba(JNIEnv* env) { auto clazz = jni::FindClass(env, "org/the_jk/cleversync/io/samba/NativeSamba"); ABORT_IF_NULL(env, clazz); + auto dir_entry_clazz = jni::FindClass(env, "org/the_jk/cleversync/io/samba/NativeSamba$DirEntry"); + ABORT_IF_NULL(env, dir_entry_clazz); static const JNINativeMethod methods[] = { { "nativeContextNew", "(I)J", reinterpret_cast<void*>(&nativeContextNew) }, { "nativeContextDestroy", "(J)V", reinterpret_cast<void*>(&nativeContextDestroy) }, @@ -143,14 +237,48 @@ void RegisterSamba(JNIEnv* env) { { "nativeContextConnect", "(JJLjava/lang/String;Ljava/lang/String;)Z", reinterpret_cast<void*>(&nativeContextConnect) }, { "nativeContextGetError", "(J)Ljava/lang/String;", reinterpret_cast<void*>(&nativeContextGetError) }, { "nativeContextOpenDir", "(JLjava/lang/String;)J", reinterpret_cast<void*>(&nativeContextOpenDir) }, + { "nativeContextEntry", "(JLjava/lang/String;)Lorg/the_jk/cleversync/io/samba/NativeSamba$DirEntry;", reinterpret_cast<void*>(&nativeContextEntry) }, + { "nativeContextMakeDir", "(JLjava/lang/String;)Z", reinterpret_cast<void*>(&nativeContextMakeDir) }, + { "nativeContextRemoveDir", "(JLjava/lang/String;)Z", reinterpret_cast<void*>(&nativeContextRemoveDir) }, + { "nativeContextUnlink", "(JLjava/lang/String;)Z", reinterpret_cast<void*>(&nativeContextUnlink) }, + { "nativeContextReadLink", "(JLjava/lang/String;)Ljava/lang/String;", reinterpret_cast<void*>(&nativeContextReadLink) }, { "nativeUrlDestroy", "(J)V", reinterpret_cast<void*>(&nativeUrlDestroy) }, { "nativeUrlPath", "(J)Ljava/lang/String;", reinterpret_cast<void*>(&nativeUrlPath) }, { "nativeDirDestroy", "(J)V", reinterpret_cast<void*>(&nativeDirDestroy) }, + { "nativeDirList", "(J)[Lorg/the_jk/cleversync/io/samba/NativeSamba$DirEntry;", reinterpret_cast<void*>(&nativeDirList) }, }; auto ret = env->RegisterNatives(clazz.get(), methods, sizeof(methods) / sizeof(methods[0])); ABORT_IF_NOT_OK(ret); + + g_CreateDirEntry = env->GetStaticMethodID(clazz.get(), "createDirEntry", "(Ljava/lang/String;IJJ)Lorg/the_jk/cleversync/io/samba/NativeSamba$DirEntry;"); + ABORT_IF_NULL(env, g_CreateDirEntry); + g_NativeSambaClass = clazz; + g_DirEntryClass = dir_entry_clazz; +} + +jni::LocalRef<jobject> CreateDirEntry(JNIEnv* env, const std::string& name, const smb2_stat_64& stat) { + auto j_name = jni::UTF8ToString(env, name); + // Kotlin size casts Long to ULong + auto size = static_cast<jlong>(stat.smb2_size); + auto last_modified = static_cast<jlong>(stat.smb2_mtime); + jint type; + switch (stat.smb2_type) { + case SMB2_TYPE_DIRECTORY: + type = 0; + break; + case SMB2_TYPE_FILE: + type = 1; + break; + case SMB2_TYPE_LINK: + type = 2; + break; + default: + return {env, nullptr}; + } + + return jni::CallStaticObjectMethod<jobject>(env, g_NativeSambaClass, g_CreateDirEntry, j_name.get(), type, size, last_modified); } } // namespace |
