1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2015 Nexenta Inc.  All rights reserved.
  14  */
  15 
  16 #include <sys/sunddi.h>
  17 #include <sys/dkio.h>
  18 #include <sys/dkioc_free_util.h>
  19 
  20 #ifdef  _KERNEL
  21 
  22 #include <sys/sysmacros.h>
  23 #include <sys/file.h>
  24 
  25 #define DFL_COPYIN_MAX_EXTS     (1024 * 1024)
  26 
  27 /*
  28  * Copy-in convenience function for variable-length dkioc_free_list_t
  29  * structures. The pointer to copied from is in `arg' (may be a pointer
  30  * to userspace), `ddi_flags' indicate whether the pointer is from user-
  31  * or kernelspace (FKIOCTL) and `kmflags' are the flags passed to
  32  * kmem_zalloc when allocating the new structure.
  33  * Returns the copied structure on success and NULL on copy failure.
  34  */
  35 dkioc_free_list_t *
  36 dfl_copyin(void *arg, int ddi_flags, int kmflags)
  37 {
  38         dkioc_free_list_t *dfl;
  39 
  40         if (ddi_flags & FKIOCTL) {
  41                 dkioc_free_list_t *dfl_in = arg;
  42                 dfl = kmem_zalloc(DFL_SZ(dfl_in->dfl_num_exts), kmflags);
  43                 if (dfl == NULL)
  44                         return (NULL);
  45                 bcopy(dfl_in, dfl, DFL_SZ(dfl_in->dfl_num_exts));
  46         } else {
  47                 uint64_t num_exts;
  48 
  49                 if (ddi_copyin(((uint8_t *)arg) + offsetof(dkioc_free_list_t,
  50                     dfl_num_exts), &num_exts, sizeof (num_exts), ddi_flags) ||
  51                     num_exts > DFL_COPYIN_MAX_EXTS)
  52                         return (NULL);
  53 
  54                 dfl = kmem_zalloc(DFL_SZ(num_exts), kmflags);
  55                 if (dfl == NULL)
  56                         return (NULL);
  57                 if (ddi_copyin(arg, dfl, DFL_SZ(num_exts), ddi_flags)) {
  58                         dfl_free(dfl);
  59                         return (NULL);
  60                 }
  61 
  62                 /* not valid from userspace */
  63                 dfl->dfl_ck_func = NULL;
  64                 dfl->dfl_ck_arg = NULL;
  65         }
  66 
  67         return (dfl);
  68 }
  69 
  70 /* Frees a variable-length dkioc_free_list_t structure. */
  71 void
  72 dfl_free(dkioc_free_list_t *dfl)
  73 {
  74         kmem_free(dfl, DFL_SZ(dfl->dfl_num_exts));
  75 }
  76 
  77 #else   /* !KERNEL */
  78 
  79 #include <stdlib.h>
  80 #include <strings.h>
  81 
  82 dkioc_free_list_t *
  83 dfl_copyin(void *arg, int ddi_flags, int kmflags)
  84 /*ARGSUSED*/
  85 {
  86         dkioc_free_list_t *dfl, *dfl_in = arg;
  87         dfl = malloc(DFL_SZ(dfl_in->dfl_num_exts));
  88         if (dfl == NULL)
  89                 return (NULL);
  90         bcopy(dfl_in, dfl, DFL_SZ(dfl_in->dfl_num_exts));
  91         return (dfl);
  92 }
  93 
  94 void
  95 dfl_free(dkioc_free_list_t *dfl)
  96 {
  97         free(dfl);
  98 }
  99 
 100 #endif  /* !KERNEL */