diff options
Diffstat (limited to 'libs/samba/src/main/cpp/samba.cpp')
| -rw-r--r-- | libs/samba/src/main/cpp/samba.cpp | 128 |
1 files changed, 128 insertions, 0 deletions
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 |
