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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 /*
  26  * Copyright 2019 Joyent, Inc.
  27  */
  28 
  29 #include <sys/types.h>
  30 #include <sys/sunddi.h>
  31 #include <sys/kmem.h>
  32 #include <sys/sysmacros.h>
  33 #include <smbsrv/smb_kproto.h>
  34 #include <smbsrv/alloc.h>
  35 
  36 #define SMB_SMH_MAGIC           0x534D485F      /* 'SMH_' */
  37 #define SMB_SMH_VALID(_smh_)    ASSERT((_smh_)->smh_magic == SMB_SMH_MAGIC)
  38 #define SMB_MEM2SMH(_mem_)      ((smb_mem_header_t *)(_mem_) - 1)
  39 
  40 typedef struct smb_mem_header {
  41         uint32_t        smh_magic;
  42         size_t          smh_size;
  43         smb_request_t   *smh_sr;
  44         list_node_t     smh_lnd;
  45 } smb_mem_header_t;
  46 
  47 static void *smb_alloc(smb_request_t *, size_t, boolean_t);
  48 static void smb_free(smb_request_t *, void *, boolean_t);
  49 static void *smb_realloc(smb_request_t *, void *, size_t, boolean_t);
  50 
  51 /*
  52  * Allocate memory.
  53  */
  54 void *
  55 smb_mem_alloc(size_t size)
  56 {
  57         return (smb_alloc(NULL, size, B_FALSE));
  58 }
  59 
  60 /*
  61  * Allocate memory and zero it out.
  62  */
  63 void *
  64 smb_mem_zalloc(size_t size)
  65 {
  66         return (smb_alloc(NULL, size, B_TRUE));
  67 }
  68 
  69 /*
  70  * Allocate or resize memory previously allocated.
  71  *
  72  * The address passed in MUST be considered invalid when this function returns.
  73  */
  74 void *
  75 smb_mem_realloc(void *ptr, size_t size)
  76 {
  77         return (smb_realloc(NULL, ptr, size, B_FALSE));
  78 }
  79 
  80 /*
  81  * Allocate or resize memory previously allocated. If the new size is greater
  82  * than the current size, the extra space is zeroed out. If the new size is less
  83  * then the current size the space truncated is zeroed out.
  84  *
  85  * The address passed in MUST be considered invalid when this function returns.
  86  */
  87 void *
  88 smb_mem_rezalloc(void *ptr, size_t size)
  89 {
  90         return (smb_realloc(NULL, ptr, size, B_TRUE));
  91 }
  92 
  93 /*
  94  * Free memory previously allocated with smb_malloc(), smb_zalloc(),
  95  * smb_remalloc() or smb_rezalloc().
  96  */
  97 void
  98 smb_mem_free(void *ptr)
  99 {
 100         smb_free(NULL, ptr, B_FALSE);
 101 }
 102 
 103 /*
 104  * Free memory previously allocated with smb_mem_malloc(), smb_mem_zalloc(),
 105  * smb_mem_remalloc() or smb_mem_rezalloc() or smb_mem_strdup(). The memory will
 106  * be zeroed out before being actually freed.
 107  */
 108 void
 109 smb_mem_zfree(void *ptr)
 110 {
 111         smb_free(NULL, ptr, B_TRUE);
 112 }
 113 
 114 /*
 115  * Duplicate a string.
 116  */
 117 char *
 118 smb_mem_strdup(const char *ptr)
 119 {
 120         char    *p;
 121         size_t  size;
 122 
 123         size = strlen(ptr) + 1;
 124         p = smb_alloc(NULL, size, B_FALSE);
 125         bcopy(ptr, p, size);
 126         return (p);
 127 }
 128 
 129 /*
 130  * Initialize the list for request-specific temporary storage.
 131  */
 132 void
 133 smb_srm_init(smb_request_t *sr)
 134 {
 135         list_create(&sr->sr_storage, sizeof (smb_mem_header_t),
 136             offsetof(smb_mem_header_t, smh_lnd));
 137 }
 138 
 139 /*
 140  * Free everything on the request-specific temporary storage list and destroy
 141  * the list.
 142  */
 143 void
 144 smb_srm_fini(smb_request_t *sr)
 145 {
 146         smb_mem_header_t        *smh;
 147 
 148         while ((smh = list_head(&sr->sr_storage)) != NULL)
 149                 smb_free(sr, ++smh, B_FALSE);
 150         list_destroy(&sr->sr_storage);
 151 }
 152 
 153 /*
 154  * Allocate memory and associate it with the specified request.
 155  * Memory allocated here can only be used for the duration of this request; it
 156  * will be freed automatically on completion of the request.
 157  */
 158 void *
 159 smb_srm_alloc(smb_request_t *sr, size_t size)
 160 {
 161         return (smb_alloc(sr, size, B_FALSE));
 162 }
 163 
 164 /*
 165  * Allocate memory, zero it out and associate it with the specified request.
 166  * Memory allocated here can only be used for the duration of this request; it
 167  * will be freed automatically on completion of the request.
 168  */
 169 void *
 170 smb_srm_zalloc(smb_request_t *sr, size_t size)
 171 {
 172         return (smb_alloc(sr, size, B_TRUE));
 173 }
 174 
 175 /*
 176  * Allocate or resize memory previously allocated for the specified request.
 177  *
 178  * The address passed in MUST be considered invalid when this function returns.
 179  */
 180 void *
 181 smb_srm_realloc(smb_request_t *sr, void *p, size_t size)
 182 {
 183         return (smb_realloc(sr, p, size, B_FALSE));
 184 }
 185 
 186 /*
 187  * Allocate or resize memory previously allocated for the specified request. If
 188  * the new size is greater than the current size, the extra space is zeroed out.
 189  * If the new size is less then the current size the space truncated is zeroed
 190  * out.
 191  *
 192  * The address passed in MUST be considered invalid when this function returns.
 193  */
 194 void *
 195 smb_srm_rezalloc(smb_request_t *sr, void *p, size_t size)
 196 {
 197         return (smb_realloc(sr, p, size, B_TRUE));
 198 }
 199 
 200 char *
 201 smb_srm_strdup(smb_request_t *sr, const char *s)
 202 {
 203         char    *p;
 204         size_t  size;
 205 
 206         size = strlen(s) + 1;
 207         p = smb_srm_alloc(sr, size);
 208         bcopy(s, p, size);
 209         return (p);
 210 }
 211 
 212 /*
 213  * Allocate memory.
 214  *
 215  * sr   If not NULL, request the memory allocated must be associated with.
 216  *
 217  * size Size of the meory to allocate.
 218  *
 219  * zero If true the memory allocated will be zeroed out.
 220  */
 221 static void *
 222 smb_alloc(smb_request_t *sr, size_t size, boolean_t zero)
 223 {
 224         smb_mem_header_t        *smh;
 225 
 226         if (zero) {
 227                 smh = kmem_zalloc(size + sizeof (smb_mem_header_t), KM_SLEEP);
 228         } else {
 229                 smh = kmem_alloc(size + sizeof (smb_mem_header_t), KM_SLEEP);
 230                 smh->smh_sr = NULL;
 231                 bzero(&smh->smh_lnd, sizeof (smh->smh_lnd));
 232         }
 233         smh->smh_sr = sr;
 234         smh->smh_size = size;
 235         smh->smh_magic = SMB_SMH_MAGIC;
 236         if (sr != NULL) {
 237                 SMB_REQ_VALID(sr);
 238                 list_insert_tail(&sr->sr_storage, smh);
 239         }
 240         return (++smh);
 241 }
 242 
 243 /*
 244  * Free memory.
 245  *
 246  * sr   If not NULL, request the memory to free is associated with.
 247  *
 248  * ptr  Memory address
 249  *
 250  * zero If true the memory is zeroed out before being freed.
 251  */
 252 static void
 253 smb_free(smb_request_t *sr, void *ptr, boolean_t zero)
 254 {
 255         smb_mem_header_t        *smh;
 256 
 257         if (ptr != NULL) {
 258                 smh = SMB_MEM2SMH(ptr);
 259                 SMB_SMH_VALID(smh);
 260                 ASSERT(sr == smh->smh_sr);
 261                 if (sr != NULL) {
 262                         SMB_REQ_VALID(sr);
 263                         list_remove(&sr->sr_storage, smh);
 264                 }
 265                 if (zero)
 266                         bzero(ptr, smh->smh_size);
 267 
 268                 smh->smh_magic = 0;
 269                 kmem_free(smh, smh->smh_size + sizeof (smb_mem_header_t));
 270         }
 271 }
 272 
 273 /*
 274  * Allocate or resize memory previously allocated.
 275  *
 276  * sr   If not NULL, request the memory is associated with.
 277  *
 278  * ptr  Memory address
 279  *
 280  * size New size
 281  *
 282  * zero If true zero out the extra space or the truncated space.
 283  */
 284 static void *
 285 smb_realloc(smb_request_t *sr, void *ptr, size_t size, boolean_t zero)
 286 {
 287         smb_mem_header_t        *smh;
 288         void                    *new_ptr;
 289 
 290         if (ptr == NULL)
 291                 return (smb_alloc(sr, size, zero));
 292 
 293         smh = SMB_MEM2SMH(ptr);
 294         SMB_SMH_VALID(smh);
 295         ASSERT(sr == smh->smh_sr);
 296 
 297         if (size == 0) {
 298                 smb_free(sr, ptr, zero);
 299                 return (NULL);
 300         }
 301         if (smh->smh_size >= size) {
 302                 if ((zero) && (smh->smh_size > size))
 303                         bzero((caddr_t)ptr + size, smh->smh_size - size);
 304                 return (ptr);
 305         }
 306         new_ptr = smb_alloc(sr, size, B_FALSE);
 307         bcopy(ptr, new_ptr, smh->smh_size);
 308         if (zero)
 309                 bzero((caddr_t)new_ptr + smh->smh_size, size - smh->smh_size);
 310 
 311         smb_free(sr, ptr, zero);
 312         return (new_ptr);
 313 }