]>
Commit | Line | Data |
---|---|---|
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> | |
37 | #include <sys/zfs_ioctl.h> | |
38 | #include <sys/zfs_znode.h> | |
39 | #include <sys/zvol.h> | |
40 | ||
41 | #ifdef _KERNEL | |
42 | #include <sys/zfs_vfsops.h> | |
43 | #endif | |
44 | ||
45 | static int | |
46 | get_objset_type(dsl_dataset_t *ds, zfs_type_t *type) | |
47 | { | |
48 | int error; | |
49 | objset_t *os; | |
50 | error = dmu_objset_from_ds(ds, &os); | |
51 | if (error != 0) | |
52 | return (error); | |
53 | if (ds->ds_is_snapshot) { | |
54 | *type = ZFS_TYPE_SNAPSHOT; | |
55 | } else { | |
56 | switch (os->os_phys->os_type) { | |
57 | case DMU_OST_ZFS: | |
58 | *type = ZFS_TYPE_FILESYSTEM; | |
59 | break; | |
60 | case DMU_OST_ZVOL: | |
61 | *type = ZFS_TYPE_VOLUME; | |
62 | break; | |
63 | default: | |
64 | return (EINVAL); | |
65 | } | |
66 | } | |
67 | return (0); | |
68 | } | |
69 | ||
70 | /* | |
71 | * Returns the string name of ds's type in str (a buffer which should be | |
72 | * at least 12 bytes long). | |
73 | */ | |
74 | static int | |
75 | get_objset_type_name(dsl_dataset_t *ds, char *str) | |
76 | { | |
77 | int error; | |
78 | zfs_type_t type; | |
79 | error = get_objset_type(ds, &type); | |
80 | if (error != 0) | |
81 | return (error); | |
82 | switch (type) { | |
83 | case ZFS_TYPE_SNAPSHOT: | |
84 | (void) strcpy(str, "snapshot"); | |
85 | break; | |
86 | case ZFS_TYPE_FILESYSTEM: | |
87 | (void) strcpy(str, "filesystem"); | |
88 | break; | |
89 | case ZFS_TYPE_VOLUME: | |
90 | (void) strcpy(str, "volume"); | |
91 | break; | |
92 | default: | |
93 | return (EINVAL); | |
94 | } | |
95 | return (0); | |
96 | } | |
97 | ||
98 | /* | |
99 | * Determines the source of a property given its setpoint and | |
100 | * property type. It pushes the source to the lua stack. | |
101 | */ | |
102 | static void | |
103 | get_prop_src(lua_State *state, const char *setpoint, zfs_prop_t prop) | |
104 | { | |
105 | if (zfs_prop_readonly(prop) || (prop == ZFS_PROP_VERSION)) { | |
106 | lua_pushnil(state); | |
107 | } else { | |
108 | const char *src; | |
109 | if (strcmp("", setpoint) == 0) { | |
110 | src = "default"; | |
111 | } else { | |
112 | src = setpoint; | |
113 | } | |
114 | (void) lua_pushstring(state, src); | |
115 | } | |
116 | } | |
117 | ||
118 | /* | |
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. | |
121 | */ | |
122 | static int | |
123 | zcp_handle_error(lua_State *state, const char *dataset_name, | |
124 | const char *property_name, int error) | |
125 | { | |
126 | ASSERT3S(error, !=, 0); | |
127 | if (error == ENOENT) { | |
128 | return (0); | |
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)); | |
137 | } else { | |
138 | return (luaL_error(state, "unexpected error %d while " | |
139 | "retrieving property '%s' on dataset '%s'", | |
140 | error, property_name, dataset_name)); | |
141 | } | |
142 | } | |
143 | ||
144 | /* | |
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. | |
147 | */ | |
148 | static int | |
149 | zcp_get_user_prop(lua_State *state, dsl_pool_t *dp, const char *dataset_name, | |
150 | const char *property_name) | |
151 | { | |
152 | int error; | |
153 | char *buf; | |
154 | char setpoint[ZFS_MAX_DATASET_NAME_LEN]; | |
155 | /* | |
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 | |
158 | * without returning. | |
159 | */ | |
160 | dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG); | |
161 | if (ds == NULL) | |
162 | return (1); /* not reached; zcp_dataset_hold() longjmp'd */ | |
163 | ||
164 | buf = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP); | |
165 | error = dsl_prop_get_ds(ds, property_name, 1, ZAP_MAXVALUELEN, | |
166 | buf, setpoint); | |
167 | dsl_dataset_rele(ds, FTAG); | |
168 | ||
169 | if (error != 0) { | |
170 | kmem_free(buf, ZAP_MAXVALUELEN); | |
171 | return (zcp_handle_error(state, dataset_name, property_name, | |
172 | error)); | |
173 | } | |
174 | (void) lua_pushstring(state, buf); | |
175 | (void) lua_pushstring(state, setpoint); | |
176 | kmem_free(buf, ZAP_MAXVALUELEN); | |
177 | return (2); | |
178 | } | |
179 | ||
180 | /* | |
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. | |
184 | */ | |
185 | static int | |
186 | get_dsl_dir_prop(dsl_dataset_t *ds, zfs_prop_t zfs_prop, | |
187 | uint64_t *val) | |
188 | { | |
189 | dsl_dir_t *dd = ds->ds_dir; | |
190 | mutex_enter(&dd->dd_lock); | |
191 | switch (zfs_prop) { | |
192 | case ZFS_PROP_USEDSNAP: | |
193 | *val = dsl_dir_get_usedsnap(dd); | |
194 | break; | |
195 | case ZFS_PROP_USEDCHILD: | |
196 | *val = dsl_dir_get_usedchild(dd); | |
197 | break; | |
198 | case ZFS_PROP_USEDDS: | |
199 | *val = dsl_dir_get_usedds(dd); | |
200 | break; | |
201 | case ZFS_PROP_USEDREFRESERV: | |
202 | *val = dsl_dir_get_usedrefreserv(dd); | |
203 | break; | |
204 | case ZFS_PROP_LOGICALUSED: | |
205 | *val = dsl_dir_get_logicalused(dd); | |
206 | break; | |
207 | default: | |
208 | mutex_exit(&dd->dd_lock); | |
209 | return (ENOENT); | |
210 | } | |
211 | mutex_exit(&dd->dd_lock); | |
212 | return (0); | |
213 | } | |
214 | ||
215 | /* | |
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. | |
220 | */ | |
221 | static int | |
222 | get_temporary_prop(dsl_dataset_t *ds, zfs_prop_t zfs_prop, uint64_t *val, | |
223 | char *setpoint) | |
224 | { | |
225 | #if !defined(_KERNEL) | |
226 | return (0); | |
227 | #else | |
228 | int error; | |
229 | zfsvfs_t *zfvp; | |
230 | vfs_t *vfsp; | |
231 | objset_t *os; | |
232 | uint64_t tmp = *val; | |
233 | ||
234 | error = dmu_objset_from_ds(ds, &os); | |
235 | if (error != 0) | |
236 | return (error); | |
237 | ||
238 | if (dmu_objset_type(os) != DMU_OST_ZFS) | |
239 | return (EINVAL); | |
240 | ||
241 | mutex_enter(&os->os_user_ptr_lock); | |
242 | zfvp = dmu_objset_get_user(os); | |
243 | mutex_exit(&os->os_user_ptr_lock); | |
244 | if (zfvp == NULL) | |
245 | return (ESRCH); | |
246 | ||
247 | vfsp = zfvp->z_vfs; | |
248 | ||
249 | switch (zfs_prop) { | |
250 | case ZFS_PROP_ATIME: | |
251 | if (vfsp->vfs_do_atime) | |
252 | tmp = vfsp->vfs_atime; | |
253 | break; | |
254 | case ZFS_PROP_RELATIME: | |
255 | if (vfsp->vfs_do_relatime) | |
256 | tmp = vfsp->vfs_relatime; | |
257 | break; | |
258 | case ZFS_PROP_DEVICES: | |
259 | if (vfsp->vfs_do_devices) | |
260 | tmp = vfsp->vfs_devices; | |
261 | break; | |
262 | case ZFS_PROP_EXEC: | |
263 | if (vfsp->vfs_do_exec) | |
264 | tmp = vfsp->vfs_exec; | |
265 | break; | |
266 | case ZFS_PROP_SETUID: | |
267 | if (vfsp->vfs_do_setuid) | |
268 | tmp = vfsp->vfs_setuid; | |
269 | break; | |
270 | case ZFS_PROP_READONLY: | |
271 | if (vfsp->vfs_do_readonly) | |
272 | tmp = vfsp->vfs_readonly; | |
273 | break; | |
274 | case ZFS_PROP_XATTR: | |
275 | if (vfsp->vfs_do_xattr) | |
276 | tmp = vfsp->vfs_xattr; | |
277 | break; | |
278 | case ZFS_PROP_NBMAND: | |
279 | if (vfsp->vfs_do_nbmand) | |
280 | tmp = vfsp->vfs_nbmand; | |
281 | break; | |
282 | default: | |
283 | return (ENOENT); | |
284 | } | |
285 | ||
286 | if (tmp != *val) { | |
287 | (void) strcpy(setpoint, "temporary"); | |
288 | *val = tmp; | |
289 | } | |
290 | return (0); | |
291 | #endif | |
292 | } | |
293 | ||
294 | /* | |
295 | * Check if the property we're looking for is stored at the dsl_dataset or | |
296 | * dsl_dir level. If so, push the property value and source onto the lua stack | |
297 | * and return 0. If it is not present or a failure occurs in lookup, return a | |
298 | * non-zero error value. | |
299 | */ | |
300 | static int | |
301 | get_special_prop(lua_State *state, dsl_dataset_t *ds, const char *dsname, | |
302 | zfs_prop_t zfs_prop) | |
303 | { | |
304 | int error = 0; | |
305 | objset_t *os; | |
306 | uint64_t numval = 0; | |
307 | char *strval = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP); | |
308 | char setpoint[ZFS_MAX_DATASET_NAME_LEN] = | |
309 | "Internal error - setpoint not determined"; | |
310 | zfs_type_t ds_type; | |
311 | zprop_type_t prop_type = zfs_prop_get_type(zfs_prop); | |
312 | (void) get_objset_type(ds, &ds_type); | |
313 | ||
314 | switch (zfs_prop) { | |
315 | case ZFS_PROP_REFRATIO: | |
316 | numval = dsl_get_refratio(ds); | |
317 | break; | |
318 | case ZFS_PROP_USED: | |
319 | numval = dsl_get_used(ds); | |
320 | break; | |
321 | case ZFS_PROP_CLONES: { | |
322 | nvlist_t *clones = fnvlist_alloc(); | |
323 | error = get_clones_stat_impl(ds, clones); | |
324 | if (error == 0) { | |
325 | /* push list to lua stack */ | |
326 | VERIFY0(zcp_nvlist_to_lua(state, clones, NULL, 0ULL)); | |
327 | /* source */ | |
328 | (void) lua_pushnil(state); | |
329 | } | |
330 | nvlist_free(clones); | |
331 | kmem_free(strval, ZAP_MAXVALUELEN); | |
332 | return (error); | |
333 | } | |
334 | case ZFS_PROP_COMPRESSRATIO: | |
335 | numval = dsl_get_compressratio(ds); | |
336 | break; | |
337 | case ZFS_PROP_CREATION: | |
338 | numval = dsl_get_creation(ds); | |
339 | break; | |
340 | case ZFS_PROP_REFERENCED: | |
341 | numval = dsl_get_referenced(ds); | |
342 | break; | |
343 | case ZFS_PROP_AVAILABLE: | |
344 | numval = dsl_get_available(ds); | |
345 | break; | |
346 | case ZFS_PROP_LOGICALREFERENCED: | |
347 | numval = dsl_get_logicalreferenced(ds); | |
348 | break; | |
349 | case ZFS_PROP_CREATETXG: | |
350 | numval = dsl_get_creationtxg(ds); | |
351 | break; | |
352 | case ZFS_PROP_GUID: | |
353 | numval = dsl_get_guid(ds); | |
354 | break; | |
355 | case ZFS_PROP_UNIQUE: | |
356 | numval = dsl_get_unique(ds); | |
357 | break; | |
358 | case ZFS_PROP_OBJSETID: | |
359 | numval = dsl_get_objsetid(ds); | |
360 | break; | |
361 | case ZFS_PROP_ORIGIN: | |
362 | dsl_dir_get_origin(ds->ds_dir, strval); | |
363 | break; | |
364 | case ZFS_PROP_USERACCOUNTING: | |
365 | error = dmu_objset_from_ds(ds, &os); | |
366 | if (error == 0) | |
367 | numval = dmu_objset_userspace_present(os); | |
368 | break; | |
369 | case ZFS_PROP_WRITTEN: | |
370 | error = dsl_get_written(ds, &numval); | |
371 | break; | |
372 | case ZFS_PROP_TYPE: | |
373 | error = get_objset_type_name(ds, strval); | |
374 | break; | |
375 | case ZFS_PROP_PREV_SNAP: | |
376 | error = dsl_get_prev_snap(ds, strval); | |
377 | break; | |
378 | case ZFS_PROP_NAME: | |
379 | dsl_dataset_name(ds, strval); | |
380 | break; | |
381 | case ZFS_PROP_MOUNTPOINT: | |
382 | error = dsl_get_mountpoint(ds, dsname, strval, setpoint); | |
383 | break; | |
384 | case ZFS_PROP_VERSION: | |
385 | /* should be a snapshot or filesystem */ | |
386 | ASSERT(ds_type != ZFS_TYPE_VOLUME); | |
387 | error = dmu_objset_from_ds(ds, &os); | |
388 | /* look in the master node for the version */ | |
389 | if (error == 0) { | |
390 | error = zap_lookup(os, MASTER_NODE_OBJ, ZPL_VERSION_STR, | |
391 | sizeof (numval), 1, &numval); | |
392 | } | |
393 | break; | |
394 | case ZFS_PROP_DEFER_DESTROY: | |
395 | numval = dsl_get_defer_destroy(ds); | |
396 | break; | |
397 | case ZFS_PROP_USERREFS: | |
398 | numval = dsl_get_userrefs(ds); | |
399 | break; | |
400 | case ZFS_PROP_FILESYSTEM_COUNT: | |
401 | error = dsl_dir_get_filesystem_count(ds->ds_dir, &numval); | |
402 | (void) strcpy(setpoint, ""); | |
403 | break; | |
404 | case ZFS_PROP_SNAPSHOT_COUNT: | |
405 | error = dsl_dir_get_snapshot_count(ds->ds_dir, &numval); | |
406 | (void) strcpy(setpoint, ""); | |
407 | break; | |
408 | case ZFS_PROP_NUMCLONES: | |
409 | numval = dsl_get_numclones(ds); | |
410 | break; | |
411 | case ZFS_PROP_INCONSISTENT: | |
412 | numval = dsl_get_inconsistent(ds); | |
413 | break; | |
414 | case ZFS_PROP_IVSET_GUID: | |
415 | if (dsl_dataset_is_zapified(ds)) { | |
416 | error = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset, | |
417 | ds->ds_object, DS_FIELD_IVSET_GUID, | |
418 | sizeof (numval), 1, &numval); | |
419 | } else { | |
420 | error = ENOENT; | |
421 | } | |
422 | break; | |
423 | case ZFS_PROP_RECEIVE_RESUME_TOKEN: { | |
424 | char *token = get_receive_resume_stats_impl(ds); | |
425 | ||
426 | VERIFY3U(strlcpy(strval, token, ZAP_MAXVALUELEN), | |
427 | <, ZAP_MAXVALUELEN); | |
428 | if (strcmp(strval, "") == 0) { | |
429 | char *childval = get_child_receive_stats(ds); | |
430 | ||
431 | VERIFY3U(strlcpy(strval, childval, ZAP_MAXVALUELEN), | |
432 | <, ZAP_MAXVALUELEN); | |
433 | if (strcmp(strval, "") == 0) | |
434 | error = ENOENT; | |
435 | ||
436 | strfree(childval); | |
437 | } | |
438 | strfree(token); | |
439 | break; | |
440 | } | |
441 | case ZFS_PROP_VOLSIZE: | |
442 | ASSERT(ds_type == ZFS_TYPE_VOLUME || | |
443 | ds_type == ZFS_TYPE_SNAPSHOT); | |
444 | error = dmu_objset_from_ds(ds, &os); | |
445 | if (error == 0) { | |
446 | error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", | |
447 | sizeof (numval), 1, &numval); | |
448 | } | |
449 | if (error == 0) | |
450 | (void) strcpy(setpoint, dsname); | |
451 | ||
452 | break; | |
453 | case ZFS_PROP_VOLBLOCKSIZE: { | |
454 | ASSERT(ds_type == ZFS_TYPE_VOLUME); | |
455 | dmu_object_info_t doi; | |
456 | error = dmu_objset_from_ds(ds, &os); | |
457 | if (error == 0) { | |
458 | error = dmu_object_info(os, ZVOL_OBJ, &doi); | |
459 | if (error == 0) | |
460 | numval = doi.doi_data_block_size; | |
461 | } | |
462 | break; | |
463 | } | |
464 | ||
465 | case ZFS_PROP_KEYSTATUS: | |
466 | case ZFS_PROP_KEYFORMAT: { | |
467 | /* provide defaults in case no crypto obj exists */ | |
468 | setpoint[0] = '\0'; | |
469 | if (zfs_prop == ZFS_PROP_KEYSTATUS) | |
470 | numval = ZFS_KEYSTATUS_NONE; | |
471 | else | |
472 | numval = ZFS_KEYFORMAT_NONE; | |
473 | ||
474 | nvlist_t *nvl, *propval; | |
475 | nvl = fnvlist_alloc(); | |
476 | dsl_dataset_crypt_stats(ds, nvl); | |
477 | if (nvlist_lookup_nvlist(nvl, zfs_prop_to_name(zfs_prop), | |
478 | &propval) == 0) { | |
479 | char *source; | |
480 | ||
481 | (void) nvlist_lookup_uint64(propval, ZPROP_VALUE, | |
482 | &numval); | |
483 | if (nvlist_lookup_string(propval, ZPROP_SOURCE, | |
484 | &source) == 0) | |
485 | strlcpy(setpoint, source, sizeof (setpoint)); | |
486 | } | |
487 | nvlist_free(nvl); | |
488 | break; | |
489 | } | |
490 | ||
491 | default: | |
492 | /* Did not match these props, check in the dsl_dir */ | |
493 | error = get_dsl_dir_prop(ds, zfs_prop, &numval); | |
494 | } | |
495 | if (error != 0) { | |
496 | kmem_free(strval, ZAP_MAXVALUELEN); | |
497 | return (error); | |
498 | } | |
499 | ||
500 | switch (prop_type) { | |
501 | case PROP_TYPE_NUMBER: { | |
502 | (void) lua_pushnumber(state, numval); | |
503 | break; | |
504 | } | |
505 | case PROP_TYPE_STRING: { | |
506 | (void) lua_pushstring(state, strval); | |
507 | break; | |
508 | } | |
509 | case PROP_TYPE_INDEX: { | |
510 | const char *propval; | |
511 | error = zfs_prop_index_to_string(zfs_prop, numval, &propval); | |
512 | if (error != 0) { | |
513 | kmem_free(strval, ZAP_MAXVALUELEN); | |
514 | return (error); | |
515 | } | |
516 | (void) lua_pushstring(state, propval); | |
517 | break; | |
518 | } | |
519 | } | |
520 | kmem_free(strval, ZAP_MAXVALUELEN); | |
521 | ||
522 | /* Push the source to the stack */ | |
523 | get_prop_src(state, setpoint, zfs_prop); | |
524 | return (0); | |
525 | } | |
526 | ||
527 | /* | |
528 | * Look up a property and its source in the zap object. If the value is | |
529 | * present and successfully retrieved, push the value and source on the | |
530 | * lua stack and return 0. On failure, return a non-zero error value. | |
531 | */ | |
532 | static int | |
533 | get_zap_prop(lua_State *state, dsl_dataset_t *ds, zfs_prop_t zfs_prop) | |
534 | { | |
535 | int error = 0; | |
536 | char setpoint[ZFS_MAX_DATASET_NAME_LEN]; | |
537 | char *strval = kmem_alloc(ZAP_MAXVALUELEN, KM_SLEEP); | |
538 | uint64_t numval; | |
539 | const char *prop_name = zfs_prop_to_name(zfs_prop); | |
540 | zprop_type_t prop_type = zfs_prop_get_type(zfs_prop); | |
541 | ||
542 | if (prop_type == PROP_TYPE_STRING) { | |
543 | /* Push value to lua stack */ | |
544 | error = dsl_prop_get_ds(ds, prop_name, 1, | |
545 | ZAP_MAXVALUELEN, strval, setpoint); | |
546 | if (error == 0) | |
547 | (void) lua_pushstring(state, strval); | |
548 | } else { | |
549 | error = dsl_prop_get_ds(ds, prop_name, sizeof (numval), | |
550 | 1, &numval, setpoint); | |
551 | ||
552 | /* Fill in temorary value for prop, if applicable */ | |
553 | (void) get_temporary_prop(ds, zfs_prop, &numval, setpoint); | |
554 | ||
555 | /* Push value to lua stack */ | |
556 | if (prop_type == PROP_TYPE_INDEX) { | |
557 | const char *propval; | |
558 | error = zfs_prop_index_to_string(zfs_prop, numval, | |
559 | &propval); | |
560 | if (error == 0) | |
561 | (void) lua_pushstring(state, propval); | |
562 | } else { | |
563 | if (error == 0) | |
564 | (void) lua_pushnumber(state, numval); | |
565 | } | |
566 | } | |
567 | kmem_free(strval, ZAP_MAXVALUELEN); | |
568 | if (error == 0) | |
569 | get_prop_src(state, setpoint, zfs_prop); | |
570 | return (error); | |
571 | } | |
572 | ||
573 | /* | |
574 | * Determine whether property is valid for a given dataset | |
575 | */ | |
576 | boolean_t | |
577 | prop_valid_for_ds(dsl_dataset_t *ds, zfs_prop_t zfs_prop) | |
578 | { | |
579 | int error; | |
580 | zfs_type_t zfs_type; | |
581 | ||
582 | /* properties not supported */ | |
583 | if ((zfs_prop == ZFS_PROP_ISCSIOPTIONS) || | |
584 | (zfs_prop == ZFS_PROP_MOUNTED)) | |
585 | return (B_FALSE); | |
586 | ||
587 | /* if we want the origin prop, ds must be a clone */ | |
588 | if ((zfs_prop == ZFS_PROP_ORIGIN) && (!dsl_dir_is_clone(ds->ds_dir))) | |
589 | return (B_FALSE); | |
590 | ||
591 | error = get_objset_type(ds, &zfs_type); | |
592 | if (error != 0) | |
593 | return (B_FALSE); | |
594 | return (zfs_prop_valid_for_type(zfs_prop, zfs_type, B_FALSE)); | |
595 | } | |
596 | ||
597 | /* | |
598 | * Look up a given dataset property. On success return 2, the number of | |
599 | * values pushed to the lua stack (property value and source). On a fatal | |
600 | * error, longjmp. On a non fatal error push nothing. | |
601 | */ | |
602 | static int | |
603 | zcp_get_system_prop(lua_State *state, dsl_pool_t *dp, const char *dataset_name, | |
604 | zfs_prop_t zfs_prop) | |
605 | { | |
606 | int error; | |
607 | /* | |
608 | * zcp_dataset_hold will either successfully return the requested | |
609 | * dataset or throw a lua error and longjmp out of the zfs.get_prop call | |
610 | * without returning. | |
611 | */ | |
612 | dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG); | |
613 | if (ds == NULL) | |
614 | return (1); /* not reached; zcp_dataset_hold() longjmp'd */ | |
615 | ||
616 | /* Check that the property is valid for the given dataset */ | |
617 | const char *prop_name = zfs_prop_to_name(zfs_prop); | |
618 | if (!prop_valid_for_ds(ds, zfs_prop)) { | |
619 | dsl_dataset_rele(ds, FTAG); | |
620 | return (0); | |
621 | } | |
622 | ||
623 | /* Check if the property can be accessed directly */ | |
624 | error = get_special_prop(state, ds, dataset_name, zfs_prop); | |
625 | if (error == 0) { | |
626 | dsl_dataset_rele(ds, FTAG); | |
627 | /* The value and source have been pushed by get_special_prop */ | |
628 | return (2); | |
629 | } | |
630 | if (error != ENOENT) { | |
631 | dsl_dataset_rele(ds, FTAG); | |
632 | return (zcp_handle_error(state, dataset_name, | |
633 | prop_name, error)); | |
634 | } | |
635 | ||
636 | /* If we were unable to find it, look in the zap object */ | |
637 | error = get_zap_prop(state, ds, zfs_prop); | |
638 | dsl_dataset_rele(ds, FTAG); | |
639 | if (error != 0) { | |
640 | return (zcp_handle_error(state, dataset_name, | |
641 | prop_name, error)); | |
642 | } | |
643 | /* The value and source have been pushed by get_zap_prop */ | |
644 | return (2); | |
645 | } | |
646 | ||
647 | #ifdef _KERNEL | |
648 | static zfs_userquota_prop_t | |
649 | get_userquota_prop(const char *prop_name) | |
650 | { | |
651 | zfs_userquota_prop_t type; | |
652 | /* Figure out the property type ({user|group}{quota|used}) */ | |
653 | for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) { | |
654 | if (strncmp(prop_name, zfs_userquota_prop_prefixes[type], | |
655 | strlen(zfs_userquota_prop_prefixes[type])) == 0) | |
656 | break; | |
657 | } | |
658 | return (type); | |
659 | } | |
660 | ||
661 | /* | |
662 | * Given the name of a zfs_userquota_prop, this function determines the | |
663 | * prop type as well as the numeric group/user ids based on the string | |
664 | * following the '@' in the property name. On success, returns 0. On failure, | |
665 | * returns a non-zero error. | |
666 | * 'domain' must be free'd by caller using strfree() | |
667 | */ | |
668 | static int | |
669 | parse_userquota_prop(const char *prop_name, zfs_userquota_prop_t *type, | |
670 | char **domain, uint64_t *rid) | |
671 | { | |
672 | char *cp, *end, *domain_val; | |
673 | ||
674 | *type = get_userquota_prop(prop_name); | |
675 | if (*type >= ZFS_NUM_USERQUOTA_PROPS) | |
676 | return (EINVAL); | |
677 | ||
678 | *rid = 0; | |
679 | cp = strchr(prop_name, '@') + 1; | |
680 | if (strncmp(cp, "S-1-", 4) == 0) { | |
681 | /* | |
682 | * It's a numeric SID (eg "S-1-234-567-89") and we want to | |
683 | * seperate the domain id and the rid | |
684 | */ | |
685 | int domain_len = strrchr(cp, '-') - cp; | |
686 | domain_val = kmem_alloc(domain_len + 1, KM_SLEEP); | |
687 | (void) strncpy(domain_val, cp, domain_len); | |
688 | domain_val[domain_len] = '\0'; | |
689 | cp += domain_len + 1; | |
690 | ||
691 | (void) ddi_strtoll(cp, &end, 10, (longlong_t *)rid); | |
692 | if (*end != '\0') { | |
693 | strfree(domain_val); | |
694 | return (EINVAL); | |
695 | } | |
696 | } else { | |
697 | /* It's only a user/group ID (eg "12345"), just get the rid */ | |
698 | domain_val = NULL; | |
699 | (void) ddi_strtoll(cp, &end, 10, (longlong_t *)rid); | |
700 | if (*end != '\0') | |
701 | return (EINVAL); | |
702 | } | |
703 | *domain = domain_val; | |
704 | return (0); | |
705 | } | |
706 | ||
707 | /* | |
708 | * Look up {user|group}{quota|used} property for given dataset. On success | |
709 | * push the value (quota or used amount) and the setpoint. On failure, push | |
710 | * a lua error. | |
711 | */ | |
712 | static int | |
713 | zcp_get_userquota_prop(lua_State *state, dsl_pool_t *dp, | |
714 | const char *dataset_name, const char *prop_name) | |
715 | { | |
716 | zfsvfs_t *zfvp; | |
717 | zfsvfs_t *zfsvfs; | |
718 | int error; | |
719 | zfs_userquota_prop_t type; | |
720 | char *domain; | |
721 | uint64_t rid, value = 0; | |
722 | objset_t *os; | |
723 | ||
724 | dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG); | |
725 | if (ds == NULL) | |
726 | return (1); /* not reached; zcp_dataset_hold() longjmp'd */ | |
727 | ||
728 | error = parse_userquota_prop(prop_name, &type, &domain, &rid); | |
729 | if (error == 0) { | |
730 | error = dmu_objset_from_ds(ds, &os); | |
731 | if (error == 0) { | |
732 | zfsvfs = kmem_zalloc(sizeof (zfsvfs_t), KM_SLEEP); | |
733 | error = zfsvfs_create_impl(&zfvp, zfsvfs, os); | |
734 | if (error == 0) { | |
735 | error = zfs_userspace_one(zfvp, type, domain, | |
736 | rid, &value); | |
737 | zfsvfs_free(zfvp); | |
738 | } | |
739 | } | |
740 | if (domain != NULL) | |
741 | strfree(domain); | |
742 | } | |
743 | dsl_dataset_rele(ds, FTAG); | |
744 | ||
745 | if ((value == 0) && ((type == ZFS_PROP_USERQUOTA) || | |
746 | (type == ZFS_PROP_GROUPQUOTA))) | |
747 | error = ENOENT; | |
748 | if (error != 0) { | |
749 | return (zcp_handle_error(state, dataset_name, | |
750 | prop_name, error)); | |
751 | } | |
752 | ||
753 | (void) lua_pushnumber(state, value); | |
754 | (void) lua_pushstring(state, dataset_name); | |
755 | return (2); | |
756 | } | |
757 | #endif | |
758 | ||
759 | /* | |
760 | * Determines the name of the snapshot referenced in the written property | |
761 | * name. Returns snapshot name in snap_name, a buffer that must be at least | |
762 | * as large as ZFS_MAX_DATASET_NAME_LEN | |
763 | */ | |
764 | static void | |
765 | parse_written_prop(const char *dataset_name, const char *prop_name, | |
766 | char *snap_name) | |
767 | { | |
768 | ASSERT(zfs_prop_written(prop_name)); | |
769 | const char *name = prop_name + ZFS_WRITTEN_PROP_PREFIX_LEN; | |
770 | if (strchr(name, '@') == NULL) { | |
771 | (void) sprintf(snap_name, "%s@%s", dataset_name, name); | |
772 | } else { | |
773 | (void) strcpy(snap_name, name); | |
774 | } | |
775 | } | |
776 | ||
777 | /* | |
778 | * Look up written@ property for given dataset. On success | |
779 | * push the value and the setpoint. If error is fatal, we will | |
780 | * longjmp, otherwise push nothing. | |
781 | */ | |
782 | static int | |
783 | zcp_get_written_prop(lua_State *state, dsl_pool_t *dp, | |
784 | const char *dataset_name, const char *prop_name) | |
785 | { | |
786 | char snap_name[ZFS_MAX_DATASET_NAME_LEN]; | |
787 | uint64_t used, comp, uncomp; | |
788 | dsl_dataset_t *old; | |
789 | int error = 0; | |
790 | ||
791 | parse_written_prop(dataset_name, prop_name, snap_name); | |
792 | dsl_dataset_t *new = zcp_dataset_hold(state, dp, dataset_name, FTAG); | |
793 | if (new == NULL) | |
794 | return (1); /* not reached; zcp_dataset_hold() longjmp'd */ | |
795 | ||
796 | error = dsl_dataset_hold(dp, snap_name, FTAG, &old); | |
797 | if (error != 0) { | |
798 | dsl_dataset_rele(new, FTAG); | |
799 | return (zcp_dataset_hold_error(state, dp, snap_name, | |
800 | error)); | |
801 | } | |
802 | error = dsl_dataset_space_written(old, new, | |
803 | &used, &comp, &uncomp); | |
804 | ||
805 | dsl_dataset_rele(old, FTAG); | |
806 | dsl_dataset_rele(new, FTAG); | |
807 | ||
808 | if (error != 0) { | |
809 | return (zcp_handle_error(state, dataset_name, | |
810 | snap_name, error)); | |
811 | } | |
812 | (void) lua_pushnumber(state, used); | |
813 | (void) lua_pushstring(state, dataset_name); | |
814 | return (2); | |
815 | } | |
816 | ||
817 | static int zcp_get_prop(lua_State *state); | |
818 | static zcp_lib_info_t zcp_get_prop_info = { | |
819 | .name = "get_prop", | |
820 | .func = zcp_get_prop, | |
821 | .pargs = { | |
822 | { .za_name = "dataset", .za_lua_type = LUA_TSTRING}, | |
823 | { .za_name = "property", .za_lua_type = LUA_TSTRING}, | |
824 | {NULL, 0} | |
825 | }, | |
826 | .kwargs = { | |
827 | {NULL, 0} | |
828 | } | |
829 | }; | |
830 | ||
831 | static int | |
832 | zcp_get_prop(lua_State *state) | |
833 | { | |
834 | const char *dataset_name; | |
835 | const char *property_name; | |
836 | dsl_pool_t *dp = zcp_run_info(state)->zri_pool; | |
837 | zcp_lib_info_t *libinfo = &zcp_get_prop_info; | |
838 | ||
839 | zcp_parse_args(state, libinfo->name, libinfo->pargs, libinfo->kwargs); | |
840 | ||
841 | dataset_name = lua_tostring(state, 1); | |
842 | property_name = lua_tostring(state, 2); | |
843 | ||
844 | /* User defined property */ | |
845 | if (zfs_prop_user(property_name)) { | |
846 | return (zcp_get_user_prop(state, dp, | |
847 | dataset_name, property_name)); | |
848 | } | |
849 | /* userspace property */ | |
850 | if (zfs_prop_userquota(property_name)) { | |
851 | #ifdef _KERNEL | |
852 | return (zcp_get_userquota_prop(state, dp, | |
853 | dataset_name, property_name)); | |
854 | #else | |
855 | return (luaL_error(state, | |
856 | "user quota properties only supported in kernel mode", | |
857 | property_name)); | |
858 | #endif | |
859 | } | |
860 | /* written@ property */ | |
861 | if (zfs_prop_written(property_name)) { | |
862 | return (zcp_get_written_prop(state, dp, | |
863 | dataset_name, property_name)); | |
864 | } | |
865 | ||
866 | zfs_prop_t zfs_prop = zfs_name_to_prop(property_name); | |
867 | /* Valid system property */ | |
868 | if (zfs_prop != ZPROP_INVAL) { | |
869 | return (zcp_get_system_prop(state, dp, dataset_name, | |
870 | zfs_prop)); | |
871 | } | |
872 | ||
873 | /* Invalid property name */ | |
874 | return (luaL_error(state, | |
875 | "'%s' is not a valid property", property_name)); | |
876 | } | |
877 | ||
878 | int | |
879 | zcp_load_get_lib(lua_State *state) | |
880 | { | |
881 | lua_pushcclosure(state, zcp_get_prop_info.func, 0); | |
882 | lua_setfield(state, -2, zcp_get_prop_info.name); | |
883 | ||
884 | return (1); | |
885 | } |