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.
20 #include <sys/lua/lua.h>
21 #include <sys/lua/lualib.h>
22 #include <sys/lua/lauxlib.h>
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/zcp_prop.h>
38 #include <sys/zfs_ioctl.h>
39 #include <sys/zfs_znode.h>
43 #include <sys/zfs_quota.h>
44 #include <sys/zfs_vfsops.h>
48 get_objset_type(dsl_dataset_t
*ds
, zfs_type_t
*type
)
52 error
= dmu_objset_from_ds(ds
, &os
);
55 if (ds
->ds_is_snapshot
) {
56 *type
= ZFS_TYPE_SNAPSHOT
;
58 switch (os
->os_phys
->os_type
) {
60 *type
= ZFS_TYPE_FILESYSTEM
;
63 *type
= ZFS_TYPE_VOLUME
;
73 * Returns the string name of ds's type in str (a buffer which should be
74 * at least 12 bytes long).
77 get_objset_type_name(dsl_dataset_t
*ds
, char *str
)
79 zfs_type_t type
= ZFS_TYPE_INVALID
;
80 int error
= get_objset_type(ds
, &type
);
84 case ZFS_TYPE_SNAPSHOT
:
85 (void) strlcpy(str
, "snapshot", ZAP_MAXVALUELEN
);
87 case ZFS_TYPE_FILESYSTEM
:
88 (void) strlcpy(str
, "filesystem", ZAP_MAXVALUELEN
);
91 (void) strlcpy(str
, "volume", ZAP_MAXVALUELEN
);
100 * Determines the source of a property given its setpoint and
101 * property type. It pushes the source to the lua stack.
104 get_prop_src(lua_State
*state
, const char *setpoint
, zfs_prop_t prop
)
106 if (zfs_prop_readonly(prop
) || (prop
== ZFS_PROP_VERSION
)) {
110 if (strcmp("", setpoint
) == 0) {
115 (void) lua_pushstring(state
, src
);
120 * Given an error encountered while getting properties, either longjmp's for
121 * a fatal error or pushes nothing to the stack for a non fatal one.
124 zcp_handle_error(lua_State
*state
, const char *dataset_name
,
125 const char *property_name
, int error
)
127 ASSERT3S(error
, !=, 0);
128 if (error
== ENOENT
) {
130 } else if (error
== EINVAL
) {
131 return (luaL_error(state
,
132 "property '%s' is not a valid property on dataset '%s'",
133 property_name
, dataset_name
));
134 } else if (error
== EIO
) {
135 return (luaL_error(state
,
136 "I/O error while retrieving property '%s' on dataset '%s'",
137 property_name
, dataset_name
));
139 return (luaL_error(state
, "unexpected error %d while "
140 "retrieving property '%s' on dataset '%s'",
141 error
, property_name
, dataset_name
));
146 * Look up a user defined property in the zap object. If it exists, push it
147 * and the setpoint onto the stack, otherwise don't push anything.
150 zcp_get_user_prop(lua_State
*state
, dsl_pool_t
*dp
, const char *dataset_name
,
151 const char *property_name
)
155 char setpoint
[ZFS_MAX_DATASET_NAME_LEN
];
157 * zcp_dataset_hold will either successfully return the requested
158 * dataset or throw a lua error and longjmp out of the zfs.get_prop call
161 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, dataset_name
, FTAG
);
163 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
165 buf
= kmem_alloc(ZAP_MAXVALUELEN
, KM_SLEEP
);
166 error
= dsl_prop_get_ds(ds
, property_name
, 1, ZAP_MAXVALUELEN
,
168 dsl_dataset_rele(ds
, FTAG
);
171 kmem_free(buf
, ZAP_MAXVALUELEN
);
172 return (zcp_handle_error(state
, dataset_name
, property_name
,
175 (void) lua_pushstring(state
, buf
);
176 (void) lua_pushstring(state
, setpoint
);
177 kmem_free(buf
, ZAP_MAXVALUELEN
);
182 * Check if the property we're looking for is stored in the ds_dir. If so,
183 * return it in the 'val' argument. Return 0 on success and ENOENT and if
184 * the property is not present.
187 get_dsl_dir_prop(dsl_dataset_t
*ds
, zfs_prop_t zfs_prop
,
190 dsl_dir_t
*dd
= ds
->ds_dir
;
191 mutex_enter(&dd
->dd_lock
);
193 case ZFS_PROP_USEDSNAP
:
194 *val
= dsl_dir_get_usedsnap(dd
);
196 case ZFS_PROP_USEDCHILD
:
197 *val
= dsl_dir_get_usedchild(dd
);
199 case ZFS_PROP_USEDDS
:
200 *val
= dsl_dir_get_usedds(dd
);
202 case ZFS_PROP_USEDREFRESERV
:
203 *val
= dsl_dir_get_usedrefreserv(dd
);
205 case ZFS_PROP_LOGICALUSED
:
206 *val
= dsl_dir_get_logicalused(dd
);
209 mutex_exit(&dd
->dd_lock
);
210 return (SET_ERROR(ENOENT
));
212 mutex_exit(&dd
->dd_lock
);
217 * Check if the property we're looking for is stored at the dsl_dataset or
218 * dsl_dir level. If so, push the property value and source onto the lua stack
219 * and return 0. If it is not present or a failure occurs in lookup, return a
220 * non-zero error value.
223 get_special_prop(lua_State
*state
, dsl_dataset_t
*ds
, const char *dsname
,
229 char *strval
= kmem_alloc(ZAP_MAXVALUELEN
, KM_SLEEP
);
230 char setpoint
[ZFS_MAX_DATASET_NAME_LEN
] =
231 "Internal error - setpoint not determined";
232 zfs_type_t ds_type
= ZFS_TYPE_INVALID
;
233 zprop_type_t prop_type
= zfs_prop_get_type(zfs_prop
);
234 (void) get_objset_type(ds
, &ds_type
);
237 case ZFS_PROP_REFRATIO
:
238 numval
= dsl_get_refratio(ds
);
241 numval
= dsl_get_used(ds
);
243 case ZFS_PROP_CLONES
: {
244 nvlist_t
*clones
= fnvlist_alloc();
245 error
= get_clones_stat_impl(ds
, clones
);
247 /* push list to lua stack */
248 VERIFY0(zcp_nvlist_to_lua(state
, clones
, NULL
, 0ULL));
250 (void) lua_pushnil(state
);
253 kmem_free(strval
, ZAP_MAXVALUELEN
);
256 case ZFS_PROP_COMPRESSRATIO
:
257 numval
= dsl_get_compressratio(ds
);
259 case ZFS_PROP_CREATION
:
260 numval
= dsl_get_creation(ds
);
262 case ZFS_PROP_REFERENCED
:
263 numval
= dsl_get_referenced(ds
);
265 case ZFS_PROP_AVAILABLE
:
266 numval
= dsl_get_available(ds
);
268 case ZFS_PROP_LOGICALREFERENCED
:
269 numval
= dsl_get_logicalreferenced(ds
);
271 case ZFS_PROP_CREATETXG
:
272 numval
= dsl_get_creationtxg(ds
);
275 numval
= dsl_get_guid(ds
);
277 case ZFS_PROP_UNIQUE
:
278 numval
= dsl_get_unique(ds
);
280 case ZFS_PROP_OBJSETID
:
281 numval
= dsl_get_objsetid(ds
);
283 case ZFS_PROP_ORIGIN
:
284 dsl_dir_get_origin(ds
->ds_dir
, strval
);
286 case ZFS_PROP_USERACCOUNTING
:
287 error
= dmu_objset_from_ds(ds
, &os
);
289 numval
= dmu_objset_userspace_present(os
);
291 case ZFS_PROP_WRITTEN
:
292 error
= dsl_get_written(ds
, &numval
);
295 error
= get_objset_type_name(ds
, strval
);
297 case ZFS_PROP_PREV_SNAP
:
298 error
= dsl_get_prev_snap(ds
, strval
);
301 dsl_dataset_name(ds
, strval
);
303 case ZFS_PROP_MOUNTPOINT
:
304 error
= dsl_get_mountpoint(ds
, dsname
, strval
, setpoint
);
306 case ZFS_PROP_VERSION
:
307 /* should be a snapshot or filesystem */
308 ASSERT(ds_type
!= ZFS_TYPE_VOLUME
);
309 error
= dmu_objset_from_ds(ds
, &os
);
310 /* look in the master node for the version */
312 error
= zap_lookup(os
, MASTER_NODE_OBJ
, ZPL_VERSION_STR
,
313 sizeof (numval
), 1, &numval
);
316 case ZFS_PROP_DEFER_DESTROY
:
317 numval
= dsl_get_defer_destroy(ds
);
319 case ZFS_PROP_USERREFS
:
320 numval
= dsl_get_userrefs(ds
);
322 case ZFS_PROP_FILESYSTEM_COUNT
:
323 error
= dsl_dir_get_filesystem_count(ds
->ds_dir
, &numval
);
324 (void) strlcpy(setpoint
, "", ZFS_MAX_DATASET_NAME_LEN
);
326 case ZFS_PROP_SNAPSHOT_COUNT
:
327 error
= dsl_dir_get_snapshot_count(ds
->ds_dir
, &numval
);
328 (void) strlcpy(setpoint
, "", ZFS_MAX_DATASET_NAME_LEN
);
330 case ZFS_PROP_NUMCLONES
:
331 numval
= dsl_get_numclones(ds
);
333 case ZFS_PROP_INCONSISTENT
:
334 numval
= dsl_get_inconsistent(ds
);
336 case ZFS_PROP_IVSET_GUID
:
337 if (dsl_dataset_is_zapified(ds
)) {
338 error
= zap_lookup(ds
->ds_dir
->dd_pool
->dp_meta_objset
,
339 ds
->ds_object
, DS_FIELD_IVSET_GUID
,
340 sizeof (numval
), 1, &numval
);
345 case ZFS_PROP_RECEIVE_RESUME_TOKEN
: {
346 char *token
= get_receive_resume_token(ds
);
348 (void) strlcpy(strval
, token
, ZAP_MAXVALUELEN
);
355 case ZFS_PROP_VOLSIZE
:
356 ASSERT(ds_type
== ZFS_TYPE_VOLUME
||
357 ds_type
== ZFS_TYPE_SNAPSHOT
);
358 error
= dmu_objset_from_ds(ds
, &os
);
360 error
= zap_lookup(os
, ZVOL_ZAP_OBJ
, "size",
361 sizeof (numval
), 1, &numval
);
364 (void) strlcpy(setpoint
, dsname
,
365 ZFS_MAX_DATASET_NAME_LEN
);
368 case ZFS_PROP_VOLBLOCKSIZE
: {
369 ASSERT(ds_type
== ZFS_TYPE_VOLUME
);
370 dmu_object_info_t doi
;
371 error
= dmu_objset_from_ds(ds
, &os
);
373 error
= dmu_object_info(os
, ZVOL_OBJ
, &doi
);
375 numval
= doi
.doi_data_block_size
;
380 case ZFS_PROP_KEYSTATUS
:
381 case ZFS_PROP_KEYFORMAT
: {
382 /* provide defaults in case no crypto obj exists */
384 if (zfs_prop
== ZFS_PROP_KEYSTATUS
)
385 numval
= ZFS_KEYSTATUS_NONE
;
387 numval
= ZFS_KEYFORMAT_NONE
;
389 nvlist_t
*nvl
, *propval
;
390 nvl
= fnvlist_alloc();
391 dsl_dataset_crypt_stats(ds
, nvl
);
392 if (nvlist_lookup_nvlist(nvl
, zfs_prop_to_name(zfs_prop
),
396 (void) nvlist_lookup_uint64(propval
, ZPROP_VALUE
,
398 if (nvlist_lookup_string(propval
, ZPROP_SOURCE
,
400 strlcpy(setpoint
, source
, sizeof (setpoint
));
406 case ZFS_PROP_SNAPSHOTS_CHANGED
:
407 numval
= dsl_dir_snap_cmtime(ds
->ds_dir
).tv_sec
;
411 /* Did not match these props, check in the dsl_dir */
412 error
= get_dsl_dir_prop(ds
, zfs_prop
, &numval
);
415 kmem_free(strval
, ZAP_MAXVALUELEN
);
420 case PROP_TYPE_NUMBER
: {
421 (void) lua_pushnumber(state
, numval
);
424 case PROP_TYPE_STRING
: {
425 (void) lua_pushstring(state
, strval
);
428 case PROP_TYPE_INDEX
: {
430 error
= zfs_prop_index_to_string(zfs_prop
, numval
, &propval
);
432 kmem_free(strval
, ZAP_MAXVALUELEN
);
435 (void) lua_pushstring(state
, propval
);
439 kmem_free(strval
, ZAP_MAXVALUELEN
);
441 /* Push the source to the stack */
442 get_prop_src(state
, setpoint
, zfs_prop
);
447 * Look up a property and its source in the zap object. If the value is
448 * present and successfully retrieved, push the value and source on the
449 * lua stack and return 0. On failure, return a non-zero error value.
452 get_zap_prop(lua_State
*state
, dsl_dataset_t
*ds
, zfs_prop_t zfs_prop
)
455 char setpoint
[ZFS_MAX_DATASET_NAME_LEN
];
456 char *strval
= kmem_alloc(ZAP_MAXVALUELEN
, KM_SLEEP
);
458 const char *prop_name
= zfs_prop_to_name(zfs_prop
);
459 zprop_type_t prop_type
= zfs_prop_get_type(zfs_prop
);
461 if (prop_type
== PROP_TYPE_STRING
) {
462 /* Push value to lua stack */
463 error
= dsl_prop_get_ds(ds
, prop_name
, 1,
464 ZAP_MAXVALUELEN
, strval
, setpoint
);
466 (void) lua_pushstring(state
, strval
);
468 error
= dsl_prop_get_ds(ds
, prop_name
, sizeof (numval
),
469 1, &numval
, setpoint
);
473 /* Fill in temporary value for prop, if applicable */
474 (void) zfs_get_temporary_prop(ds
, zfs_prop
, &numval
, setpoint
);
476 kmem_free(strval
, ZAP_MAXVALUELEN
);
477 return (luaL_error(state
,
478 "temporary properties only supported in kernel mode",
481 /* Push value to lua stack */
482 if (prop_type
== PROP_TYPE_INDEX
) {
484 error
= zfs_prop_index_to_string(zfs_prop
, numval
,
487 (void) lua_pushstring(state
, propval
);
490 (void) lua_pushnumber(state
, numval
);
494 kmem_free(strval
, ZAP_MAXVALUELEN
);
496 get_prop_src(state
, setpoint
, zfs_prop
);
501 * Determine whether property is valid for a given dataset
504 prop_valid_for_ds(dsl_dataset_t
*ds
, zfs_prop_t zfs_prop
)
506 zfs_type_t zfs_type
= ZFS_TYPE_INVALID
;
508 /* properties not supported */
509 if ((zfs_prop
== ZFS_PROP_ISCSIOPTIONS
) ||
510 (zfs_prop
== ZFS_PROP_MOUNTED
))
513 /* if we want the origin prop, ds must be a clone */
514 if ((zfs_prop
== ZFS_PROP_ORIGIN
) && (!dsl_dir_is_clone(ds
->ds_dir
)))
517 int error
= get_objset_type(ds
, &zfs_type
);
520 return (zfs_prop_valid_for_type(zfs_prop
, zfs_type
, B_FALSE
));
524 * Look up a given dataset property. On success return 2, the number of
525 * values pushed to the lua stack (property value and source). On a fatal
526 * error, longjmp. On a non fatal error push nothing.
529 zcp_get_system_prop(lua_State
*state
, dsl_pool_t
*dp
, const char *dataset_name
,
534 * zcp_dataset_hold will either successfully return the requested
535 * dataset or throw a lua error and longjmp out of the zfs.get_prop call
538 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, dataset_name
, FTAG
);
540 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
542 /* Check that the property is valid for the given dataset */
543 const char *prop_name
= zfs_prop_to_name(zfs_prop
);
544 if (!prop_valid_for_ds(ds
, zfs_prop
)) {
545 dsl_dataset_rele(ds
, FTAG
);
549 /* Check if the property can be accessed directly */
550 error
= get_special_prop(state
, ds
, dataset_name
, zfs_prop
);
552 dsl_dataset_rele(ds
, FTAG
);
553 /* The value and source have been pushed by get_special_prop */
556 if (error
!= ENOENT
) {
557 dsl_dataset_rele(ds
, FTAG
);
558 return (zcp_handle_error(state
, dataset_name
,
562 /* If we were unable to find it, look in the zap object */
563 error
= get_zap_prop(state
, ds
, zfs_prop
);
564 dsl_dataset_rele(ds
, FTAG
);
566 return (zcp_handle_error(state
, dataset_name
,
569 /* The value and source have been pushed by get_zap_prop */
574 static zfs_userquota_prop_t
575 get_userquota_prop(const char *prop_name
)
577 zfs_userquota_prop_t type
;
578 /* Figure out the property type ({user|group}{quota|used}) */
579 for (type
= 0; type
< ZFS_NUM_USERQUOTA_PROPS
; type
++) {
580 if (strncmp(prop_name
, zfs_userquota_prop_prefixes
[type
],
581 strlen(zfs_userquota_prop_prefixes
[type
])) == 0)
588 * Given the name of a zfs_userquota_prop, this function determines the
589 * prop type as well as the numeric group/user ids based on the string
590 * following the '@' in the property name. On success, returns 0. On failure,
591 * returns a non-zero error.
592 * 'domain' must be free'd by caller using kmem_strfree()
595 parse_userquota_prop(const char *prop_name
, zfs_userquota_prop_t
*type
,
596 char **domain
, uint64_t *rid
)
598 char *cp
, *end
, *domain_val
;
600 *type
= get_userquota_prop(prop_name
);
601 if (*type
>= ZFS_NUM_USERQUOTA_PROPS
)
605 cp
= strchr(prop_name
, '@') + 1;
606 if (strncmp(cp
, "S-1-", 4) == 0) {
608 * It's a numeric SID (eg "S-1-234-567-89") and we want to
609 * separate the domain id and the rid
611 int domain_len
= strrchr(cp
, '-') - cp
;
612 domain_val
= kmem_alloc(domain_len
+ 1, KM_SLEEP
);
613 (void) strlcpy(domain_val
, cp
, domain_len
+ 1);
614 cp
+= domain_len
+ 1;
616 (void) ddi_strtoll(cp
, &end
, 10, (longlong_t
*)rid
);
618 kmem_strfree(domain_val
);
622 /* It's only a user/group ID (eg "12345"), just get the rid */
624 (void) ddi_strtoll(cp
, &end
, 10, (longlong_t
*)rid
);
628 *domain
= domain_val
;
633 * Look up {user|group}{quota|used} property for given dataset. On success
634 * push the value (quota or used amount) and the setpoint. On failure, push
638 zcp_get_userquota_prop(lua_State
*state
, dsl_pool_t
*dp
,
639 const char *dataset_name
, const char *prop_name
)
644 zfs_userquota_prop_t type
;
646 uint64_t rid
, value
= 0;
649 dsl_dataset_t
*ds
= zcp_dataset_hold(state
, dp
, dataset_name
, FTAG
);
651 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
653 error
= parse_userquota_prop(prop_name
, &type
, &domain
, &rid
);
655 error
= dmu_objset_from_ds(ds
, &os
);
657 zfsvfs
= kmem_zalloc(sizeof (zfsvfs_t
), KM_SLEEP
);
658 error
= zfsvfs_create_impl(&zfvp
, zfsvfs
, os
);
660 error
= zfs_userspace_one(zfvp
, type
, domain
,
666 kmem_strfree(domain
);
668 dsl_dataset_rele(ds
, FTAG
);
670 if ((value
== 0) && ((type
== ZFS_PROP_USERQUOTA
) ||
671 (type
== ZFS_PROP_GROUPQUOTA
)))
672 error
= SET_ERROR(ENOENT
);
674 return (zcp_handle_error(state
, dataset_name
,
678 (void) lua_pushnumber(state
, value
);
679 (void) lua_pushstring(state
, dataset_name
);
685 * Determines the name of the snapshot referenced in the written property
686 * name. Returns snapshot name in snap_name, a buffer that must be at least
687 * as large as ZFS_MAX_DATASET_NAME_LEN
690 parse_written_prop(const char *dataset_name
, const char *prop_name
,
693 ASSERT(zfs_prop_written(prop_name
));
694 const char *name
= prop_name
+ ZFS_WRITTEN_PROP_PREFIX_LEN
;
695 if (strchr(name
, '@') == NULL
) {
696 (void) snprintf(snap_name
, ZFS_MAX_DATASET_NAME_LEN
, "%s@%s",
699 (void) strlcpy(snap_name
, name
, ZFS_MAX_DATASET_NAME_LEN
);
704 * Look up written@ property for given dataset. On success
705 * push the value and the setpoint. If error is fatal, we will
706 * longjmp, otherwise push nothing.
709 zcp_get_written_prop(lua_State
*state
, dsl_pool_t
*dp
,
710 const char *dataset_name
, const char *prop_name
)
712 char snap_name
[ZFS_MAX_DATASET_NAME_LEN
];
713 uint64_t used
, comp
, uncomp
;
717 parse_written_prop(dataset_name
, prop_name
, snap_name
);
718 dsl_dataset_t
*new = zcp_dataset_hold(state
, dp
, dataset_name
, FTAG
);
720 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
722 error
= dsl_dataset_hold(dp
, snap_name
, FTAG
, &old
);
724 dsl_dataset_rele(new, FTAG
);
725 return (zcp_dataset_hold_error(state
, dp
, snap_name
,
728 error
= dsl_dataset_space_written(old
, new,
729 &used
, &comp
, &uncomp
);
731 dsl_dataset_rele(old
, FTAG
);
732 dsl_dataset_rele(new, FTAG
);
735 return (zcp_handle_error(state
, dataset_name
,
738 (void) lua_pushnumber(state
, used
);
739 (void) lua_pushstring(state
, dataset_name
);
743 static int zcp_get_prop(lua_State
*state
);
744 static const zcp_lib_info_t zcp_get_prop_info
= {
746 .func
= zcp_get_prop
,
748 { .za_name
= "dataset", .za_lua_type
= LUA_TSTRING
},
749 { .za_name
= "property", .za_lua_type
= LUA_TSTRING
},
758 zcp_get_prop(lua_State
*state
)
760 const char *dataset_name
;
761 const char *property_name
;
762 dsl_pool_t
*dp
= zcp_run_info(state
)->zri_pool
;
763 const zcp_lib_info_t
*libinfo
= &zcp_get_prop_info
;
765 zcp_parse_args(state
, libinfo
->name
, libinfo
->pargs
, libinfo
->kwargs
);
767 dataset_name
= lua_tostring(state
, 1);
768 property_name
= lua_tostring(state
, 2);
770 /* User defined property */
771 if (zfs_prop_user(property_name
)) {
772 return (zcp_get_user_prop(state
, dp
,
773 dataset_name
, property_name
));
775 /* userspace property */
776 if (zfs_prop_userquota(property_name
)) {
778 return (zcp_get_userquota_prop(state
, dp
,
779 dataset_name
, property_name
));
781 return (luaL_error(state
,
782 "user quota properties only supported in kernel mode",
786 /* written@ property */
787 if (zfs_prop_written(property_name
)) {
788 return (zcp_get_written_prop(state
, dp
,
789 dataset_name
, property_name
));
792 zfs_prop_t zfs_prop
= zfs_name_to_prop(property_name
);
793 /* Valid system property */
794 if (zfs_prop
!= ZPROP_INVAL
) {
795 return (zcp_get_system_prop(state
, dp
, dataset_name
,
799 /* Invalid property name */
800 return (luaL_error(state
,
801 "'%s' is not a valid property", property_name
));
805 zcp_load_get_lib(lua_State
*state
)
807 lua_pushcclosure(state
, zcp_get_prop_info
.func
, 0);
808 lua_setfield(state
, -2, zcp_get_prop_info
.name
);