diff options
Diffstat (limited to 'drivers/net/pppol2tp.c')
| -rw-r--r-- | drivers/net/pppol2tp.c | 144 |
1 files changed, 108 insertions, 36 deletions
diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index 79359919335b..f9298827a76c 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c | |||
| @@ -240,12 +240,15 @@ static inline struct pppol2tp_session *pppol2tp_sock_to_session(struct sock *sk) | |||
| 240 | if (sk == NULL) | 240 | if (sk == NULL) |
| 241 | return NULL; | 241 | return NULL; |
| 242 | 242 | ||
| 243 | sock_hold(sk); | ||
| 243 | session = (struct pppol2tp_session *)(sk->sk_user_data); | 244 | session = (struct pppol2tp_session *)(sk->sk_user_data); |
| 244 | if (session == NULL) | 245 | if (session == NULL) { |
| 245 | return NULL; | 246 | sock_put(sk); |
| 247 | goto out; | ||
| 248 | } | ||
| 246 | 249 | ||
| 247 | BUG_ON(session->magic != L2TP_SESSION_MAGIC); | 250 | BUG_ON(session->magic != L2TP_SESSION_MAGIC); |
| 248 | 251 | out: | |
| 249 | return session; | 252 | return session; |
| 250 | } | 253 | } |
| 251 | 254 | ||
| @@ -256,12 +259,15 @@ static inline struct pppol2tp_tunnel *pppol2tp_sock_to_tunnel(struct sock *sk) | |||
| 256 | if (sk == NULL) | 259 | if (sk == NULL) |
| 257 | return NULL; | 260 | return NULL; |
| 258 | 261 | ||
| 262 | sock_hold(sk); | ||
| 259 | tunnel = (struct pppol2tp_tunnel *)(sk->sk_user_data); | 263 | tunnel = (struct pppol2tp_tunnel *)(sk->sk_user_data); |
| 260 | if (tunnel == NULL) | 264 | if (tunnel == NULL) { |
| 261 | return NULL; | 265 | sock_put(sk); |
| 266 | goto out; | ||
| 267 | } | ||
| 262 | 268 | ||
| 263 | BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC); | 269 | BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC); |
| 264 | 270 | out: | |
| 265 | return tunnel; | 271 | return tunnel; |
| 266 | } | 272 | } |
| 267 | 273 | ||
| @@ -716,12 +722,14 @@ discard: | |||
| 716 | session->stats.rx_errors++; | 722 | session->stats.rx_errors++; |
| 717 | kfree_skb(skb); | 723 | kfree_skb(skb); |
| 718 | sock_put(session->sock); | 724 | sock_put(session->sock); |
| 725 | sock_put(sock); | ||
| 719 | 726 | ||
| 720 | return 0; | 727 | return 0; |
| 721 | 728 | ||
| 722 | error: | 729 | error: |
| 723 | /* Put UDP header back */ | 730 | /* Put UDP header back */ |
| 724 | __skb_push(skb, sizeof(struct udphdr)); | 731 | __skb_push(skb, sizeof(struct udphdr)); |
| 732 | sock_put(sock); | ||
| 725 | 733 | ||
| 726 | no_tunnel: | 734 | no_tunnel: |
| 727 | return 1; | 735 | return 1; |
| @@ -745,10 +753,13 @@ static int pppol2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb) | |||
| 745 | "%s: received %d bytes\n", tunnel->name, skb->len); | 753 | "%s: received %d bytes\n", tunnel->name, skb->len); |
| 746 | 754 | ||
| 747 | if (pppol2tp_recv_core(sk, skb)) | 755 | if (pppol2tp_recv_core(sk, skb)) |
| 748 | goto pass_up; | 756 | goto pass_up_put; |
| 749 | 757 | ||
| 758 | sock_put(sk); | ||
| 750 | return 0; | 759 | return 0; |
| 751 | 760 | ||
| 761 | pass_up_put: | ||
| 762 | sock_put(sk); | ||
| 752 | pass_up: | 763 | pass_up: |
| 753 | return 1; | 764 | return 1; |
| 754 | } | 765 | } |
| @@ -772,14 +783,18 @@ static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock, | |||
| 772 | err = 0; | 783 | err = 0; |
| 773 | skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, | 784 | skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, |
| 774 | flags & MSG_DONTWAIT, &err); | 785 | flags & MSG_DONTWAIT, &err); |
| 775 | if (skb) { | 786 | if (!skb) |
| 776 | err = memcpy_toiovec(msg->msg_iov, (unsigned char *) skb->data, | 787 | goto end; |
| 777 | skb->len); | 788 | |
| 778 | if (err < 0) | 789 | if (len > skb->len) |
| 779 | goto do_skb_free; | 790 | len = skb->len; |
| 780 | err = skb->len; | 791 | else if (len < skb->len) |
| 781 | } | 792 | msg->msg_flags |= MSG_TRUNC; |
| 782 | do_skb_free: | 793 | |
| 794 | err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len); | ||
| 795 | if (likely(err == 0)) | ||
| 796 | err = len; | ||
| 797 | |||
| 783 | kfree_skb(skb); | 798 | kfree_skb(skb); |
| 784 | end: | 799 | end: |
| 785 | return err; | 800 | return err; |
| @@ -858,7 +873,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh | |||
| 858 | 873 | ||
| 859 | tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); | 874 | tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); |
| 860 | if (tunnel == NULL) | 875 | if (tunnel == NULL) |
| 861 | goto error; | 876 | goto error_put_sess; |
| 862 | 877 | ||
| 863 | /* What header length is configured for this session? */ | 878 | /* What header length is configured for this session? */ |
| 864 | hdr_len = pppol2tp_l2tp_header_len(session); | 879 | hdr_len = pppol2tp_l2tp_header_len(session); |
| @@ -870,7 +885,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh | |||
| 870 | sizeof(ppph) + total_len, | 885 | sizeof(ppph) + total_len, |
| 871 | 0, GFP_KERNEL); | 886 | 0, GFP_KERNEL); |
| 872 | if (!skb) | 887 | if (!skb) |
| 873 | goto error; | 888 | goto error_put_sess_tun; |
| 874 | 889 | ||
| 875 | /* Reserve space for headers. */ | 890 | /* Reserve space for headers. */ |
| 876 | skb_reserve(skb, NET_SKB_PAD); | 891 | skb_reserve(skb, NET_SKB_PAD); |
| @@ -900,7 +915,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh | |||
| 900 | error = memcpy_fromiovec(skb->data, m->msg_iov, total_len); | 915 | error = memcpy_fromiovec(skb->data, m->msg_iov, total_len); |
| 901 | if (error < 0) { | 916 | if (error < 0) { |
| 902 | kfree_skb(skb); | 917 | kfree_skb(skb); |
| 903 | goto error; | 918 | goto error_put_sess_tun; |
| 904 | } | 919 | } |
| 905 | skb_put(skb, total_len); | 920 | skb_put(skb, total_len); |
| 906 | 921 | ||
| @@ -947,10 +962,33 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh | |||
| 947 | session->stats.tx_errors++; | 962 | session->stats.tx_errors++; |
| 948 | } | 963 | } |
| 949 | 964 | ||
| 965 | return error; | ||
| 966 | |||
| 967 | error_put_sess_tun: | ||
| 968 | sock_put(session->tunnel_sock); | ||
| 969 | error_put_sess: | ||
| 970 | sock_put(sk); | ||
| 950 | error: | 971 | error: |
| 951 | return error; | 972 | return error; |
| 952 | } | 973 | } |
| 953 | 974 | ||
| 975 | /* Automatically called when the skb is freed. | ||
| 976 | */ | ||
| 977 | static void pppol2tp_sock_wfree(struct sk_buff *skb) | ||
| 978 | { | ||
| 979 | sock_put(skb->sk); | ||
| 980 | } | ||
| 981 | |||
| 982 | /* For data skbs that we transmit, we associate with the tunnel socket | ||
| 983 | * but don't do accounting. | ||
| 984 | */ | ||
| 985 | static inline void pppol2tp_skb_set_owner_w(struct sk_buff *skb, struct sock *sk) | ||
| 986 | { | ||
| 987 | sock_hold(sk); | ||
| 988 | skb->sk = sk; | ||
| 989 | skb->destructor = pppol2tp_sock_wfree; | ||
| 990 | } | ||
| 991 | |||
| 954 | /* Transmit function called by generic PPP driver. Sends PPP frame | 992 | /* Transmit function called by generic PPP driver. Sends PPP frame |
| 955 | * over PPPoL2TP socket. | 993 | * over PPPoL2TP socket. |
| 956 | * | 994 | * |
| @@ -980,6 +1018,8 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) | |||
| 980 | __wsum csum = 0; | 1018 | __wsum csum = 0; |
| 981 | struct udphdr *uh; | 1019 | struct udphdr *uh; |
| 982 | unsigned int len; | 1020 | unsigned int len; |
| 1021 | int old_headroom; | ||
| 1022 | int new_headroom; | ||
| 983 | 1023 | ||
| 984 | if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) | 1024 | if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) |
| 985 | goto abort; | 1025 | goto abort; |
| @@ -991,25 +1031,27 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) | |||
| 991 | 1031 | ||
| 992 | sk_tun = session->tunnel_sock; | 1032 | sk_tun = session->tunnel_sock; |
| 993 | if (sk_tun == NULL) | 1033 | if (sk_tun == NULL) |
| 994 | goto abort; | 1034 | goto abort_put_sess; |
| 995 | tunnel = pppol2tp_sock_to_tunnel(sk_tun); | 1035 | tunnel = pppol2tp_sock_to_tunnel(sk_tun); |
| 996 | if (tunnel == NULL) | 1036 | if (tunnel == NULL) |
| 997 | goto abort; | 1037 | goto abort_put_sess; |
| 998 | 1038 | ||
| 999 | /* What header length is configured for this session? */ | 1039 | /* What header length is configured for this session? */ |
| 1000 | hdr_len = pppol2tp_l2tp_header_len(session); | 1040 | hdr_len = pppol2tp_l2tp_header_len(session); |
| 1001 | 1041 | ||
| 1002 | /* Check that there's enough headroom in the skb to insert IP, | 1042 | /* Check that there's enough headroom in the skb to insert IP, |
| 1003 | * UDP and L2TP and PPP headers. If not enough, expand it to | 1043 | * UDP and L2TP and PPP headers. If not enough, expand it to |
| 1004 | * make room. Note that a new skb (or a clone) is | 1044 | * make room. Adjust truesize. |
| 1005 | * allocated. If we return an error from this point on, make | ||
| 1006 | * sure we free the new skb but do not free the original skb | ||
| 1007 | * since that is done by the caller for the error case. | ||
| 1008 | */ | 1045 | */ |
| 1009 | headroom = NET_SKB_PAD + sizeof(struct iphdr) + | 1046 | headroom = NET_SKB_PAD + sizeof(struct iphdr) + |
| 1010 | sizeof(struct udphdr) + hdr_len + sizeof(ppph); | 1047 | sizeof(struct udphdr) + hdr_len + sizeof(ppph); |
| 1048 | old_headroom = skb_headroom(skb); | ||
| 1011 | if (skb_cow_head(skb, headroom)) | 1049 | if (skb_cow_head(skb, headroom)) |
| 1012 | goto abort; | 1050 | goto abort_put_sess_tun; |
| 1051 | |||
| 1052 | new_headroom = skb_headroom(skb); | ||
| 1053 | skb_orphan(skb); | ||
| 1054 | skb->truesize += new_headroom - old_headroom; | ||
| 1013 | 1055 | ||
| 1014 | /* Setup PPP header */ | 1056 | /* Setup PPP header */ |
| 1015 | __skb_push(skb, sizeof(ppph)); | 1057 | __skb_push(skb, sizeof(ppph)); |
| @@ -1065,8 +1107,7 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) | |||
| 1065 | /* Get routing info from the tunnel socket */ | 1107 | /* Get routing info from the tunnel socket */ |
| 1066 | dst_release(skb->dst); | 1108 | dst_release(skb->dst); |
| 1067 | skb->dst = dst_clone(__sk_dst_get(sk_tun)); | 1109 | skb->dst = dst_clone(__sk_dst_get(sk_tun)); |
| 1068 | skb_orphan(skb); | 1110 | pppol2tp_skb_set_owner_w(skb, sk_tun); |
| 1069 | skb->sk = sk_tun; | ||
| 1070 | 1111 | ||
| 1071 | /* Queue the packet to IP for output */ | 1112 | /* Queue the packet to IP for output */ |
| 1072 | len = skb->len; | 1113 | len = skb->len; |
| @@ -1083,8 +1124,14 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) | |||
| 1083 | session->stats.tx_errors++; | 1124 | session->stats.tx_errors++; |
| 1084 | } | 1125 | } |
| 1085 | 1126 | ||
| 1127 | sock_put(sk_tun); | ||
| 1128 | sock_put(sk); | ||
| 1086 | return 1; | 1129 | return 1; |
| 1087 | 1130 | ||
| 1131 | abort_put_sess_tun: | ||
| 1132 | sock_put(sk_tun); | ||
| 1133 | abort_put_sess: | ||
| 1134 | sock_put(sk); | ||
| 1088 | abort: | 1135 | abort: |
| 1089 | /* Free the original skb */ | 1136 | /* Free the original skb */ |
| 1090 | kfree_skb(skb); | 1137 | kfree_skb(skb); |
| @@ -1188,7 +1235,7 @@ static void pppol2tp_tunnel_destruct(struct sock *sk) | |||
| 1188 | { | 1235 | { |
| 1189 | struct pppol2tp_tunnel *tunnel; | 1236 | struct pppol2tp_tunnel *tunnel; |
| 1190 | 1237 | ||
| 1191 | tunnel = pppol2tp_sock_to_tunnel(sk); | 1238 | tunnel = sk->sk_user_data; |
| 1192 | if (tunnel == NULL) | 1239 | if (tunnel == NULL) |
| 1193 | goto end; | 1240 | goto end; |
| 1194 | 1241 | ||
| @@ -1227,10 +1274,12 @@ static void pppol2tp_session_destruct(struct sock *sk) | |||
| 1227 | if (sk->sk_user_data != NULL) { | 1274 | if (sk->sk_user_data != NULL) { |
| 1228 | struct pppol2tp_tunnel *tunnel; | 1275 | struct pppol2tp_tunnel *tunnel; |
| 1229 | 1276 | ||
| 1230 | session = pppol2tp_sock_to_session(sk); | 1277 | session = sk->sk_user_data; |
| 1231 | if (session == NULL) | 1278 | if (session == NULL) |
| 1232 | goto out; | 1279 | goto out; |
| 1233 | 1280 | ||
| 1281 | BUG_ON(session->magic != L2TP_SESSION_MAGIC); | ||
| 1282 | |||
| 1234 | /* Don't use pppol2tp_sock_to_tunnel() here to | 1283 | /* Don't use pppol2tp_sock_to_tunnel() here to |
| 1235 | * get the tunnel context because the tunnel | 1284 | * get the tunnel context because the tunnel |
| 1236 | * socket might have already been closed (its | 1285 | * socket might have already been closed (its |
| @@ -1276,6 +1325,7 @@ out: | |||
| 1276 | static int pppol2tp_release(struct socket *sock) | 1325 | static int pppol2tp_release(struct socket *sock) |
| 1277 | { | 1326 | { |
| 1278 | struct sock *sk = sock->sk; | 1327 | struct sock *sk = sock->sk; |
| 1328 | struct pppol2tp_session *session; | ||
| 1279 | int error; | 1329 | int error; |
| 1280 | 1330 | ||
| 1281 | if (!sk) | 1331 | if (!sk) |
| @@ -1293,9 +1343,18 @@ static int pppol2tp_release(struct socket *sock) | |||
| 1293 | sock_orphan(sk); | 1343 | sock_orphan(sk); |
| 1294 | sock->sk = NULL; | 1344 | sock->sk = NULL; |
| 1295 | 1345 | ||
| 1346 | session = pppol2tp_sock_to_session(sk); | ||
| 1347 | |||
| 1296 | /* Purge any queued data */ | 1348 | /* Purge any queued data */ |
| 1297 | skb_queue_purge(&sk->sk_receive_queue); | 1349 | skb_queue_purge(&sk->sk_receive_queue); |
| 1298 | skb_queue_purge(&sk->sk_write_queue); | 1350 | skb_queue_purge(&sk->sk_write_queue); |
| 1351 | if (session != NULL) { | ||
| 1352 | struct sk_buff *skb; | ||
| 1353 | while ((skb = skb_dequeue(&session->reorder_q))) { | ||
| 1354 | kfree_skb(skb); | ||
| 1355 | sock_put(sk); | ||
| 1356 | } | ||
| 1357 | } | ||
| 1299 | 1358 | ||
| 1300 | release_sock(sk); | 1359 | release_sock(sk); |
| 1301 | 1360 | ||
| @@ -1598,7 +1657,7 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, | |||
| 1598 | 1657 | ||
| 1599 | error = ppp_register_channel(&po->chan); | 1658 | error = ppp_register_channel(&po->chan); |
| 1600 | if (error) | 1659 | if (error) |
| 1601 | goto end; | 1660 | goto end_put_tun; |
| 1602 | 1661 | ||
| 1603 | /* This is how we get the session context from the socket. */ | 1662 | /* This is how we get the session context from the socket. */ |
| 1604 | sk->sk_user_data = session; | 1663 | sk->sk_user_data = session; |
| @@ -1618,6 +1677,8 @@ out_no_ppp: | |||
| 1618 | PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, | 1677 | PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO, |
| 1619 | "%s: created\n", session->name); | 1678 | "%s: created\n", session->name); |
| 1620 | 1679 | ||
| 1680 | end_put_tun: | ||
| 1681 | sock_put(tunnel_sock); | ||
| 1621 | end: | 1682 | end: |
| 1622 | release_sock(sk); | 1683 | release_sock(sk); |
| 1623 | 1684 | ||
| @@ -1665,6 +1726,7 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr, | |||
| 1665 | *usockaddr_len = len; | 1726 | *usockaddr_len = len; |
| 1666 | 1727 | ||
| 1667 | error = 0; | 1728 | error = 0; |
| 1729 | sock_put(sock->sk); | ||
| 1668 | 1730 | ||
| 1669 | end: | 1731 | end: |
| 1670 | return error; | 1732 | return error; |
| @@ -1903,14 +1965,17 @@ static int pppol2tp_ioctl(struct socket *sock, unsigned int cmd, | |||
| 1903 | err = -EBADF; | 1965 | err = -EBADF; |
| 1904 | tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); | 1966 | tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); |
| 1905 | if (tunnel == NULL) | 1967 | if (tunnel == NULL) |
| 1906 | goto end; | 1968 | goto end_put_sess; |
| 1907 | 1969 | ||
| 1908 | err = pppol2tp_tunnel_ioctl(tunnel, cmd, arg); | 1970 | err = pppol2tp_tunnel_ioctl(tunnel, cmd, arg); |
| 1909 | goto end; | 1971 | sock_put(session->tunnel_sock); |
| 1972 | goto end_put_sess; | ||
| 1910 | } | 1973 | } |
| 1911 | 1974 | ||
| 1912 | err = pppol2tp_session_ioctl(session, cmd, arg); | 1975 | err = pppol2tp_session_ioctl(session, cmd, arg); |
| 1913 | 1976 | ||
| 1977 | end_put_sess: | ||
| 1978 | sock_put(sk); | ||
| 1914 | end: | 1979 | end: |
| 1915 | return err; | 1980 | return err; |
| 1916 | } | 1981 | } |
| @@ -2056,14 +2121,17 @@ static int pppol2tp_setsockopt(struct socket *sock, int level, int optname, | |||
| 2056 | err = -EBADF; | 2121 | err = -EBADF; |
| 2057 | tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); | 2122 | tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); |
| 2058 | if (tunnel == NULL) | 2123 | if (tunnel == NULL) |
| 2059 | goto end; | 2124 | goto end_put_sess; |
| 2060 | 2125 | ||
| 2061 | err = pppol2tp_tunnel_setsockopt(sk, tunnel, optname, val); | 2126 | err = pppol2tp_tunnel_setsockopt(sk, tunnel, optname, val); |
| 2127 | sock_put(session->tunnel_sock); | ||
| 2062 | } else | 2128 | } else |
| 2063 | err = pppol2tp_session_setsockopt(sk, session, optname, val); | 2129 | err = pppol2tp_session_setsockopt(sk, session, optname, val); |
| 2064 | 2130 | ||
| 2065 | err = 0; | 2131 | err = 0; |
| 2066 | 2132 | ||
| 2133 | end_put_sess: | ||
| 2134 | sock_put(sk); | ||
| 2067 | end: | 2135 | end: |
| 2068 | return err; | 2136 | return err; |
| 2069 | } | 2137 | } |
| @@ -2178,20 +2246,24 @@ static int pppol2tp_getsockopt(struct socket *sock, int level, | |||
| 2178 | err = -EBADF; | 2246 | err = -EBADF; |
| 2179 | tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); | 2247 | tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock); |
| 2180 | if (tunnel == NULL) | 2248 | if (tunnel == NULL) |
| 2181 | goto end; | 2249 | goto end_put_sess; |
| 2182 | 2250 | ||
| 2183 | err = pppol2tp_tunnel_getsockopt(sk, tunnel, optname, &val); | 2251 | err = pppol2tp_tunnel_getsockopt(sk, tunnel, optname, &val); |
| 2252 | sock_put(session->tunnel_sock); | ||
| 2184 | } else | 2253 | } else |
| 2185 | err = pppol2tp_session_getsockopt(sk, session, optname, &val); | 2254 | err = pppol2tp_session_getsockopt(sk, session, optname, &val); |
| 2186 | 2255 | ||
| 2187 | err = -EFAULT; | 2256 | err = -EFAULT; |
| 2188 | if (put_user(len, (int __user *) optlen)) | 2257 | if (put_user(len, (int __user *) optlen)) |
| 2189 | goto end; | 2258 | goto end_put_sess; |
| 2190 | 2259 | ||
| 2191 | if (copy_to_user((void __user *) optval, &val, len)) | 2260 | if (copy_to_user((void __user *) optval, &val, len)) |
| 2192 | goto end; | 2261 | goto end_put_sess; |
| 2193 | 2262 | ||
| 2194 | err = 0; | 2263 | err = 0; |
| 2264 | |||
| 2265 | end_put_sess: | ||
| 2266 | sock_put(sk); | ||
| 2195 | end: | 2267 | end: |
| 2196 | return err; | 2268 | return err; |
| 2197 | } | 2269 | } |
