4 * This file and its contents are supplied under the terms of the
5 * Common Development and Distribution License ("CDDL"), version 1.0.
6 * You may only use this file in accordance with the terms of version
9 * A full copy of the text of the CDDL should have accompanied this
10 * source. A copy of the CDDL is also available via the Internet at
11 * http://www.illumos.org/license/CDDL.
17 * Copyright (c) 2016 by Delphix. All rights reserved.
26 #include <sys/dsl_prop.h>
27 #include <sys/dsl_synctask.h>
28 #include <sys/dsl_dataset.h>
29 #include <sys/dsl_dir.h>
30 #include <sys/dmu_objset.h>
31 #include <sys/mntent.h>
32 #include <sys/sunddi.h>
35 #include <sys/zcp_iter.h>
36 #include <sys/zcp_global.h>
37 #include <sys/zfs_ioctl.h>
38 #include <sys/zfs_znode.h>
42 #include <sys/zfs_vfsops.h>
46 get_objset_type(dsl_dataset_t
*ds
, zfs_type_t
*type
)
50 error
= dmu_objset_from_ds(ds
, &os
);
53 if (ds
->ds_is_snapshot
) {
54 *type
= ZFS_TYPE_SNAPSHOT
;
56 switch (os
->os_phys
->os_type
) {
58 *type
= ZFS_TYPE_FILESYSTEM
;
61 *type
= ZFS_TYPE_VOLUME
;
71 * Returns the string name of ds's type in str (a buffer which should be
72 * at least 12 bytes long).
75 get_objset_type_name(dsl_dataset_t
*ds
, char *str
)
79 error
= get_objset_type(ds
, &type
);
83 case ZFS_TYPE_SNAPSHOT
:
84 (void) strcpy(str
, "snapshot");
86 case ZFS_TYPE_FILESYSTEM
:
87 (void) strcpy(str
, "filesystem");
90 (void) strcpy(str
, "volume");
99 * Determines the source of a property given its setpoint and
100 * property type. It pushes the source to the lua stack.
103 get_prop_src(lua_State
*state
, const char *setpoint
, zfs_prop_t prop
)
105 if (zfs_prop_readonly(prop
) || (prop
== ZFS_PROP_VERSION
)) {
109 if (strcmp("", setpoint
) == 0) {
114 (void) lua_pushstring(state
, src
);
119 * Given an error encountered while getting properties, either longjmp's for
120 * a fatal error or pushes nothing to the stack for a non fatal one.
123 zcp_handle_error(lua_State
*state
, const char *dataset_name
,
124 const char *property_name
, int error
)
126 ASSERT3S(error
, !=, 0);
127 if (error
== ENOENT
) {
129 } else if (error
== EINVAL
) {
130 return (luaL_error(state
,
131 "property '%s' is not a valid property on dataset '%s'",
132 property_name
, dataset_name
));
133 } else if (error
== EIO
) {
134 return (luaL_error(state
,
135 "I/O error while retrieving property '%s' on dataset '%s'",
136 property_name
, dataset_name
));
138 return (luaL_error(state
, "unexpected error %d while "
139 "retrieving property '%s' on dataset '%s'",
140 error
, property_name
, dataset_name
));
145 * Look up a user defined property in the zap object. If it exists, push it
146 * and the setpoint onto the stack, otherwise don't push anything.
149 zcp_get_user_prop(lua_State
*state
, dsl_pool_t
*dp
, const char *dataset_name
,
150 const char *property_name
)
154 char setpoint
[ZFS_MAX_DATASET_NAME_LEN
];
156 * zcp_dataset_hold will either successfully return the requested
157 * dataset or throw a lua error and longjmp out of the zfs.get_prop call
160 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, dataset_name
, FTAG
);
162 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
164 buf
= kmem_alloc(ZAP_MAXVALUELEN
, KM_SLEEP
);
165 error
= dsl_prop_get_ds(ds
, property_name
, 1, ZAP_MAXVALUELEN
,
167 dsl_dataset_rele(ds
, FTAG
);
170 kmem_free(buf
, ZAP_MAXVALUELEN
);
171 return (zcp_handle_error(state
, dataset_name
, property_name
,
174 (void) lua_pushstring(state
, buf
);
175 (void) lua_pushstring(state
, setpoint
);
176 kmem_free(buf
, ZAP_MAXVALUELEN
);
181 * Check if the property we're looking for is stored in the ds_dir. If so,
182 * return it in the 'val' argument. Return 0 on success and ENOENT and if
183 * the property is not present.
186 get_dsl_dir_prop(dsl_dataset_t
*ds
, zfs_prop_t zfs_prop
,
189 dsl_dir_t
*dd
= ds
->ds_dir
;
190 mutex_enter(&dd
->dd_lock
);
192 case ZFS_PROP_USEDSNAP
:
193 *val
= dsl_dir_get_usedsnap(dd
);
195 case ZFS_PROP_USEDCHILD
:
196 *val
= dsl_dir_get_usedchild(dd
);
198 case ZFS_PROP_USEDDS
:
199 *val
= dsl_dir_get_usedds(dd
);
201 case ZFS_PROP_USEDREFRESERV
:
202 *val
= dsl_dir_get_usedrefreserv(dd
);
204 case ZFS_PROP_LOGICALUSED
:
205 *val
= dsl_dir_get_logicalused(dd
);
208 mutex_exit(&dd
->dd_lock
);
211 mutex_exit(&dd
->dd_lock
);
216 * Takes a dataset, a property, a value and that value's setpoint as
217 * found in the ZAP. Checks if the property has been changed in the vfs.
218 * If so, val and setpoint will be overwritten with updated content.
219 * Otherwise, they are left unchanged.
222 get_temporary_prop(dsl_dataset_t
*ds
, zfs_prop_t zfs_prop
, uint64_t *val
,
234 error
= dmu_objset_from_ds(ds
, &os
);
238 error
= getzfsvfs_impl(os
, &zfvp
);
246 if (vfs_optionisset(vfsp
, MNTOPT_NOATIME
, NULL
))
248 if (vfs_optionisset(vfsp
, MNTOPT_ATIME
, NULL
))
251 case ZFS_PROP_DEVICES
:
252 if (vfs_optionisset(vfsp
, MNTOPT_NODEVICES
, NULL
))
254 if (vfs_optionisset(vfsp
, MNTOPT_DEVICES
, NULL
))
258 if (vfs_optionisset(vfsp
, MNTOPT_NOEXEC
, NULL
))
260 if (vfs_optionisset(vfsp
, MNTOPT_EXEC
, NULL
))
263 case ZFS_PROP_SETUID
:
264 if (vfs_optionisset(vfsp
, MNTOPT_NOSETUID
, NULL
))
266 if (vfs_optionisset(vfsp
, MNTOPT_SETUID
, NULL
))
269 case ZFS_PROP_READONLY
:
270 if (vfs_optionisset(vfsp
, MNTOPT_RW
, NULL
))
272 if (vfs_optionisset(vfsp
, MNTOPT_RO
, NULL
))
276 if (vfs_optionisset(vfsp
, MNTOPT_NOXATTR
, NULL
))
278 if (vfs_optionisset(vfsp
, MNTOPT_XATTR
, NULL
))
281 case ZFS_PROP_NBMAND
:
282 if (vfs_optionisset(vfsp
, MNTOPT_NONBMAND
, NULL
))
284 if (vfs_optionisset(vfsp
, MNTOPT_NBMAND
, NULL
))
294 (void) strcpy(setpoint
, "temporary");
302 * Check if the property we're looking for is stored at the dsl_dataset or
303 * dsl_dir level. If so, push the property value and source onto the lua stack
304 * and return 0. If it is not present or a failure occurs in lookup, return a
305 * non-zero error value.
308 get_special_prop(lua_State
*state
, dsl_dataset_t
*ds
, const char *dsname
,
314 char *strval
= kmem_alloc(ZAP_MAXVALUELEN
, KM_SLEEP
);
315 char setpoint
[ZFS_MAX_DATASET_NAME_LEN
] =
316 "Internal error - setpoint not determined";
318 zprop_type_t prop_type
= zfs_prop_get_type(zfs_prop
);
319 (void) get_objset_type(ds
, &ds_type
);
322 case ZFS_PROP_REFRATIO
:
323 numval
= dsl_get_refratio(ds
);
326 numval
= dsl_get_used(ds
);
328 case ZFS_PROP_CLONES
: {
329 nvlist_t
*clones
= fnvlist_alloc();
330 error
= get_clones_stat_impl(ds
, clones
);
332 /* push list to lua stack */
333 VERIFY0(zcp_nvlist_to_lua(state
, clones
, NULL
,
336 (void) lua_pushnil(state
);
339 kmem_free(strval
, ZAP_MAXVALUELEN
);
342 case ZFS_PROP_COMPRESSRATIO
:
343 numval
= dsl_get_compressratio(ds
);
345 case ZFS_PROP_CREATION
:
346 numval
= dsl_get_creation(ds
);
348 case ZFS_PROP_REFERENCED
:
349 numval
= dsl_get_referenced(ds
);
351 case ZFS_PROP_AVAILABLE
:
352 numval
= dsl_get_available(ds
);
354 case ZFS_PROP_LOGICALREFERENCED
:
355 numval
= dsl_get_logicalreferenced(ds
);
357 case ZFS_PROP_CREATETXG
:
358 numval
= dsl_get_creationtxg(ds
);
361 numval
= dsl_get_guid(ds
);
363 case ZFS_PROP_UNIQUE
:
364 numval
= dsl_get_unique(ds
);
366 case ZFS_PROP_OBJSETID
:
367 numval
= dsl_get_objsetid(ds
);
369 case ZFS_PROP_ORIGIN
:
370 dsl_dir_get_origin(ds
->ds_dir
, strval
);
372 case ZFS_PROP_USERACCOUNTING
:
373 error
= dmu_objset_from_ds(ds
, &os
);
375 numval
= dmu_objset_userspace_present(os
);
377 case ZFS_PROP_WRITTEN
:
378 error
= dsl_get_written(ds
, &numval
);
381 error
= get_objset_type_name(ds
, strval
);
383 case ZFS_PROP_PREV_SNAP
:
384 error
= dsl_get_prev_snap(ds
, strval
);
387 dsl_dataset_name(ds
, strval
);
389 case ZFS_PROP_MOUNTPOINT
:
390 error
= dsl_get_mountpoint(ds
, dsname
, strval
, setpoint
);
392 case ZFS_PROP_VERSION
:
393 /* should be a snapshot or filesystem */
394 ASSERT(ds_type
!= ZFS_TYPE_VOLUME
);
395 error
= dmu_objset_from_ds(ds
, &os
);
396 /* look in the master node for the version */
398 error
= zap_lookup(os
, MASTER_NODE_OBJ
, ZPL_VERSION_STR
,
399 sizeof (numval
), 1, &numval
);
402 case ZFS_PROP_DEFER_DESTROY
:
403 numval
= dsl_get_defer_destroy(ds
);
405 case ZFS_PROP_USERREFS
:
406 numval
= dsl_get_userrefs(ds
);
408 case ZFS_PROP_FILESYSTEM_COUNT
:
409 error
= dsl_dir_get_filesystem_count(ds
->ds_dir
, &numval
);
410 (void) strcpy(setpoint
, "");
412 case ZFS_PROP_SNAPSHOT_COUNT
:
413 error
= dsl_dir_get_snapshot_count(ds
->ds_dir
, &numval
);
414 (void) strcpy(setpoint
, "");
416 case ZFS_PROP_REMAPTXG
:
417 error
= dsl_dir_get_remaptxg(ds
->ds_dir
, &numval
);
419 case ZFS_PROP_NUMCLONES
:
420 numval
= dsl_get_numclones(ds
);
422 case ZFS_PROP_INCONSISTENT
:
423 numval
= dsl_get_inconsistent(ds
);
425 case ZFS_PROP_RECEIVE_RESUME_TOKEN
:
426 VERIFY3U(strlcpy(strval
, get_receive_resume_stats_impl(ds
),
427 ZAP_MAXVALUELEN
), <, ZAP_MAXVALUELEN
);
428 if (strcmp(strval
, "") == 0) {
429 VERIFY3U(strlcpy(strval
, get_child_receive_stats(ds
),
430 ZAP_MAXVALUELEN
), <, ZAP_MAXVALUELEN
);
431 if (strcmp(strval
, "") == 0)
435 case ZFS_PROP_VOLSIZE
:
436 ASSERT(ds_type
== ZFS_TYPE_VOLUME
);
437 error
= dmu_objset_from_ds(ds
, &os
);
439 error
= zap_lookup(os
, ZVOL_ZAP_OBJ
, "size",
440 sizeof (numval
), 1, &numval
);
443 (void) strcpy(setpoint
, dsname
);
446 case ZFS_PROP_VOLBLOCKSIZE
: {
447 ASSERT(ds_type
== ZFS_TYPE_VOLUME
);
448 dmu_object_info_t doi
;
449 error
= dmu_objset_from_ds(ds
, &os
);
451 error
= dmu_object_info(os
, ZVOL_OBJ
, &doi
);
453 numval
= doi
.doi_data_block_size
;
458 /* Did not match these props, check in the dsl_dir */
459 error
= get_dsl_dir_prop(ds
, zfs_prop
, &numval
);
462 kmem_free(strval
, ZAP_MAXVALUELEN
);
467 case PROP_TYPE_NUMBER
: {
468 (void) lua_pushnumber(state
, numval
);
471 case PROP_TYPE_STRING
: {
472 (void) lua_pushstring(state
, strval
);
475 case PROP_TYPE_INDEX
: {
477 error
= zfs_prop_index_to_string(zfs_prop
, numval
, &propval
);
479 kmem_free(strval
, ZAP_MAXVALUELEN
);
482 (void) lua_pushstring(state
, propval
);
486 kmem_free(strval
, ZAP_MAXVALUELEN
);
488 /* Push the source to the stack */
489 get_prop_src(state
, setpoint
, zfs_prop
);
494 * Look up a property and its source in the zap object. If the value is
495 * present and successfully retrieved, push the value and source on the
496 * lua stack and return 0. On failure, return a non-zero error value.
499 get_zap_prop(lua_State
*state
, dsl_dataset_t
*ds
, zfs_prop_t zfs_prop
)
502 char setpoint
[ZFS_MAX_DATASET_NAME_LEN
];
503 char *strval
= kmem_alloc(ZAP_MAXVALUELEN
, KM_SLEEP
);
505 const char *prop_name
= zfs_prop_to_name(zfs_prop
);
506 zprop_type_t prop_type
= zfs_prop_get_type(zfs_prop
);
508 if (prop_type
== PROP_TYPE_STRING
) {
509 /* Push value to lua stack */
510 error
= dsl_prop_get_ds(ds
, prop_name
, 1,
511 ZAP_MAXVALUELEN
, strval
, setpoint
);
513 (void) lua_pushstring(state
, strval
);
515 error
= dsl_prop_get_ds(ds
, prop_name
, sizeof (numval
),
516 1, &numval
, setpoint
);
518 /* Fill in temorary value for prop, if applicable */
519 (void) get_temporary_prop(ds
, zfs_prop
, &numval
, setpoint
);
521 /* Push value to lua stack */
522 if (prop_type
== PROP_TYPE_INDEX
) {
524 error
= zfs_prop_index_to_string(zfs_prop
, numval
,
527 (void) lua_pushstring(state
, propval
);
530 (void) lua_pushnumber(state
, numval
);
533 kmem_free(strval
, ZAP_MAXVALUELEN
);
535 get_prop_src(state
, setpoint
, zfs_prop
);
540 * Determine whether property is valid for a given dataset
543 prop_valid_for_ds(dsl_dataset_t
*ds
, zfs_prop_t zfs_prop
)
548 /* properties not supported */
549 if ((zfs_prop
== ZFS_PROP_ISCSIOPTIONS
) ||
550 (zfs_prop
== ZFS_PROP_MOUNTED
))
553 /* if we want the origin prop, ds must be a clone */
554 if ((zfs_prop
== ZFS_PROP_ORIGIN
) && (!dsl_dir_is_clone(ds
->ds_dir
)))
557 error
= get_objset_type(ds
, &zfs_type
);
560 return (zfs_prop_valid_for_type(zfs_prop
, zfs_type
));
564 * Look up a given dataset property. On success return 2, the number of
565 * values pushed to the lua stack (property value and source). On a fatal
566 * error, longjmp. On a non fatal error push nothing.
569 zcp_get_system_prop(lua_State
*state
, dsl_pool_t
*dp
, const char *dataset_name
,
574 * zcp_dataset_hold will either successfully return the requested
575 * dataset or throw a lua error and longjmp out of the zfs.get_prop call
578 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, dataset_name
, FTAG
);
580 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
582 /* Check that the property is valid for the given dataset */
583 const char *prop_name
= zfs_prop_to_name(zfs_prop
);
584 if (!prop_valid_for_ds(ds
, zfs_prop
)) {
585 dsl_dataset_rele(ds
, FTAG
);
589 /* Check if the property can be accessed directly */
590 error
= get_special_prop(state
, ds
, dataset_name
, zfs_prop
);
592 dsl_dataset_rele(ds
, FTAG
);
593 /* The value and source have been pushed by get_special_prop */
596 if (error
!= ENOENT
) {
597 dsl_dataset_rele(ds
, FTAG
);
598 return (zcp_handle_error(state
, dataset_name
,
602 /* If we were unable to find it, look in the zap object */
603 error
= get_zap_prop(state
, ds
, zfs_prop
);
604 dsl_dataset_rele(ds
, FTAG
);
606 return (zcp_handle_error(state
, dataset_name
,
609 /* The value and source have been pushed by get_zap_prop */
613 static zfs_userquota_prop_t
614 get_userquota_prop(const char *prop_name
)
616 zfs_userquota_prop_t type
;
617 /* Figure out the property type ({user|group}{quota|used}) */
618 for (type
= 0; type
< ZFS_NUM_USERQUOTA_PROPS
; type
++) {
619 if (strncmp(prop_name
, zfs_userquota_prop_prefixes
[type
],
620 strlen(zfs_userquota_prop_prefixes
[type
])) == 0)
628 * Given the name of a zfs_userquota_prop, this function determines the
629 * prop type as well as the numeric group/user ids based on the string
630 * following the '@' in the property name. On success, returns 0. On failure,
631 * returns a non-zero error.
632 * 'domain' must be free'd by caller using strfree()
635 parse_userquota_prop(const char *prop_name
, zfs_userquota_prop_t
*type
,
636 char **domain
, uint64_t *rid
)
638 char *cp
, *end
, *domain_val
;
640 *type
= get_userquota_prop(prop_name
);
641 if (*type
>= ZFS_NUM_USERQUOTA_PROPS
)
645 cp
= strchr(prop_name
, '@') + 1;
646 if (strncmp(cp
, "S-1-", 4) == 0) {
648 * It's a numeric SID (eg "S-1-234-567-89") and we want to
649 * seperate the domain id and the rid
651 int domain_len
= strrchr(cp
, '-') - cp
;
652 domain_val
= kmem_alloc(domain_len
+ 1, KM_SLEEP
);
653 (void) strncpy(domain_val
, cp
, domain_len
);
654 domain_val
[domain_len
] = '\0';
655 cp
+= domain_len
+ 1;
657 (void) ddi_strtoll(cp
, &end
, 10, (longlong_t
*)rid
);
663 /* It's only a user/group ID (eg "12345"), just get the rid */
665 (void) ddi_strtoll(cp
, &end
, 10, (longlong_t
*)rid
);
669 *domain
= domain_val
;
674 * Look up {user|group}{quota|used} property for given dataset. On success
675 * push the value (quota or used amount) and the setpoint. On failure, push
679 zcp_get_userquota_prop(lua_State
*state
, dsl_pool_t
*dp
,
680 const char *dataset_name
, const char *prop_name
)
685 zfs_userquota_prop_t type
;
690 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, dataset_name
, FTAG
);
692 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
694 error
= parse_userquota_prop(prop_name
, &type
, &domain
, &rid
);
696 error
= dmu_objset_from_ds(ds
, &os
);
698 zfsvfs
= kmem_zalloc(sizeof (zfsvfs_t
), KM_SLEEP
);
699 error
= zfsvfs_create_impl(&zfvp
, zfsvfs
, os
);
701 error
= zfs_userspace_one(zfvp
, type
, domain
,
709 dsl_dataset_rele(ds
, FTAG
);
711 if ((value
== 0) && ((type
== ZFS_PROP_USERQUOTA
) ||
712 (type
== ZFS_PROP_GROUPQUOTA
)))
715 return (zcp_handle_error(state
, dataset_name
,
719 (void) lua_pushnumber(state
, value
);
720 (void) lua_pushstring(state
, dataset_name
);
726 * Determines the name of the snapshot referenced in the written property
727 * name. Returns snapshot name in snap_name, a buffer that must be at least
728 * as large as ZFS_MAX_DATASET_NAME_LEN
731 parse_written_prop(const char *dataset_name
, const char *prop_name
,
734 ASSERT(zfs_prop_written(prop_name
));
735 const char *name
= prop_name
+ ZFS_WRITTEN_PROP_PREFIX_LEN
;
736 if (strchr(name
, '@') == NULL
) {
737 (void) sprintf(snap_name
, "%s@%s", dataset_name
, name
);
739 (void) strcpy(snap_name
, name
);
744 * Look up written@ property for given dataset. On success
745 * push the value and the setpoint. If error is fatal, we will
746 * longjmp, otherwise push nothing.
749 zcp_get_written_prop(lua_State
*state
, dsl_pool_t
*dp
,
750 const char *dataset_name
, const char *prop_name
)
752 char snap_name
[ZFS_MAX_DATASET_NAME_LEN
];
753 uint64_t used
, comp
, uncomp
;
757 parse_written_prop(dataset_name
, prop_name
, snap_name
);
758 dsl_dataset_t
*new = zcp_dataset_hold(state
, dp
, dataset_name
, FTAG
);
760 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
762 error
= dsl_dataset_hold(dp
, snap_name
, FTAG
, &old
);
764 dsl_dataset_rele(new, FTAG
);
765 return (zcp_dataset_hold_error(state
, dp
, snap_name
,
768 error
= dsl_dataset_space_written(old
, new,
769 &used
, &comp
, &uncomp
);
771 dsl_dataset_rele(old
, FTAG
);
772 dsl_dataset_rele(new, FTAG
);
775 return (zcp_handle_error(state
, dataset_name
,
778 (void) lua_pushnumber(state
, used
);
779 (void) lua_pushstring(state
, dataset_name
);
783 static int zcp_get_prop(lua_State
*state
);
784 static zcp_lib_info_t zcp_get_prop_info
= {
786 .func
= zcp_get_prop
,
788 { .za_name
= "dataset", .za_lua_type
= LUA_TSTRING
},
789 { .za_name
= "property", .za_lua_type
= LUA_TSTRING
},
798 zcp_get_prop(lua_State
*state
)
800 const char *dataset_name
;
801 const char *property_name
;
802 dsl_pool_t
*dp
= zcp_run_info(state
)->zri_pool
;
803 zcp_lib_info_t
*libinfo
= &zcp_get_prop_info
;
805 zcp_parse_args(state
, libinfo
->name
, libinfo
->pargs
, libinfo
->kwargs
);
807 dataset_name
= lua_tostring(state
, 1);
808 property_name
= lua_tostring(state
, 2);
810 /* User defined property */
811 if (zfs_prop_user(property_name
)) {
812 return (zcp_get_user_prop(state
, dp
,
813 dataset_name
, property_name
));
815 /* userspace property */
816 if (zfs_prop_userquota(property_name
)) {
818 return (zcp_get_userquota_prop(state
, dp
,
819 dataset_name
, property_name
));
821 return (luaL_error(state
,
822 "user quota properties only supported in kernel mode",
826 /* written@ property */
827 if (zfs_prop_written(property_name
)) {
828 return (zcp_get_written_prop(state
, dp
,
829 dataset_name
, property_name
));
832 zfs_prop_t zfs_prop
= zfs_name_to_prop(property_name
);
833 /* Valid system property */
834 if (zfs_prop
!= ZPROP_INVAL
) {
835 return (zcp_get_system_prop(state
, dp
, dataset_name
,
839 /* Invalid property name */
840 return (luaL_error(state
,
841 "'%s' is not a valid property", property_name
));
845 zcp_load_get_lib(lua_State
*state
)
847 lua_pushcclosure(state
, zcp_get_prop_info
.func
, 0);
848 lua_setfield(state
, -2, zcp_get_prop_info
.name
);