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