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 *
smb_mem_alloc(size_t size)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 *
smb_mem_zalloc(size_t size)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 *
smb_mem_realloc(void * ptr,size_t size)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 *
smb_mem_rezalloc(void * ptr,size_t size)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
smb_mem_free(void * ptr)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
smb_mem_zfree(void * ptr)109 smb_mem_zfree(void *ptr)
110 {
111 	smb_free(NULL, ptr, B_TRUE);
112 }
113 
114 /*
115  * Duplicate a string.
116  */
117 char *
smb_mem_strdup(const char * ptr)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
smb_srm_init(smb_request_t * sr)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
smb_srm_fini(smb_request_t * sr)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 *
smb_srm_alloc(smb_request_t * sr,size_t size)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 *
smb_srm_zalloc(smb_request_t * sr,size_t size)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 *
smb_srm_realloc(smb_request_t * sr,void * p,size_t size)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 *
smb_srm_rezalloc(smb_request_t * sr,void * p,size_t size)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 *
smb_srm_strdup(smb_request_t * sr,const char * s)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 *
smb_alloc(smb_request_t * sr,size_t size,boolean_t zero)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
smb_free(smb_request_t * sr,void * ptr,boolean_t zero)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 *
smb_realloc(smb_request_t * sr,void * ptr,size_t size,boolean_t zero)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 }
314