xref: /aosp_15_r20/external/aws-crt-java/src/native/directory_traversal.c (revision 3c7ae9de214676c52d19f01067dc1a404272dc11)
1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 #include "crt.h"
6 #include "java_class_ids.h"
7 #include <aws/common/file.h>
8 #include <aws/common/string.h>
9 
10 struct directory_traversal_callback_ctx {
11     JNIEnv *env;
12     jobject handler;
13 };
14 
s_on_directory_entry(const struct aws_directory_entry * entry,void * user_data)15 static bool s_on_directory_entry(const struct aws_directory_entry *entry, void *user_data) {
16 
17     struct directory_traversal_callback_ctx *ctx = user_data;
18 
19     /* this callback is synchronous, therefore no need to use aws_jni_get_thread_env() */
20     JNIEnv *env = ctx->env;
21 
22     jobject directory_entry_object = (*env)->NewObject(
23         env,
24         directory_entry_properties.directory_entry_class,
25         directory_entry_properties.directory_entry_constructor_method_id);
26     if ((*env)->ExceptionCheck(env) || directory_entry_object == NULL) {
27         return false;
28     }
29 
30     /* aws_jni_string_from_cursor() does not return NULL */
31     jstring path = aws_jni_string_from_cursor(env, &entry->path);
32     jstring relativePath = aws_jni_string_from_cursor(env, &entry->relative_path);
33 
34     (*env)->SetObjectField(env, directory_entry_object, directory_entry_properties.path_field_id, path);
35     (*env)->SetObjectField(
36         env, directory_entry_object, directory_entry_properties.relative_path_field_id, relativePath);
37     (*env)->SetBooleanField(
38         env,
39         directory_entry_object,
40         directory_entry_properties.is_directory_field_id,
41         (entry->file_type & AWS_FILE_TYPE_DIRECTORY) != 0);
42     (*env)->SetBooleanField(
43         env,
44         directory_entry_object,
45         directory_entry_properties.is_symlink_field_id,
46         (entry->file_type & AWS_FILE_TYPE_SYM_LINK) != 0);
47     (*env)->SetBooleanField(
48         env,
49         directory_entry_object,
50         directory_entry_properties.is_file_field_id,
51         (entry->file_type & AWS_FILE_TYPE_FILE) != 0);
52     (*env)->SetLongField(
53         env, directory_entry_object, directory_entry_properties.file_size_field_id, (jlong)entry->file_size);
54 
55     jboolean callback_result = (*env)->CallBooleanMethod(
56         env, ctx->handler, directory_traversal_handler_properties.on_directory_entry_method_id, directory_entry_object);
57 
58     if ((*env)->ExceptionCheck(env)) {
59         /* If an exception is thrown by the user callback, the traversal is cancelled.
60            Cancelling the iteration either via the user callback returning false or throwing, results
61            in DirectoryTraversal.traverse() throwing an exception to notify user about incomplete results. */
62         callback_result = JNI_FALSE;
63     }
64 
65     /* clean-up */
66     (*env)->DeleteLocalRef(env, directory_entry_object);
67 
68     if (path != NULL) {
69         (*env)->DeleteLocalRef(env, path);
70     }
71 
72     if (relativePath != NULL) {
73         (*env)->DeleteLocalRef(env, relativePath);
74     }
75 
76     return (bool)callback_result;
77 }
78 
Java_software_amazon_awssdk_crt_io_DirectoryTraversal_crtTraverse(JNIEnv * env,jclass jni_class,jstring path,jboolean recursive,jobject handler)79 JNIEXPORT void JNICALL Java_software_amazon_awssdk_crt_io_DirectoryTraversal_crtTraverse(
80     JNIEnv *env,
81     jclass jni_class,
82     jstring path,
83     jboolean recursive,
84     jobject handler) {
85     (void)jni_class;
86     aws_cache_jni_ids(env);
87 
88     struct aws_string *path_str = aws_jni_new_string_from_jstring(env, path);
89     if (path_str == NULL) {
90         aws_jni_throw_runtime_exception(env, "failed to get path string");
91         return;
92     }
93 
94     struct directory_traversal_callback_ctx ctx = {
95         .env = env,
96         .handler = handler,
97     };
98 
99     struct aws_allocator *allocator = aws_jni_get_allocator();
100     if (aws_directory_traverse(allocator, path_str, (bool)recursive, s_on_directory_entry, &ctx)) {
101         /* If there's already a Java exception being thrown from the callback, then we don't need to throw another */
102         if (!(*env)->ExceptionCheck(env)) {
103             aws_jni_throw_runtime_exception(env, "Directory traversal failed");
104         }
105     }
106 
107     aws_string_destroy(path_str);
108 }
109