1c398230bSWarner Losh /*- 2681a5bbeSBoris Popov * Copyright (c) 2000-2001 Boris Popov 3681a5bbeSBoris Popov * All rights reserved. 4681a5bbeSBoris Popov * 5681a5bbeSBoris Popov * Redistribution and use in source and binary forms, with or without 6681a5bbeSBoris Popov * modification, are permitted provided that the following conditions 7681a5bbeSBoris Popov * are met: 8681a5bbeSBoris Popov * 1. Redistributions of source code must retain the above copyright 9681a5bbeSBoris Popov * notice, this list of conditions and the following disclaimer. 10681a5bbeSBoris Popov * 2. Redistributions in binary form must reproduce the above copyright 11681a5bbeSBoris Popov * notice, this list of conditions and the following disclaimer in the 12681a5bbeSBoris Popov * documentation and/or other materials provided with the distribution. 13681a5bbeSBoris Popov * 3. All advertising materials mentioning features or use of this software 14681a5bbeSBoris Popov * must display the following acknowledgement: 15681a5bbeSBoris Popov * This product includes software developed by Boris Popov. 16681a5bbeSBoris Popov * 4. Neither the name of the author nor the names of any co-contributors 17681a5bbeSBoris Popov * may be used to endorse or promote products derived from this software 18681a5bbeSBoris Popov * without specific prior written permission. 19681a5bbeSBoris Popov * 20681a5bbeSBoris Popov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21681a5bbeSBoris Popov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22681a5bbeSBoris Popov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23681a5bbeSBoris Popov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24681a5bbeSBoris Popov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25681a5bbeSBoris Popov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26681a5bbeSBoris Popov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27681a5bbeSBoris Popov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28681a5bbeSBoris Popov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29681a5bbeSBoris Popov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30681a5bbeSBoris Popov * SUCH DAMAGE. 31681a5bbeSBoris Popov */ 32681a5bbeSBoris Popov 33ab0de15bSDavid E. O'Brien #include <sys/cdefs.h> 34ab0de15bSDavid E. O'Brien __FBSDID("$FreeBSD$"); 35ab0de15bSDavid E. O'Brien 36681a5bbeSBoris Popov #include <sys/param.h> 37681a5bbeSBoris Popov #include <sys/systm.h> 38a30d4b32SMike Barcroft #include <sys/endian.h> 39681a5bbeSBoris Popov #include <sys/proc.h> 40681a5bbeSBoris Popov #include <sys/kernel.h> 41681a5bbeSBoris Popov #include <sys/kthread.h> 42681a5bbeSBoris Popov #include <sys/malloc.h> 43681a5bbeSBoris Popov #include <sys/mbuf.h> 44681a5bbeSBoris Popov #include <sys/unistd.h> 45681a5bbeSBoris Popov 46681a5bbeSBoris Popov #include <netsmb/smb.h> 47681a5bbeSBoris Popov #include <netsmb/smb_conn.h> 48681a5bbeSBoris Popov #include <netsmb/smb_rq.h> 49681a5bbeSBoris Popov #include <netsmb/smb_tran.h> 50681a5bbeSBoris Popov #include <netsmb/smb_trantcp.h> 51681a5bbeSBoris Popov 52681a5bbeSBoris Popov 53681a5bbeSBoris Popov #define SMBIOD_SLEEP_TIMO 2 54681a5bbeSBoris Popov #define SMBIOD_PING_TIMO 60 /* seconds */ 55681a5bbeSBoris Popov 56681a5bbeSBoris Popov #define SMB_IOD_EVLOCKPTR(iod) (&((iod)->iod_evlock)) 57681a5bbeSBoris Popov #define SMB_IOD_EVLOCK(iod) smb_sl_lock(&((iod)->iod_evlock)) 58681a5bbeSBoris Popov #define SMB_IOD_EVUNLOCK(iod) smb_sl_unlock(&((iod)->iod_evlock)) 59681a5bbeSBoris Popov 60681a5bbeSBoris Popov #define SMB_IOD_RQLOCKPTR(iod) (&((iod)->iod_rqlock)) 61681a5bbeSBoris Popov #define SMB_IOD_RQLOCK(iod) smb_sl_lock(&((iod)->iod_rqlock)) 62681a5bbeSBoris Popov #define SMB_IOD_RQUNLOCK(iod) smb_sl_unlock(&((iod)->iod_rqlock)) 63681a5bbeSBoris Popov 64681a5bbeSBoris Popov #define smb_iod_wakeup(iod) wakeup(&(iod)->iod_flags) 65681a5bbeSBoris Popov 66681a5bbeSBoris Popov 67681a5bbeSBoris Popov static MALLOC_DEFINE(M_SMBIOD, "SMBIOD", "SMB network io daemon"); 68681a5bbeSBoris Popov 69681a5bbeSBoris Popov static int smb_iod_next; 70681a5bbeSBoris Popov 71681a5bbeSBoris Popov static int smb_iod_sendall(struct smbiod *iod); 72681a5bbeSBoris Popov static int smb_iod_disconnect(struct smbiod *iod); 73681a5bbeSBoris Popov static void smb_iod_thread(void *); 74681a5bbeSBoris Popov 75681a5bbeSBoris Popov static __inline void 76681a5bbeSBoris Popov smb_iod_rqprocessed(struct smb_rq *rqp, int error) 77681a5bbeSBoris Popov { 78681a5bbeSBoris Popov SMBRQ_SLOCK(rqp); 79681a5bbeSBoris Popov rqp->sr_lerror = error; 80681a5bbeSBoris Popov rqp->sr_rpgen++; 81681a5bbeSBoris Popov rqp->sr_state = SMBRQ_NOTIFIED; 82681a5bbeSBoris Popov wakeup(&rqp->sr_state); 83681a5bbeSBoris Popov SMBRQ_SUNLOCK(rqp); 84681a5bbeSBoris Popov } 85681a5bbeSBoris Popov 86681a5bbeSBoris Popov static void 87681a5bbeSBoris Popov smb_iod_invrq(struct smbiod *iod) 88681a5bbeSBoris Popov { 89681a5bbeSBoris Popov struct smb_rq *rqp; 90681a5bbeSBoris Popov 91681a5bbeSBoris Popov /* 92681a5bbeSBoris Popov * Invalidate all outstanding requests for this connection 93681a5bbeSBoris Popov */ 94681a5bbeSBoris Popov SMB_IOD_RQLOCK(iod); 95681a5bbeSBoris Popov TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) { 96681a5bbeSBoris Popov if (rqp->sr_flags & SMBR_INTERNAL) 97681a5bbeSBoris Popov SMBRQ_SUNLOCK(rqp); 98681a5bbeSBoris Popov rqp->sr_flags |= SMBR_RESTART; 99681a5bbeSBoris Popov smb_iod_rqprocessed(rqp, ENOTCONN); 100681a5bbeSBoris Popov } 101681a5bbeSBoris Popov SMB_IOD_RQUNLOCK(iod); 102681a5bbeSBoris Popov } 103681a5bbeSBoris Popov 104681a5bbeSBoris Popov static void 105681a5bbeSBoris Popov smb_iod_closetran(struct smbiod *iod) 106681a5bbeSBoris Popov { 107681a5bbeSBoris Popov struct smb_vc *vcp = iod->iod_vc; 108fce6fbfaSBoris Popov struct thread *td = iod->iod_td; 109681a5bbeSBoris Popov 110681a5bbeSBoris Popov if (vcp->vc_tdata == NULL) 111681a5bbeSBoris Popov return; 112fce6fbfaSBoris Popov SMB_TRAN_DISCONNECT(vcp, td); 113fce6fbfaSBoris Popov SMB_TRAN_DONE(vcp, td); 114681a5bbeSBoris Popov vcp->vc_tdata = NULL; 115681a5bbeSBoris Popov } 116681a5bbeSBoris Popov 117681a5bbeSBoris Popov static void 118681a5bbeSBoris Popov smb_iod_dead(struct smbiod *iod) 119681a5bbeSBoris Popov { 120681a5bbeSBoris Popov iod->iod_state = SMBIOD_ST_DEAD; 121681a5bbeSBoris Popov smb_iod_closetran(iod); 122681a5bbeSBoris Popov smb_iod_invrq(iod); 123681a5bbeSBoris Popov } 124681a5bbeSBoris Popov 125681a5bbeSBoris Popov static int 126681a5bbeSBoris Popov smb_iod_connect(struct smbiod *iod) 127681a5bbeSBoris Popov { 128681a5bbeSBoris Popov struct smb_vc *vcp = iod->iod_vc; 129fce6fbfaSBoris Popov struct thread *td = iod->iod_td; 130681a5bbeSBoris Popov int error; 131681a5bbeSBoris Popov 132681a5bbeSBoris Popov SMBIODEBUG("%d\n", iod->iod_state); 133681a5bbeSBoris Popov switch(iod->iod_state) { 134681a5bbeSBoris Popov case SMBIOD_ST_VCACTIVE: 135681a5bbeSBoris Popov SMBERROR("called for already opened connection\n"); 136681a5bbeSBoris Popov return EISCONN; 137681a5bbeSBoris Popov case SMBIOD_ST_DEAD: 138681a5bbeSBoris Popov return ENOTCONN; /* XXX: last error code ? */ 139681a5bbeSBoris Popov default: 140681a5bbeSBoris Popov break; 141681a5bbeSBoris Popov } 142681a5bbeSBoris Popov vcp->vc_genid++; 143681a5bbeSBoris Popov error = 0; 144e51fe875SMarcel Moolenaar 145e51fe875SMarcel Moolenaar error = (int)SMB_TRAN_CREATE(vcp, td); 146e51fe875SMarcel Moolenaar if (error) 147e51fe875SMarcel Moolenaar goto fail; 148681a5bbeSBoris Popov SMBIODEBUG("tcreate\n"); 149681a5bbeSBoris Popov if (vcp->vc_laddr) { 150e51fe875SMarcel Moolenaar error = (int)SMB_TRAN_BIND(vcp, vcp->vc_laddr, td); 151e51fe875SMarcel Moolenaar if (error) 152e51fe875SMarcel Moolenaar goto fail; 153681a5bbeSBoris Popov } 154681a5bbeSBoris Popov SMBIODEBUG("tbind\n"); 155e51fe875SMarcel Moolenaar error = (int)SMB_TRAN_CONNECT(vcp, vcp->vc_paddr, td); 156e51fe875SMarcel Moolenaar if (error) 157e51fe875SMarcel Moolenaar goto fail; 158681a5bbeSBoris Popov SMB_TRAN_SETPARAM(vcp, SMBTP_SELECTID, &iod->iod_flags); 159681a5bbeSBoris Popov iod->iod_state = SMBIOD_ST_TRANACTIVE; 160681a5bbeSBoris Popov SMBIODEBUG("tconnect\n"); 161681a5bbeSBoris Popov /* vcp->vc_mid = 0;*/ 162e51fe875SMarcel Moolenaar error = (int)smb_smb_negotiate(vcp, &iod->iod_scred); 163e51fe875SMarcel Moolenaar if (error) 164e51fe875SMarcel Moolenaar goto fail; 165681a5bbeSBoris Popov SMBIODEBUG("snegotiate\n"); 166e51fe875SMarcel Moolenaar error = (int)smb_smb_ssnsetup(vcp, &iod->iod_scred); 167e51fe875SMarcel Moolenaar if (error) 168e51fe875SMarcel Moolenaar goto fail; 169681a5bbeSBoris Popov iod->iod_state = SMBIOD_ST_VCACTIVE; 170681a5bbeSBoris Popov SMBIODEBUG("completed\n"); 171681a5bbeSBoris Popov smb_iod_invrq(iod); 172e51fe875SMarcel Moolenaar return (0); 173e51fe875SMarcel Moolenaar 174e51fe875SMarcel Moolenaar fail: 175681a5bbeSBoris Popov smb_iod_dead(iod); 176e51fe875SMarcel Moolenaar return (error); 177681a5bbeSBoris Popov } 178681a5bbeSBoris Popov 179681a5bbeSBoris Popov static int 180681a5bbeSBoris Popov smb_iod_disconnect(struct smbiod *iod) 181681a5bbeSBoris Popov { 182681a5bbeSBoris Popov struct smb_vc *vcp = iod->iod_vc; 183681a5bbeSBoris Popov 184681a5bbeSBoris Popov SMBIODEBUG("\n"); 185681a5bbeSBoris Popov if (iod->iod_state == SMBIOD_ST_VCACTIVE) { 186681a5bbeSBoris Popov smb_smb_ssnclose(vcp, &iod->iod_scred); 187681a5bbeSBoris Popov iod->iod_state = SMBIOD_ST_TRANACTIVE; 188681a5bbeSBoris Popov } 189681a5bbeSBoris Popov vcp->vc_smbuid = SMB_UID_UNKNOWN; 190681a5bbeSBoris Popov smb_iod_closetran(iod); 191681a5bbeSBoris Popov iod->iod_state = SMBIOD_ST_NOTCONN; 192681a5bbeSBoris Popov return 0; 193681a5bbeSBoris Popov } 194681a5bbeSBoris Popov 195681a5bbeSBoris Popov static int 196681a5bbeSBoris Popov smb_iod_treeconnect(struct smbiod *iod, struct smb_share *ssp) 197681a5bbeSBoris Popov { 198681a5bbeSBoris Popov int error; 199681a5bbeSBoris Popov 200681a5bbeSBoris Popov if (iod->iod_state != SMBIOD_ST_VCACTIVE) { 201681a5bbeSBoris Popov if (iod->iod_state != SMBIOD_ST_DEAD) 202681a5bbeSBoris Popov return ENOTCONN; 203681a5bbeSBoris Popov iod->iod_state = SMBIOD_ST_RECONNECT; 204681a5bbeSBoris Popov error = smb_iod_connect(iod); 205681a5bbeSBoris Popov if (error) 206681a5bbeSBoris Popov return error; 207681a5bbeSBoris Popov } 208681a5bbeSBoris Popov SMBIODEBUG("tree reconnect\n"); 209681a5bbeSBoris Popov SMBS_ST_LOCK(ssp); 210681a5bbeSBoris Popov ssp->ss_flags |= SMBS_RECONNECTING; 211681a5bbeSBoris Popov SMBS_ST_UNLOCK(ssp); 212681a5bbeSBoris Popov error = smb_smb_treeconnect(ssp, &iod->iod_scred); 213681a5bbeSBoris Popov SMBS_ST_LOCK(ssp); 214681a5bbeSBoris Popov ssp->ss_flags &= ~SMBS_RECONNECTING; 215681a5bbeSBoris Popov SMBS_ST_UNLOCK(ssp); 216681a5bbeSBoris Popov wakeup(&ssp->ss_vcgenid); 217681a5bbeSBoris Popov return error; 218681a5bbeSBoris Popov } 219681a5bbeSBoris Popov 220681a5bbeSBoris Popov static int 221681a5bbeSBoris Popov smb_iod_sendrq(struct smbiod *iod, struct smb_rq *rqp) 222681a5bbeSBoris Popov { 223fce6fbfaSBoris Popov struct thread *td = iod->iod_td; 224681a5bbeSBoris Popov struct smb_vc *vcp = iod->iod_vc; 225681a5bbeSBoris Popov struct smb_share *ssp = rqp->sr_share; 226681a5bbeSBoris Popov struct mbuf *m; 227681a5bbeSBoris Popov int error; 228681a5bbeSBoris Popov 229681a5bbeSBoris Popov SMBIODEBUG("iod_state = %d\n", iod->iod_state); 230681a5bbeSBoris Popov switch (iod->iod_state) { 231681a5bbeSBoris Popov case SMBIOD_ST_NOTCONN: 232681a5bbeSBoris Popov smb_iod_rqprocessed(rqp, ENOTCONN); 233681a5bbeSBoris Popov return 0; 234681a5bbeSBoris Popov case SMBIOD_ST_DEAD: 235681a5bbeSBoris Popov iod->iod_state = SMBIOD_ST_RECONNECT; 236681a5bbeSBoris Popov return 0; 237681a5bbeSBoris Popov case SMBIOD_ST_RECONNECT: 238681a5bbeSBoris Popov return 0; 239681a5bbeSBoris Popov default: 240681a5bbeSBoris Popov break; 241681a5bbeSBoris Popov } 242681a5bbeSBoris Popov if (rqp->sr_sendcnt == 0) { 243681a5bbeSBoris Popov #ifdef movedtoanotherplace 244681a5bbeSBoris Popov if (vcp->vc_maxmux != 0 && iod->iod_muxcnt >= vcp->vc_maxmux) 245681a5bbeSBoris Popov return 0; 246681a5bbeSBoris Popov #endif 247a6a4232fSMarcel Moolenaar le16enc(rqp->sr_rqtid, ssp ? ssp->ss_tid : SMB_TID_UNKNOWN); 248a6a4232fSMarcel Moolenaar le16enc(rqp->sr_rquid, vcp ? vcp->vc_smbuid : 0); 249681a5bbeSBoris Popov mb_fixhdr(&rqp->sr_rq); 250190b2c4fSTim J. Robbins if (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) 251190b2c4fSTim J. Robbins smb_rq_sign(rqp); 252681a5bbeSBoris Popov } 253681a5bbeSBoris Popov if (rqp->sr_sendcnt++ > 5) { 254681a5bbeSBoris Popov rqp->sr_flags |= SMBR_RESTART; 255681a5bbeSBoris Popov smb_iod_rqprocessed(rqp, rqp->sr_lerror); 256681a5bbeSBoris Popov /* 257681a5bbeSBoris Popov * If all attempts to send a request failed, then 258681a5bbeSBoris Popov * something is seriously hosed. 259681a5bbeSBoris Popov */ 260681a5bbeSBoris Popov return ENOTCONN; 261681a5bbeSBoris Popov } 262681a5bbeSBoris Popov SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x\n", rqp->sr_mid, 0, 0, 0); 263681a5bbeSBoris Popov m_dumpm(rqp->sr_rq.mb_top); 264a163d034SWarner Losh m = m_copym(rqp->sr_rq.mb_top, 0, M_COPYALL, M_TRYWAIT); 265fce6fbfaSBoris Popov error = rqp->sr_lerror = m ? SMB_TRAN_SEND(vcp, m, td) : ENOBUFS; 266681a5bbeSBoris Popov if (error == 0) { 267681a5bbeSBoris Popov getnanotime(&rqp->sr_timesent); 268681a5bbeSBoris Popov iod->iod_lastrqsent = rqp->sr_timesent; 269681a5bbeSBoris Popov rqp->sr_flags |= SMBR_SENT; 270681a5bbeSBoris Popov rqp->sr_state = SMBRQ_SENT; 271681a5bbeSBoris Popov return 0; 272681a5bbeSBoris Popov } 273681a5bbeSBoris Popov /* 274681a5bbeSBoris Popov * Check for fatal errors 275681a5bbeSBoris Popov */ 276681a5bbeSBoris Popov if (SMB_TRAN_FATAL(vcp, error)) { 277681a5bbeSBoris Popov /* 278681a5bbeSBoris Popov * No further attempts should be made 279681a5bbeSBoris Popov */ 280681a5bbeSBoris Popov return ENOTCONN; 281681a5bbeSBoris Popov } 282681a5bbeSBoris Popov if (smb_rq_intr(rqp)) 283681a5bbeSBoris Popov smb_iod_rqprocessed(rqp, EINTR); 284681a5bbeSBoris Popov return 0; 285681a5bbeSBoris Popov } 286681a5bbeSBoris Popov 287681a5bbeSBoris Popov /* 288681a5bbeSBoris Popov * Process incoming packets 289681a5bbeSBoris Popov */ 290681a5bbeSBoris Popov static int 291681a5bbeSBoris Popov smb_iod_recvall(struct smbiod *iod) 292681a5bbeSBoris Popov { 293681a5bbeSBoris Popov struct smb_vc *vcp = iod->iod_vc; 294fce6fbfaSBoris Popov struct thread *td = iod->iod_td; 295681a5bbeSBoris Popov struct smb_rq *rqp; 296681a5bbeSBoris Popov struct mbuf *m; 297681a5bbeSBoris Popov u_char *hp; 298681a5bbeSBoris Popov u_short mid; 299681a5bbeSBoris Popov int error; 300681a5bbeSBoris Popov 301681a5bbeSBoris Popov switch (iod->iod_state) { 302681a5bbeSBoris Popov case SMBIOD_ST_NOTCONN: 303681a5bbeSBoris Popov case SMBIOD_ST_DEAD: 304681a5bbeSBoris Popov case SMBIOD_ST_RECONNECT: 305681a5bbeSBoris Popov return 0; 306681a5bbeSBoris Popov default: 307681a5bbeSBoris Popov break; 308681a5bbeSBoris Popov } 309681a5bbeSBoris Popov for (;;) { 310681a5bbeSBoris Popov m = NULL; 311fce6fbfaSBoris Popov error = SMB_TRAN_RECV(vcp, &m, td); 312681a5bbeSBoris Popov if (error == EWOULDBLOCK) 313681a5bbeSBoris Popov break; 314681a5bbeSBoris Popov if (SMB_TRAN_FATAL(vcp, error)) { 315681a5bbeSBoris Popov smb_iod_dead(iod); 316681a5bbeSBoris Popov break; 317681a5bbeSBoris Popov } 318681a5bbeSBoris Popov if (error) 319681a5bbeSBoris Popov break; 320681a5bbeSBoris Popov if (m == NULL) { 321681a5bbeSBoris Popov SMBERROR("tran return NULL without error\n"); 322681a5bbeSBoris Popov error = EPIPE; 323681a5bbeSBoris Popov continue; 324681a5bbeSBoris Popov } 325681a5bbeSBoris Popov m = m_pullup(m, SMB_HDRLEN); 326681a5bbeSBoris Popov if (m == NULL) 327681a5bbeSBoris Popov continue; /* wait for a good packet */ 328681a5bbeSBoris Popov /* 329681a5bbeSBoris Popov * Now we got an entire and possibly invalid SMB packet. 330681a5bbeSBoris Popov * Be careful while parsing it. 331681a5bbeSBoris Popov */ 332681a5bbeSBoris Popov m_dumpm(m); 333681a5bbeSBoris Popov hp = mtod(m, u_char*); 334681a5bbeSBoris Popov if (bcmp(hp, SMB_SIGNATURE, SMB_SIGLEN) != 0) { 335681a5bbeSBoris Popov m_freem(m); 336681a5bbeSBoris Popov continue; 337681a5bbeSBoris Popov } 338681a5bbeSBoris Popov mid = SMB_HDRMID(hp); 339681a5bbeSBoris Popov SMBSDEBUG("mid %04x\n", (u_int)mid); 340681a5bbeSBoris Popov SMB_IOD_RQLOCK(iod); 341681a5bbeSBoris Popov TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) { 342681a5bbeSBoris Popov if (rqp->sr_mid != mid) 343681a5bbeSBoris Popov continue; 344681a5bbeSBoris Popov SMBRQ_SLOCK(rqp); 345681a5bbeSBoris Popov if (rqp->sr_rp.md_top == NULL) { 346681a5bbeSBoris Popov md_initm(&rqp->sr_rp, m); 347681a5bbeSBoris Popov } else { 348681a5bbeSBoris Popov if (rqp->sr_flags & SMBR_MULTIPACKET) { 349681a5bbeSBoris Popov md_append_record(&rqp->sr_rp, m); 350681a5bbeSBoris Popov } else { 351681a5bbeSBoris Popov SMBRQ_SUNLOCK(rqp); 352681a5bbeSBoris Popov SMBERROR("duplicate response %d (ignored)\n", mid); 353681a5bbeSBoris Popov break; 354681a5bbeSBoris Popov } 355681a5bbeSBoris Popov } 356681a5bbeSBoris Popov SMBRQ_SUNLOCK(rqp); 357681a5bbeSBoris Popov smb_iod_rqprocessed(rqp, 0); 358681a5bbeSBoris Popov break; 359681a5bbeSBoris Popov } 360681a5bbeSBoris Popov SMB_IOD_RQUNLOCK(iod); 361681a5bbeSBoris Popov if (rqp == NULL) { 362681a5bbeSBoris Popov SMBERROR("drop resp with mid %d\n", (u_int)mid); 363681a5bbeSBoris Popov /* smb_printrqlist(vcp);*/ 364681a5bbeSBoris Popov m_freem(m); 365681a5bbeSBoris Popov } 366681a5bbeSBoris Popov } 367681a5bbeSBoris Popov /* 368681a5bbeSBoris Popov * check for interrupts 369681a5bbeSBoris Popov */ 370681a5bbeSBoris Popov SMB_IOD_RQLOCK(iod); 371681a5bbeSBoris Popov TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) { 3724093529dSJeff Roberson if (smb_td_intr(rqp->sr_cred->scr_td)) { 373681a5bbeSBoris Popov smb_iod_rqprocessed(rqp, EINTR); 374681a5bbeSBoris Popov } 375681a5bbeSBoris Popov } 376681a5bbeSBoris Popov SMB_IOD_RQUNLOCK(iod); 377681a5bbeSBoris Popov return 0; 378681a5bbeSBoris Popov } 379681a5bbeSBoris Popov 380681a5bbeSBoris Popov int 381681a5bbeSBoris Popov smb_iod_request(struct smbiod *iod, int event, void *ident) 382681a5bbeSBoris Popov { 383681a5bbeSBoris Popov struct smbiod_event *evp; 384681a5bbeSBoris Popov int error; 385681a5bbeSBoris Popov 386681a5bbeSBoris Popov SMBIODEBUG("\n"); 387a163d034SWarner Losh evp = smb_zmalloc(sizeof(*evp), M_SMBIOD, M_WAITOK); 388681a5bbeSBoris Popov evp->ev_type = event; 389681a5bbeSBoris Popov evp->ev_ident = ident; 390681a5bbeSBoris Popov SMB_IOD_EVLOCK(iod); 391681a5bbeSBoris Popov STAILQ_INSERT_TAIL(&iod->iod_evlist, evp, ev_link); 392681a5bbeSBoris Popov if ((event & SMBIOD_EV_SYNC) == 0) { 393681a5bbeSBoris Popov SMB_IOD_EVUNLOCK(iod); 394681a5bbeSBoris Popov smb_iod_wakeup(iod); 395681a5bbeSBoris Popov return 0; 396681a5bbeSBoris Popov } 397681a5bbeSBoris Popov smb_iod_wakeup(iod); 398681a5bbeSBoris Popov msleep(evp, SMB_IOD_EVLOCKPTR(iod), PWAIT | PDROP, "90evw", 0); 399681a5bbeSBoris Popov error = evp->ev_error; 400681a5bbeSBoris Popov free(evp, M_SMBIOD); 401681a5bbeSBoris Popov return error; 402681a5bbeSBoris Popov } 403681a5bbeSBoris Popov 404681a5bbeSBoris Popov /* 405681a5bbeSBoris Popov * Place request in the queue. 406681a5bbeSBoris Popov * Request from smbiod have a high priority. 407681a5bbeSBoris Popov */ 408681a5bbeSBoris Popov int 409681a5bbeSBoris Popov smb_iod_addrq(struct smb_rq *rqp) 410681a5bbeSBoris Popov { 411681a5bbeSBoris Popov struct smb_vc *vcp = rqp->sr_vc; 412681a5bbeSBoris Popov struct smbiod *iod = vcp->vc_iod; 413681a5bbeSBoris Popov int error; 414681a5bbeSBoris Popov 415681a5bbeSBoris Popov SMBIODEBUG("\n"); 41662ca80a7STim J. Robbins if (rqp->sr_cred->scr_td != NULL && 41762ca80a7STim J. Robbins rqp->sr_cred->scr_td->td_proc == iod->iod_p) { 418681a5bbeSBoris Popov rqp->sr_flags |= SMBR_INTERNAL; 419681a5bbeSBoris Popov SMB_IOD_RQLOCK(iod); 420681a5bbeSBoris Popov TAILQ_INSERT_HEAD(&iod->iod_rqlist, rqp, sr_link); 421681a5bbeSBoris Popov SMB_IOD_RQUNLOCK(iod); 422681a5bbeSBoris Popov for (;;) { 423681a5bbeSBoris Popov if (smb_iod_sendrq(iod, rqp) != 0) { 424681a5bbeSBoris Popov smb_iod_dead(iod); 425681a5bbeSBoris Popov break; 426681a5bbeSBoris Popov } 427681a5bbeSBoris Popov /* 428681a5bbeSBoris Popov * we don't need to lock state field here 429681a5bbeSBoris Popov */ 430681a5bbeSBoris Popov if (rqp->sr_state != SMBRQ_NOTSENT) 431681a5bbeSBoris Popov break; 432681a5bbeSBoris Popov tsleep(&iod->iod_flags, PWAIT, "90sndw", hz); 433681a5bbeSBoris Popov } 434681a5bbeSBoris Popov if (rqp->sr_lerror) 435681a5bbeSBoris Popov smb_iod_removerq(rqp); 436681a5bbeSBoris Popov return rqp->sr_lerror; 437681a5bbeSBoris Popov } 438681a5bbeSBoris Popov 439681a5bbeSBoris Popov switch (iod->iod_state) { 440681a5bbeSBoris Popov case SMBIOD_ST_NOTCONN: 441681a5bbeSBoris Popov return ENOTCONN; 442681a5bbeSBoris Popov case SMBIOD_ST_DEAD: 443681a5bbeSBoris Popov error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL); 444681a5bbeSBoris Popov if (error) 445681a5bbeSBoris Popov return error; 446681a5bbeSBoris Popov return EXDEV; 447681a5bbeSBoris Popov default: 448681a5bbeSBoris Popov break; 449681a5bbeSBoris Popov } 450681a5bbeSBoris Popov 451681a5bbeSBoris Popov SMB_IOD_RQLOCK(iod); 452681a5bbeSBoris Popov for (;;) { 453681a5bbeSBoris Popov if (vcp->vc_maxmux == 0) { 454681a5bbeSBoris Popov SMBERROR("maxmux == 0\n"); 455681a5bbeSBoris Popov break; 456681a5bbeSBoris Popov } 457681a5bbeSBoris Popov if (iod->iod_muxcnt < vcp->vc_maxmux) 458681a5bbeSBoris Popov break; 459681a5bbeSBoris Popov iod->iod_muxwant++; 460681a5bbeSBoris Popov msleep(&iod->iod_muxwant, SMB_IOD_RQLOCKPTR(iod), 461681a5bbeSBoris Popov PWAIT, "90mux", 0); 462681a5bbeSBoris Popov } 463681a5bbeSBoris Popov iod->iod_muxcnt++; 464681a5bbeSBoris Popov TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link); 465681a5bbeSBoris Popov SMB_IOD_RQUNLOCK(iod); 466681a5bbeSBoris Popov smb_iod_wakeup(iod); 467681a5bbeSBoris Popov return 0; 468681a5bbeSBoris Popov } 469681a5bbeSBoris Popov 470681a5bbeSBoris Popov int 471681a5bbeSBoris Popov smb_iod_removerq(struct smb_rq *rqp) 472681a5bbeSBoris Popov { 473681a5bbeSBoris Popov struct smb_vc *vcp = rqp->sr_vc; 474681a5bbeSBoris Popov struct smbiod *iod = vcp->vc_iod; 475681a5bbeSBoris Popov 476681a5bbeSBoris Popov SMBIODEBUG("\n"); 477681a5bbeSBoris Popov if (rqp->sr_flags & SMBR_INTERNAL) { 478681a5bbeSBoris Popov SMB_IOD_RQLOCK(iod); 479681a5bbeSBoris Popov TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link); 480681a5bbeSBoris Popov SMB_IOD_RQUNLOCK(iod); 481681a5bbeSBoris Popov return 0; 482681a5bbeSBoris Popov } 483681a5bbeSBoris Popov SMB_IOD_RQLOCK(iod); 484681a5bbeSBoris Popov while (rqp->sr_flags & SMBR_XLOCK) { 485681a5bbeSBoris Popov rqp->sr_flags |= SMBR_XLOCKWANT; 486681a5bbeSBoris Popov msleep(rqp, SMB_IOD_RQLOCKPTR(iod), PWAIT, "90xrm", 0); 487681a5bbeSBoris Popov } 488681a5bbeSBoris Popov TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link); 489681a5bbeSBoris Popov iod->iod_muxcnt--; 490681a5bbeSBoris Popov if (iod->iod_muxwant) { 491681a5bbeSBoris Popov iod->iod_muxwant--; 492681a5bbeSBoris Popov wakeup(&iod->iod_muxwant); 493681a5bbeSBoris Popov } 494681a5bbeSBoris Popov SMB_IOD_RQUNLOCK(iod); 495681a5bbeSBoris Popov return 0; 496681a5bbeSBoris Popov } 497681a5bbeSBoris Popov 498681a5bbeSBoris Popov int 499681a5bbeSBoris Popov smb_iod_waitrq(struct smb_rq *rqp) 500681a5bbeSBoris Popov { 501681a5bbeSBoris Popov struct smbiod *iod = rqp->sr_vc->vc_iod; 502681a5bbeSBoris Popov int error; 503681a5bbeSBoris Popov 504681a5bbeSBoris Popov SMBIODEBUG("\n"); 505681a5bbeSBoris Popov if (rqp->sr_flags & SMBR_INTERNAL) { 506681a5bbeSBoris Popov for (;;) { 507681a5bbeSBoris Popov smb_iod_sendall(iod); 508681a5bbeSBoris Popov smb_iod_recvall(iod); 509681a5bbeSBoris Popov if (rqp->sr_rpgen != rqp->sr_rplast) 510681a5bbeSBoris Popov break; 511681a5bbeSBoris Popov tsleep(&iod->iod_flags, PWAIT, "90irq", hz); 512681a5bbeSBoris Popov } 513681a5bbeSBoris Popov smb_iod_removerq(rqp); 514681a5bbeSBoris Popov return rqp->sr_lerror; 515681a5bbeSBoris Popov 516681a5bbeSBoris Popov } 517681a5bbeSBoris Popov SMBRQ_SLOCK(rqp); 518681a5bbeSBoris Popov if (rqp->sr_rpgen == rqp->sr_rplast) 519681a5bbeSBoris Popov msleep(&rqp->sr_state, SMBRQ_SLOCKPTR(rqp), PWAIT, "90wrq", 0); 520681a5bbeSBoris Popov rqp->sr_rplast++; 521681a5bbeSBoris Popov SMBRQ_SUNLOCK(rqp); 522681a5bbeSBoris Popov error = rqp->sr_lerror; 523681a5bbeSBoris Popov if (rqp->sr_flags & SMBR_MULTIPACKET) { 524681a5bbeSBoris Popov /* 525681a5bbeSBoris Popov * If request should stay in the list, then reinsert it 526681a5bbeSBoris Popov * at the end of queue so other waiters have chance to concur 527681a5bbeSBoris Popov */ 528681a5bbeSBoris Popov SMB_IOD_RQLOCK(iod); 529681a5bbeSBoris Popov TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link); 530681a5bbeSBoris Popov TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link); 531681a5bbeSBoris Popov SMB_IOD_RQUNLOCK(iod); 532681a5bbeSBoris Popov } else 533681a5bbeSBoris Popov smb_iod_removerq(rqp); 534681a5bbeSBoris Popov return error; 535681a5bbeSBoris Popov } 536681a5bbeSBoris Popov 537681a5bbeSBoris Popov 538681a5bbeSBoris Popov static int 539681a5bbeSBoris Popov smb_iod_sendall(struct smbiod *iod) 540681a5bbeSBoris Popov { 541681a5bbeSBoris Popov struct smb_vc *vcp = iod->iod_vc; 542681a5bbeSBoris Popov struct smb_rq *rqp; 543681a5bbeSBoris Popov struct timespec ts, tstimeout; 544681a5bbeSBoris Popov int herror; 545681a5bbeSBoris Popov 546681a5bbeSBoris Popov herror = 0; 547681a5bbeSBoris Popov /* 548681a5bbeSBoris Popov * Loop through the list of requests and send them if possible 549681a5bbeSBoris Popov */ 550681a5bbeSBoris Popov SMB_IOD_RQLOCK(iod); 551681a5bbeSBoris Popov TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) { 552681a5bbeSBoris Popov switch (rqp->sr_state) { 553681a5bbeSBoris Popov case SMBRQ_NOTSENT: 554681a5bbeSBoris Popov rqp->sr_flags |= SMBR_XLOCK; 555681a5bbeSBoris Popov SMB_IOD_RQUNLOCK(iod); 556681a5bbeSBoris Popov herror = smb_iod_sendrq(iod, rqp); 557681a5bbeSBoris Popov SMB_IOD_RQLOCK(iod); 558681a5bbeSBoris Popov rqp->sr_flags &= ~SMBR_XLOCK; 559681a5bbeSBoris Popov if (rqp->sr_flags & SMBR_XLOCKWANT) { 560681a5bbeSBoris Popov rqp->sr_flags &= ~SMBR_XLOCKWANT; 561681a5bbeSBoris Popov wakeup(rqp); 562681a5bbeSBoris Popov } 563681a5bbeSBoris Popov break; 564681a5bbeSBoris Popov case SMBRQ_SENT: 565681a5bbeSBoris Popov SMB_TRAN_GETPARAM(vcp, SMBTP_TIMEOUT, &tstimeout); 566681a5bbeSBoris Popov timespecadd(&tstimeout, &tstimeout); 567681a5bbeSBoris Popov getnanotime(&ts); 568681a5bbeSBoris Popov timespecsub(&ts, &tstimeout); 569681a5bbeSBoris Popov if (timespeccmp(&ts, &rqp->sr_timesent, >)) { 570681a5bbeSBoris Popov smb_iod_rqprocessed(rqp, ETIMEDOUT); 571681a5bbeSBoris Popov } 572681a5bbeSBoris Popov break; 573681a5bbeSBoris Popov default: 574dacd8bbbSPeter Wemm break; 575681a5bbeSBoris Popov } 576681a5bbeSBoris Popov if (herror) 577681a5bbeSBoris Popov break; 578681a5bbeSBoris Popov } 579681a5bbeSBoris Popov SMB_IOD_RQUNLOCK(iod); 580681a5bbeSBoris Popov if (herror == ENOTCONN) 581681a5bbeSBoris Popov smb_iod_dead(iod); 582681a5bbeSBoris Popov return 0; 583681a5bbeSBoris Popov } 584681a5bbeSBoris Popov 585681a5bbeSBoris Popov /* 586681a5bbeSBoris Popov * "main" function for smbiod daemon 587681a5bbeSBoris Popov */ 588681a5bbeSBoris Popov static __inline void 589681a5bbeSBoris Popov smb_iod_main(struct smbiod *iod) 590681a5bbeSBoris Popov { 591681a5bbeSBoris Popov /* struct smb_vc *vcp = iod->iod_vc;*/ 592681a5bbeSBoris Popov struct smbiod_event *evp; 593681a5bbeSBoris Popov /* struct timespec tsnow;*/ 594681a5bbeSBoris Popov int error; 595681a5bbeSBoris Popov 596681a5bbeSBoris Popov SMBIODEBUG("\n"); 597681a5bbeSBoris Popov error = 0; 598681a5bbeSBoris Popov 599681a5bbeSBoris Popov /* 600681a5bbeSBoris Popov * Check all interesting events 601681a5bbeSBoris Popov */ 602681a5bbeSBoris Popov for (;;) { 603681a5bbeSBoris Popov SMB_IOD_EVLOCK(iod); 604681a5bbeSBoris Popov evp = STAILQ_FIRST(&iod->iod_evlist); 605681a5bbeSBoris Popov if (evp == NULL) { 606681a5bbeSBoris Popov SMB_IOD_EVUNLOCK(iod); 607681a5bbeSBoris Popov break; 608681a5bbeSBoris Popov } 609681a5bbeSBoris Popov STAILQ_REMOVE_HEAD(&iod->iod_evlist, ev_link); 610681a5bbeSBoris Popov evp->ev_type |= SMBIOD_EV_PROCESSING; 611681a5bbeSBoris Popov SMB_IOD_EVUNLOCK(iod); 612681a5bbeSBoris Popov switch (evp->ev_type & SMBIOD_EV_MASK) { 613681a5bbeSBoris Popov case SMBIOD_EV_CONNECT: 614681a5bbeSBoris Popov iod->iod_state = SMBIOD_ST_RECONNECT; 615681a5bbeSBoris Popov evp->ev_error = smb_iod_connect(iod); 616681a5bbeSBoris Popov break; 617681a5bbeSBoris Popov case SMBIOD_EV_DISCONNECT: 618681a5bbeSBoris Popov evp->ev_error = smb_iod_disconnect(iod); 619681a5bbeSBoris Popov break; 620681a5bbeSBoris Popov case SMBIOD_EV_TREECONNECT: 621681a5bbeSBoris Popov evp->ev_error = smb_iod_treeconnect(iod, evp->ev_ident); 622681a5bbeSBoris Popov break; 623681a5bbeSBoris Popov case SMBIOD_EV_SHUTDOWN: 624681a5bbeSBoris Popov iod->iod_flags |= SMBIOD_SHUTDOWN; 625681a5bbeSBoris Popov break; 626681a5bbeSBoris Popov case SMBIOD_EV_NEWRQ: 627681a5bbeSBoris Popov break; 628681a5bbeSBoris Popov } 629681a5bbeSBoris Popov if (evp->ev_type & SMBIOD_EV_SYNC) { 630681a5bbeSBoris Popov SMB_IOD_EVLOCK(iod); 631681a5bbeSBoris Popov wakeup(evp); 632681a5bbeSBoris Popov SMB_IOD_EVUNLOCK(iod); 633681a5bbeSBoris Popov } else 634681a5bbeSBoris Popov free(evp, M_SMBIOD); 635681a5bbeSBoris Popov } 636681a5bbeSBoris Popov #if 0 637681a5bbeSBoris Popov if (iod->iod_state == SMBIOD_ST_VCACTIVE) { 638681a5bbeSBoris Popov getnanotime(&tsnow); 639681a5bbeSBoris Popov timespecsub(&tsnow, &iod->iod_pingtimo); 640681a5bbeSBoris Popov if (timespeccmp(&tsnow, &iod->iod_lastrqsent, >)) { 641681a5bbeSBoris Popov smb_smb_echo(vcp, &iod->iod_scred); 642681a5bbeSBoris Popov } 643681a5bbeSBoris Popov } 644681a5bbeSBoris Popov #endif 645681a5bbeSBoris Popov smb_iod_sendall(iod); 646681a5bbeSBoris Popov smb_iod_recvall(iod); 647681a5bbeSBoris Popov return; 648681a5bbeSBoris Popov } 649681a5bbeSBoris Popov 650681a5bbeSBoris Popov void 651681a5bbeSBoris Popov smb_iod_thread(void *arg) 652681a5bbeSBoris Popov { 653681a5bbeSBoris Popov struct smbiod *iod = arg; 654681a5bbeSBoris Popov 655681a5bbeSBoris Popov mtx_lock(&Giant); 656fce6fbfaSBoris Popov /* 657fce6fbfaSBoris Popov * Here we assume that the thread structure will be the same 658fce6fbfaSBoris Popov * for an entire kthread (kproc, to be more precise) life. 659fce6fbfaSBoris Popov */ 660fce6fbfaSBoris Popov iod->iod_td = curthread; 661fce6fbfaSBoris Popov smb_makescred(&iod->iod_scred, iod->iod_td, NULL); 662681a5bbeSBoris Popov while ((iod->iod_flags & SMBIOD_SHUTDOWN) == 0) { 663681a5bbeSBoris Popov smb_iod_main(iod); 664681a5bbeSBoris Popov SMBIODEBUG("going to sleep for %d ticks\n", iod->iod_sleeptimo); 665681a5bbeSBoris Popov /* mtx_unlock(&Giant, MTX_DEF);*/ 666681a5bbeSBoris Popov if (iod->iod_flags & SMBIOD_SHUTDOWN) 667681a5bbeSBoris Popov break; 668681a5bbeSBoris Popov tsleep(&iod->iod_flags, PWAIT, "90idle", iod->iod_sleeptimo); 669681a5bbeSBoris Popov } 670681a5bbeSBoris Popov /* mtx_lock(&Giant, MTX_DEF);*/ 6713745c395SJulian Elischer kproc_exit(0); 672681a5bbeSBoris Popov } 673681a5bbeSBoris Popov 674681a5bbeSBoris Popov int 675681a5bbeSBoris Popov smb_iod_create(struct smb_vc *vcp) 676681a5bbeSBoris Popov { 677681a5bbeSBoris Popov struct smbiod *iod; 678681a5bbeSBoris Popov int error; 679681a5bbeSBoris Popov 680a163d034SWarner Losh iod = smb_zmalloc(sizeof(*iod), M_SMBIOD, M_WAITOK); 681681a5bbeSBoris Popov iod->iod_id = smb_iod_next++; 682681a5bbeSBoris Popov iod->iod_state = SMBIOD_ST_NOTCONN; 683681a5bbeSBoris Popov iod->iod_vc = vcp; 684681a5bbeSBoris Popov iod->iod_sleeptimo = hz * SMBIOD_SLEEP_TIMO; 685681a5bbeSBoris Popov iod->iod_pingtimo.tv_sec = SMBIOD_PING_TIMO; 686681a5bbeSBoris Popov getnanotime(&iod->iod_lastrqsent); 687681a5bbeSBoris Popov vcp->vc_iod = iod; 688681a5bbeSBoris Popov smb_sl_init(&iod->iod_rqlock, "90rql"); 689681a5bbeSBoris Popov TAILQ_INIT(&iod->iod_rqlist); 690681a5bbeSBoris Popov smb_sl_init(&iod->iod_evlock, "90evl"); 691681a5bbeSBoris Popov STAILQ_INIT(&iod->iod_evlist); 6923745c395SJulian Elischer error = kproc_create(smb_iod_thread, iod, &iod->iod_p, 693316ec49aSScott Long RFNOWAIT, 0, "smbiod%d", iod->iod_id); 694681a5bbeSBoris Popov if (error) { 695681a5bbeSBoris Popov SMBERROR("can't start smbiod: %d", error); 696681a5bbeSBoris Popov free(iod, M_SMBIOD); 697681a5bbeSBoris Popov return error; 698681a5bbeSBoris Popov } 699681a5bbeSBoris Popov return 0; 700681a5bbeSBoris Popov } 701681a5bbeSBoris Popov 702681a5bbeSBoris Popov int 703681a5bbeSBoris Popov smb_iod_destroy(struct smbiod *iod) 704681a5bbeSBoris Popov { 705681a5bbeSBoris Popov smb_iod_request(iod, SMBIOD_EV_SHUTDOWN | SMBIOD_EV_SYNC, NULL); 706e7681448SBoris Popov smb_sl_destroy(&iod->iod_rqlock); 707e7681448SBoris Popov smb_sl_destroy(&iod->iod_evlock); 708681a5bbeSBoris Popov free(iod, M_SMBIOD); 709681a5bbeSBoris Popov return 0; 710681a5bbeSBoris Popov } 711681a5bbeSBoris Popov 712681a5bbeSBoris Popov int 713681a5bbeSBoris Popov smb_iod_init(void) 714681a5bbeSBoris Popov { 715681a5bbeSBoris Popov return 0; 716681a5bbeSBoris Popov } 717681a5bbeSBoris Popov 718681a5bbeSBoris Popov int 719681a5bbeSBoris Popov smb_iod_done(void) 720681a5bbeSBoris Popov { 721681a5bbeSBoris Popov return 0; 722681a5bbeSBoris Popov } 723681a5bbeSBoris Popov 724