]>
Commit | Line | Data |
---|---|---|
d99a0153 CW |
1 | /* |
2 | * CDDL HEADER START | |
3 | * | |
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 | |
7 | * 1.0 of the CDDL. | |
8 | * | |
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. | |
12 | * | |
13 | * CDDL HEADER END | |
14 | */ | |
15 | ||
16 | /* | |
17 | * Copyright (c) 2016 by Delphix. All rights reserved. | |
18 | */ | |
19 | ||
20 | #include <sys/lua/lua.h> | |
21 | #include <sys/lua/lualib.h> | |
22 | #include <sys/lua/lauxlib.h> | |
23 | ||
24 | #include <zfs_prop.h> | |
25 | ||
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> | |
33 | #include <sys/zap.h> | |
34 | #include <sys/zcp.h> | |
35 | #include <sys/zcp_iter.h> | |
36 | #include <sys/zcp_global.h> | |
60356b1a | 37 | #include <sys/zcp_prop.h> |
d99a0153 CW |
38 | #include <sys/zfs_ioctl.h> |
39 | #include <sys/zfs_znode.h> | |
40 | #include <sys/zvol.h> | |
41 | ||
42 | #ifdef _KERNEL | |
957c7aa2 | 43 | #include <sys/zfs_quota.h> |
d99a0153 CW |
44 | #include <sys/zfs_vfsops.h> |
45 | #endif | |
46 | ||
47 | static int | |
48 | get_objset_type(dsl_dataset_t *ds, zfs_type_t *type) | |
49 | { | |
50 | int error; | |
51 | objset_t *os; | |
52 | error = dmu_objset_from_ds(ds, &os); | |
53 | if (error != 0) | |
54 | return (error); | |
55 | if (ds->ds_is_snapshot) { | |
56 | *type = ZFS_TYPE_SNAPSHOT; | |
57 | } else { | |
58 | switch (os->os_phys->os_type) { | |
59 | case DMU_OST_ZFS: | |
60 | *type = ZFS_TYPE_FILESYSTEM; | |
61 | break; | |
62 | case DMU_OST_ZVOL: | |
63 | *type = ZFS_TYPE_VOLUME; | |
64 | break; | |
65 | default: | |
66 | return (EINVAL); | |
67 | } | |
68 | } | |
69 | return (0); | |
70 | } | |
71 | ||
72 | /* | |
73 | * Returns the string name of ds's type in str (a buffer which should be | |
74 | * at least 12 bytes long). | |
75 | */ | |
76 | static int | |
77 | get_objset_type_name(dsl_dataset_t *ds, char *str) | |
78 | { | |
806739f9 DS |
79 | zfs_type_t type = ZFS_TYPE_INVALID; |
80 | int error = get_objset_type(ds, &type); | |
d99a0153 CW |
81 | if (error != 0) |
82 | return (error); | |
83 | switch (type) { | |
84 | case ZFS_TYPE_SNAPSHOT: | |
c9e319fa | 85 | (void) strlcpy(str, "snapshot", ZAP_MAXVALUELEN); |
d99a0153 CW |
86 | break; |
87 | case ZFS_TYPE_FILESYSTEM: | |
c9e319fa | 88 | (void) strlcpy(str, "filesystem", ZAP_MAXVALUELEN); |
d99a0153 CW |
89 | break; |
90 | case ZFS_TYPE_VOLUME: | |
c9e319fa | 91 | (void) strlcpy(str, "volume", ZAP_MAXVALUELEN); |
d99a0153 CW |
92 | break; |
93 | default: | |
94 | return (EINVAL); | |
95 | } | |
96 | return (0); | |
97 | } | |
98 | ||
99 | /* | |
100 | * Determines the source of a property given its setpoint and | |
101 | * property type. It pushes the source to the lua stack. | |
102 | */ | |
103 | static void | |
104 | get_prop_src(lua_State *state, const char *setpoint, zfs_prop_t prop) | |
105 | { | |
106 | if (zfs_prop_readonly(prop) || (prop == ZFS_PROP_VERSION)) { | |
107 | lua_pushnil(state); | |
108 | } else { | |
109 | const char *src; | |
110 | if (strcmp("", setpoint) == 0) { | |
111 | src = "default"; | |
112 | } else { | |
113 | src = setpoint; | |
114 | } | |
115 | (void) lua_pushstring(state, src); | |
116 | } | |
117 | } | |
118 | ||
119 | /* | |
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. | |
122 | */ | |
123 | static int | |
124 | zcp_handle_error(lua_State *state, const char *dataset_name, | |
125 | const char *property_name, int error) | |
126 | { | |
127 | ASSERT3S(error, !=, 0); | |
128 | if (error == ENOENT) { | |
129 | return (0); | |
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)); | |
138 | } else { | |
139 | return (luaL_error(state, "unexpected error %d while " | |
140 | "retrieving property '%s' on dataset '%s'", | |
141 | error, property_name, dataset_name)); | |
142 | } | |
143 | } | |
144 | ||
145 | /* | |
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. | |
148 | */ | |
149 | static int | |
150 | zcp_get_user_prop(lua_State *state, dsl_pool_t *dp, const char *dataset_name, | |
151 | const char *property_name) | |
152 | { | |
153 | int error; | |
154 | char *buf; | |
155 | char setpoint[ZFS_MAX_DATASET_NAME_LEN]; | |
156 | /* | |
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 | |
159 | * without returning. | |
160 | */ | |
161 | dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG); | |
162 | if (ds == NULL) | |
163 | return (1); /* not reached; zcp_dataset_hold() longjmp'd */ | |
164 | ||
165 | buf = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP); | |
166 | error = dsl_prop_get_ds(ds, property_name, 1, ZAP_MAXVALUELEN, | |
167 | buf, setpoint); | |
168 | dsl_dataset_rele(ds, FTAG); | |
169 | ||
170 | if (error != 0) { | |
171 | kmem_free(buf, ZAP_MAXVALUELEN); | |
172 | return (zcp_handle_error(state, dataset_name, property_name, | |
173 | error)); | |
174 | } | |
175 | (void) lua_pushstring(state, buf); | |
176 | (void) lua_pushstring(state, setpoint); | |
177 | kmem_free(buf, ZAP_MAXVALUELEN); | |
178 | return (2); | |
179 | } | |
180 | ||
181 | /* | |
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. | |
185 | */ | |
186 | static int | |
187 | get_dsl_dir_prop(dsl_dataset_t *ds, zfs_prop_t zfs_prop, | |
188 | uint64_t *val) | |
189 | { | |
190 | dsl_dir_t *dd = ds->ds_dir; | |
191 | mutex_enter(&dd->dd_lock); | |
192 | switch (zfs_prop) { | |
193 | case ZFS_PROP_USEDSNAP: | |
194 | *val = dsl_dir_get_usedsnap(dd); | |
195 | break; | |
196 | case ZFS_PROP_USEDCHILD: | |
197 | *val = dsl_dir_get_usedchild(dd); | |
198 | break; | |
199 | case ZFS_PROP_USEDDS: | |
200 | *val = dsl_dir_get_usedds(dd); | |
201 | break; | |
202 | case ZFS_PROP_USEDREFRESERV: | |
203 | *val = dsl_dir_get_usedrefreserv(dd); | |
204 | break; | |
205 | case ZFS_PROP_LOGICALUSED: | |
206 | *val = dsl_dir_get_logicalused(dd); | |
207 | break; | |
208 | default: | |
209 | mutex_exit(&dd->dd_lock); | |
28caa74b | 210 | return (SET_ERROR(ENOENT)); |
d99a0153 CW |
211 | } |
212 | mutex_exit(&dd->dd_lock); | |
213 | return (0); | |
214 | } | |
215 | ||
d99a0153 CW |
216 | /* |
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. | |
221 | */ | |
222 | static int | |
223 | get_special_prop(lua_State *state, dsl_dataset_t *ds, const char *dsname, | |
224 | zfs_prop_t zfs_prop) | |
225 | { | |
226 | int error = 0; | |
227 | objset_t *os; | |
cbce5813 | 228 | uint64_t numval = 0; |
d99a0153 CW |
229 | char *strval = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP); |
230 | char setpoint[ZFS_MAX_DATASET_NAME_LEN] = | |
231 | "Internal error - setpoint not determined"; | |
806739f9 | 232 | zfs_type_t ds_type = ZFS_TYPE_INVALID; |
d99a0153 CW |
233 | zprop_type_t prop_type = zfs_prop_get_type(zfs_prop); |
234 | (void) get_objset_type(ds, &ds_type); | |
235 | ||
236 | switch (zfs_prop) { | |
237 | case ZFS_PROP_REFRATIO: | |
238 | numval = dsl_get_refratio(ds); | |
239 | break; | |
240 | case ZFS_PROP_USED: | |
241 | numval = dsl_get_used(ds); | |
242 | break; | |
243 | case ZFS_PROP_CLONES: { | |
244 | nvlist_t *clones = fnvlist_alloc(); | |
245 | error = get_clones_stat_impl(ds, clones); | |
246 | if (error == 0) { | |
247 | /* push list to lua stack */ | |
248 | VERIFY0(zcp_nvlist_to_lua(state, clones, NULL, 0ULL)); | |
249 | /* source */ | |
250 | (void) lua_pushnil(state); | |
251 | } | |
252 | nvlist_free(clones); | |
253 | kmem_free(strval, ZAP_MAXVALUELEN); | |
254 | return (error); | |
255 | } | |
256 | case ZFS_PROP_COMPRESSRATIO: | |
257 | numval = dsl_get_compressratio(ds); | |
258 | break; | |
259 | case ZFS_PROP_CREATION: | |
260 | numval = dsl_get_creation(ds); | |
261 | break; | |
262 | case ZFS_PROP_REFERENCED: | |
263 | numval = dsl_get_referenced(ds); | |
264 | break; | |
265 | case ZFS_PROP_AVAILABLE: | |
266 | numval = dsl_get_available(ds); | |
267 | break; | |
268 | case ZFS_PROP_LOGICALREFERENCED: | |
269 | numval = dsl_get_logicalreferenced(ds); | |
270 | break; | |
271 | case ZFS_PROP_CREATETXG: | |
272 | numval = dsl_get_creationtxg(ds); | |
273 | break; | |
274 | case ZFS_PROP_GUID: | |
275 | numval = dsl_get_guid(ds); | |
276 | break; | |
277 | case ZFS_PROP_UNIQUE: | |
278 | numval = dsl_get_unique(ds); | |
279 | break; | |
280 | case ZFS_PROP_OBJSETID: | |
281 | numval = dsl_get_objsetid(ds); | |
282 | break; | |
283 | case ZFS_PROP_ORIGIN: | |
284 | dsl_dir_get_origin(ds->ds_dir, strval); | |
285 | break; | |
286 | case ZFS_PROP_USERACCOUNTING: | |
287 | error = dmu_objset_from_ds(ds, &os); | |
288 | if (error == 0) | |
289 | numval = dmu_objset_userspace_present(os); | |
290 | break; | |
291 | case ZFS_PROP_WRITTEN: | |
292 | error = dsl_get_written(ds, &numval); | |
293 | break; | |
294 | case ZFS_PROP_TYPE: | |
295 | error = get_objset_type_name(ds, strval); | |
296 | break; | |
297 | case ZFS_PROP_PREV_SNAP: | |
298 | error = dsl_get_prev_snap(ds, strval); | |
299 | break; | |
300 | case ZFS_PROP_NAME: | |
301 | dsl_dataset_name(ds, strval); | |
302 | break; | |
303 | case ZFS_PROP_MOUNTPOINT: | |
304 | error = dsl_get_mountpoint(ds, dsname, strval, setpoint); | |
305 | break; | |
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 */ | |
311 | if (error == 0) { | |
312 | error = zap_lookup(os, MASTER_NODE_OBJ, ZPL_VERSION_STR, | |
313 | sizeof (numval), 1, &numval); | |
314 | } | |
315 | break; | |
316 | case ZFS_PROP_DEFER_DESTROY: | |
317 | numval = dsl_get_defer_destroy(ds); | |
318 | break; | |
319 | case ZFS_PROP_USERREFS: | |
320 | numval = dsl_get_userrefs(ds); | |
321 | break; | |
322 | case ZFS_PROP_FILESYSTEM_COUNT: | |
323 | error = dsl_dir_get_filesystem_count(ds->ds_dir, &numval); | |
c9e319fa | 324 | (void) strlcpy(setpoint, "", ZFS_MAX_DATASET_NAME_LEN); |
d99a0153 CW |
325 | break; |
326 | case ZFS_PROP_SNAPSHOT_COUNT: | |
327 | error = dsl_dir_get_snapshot_count(ds->ds_dir, &numval); | |
c9e319fa | 328 | (void) strlcpy(setpoint, "", ZFS_MAX_DATASET_NAME_LEN); |
d99a0153 CW |
329 | break; |
330 | case ZFS_PROP_NUMCLONES: | |
331 | numval = dsl_get_numclones(ds); | |
332 | break; | |
333 | case ZFS_PROP_INCONSISTENT: | |
334 | numval = dsl_get_inconsistent(ds); | |
335 | break; | |
f00ab3f2 TC |
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); | |
341 | } else { | |
342 | error = ENOENT; | |
343 | } | |
344 | break; | |
d99a0153 | 345 | case ZFS_PROP_RECEIVE_RESUME_TOKEN: { |
15aa3869 RM |
346 | char *token = get_receive_resume_token(ds); |
347 | if (token != NULL) { | |
348 | (void) strlcpy(strval, token, ZAP_MAXVALUELEN); | |
349 | kmem_strfree(token); | |
350 | } else { | |
351 | error = ENOENT; | |
d99a0153 | 352 | } |
d99a0153 CW |
353 | break; |
354 | } | |
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); | |
359 | if (error == 0) { | |
360 | error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", | |
361 | sizeof (numval), 1, &numval); | |
362 | } | |
363 | if (error == 0) | |
c9e319fa JL |
364 | (void) strlcpy(setpoint, dsname, |
365 | ZFS_MAX_DATASET_NAME_LEN); | |
d99a0153 CW |
366 | |
367 | break; | |
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); | |
372 | if (error == 0) { | |
373 | error = dmu_object_info(os, ZVOL_OBJ, &doi); | |
374 | if (error == 0) | |
375 | numval = doi.doi_data_block_size; | |
376 | } | |
377 | break; | |
378 | } | |
379 | ||
380 | case ZFS_PROP_KEYSTATUS: | |
381 | case ZFS_PROP_KEYFORMAT: { | |
382 | /* provide defaults in case no crypto obj exists */ | |
383 | setpoint[0] = '\0'; | |
384 | if (zfs_prop == ZFS_PROP_KEYSTATUS) | |
385 | numval = ZFS_KEYSTATUS_NONE; | |
386 | else | |
387 | numval = ZFS_KEYFORMAT_NONE; | |
388 | ||
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), | |
393 | &propval) == 0) { | |
d1807f16 | 394 | const char *source; |
d99a0153 CW |
395 | |
396 | (void) nvlist_lookup_uint64(propval, ZPROP_VALUE, | |
397 | &numval); | |
398 | if (nvlist_lookup_string(propval, ZPROP_SOURCE, | |
399 | &source) == 0) | |
400 | strlcpy(setpoint, source, sizeof (setpoint)); | |
401 | } | |
402 | nvlist_free(nvl); | |
403 | break; | |
404 | } | |
405 | ||
9681de46 US |
406 | case ZFS_PROP_SNAPSHOTS_CHANGED: |
407 | numval = dsl_dir_snap_cmtime(ds->ds_dir).tv_sec; | |
408 | break; | |
409 | ||
d99a0153 CW |
410 | default: |
411 | /* Did not match these props, check in the dsl_dir */ | |
412 | error = get_dsl_dir_prop(ds, zfs_prop, &numval); | |
413 | } | |
414 | if (error != 0) { | |
415 | kmem_free(strval, ZAP_MAXVALUELEN); | |
416 | return (error); | |
417 | } | |
418 | ||
419 | switch (prop_type) { | |
420 | case PROP_TYPE_NUMBER: { | |
421 | (void) lua_pushnumber(state, numval); | |
422 | break; | |
423 | } | |
424 | case PROP_TYPE_STRING: { | |
425 | (void) lua_pushstring(state, strval); | |
426 | break; | |
427 | } | |
428 | case PROP_TYPE_INDEX: { | |
429 | const char *propval; | |
430 | error = zfs_prop_index_to_string(zfs_prop, numval, &propval); | |
431 | if (error != 0) { | |
432 | kmem_free(strval, ZAP_MAXVALUELEN); | |
433 | return (error); | |
434 | } | |
435 | (void) lua_pushstring(state, propval); | |
436 | break; | |
437 | } | |
438 | } | |
439 | kmem_free(strval, ZAP_MAXVALUELEN); | |
440 | ||
441 | /* Push the source to the stack */ | |
442 | get_prop_src(state, setpoint, zfs_prop); | |
443 | return (0); | |
444 | } | |
445 | ||
446 | /* | |
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. | |
450 | */ | |
451 | static int | |
452 | get_zap_prop(lua_State *state, dsl_dataset_t *ds, zfs_prop_t zfs_prop) | |
453 | { | |
454 | int error = 0; | |
455 | char setpoint[ZFS_MAX_DATASET_NAME_LEN]; | |
456 | char *strval = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP); | |
457 | uint64_t numval; | |
458 | const char *prop_name = zfs_prop_to_name(zfs_prop); | |
459 | zprop_type_t prop_type = zfs_prop_get_type(zfs_prop); | |
460 | ||
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); | |
465 | if (error == 0) | |
466 | (void) lua_pushstring(state, strval); | |
467 | } else { | |
468 | error = dsl_prop_get_ds(ds, prop_name, sizeof (numval), | |
469 | 1, &numval, setpoint); | |
6a42939f RY |
470 | if (error != 0) |
471 | goto out; | |
2516a878 | 472 | #ifdef _KERNEL |
e1cfd73f | 473 | /* Fill in temporary value for prop, if applicable */ |
2516a878 MM |
474 | (void) zfs_get_temporary_prop(ds, zfs_prop, &numval, setpoint); |
475 | #else | |
f7bda2de | 476 | kmem_free(strval, ZAP_MAXVALUELEN); |
2516a878 MM |
477 | return (luaL_error(state, |
478 | "temporary properties only supported in kernel mode", | |
479 | prop_name)); | |
480 | #endif | |
d99a0153 CW |
481 | /* Push value to lua stack */ |
482 | if (prop_type == PROP_TYPE_INDEX) { | |
483 | const char *propval; | |
484 | error = zfs_prop_index_to_string(zfs_prop, numval, | |
485 | &propval); | |
486 | if (error == 0) | |
487 | (void) lua_pushstring(state, propval); | |
488 | } else { | |
489 | if (error == 0) | |
490 | (void) lua_pushnumber(state, numval); | |
491 | } | |
492 | } | |
6a42939f | 493 | out: |
d99a0153 CW |
494 | kmem_free(strval, ZAP_MAXVALUELEN); |
495 | if (error == 0) | |
496 | get_prop_src(state, setpoint, zfs_prop); | |
497 | return (error); | |
498 | } | |
499 | ||
500 | /* | |
501 | * Determine whether property is valid for a given dataset | |
502 | */ | |
503 | boolean_t | |
504 | prop_valid_for_ds(dsl_dataset_t *ds, zfs_prop_t zfs_prop) | |
505 | { | |
806739f9 | 506 | zfs_type_t zfs_type = ZFS_TYPE_INVALID; |
d99a0153 CW |
507 | |
508 | /* properties not supported */ | |
509 | if ((zfs_prop == ZFS_PROP_ISCSIOPTIONS) || | |
510 | (zfs_prop == ZFS_PROP_MOUNTED)) | |
511 | return (B_FALSE); | |
512 | ||
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))) | |
515 | return (B_FALSE); | |
516 | ||
806739f9 | 517 | int error = get_objset_type(ds, &zfs_type); |
d99a0153 CW |
518 | if (error != 0) |
519 | return (B_FALSE); | |
520 | return (zfs_prop_valid_for_type(zfs_prop, zfs_type, B_FALSE)); | |
521 | } | |
522 | ||
523 | /* | |
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. | |
527 | */ | |
528 | static int | |
529 | zcp_get_system_prop(lua_State *state, dsl_pool_t *dp, const char *dataset_name, | |
530 | zfs_prop_t zfs_prop) | |
531 | { | |
532 | int error; | |
533 | /* | |
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 | |
536 | * without returning. | |
537 | */ | |
538 | dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG); | |
539 | if (ds == NULL) | |
540 | return (1); /* not reached; zcp_dataset_hold() longjmp'd */ | |
541 | ||
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); | |
546 | return (0); | |
547 | } | |
548 | ||
549 | /* Check if the property can be accessed directly */ | |
550 | error = get_special_prop(state, ds, dataset_name, zfs_prop); | |
551 | if (error == 0) { | |
552 | dsl_dataset_rele(ds, FTAG); | |
553 | /* The value and source have been pushed by get_special_prop */ | |
554 | return (2); | |
555 | } | |
556 | if (error != ENOENT) { | |
557 | dsl_dataset_rele(ds, FTAG); | |
558 | return (zcp_handle_error(state, dataset_name, | |
559 | prop_name, error)); | |
560 | } | |
561 | ||
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); | |
565 | if (error != 0) { | |
566 | return (zcp_handle_error(state, dataset_name, | |
567 | prop_name, error)); | |
568 | } | |
569 | /* The value and source have been pushed by get_zap_prop */ | |
570 | return (2); | |
571 | } | |
572 | ||
573 | #ifdef _KERNEL | |
574 | static zfs_userquota_prop_t | |
575 | get_userquota_prop(const char *prop_name) | |
576 | { | |
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) | |
582 | break; | |
583 | } | |
584 | return (type); | |
585 | } | |
586 | ||
587 | /* | |
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. | |
e4f5fa12 | 592 | * 'domain' must be free'd by caller using kmem_strfree() |
d99a0153 CW |
593 | */ |
594 | static int | |
595 | parse_userquota_prop(const char *prop_name, zfs_userquota_prop_t *type, | |
596 | char **domain, uint64_t *rid) | |
597 | { | |
598 | char *cp, *end, *domain_val; | |
599 | ||
600 | *type = get_userquota_prop(prop_name); | |
601 | if (*type >= ZFS_NUM_USERQUOTA_PROPS) | |
602 | return (EINVAL); | |
603 | ||
604 | *rid = 0; | |
605 | cp = strchr(prop_name, '@') + 1; | |
606 | if (strncmp(cp, "S-1-", 4) == 0) { | |
607 | /* | |
608 | * It's a numeric SID (eg "S-1-234-567-89") and we want to | |
e1cfd73f | 609 | * separate the domain id and the rid |
d99a0153 CW |
610 | */ |
611 | int domain_len = strrchr(cp, '-') - cp; | |
612 | domain_val = kmem_alloc(domain_len + 1, KM_SLEEP); | |
7584fbe8 | 613 | (void) strlcpy(domain_val, cp, domain_len + 1); |
d99a0153 CW |
614 | cp += domain_len + 1; |
615 | ||
616 | (void) ddi_strtoll(cp, &end, 10, (longlong_t *)rid); | |
617 | if (*end != '\0') { | |
e4f5fa12 | 618 | kmem_strfree(domain_val); |
d99a0153 CW |
619 | return (EINVAL); |
620 | } | |
621 | } else { | |
622 | /* It's only a user/group ID (eg "12345"), just get the rid */ | |
623 | domain_val = NULL; | |
624 | (void) ddi_strtoll(cp, &end, 10, (longlong_t *)rid); | |
625 | if (*end != '\0') | |
626 | return (EINVAL); | |
627 | } | |
628 | *domain = domain_val; | |
629 | return (0); | |
630 | } | |
631 | ||
632 | /* | |
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 | |
635 | * a lua error. | |
636 | */ | |
637 | static int | |
638 | zcp_get_userquota_prop(lua_State *state, dsl_pool_t *dp, | |
639 | const char *dataset_name, const char *prop_name) | |
640 | { | |
641 | zfsvfs_t *zfvp; | |
642 | zfsvfs_t *zfsvfs; | |
643 | int error; | |
644 | zfs_userquota_prop_t type; | |
645 | char *domain; | |
646 | uint64_t rid, value = 0; | |
647 | objset_t *os; | |
648 | ||
649 | dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG); | |
650 | if (ds == NULL) | |
651 | return (1); /* not reached; zcp_dataset_hold() longjmp'd */ | |
652 | ||
653 | error = parse_userquota_prop(prop_name, &type, &domain, &rid); | |
654 | if (error == 0) { | |
655 | error = dmu_objset_from_ds(ds, &os); | |
656 | if (error == 0) { | |
657 | zfsvfs = kmem_zalloc(sizeof (zfsvfs_t), KM_SLEEP); | |
658 | error = zfsvfs_create_impl(&zfvp, zfsvfs, os); | |
659 | if (error == 0) { | |
660 | error = zfs_userspace_one(zfvp, type, domain, | |
661 | rid, &value); | |
662 | zfsvfs_free(zfvp); | |
663 | } | |
664 | } | |
665 | if (domain != NULL) | |
e4f5fa12 | 666 | kmem_strfree(domain); |
d99a0153 CW |
667 | } |
668 | dsl_dataset_rele(ds, FTAG); | |
669 | ||
670 | if ((value == 0) && ((type == ZFS_PROP_USERQUOTA) || | |
671 | (type == ZFS_PROP_GROUPQUOTA))) | |
28caa74b | 672 | error = SET_ERROR(ENOENT); |
d99a0153 CW |
673 | if (error != 0) { |
674 | return (zcp_handle_error(state, dataset_name, | |
675 | prop_name, error)); | |
676 | } | |
677 | ||
678 | (void) lua_pushnumber(state, value); | |
679 | (void) lua_pushstring(state, dataset_name); | |
680 | return (2); | |
681 | } | |
682 | #endif | |
683 | ||
684 | /* | |
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 | |
688 | */ | |
689 | static void | |
690 | parse_written_prop(const char *dataset_name, const char *prop_name, | |
691 | char *snap_name) | |
692 | { | |
693 | ASSERT(zfs_prop_written(prop_name)); | |
694 | const char *name = prop_name + ZFS_WRITTEN_PROP_PREFIX_LEN; | |
695 | if (strchr(name, '@') == NULL) { | |
c9e319fa JL |
696 | (void) snprintf(snap_name, ZFS_MAX_DATASET_NAME_LEN, "%s@%s", |
697 | dataset_name, name); | |
d99a0153 | 698 | } else { |
c9e319fa | 699 | (void) strlcpy(snap_name, name, ZFS_MAX_DATASET_NAME_LEN); |
d99a0153 CW |
700 | } |
701 | } | |
702 | ||
703 | /* | |
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. | |
707 | */ | |
708 | static int | |
709 | zcp_get_written_prop(lua_State *state, dsl_pool_t *dp, | |
710 | const char *dataset_name, const char *prop_name) | |
711 | { | |
712 | char snap_name[ZFS_MAX_DATASET_NAME_LEN]; | |
713 | uint64_t used, comp, uncomp; | |
714 | dsl_dataset_t *old; | |
715 | int error = 0; | |
716 | ||
717 | parse_written_prop(dataset_name, prop_name, snap_name); | |
718 | dsl_dataset_t *new = zcp_dataset_hold(state, dp, dataset_name, FTAG); | |
719 | if (new == NULL) | |
720 | return (1); /* not reached; zcp_dataset_hold() longjmp'd */ | |
721 | ||
722 | error = dsl_dataset_hold(dp, snap_name, FTAG, &old); | |
723 | if (error != 0) { | |
724 | dsl_dataset_rele(new, FTAG); | |
725 | return (zcp_dataset_hold_error(state, dp, snap_name, | |
726 | error)); | |
727 | } | |
728 | error = dsl_dataset_space_written(old, new, | |
729 | &used, &comp, &uncomp); | |
730 | ||
731 | dsl_dataset_rele(old, FTAG); | |
732 | dsl_dataset_rele(new, FTAG); | |
733 | ||
734 | if (error != 0) { | |
735 | return (zcp_handle_error(state, dataset_name, | |
736 | snap_name, error)); | |
737 | } | |
738 | (void) lua_pushnumber(state, used); | |
739 | (void) lua_pushstring(state, dataset_name); | |
740 | return (2); | |
741 | } | |
742 | ||
743 | static int zcp_get_prop(lua_State *state); | |
18168da7 | 744 | static const zcp_lib_info_t zcp_get_prop_info = { |
d99a0153 CW |
745 | .name = "get_prop", |
746 | .func = zcp_get_prop, | |
747 | .pargs = { | |
18168da7 AZ |
748 | { .za_name = "dataset", .za_lua_type = LUA_TSTRING }, |
749 | { .za_name = "property", .za_lua_type = LUA_TSTRING }, | |
d99a0153 CW |
750 | {NULL, 0} |
751 | }, | |
752 | .kwargs = { | |
753 | {NULL, 0} | |
754 | } | |
755 | }; | |
756 | ||
757 | static int | |
758 | zcp_get_prop(lua_State *state) | |
759 | { | |
760 | const char *dataset_name; | |
761 | const char *property_name; | |
762 | dsl_pool_t *dp = zcp_run_info(state)->zri_pool; | |
18168da7 | 763 | const zcp_lib_info_t *libinfo = &zcp_get_prop_info; |
d99a0153 CW |
764 | |
765 | zcp_parse_args(state, libinfo->name, libinfo->pargs, libinfo->kwargs); | |
766 | ||
767 | dataset_name = lua_tostring(state, 1); | |
768 | property_name = lua_tostring(state, 2); | |
769 | ||
770 | /* User defined property */ | |
771 | if (zfs_prop_user(property_name)) { | |
772 | return (zcp_get_user_prop(state, dp, | |
773 | dataset_name, property_name)); | |
774 | } | |
775 | /* userspace property */ | |
776 | if (zfs_prop_userquota(property_name)) { | |
777 | #ifdef _KERNEL | |
778 | return (zcp_get_userquota_prop(state, dp, | |
779 | dataset_name, property_name)); | |
780 | #else | |
781 | return (luaL_error(state, | |
782 | "user quota properties only supported in kernel mode", | |
783 | property_name)); | |
784 | #endif | |
785 | } | |
786 | /* written@ property */ | |
787 | if (zfs_prop_written(property_name)) { | |
788 | return (zcp_get_written_prop(state, dp, | |
789 | dataset_name, property_name)); | |
790 | } | |
791 | ||
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, | |
796 | zfs_prop)); | |
797 | } | |
798 | ||
799 | /* Invalid property name */ | |
800 | return (luaL_error(state, | |
801 | "'%s' is not a valid property", property_name)); | |
802 | } | |
803 | ||
804 | int | |
805 | zcp_load_get_lib(lua_State *state) | |
806 | { | |
807 | lua_pushcclosure(state, zcp_get_prop_info.func, 0); | |
808 | lua_setfield(state, -2, zcp_get_prop_info.name); | |
809 | ||
810 | return (1); | |
811 | } |