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