1323530Savg/*
2323530Savg * CDDL HEADER START
3323530Savg *
4323530Savg * This file and its contents are supplied under the terms of the
5323530Savg * Common Development and Distribution License ("CDDL"), version 1.0.
6323530Savg * You may only use this file in accordance with the terms of version
7323530Savg * 1.0 of the CDDL.
8323530Savg *
9323530Savg * A full copy of the text of the CDDL should have accompanied this
10323530Savg * source.  A copy of the CDDL is also available via the Internet at
11323530Savg * http://www.illumos.org/license/CDDL.
12323530Savg *
13323530Savg * CDDL HEADER END
14323530Savg */
15323530Savg
16323530Savg/*
17323530Savg * Copyright (c) 2016 by Delphix. All rights reserved.
18323530Savg */
19323530Savg
20323530Savg#include "lua.h"
21323530Savg#include "lualib.h"
22323530Savg#include "lauxlib.h"
23323530Savg
24323530Savg#include <zfs_prop.h>
25323530Savg
26323530Savg#include <sys/dsl_prop.h>
27323530Savg#include <sys/dsl_synctask.h>
28323530Savg#include <sys/dsl_dataset.h>
29323530Savg#include <sys/dsl_dir.h>
30323530Savg#include <sys/dmu_objset.h>
31323530Savg#include <sys/mntent.h>
32323530Savg#include <sys/sunddi.h>
33323530Savg#include <sys/zap.h>
34323530Savg#include <sys/zcp.h>
35323530Savg#include <sys/zcp_iter.h>
36323530Savg#include <sys/zcp_global.h>
37323530Savg#include <sys/zfs_ioctl.h>
38323530Savg#include <sys/zfs_znode.h>
39323530Savg#include <sys/zvol.h>
40323530Savg
41323530Savg#ifdef _KERNEL
42323530Savg#include <sys/zfs_vfsops.h>
43323530Savg#endif
44323530Savg
45323530Savgstatic int
46323530Savgget_objset_type(dsl_dataset_t *ds, zfs_type_t *type)
47323530Savg{
48323530Savg	int error;
49323530Savg	objset_t *os;
50323530Savg	error = dmu_objset_from_ds(ds, &os);
51323530Savg	if (error != 0)
52323530Savg		return (error);
53323530Savg	if (ds->ds_is_snapshot) {
54323530Savg		*type = ZFS_TYPE_SNAPSHOT;
55323530Savg	} else {
56323530Savg		switch (os->os_phys->os_type) {
57323530Savg		case DMU_OST_ZFS:
58323530Savg			*type = ZFS_TYPE_FILESYSTEM;
59323530Savg			break;
60323530Savg		case DMU_OST_ZVOL:
61323530Savg			*type = ZFS_TYPE_VOLUME;
62323530Savg			break;
63323530Savg		default:
64323530Savg			return (EINVAL);
65323530Savg		}
66323530Savg	}
67323530Savg	return (0);
68323530Savg}
69323530Savg
70323530Savg/*
71323530Savg * Returns the string name of ds's type in str (a buffer which should be
72323530Savg * at least 12 bytes long).
73323530Savg */
74323530Savgstatic int
75323530Savgget_objset_type_name(dsl_dataset_t *ds, char *str)
76323530Savg{
77323530Savg	int error;
78323530Savg	zfs_type_t type;
79323530Savg	error = get_objset_type(ds, &type);
80323530Savg	if (error != 0)
81323530Savg		return (error);
82323530Savg	switch (type) {
83323530Savg	case ZFS_TYPE_SNAPSHOT:
84323530Savg		(void) strcpy(str, "snapshot");
85323530Savg		break;
86323530Savg	case ZFS_TYPE_FILESYSTEM:
87323530Savg		(void) strcpy(str, "filesystem");
88323530Savg		break;
89323530Savg	case ZFS_TYPE_VOLUME:
90323530Savg		(void) strcpy(str, "volume");
91323530Savg		break;
92323530Savg	default:
93323530Savg		return (EINVAL);
94323530Savg	}
95323530Savg	return (0);
96323530Savg}
97323530Savg
98323530Savg/*
99323530Savg * Determines the source of a property given its setpoint and
100323530Savg * property type. It pushes the source to the lua stack.
101323530Savg */
102323530Savgstatic void
103323530Savgget_prop_src(lua_State *state, const char *setpoint, zfs_prop_t prop)
104323530Savg{
105323530Savg	if (zfs_prop_readonly(prop) || (prop == ZFS_PROP_VERSION)) {
106323530Savg		lua_pushnil(state);
107323530Savg	} else {
108323530Savg		const char *src;
109323530Savg		if (strcmp("", setpoint) == 0) {
110323530Savg			src = "default";
111323530Savg		} else {
112323530Savg			src = setpoint;
113323530Savg		}
114323530Savg		(void) lua_pushstring(state, src);
115323530Savg	}
116323530Savg}
117323530Savg
118323530Savg/*
119323530Savg * Given an error encountered while getting properties, either longjmp's for
120323530Savg * a fatal error or pushes nothing to the stack for a non fatal one.
121323530Savg */
122323530Savgstatic int
123323530Savgzcp_handle_error(lua_State *state, const char *dataset_name,
124323530Savg    const char *property_name, int error)
125323530Savg{
126323530Savg	ASSERT3S(error, !=, 0);
127323530Savg	if (error == ENOENT) {
128323530Savg		return (0);
129323530Savg	} else if (error == EINVAL) {
130323530Savg		return (luaL_error(state,
131323530Savg		    "property '%s' is not a valid property on dataset '%s'",
132323530Savg		    property_name, dataset_name));
133323530Savg	} else if (error == EIO) {
134323530Savg		return (luaL_error(state,
135323530Savg		    "I/O error while retrieving property '%s' on dataset '%s'",
136323530Savg		    property_name, dataset_name));
137323530Savg	} else {
138323530Savg		return (luaL_error(state, "unexpected error %d while "
139323530Savg		    "retrieving property '%s' on dataset '%s'",
140323530Savg		    error, property_name, dataset_name));
141323530Savg	}
142323530Savg}
143323530Savg
144323530Savg/*
145323530Savg * Look up a user defined property in the zap object. If it exists, push it
146323530Savg * and the setpoint onto the stack, otherwise don't push anything.
147323530Savg */
148323530Savgstatic int
149323530Savgzcp_get_user_prop(lua_State *state, dsl_pool_t *dp, const char *dataset_name,
150323530Savg    const char *property_name)
151323530Savg{
152323530Savg	int error;
153323530Savg	char *buf;
154323530Savg	char setpoint[ZFS_MAX_DATASET_NAME_LEN];
155323530Savg	/*
156323530Savg	 * zcp_dataset_hold will either successfully return the requested
157323530Savg	 * dataset or throw a lua error and longjmp out of the zfs.get_prop call
158323530Savg	 * without returning.
159323530Savg	 */
160323530Savg	dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG);
161323530Savg	if (ds == NULL)
162323530Savg		return (1); /* not reached; zcp_dataset_hold() longjmp'd */
163323530Savg
164323530Savg	buf = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP);
165323530Savg	error = dsl_prop_get_ds(ds, property_name, 1, ZAP_MAXVALUELEN,
166323530Savg	    buf, setpoint);
167323530Savg	dsl_dataset_rele(ds, FTAG);
168323530Savg
169323530Savg	if (error != 0) {
170323530Savg		kmem_free(buf, ZAP_MAXVALUELEN);
171323530Savg		return (zcp_handle_error(state, dataset_name, property_name,
172323530Savg		    error));
173323530Savg	}
174323530Savg	(void) lua_pushstring(state, buf);
175323530Savg	(void) lua_pushstring(state, setpoint);
176323530Savg	kmem_free(buf, ZAP_MAXVALUELEN);
177323530Savg	return (2);
178323530Savg}
179323530Savg
180323530Savg/*
181323530Savg * Check if the property we're looking for is stored in the ds_dir. If so,
182323530Savg * return it in the 'val' argument. Return 0 on success and ENOENT and if
183323530Savg * the property is not present.
184323530Savg */
185323530Savgstatic int
186323530Savgget_dsl_dir_prop(dsl_dataset_t *ds, zfs_prop_t zfs_prop,
187323530Savg    uint64_t *val)
188323530Savg{
189323530Savg	dsl_dir_t *dd = ds->ds_dir;
190323530Savg	mutex_enter(&dd->dd_lock);
191323530Savg	switch (zfs_prop) {
192323530Savg	case ZFS_PROP_USEDSNAP:
193323530Savg		*val = dsl_dir_get_usedsnap(dd);
194323530Savg		break;
195323530Savg	case ZFS_PROP_USEDCHILD:
196323530Savg		*val = dsl_dir_get_usedchild(dd);
197323530Savg		break;
198323530Savg	case ZFS_PROP_USEDDS:
199323530Savg		*val = dsl_dir_get_usedds(dd);
200323530Savg		break;
201323530Savg	case ZFS_PROP_USEDREFRESERV:
202323530Savg		*val = dsl_dir_get_usedrefreserv(dd);
203323530Savg		break;
204323530Savg	case ZFS_PROP_LOGICALUSED:
205323530Savg		*val = dsl_dir_get_logicalused(dd);
206323530Savg		break;
207323530Savg	default:
208323530Savg		mutex_exit(&dd->dd_lock);
209323530Savg		return (ENOENT);
210323530Savg	}
211323530Savg	mutex_exit(&dd->dd_lock);
212323530Savg	return (0);
213323530Savg}
214323530Savg
215323530Savg/*
216323530Savg * Takes a dataset, a property, a value and that value's setpoint as
217323530Savg * found in the ZAP. Checks if the property has been changed in the vfs.
218323530Savg * If so, val and setpoint will be overwritten with updated content.
219323530Savg * Otherwise, they are left unchanged.
220323530Savg */
221323530Savgstatic int
222323530Savgget_temporary_prop(dsl_dataset_t *ds, zfs_prop_t zfs_prop, uint64_t *val,
223323530Savg    char *setpoint)
224323530Savg{
225323530Savg#ifndef _KERNEL
226323530Savg	return (0);
227323530Savg#else
228323530Savg	int error;
229330990Savg#ifdef illumos
230323530Savg	zfsvfs_t *zfvp;
231330990Savg#endif
232323530Savg	vfs_t *vfsp;
233323530Savg	objset_t *os;
234323530Savg	uint64_t tmp = *val;
235323530Savg
236323530Savg	error = dmu_objset_from_ds(ds, &os);
237323530Savg	if (error != 0)
238323530Savg		return (error);
239323530Savg
240330990Savg	error = getzfsvfs_impl(os, &vfsp);
241323530Savg	if (error != 0)
242323530Savg		return (error);
243330990Savg#ifdef illumos
244323530Savg	vfsp = zfvp->z_vfs;
245330990Savg#endif
246323530Savg	switch (zfs_prop) {
247323530Savg	case ZFS_PROP_ATIME:
248323530Savg		if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL))
249323530Savg			tmp = 0;
250323530Savg		if (vfs_optionisset(vfsp, MNTOPT_ATIME, NULL))
251323530Savg			tmp = 1;
252323530Savg		break;
253323530Savg	case ZFS_PROP_DEVICES:
254323530Savg		if (vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL))
255323530Savg			tmp = 0;
256323530Savg		if (vfs_optionisset(vfsp, MNTOPT_DEVICES, NULL))
257323530Savg			tmp = 1;
258323530Savg		break;
259323530Savg	case ZFS_PROP_EXEC:
260323530Savg		if (vfs_optionisset(vfsp, MNTOPT_NOEXEC, NULL))
261323530Savg			tmp = 0;
262323530Savg		if (vfs_optionisset(vfsp, MNTOPT_EXEC, NULL))
263323530Savg			tmp = 1;
264323530Savg		break;
265323530Savg	case ZFS_PROP_SETUID:
266323530Savg		if (vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL))
267323530Savg			tmp = 0;
268323530Savg		if (vfs_optionisset(vfsp, MNTOPT_SETUID, NULL))
269323530Savg			tmp = 1;
270323530Savg		break;
271323530Savg	case ZFS_PROP_READONLY:
272323530Savg		if (vfs_optionisset(vfsp, MNTOPT_RW, NULL))
273323530Savg			tmp = 0;
274323530Savg		if (vfs_optionisset(vfsp, MNTOPT_RO, NULL))
275323530Savg			tmp = 1;
276323530Savg		break;
277323530Savg	case ZFS_PROP_XATTR:
278323530Savg		if (vfs_optionisset(vfsp, MNTOPT_NOXATTR, NULL))
279323530Savg			tmp = 0;
280323530Savg		if (vfs_optionisset(vfsp, MNTOPT_XATTR, NULL))
281323530Savg			tmp = 1;
282323530Savg		break;
283323530Savg	case ZFS_PROP_NBMAND:
284323530Savg		if (vfs_optionisset(vfsp, MNTOPT_NONBMAND, NULL))
285323530Savg			tmp = 0;
286323530Savg		if (vfs_optionisset(vfsp, MNTOPT_NBMAND, NULL))
287323530Savg			tmp = 1;
288323530Savg		break;
289323530Savg	default:
290324163Savg#ifdef illumos
291323530Savg		VFS_RELE(vfsp);
292324163Savg#else
293325534Savg		vfs_rel(vfsp);
294324163Savg#endif
295323530Savg		return (ENOENT);
296323530Savg	}
297323530Savg
298324163Savg#ifdef illumos
299323530Savg	VFS_RELE(vfsp);
300324163Savg#else
301325534Savg	vfs_rel(vfsp);
302324163Savg#endif
303323530Savg	if (tmp != *val) {
304323530Savg		(void) strcpy(setpoint, "temporary");
305323530Savg		*val = tmp;
306323530Savg	}
307323530Savg	return (0);
308323530Savg#endif
309323530Savg}
310323530Savg
311323530Savg/*
312323530Savg * Check if the property we're looking for is stored at the dsl_dataset or
313323530Savg * dsl_dir level. If so, push the property value and source onto the lua stack
314323530Savg * and return 0. If it is not present or a failure occurs in lookup, return a
315323530Savg * non-zero error value.
316323530Savg */
317323530Savgstatic int
318323530Savgget_special_prop(lua_State *state, dsl_dataset_t *ds, const char *dsname,
319323530Savg    zfs_prop_t zfs_prop)
320323530Savg{
321323530Savg	int error = 0;
322323530Savg	objset_t *os;
323323530Savg	uint64_t numval;
324323530Savg	char *strval = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP);
325323530Savg	char setpoint[ZFS_MAX_DATASET_NAME_LEN] =
326323530Savg	    "Internal error - setpoint not determined";
327323530Savg	zfs_type_t ds_type;
328323530Savg	zprop_type_t prop_type = zfs_prop_get_type(zfs_prop);
329323530Savg	(void) get_objset_type(ds, &ds_type);
330323530Savg
331323530Savg	switch (zfs_prop) {
332323530Savg	case ZFS_PROP_REFRATIO:
333323530Savg		numval = dsl_get_refratio(ds);
334323530Savg		break;
335323530Savg	case ZFS_PROP_USED:
336323530Savg		numval = dsl_get_used(ds);
337323530Savg		break;
338323530Savg	case ZFS_PROP_CLONES: {
339323530Savg		nvlist_t *clones = fnvlist_alloc();
340323530Savg		error = get_clones_stat_impl(ds, clones);
341323530Savg		if (error == 0) {
342323530Savg			/* push list to lua stack */
343324163Savg			VERIFY0(zcp_nvlist_to_lua(state, clones, NULL, 0));
344323530Savg			/* source */
345323530Savg			(void) lua_pushnil(state);
346323530Savg		}
347323530Savg		nvlist_free(clones);
348323530Savg		kmem_free(strval, ZAP_MAXVALUELEN);
349323530Savg		return (error);
350323530Savg	}
351323530Savg	case ZFS_PROP_COMPRESSRATIO:
352323530Savg		numval = dsl_get_compressratio(ds);
353323530Savg		break;
354323530Savg	case ZFS_PROP_CREATION:
355323530Savg		numval = dsl_get_creation(ds);
356323530Savg		break;
357323530Savg	case ZFS_PROP_REFERENCED:
358323530Savg		numval = dsl_get_referenced(ds);
359323530Savg		break;
360323530Savg	case ZFS_PROP_AVAILABLE:
361323530Savg		numval = dsl_get_available(ds);
362323530Savg		break;
363323530Savg	case ZFS_PROP_LOGICALREFERENCED:
364323530Savg		numval = dsl_get_logicalreferenced(ds);
365323530Savg		break;
366323530Savg	case ZFS_PROP_CREATETXG:
367323530Savg		numval = dsl_get_creationtxg(ds);
368323530Savg		break;
369323530Savg	case ZFS_PROP_GUID:
370323530Savg		numval = dsl_get_guid(ds);
371323530Savg		break;
372323530Savg	case ZFS_PROP_UNIQUE:
373323530Savg		numval = dsl_get_unique(ds);
374323530Savg		break;
375323530Savg	case ZFS_PROP_OBJSETID:
376323530Savg		numval = dsl_get_objsetid(ds);
377323530Savg		break;
378323530Savg	case ZFS_PROP_ORIGIN:
379323530Savg		dsl_dir_get_origin(ds->ds_dir, strval);
380323530Savg		break;
381323530Savg	case ZFS_PROP_USERACCOUNTING:
382323530Savg		error = dmu_objset_from_ds(ds, &os);
383323530Savg		if (error == 0)
384323530Savg			numval = dmu_objset_userspace_present(os);
385323530Savg		break;
386323530Savg	case ZFS_PROP_WRITTEN:
387323530Savg		error = dsl_get_written(ds, &numval);
388323530Savg		break;
389323530Savg	case ZFS_PROP_TYPE:
390323530Savg		error = get_objset_type_name(ds, strval);
391323530Savg		break;
392323530Savg	case ZFS_PROP_PREV_SNAP:
393323530Savg		error = dsl_get_prev_snap(ds, strval);
394323530Savg		break;
395323530Savg	case ZFS_PROP_NAME:
396323530Savg		dsl_dataset_name(ds, strval);
397323530Savg		break;
398323530Savg	case ZFS_PROP_MOUNTPOINT:
399323530Savg		error = dsl_get_mountpoint(ds, dsname, strval, setpoint);
400323530Savg		break;
401323530Savg	case ZFS_PROP_VERSION:
402323530Savg		/* should be a snapshot or filesystem */
403323530Savg		ASSERT(ds_type != ZFS_TYPE_VOLUME);
404323530Savg		error = dmu_objset_from_ds(ds, &os);
405323530Savg		/* look in the master node for the version */
406323530Savg		if (error == 0) {
407323530Savg			error = zap_lookup(os, MASTER_NODE_OBJ, ZPL_VERSION_STR,
408323530Savg			    sizeof (numval), 1, &numval);
409323530Savg		}
410323530Savg		break;
411323530Savg	case ZFS_PROP_DEFER_DESTROY:
412323530Savg		numval = dsl_get_defer_destroy(ds);
413323530Savg		break;
414323530Savg	case ZFS_PROP_USERREFS:
415323530Savg		numval = dsl_get_userrefs(ds);
416323530Savg		break;
417323530Savg	case ZFS_PROP_FILESYSTEM_COUNT:
418323530Savg		error = dsl_dir_get_filesystem_count(ds->ds_dir, &numval);
419323530Savg		(void) strcpy(setpoint, "");
420323530Savg		break;
421323530Savg	case ZFS_PROP_SNAPSHOT_COUNT:
422323530Savg		error = dsl_dir_get_snapshot_count(ds->ds_dir, &numval);
423323530Savg		(void) strcpy(setpoint, "");
424323530Savg		break;
425332525Smav	case ZFS_PROP_REMAPTXG:
426332525Smav		error = dsl_dir_get_remaptxg(ds->ds_dir, &numval);
427332525Smav		break;
428323530Savg	case ZFS_PROP_NUMCLONES:
429323530Savg		numval = dsl_get_numclones(ds);
430323530Savg		break;
431323530Savg	case ZFS_PROP_INCONSISTENT:
432323530Savg		numval = dsl_get_inconsistent(ds);
433323530Savg		break;
434323530Savg	case ZFS_PROP_RECEIVE_RESUME_TOKEN:
435323530Savg		VERIFY3U(strlcpy(strval, get_receive_resume_stats_impl(ds),
436323530Savg		    ZAP_MAXVALUELEN), <, ZAP_MAXVALUELEN);
437323530Savg		if (strcmp(strval, "") == 0) {
438323530Savg			VERIFY3U(strlcpy(strval, get_child_receive_stats(ds),
439323530Savg			    ZAP_MAXVALUELEN), <, ZAP_MAXVALUELEN);
440323530Savg			if (strcmp(strval, "") == 0)
441323530Savg				error = ENOENT;
442323530Savg		}
443323530Savg		break;
444323530Savg	case ZFS_PROP_VOLSIZE:
445323530Savg		ASSERT(ds_type == ZFS_TYPE_VOLUME);
446323530Savg		error = dmu_objset_from_ds(ds, &os);
447323530Savg		if (error == 0) {
448323530Savg			error = zap_lookup(os, ZVOL_ZAP_OBJ, "size",
449323530Savg			    sizeof (numval), 1, &numval);
450323530Savg		}
451323530Savg		if (error == 0)
452323530Savg			(void) strcpy(setpoint, dsname);
453323530Savg
454323530Savg		break;
455323530Savg	case ZFS_PROP_VOLBLOCKSIZE: {
456323530Savg		ASSERT(ds_type == ZFS_TYPE_VOLUME);
457323530Savg		dmu_object_info_t doi;
458323530Savg		error = dmu_objset_from_ds(ds, &os);
459323530Savg		if (error == 0) {
460323530Savg			error = dmu_object_info(os, ZVOL_OBJ, &doi);
461323530Savg			if (error == 0)
462323530Savg				numval = doi.doi_data_block_size;
463323530Savg		}
464323530Savg		break;
465323530Savg	}
466323530Savg	default:
467323530Savg		/* Did not match these props, check in the dsl_dir */
468323530Savg		error = get_dsl_dir_prop(ds, zfs_prop, &numval);
469323530Savg	}
470323530Savg	if (error != 0) {
471323530Savg		kmem_free(strval, ZAP_MAXVALUELEN);
472323530Savg		return (error);
473323530Savg	}
474323530Savg
475323530Savg	switch (prop_type) {
476323530Savg	case PROP_TYPE_NUMBER: {
477323530Savg		(void) lua_pushnumber(state, numval);
478323530Savg		break;
479323530Savg	}
480323530Savg	case PROP_TYPE_STRING: {
481323530Savg		(void) lua_pushstring(state, strval);
482323530Savg		break;
483323530Savg	}
484323530Savg	case PROP_TYPE_INDEX: {
485323530Savg		const char *propval;
486323530Savg		error = zfs_prop_index_to_string(zfs_prop, numval, &propval);
487323530Savg		if (error != 0) {
488323530Savg			kmem_free(strval, ZAP_MAXVALUELEN);
489323530Savg			return (error);
490323530Savg		}
491323530Savg		(void) lua_pushstring(state, propval);
492323530Savg		break;
493323530Savg	}
494323530Savg	}
495323530Savg	kmem_free(strval, ZAP_MAXVALUELEN);
496323530Savg
497323530Savg	/* Push the source to the stack */
498323530Savg	get_prop_src(state, setpoint, zfs_prop);
499323530Savg	return (0);
500323530Savg}
501323530Savg
502323530Savg/*
503323530Savg * Look up a property and its source in the zap object. If the value is
504323530Savg * present and successfully retrieved, push the value and source on the
505323530Savg * lua stack and return 0. On failure, return a non-zero error value.
506323530Savg */
507323530Savgstatic int
508323530Savgget_zap_prop(lua_State *state, dsl_dataset_t *ds, zfs_prop_t zfs_prop)
509323530Savg{
510323530Savg	int error = 0;
511323530Savg	char setpoint[ZFS_MAX_DATASET_NAME_LEN];
512323530Savg	char *strval = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP);
513323530Savg	uint64_t numval;
514323530Savg	const char *prop_name = zfs_prop_to_name(zfs_prop);
515323530Savg	zprop_type_t prop_type = zfs_prop_get_type(zfs_prop);
516323530Savg
517323530Savg	if (prop_type == PROP_TYPE_STRING) {
518323530Savg		/* Push value to lua stack */
519323530Savg		error = dsl_prop_get_ds(ds, prop_name, 1,
520323530Savg		    ZAP_MAXVALUELEN, strval, setpoint);
521323530Savg		if (error == 0)
522323530Savg			(void) lua_pushstring(state, strval);
523323530Savg	} else {
524323530Savg		error = dsl_prop_get_ds(ds, prop_name, sizeof (numval),
525323530Savg		    1, &numval, setpoint);
526323530Savg
527323530Savg		/* Fill in temorary value for prop, if applicable */
528323530Savg		(void) get_temporary_prop(ds, zfs_prop, &numval, setpoint);
529323530Savg
530323530Savg		/* Push value to lua stack */
531323530Savg		if (prop_type == PROP_TYPE_INDEX) {
532323530Savg			const char *propval;
533323530Savg			error = zfs_prop_index_to_string(zfs_prop, numval,
534323530Savg			    &propval);
535323530Savg			if (error == 0)
536323530Savg				(void) lua_pushstring(state, propval);
537323530Savg		} else {
538323530Savg			if (error == 0)
539323530Savg				(void) lua_pushnumber(state, numval);
540323530Savg		}
541323530Savg	}
542323530Savg	kmem_free(strval, ZAP_MAXVALUELEN);
543323530Savg	if (error == 0)
544323530Savg		get_prop_src(state, setpoint, zfs_prop);
545323530Savg	return (error);
546323530Savg}
547323530Savg
548323530Savg/*
549323530Savg * Determine whether property is valid for a given dataset
550323530Savg */
551323530Savgboolean_t
552323530Savgprop_valid_for_ds(dsl_dataset_t *ds, zfs_prop_t zfs_prop)
553323530Savg{
554323530Savg	int error;
555323530Savg	zfs_type_t zfs_type;
556323530Savg
557323530Savg	/* properties not supported */
558323530Savg	if ((zfs_prop == ZFS_PROP_ISCSIOPTIONS) ||
559323530Savg	    (zfs_prop == ZFS_PROP_MOUNTED))
560323530Savg		return (B_FALSE);
561323530Savg
562323530Savg	/* if we want the origin prop, ds must be a clone */
563323530Savg	if ((zfs_prop == ZFS_PROP_ORIGIN) && (!dsl_dir_is_clone(ds->ds_dir)))
564323530Savg		return (B_FALSE);
565323530Savg
566323530Savg	error = get_objset_type(ds, &zfs_type);
567323530Savg	if (error != 0)
568323530Savg		return (B_FALSE);
569323530Savg	return (zfs_prop_valid_for_type(zfs_prop, zfs_type));
570323530Savg}
571323530Savg
572323530Savg/*
573323530Savg * Look up a given dataset property. On success return 2, the number of
574323530Savg * values pushed to the lua stack (property value and source). On a fatal
575323530Savg * error, longjmp. On a non fatal error push nothing.
576323530Savg */
577323530Savgstatic int
578323530Savgzcp_get_system_prop(lua_State *state, dsl_pool_t *dp, const char *dataset_name,
579323530Savg    zfs_prop_t zfs_prop)
580323530Savg{
581323530Savg	int error;
582323530Savg	/*
583323530Savg	 * zcp_dataset_hold will either successfully return the requested
584323530Savg	 * dataset or throw a lua error and longjmp out of the zfs.get_prop call
585323530Savg	 * without returning.
586323530Savg	 */
587323530Savg	dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG);
588323530Savg	if (ds == NULL)
589323530Savg		return (1); /* not reached; zcp_dataset_hold() longjmp'd */
590323530Savg
591323530Savg	/* Check that the property is valid for the given dataset */
592323530Savg	const char *prop_name = zfs_prop_to_name(zfs_prop);
593323530Savg	if (!prop_valid_for_ds(ds, zfs_prop)) {
594323530Savg		dsl_dataset_rele(ds, FTAG);
595323530Savg		return (0);
596323530Savg	}
597323530Savg
598323530Savg	/* Check if the property can be accessed directly */
599323530Savg	error = get_special_prop(state, ds, dataset_name, zfs_prop);
600323530Savg	if (error == 0) {
601323530Savg		dsl_dataset_rele(ds, FTAG);
602323530Savg		/* The value and source have been pushed by get_special_prop */
603323530Savg		return (2);
604323530Savg	}
605323530Savg	if (error != ENOENT) {
606323530Savg		dsl_dataset_rele(ds, FTAG);
607323530Savg		return (zcp_handle_error(state, dataset_name,
608323530Savg		    prop_name, error));
609323530Savg	}
610323530Savg
611323530Savg	/* If we were unable to find it, look in the zap object */
612323530Savg	error = get_zap_prop(state, ds, zfs_prop);
613323530Savg	dsl_dataset_rele(ds, FTAG);
614323530Savg	if (error != 0) {
615323530Savg		return (zcp_handle_error(state, dataset_name,
616323530Savg		    prop_name, error));
617323530Savg	}
618323530Savg	/* The value and source have been pushed by get_zap_prop */
619323530Savg	return (2);
620323530Savg}
621323530Savg
622323530Savgstatic zfs_userquota_prop_t
623323530Savgget_userquota_prop(const char *prop_name)
624323530Savg{
625323530Savg	zfs_userquota_prop_t type;
626323530Savg	/* Figure out the property type ({user|group}{quota|used}) */
627323530Savg	for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) {
628323530Savg		if (strncmp(prop_name, zfs_userquota_prop_prefixes[type],
629323530Savg		    strlen(zfs_userquota_prop_prefixes[type])) == 0)
630323530Savg			break;
631323530Savg	}
632323530Savg	return (type);
633323530Savg}
634323530Savg
635323530Savg#ifdef _KERNEL
636323530Savg/*
637323530Savg * Given the name of a zfs_userquota_prop, this function determines the
638323530Savg * prop type as well as the numeric group/user ids based on the string
639323530Savg * following the '@' in the property name. On success, returns 0. On failure,
640323530Savg * returns a non-zero error.
641323530Savg * 'domain' must be free'd by caller using strfree()
642323530Savg */
643323530Savgstatic int
644323530Savgparse_userquota_prop(const char *prop_name, zfs_userquota_prop_t *type,
645323530Savg    char **domain, uint64_t *rid)
646323530Savg{
647323530Savg	char *cp, *end, *domain_val;
648323530Savg
649323530Savg	*type = get_userquota_prop(prop_name);
650323530Savg	if (*type >= ZFS_NUM_USERQUOTA_PROPS)
651323530Savg		return (EINVAL);
652323530Savg
653323530Savg	*rid = 0;
654323530Savg	cp = strchr(prop_name, '@') + 1;
655323530Savg	if (strncmp(cp, "S-1-", 4) == 0) {
656323530Savg		/*
657323530Savg		 * It's a numeric SID (eg "S-1-234-567-89") and we want to
658323530Savg		 * seperate the domain id and the rid
659323530Savg		 */
660323530Savg		int domain_len = strrchr(cp, '-') - cp;
661323530Savg		domain_val = kmem_alloc(domain_len + 1, KM_SLEEP);
662323530Savg		(void) strncpy(domain_val, cp, domain_len);
663323530Savg		domain_val[domain_len] = '\0';
664323530Savg		cp += domain_len + 1;
665323530Savg
666323530Savg		(void) ddi_strtoll(cp, &end, 10, (longlong_t *)rid);
667323530Savg		if (*end != '\0') {
668323530Savg			strfree(domain_val);
669323530Savg			return (EINVAL);
670323530Savg		}
671323530Savg	} else {
672323530Savg		/* It's only a user/group ID (eg "12345"), just get the rid */
673323530Savg		domain_val = NULL;
674323530Savg		(void) ddi_strtoll(cp, &end, 10, (longlong_t *)rid);
675323530Savg		if (*end != '\0')
676323530Savg			return (EINVAL);
677323530Savg	}
678323530Savg	*domain = domain_val;
679323530Savg	return (0);
680323530Savg}
681323530Savg
682323530Savg/*
683323530Savg * Look up {user|group}{quota|used} property for given dataset. On success
684323530Savg * push the value (quota or used amount) and the setpoint. On failure, push
685323530Savg * a lua error.
686323530Savg */
687323530Savgstatic int
688323530Savgzcp_get_userquota_prop(lua_State *state, dsl_pool_t *dp,
689323530Savg    const char *dataset_name, const char *prop_name)
690323530Savg{
691323530Savg	zfsvfs_t *zfvp;
692323530Savg	zfsvfs_t *zfsvfs;
693323530Savg	int error;
694323530Savg	zfs_userquota_prop_t type;
695323530Savg	char *domain;
696323530Savg	uint64_t rid, value;
697323530Savg	objset_t *os;
698323530Savg
699323530Savg	dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG);
700323530Savg	if (ds == NULL)
701323530Savg		return (1); /* not reached; zcp_dataset_hold() longjmp'd */
702323530Savg
703323530Savg	error = parse_userquota_prop(prop_name, &type, &domain, &rid);
704323530Savg	if (error == 0) {
705323530Savg		error = dmu_objset_from_ds(ds, &os);
706323530Savg		if (error == 0) {
707323530Savg			zfsvfs = kmem_zalloc(sizeof (zfsvfs_t), KM_SLEEP);
708323530Savg			error = zfsvfs_create_impl(&zfvp, zfsvfs, os);
709323530Savg			if (error == 0) {
710323530Savg				error = zfs_userspace_one(zfvp, type, domain,
711323530Savg				    rid, &value);
712323530Savg				zfsvfs_free(zfvp);
713323530Savg			}
714323530Savg		}
715323530Savg		if (domain != NULL)
716323530Savg			strfree(domain);
717323530Savg	}
718323530Savg	dsl_dataset_rele(ds, FTAG);
719323530Savg
720323530Savg	if ((value == 0) && ((type == ZFS_PROP_USERQUOTA) ||
721323530Savg	    (type == ZFS_PROP_GROUPQUOTA)))
722323530Savg		error = ENOENT;
723323530Savg	if (error != 0) {
724323530Savg		return (zcp_handle_error(state, dataset_name,
725323530Savg		    prop_name, error));
726323530Savg	}
727323530Savg
728323530Savg	(void) lua_pushnumber(state, value);
729323530Savg	(void) lua_pushstring(state, dataset_name);
730323530Savg	return (2);
731323530Savg}
732323530Savg#endif
733323530Savg
734323530Savg/*
735323530Savg * Determines the name of the snapshot referenced in the written property
736323530Savg * name. Returns snapshot name in snap_name, a buffer that must be at least
737323530Savg * as large as ZFS_MAX_DATASET_NAME_LEN
738323530Savg */
739323530Savgstatic void
740323530Savgparse_written_prop(const char *dataset_name, const char *prop_name,
741323530Savg    char *snap_name)
742323530Savg{
743323530Savg	ASSERT(zfs_prop_written(prop_name));
744323530Savg	const char *name = prop_name + ZFS_WRITTEN_PROP_PREFIX_LEN;
745323530Savg	if (strchr(name, '@') == NULL) {
746323530Savg		(void) sprintf(snap_name, "%s@%s", dataset_name, name);
747323530Savg	} else {
748323530Savg		(void) strcpy(snap_name, name);
749323530Savg	}
750323530Savg}
751323530Savg
752323530Savg/*
753323530Savg * Look up written@ property for given dataset. On success
754323530Savg * push the value and the setpoint. If error is fatal, we will
755323530Savg * longjmp, otherwise push nothing.
756323530Savg */
757323530Savgstatic int
758323530Savgzcp_get_written_prop(lua_State *state, dsl_pool_t *dp,
759323530Savg    const char *dataset_name, const char *prop_name)
760323530Savg{
761323530Savg	char snap_name[ZFS_MAX_DATASET_NAME_LEN];
762323530Savg	uint64_t used, comp, uncomp;
763323530Savg	dsl_dataset_t *old;
764323530Savg	int error = 0;
765323530Savg
766323530Savg	parse_written_prop(dataset_name, prop_name, snap_name);
767323530Savg	dsl_dataset_t *new = zcp_dataset_hold(state, dp, dataset_name, FTAG);
768323530Savg	if (new == NULL)
769323530Savg		return (1); /* not reached; zcp_dataset_hold() longjmp'd */
770323530Savg
771323530Savg	error = dsl_dataset_hold(dp, snap_name, FTAG, &old);
772323530Savg	if (error != 0) {
773323530Savg		dsl_dataset_rele(new, FTAG);
774323530Savg		return (zcp_dataset_hold_error(state, dp, snap_name,
775323530Savg		    error));
776323530Savg	}
777323530Savg	error = dsl_dataset_space_written(old, new,
778323530Savg	    &used, &comp, &uncomp);
779323530Savg
780323530Savg	dsl_dataset_rele(old, FTAG);
781323530Savg	dsl_dataset_rele(new, FTAG);
782323530Savg
783323530Savg	if (error != 0) {
784323530Savg		return (zcp_handle_error(state, dataset_name,
785323530Savg		    snap_name, error));
786323530Savg	}
787323530Savg	(void) lua_pushnumber(state, used);
788323530Savg	(void) lua_pushstring(state, dataset_name);
789323530Savg	return (2);
790323530Savg}
791323530Savg
792323530Savgstatic int zcp_get_prop(lua_State *state);
793323530Savgstatic zcp_lib_info_t zcp_get_prop_info = {
794323530Savg	.name = "get_prop",
795323530Savg	.func = zcp_get_prop,
796323530Savg	.pargs = {
797323530Savg	    { .za_name = "dataset", .za_lua_type = LUA_TSTRING},
798323530Savg	    { .za_name = "property", .za_lua_type =  LUA_TSTRING},
799324163Savg	    {NULL, 0}
800323530Savg	},
801323530Savg	.kwargs = {
802324163Savg	    {NULL, 0}
803323530Savg	}
804323530Savg};
805323530Savg
806323530Savgstatic int
807323530Savgzcp_get_prop(lua_State *state)
808323530Savg{
809323530Savg	const char *dataset_name;
810323530Savg	const char *property_name;
811323530Savg	dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
812323530Savg	zcp_lib_info_t *libinfo = &zcp_get_prop_info;
813323530Savg
814323530Savg	zcp_parse_args(state, libinfo->name, libinfo->pargs, libinfo->kwargs);
815323530Savg
816323530Savg	dataset_name = lua_tostring(state, 1);
817323530Savg	property_name = lua_tostring(state, 2);
818323530Savg
819323530Savg	/* User defined property */
820323530Savg	if (zfs_prop_user(property_name)) {
821323530Savg		return (zcp_get_user_prop(state, dp,
822323530Savg		    dataset_name, property_name));
823323530Savg	}
824323530Savg	/* userspace property */
825323530Savg	if (zfs_prop_userquota(property_name)) {
826323530Savg#ifdef _KERNEL
827323530Savg		return (zcp_get_userquota_prop(state, dp,
828323530Savg		    dataset_name, property_name));
829323530Savg#else
830323530Savg		return (luaL_error(state,
831323530Savg		    "user quota properties only supported in kernel mode",
832323530Savg		    property_name));
833323530Savg#endif
834323530Savg	}
835323530Savg	/* written@ property */
836323530Savg	if (zfs_prop_written(property_name)) {
837323530Savg		return (zcp_get_written_prop(state, dp,
838323530Savg		    dataset_name, property_name));
839323530Savg	}
840323530Savg
841323530Savg	zfs_prop_t zfs_prop = zfs_name_to_prop(property_name);
842323530Savg	/* Valid system property */
843323530Savg	if (zfs_prop != ZPROP_INVAL) {
844323530Savg		return (zcp_get_system_prop(state, dp, dataset_name,
845323530Savg		    zfs_prop));
846323530Savg	}
847323530Savg
848323530Savg	/* Invalid property name */
849323530Savg	return (luaL_error(state,
850323530Savg	    "'%s' is not a valid property", property_name));
851323530Savg}
852323530Savg
853323530Savgint
854323530Savgzcp_load_get_lib(lua_State *state)
855323530Savg{
856323530Savg	lua_pushcclosure(state, zcp_get_prop_info.func, 0);
857323530Savg	lua_setfield(state, -2, zcp_get_prop_info.name);
858323530Savg
859323530Savg	return (1);
860323530Savg}
861