/* * Copyright (c) 2000, 2001 Boris Popov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Boris Popov. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: src/sys/kern/subr_mchain.c,v 1.1 2001/02/24 15:44:29 bp Exp $ */ /* * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* BEGIN CSTYLED */ /* * BSD-style mbufs, vs SysV-style mblks: * One big difference: the mbuf payload is: * m_data ... (m_data + m_len) * In Unix STREAMS, the mblk payload is: * b_rptr ... b_wptr * * Here are some handy conversion notes: * * struct mbuf struct mblk * m->m_next m->b_cont * m->m_nextpkt m->b_next * m->m_data m->b_rptr * m->m_len MBLKL(m) * m->m_dat[] m->b_datap->db_base * &m->m_dat[MLEN] m->b_datap->db_lim * M_TRAILINGSPACE(m) MBLKTAIL(m) * m_freem(m) freemsg(m) * * Note that mbufs chains also have a special "packet" header, * which has the length of the whole message. In STREAMS one * typically just calls msgdsize(m) to get that. */ /* END CSTYLED */ /* * * MODULE_VERSION(libmchain, 1); */ #ifdef __GNUC__ #define MBERROR(format, args...) printf("%s(%d): "format, \ __FUNCTION__, __LINE__, ## args) #define MBPANIC(format, args...) printf("%s(%d): "format, \ __FUNCTION__, __LINE__, ## args) #else #define MBERROR(...) \ smb_errmsg(CE_NOTE, __func__, __VA_ARGS__) #define MBPANIC(...) \ smb_errmsg(CE_PANIC, __func__, __VA_ARGS__) #endif /* * MLEN: The smallest mblk we'll allocate. * * There's more to MLEN than you might think. * Some ethernet drivers may send each mblk as a * separate frame, so we want MLEN at least 1K. * We could have used 1K here, but that might * hurt transports that support larger frames. * 4K fits nicely in 3 Ethernet frames (3 * 1500) * leaving about 500 bytes for protocol headers. */ #define MLEN 4096 /* * Some UIO routines. * Taken from Darwin Sourcecs. */ /* * uio_isuserspace - non zero value if the address space * flag is for a user address space (could be 32 or 64 bit). */ #define uio_isuserspace(uio) (uio->uio_segflg == UIO_USERSPACE) /* * uio_curriovbase - return the base address of the current iovec associated * with the given uio_t. May return 0. */ caddr_t uio_curriovbase(uio_t *a_uio) { if (a_uio->uio_iovcnt < 1) { return (0); } return ((caddr_t)((uintptr_t)a_uio->uio_iov->iov_base)); } /* * uio_curriovlen - return the length value of the current iovec associated * with the given uio_t. */ size_t uio_curriovlen(uio_t *a_uio) { if (a_uio->uio_iovcnt < 1) { return (0); } return ((size_t)a_uio->uio_iov->iov_len); } /* * uio_update - update the given uio_t for a_count of completed IO. * This call decrements the current iovec length and residual IO value * and increments the current iovec base address and offset value. * If the current iovec length is 0 then advance to the next * iovec (if any). * If the a_count passed in is 0, than only do the advancement * over any 0 length iovec's. */ void uio_update(uio_t *a_uio, size_t a_count) { if (a_uio->uio_iovcnt < 1) { return; } /* * if a_count == 0, then we are asking to skip over * any empty iovs */ if (a_count) { if (a_count > a_uio->uio_iov->iov_len) { a_uio->uio_iov->iov_base += a_uio->uio_iov->iov_len; a_uio->uio_iov->iov_len = 0; } else { a_uio->uio_iov->iov_base += a_count; a_uio->uio_iov->iov_len -= a_count; } if (a_uio->uio_resid < 0) { a_uio->uio_resid = 0; } if (a_count > (size_t)a_uio->uio_resid) { a_uio->uio_loffset += a_uio->uio_resid; a_uio->uio_resid = 0; } else { a_uio->uio_loffset += a_count; a_uio->uio_resid -= a_count; } } /* * advance to next iovec if current one is totally consumed */ while (a_uio->uio_iovcnt > 0 && a_uio->uio_iov->iov_len == 0) { a_uio->uio_iovcnt--; if (a_uio->uio_iovcnt > 0) { a_uio->uio_iov++; } } } /* * This is now used only to extend an existing mblk chain, * so don't need to use allocb_cred_wait here. */ /*ARGSUSED*/ mblk_t * m_getblk(int size, int type) { mblk_t *mblk; int error; /* Make size at least MLEN. */ if (size < MLEN) size = MLEN; mblk = allocb_wait(size, BPRI_LO, STR_NOSIG, &error); ASSERT(mblk); return (mblk); } void mb_done(struct mbchain *mbp) { if (mbp->mb_top) { freemsg(mbp->mb_top); mbp->mb_top = NULL; } /* Avoid dangling references */ mbp->mb_cur = NULL; } unsigned int m_length(mblk_t *mblk) { uint64_t diff; diff = (uintptr_t)mblk->b_datap->db_lim - (uintptr_t)mblk->b_datap->db_base; ASSERT(diff == (uint64_t)((unsigned int)diff)); return ((unsigned int)diff); } void mb_initm(struct mbchain *mbp, mblk_t *m) { bzero(mbp, sizeof (*mbp)); mbp->mb_top = mbp->mb_cur = m; } int mb_init(struct mbchain *mbp) { cred_t *cr; mblk_t *mblk; int error; /* * This message will be the head of a new mblk chain, * so we'd like its db_credp set. If we extend this * chain later, we'll just use allocb_wait() */ cr = ddi_get_cred(); mblk = allocb_cred_wait(MLEN, STR_NOSIG, &error, cr, NOPID); /* * Leave room in this first mblk so we can * prepend a 4-byte NetBIOS header. * See smb_nbst_send() */ mblk->b_wptr += 4; mblk->b_rptr = mblk->b_wptr; mb_initm(mbp, mblk); return (0); } /* * mb_detach() function returns the value of mbp->mb_top field * and sets its * value to NULL. */ mblk_t * mb_detach(struct mbchain *mbp) { mblk_t *m; m = mbp->mb_top; mbp->mb_top = mbp->mb_cur = NULL; return (m); } /* * Returns the length of the mblk_t data. * Should be m_totlen() perhaps? */ int m_fixhdr(mblk_t *m0) { size_t dsz; dsz = msgdsize(m0); return ((int)dsz); } /* * BSD code set the message header length here, and * returned the length. We don't have that field, so * just return the message length. */ int mb_fixhdr(struct mbchain *mbp) { return (m_fixhdr(mbp->mb_top)); } /* * Check if object of size 'size' fit to the current position and * allocate new mbuf if not. Advance pointers and increase len. of mbuf(s). * Return pointer to the object placeholder or NULL if any error occured. * Note: size should be <= MLEN */ void * mb_reserve(struct mbchain *mbp, int size) { mblk_t *m, *mn; void *bpos; m = mbp->mb_cur; /* * If the requested size is more than the space left. * Allocate and appenad a new mblk. */ if (MBLKTAIL(m) < size) { mn = m_getblk(size, 1); if (mn == NULL) return (NULL); mbp->mb_cur = m->b_cont = mn; m = mn; } /* * If 'size' bytes fits into the buffer, then * 1. increment the write pointer to the size. * 2. return the position from where the memory is reserved. */ bpos = m->b_wptr; m->b_wptr += size; mbp->mb_count += size; return (bpos); } /* * All mb_put_*() functions perform an actual copy of the data into mbuf * chain. Functions which have le or be suffixes will perform conversion to * the little- or big-endian data formats. * * Inline version of mb_put_mem(). Handles the easy case in-line, * and calls mb_put_mem() if crossing mblk boundaries, etc. * * We build with -xspace, which causes these inline functions * to not be inlined. Using macros instead for now. */ #ifdef INLINE_WORKS static inline int mb_put_inline(struct mbchain *mbp, void *src, int size) { mblk_t *m = mbp->mb_cur; if (m != NULL && size <= MBLKTAIL(m)) { uchar_t *p = src; int n = size; while (n--) *(m->b_wptr)++ = *p++; mbp->mb_count += size; return (0); } return (mb_put_mem(mbp, src, size, MB_MINLINE)); } #define MB_PUT_INLINE(MBP, SRC, SZ) \ return (mb_put_inline(MBP, SRC, SZ)) #else /* INLINE_WORKS */ #define MB_PUT_INLINE(MBP, SRC, SZ) \ mblk_t *m = MBP->mb_cur; \ if (m != NULL && SZ <= MBLKTAIL(m)) { \ uchar_t *p = (void *) SRC; \ int n = SZ; \ while (n--) \ *(m->b_wptr)++ = *p++; \ MBP->mb_count += SZ; \ return (0); \ } \ return (mb_put_mem(MBP, SRC, SZ, MB_MINLINE)) #endif /* INLINE_WORKS */ /* * Assumes total data length in previous mblks is EVEN. * Might need to compute the offset from mb_top instead. */ int mb_put_padbyte(struct mbchain *mbp) { uintptr_t dst; char v = 0; dst = (uintptr_t)mbp->mb_cur->b_wptr; /* only add padding if address is odd */ if (dst & 1) { MB_PUT_INLINE(mbp, &v, sizeof (v)); } return (0); } int mb_put_uint8(struct mbchain *mbp, u_int8_t x) { u_int8_t v = x; MB_PUT_INLINE(mbp, &v, sizeof (v)); } int mb_put_uint16be(struct mbchain *mbp, u_int16_t x) { u_int16_t v = htobes(x); MB_PUT_INLINE(mbp, &v, sizeof (v)); } int mb_put_uint16le(struct mbchain *mbp, u_int16_t x) { u_int16_t v = htoles(x); MB_PUT_INLINE(mbp, &v, sizeof (v)); } int mb_put_uint32be(struct mbchain *mbp, u_int32_t x) { u_int32_t v = htobel(x); MB_PUT_INLINE(mbp, &v, sizeof (v)); } int mb_put_uint32le(struct mbchain *mbp, u_int32_t x) { u_int32_t v = htolel(x); MB_PUT_INLINE(mbp, &v, sizeof (v)); } int mb_put_uint64be(struct mbchain *mbp, u_int64_t x) { u_int64_t v = htobeq(x); MB_PUT_INLINE(mbp, &v, sizeof (v)); } int mb_put_uint64le(struct mbchain *mbp, u_int64_t x) { u_int64_t v = htoleq(x); MB_PUT_INLINE(mbp, &v, sizeof (v)); } /* * mb_put_mem() function copies size bytes of data specified by the source * argument to an mbuf chain. The type argument specifies the method used * to perform a copy */ int mb_put_mem(struct mbchain *mbp, const void *vsrc, int size, int type) { mblk_t *n, *m = mbp->mb_cur; c_caddr_t source = vsrc; c_caddr_t src; caddr_t dst; uint64_t diff; int cplen, mleft, count; diff = MBLKTAIL(m); ASSERT(diff == (uint64_t)((int)diff)); mleft = (int)diff; while (size > 0) { if (mleft == 0) { if (m->b_cont == NULL) { /* * Changed m_getm() to m_getblk() * with the requested size, so we * don't need m_getm() anymore. */ n = m_getblk(size, 1); if (n == NULL) return (ENOBUFS); m->b_cont = n; } m = m->b_cont; diff = MBLKTAIL(m); ASSERT(diff == (uint64_t)((int)diff)); mleft = (int)diff; continue; } cplen = mleft > size ? size : mleft; dst = (caddr_t)m->b_wptr; switch (type) { case MB_MINLINE: for (src = source, count = cplen; count; count--) *dst++ = *src++; break; case MB_MSYSTEM: bcopy(source, dst, cplen); break; case MB_MUSER: if (copyin((void *)source, dst, cplen)) return (EFAULT); break; case MB_MZERO: bzero(dst, cplen); break; } size -= cplen; source += cplen; mleft -= cplen; m->b_wptr += cplen; mbp->mb_count += cplen; } mbp->mb_cur = m; return (0); } /* * Append an mblk to the chain. */ int mb_put_mbuf(struct mbchain *mbp, mblk_t *m) { mblk_t *nm, *tail_mb; size_t size; /* See: linkb(9f) */ tail_mb = mbp->mb_cur; while (tail_mb->b_cont != NULL) tail_mb = tail_mb->b_cont; /* * Avoid small frags: Only link if the size of the * new mbuf is larger than the space left in the last * mblk of the chain (tail), otherwise just copy. */ while (m != NULL) { size = MBLKL(m); if (size > MBLKTAIL(tail_mb)) { /* Link */ tail_mb->b_cont = m; mbp->mb_cur = m; mbp->mb_count += msgdsize(m); return (0); } /* Copy */ bcopy(m->b_rptr, tail_mb->b_wptr, size); tail_mb->b_wptr += size; mbp->mb_count += size; nm = unlinkb(m); freeb(m); m = nm; } return (0); } /* * copies a uio scatter/gather list to an mbuf chain. */ int mb_put_uio(struct mbchain *mbp, uio_t *uiop, size_t size) { size_t left; int mtype, error; mtype = (uio_isuserspace(uiop) ? MB_MUSER : MB_MSYSTEM); while (size > 0 && uiop->uio_resid) { if (uiop->uio_iovcnt <= 0 || uio_curriovbase(uiop) == USER_ADDR_NULL) return (EFBIG); left = uio_curriovlen(uiop); if (left > size) left = size; error = mb_put_mem(mbp, CAST_DOWN(caddr_t, uio_curriovbase(uiop)), left, mtype); if (error) return (error); uio_update(uiop, left); size -= left; } return (0); } /* * Routines for fetching data from an mbuf chain */ void md_initm(struct mdchain *mdp, mblk_t *m) { bzero(mdp, sizeof (*mdp)); mdp->md_top = mdp->md_cur = m; mdp->md_pos = m->b_rptr; } void md_done(struct mdchain *mdp) { mblk_t *m; /* * Deal with the fact that we can error out of * smb_t2_reply or smb_nt_reply without using up * all the "records" added by md_append_record(). */ while ((m = mdp->md_top) != NULL) { mdp->md_top = m->b_next; m->b_next = NULL; freemsg(m); } /* Avoid dangling references */ mdp->md_cur = NULL; mdp->md_pos = NULL; } /* * Append a new message (separate mbuf chain). * It is caller responsibility to prevent * multiple calls to fetch/record routines. * Note unusual use of mblk->b_next here. */ void md_append_record(struct mdchain *mdp, mblk_t *top) { mblk_t *m; top->b_next = NULL; if (mdp->md_top == NULL) { md_initm(mdp, top); return; } m = mdp->md_top; /* Get to last message (not b_cont chain) */ while (m->b_next) m = m->b_next; m->b_next = top; } /* * Advance mdp->md_top to the next message. * Note unusual use of mblk->b_next here. */ void md_next_record(struct mdchain *mdp) { mblk_t *m, *top; if ((top = mdp->md_top) == NULL) return; /* * Get the next message, if any, * stored by md_append_record. * Note: NOT b_cont chain */ m = top->b_next; top->b_next = NULL; /* Done with old "top". */ md_done(mdp); if (m == NULL) return; /* Setup new "top". */ md_initm(mdp, m); } /* * Inline version of md_get_mem(). Handles the easy case in-line, * and calls md_get_mem() if crossing mblk boundaries, etc. */ #ifdef INLINE_WORKS /* see above */ static inline int md_get_inline(struct mdchain *mdp, void *dst, int size) { mblk_t *m = mdp->md_cur; if (m != NULL && mdp->md_pos + size <= m->b_wptr) { uchar_t *p = dst; int n = size; while (n--) *p++ = *(mdp->md_pos)++; /* no md_count += size */ return (0); } return (md_get_mem(mdp, dst, size, MB_MINLINE)); } #define MD_GET_INLINE(MDP, DST, SZ) \ error = md_get_inline(MDP, DST, SZ) #else /* INLINE_WORKS */ /* Note, sets variable: error */ #define MD_GET_INLINE(MDP, DST, SZ) \ mblk_t *m = MDP->md_cur; \ if (m != NULL && MDP->md_pos + SZ <= m->b_wptr) { \ uchar_t *p = (void *) DST; \ int n = SZ; \ while (n--) \ *p++ = *(mdp->md_pos)++; \ /* no md_count += SZ */ \ error = 0; \ } else \ error = md_get_mem(MDP, DST, SZ, MB_MINLINE) #endif /* INLINE_WORKS */ int md_get_uint8(struct mdchain *mdp, u_int8_t *x) { uint8_t v; int error; MD_GET_INLINE(mdp, &v, sizeof (v)); if (x) *x = v; return (error); } int md_get_uint16be(struct mdchain *mdp, u_int16_t *x) { u_int16_t v; int error; MD_GET_INLINE(mdp, &v, sizeof (v)); if (x) *x = betohs(v); return (error); } int md_get_uint16le(struct mdchain *mdp, u_int16_t *x) { u_int16_t v; int error; MD_GET_INLINE(mdp, &v, sizeof (v)); if (x) *x = letohs(v); return (error); } int md_get_uint32be(struct mdchain *mdp, u_int32_t *x) { u_int32_t v; int error; MD_GET_INLINE(mdp, &v, sizeof (v)); if (x) *x = betohl(v); return (error); } int md_get_uint32le(struct mdchain *mdp, u_int32_t *x) { u_int32_t v; int error; MD_GET_INLINE(mdp, &v, sizeof (v)); if (x) *x = letohl(v); return (error); } int md_get_uint64be(struct mdchain *mdp, u_int64_t *x) { u_int64_t v; int error; MD_GET_INLINE(mdp, &v, sizeof (v)); if (x) *x = betohq(v); return (error); } int md_get_uint64le(struct mdchain *mdp, u_int64_t *x) { u_int64_t v; int error; MD_GET_INLINE(mdp, &v, sizeof (v)); if (x) *x = letohq(v); return (error); } int md_get_mem(struct mdchain *mdp, void *vdst, int size, int type) { mblk_t *m = mdp->md_cur; caddr_t target = vdst; unsigned char *s; uint64_t diff; int count; while (size > 0) { if (m == NULL) { SMBSDEBUG("incomplete copy\n"); return (EBADRPC); } /* * Offset in the current MBUF. */ s = mdp->md_pos; ASSERT((m->b_rptr <= s) && (s <= m->b_wptr)); /* Data remaining. */ diff = (uintptr_t)m->b_wptr - (uintptr_t)s; ASSERT(diff == (uint64_t)((int)diff)); count = (int)diff; /* * Check if the no. of bytes remaining is less than * the bytes requested. */ if (count == 0) { m = m->b_cont; if (m) { mdp->md_cur = m; mdp->md_pos = s = m->b_rptr; } continue; } if (count > size) count = size; size -= count; mdp->md_pos += count; if (target == NULL) continue; switch (type) { case MB_MUSER: if (copyout(s, target, count)) return (EFAULT); break; case MB_MSYSTEM: bcopy(s, target, count); break; case MB_MINLINE: while (count--) *target++ = *s++; continue; } target += count; } return (0); } /* * Get the next SIZE bytes as a separate mblk. */ int md_get_mbuf(struct mdchain *mdp, int size, mblk_t **ret) { mblk_t *m, *rm; unsigned char *s; uint64_t diff; int off; /* * Offset in the current MBUF. */ m = mdp->md_cur; s = mdp->md_pos; ASSERT((m->b_rptr <= s) && (s <= m->b_wptr)); diff = (uintptr_t)s - (uintptr_t)m->b_rptr; ASSERT(diff == (uint64_t)((int)diff)); off = (int)diff; rm = m_copym(m, off, size, M_WAITOK); if (rm == NULL) return (EBADRPC); *ret = rm; return (0); } int md_get_uio(struct mdchain *mdp, uio_t *uiop, size_t size) { size_t left; int mtype, error; mtype = (uio_isuserspace(uiop) ? MB_MUSER : MB_MSYSTEM); while (size > 0 && uiop->uio_resid) { if (uiop->uio_iovcnt <= 0 || uio_curriovbase(uiop) == USER_ADDR_NULL) return (EFBIG); left = uio_curriovlen(uiop); if (left > size) left = size; error = md_get_mem(mdp, CAST_DOWN(caddr_t, uio_curriovbase(uiop)), left, mtype); if (error) return (error); uio_update(uiop, left); size -= left; } return (0); } /* * Additions for Solaris */ /* * concatenate mblk chain n to m. * go till end of data in m. * then add the link of b_cont to n. * See: linkb(9f) */ void m_cat( mblk_t *m, mblk_t *n) { if (!n) return; while (m->b_cont) { m = m->b_cont; } m->b_cont = n; } /*ARGSUSED*/ mblk_t * m_copym(mblk_t *m, int off, int len, int wait) { mblk_t *n; size_t dsz; ssize_t adj; dsz = msgdsize(m); if (len == M_COPYALL) { if (off > dsz) return (0); } else { if ((off + len) > dsz) return (0); } if ((n = dupmsg(m)) == NULL) return (0); /* trim from head */ adj = off; if (!adjmsg(n, adj)) { freemsg(n); return (0); } /* trim from tail */ if (len != M_COPYALL) { dsz = msgdsize(n); ASSERT(len <= dsz); if (len < dsz) { adj = (ssize_t)len - (ssize_t)dsz; ASSERT(adj < 0); (void) adjmsg(n, adj); } } return (n); } /* * Get "rqlen" contiguous bytes into the first mblk of a chain. */ mblk_t * m_pullup( mblk_t *m, int rqlen) { ptrdiff_t diff; diff = MBLKL(m); ASSERT(diff == (ptrdiff_t)((int)diff)); if ((int)diff < rqlen) { /* This should be rare. */ if (!pullupmsg(m, rqlen)) { SMBSDEBUG("pullupmsg failed!\n"); freemsg(m); return (NULL); } } return (m); } /* * m_split : split the mblk from the offset(len0) to the end. * Partition an mbuf chain in two pieces, returning the tail -- * all but the first len0 bytes. In case of failure, it returns NULL and * attempts to restore the chain to its original state. * Similar to dupmsg() + adjmsg() on Solaris. */ /*ARGSUSED*/ mblk_t * m_split( mblk_t *m0, int len0, int wait) { mblk_t *m, *n; int mbl, len = len0; ptrdiff_t diff; #if 0 /* If life were simple, this would be: */ for (m = m0; m && len > MBLKL(m); m = m->b_cont) len -= MBLKL(m); #else /* but with LP64 and picky lint we have: */ for (m = m0; m; m = m->b_cont) { diff = MBLKL(m); ASSERT(diff == (ptrdiff_t)((int)diff)); mbl = (int)diff; if (len <= mbl) break; len -= mbl; } #endif if (m == 0) return (0); /* This is the one to split (dupb, adjust) */ if ((n = dupb(m)) == 0) return (0); ASSERT(len <= MBLKL(m)); m->b_wptr = m->b_rptr + len; n->b_rptr += len; /* Move any b_cont (tail) to the new head. */ n->b_cont = m->b_cont; m->b_cont = NULL; return (n); }