]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / sys / cddl / contrib / opensolaris / common / zfs / zfs_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 2010 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
23  * Portions Copyright 2005, 2010, Oracle and/or its affiliates.
24  * All rights reserved.
25  * Use is subject to license terms.
26  */
27
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <sys/cred.h>
31 #include <sys/dmu.h>
32 #include <sys/zio.h>
33 #include <sys/nvpair.h>
34 #include <sys/dsl_deleg.h>
35 #include <sys/zfs_ioctl.h>
36 #include "zfs_ioctl_compat.h"
37
38 /*
39  * FreeBSD zfs_cmd compatibility with v15 and older binaries
40  * appropriately remap/extend the zfs_cmd_t structure
41  */
42 void
43 zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag)
44 {
45         zfs_cmd_v15_t *zc_c;
46
47         if (cflag == ZFS_CMD_COMPAT_V15) {
48                 zc_c = (void *)addr;
49
50                 /* zc */
51                 strlcpy(zc->zc_name,zc_c->zc_name,MAXPATHLEN);
52                 strlcpy(zc->zc_value,zc_c->zc_value,MAXPATHLEN);
53                 strlcpy(zc->zc_string,zc_c->zc_string,MAXPATHLEN);
54                 zc->zc_guid = zc_c->zc_guid;
55                 zc->zc_nvlist_conf = zc_c->zc_nvlist_conf;
56                 zc->zc_nvlist_conf_size = zc_c->zc_nvlist_conf_size;
57                 zc->zc_nvlist_src = zc_c->zc_nvlist_src;
58                 zc->zc_nvlist_src_size = zc_c->zc_nvlist_src_size;
59                 zc->zc_nvlist_dst = zc_c->zc_nvlist_dst;
60                 zc->zc_nvlist_dst_size = zc_c->zc_nvlist_dst_size;
61                 zc->zc_cookie = zc_c->zc_cookie;
62                 zc->zc_objset_type = zc_c->zc_objset_type;
63                 zc->zc_perm_action = zc_c->zc_perm_action;
64                 zc->zc_history = zc_c->zc_history;
65                 zc->zc_history_len = zc_c->zc_history_len;
66                 zc->zc_history_offset = zc_c->zc_history_offset;
67                 zc->zc_obj = zc_c->zc_obj;
68                 zc->zc_share = zc_c->zc_share;
69                 zc->zc_jailid = zc_c->zc_jailid;
70                 zc->zc_objset_stats = zc_c->zc_objset_stats;
71                 zc->zc_begin_record = zc_c->zc_begin_record;
72
73                 /* zc->zc_inject_record */
74                 zc->zc_inject_record.zi_objset =
75                     zc_c->zc_inject_record.zi_objset;
76                 zc->zc_inject_record.zi_object =
77                     zc_c->zc_inject_record.zi_object;
78                 zc->zc_inject_record.zi_start =
79                     zc_c->zc_inject_record.zi_start;
80                 zc->zc_inject_record.zi_end =
81                     zc_c->zc_inject_record.zi_end;
82                 zc->zc_inject_record.zi_guid =
83                     zc_c->zc_inject_record.zi_guid;
84                 zc->zc_inject_record.zi_level =
85                     zc_c->zc_inject_record.zi_level;
86                 zc->zc_inject_record.zi_error =
87                     zc_c->zc_inject_record.zi_error;
88                 zc->zc_inject_record.zi_type =
89                     zc_c->zc_inject_record.zi_type;
90                 zc->zc_inject_record.zi_freq =
91                     zc_c->zc_inject_record.zi_freq;
92                 zc->zc_inject_record.zi_failfast =
93                     zc_c->zc_inject_record.zi_failfast;
94         }
95 }
96
97 void
98 zfs_cmd_compat_put(zfs_cmd_t *zc, caddr_t addr, const int cflag)
99 {
100         zfs_cmd_v15_t *zc_c;
101
102         switch (cflag) {
103         case ZFS_CMD_COMPAT_V15:
104                 zc_c = (void *)addr;
105
106                 /* zc */
107                 strlcpy(zc_c->zc_name,zc->zc_name,MAXPATHLEN);
108                 strlcpy(zc_c->zc_value,zc->zc_value,MAXPATHLEN);
109                 strlcpy(zc_c->zc_string,zc->zc_string,MAXPATHLEN);
110                 zc_c->zc_guid = zc->zc_guid;
111                 zc_c->zc_nvlist_conf = zc->zc_nvlist_conf;
112                 zc_c->zc_nvlist_conf_size = zc->zc_nvlist_conf_size;
113                 zc_c->zc_nvlist_src = zc->zc_nvlist_src;
114                 zc_c->zc_nvlist_src_size = zc->zc_nvlist_src_size;
115                 zc_c->zc_nvlist_dst = zc->zc_nvlist_dst;
116                 zc_c->zc_nvlist_dst_size = zc->zc_nvlist_dst_size;
117                 zc_c->zc_cookie = zc->zc_cookie;
118                 zc_c->zc_objset_type = zc->zc_objset_type;
119                 zc_c->zc_perm_action = zc->zc_perm_action;
120                 zc_c->zc_history = zc->zc_history;
121                 zc_c->zc_history_len = zc->zc_history_len;
122                 zc_c->zc_history_offset = zc->zc_history_offset;
123                 zc_c->zc_obj = zc->zc_obj;
124                 zc_c->zc_share = zc->zc_share;
125                 zc_c->zc_jailid = zc->zc_jailid;
126                 zc_c->zc_objset_stats = zc->zc_objset_stats;
127                 zc_c->zc_begin_record = zc->zc_begin_record;
128
129                 /* zc_inject_record */
130                 zc_c->zc_inject_record.zi_objset =
131                     zc->zc_inject_record.zi_objset;
132                 zc_c->zc_inject_record.zi_object =
133                     zc->zc_inject_record.zi_object;
134                 zc_c->zc_inject_record.zi_start =
135                     zc->zc_inject_record.zi_start;
136                 zc_c->zc_inject_record.zi_end =
137                     zc->zc_inject_record.zi_end;
138                 zc_c->zc_inject_record.zi_guid =
139                     zc->zc_inject_record.zi_guid;
140                 zc_c->zc_inject_record.zi_level =
141                     zc->zc_inject_record.zi_level;
142                 zc_c->zc_inject_record.zi_error =
143                     zc->zc_inject_record.zi_error;
144                 zc_c->zc_inject_record.zi_type =
145                     zc->zc_inject_record.zi_type;
146                 zc_c->zc_inject_record.zi_freq =
147                     zc->zc_inject_record.zi_freq;
148                 zc_c->zc_inject_record.zi_failfast =
149                     zc->zc_inject_record.zi_failfast;
150
151                 break;
152         }
153 }
154
155 static int
156 zfs_ioctl_compat_get_nvlist(uint64_t nvl, size_t size, int iflag,
157     nvlist_t **nvp)
158 {
159         char *packed;
160         int error;
161         nvlist_t *list = NULL;
162
163         /*
164          * Read in and unpack the user-supplied nvlist.
165          */
166         if (size == 0)
167                 return (EINVAL);
168
169 #ifdef _KERNEL
170         packed = kmem_alloc(size, KM_SLEEP);
171         if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size,
172             iflag)) != 0) {
173                 kmem_free(packed, size);
174                 return (error);
175         }
176 #else
177         packed = (void *)(uintptr_t)nvl;
178 #endif
179
180         error = nvlist_unpack(packed, size, &list, 0);
181
182 #ifdef _KERNEL
183         kmem_free(packed, size);
184 #endif
185
186         if (error != 0)
187                 return (error);
188
189         *nvp = list;
190         return (0);
191 }
192
193 static int
194 zfs_ioctl_compat_put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
195 {
196         char *packed = NULL;
197         int error = 0;
198         size_t size;
199
200         VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
201
202 #ifdef _KERNEL
203         packed = kmem_alloc(size, KM_SLEEP);
204         VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
205             KM_SLEEP) == 0);
206
207         if (ddi_copyout(packed,
208             (void *)(uintptr_t)zc->zc_nvlist_dst, size, zc->zc_iflags) != 0)
209                 error = EFAULT;
210         kmem_free(packed, size);
211 #else
212         packed = (void *)(uintptr_t)zc->zc_nvlist_dst;
213         VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
214             0) == 0);
215 #endif
216
217         zc->zc_nvlist_dst_size = size;
218         return (error);
219 }
220
221 static void
222 zfs_ioctl_compat_fix_stats_nvlist(nvlist_t *nvl)
223 {
224         nvlist_t **child;
225         nvlist_t *nvroot = NULL;
226         vdev_stat_t *vs;
227         uint_t c, children, nelem;
228
229         if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_CHILDREN,
230             &child, &children) == 0) {
231                 for (c = 0; c < children; c++) {
232                         zfs_ioctl_compat_fix_stats_nvlist(child[c]);
233                 }
234         }
235
236         if (nvlist_lookup_nvlist(nvl, ZPOOL_CONFIG_VDEV_TREE,
237             &nvroot) == 0)
238                 zfs_ioctl_compat_fix_stats_nvlist(nvroot);
239 #ifdef _KERNEL
240         if ((nvlist_lookup_uint64_array(nvl, ZPOOL_CONFIG_VDEV_STATS,
241 #else
242         if ((nvlist_lookup_uint64_array(nvl, "stats",
243 #endif
244
245             (uint64_t **)&vs, &nelem) == 0)) {
246                 nvlist_add_uint64_array(nvl,
247 #ifdef _KERNEL
248                     "stats",
249 #else
250                     ZPOOL_CONFIG_VDEV_STATS,
251 #endif
252                     (uint64_t *)vs, nelem);
253 #ifdef _KERNEL
254                 nvlist_remove(nvl, ZPOOL_CONFIG_VDEV_STATS,
255 #else
256                 nvlist_remove(nvl, "stats",
257 #endif
258                     DATA_TYPE_UINT64_ARRAY);
259         }
260 }
261
262 static int
263 zfs_ioctl_compat_fix_stats(zfs_cmd_t *zc, const int cflag)
264 {
265         nvlist_t *nv, *nvp = NULL;
266         nvpair_t *elem;
267         int error;
268
269         if ((error = zfs_ioctl_compat_get_nvlist(zc->zc_nvlist_dst,
270             zc->zc_nvlist_dst_size, zc->zc_iflags, &nv)) != 0)
271                 return (error);
272
273         if (cflag == 5) { /* ZFS_IOC_POOL_STATS */
274                 elem = NULL;
275                 while ((elem = nvlist_next_nvpair(nv, elem)) != NULL) {
276                         if (nvpair_value_nvlist(elem, &nvp) == 0)
277                                 zfs_ioctl_compat_fix_stats_nvlist(nvp);
278                 }
279                 elem = NULL;
280         } else
281                 zfs_ioctl_compat_fix_stats_nvlist(nv);
282
283         error = zfs_ioctl_compat_put_nvlist(zc, nv);
284
285         nvlist_free(nv);
286
287         return (error);
288 }
289
290 static int
291 zfs_ioctl_compat_pool_get_props(zfs_cmd_t *zc)
292 {
293         nvlist_t *nv, *nva = NULL;
294         int error;
295
296         if ((error = zfs_ioctl_compat_get_nvlist(zc->zc_nvlist_dst,
297             zc->zc_nvlist_dst_size, zc->zc_iflags, &nv)) != 0)
298                 return (error);
299
300 #ifdef _KERNEL
301         if (nvlist_lookup_nvlist(nv, "allocated", &nva) == 0) {
302                 nvlist_add_nvlist(nv, "used", nva);
303                 nvlist_remove(nv, "allocated", DATA_TYPE_NVLIST);
304         }
305
306         if (nvlist_lookup_nvlist(nv, "free", &nva) == 0) {
307                 nvlist_add_nvlist(nv, "available", nva);
308                 nvlist_remove(nv, "free", DATA_TYPE_NVLIST);
309         }
310 #else
311         if (nvlist_lookup_nvlist(nv, "used", &nva) == 0) {
312                 nvlist_add_nvlist(nv, "allocated", nva);
313                 nvlist_remove(nv, "used", DATA_TYPE_NVLIST);
314         }
315
316         if (nvlist_lookup_nvlist(nv, "available", &nva) == 0) {
317                 nvlist_add_nvlist(nv, "free", nva);
318                 nvlist_remove(nv, "available", DATA_TYPE_NVLIST);
319         }
320 #endif
321
322         error = zfs_ioctl_compat_put_nvlist(zc, nv);
323
324         nvlist_free(nv);
325
326         return (error);
327 }
328
329 #ifndef _KERNEL
330 int
331 zcmd_ioctl_compat(int fd, unsigned long cmd, zfs_cmd_t *zc, const int cflag)
332 {
333         int nc, ret;
334         void *zc_c;
335         unsigned long ncmd;
336
337         if (cflag == ZFS_CMD_COMPAT_NONE) {
338                 ret = ioctl(fd, cmd, zc);
339                 return (ret);
340         }
341
342         if (cflag == ZFS_CMD_COMPAT_V15) {
343                 nc = zfs_ioctl_v28_to_v15[ZFS_IOC(cmd)];
344                 zc_c = malloc(sizeof(zfs_cmd_v15_t));
345                 ncmd = _IOWR('Z', nc, struct zfs_cmd_v15);
346         } else
347                 return (EINVAL);
348
349         if (ZFS_IOC(ncmd) == ZFS_IOC_COMPAT_FAIL)
350                 return (ENOTSUP);
351
352         zfs_cmd_compat_put(zc, (caddr_t)zc_c, cflag);
353         ret = ioctl(fd, ncmd, zc_c);
354         if (cflag == ZFS_CMD_COMPAT_V15 &&
355             nc == 2 /* ZFS_IOC_POOL_IMPORT */)
356                 ret = ioctl(fd, _IOWR('Z', 4 /* ZFS_IOC_POOL_CONFIGS */,
357                     struct zfs_cmd_v15), zc_c);
358         zfs_cmd_compat_get(zc, (caddr_t)zc_c, cflag);
359         free(zc_c);
360
361         switch (nc) {
362         case 2: /* ZFS_IOC_POOL_IMPORT */
363         case 4: /* ZFS_IOC_POOL_CONFIGS */
364         case 5: /* ZFS_IOC_POOL_STATS */
365         case 6: /* ZFS_IOC_POOL_TRYIMPORT */
366                 zfs_ioctl_compat_fix_stats(zc, nc);
367                 break;
368         case 41: /* ZFS_IOC_POOL_GET_PROPS (v15) */
369                 zfs_ioctl_compat_pool_get_props(zc);
370                 break;
371         }
372
373         return (ret);
374 }
375 #else /* _KERNEL */
376 void
377 zfs_ioctl_compat_pre(zfs_cmd_t *zc, int *vec, const int cflag)
378 {
379         if (cflag == ZFS_CMD_COMPAT_V15)
380                 switch (*vec) {
381
382                 case 7: /* ZFS_IOC_POOL_SCRUB (v15) */
383                         zc->zc_cookie = POOL_SCAN_SCRUB;
384                         break;
385                 }
386 }
387
388 void
389 zfs_ioctl_compat_post(zfs_cmd_t *zc, int vec, const int cflag)
390 {
391         if (cflag == ZFS_CMD_COMPAT_V15) {
392                 switch (vec) {
393                 case 4: /* ZFS_IOC_POOL_CONFIGS */
394                 case 5: /* ZFS_IOC_POOL_STATS */
395                 case 6: /* ZFS_IOC_POOL_TRYIMPORT */
396                         zfs_ioctl_compat_fix_stats(zc, vec);
397                         break;
398                 case 41: /* ZFS_IOC_POOL_GET_PROPS (v15) */
399                         zfs_ioctl_compat_pool_get_props(zc);
400                         break;
401                 }
402         }
403 }
404 #endif /* KERNEL */