1/*
2 * Copyright (c) 2008 - 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <sys/types.h>
25#include <ctype.h>
26#include <errno.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31#include <netsmb/upi_mbuf.h>
32#include <netsmb/smb_lib.h>
33
34
35/* mbuf flags */
36#define M_EXT           0x0001  /* has associated external storage */
37#define M_PKTHDR        0x0002  /* start of record */
38#define M_EOR           0x0004  /* end of record */
39
40
41struct smb_mbuf {
42	uint32_t			m_type;
43	uint32_t			m_flags;
44	size_t				m_maxlen;
45	size_t				m_len;
46	size_t				m_pkthdr_len;
47	char				*m_data;
48	struct smb_mbuf		*m_next;
49	void (*m_extfree)(caddr_t , size_t, caddr_t);
50	caddr_t				m_extarg;
51};
52
53/*
54 * mbuf_free
55 *
56 * Frees a single mbuf. Not commonly used outside of the file because it
57 * doesn't touch the rest of the mbufs on the chain.
58 * params:
59 *		mbuf - The mbuf to free.
60 * result:
61 *		The next mbuf in the chain.
62 */
63mbuf_t mbuf_free(mbuf_t mbuf)
64{
65	mbuf_t next = NULL;
66
67	if (mbuf == NULL)
68		return next; /* nothing to free */
69
70	next = mbuf->m_next;
71	if (mbuf->m_type == MBUF_TYPE_FREE) {
72		smb_log_info("%s: Double FREE", ASL_LEVEL_DEBUG, __FUNCTION__);
73	}
74	if (mbuf->m_flags & M_EXT) {
75		if (mbuf->m_extfree)
76			mbuf->m_extfree(mbuf->m_extarg, mbuf->m_maxlen, (caddr_t)mbuf->m_data);
77	} else if (mbuf->m_data) {
78		free(mbuf->m_data);
79	}
80	mbuf->m_next  = NULL;
81	mbuf->m_type = MBUF_TYPE_FREE;
82	mbuf->m_data = NULL;
83	free(mbuf);
84	return next;
85}
86
87/*
88 * smb_mbuf_get
89 *
90 * Internal routine that allocates all userland mbufs. Only creates
91 * data storage if maxlen is not zero.
92 * params:
93 *		how		- How to create the mbuf, always MBUF_WAITOK in userland.
94 *		type	- Type of mbuf to create, always MBUF_TYPE_DATA in userland.
95 *		mbuf	- Return location for the mbuf we created.
96 *		maxlen	- The data size that should be allocated for this mbuf
97 * result:
98 *		Error	- Either zero or the appropriate errno
99 */
100static
101int smb_mbuf_get(uint32_t how, uint32_t type, mbuf_t *mbuf, size_t maxlen)
102{
103	struct smb_mbuf *m;
104
105	if ((type != MBUF_TYPE_DATA) || (how != MBUF_WAITOK))
106		return (EINVAL);
107
108	m = malloc(sizeof(struct smb_mbuf));
109	if (m == NULL)
110		return ENOMEM;
111
112	bzero(m, sizeof(struct smb_mbuf));
113	m->m_type = type;
114	if (maxlen) {
115		m->m_data = malloc(maxlen);
116		if (m->m_data == NULL) {
117			(void)mbuf_free(m);
118			return ENOMEM;
119		}
120		m->m_maxlen = maxlen;
121	}
122	*mbuf = m;
123	return 0;
124}
125
126/*
127 * mbuf_freem
128 *
129 * Frees a chain of mbufs link through mnext.
130 * params:
131 *		mbuf - The first mbuf in the chain to free.
132 */
133void mbuf_freem(mbuf_t mbuf)
134{
135	struct smb_mbuf *m;
136
137	while (mbuf) {
138		m = mbuf_free(mbuf);
139		mbuf = m;
140	}
141}
142
143/*
144 * mbuf_gethdr
145 *
146 * Allocates an mbuf without a cluster for external data. Sets a flag to
147 * indicate there is a packet header and initializes the packet header.
148 * params:
149 *		how		- How to create the mbuf, always MBUF_WAITOK in userland.
150 *		type	- Type of mbuf to create, always MBUF_TYPE_DATA in userland.
151 *		mbuf	- Return location for the mbuf we created.
152 * result:
153 *		Error	- Either zero or the appropriate errno
154 */
155int mbuf_gethdr(uint32_t how, uint32_t type, mbuf_t *mbuf)
156{
157	int error = smb_mbuf_get(how, type, mbuf, getpagesize());
158	if (error)
159		return error;
160
161	(*mbuf)->m_flags |= M_PKTHDR | M_EOR;
162	return 0;
163}
164
165/*
166 * mbuf_get
167 *
168 * Allocates an mbuf without a cluster for external data.
169 * params:
170 *		how		- How to create the mbuf, always MBUF_WAITOK in userland.
171 *		type	- Type of mbuf to create, always MBUF_TYPE_DATA in userland.
172 *		mbuf	- Return location for the mbuf we created.
173 * result:
174 *		Error	- Either zero or the appropriate errno
175 */
176int mbuf_get(uint32_t how, uint32_t type, mbuf_t *mbuf)
177{
178	return smb_mbuf_get(how, type, mbuf, getpagesize());
179}
180
181/*
182 * mbuf_getcluster
183 *
184 * Allocate a cluster of the requested size and attach it to an mbuf for use as
185 * external data. If mbuf points to a NULL mbuf_t, an mbuf will be allocated for
186 * you. If mbuf points to  a non-NULL mbuf_t, mbuf_getcluster may return a different
187 * mbuf_t than the one you passed in.
188 * params:
189 *		how		- How to create the mbuf, always MBUF_WAITOK in userland.
190 *		type	- Type of mbuf to create, always MBUF_TYPE_DATA in userland.
191 *		size	- The size of the cluster to be allocated. This routine allows
192 *					any size, unlike the kernel kpi mbuf code.
193 *		mbuf	- The mbuf the cluster will be attached to.
194 * result:
195 *		Error	- Either zero or the appropriate errno
196 */
197int mbuf_getcluster(uint32_t how, uint32_t type, size_t size, mbuf_t *mbuf)
198{
199	int error;
200	/* We currently relocate the mbuf always */
201	if (*mbuf) {
202		mbuf_freem( *mbuf);
203		*mbuf = NULL;
204	}
205	error = smb_mbuf_get(how, type, mbuf, size);
206	if (!error && *mbuf)
207		(*mbuf)->m_flags |= M_PKTHDR | M_EOR;
208	return error;
209}
210
211/*
212 * mbuf_attachcluster
213 *
214 * Attach an external buffer as a cluster for an mbuf.  If mbuf points to a NULL
215 * mbuf_t, an mbuf will be allocated for you.  If mbuf points to a non-NULL mbuf_t,
216 * the user-supplied mbuf will be used instead.
217 * params:
218 *		how		- How to create the mbuf, always MBUF_WAITOK in userland.
219 *		type	- Type of mbuf to create, always MBUF_TYPE_DATA in userland.
220 *		mbuf	- Pointer to the address of the mbuf; if NULL, an mbuf will be
221 *					allocated, otherwise, it must point to a valid mbuf address.
222 *		extbuf	- Address of the external buffer.
223 *		extfree	- Free routine for the external buffer; the caller is required
224 *					to defined a routine that will be invoked when the mbuf is
225 *					freed. Userland code allows this to be null.
226 *		extsize	- Size of the external buffer.
227 *		extarg	- Private value that will be passed to the free routine when it
228 *					is called at the time the mbuf is freed.
229 * result:
230 *		Error	- Either zero or the appropriate errno
231 */
232int mbuf_attachcluster(uint32_t how, uint32_t type,
233					   mbuf_t *mbuf, void * extbuf,
234					   void (*extfree)(caddr_t , size_t, caddr_t),
235					   size_t extsize, caddr_t extarg)
236{
237	int error = 0;
238
239	if ((extbuf == NULL) || (extsize == 0)) {
240		error = EINVAL;
241	} else if (*mbuf == NULL) {
242		error = smb_mbuf_get(how, type, mbuf, 0);
243	} else if ((*mbuf)->m_data) {
244		free((*mbuf)->m_data);
245		(*mbuf)->m_data = NULL;
246	}
247	if (error)
248		return error;
249
250	(*mbuf)->m_flags = M_EXT | M_PKTHDR | M_EOR;
251	(*mbuf)->m_maxlen = extsize;
252	(*mbuf)->m_data = extbuf;
253	(*mbuf)->m_extfree = extfree;
254	(*mbuf)->m_extarg = extarg;
255	return 0;
256}
257
258/*
259 * mbuf_len
260 *
261 * Gets the length of data in this mbuf.
262 * params:
263 *		mbuf	- The mbuf.
264 * result:
265 *		size_t	- The  length of data in this mbuf.
266 */
267size_t mbuf_len(const mbuf_t mbuf)
268{
269	if (mbuf) {
270		return mbuf->m_len;
271	} else {
272		return 0;
273	}
274}
275
276/*
277 * mbuf_maxlen
278 *
279 * Retrieves the maximum length of data that may be stored in this mbuf.
280 * params:
281 *		mbuf	- The mbuf.
282 * result:
283 *		size_t	- The maximum length of data for this mbuf.
284 */
285size_t mbuf_maxlen(const mbuf_t mbuf)
286{
287	if (mbuf) {
288		return mbuf->m_maxlen;
289	} else {
290		return 0;
291	}
292}
293
294/*
295 * mbuf_setlen
296 *
297 * Sets the length of data in this packet. Be careful to not set the length over
298 * the space available in the mbuf.
299 * param:
300 *		mbuf	- The mbuf.
301 *		len		- The new length.
302 */
303void mbuf_setlen(mbuf_t mbuf, size_t len)
304{
305	if (mbuf) {
306		mbuf->m_len = len;
307	}
308}
309
310/*
311 * mbuf_pkthdr_len
312 *
313 * Returns the length as reported by the packet header.
314 * params:
315 *		mbuf	- The mbuf containing the packet header with the length to
316 *					be changed.
317 * result:
318 *		size_t	- The length, in bytes, of the packet.
319 */
320size_t mbuf_pkthdr_len(const mbuf_t mbuf)
321{
322	return mbuf->m_pkthdr_len;
323}
324
325/*
326 * mbuf_pkthdr_setlen
327 *
328 * Sets the length of the packet in the packet header.
329 * params:
330 *		mbuf	- The mbuf containing the packet header.
331 */
332void mbuf_pkthdr_setlen(mbuf_t mbuf, size_t len)
333{
334	mbuf->m_pkthdr_len = len;
335}
336
337/*
338 * mbuf_pkthdr_adjustlen
339 *
340 * Adjusts the length of the packet in the packet header.
341 * params:
342 *		mbuf	- The mbuf containing the packet header.
343 *		amount	- The number of bytes to adjust the packet header length field.
344 */
345void mbuf_pkthdr_adjustlen(mbuf_t mbuf, int amount)
346{
347	mbuf->m_pkthdr_len += amount;
348}
349
350/*
351 * mbuf_next
352 *
353 * Returns the next mbuf in the chain.
354 * params:
355 *		mbuf	- The mbuf
356 * result:
357 *		mbuf_t	- The next mbuf in the chain.
358 */
359mbuf_t mbuf_next(const mbuf_t mbuf)
360{
361	if (mbuf) {
362		return mbuf->m_next;
363	} else {
364		return NULL;
365	}
366}
367
368/*
369 * mbuf_setnext
370 *
371 * Sets the next mbuf in the chain.
372 * params:
373 *		mbuf	- The mbuf
374 *		next	- The new next mbuf.
375 * result:
376 *		Error	- Either zero or the appropriate errno
377 */
378int mbuf_setnext(mbuf_t mbuf, mbuf_t next)
379{
380	if (!next || (next->m_type == MBUF_TYPE_FREE))
381		return EINVAL;
382	if (mbuf->m_flags & M_EOR) {
383		mbuf->m_flags &= ~M_EOR;
384		next->m_flags |=M_EOR;
385	}
386	mbuf->m_next = next;
387	return 0;
388}
389
390/*
391 * mbuf_data
392 *
393 * Returns a pointer to the start of data in this mbuf. There may be additional
394 * data on chained mbufs. The data you're looking for may not be virtually
395 * contiguous if it spans more than one mbuf.  In addition, data that is virtually
396 * contiguous might not be represented by physically contiguous pages; see
397 * further comments in mbuf_data_to_physical.  Use mbuf_len to determine the length
398 * of data available in this mbuf. If a data structure you want to access stradles
399 * two mbufs in a chain, either use mbuf_pullup to get the data contiguous in one mbuf
400 * or copy the pieces of data from each mbuf in to a contiguous buffer. Using
401 * mbuf_pullup has the advantage of not having to copy the data. On the other hand,
402 * if you don't make sure there is space in the mbuf, mbuf_pullup may fail and
403 * free the mbuf.
404 * params:
405 *		mbuf	- The mbuf
406 *		next	- The new next mbuf.
407 * result:
408 *		void *	- A pointer to the data in the mbuf.
409 */
410void *mbuf_data(const mbuf_t mbuf)
411{
412	if (mbuf) {
413		return (void *)mbuf->m_data;
414	} else {
415		return NULL;
416	}
417}
418
419/*
420 * mbuf_trailingspace
421 *
422 * Determines the space available in the mbuf following the current data.
423 * params:
424 *		mbuf	- The mbuf
425 * result:
426 *		size_t	- The number of unused bytes following the current data.
427 */
428size_t mbuf_trailingspace(const mbuf_t mbuf)
429{
430	return mbuf->m_maxlen - mbuf->m_len;
431}
432
433/*
434 * mbuf_copydata
435 *
436 * Copies data out of an mbuf in to a specified buffer. If the data is stored in
437 * a chain of mbufs, the data will be copied from each mbuf in the chain until
438 * length bytes have been copied.
439 * params:
440 *		mbuf	- The mbuf chain to copy data out of.
441 *		offset	- The offset in to the mbuf to start copying.
442 *		length	- The number of bytes to copy.
443 *		out_data - A pointer to the location where the data will be copied.
444 * result:
445 *		Error	- Either zero or the appropriate errno
446 */
447int mbuf_copydata(const mbuf_t mbuf, size_t offset, size_t length, void *out_data)
448{
449	/* Copied m_copydata, added error handling (don't just panic) */
450	size_t count;
451	mbuf_t  m = mbuf;
452
453	while (offset > 0) {
454		if (m == 0)
455			return EINVAL;
456		if (offset < (size_t)m->m_len)
457			break;
458		offset -= m->m_len;
459		m = m->m_next;
460	}
461	while (length > 0) {
462		if (m == 0)
463			return EINVAL;
464		count = m->m_len - offset > length ? length : m->m_len - offset;
465		bcopy((caddr_t)mbuf_data(m) + offset, out_data, count);
466		length -= count;
467		out_data = ((char*)out_data) + count;
468		offset = 0;
469		m = m->m_next;
470	}
471
472	return 0;
473}
474