1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include "libzfs_jni_dataset.h" 30 #include "libzfs_jni_property.h" 31 #include <strings.h> 32 33 #define REGEX_ZFS_NAME "^((([^/]*)(/.+)?)[/@])?([^/]+)/*" 34 #define REGEX_ZFS_NAME_NGROUPS 6 35 #define REGEX_ZFS_NAME_POOL_GROUP 3 36 #define REGEX_ZFS_NAME_PARENT_GROUP 2 37 #define REGEX_ZFS_NAME_BASE_GROUP 5 38 39 /* 40 * Types 41 */ 42 43 typedef struct DatasetBean { 44 zjni_Object_t super; 45 46 jmethodID method_setPoolName; 47 jmethodID method_setParentName; 48 jmethodID method_setBaseName; 49 jmethodID method_setProperties; 50 jmethodID method_addProperty; 51 } DatasetBean_t; 52 53 typedef struct FileSystemBean { 54 DatasetBean_t super; 55 } FileSystemBean_t; 56 57 typedef struct PoolBean { 58 FileSystemBean_t super; 59 } PoolBean_t; 60 61 typedef struct VolumeBean { 62 DatasetBean_t super; 63 } VolumeBean_t; 64 65 typedef struct SnapshotBean { 66 DatasetBean_t super; 67 } SnapshotBean_t; 68 69 typedef struct FileSystemSnapshotBean { 70 DatasetBean_t super; 71 } FileSystemSnapshotBean_t; 72 73 typedef struct VolumeSnapshotBean { 74 DatasetBean_t super; 75 } VolumeSnapshotBean_t; 76 77 /* 78 * Function prototypes 79 */ 80 81 static void new_DatasetBean(JNIEnv *, DatasetBean_t *); 82 static void new_PoolBean(JNIEnv *, PoolBean_t *); 83 static void new_FileSystemBean(JNIEnv *, FileSystemBean_t *); 84 static void new_VolumeBean(JNIEnv *, VolumeBean_t *); 85 static void new_SnapshotBean(JNIEnv *, SnapshotBean_t *); 86 static void new_FileSystemSnapshotBean(JNIEnv *, FileSystemSnapshotBean_t *); 87 static void new_VolumeSnapshotBean(JNIEnv *, VolumeSnapshotBean_t *); 88 static int populate_DatasetBean(JNIEnv *, zfs_handle_t *, DatasetBean_t *); 89 static int populate_PoolBean(JNIEnv *, zfs_handle_t *, PoolBean_t *); 90 static int populate_FileSystemBean( 91 JNIEnv *, zfs_handle_t *, FileSystemBean_t *); 92 static int populate_VolumeBean( 93 JNIEnv *, zfs_handle_t *, VolumeBean_t *); 94 static int populate_SnapshotBean(JNIEnv *, zfs_handle_t *, SnapshotBean_t *); 95 static int populate_FileSystemSnapshotBean( 96 JNIEnv *, zfs_handle_t *, FileSystemSnapshotBean_t *); 97 static int populate_VolumeSnapshotBean( 98 JNIEnv *, zfs_handle_t *, VolumeSnapshotBean_t *); 99 static jobject create_PoolBean(JNIEnv *, zfs_handle_t *); 100 static jobject create_FileSystemBean(JNIEnv *, zfs_handle_t *); 101 static jobject create_VolumeBean(JNIEnv *, zfs_handle_t *); 102 static jobject create_FileSystemSnapshotBean(JNIEnv *, zfs_handle_t *); 103 static jobject create_VolumeSnapshotBean(JNIEnv *, zfs_handle_t *); 104 static jobject create_DatasetBean(JNIEnv *, zfs_handle_t *); 105 static int is_fs_snapshot(zfs_handle_t *); 106 static int is_pool(zfs_handle_t *); 107 static zfs_handle_t *open_device(JNIEnv *, jstring, zfs_type_t); 108 109 /* 110 * Static functions 111 */ 112 113 /* Create a DatasetBean */ 114 static void 115 new_DatasetBean(JNIEnv *env, DatasetBean_t *bean) 116 { 117 zjni_Object_t *object = (zjni_Object_t *)bean; 118 119 if (object->object == NULL) { 120 object->class = 121 (*env)->FindClass(env, ZFSJNI_PACKAGE_DATA "DatasetBean"); 122 123 object->constructor = 124 (*env)->GetMethodID(env, object->class, "<init>", "()V"); 125 126 object->object = 127 (*env)->NewObject(env, object->class, object->constructor); 128 } 129 130 bean->method_setPoolName = (*env)->GetMethodID( 131 env, object->class, "setPoolName", "(Ljava/lang/String;)V"); 132 133 bean->method_setParentName = (*env)->GetMethodID( 134 env, object->class, "setParentName", "(Ljava/lang/String;)V"); 135 136 bean->method_setBaseName = (*env)->GetMethodID( 137 env, object->class, "setBaseName", "(Ljava/lang/String;)V"); 138 139 bean->method_setProperties = (*env)->GetMethodID( 140 env, object->class, "setProperties", 141 "([L" ZFSJNI_PACKAGE_DATA "Property;)V"); 142 143 bean->method_addProperty = (*env)->GetMethodID( 144 env, object->class, "addProperty", 145 "(L" ZFSJNI_PACKAGE_DATA "Property;)V"); 146 } 147 148 /* Create a PoolBean */ 149 static void 150 new_PoolBean(JNIEnv *env, PoolBean_t *bean) 151 { 152 zjni_Object_t *object = (zjni_Object_t *)bean; 153 154 if (object->object == NULL) { 155 156 object->class = 157 (*env)->FindClass(env, ZFSJNI_PACKAGE_DATA "PoolBean"); 158 159 object->constructor = 160 (*env)->GetMethodID(env, object->class, "<init>", "()V"); 161 162 object->object = 163 (*env)->NewObject(env, object->class, object->constructor); 164 } 165 166 new_FileSystemBean(env, (FileSystemBean_t *)bean); 167 } 168 169 /* Create a FileSystemBean */ 170 static void 171 new_FileSystemBean(JNIEnv *env, FileSystemBean_t *bean) 172 { 173 zjni_Object_t *object = (zjni_Object_t *)bean; 174 175 if (object->object == NULL) { 176 object->class = 177 (*env)->FindClass(env, 178 ZFSJNI_PACKAGE_DATA "FileSystemBean"); 179 180 object->constructor = 181 (*env)->GetMethodID(env, object->class, "<init>", "()V"); 182 183 object->object = 184 (*env)->NewObject(env, object->class, object->constructor); 185 } 186 187 new_DatasetBean(env, (DatasetBean_t *)bean); 188 } 189 190 /* Create a VolumeBean */ 191 static void 192 new_VolumeBean(JNIEnv *env, VolumeBean_t *bean) 193 { 194 zjni_Object_t *object = (zjni_Object_t *)bean; 195 196 if (object->object == NULL) { 197 object->class = 198 (*env)->FindClass(env, 199 ZFSJNI_PACKAGE_DATA "VolumeBean"); 200 201 object->constructor = 202 (*env)->GetMethodID(env, object->class, "<init>", "()V"); 203 204 object->object = 205 (*env)->NewObject(env, object->class, object->constructor); 206 } 207 208 new_DatasetBean(env, (DatasetBean_t *)bean); 209 } 210 211 /* Create a SnapshotBean */ 212 static void 213 new_SnapshotBean(JNIEnv *env, SnapshotBean_t *bean) 214 { 215 zjni_Object_t *object = (zjni_Object_t *)bean; 216 217 if (object->object == NULL) { 218 object->class = 219 (*env)->FindClass(env, 220 ZFSJNI_PACKAGE_DATA "SnapshotBean"); 221 222 object->constructor = 223 (*env)->GetMethodID(env, object->class, "<init>", "()V"); 224 225 object->object = 226 (*env)->NewObject(env, object->class, object->constructor); 227 } 228 229 new_DatasetBean(env, (DatasetBean_t *)bean); 230 } 231 232 /* Create a FileSystemSnapshotBean */ 233 static void 234 new_FileSystemSnapshotBean(JNIEnv *env, FileSystemSnapshotBean_t *bean) 235 { 236 zjni_Object_t *object = (zjni_Object_t *)bean; 237 238 if (object->object == NULL) { 239 object->class = 240 (*env)->FindClass(env, 241 ZFSJNI_PACKAGE_DATA "FileSystemSnapshotBean"); 242 243 object->constructor = 244 (*env)->GetMethodID(env, object->class, "<init>", "()V"); 245 246 object->object = 247 (*env)->NewObject(env, object->class, object->constructor); 248 } 249 250 new_SnapshotBean(env, (SnapshotBean_t *)bean); 251 } 252 253 /* Create a VolumeSnapshotBean */ 254 static void 255 new_VolumeSnapshotBean(JNIEnv *env, VolumeSnapshotBean_t *bean) 256 { 257 zjni_Object_t *object = (zjni_Object_t *)bean; 258 259 if (object->object == NULL) { 260 object->class = 261 (*env)->FindClass(env, 262 ZFSJNI_PACKAGE_DATA "VolumeSnapshotBean"); 263 264 object->constructor = 265 (*env)->GetMethodID(env, object->class, "<init>", "()V"); 266 267 object->object = 268 (*env)->NewObject(env, object->class, object->constructor); 269 } 270 271 new_SnapshotBean(env, (SnapshotBean_t *)bean); 272 } 273 274 static int 275 populate_DatasetBean(JNIEnv *env, zfs_handle_t *zhp, DatasetBean_t *bean) 276 { 277 jstring poolUTF; 278 jstring parentUTF; 279 jstring baseUTF; 280 jobjectArray properties; 281 zjni_Object_t *object = (zjni_Object_t *)bean; 282 283 /* 284 * zhp->zfs_name has the format 285 * <pool>[[/<container...>]/<dataset>[@<snapshot>]] 286 */ 287 288 regex_t re; 289 regmatch_t matches[REGEX_ZFS_NAME_NGROUPS]; 290 291 char *name = (char *)zfs_get_name(zhp); 292 if (regcomp(&re, REGEX_ZFS_NAME, REG_EXTENDED) != 0 || 293 regexec(&re, name, REGEX_ZFS_NAME_NGROUPS, matches, 0) != 0) { 294 regfree(&re); 295 zjni_throw_exception(env, "invalid name: %s", name); 296 return (-1); 297 } 298 299 regfree(&re); 300 301 /* Set names */ 302 poolUTF = zjni_get_matched_string( 303 env, name, matches + REGEX_ZFS_NAME_POOL_GROUP); 304 parentUTF = zjni_get_matched_string( 305 env, name, matches + REGEX_ZFS_NAME_PARENT_GROUP); 306 baseUTF = zjni_get_matched_string( 307 env, name, matches + REGEX_ZFS_NAME_BASE_GROUP); 308 309 if (poolUTF == NULL) { 310 poolUTF = baseUTF; 311 } 312 313 (*env)->CallVoidMethod( 314 env, object->object, bean->method_setPoolName, poolUTF); 315 (*env)->CallVoidMethod( 316 env, object->object, bean->method_setBaseName, baseUTF); 317 318 if (parentUTF != NULL) { 319 (*env)->CallVoidMethod( 320 env, object->object, bean->method_setParentName, parentUTF); 321 } 322 323 properties = zjni_get_Dataset_properties(env, zhp); 324 if (properties == NULL) { 325 /* Must not call any more Java methods to preserve exception */ 326 return (-1); 327 } 328 329 (*env)->CallVoidMethod( 330 env, object->object, bean->method_setProperties, properties); 331 332 return (0); 333 } 334 335 static int 336 populate_PoolBean(JNIEnv *env, zfs_handle_t *zhp, PoolBean_t *bean) 337 { 338 return (populate_FileSystemBean(env, zhp, (FileSystemBean_t *)bean)); 339 } 340 341 static int 342 populate_FileSystemBean(JNIEnv *env, zfs_handle_t *zhp, FileSystemBean_t *bean) 343 { 344 return (populate_DatasetBean(env, zhp, (DatasetBean_t *)bean)); 345 } 346 347 static int 348 populate_VolumeBean(JNIEnv *env, zfs_handle_t *zhp, VolumeBean_t *bean) 349 { 350 return (populate_DatasetBean(env, zhp, (DatasetBean_t *)bean)); 351 } 352 353 static int 354 populate_SnapshotBean(JNIEnv *env, zfs_handle_t *zhp, SnapshotBean_t *bean) 355 { 356 return (populate_DatasetBean(env, zhp, (DatasetBean_t *)bean)); 357 } 358 359 static int 360 populate_FileSystemSnapshotBean(JNIEnv *env, zfs_handle_t *zhp, 361 FileSystemSnapshotBean_t *bean) 362 { 363 return (populate_SnapshotBean(env, zhp, (SnapshotBean_t *)bean)); 364 } 365 366 static int 367 populate_VolumeSnapshotBean(JNIEnv *env, zfs_handle_t *zhp, 368 VolumeSnapshotBean_t *bean) 369 { 370 return (populate_SnapshotBean(env, zhp, (SnapshotBean_t *)bean)); 371 } 372 373 static jobject 374 create_PoolBean(JNIEnv *env, zfs_handle_t *zhp) 375 { 376 int result; 377 PoolBean_t bean_obj = {0}; 378 PoolBean_t *bean = &bean_obj; 379 380 /* Construct PoolBean */ 381 new_PoolBean(env, bean); 382 383 result = populate_PoolBean(env, zhp, bean); 384 if (result) { 385 /* Must not call any more Java methods to preserve exception */ 386 return (NULL); 387 } 388 389 return (((zjni_Object_t *)bean)->object); 390 } 391 392 static jobject 393 create_FileSystemBean(JNIEnv *env, zfs_handle_t *zhp) 394 { 395 int result; 396 FileSystemBean_t bean_obj = {0}; 397 FileSystemBean_t *bean = &bean_obj; 398 399 /* Construct FileSystemBean */ 400 new_FileSystemBean(env, bean); 401 402 result = populate_FileSystemBean(env, zhp, bean); 403 if (result) { 404 /* Must not call any more Java methods to preserve exception */ 405 return (NULL); 406 } 407 408 return (((zjni_Object_t *)bean)->object); 409 } 410 411 static jobject 412 create_VolumeBean(JNIEnv *env, zfs_handle_t *zhp) 413 { 414 int result; 415 VolumeBean_t bean_obj = {0}; 416 VolumeBean_t *bean = &bean_obj; 417 418 /* Construct VolumeBean */ 419 new_VolumeBean(env, bean); 420 421 result = populate_VolumeBean(env, zhp, bean); 422 if (result) { 423 /* Must not call any more Java methods to preserve exception */ 424 return (NULL); 425 } 426 427 return (((zjni_Object_t *)bean)->object); 428 } 429 430 static jobject 431 create_FileSystemSnapshotBean(JNIEnv *env, zfs_handle_t *zhp) 432 { 433 int result; 434 FileSystemSnapshotBean_t bean_obj = {0}; 435 FileSystemSnapshotBean_t *bean = &bean_obj; 436 437 /* Construct FileSystemSnapshotBean */ 438 new_FileSystemSnapshotBean(env, bean); 439 440 result = populate_FileSystemSnapshotBean(env, zhp, bean); 441 if (result) { 442 /* Must not call any more Java methods to preserve exception */ 443 return (NULL); 444 } 445 446 return (((zjni_Object_t *)bean)->object); 447 } 448 449 static jobject 450 create_VolumeSnapshotBean(JNIEnv *env, zfs_handle_t *zhp) 451 { 452 int result; 453 VolumeSnapshotBean_t bean_obj = {0}; 454 VolumeSnapshotBean_t *bean = &bean_obj; 455 456 /* Construct VolumeSnapshotBean */ 457 new_VolumeSnapshotBean(env, bean); 458 459 result = populate_VolumeSnapshotBean(env, zhp, bean); 460 if (result) { 461 /* Must not call any more Java methods to preserve exception */ 462 return (NULL); 463 } 464 465 return (((zjni_Object_t *)bean)->object); 466 } 467 468 static jobject 469 create_DatasetBean(JNIEnv *env, zfs_handle_t *zhp) 470 { 471 jobject object = NULL; 472 473 switch (zfs_get_type(zhp)) { 474 case ZFS_TYPE_FILESYSTEM: 475 object = is_pool(zhp) ? 476 create_PoolBean(env, zhp) : 477 create_FileSystemBean(env, zhp); 478 break; 479 480 case ZFS_TYPE_VOLUME: 481 object = create_VolumeBean(env, zhp); 482 break; 483 484 case ZFS_TYPE_SNAPSHOT: 485 object = is_fs_snapshot(zhp) ? 486 create_FileSystemSnapshotBean(env, zhp) : 487 create_VolumeSnapshotBean(env, zhp); 488 break; 489 } 490 491 return (object); 492 } 493 494 /* 495 * Determines whether the given snapshot is a snapshot of a file 496 * system or of a volume. 497 * 498 * Returns: 499 * 500 * 0 if it is a volume snapshot 501 * 1 if it is a file system snapshot 502 * -1 on error 503 */ 504 static int 505 is_fs_snapshot(zfs_handle_t *zhp) 506 { 507 char parent[ZFS_MAXNAMELEN]; 508 zfs_handle_t *parent_zhp; 509 int isfs; 510 511 if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) { 512 return (-1); 513 } 514 515 zjni_get_dataset_from_snapshot( 516 zfs_get_name(zhp), parent, sizeof (parent)); 517 518 parent_zhp = zfs_open(parent, ZFS_TYPE_ANY); 519 if (parent_zhp == NULL) { 520 return (-1); 521 } 522 523 isfs = zfs_get_type(parent_zhp) == ZFS_TYPE_FILESYSTEM; 524 zfs_close(parent_zhp); 525 526 return (isfs); 527 } 528 529 static int 530 is_pool(zfs_handle_t *zhp) 531 { 532 return (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM && 533 strchr(zfs_get_name(zhp), '/') == NULL); 534 } 535 536 static zfs_handle_t * 537 open_device(JNIEnv *env, jstring nameUTF, zfs_type_t typemask) 538 { 539 zfs_handle_t *zhp = NULL; 540 541 if (nameUTF != NULL) { 542 const char *name = 543 (*env)->GetStringUTFChars(env, nameUTF, NULL); 544 545 zhp = zfs_open(name, typemask); 546 if (zhp == NULL) { 547 zjni_throw_exception(env, "invalid device name: %s", 548 name); 549 } 550 551 (*env)->ReleaseStringUTFChars(env, nameUTF, name); 552 } 553 554 return (zhp); 555 } 556 557 /* 558 * Package-private functions 559 */ 560 561 /* 562 * Callback function for zfs_iter_children(). Creates the appropriate 563 * Dataset and adds it to the given zjni_ArrayList. Per the contract 564 * with zfs_iter_children(), calls zfs_close() on the given 565 * zfs_handle_t. 566 */ 567 int 568 zjni_create_add_Dataset(zfs_handle_t *zhp, void *data) 569 { 570 JNIEnv *env = ((zjni_ArrayCallbackData_t *)data)->env; 571 zjni_Collection_t *list = ((zjni_ArrayCallbackData_t *)data)->list; 572 zfs_type_t typemask = 573 ((zjni_DatasetArrayCallbackData_t *)data)->typemask; 574 575 /* Only add allowed types */ 576 if (zfs_get_type(zhp) & typemask) { 577 578 jobject bean = create_DatasetBean(env, zhp); 579 zfs_close(zhp); 580 581 if (bean == NULL) { 582 /* 583 * Must not call any more Java methods to preserve 584 * exception 585 */ 586 return (-1); 587 } 588 589 /* Add pool to zjni_ArrayList */ 590 (*env)->CallBooleanMethod(env, ((zjni_Object_t *)list)->object, 591 ((zjni_Collection_t *)list)->method_add, bean); 592 } 593 594 return (0); 595 } 596 597 jobjectArray 598 zjni_get_Datasets_below(JNIEnv *env, jstring parentUTF, 599 zfs_type_t parent_typemask, zfs_type_t child_typemask, char *arrayClass) 600 { 601 jobjectArray array = NULL; 602 zfs_handle_t *zhp; 603 604 /* Create an array list to hold the children */ 605 zjni_DatasetSet_t list_obj = {0}; 606 zjni_DatasetSet_t *list = &list_obj; 607 zjni_new_DatasetSet(env, list); 608 609 /* Retrieve parent */ 610 zhp = open_device(env, parentUTF, parent_typemask); 611 if (zhp != NULL) { 612 613 if (!(zfs_get_type(zhp) & parent_typemask)) { 614 zjni_throw_exception(env, "wrong type: %s", 615 zfs_get_name(zhp)); 616 } else { 617 618 zjni_DatasetArrayCallbackData_t data = {0}; 619 data.data.env = env; 620 data.data.list = (zjni_Collection_t *)list; 621 data.typemask = child_typemask; 622 623 (void) zfs_iter_children(zhp, zjni_create_add_Dataset, 624 &data); 625 } 626 627 zfs_close(zhp); 628 } 629 630 if ((*env)->ExceptionOccurred(env) == NULL) { 631 array = zjni_Collection_to_array( 632 env, (zjni_Collection_t *)list, arrayClass); 633 } 634 635 return (array); 636 } 637 638 jobjectArray 639 zjni_get_Datasets_dependents(JNIEnv *env, jobjectArray paths) 640 { 641 jint i; 642 jint npaths; 643 zjni_DatasetArrayCallbackData_t data = {0}; 644 jobjectArray array = NULL; 645 646 /* Create a list to hold the children */ 647 zjni_DatasetSet_t list_obj = {0}; 648 zjni_DatasetSet_t *list = &list_obj; 649 zjni_new_DatasetSet(env, list); 650 651 data.data.env = env; 652 data.data.list = (zjni_Collection_t *)list; 653 data.typemask = ZFS_TYPE_ANY; 654 655 npaths = (*env)->GetArrayLength(env, paths); 656 for (i = 0; i < npaths; i++) { 657 658 jstring pathUTF = (jstring) 659 ((*env)->GetObjectArrayElement(env, paths, i)); 660 661 zfs_handle_t *zhp = open_device(env, pathUTF, ZFS_TYPE_ANY); 662 if (zhp == NULL) { 663 /* Clear the exception */ 664 (*env)->ExceptionClear(env); 665 } else { 666 667 /* Add all dependents of this Dataset to the list */ 668 (void) zfs_iter_dependents(zhp, 669 zjni_create_add_Dataset, &data); 670 671 /* Add this Dataset to the list (and close zhp) */ 672 (void) zjni_create_add_Dataset(zhp, &data); 673 } 674 } 675 676 if ((*env)->ExceptionOccurred(env) == NULL) { 677 array = zjni_Collection_to_array(env, (zjni_Collection_t *)list, 678 ZFSJNI_PACKAGE_DATA "Dataset"); 679 } 680 681 return (array); 682 } 683 684 /* 685 * Gets a Dataset of the given name and type, or NULL if no such 686 * Dataset exists. 687 */ 688 jobject 689 zjni_get_Dataset(JNIEnv *env, jstring nameUTF, zfs_type_t typemask) 690 { 691 jobject device = NULL; 692 zfs_handle_t *zhp = open_device(env, nameUTF, typemask); 693 if (zhp == NULL) { 694 /* 695 * Clear the exception -- this function returns NULL 696 * on invalid device 697 */ 698 (*env)->ExceptionClear(env); 699 } else { 700 701 /* Is this device the expected type? */ 702 if (zfs_get_type(zhp) & typemask) { 703 /* Creates an object of the appropriate class */ 704 device = create_DatasetBean(env, zhp); 705 } 706 zfs_close(zhp); 707 } 708 709 return (device); 710 } 711