static int skb_remove_foreign_references(struct sk_buff *skb) { struct page *page; unsigned long pfn; int i, off; char *vaddr; BUG_ON(skb_shinfo(skb)->frag_list); if (skb_cloned(skb) && unlikely(pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) return 0; for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { pfn = page_to_pfn(skb_shinfo(skb)->frags[i].page); if (!is_foreign(pfn)) continue; page = alloc_page(GFP_ATOMIC | __GFP_NOWARN); if (unlikely(!page)) return 0; vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]); off = skb_shinfo(skb)->frags[i].page_offset; memcpy(page_address(page) + off, vaddr + off, skb_shinfo(skb)->frags[i].size); kunmap_skb_frag(vaddr); put_page(skb_shinfo(skb)->frags[i].page); skb_shinfo(skb)->frags[i].page = page; } return 1; }
/** Copy some data bits from a kernel buffer to an skb. * Derived in the obvious way from skb_copy_bits(). */ int skb_put_bits(const struct sk_buff *skb, int offset, void *src, int len) { int i, copy; int start = skb->len - skb->data_len; if (offset > (int)skb->len-len) goto fault; /* Copy header. */ if ((copy = start-offset) > 0) { if (copy > len) copy = len; memcpy(skb->data + offset, src, copy); if ((len -= copy) == 0) return 0; offset += copy; src += copy; } #ifdef __KERNEL__ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { int end; BUG_TRAP(start <= offset+len); end = start + skb_shinfo(skb)->frags[i].size; if ((copy = end-offset) > 0) { u8 *vaddr; if (copy > len) copy = len; vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]); memcpy(vaddr + skb_shinfo(skb)->frags[i].page_offset + offset - start, src, copy); kunmap_skb_frag(vaddr); if ((len -= copy) == 0) return 0; offset += copy; src += copy; } start = end; } if (skb_shinfo(skb)->frag_list) { struct sk_buff *list; for (list = skb_shinfo(skb)->frag_list; list; list=list->next) { int end; BUG_TRAP(start <= offset+len); end = start + list->len; if ((copy = end-offset) > 0) { if (copy > len) copy = len; if (skb_put_bits(list, offset-start, src, copy)) goto fault; if ((len -= copy) == 0) return 0; offset += copy; src += copy; } start = end; } } #else i=0; #endif if (len == 0) return 0; fault: return -EFAULT; }