|
1 | 1 | #include <jni.h> |
2 | 2 |
|
| 3 | +#include "exact_directory_retirement.h" |
| 4 | + |
3 | 5 | #include <cerrno> |
| 6 | +#include <cstdlib> |
4 | 7 | #include <cstring> |
5 | 8 | #include <fcntl.h> |
6 | 9 | #include <linux/fs.h> |
@@ -39,6 +42,35 @@ bool matches_identity(const struct stat& value, const char* expected) { |
39 | 42 | + std::to_string(static_cast<unsigned long long>(value.st_ino))) == expected; |
40 | 43 | } |
41 | 44 |
|
| 45 | +bool parse_identity( |
| 46 | + const char* encoded, |
| 47 | + mindwtr::attachment_file_installer::DirectoryIdentity* output) { |
| 48 | + if (encoded == nullptr || output == nullptr) return false; |
| 49 | + const std::string value(encoded); |
| 50 | + const size_t separator = value.find(':'); |
| 51 | + if ( |
| 52 | + separator == std::string::npos |
| 53 | + || separator == 0 |
| 54 | + || separator + 1 >= value.size() |
| 55 | + || value.find(':', separator + 1) != std::string::npos) { |
| 56 | + return false; |
| 57 | + } |
| 58 | + const std::string device_text = value.substr(0, separator); |
| 59 | + const std::string inode_text = value.substr(separator + 1); |
| 60 | + char* device_end = nullptr; |
| 61 | + char* inode_end = nullptr; |
| 62 | + errno = 0; |
| 63 | + const unsigned long long device = strtoull(device_text.c_str(), &device_end, 10); |
| 64 | + if (errno != 0 || device_end == nullptr || *device_end != '\0') return false; |
| 65 | + errno = 0; |
| 66 | + const unsigned long long inode = strtoull(inode_text.c_str(), &inode_end, 10); |
| 67 | + if (errno != 0 || inode_end == nullptr || *inode_end != '\0') return false; |
| 68 | + *output = { |
| 69 | + static_cast<uint64_t>(device), |
| 70 | + static_cast<uint64_t>(inode)}; |
| 71 | + return true; |
| 72 | +} |
| 73 | + |
42 | 74 | constexpr bool rename_noreplace_needs_exact_handle_fallback(int error) { |
43 | 75 | return error == ENOSYS || error == EOPNOTSUPP || error == EINVAL; |
44 | 76 | } |
@@ -159,3 +191,111 @@ Java_tech_dongdongbh_mindwtr_attachmentfileinstaller_ExactAttachmentPublisherNat |
159 | 191 | } |
160 | 192 | return JNI_TRUE; |
161 | 193 | } |
| 194 | + |
| 195 | +extern "C" JNIEXPORT jint JNICALL |
| 196 | +Java_tech_dongdongbh_mindwtr_attachmentfileinstaller_ExactAttachmentPublisherNative_retireEmptyDirectoryIfIdentity( |
| 197 | + JNIEnv* env, |
| 198 | + jobject, |
| 199 | + jstring parent_directory_path, |
| 200 | + jstring directory_name, |
| 201 | + jstring expected_directory_identity, |
| 202 | + jstring expected_parent_identity) { |
| 203 | + ScopedUtfChars parent_path(env, parent_directory_path); |
| 204 | + ScopedUtfChars directory_leaf(env, directory_name); |
| 205 | + ScopedUtfChars expected_directory(env, expected_directory_identity); |
| 206 | + ScopedUtfChars expected_parent(env, expected_parent_identity); |
| 207 | + if (parent_path.get() == nullptr || directory_leaf.get() == nullptr |
| 208 | + || expected_directory.get() == nullptr || expected_parent.get() == nullptr) { |
| 209 | + return static_cast<jint>( |
| 210 | + mindwtr::attachment_file_installer::DirectoryRetirementResult::kIoError); |
| 211 | + } |
| 212 | + const std::string directory_leaf_value(directory_leaf.get()); |
| 213 | + if (directory_leaf_value.empty() || directory_leaf_value == "." || directory_leaf_value == ".." |
| 214 | + || directory_leaf_value.find('/') != std::string::npos) { |
| 215 | + throw_io_exception(env, "Attachment publication directory name is invalid"); |
| 216 | + return static_cast<jint>( |
| 217 | + mindwtr::attachment_file_installer::DirectoryRetirementResult::kIoError); |
| 218 | + } |
| 219 | + mindwtr::attachment_file_installer::DirectoryIdentity directory_identity{}; |
| 220 | + mindwtr::attachment_file_installer::DirectoryIdentity parent_identity{}; |
| 221 | + if ( |
| 222 | + !parse_identity(expected_directory.get(), &directory_identity) |
| 223 | + || !parse_identity(expected_parent.get(), &parent_identity)) { |
| 224 | + throw_io_exception(env, "Attachment publication directory identity is invalid"); |
| 225 | + return static_cast<jint>( |
| 226 | + mindwtr::attachment_file_installer::DirectoryRetirementResult::kIoError); |
| 227 | + } |
| 228 | + |
| 229 | + const int parent_fd = open( |
| 230 | + parent_path.get(), |
| 231 | + O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); |
| 232 | + if (parent_fd < 0) { |
| 233 | + throw_io_exception( |
| 234 | + env, |
| 235 | + std::string("Could not retain attachment publication parent: ") + strerror(errno)); |
| 236 | + return static_cast<jint>( |
| 237 | + mindwtr::attachment_file_installer::DirectoryRetirementResult::kIoError); |
| 238 | + } |
| 239 | + int error_number = 0; |
| 240 | + const auto result = mindwtr::attachment_file_installer::retire_empty_directory_if_identity( |
| 241 | + parent_fd, |
| 242 | + directory_leaf.get(), |
| 243 | + directory_identity, |
| 244 | + parent_identity, |
| 245 | + nullptr, |
| 246 | + nullptr, |
| 247 | + &error_number); |
| 248 | + close(parent_fd); |
| 249 | + if (result == mindwtr::attachment_file_installer::DirectoryRetirementResult::kIoError) { |
| 250 | + throw_io_exception( |
| 251 | + env, |
| 252 | + std::string("Could not retire private attachment publication directory: ") |
| 253 | + + strerror(error_number)); |
| 254 | + } |
| 255 | + return static_cast<jint>(result); |
| 256 | +} |
| 257 | + |
| 258 | +extern "C" JNIEXPORT jint JNICALL |
| 259 | +Java_tech_dongdongbh_mindwtr_attachmentfileinstaller_ExactAttachmentPublisherNative_retireReservedPrivateStage( |
| 260 | + JNIEnv* env, |
| 261 | + jobject, |
| 262 | + jstring parent_directory_path, |
| 263 | + jstring directory_name) { |
| 264 | + ScopedUtfChars parent_path(env, parent_directory_path); |
| 265 | + ScopedUtfChars directory_leaf(env, directory_name); |
| 266 | + if (parent_path.get() == nullptr || directory_leaf.get() == nullptr) { |
| 267 | + return static_cast<jint>( |
| 268 | + mindwtr::attachment_file_installer::DirectoryRetirementResult::kIoError); |
| 269 | + } |
| 270 | + const std::string directory_leaf_value(directory_leaf.get()); |
| 271 | + if (directory_leaf_value.empty() || directory_leaf_value == "." || directory_leaf_value == ".." |
| 272 | + || directory_leaf_value.find('/') != std::string::npos) { |
| 273 | + throw_io_exception(env, "Attachment publication directory name is invalid"); |
| 274 | + return static_cast<jint>( |
| 275 | + mindwtr::attachment_file_installer::DirectoryRetirementResult::kIoError); |
| 276 | + } |
| 277 | + |
| 278 | + const int parent_fd = open( |
| 279 | + parent_path.get(), |
| 280 | + O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); |
| 281 | + if (parent_fd < 0) { |
| 282 | + throw_io_exception( |
| 283 | + env, |
| 284 | + std::string("Could not retain reserved attachment publication parent: ") + strerror(errno)); |
| 285 | + return static_cast<jint>( |
| 286 | + mindwtr::attachment_file_installer::DirectoryRetirementResult::kIoError); |
| 287 | + } |
| 288 | + int error_number = 0; |
| 289 | + const auto result = mindwtr::attachment_file_installer::retire_reserved_private_stage( |
| 290 | + parent_fd, |
| 291 | + directory_leaf.get(), |
| 292 | + &error_number); |
| 293 | + close(parent_fd); |
| 294 | + if (result == mindwtr::attachment_file_installer::DirectoryRetirementResult::kIoError) { |
| 295 | + throw_io_exception( |
| 296 | + env, |
| 297 | + std::string("Could not retire reserved private attachment stage: ") |
| 298 | + + strerror(error_number)); |
| 299 | + } |
| 300 | + return static_cast<jint>(result); |
| 301 | +} |
0 commit comments