summaryrefslogtreecommitdiff
path: root/libs/samba/src/main/cpp/samba.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/samba/src/main/cpp/samba.cpp')
-rw-r--r--libs/samba/src/main/cpp/samba.cpp128
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