]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libzfs/os/freebsd/libzfs_ioctl_compat.c
Vendor import of openzfs master @ 184df27eef0abdc7ab2105b21257f753834b936b
[FreeBSD/FreeBSD.git] / lib / libzfs / os / freebsd / libzfs_ioctl_compat.c
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2013 Xin Li <delphij@FreeBSD.org>. All rights reserved.
23  * Copyright 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
24  * Portions Copyright 2005, 2010, Oracle and/or its affiliates.
25  * All rights reserved.
26  * Use is subject to license terms.
27  */
28
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/cred.h>
32 #include <sys/dmu.h>
33 #include <sys/zio.h>
34 #include <sys/nvpair.h>
35 #include <sys/dsl_deleg.h>
36 #include <sys/zfs_ioctl.h>
37 #include "zfs_namecheck.h"
38 #include <os/freebsd/zfs/sys/zfs_ioctl_compat.h>
39
40 /*
41  * FreeBSD zfs_cmd compatibility with older binaries
42  * appropriately remap/extend the zfs_cmd_t structure
43  */
44 void
45 zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag)
46 {
47
48 }
49 #if 0
50 static int
51 zfs_ioctl_compat_get_nvlist(uint64_t nvl, size_t size, int iflag,
52     nvlist_t **nvp)
53 {
54         char *packed;
55         int error;
56         nvlist_t *list = NULL;
57
58         /*
59          * Read in and unpack the user-supplied nvlist.
60          */
61         if (size == 0)
62                 return (EINVAL);
63
64 #ifdef _KERNEL
65         packed = kmem_alloc(size, KM_SLEEP);
66         if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size,
67             iflag)) != 0) {
68                 kmem_free(packed, size);
69                 return (error);
70         }
71 #else
72         packed = (void *)(uintptr_t)nvl;
73 #endif
74
75         error = nvlist_unpack(packed, size, &list, 0);
76
77 #ifdef _KERNEL
78         kmem_free(packed, size);
79 #endif
80
81         if (error != 0)
82                 return (error);
83
84         *nvp = list;
85         return (0);
86 }
87
88 static int
89 zfs_ioctl_compat_put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
90 {
91         char *packed = NULL;
92         int error = 0;
93         size_t size;
94
95         VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
96
97 #ifdef _KERNEL
98         packed = kmem_alloc(size, KM_SLEEP);
99         VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
100             KM_SLEEP) == 0);
101
102         if (ddi_copyout(packed,
103             (void *)(uintptr_t)zc->zc_nvlist_dst, size, zc->zc_iflags) != 0)
104                 error = EFAULT;
105         kmem_free(packed, size);
106 #else
107         packed = (void *)(uintptr_t)zc->zc_nvlist_dst;
108         VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
109             0) == 0);
110 #endif
111
112         zc->zc_nvlist_dst_size = size;
113         return (error);
114 }
115
116 static void
117 zfs_ioctl_compat_fix_stats_nvlist(nvlist_t *nvl)
118 {
119         nvlist_t **child;
120         nvlist_t *nvroot = NULL;
121         vdev_stat_t *vs;
122         uint_t c, children, nelem;
123
124         if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_CHILDREN,
125             &child, &children) == 0) {
126                 for (c = 0; c < children; c++) {
127                         zfs_ioctl_compat_fix_stats_nvlist(child[c]);
128                 }
129         }
130
131         if (nvlist_lookup_nvlist(nvl, ZPOOL_CONFIG_VDEV_TREE,
132             &nvroot) == 0)
133                 zfs_ioctl_compat_fix_stats_nvlist(nvroot);
134         if ((nvlist_lookup_uint64_array(nvl, "stats",
135             (uint64_t **)&vs, &nelem) == 0)) {
136                 nvlist_add_uint64_array(nvl,
137                     ZPOOL_CONFIG_VDEV_STATS,
138                     (uint64_t *)vs, nelem);
139                 nvlist_remove(nvl, "stats",
140                     DATA_TYPE_UINT64_ARRAY);
141         }
142 }
143
144
145 static int
146 zfs_ioctl_compat_fix_stats(zfs_cmd_t *zc, const int nc)
147 {
148         nvlist_t *nv, *nvp = NULL;
149         nvpair_t *elem;
150         int error;
151
152         if ((error = zfs_ioctl_compat_get_nvlist(zc->zc_nvlist_dst,
153             zc->zc_nvlist_dst_size, zc->zc_iflags, &nv)) != 0)
154                 return (error);
155
156         if (nc == 5) { /* ZFS_IOC_POOL_STATS */
157                 elem = NULL;
158                 while ((elem = nvlist_next_nvpair(nv, elem)) != NULL) {
159                         if (nvpair_value_nvlist(elem, &nvp) == 0)
160                                 zfs_ioctl_compat_fix_stats_nvlist(nvp);
161                 }
162                 elem = NULL;
163         } else
164                 zfs_ioctl_compat_fix_stats_nvlist(nv);
165
166         error = zfs_ioctl_compat_put_nvlist(zc, nv);
167
168         nvlist_free(nv);
169
170         return (error);
171 }
172
173 static int
174 zfs_ioctl_compat_pool_get_props(zfs_cmd_t *zc)
175 {
176         nvlist_t *nv, *nva = NULL;
177         int error;
178
179         if ((error = zfs_ioctl_compat_get_nvlist(zc->zc_nvlist_dst,
180             zc->zc_nvlist_dst_size, zc->zc_iflags, &nv)) != 0)
181                 return (error);
182
183         if (nvlist_lookup_nvlist(nv, "used", &nva) == 0) {
184                 nvlist_add_nvlist(nv, "allocated", nva);
185                 nvlist_remove(nv, "used", DATA_TYPE_NVLIST);
186         }
187
188         if (nvlist_lookup_nvlist(nv, "available", &nva) == 0) {
189                 nvlist_add_nvlist(nv, "free", nva);
190                 nvlist_remove(nv, "available", DATA_TYPE_NVLIST);
191         }
192
193         error = zfs_ioctl_compat_put_nvlist(zc, nv);
194
195         nvlist_free(nv);
196
197         return (error);
198 }
199 #endif
200
201 #ifdef _KERNEL
202 int
203 zfs_ioctl_compat_pre(zfs_cmd_t *zc, int *vec, const int cflag)
204 {
205         int error = 0;
206
207         /* are we creating a clone? */
208         if (*vec == ZFS_IOC_CREATE && zc->zc_value[0] != '\0')
209                 *vec = ZFS_IOC_CLONE;
210
211         if (cflag == ZFS_CMD_COMPAT_V15) {
212                 switch (*vec) {
213
214                 case 7: /* ZFS_IOC_POOL_SCRUB (v15) */
215                         zc->zc_cookie = POOL_SCAN_SCRUB;
216                         break;
217                 }
218         }
219
220         return (error);
221 }
222
223 void
224 zfs_ioctl_compat_post(zfs_cmd_t *zc, int vec, const int cflag)
225 {
226         if (cflag == ZFS_CMD_COMPAT_V15) {
227                 switch (vec) {
228                 case ZFS_IOC_POOL_CONFIGS:
229                 case ZFS_IOC_POOL_STATS:
230                 case ZFS_IOC_POOL_TRYIMPORT:
231                         zfs_ioctl_compat_fix_stats(zc, vec);
232                         break;
233                 case 41: /* ZFS_IOC_POOL_GET_PROPS (v15) */
234                         zfs_ioctl_compat_pool_get_props(zc);
235                         break;
236                 }
237         }
238 }
239
240 nvlist_t *
241 zfs_ioctl_compat_innvl(zfs_cmd_t *zc, nvlist_t *innvl, const int vec,
242     const int cflag)
243 {
244         nvlist_t *nvl, *tmpnvl, *hnvl;
245         nvpair_t *elem;
246         char *poolname, *snapname;
247         int err;
248
249         if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC ||
250             cflag == ZFS_CMD_COMPAT_ZCMD || cflag == ZFS_CMD_COMPAT_EDBP ||
251             cflag == ZFS_CMD_COMPAT_RESUME || cflag == ZFS_CMD_COMPAT_INLANES)
252                 goto out;
253
254         switch (vec) {
255         case ZFS_IOC_CREATE:
256                 nvl = fnvlist_alloc();
257                 fnvlist_add_int32(nvl, "type", zc->zc_objset_type);
258                 if (innvl != NULL) {
259                         fnvlist_add_nvlist(nvl, "props", innvl);
260                         nvlist_free(innvl);
261                 }
262                 return (nvl);
263         break;
264         case ZFS_IOC_CLONE:
265                 nvl = fnvlist_alloc();
266                 fnvlist_add_string(nvl, "origin", zc->zc_value);
267                 if (innvl != NULL) {
268                         fnvlist_add_nvlist(nvl, "props", innvl);
269                         nvlist_free(innvl);
270                 }
271                 return (nvl);
272         break;
273         case ZFS_IOC_SNAPSHOT:
274                 if (innvl == NULL)
275                         goto out;
276                 nvl = fnvlist_alloc();
277                 fnvlist_add_nvlist(nvl, "props", innvl);
278                 tmpnvl = fnvlist_alloc();
279                 snapname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value);
280                 fnvlist_add_boolean(tmpnvl, snapname);
281                 kmem_free(snapname, strlen(snapname + 1));
282                 /* check if we are doing a recursive snapshot */
283                 if (zc->zc_cookie)
284                         dmu_get_recursive_snaps_nvl(zc->zc_name, zc->zc_value,
285                             tmpnvl);
286                 fnvlist_add_nvlist(nvl, "snaps", tmpnvl);
287                 fnvlist_free(tmpnvl);
288                 nvlist_free(innvl);
289                 /* strip dataset part from zc->zc_name */
290                 zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
291                 return (nvl);
292         break;
293         case ZFS_IOC_SPACE_SNAPS:
294                 nvl = fnvlist_alloc();
295                 fnvlist_add_string(nvl, "firstsnap", zc->zc_value);
296                 if (innvl != NULL)
297                         nvlist_free(innvl);
298                 return (nvl);
299         break;
300         case ZFS_IOC_DESTROY_SNAPS:
301                 if (innvl == NULL && cflag == ZFS_CMD_COMPAT_DEADMAN)
302                         goto out;
303                 nvl = fnvlist_alloc();
304                 if (innvl != NULL) {
305                         fnvlist_add_nvlist(nvl, "snaps", innvl);
306                 } else {
307                         /*
308                          * We are probably called by even older binaries,
309                          * allocate and populate nvlist with recursive
310                          * snapshots
311                          */
312                         if (zfs_component_namecheck(zc->zc_value, NULL,
313                             NULL) == 0) {
314                                 tmpnvl = fnvlist_alloc();
315                                 if (dmu_get_recursive_snaps_nvl(zc->zc_name,
316                                     zc->zc_value, tmpnvl) == 0)
317                                         fnvlist_add_nvlist(nvl, "snaps",
318                                             tmpnvl);
319                                 nvlist_free(tmpnvl);
320                         }
321                 }
322                 if (innvl != NULL)
323                         nvlist_free(innvl);
324                 /* strip dataset part from zc->zc_name */
325                 zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
326                 return (nvl);
327         break;
328         case ZFS_IOC_HOLD:
329                 nvl = fnvlist_alloc();
330                 tmpnvl = fnvlist_alloc();
331                 if (zc->zc_cleanup_fd != -1)
332                         fnvlist_add_int32(nvl, "cleanup_fd",
333                             (int32_t)zc->zc_cleanup_fd);
334                 if (zc->zc_cookie) {
335                         hnvl = fnvlist_alloc();
336                         if (dmu_get_recursive_snaps_nvl(zc->zc_name,
337                             zc->zc_value, hnvl) == 0) {
338                                 elem = NULL;
339                                 while ((elem = nvlist_next_nvpair(hnvl,
340                                     elem)) != NULL) {
341                                         nvlist_add_string(tmpnvl,
342                                             nvpair_name(elem), zc->zc_string);
343                                 }
344                         }
345                         nvlist_free(hnvl);
346                 } else {
347                         snapname = kmem_asprintf("%s@%s", zc->zc_name,
348                             zc->zc_value);
349                         nvlist_add_string(tmpnvl, snapname, zc->zc_string);
350                         kmem_free(snapname, strlen(snapname + 1));
351                 }
352                 fnvlist_add_nvlist(nvl, "holds", tmpnvl);
353                 nvlist_free(tmpnvl);
354                 if (innvl != NULL)
355                         nvlist_free(innvl);
356                 /* strip dataset part from zc->zc_name */
357                 zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
358                 return (nvl);
359         break;
360         case ZFS_IOC_RELEASE:
361                 nvl = fnvlist_alloc();
362                 tmpnvl = fnvlist_alloc();
363                 if (zc->zc_cookie) {
364                         hnvl = fnvlist_alloc();
365                         if (dmu_get_recursive_snaps_nvl(zc->zc_name,
366                             zc->zc_value, hnvl) == 0) {
367                                 elem = NULL;
368                                 while ((elem = nvlist_next_nvpair(hnvl,
369                                     elem)) != NULL) {
370                                         fnvlist_add_boolean(tmpnvl,
371                                             zc->zc_string);
372                                         fnvlist_add_nvlist(nvl,
373                                             nvpair_name(elem), tmpnvl);
374                                 }
375                         }
376                         nvlist_free(hnvl);
377                 } else {
378                         snapname = kmem_asprintf("%s@%s", zc->zc_name,
379                             zc->zc_value);
380                         fnvlist_add_boolean(tmpnvl, zc->zc_string);
381                         fnvlist_add_nvlist(nvl, snapname, tmpnvl);
382                         kmem_free(snapname, strlen(snapname + 1));
383                 }
384                 nvlist_free(tmpnvl);
385                 if (innvl != NULL)
386                         nvlist_free(innvl);
387                 /* strip dataset part from zc->zc_name */
388                 zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
389                 return (nvl);
390         break;
391         }
392 out:
393         return (innvl);
394 }
395
396 nvlist_t *
397 zfs_ioctl_compat_outnvl(zfs_cmd_t *zc, nvlist_t *outnvl, const int vec,
398     const int cflag)
399 {
400         nvlist_t *tmpnvl;
401
402         if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC ||
403             cflag == ZFS_CMD_COMPAT_ZCMD || cflag == ZFS_CMD_COMPAT_EDBP ||
404             cflag == ZFS_CMD_COMPAT_RESUME || cflag == ZFS_CMD_COMPAT_INLANES)
405                 return (outnvl);
406
407         switch (vec) {
408         case ZFS_IOC_SPACE_SNAPS:
409                 (void) nvlist_lookup_uint64(outnvl, "used", &zc->zc_cookie);
410                 (void) nvlist_lookup_uint64(outnvl, "compressed",
411                     &zc->zc_objset_type);
412                 (void) nvlist_lookup_uint64(outnvl, "uncompressed",
413                     &zc->zc_perm_action);
414                 nvlist_free(outnvl);
415                 /* return empty outnvl */
416                 tmpnvl = fnvlist_alloc();
417                 return (tmpnvl);
418         break;
419         case ZFS_IOC_CREATE:
420         case ZFS_IOC_CLONE:
421         case ZFS_IOC_HOLD:
422         case ZFS_IOC_RELEASE:
423                 nvlist_free(outnvl);
424                 /* return empty outnvl */
425                 tmpnvl = fnvlist_alloc();
426                 return (tmpnvl);
427         break;
428         }
429
430         return (outnvl);
431 }
432 #endif /* KERNEL */