/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2015 by Delphix. All rights reserved. */ #include "libzfs_jni_util.h" #include "libzfs_jni_dataset.h" #include "libzfs_jni_property.h" #include "libzfs_jni_pool.h" #include #define REGEX_ZFS_NAME "^((([^/]*)(/.+)?)[/@])?([^/]+)/*" #define REGEX_ZFS_NAME_NGROUPS 6 #define REGEX_ZFS_NAME_POOL_GROUP 3 #define REGEX_ZFS_NAME_PARENT_GROUP 2 #define REGEX_ZFS_NAME_BASE_GROUP 5 /* * Types */ typedef struct DatasetBean { zjni_Object_t super; jmethodID method_setPoolName; jmethodID method_setParentName; jmethodID method_setBaseName; jmethodID method_setProperties; jmethodID method_addProperty; } DatasetBean_t; typedef struct FileSystemBean { DatasetBean_t super; } FileSystemBean_t; typedef struct PoolBean { FileSystemBean_t super; PoolStatsBean_t interface_PoolStats; } PoolBean_t; typedef struct VolumeBean { DatasetBean_t super; } VolumeBean_t; typedef struct SnapshotBean { DatasetBean_t super; } SnapshotBean_t; typedef struct FileSystemSnapshotBean { DatasetBean_t super; } FileSystemSnapshotBean_t; typedef struct VolumeSnapshotBean { DatasetBean_t super; } VolumeSnapshotBean_t; /* * Function prototypes */ static void new_DatasetBean(JNIEnv *, DatasetBean_t *); static void new_PoolBean(JNIEnv *, PoolBean_t *); static void new_FileSystemBean(JNIEnv *, FileSystemBean_t *); static void new_VolumeBean(JNIEnv *, VolumeBean_t *); static void new_SnapshotBean(JNIEnv *, SnapshotBean_t *); static void new_FileSystemSnapshotBean(JNIEnv *, FileSystemSnapshotBean_t *); static void new_VolumeSnapshotBean(JNIEnv *, VolumeSnapshotBean_t *); static int set_name_in_DatasetBean(JNIEnv *, char *, DatasetBean_t *); static int populate_DatasetBean(JNIEnv *, zfs_handle_t *, DatasetBean_t *); static int populate_PoolBean( JNIEnv *, zpool_handle_t *, zfs_handle_t *, PoolBean_t *); static int populate_FileSystemBean( JNIEnv *, zfs_handle_t *, FileSystemBean_t *); static int populate_VolumeBean( JNIEnv *, zfs_handle_t *, VolumeBean_t *); static int populate_SnapshotBean(JNIEnv *, zfs_handle_t *, SnapshotBean_t *); static int populate_FileSystemSnapshotBean( JNIEnv *, zfs_handle_t *, FileSystemSnapshotBean_t *); static int populate_VolumeSnapshotBean( JNIEnv *, zfs_handle_t *, VolumeSnapshotBean_t *); static jobject create_PoolBean(JNIEnv *, zpool_handle_t *, zfs_handle_t *); static jobject create_FileSystemBean(JNIEnv *, zfs_handle_t *); static jobject create_VolumeBean(JNIEnv *, zfs_handle_t *); static jobject create_FileSystemSnapshotBean(JNIEnv *, zfs_handle_t *); static jobject create_VolumeSnapshotBean(JNIEnv *, zfs_handle_t *); static jobject create_DatasetBean(JNIEnv *, zfs_handle_t *); static int is_fs_snapshot(zfs_handle_t *); static int is_pool_name(const char *); /* * Static functions */ /* Create a DatasetBean */ static void new_DatasetBean(JNIEnv *env, DatasetBean_t *bean) { zjni_Object_t *object = (zjni_Object_t *)bean; if (object->object == NULL) { object->class = (*env)->FindClass(env, ZFSJNI_PACKAGE_DATA "DatasetBean"); object->constructor = (*env)->GetMethodID(env, object->class, "", "()V"); object->object = (*env)->NewObject(env, object->class, object->constructor); } bean->method_setPoolName = (*env)->GetMethodID( env, object->class, "setPoolName", "(Ljava/lang/String;)V"); bean->method_setParentName = (*env)->GetMethodID( env, object->class, "setParentName", "(Ljava/lang/String;)V"); bean->method_setBaseName = (*env)->GetMethodID( env, object->class, "setBaseName", "(Ljava/lang/String;)V"); bean->method_setProperties = (*env)->GetMethodID( env, object->class, "setProperties", "([L" ZFSJNI_PACKAGE_DATA "Property;)V"); bean->method_addProperty = (*env)->GetMethodID( env, object->class, "addProperty", "(L" ZFSJNI_PACKAGE_DATA "Property;)V"); } /* Create a PoolBean */ static void new_PoolBean(JNIEnv *env, PoolBean_t *bean) { zjni_Object_t *object = (zjni_Object_t *)bean; if (object->object == NULL) { object->class = (*env)->FindClass(env, ZFSJNI_PACKAGE_DATA "PoolBean"); object->constructor = (*env)->GetMethodID(env, object->class, "", "()V"); object->object = (*env)->NewObject(env, object->class, object->constructor); } new_FileSystemBean(env, (FileSystemBean_t *)bean); new_PoolStats(env, &(bean->interface_PoolStats), object); } /* Create a FileSystemBean */ static void new_FileSystemBean(JNIEnv *env, FileSystemBean_t *bean) { zjni_Object_t *object = (zjni_Object_t *)bean; if (object->object == NULL) { object->class = (*env)->FindClass(env, ZFSJNI_PACKAGE_DATA "FileSystemBean"); object->constructor = (*env)->GetMethodID(env, object->class, "", "()V"); object->object = (*env)->NewObject(env, object->class, object->constructor); } new_DatasetBean(env, (DatasetBean_t *)bean); } /* Create a VolumeBean */ static void new_VolumeBean(JNIEnv *env, VolumeBean_t *bean) { zjni_Object_t *object = (zjni_Object_t *)bean; if (object->object == NULL) { object->class = (*env)->FindClass(env, ZFSJNI_PACKAGE_DATA "VolumeBean"); object->constructor = (*env)->GetMethodID(env, object->class, "", "()V"); object->object = (*env)->NewObject(env, object->class, object->constructor); } new_DatasetBean(env, (DatasetBean_t *)bean); } /* Create a SnapshotBean */ static void new_SnapshotBean(JNIEnv *env, SnapshotBean_t *bean) { zjni_Object_t *object = (zjni_Object_t *)bean; if (object->object == NULL) { object->class = (*env)->FindClass(env, ZFSJNI_PACKAGE_DATA "SnapshotBean"); object->constructor = (*env)->GetMethodID(env, object->class, "", "()V"); object->object = (*env)->NewObject(env, object->class, object->constructor); } new_DatasetBean(env, (DatasetBean_t *)bean); } /* Create a FileSystemSnapshotBean */ static void new_FileSystemSnapshotBean(JNIEnv *env, FileSystemSnapshotBean_t *bean) { zjni_Object_t *object = (zjni_Object_t *)bean; if (object->object == NULL) { object->class = (*env)->FindClass(env, ZFSJNI_PACKAGE_DATA "FileSystemSnapshotBean"); object->constructor = (*env)->GetMethodID(env, object->class, "", "()V"); object->object = (*env)->NewObject(env, object->class, object->constructor); } new_SnapshotBean(env, (SnapshotBean_t *)bean); } /* Create a VolumeSnapshotBean */ static void new_VolumeSnapshotBean(JNIEnv *env, VolumeSnapshotBean_t *bean) { zjni_Object_t *object = (zjni_Object_t *)bean; if (object->object == NULL) { object->class = (*env)->FindClass(env, ZFSJNI_PACKAGE_DATA "VolumeSnapshotBean"); object->constructor = (*env)->GetMethodID(env, object->class, "", "()V"); object->object = (*env)->NewObject(env, object->class, object->constructor); } new_SnapshotBean(env, (SnapshotBean_t *)bean); } static int set_name_in_DatasetBean(JNIEnv *env, char *name, DatasetBean_t *bean) { jstring poolUTF; jstring parentUTF; jstring baseUTF; zjni_Object_t *object = (zjni_Object_t *)bean; /* * zhp->zfs_name has the format * [[/]/[@]] */ regex_t re; regmatch_t matches[REGEX_ZFS_NAME_NGROUPS]; if (regcomp(&re, REGEX_ZFS_NAME, REG_EXTENDED) != 0 || regexec(&re, name, REGEX_ZFS_NAME_NGROUPS, matches, 0) != 0) { regfree(&re); zjni_throw_exception(env, "invalid name: %s", name); return (-1); } regfree(&re); /* Set names */ poolUTF = zjni_get_matched_string( env, name, matches + REGEX_ZFS_NAME_POOL_GROUP); parentUTF = zjni_get_matched_string( env, name, matches + REGEX_ZFS_NAME_PARENT_GROUP); baseUTF = zjni_get_matched_string( env, name, matches + REGEX_ZFS_NAME_BASE_GROUP); if (poolUTF == NULL) { poolUTF = baseUTF; } (*env)->CallVoidMethod( env, object->object, bean->method_setPoolName, poolUTF); (*env)->CallVoidMethod( env, object->object, bean->method_setBaseName, baseUTF); if (parentUTF != NULL) { (*env)->CallVoidMethod( env, object->object, bean->method_setParentName, parentUTF); } return (0); } static int populate_DatasetBean(JNIEnv *env, zfs_handle_t *zhp, DatasetBean_t *bean) { jobjectArray properties; zjni_Object_t *object = (zjni_Object_t *)bean; int result = set_name_in_DatasetBean( env, (char *)zfs_get_name(zhp), bean); if (result != 0) { /* Must not call any more Java methods to preserve exception */ return (-1); } properties = zjni_get_Dataset_properties(env, zhp); if (properties == NULL) { /* Must not call any more Java methods to preserve exception */ return (-1); } (*env)->CallVoidMethod( env, object->object, bean->method_setProperties, properties); return (0); } static int populate_PoolBean(JNIEnv *env, zpool_handle_t *zphp, zfs_handle_t *zhp, PoolBean_t *bean) { int result = 0; zjni_Object_t *object = (zjni_Object_t *)bean; PoolStatsBean_t *pool_stats = &(bean->interface_PoolStats); DeviceStatsBean_t *dev_stats = (DeviceStatsBean_t *)pool_stats; nvlist_t *devices = zjni_get_root_vdev(zphp); if (devices == NULL || populate_DeviceStatsBean(env, devices, dev_stats, object)) { result = -1; } else { char *msgid; /* Override value set in populate_DeviceStatsBean */ (*env)->CallVoidMethod(env, object->object, dev_stats->method_setSize, zpool_get_prop_int(zphp, ZPOOL_PROP_SIZE, NULL)); (*env)->CallVoidMethod(env, object->object, pool_stats->method_setPoolState, zjni_pool_state_to_obj( env, zpool_get_state(zphp))); (*env)->CallVoidMethod(env, object->object, pool_stats->method_setPoolStatus, zjni_pool_status_to_obj(env, zpool_get_status(zphp, &msgid, NULL))); (*env)->CallVoidMethod(env, object->object, pool_stats->method_setPoolVersion, zpool_get_prop_int(zphp, ZPOOL_PROP_VERSION, NULL)); /* * If a root file system does not exist for this pool, the pool * is likely faulted, so just set its name in the Java object. * Otherwise, populate all fields of the Java object. */ if (zhp == NULL) { result = set_name_in_DatasetBean(env, (char *)zpool_get_name(zphp), (DatasetBean_t *)bean); } else { result = populate_FileSystemBean( env, zhp, (FileSystemBean_t *)bean); } } return (result != 0); } static int populate_FileSystemBean(JNIEnv *env, zfs_handle_t *zhp, FileSystemBean_t *bean) { return (populate_DatasetBean(env, zhp, (DatasetBean_t *)bean)); } static int populate_VolumeBean(JNIEnv *env, zfs_handle_t *zhp, VolumeBean_t *bean) { return (populate_DatasetBean(env, zhp, (DatasetBean_t *)bean)); } static int populate_SnapshotBean(JNIEnv *env, zfs_handle_t *zhp, SnapshotBean_t *bean) { return (populate_DatasetBean(env, zhp, (DatasetBean_t *)bean)); } static int populate_FileSystemSnapshotBean(JNIEnv *env, zfs_handle_t *zhp, FileSystemSnapshotBean_t *bean) { return (populate_SnapshotBean(env, zhp, (SnapshotBean_t *)bean)); } static int populate_VolumeSnapshotBean(JNIEnv *env, zfs_handle_t *zhp, VolumeSnapshotBean_t *bean) { return (populate_SnapshotBean(env, zhp, (SnapshotBean_t *)bean)); } static jobject create_PoolBean(JNIEnv *env, zpool_handle_t *zphp, zfs_handle_t *zhp) { int result; PoolBean_t bean_obj = {0}; PoolBean_t *bean = &bean_obj; /* Construct PoolBean */ new_PoolBean(env, bean); result = populate_PoolBean(env, zphp, zhp, bean); if (result) { /* Must not call any more Java methods to preserve exception */ return (NULL); } return (((zjni_Object_t *)bean)->object); } static jobject create_FileSystemBean(JNIEnv *env, zfs_handle_t *zhp) { int result; FileSystemBean_t bean_obj = {0}; FileSystemBean_t *bean = &bean_obj; /* Construct FileSystemBean */ new_FileSystemBean(env, bean); result = populate_FileSystemBean(env, zhp, bean); if (result) { /* Must not call any more Java methods to preserve exception */ return (NULL); } return (((zjni_Object_t *)bean)->object); } static jobject create_VolumeBean(JNIEnv *env, zfs_handle_t *zhp) { int result; VolumeBean_t bean_obj = {0}; VolumeBean_t *bean = &bean_obj; /* Construct VolumeBean */ new_VolumeBean(env, bean); result = populate_VolumeBean(env, zhp, bean); if (result) { /* Must not call any more Java methods to preserve exception */ return (NULL); } return (((zjni_Object_t *)bean)->object); } static jobject create_FileSystemSnapshotBean(JNIEnv *env, zfs_handle_t *zhp) { int result; FileSystemSnapshotBean_t bean_obj = {0}; FileSystemSnapshotBean_t *bean = &bean_obj; /* Construct FileSystemSnapshotBean */ new_FileSystemSnapshotBean(env, bean); result = populate_FileSystemSnapshotBean(env, zhp, bean); if (result) { /* Must not call any more Java methods to preserve exception */ return (NULL); } return (((zjni_Object_t *)bean)->object); } static jobject create_VolumeSnapshotBean(JNIEnv *env, zfs_handle_t *zhp) { int result; VolumeSnapshotBean_t bean_obj = {0}; VolumeSnapshotBean_t *bean = &bean_obj; /* Construct VolumeSnapshotBean */ new_VolumeSnapshotBean(env, bean); result = populate_VolumeSnapshotBean(env, zhp, bean); if (result) { /* Must not call any more Java methods to preserve exception */ return (NULL); } return (((zjni_Object_t *)bean)->object); } static jobject create_DatasetBean(JNIEnv *env, zfs_handle_t *zhp) { jobject object = NULL; switch (zfs_get_type(zhp)) { case ZFS_TYPE_FILESYSTEM: object = create_FileSystemBean(env, zhp); break; case ZFS_TYPE_VOLUME: object = create_VolumeBean(env, zhp); break; case ZFS_TYPE_SNAPSHOT: object = is_fs_snapshot(zhp) ? create_FileSystemSnapshotBean(env, zhp) : create_VolumeSnapshotBean(env, zhp); break; } return (object); } /* * Determines whether the given snapshot is a snapshot of a file * system or of a volume. * * Returns: * * 0 if it is a volume snapshot * 1 if it is a file system snapshot * -1 on error */ static int is_fs_snapshot(zfs_handle_t *zhp) { char parent[ZFS_MAX_DATASET_NAME_LEN]; zfs_handle_t *parent_zhp; int isfs; if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) { return (-1); } zjni_get_dataset_from_snapshot( zfs_get_name(zhp), parent, sizeof (parent)); parent_zhp = zfs_open(g_zfs, parent, ZFS_TYPE_DATASET); if (parent_zhp == NULL) { return (-1); } isfs = zfs_get_type(parent_zhp) == ZFS_TYPE_FILESYSTEM; zfs_close(parent_zhp); return (isfs); } static int is_pool_name(const char *name) { return (strchr(name, '/') == NULL && strchr(name, '@') == NULL); } /* * Package-private functions */ /* * Callback function for zpool_iter(). Creates a Pool and adds it to * the given zjni_ArrayList. */ int zjni_create_add_Pool(zpool_handle_t *zphp, void *data) { JNIEnv *env = ((zjni_ArrayCallbackData_t *)data)->env; zjni_Collection_t *list = ((zjni_ArrayCallbackData_t *)data)->list; /* Get root fs for this pool -- may be NULL if pool is faulted */ zfs_handle_t *zhp = zfs_open(g_zfs, zpool_get_name(zphp), ZFS_TYPE_FILESYSTEM); jobject bean = create_PoolBean(env, zphp, zhp); if (zhp != NULL) zfs_close(zhp); zpool_close(zphp); if (bean == NULL) { /* Must not call any more Java methods to preserve exception */ return (-1); } /* Add pool to zjni_ArrayList */ (*env)->CallBooleanMethod(env, ((zjni_Object_t *)list)->object, ((zjni_Collection_t *)list)->method_add, bean); return (0); } /* * Callback function for zfs_iter_children(). Creates the appropriate * Dataset and adds it to the given zjni_ArrayList. Per the contract * with zfs_iter_children(), calls zfs_close() on the given * zfs_handle_t. */ int zjni_create_add_Dataset(zfs_handle_t *zhp, void *data) { JNIEnv *env = ((zjni_ArrayCallbackData_t *)data)->env; zjni_Collection_t *list = ((zjni_ArrayCallbackData_t *)data)->list; zfs_type_t typemask = ((zjni_DatasetArrayCallbackData_t *)data)->typemask; /* Only add allowed types */ if (zfs_get_type(zhp) & typemask) { jobject bean = create_DatasetBean(env, zhp); zfs_close(zhp); if (bean == NULL) { /* * Must not call any more Java methods to preserve * exception */ return (-1); } /* Add Dataset to zjni_ArrayList */ (*env)->CallBooleanMethod(env, ((zjni_Object_t *)list)->object, ((zjni_Collection_t *)list)->method_add, bean); } else { zfs_close(zhp); } return (0); } jobjectArray zjni_get_Datasets_below(JNIEnv *env, jstring parentUTF, zfs_type_t parent_typemask, zfs_type_t child_typemask, char *arrayClass) { jobjectArray array = NULL; if (parentUTF != NULL) { zfs_handle_t *zhp; int error = 1; const char *name = (*env)->GetStringUTFChars(env, parentUTF, NULL); /* Create an array list to hold the children */ zjni_DatasetSet_t list_obj = {0}; zjni_DatasetSet_t *list = &list_obj; zjni_new_DatasetSet(env, list); /* Retrieve parent dataset */ zhp = zfs_open(g_zfs, name, parent_typemask); if (zhp != NULL) { zjni_DatasetArrayCallbackData_t data = {0}; data.data.env = env; data.data.list = (zjni_Collection_t *)list; data.typemask = child_typemask; (void) zfs_iter_children(zhp, zjni_create_add_Dataset, &data); zfs_close(zhp); if ((*env)->ExceptionOccurred(env) == NULL) { error = 0; } } else /* Parent is not a dataset -- see if it's a faulted pool */ if ((parent_typemask & ZFS_TYPE_FILESYSTEM) && is_pool_name(name)) { zpool_handle_t *zphp = zpool_open_canfail(g_zfs, name); if (zphp != NULL) { /* A faulted pool has no datasets */ error = 0; zpool_close(zphp); } } (*env)->ReleaseStringUTFChars(env, parentUTF, name); if (!error) { array = zjni_Collection_to_array( env, (zjni_Collection_t *)list, arrayClass); } } return (array); } jobjectArray zjni_get_Datasets_dependents(JNIEnv *env, jobjectArray paths) { jint i; jint npaths; zjni_DatasetArrayCallbackData_t data = {0}; jobjectArray array = NULL; /* Create a list to hold the children */ zjni_DatasetSet_t list_obj = {0}; zjni_DatasetSet_t *list = &list_obj; zjni_new_DatasetSet(env, list); data.data.env = env; data.data.list = (zjni_Collection_t *)list; data.typemask = ZFS_TYPE_DATASET; npaths = (*env)->GetArrayLength(env, paths); for (i = 0; i < npaths; i++) { jstring pathUTF = (jstring) ((*env)->GetObjectArrayElement(env, paths, i)); if (pathUTF != NULL) { const char *path = (*env)->GetStringUTFChars(env, pathUTF, NULL); zfs_handle_t *zhp = zfs_open(g_zfs, path, ZFS_TYPE_DATASET); if (zhp != NULL) { /* Add all dependents of this Dataset to list */ (void) zfs_iter_dependents(zhp, B_FALSE, zjni_create_add_Dataset, &data); /* Add this Dataset to list (and close zhp) */ (void) zjni_create_add_Dataset(zhp, &data); } else if (is_pool_name(path)) { /* * Path is not a dataset - * see if it's a faulted pool */ zpool_handle_t *zphp = zpool_open_canfail(g_zfs, path); if (zphp != NULL) { /* * Add this Pool to list (and * close zphp) */ (void) zjni_create_add_Pool(zphp, &data.data); } } (*env)->ReleaseStringUTFChars(env, pathUTF, path); } } if ((*env)->ExceptionOccurred(env) == NULL) { array = zjni_Collection_to_array(env, (zjni_Collection_t *)list, ZFSJNI_PACKAGE_DATA "Dataset"); } return (array); } /* * Gets a Dataset of the given name and type, or NULL if no such * Dataset exists. */ jobject zjni_get_Dataset(JNIEnv *env, jstring nameUTF, zfs_type_t typemask) { jobject device = NULL; const char *name = (*env)->GetStringUTFChars(env, nameUTF, NULL); zfs_handle_t *zhp = zfs_open(g_zfs, name, typemask); if ((typemask & ZFS_TYPE_FILESYSTEM) && is_pool_name(name)) { zpool_handle_t *zphp = zpool_open_canfail(g_zfs, name); if (zphp != NULL) { device = create_PoolBean(env, zphp, zhp); zpool_close(zphp); } } else if (zhp != NULL) { /* Creates a Dataset object of the appropriate class */ device = create_DatasetBean(env, zhp); } if (zhp != NULL) { zfs_close(zhp); } (*env)->ReleaseStringUTFChars(env, nameUTF, name); return (device); }