1*68b2bbf2SGordon Ross /* 2*68b2bbf2SGordon Ross * This file and its contents are supplied under the terms of the 3*68b2bbf2SGordon Ross * Common Development and Distribution License ("CDDL"), version 1.0. 4*68b2bbf2SGordon Ross * You may only use this file in accordance with the terms of version 5*68b2bbf2SGordon Ross * 1.0 of the CDDL. 6*68b2bbf2SGordon Ross * 7*68b2bbf2SGordon Ross * A full copy of the text of the CDDL should have accompanied this 8*68b2bbf2SGordon Ross * source. A copy of the CDDL is also available via the Internet at 9*68b2bbf2SGordon Ross * http://www.illumos.org/license/CDDL. 10*68b2bbf2SGordon Ross */ 11*68b2bbf2SGordon Ross 12*68b2bbf2SGordon Ross /* 13*68b2bbf2SGordon Ross * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 14*68b2bbf2SGordon Ross */ 15*68b2bbf2SGordon Ross 16*68b2bbf2SGordon Ross /* 17*68b2bbf2SGordon Ross * This is the named pipe service for smbd. 18*68b2bbf2SGordon Ross */ 19*68b2bbf2SGordon Ross 20*68b2bbf2SGordon Ross #include <sys/types.h> 21*68b2bbf2SGordon Ross #include <sys/stat.h> 22*68b2bbf2SGordon Ross 23*68b2bbf2SGordon Ross #include <stdio.h> 24*68b2bbf2SGordon Ross #include <strings.h> 25*68b2bbf2SGordon Ross #include <stdlib.h> 26*68b2bbf2SGordon Ross #include <synch.h> 27*68b2bbf2SGordon Ross #include <unistd.h> 28*68b2bbf2SGordon Ross #include <fcntl.h> 29*68b2bbf2SGordon Ross #include <door.h> 30*68b2bbf2SGordon Ross #include <errno.h> 31*68b2bbf2SGordon Ross #include <pthread.h> 32*68b2bbf2SGordon Ross #include <signal.h> 33*68b2bbf2SGordon Ross 34*68b2bbf2SGordon Ross #include <smbsrv/libsmb.h> 35*68b2bbf2SGordon Ross #include <smbsrv/libmlsvc.h> 36*68b2bbf2SGordon Ross #include <smbsrv/smb_xdr.h> 37*68b2bbf2SGordon Ross #include "smbd.h" 38*68b2bbf2SGordon Ross 39*68b2bbf2SGordon Ross struct pipe_listener { 40*68b2bbf2SGordon Ross const char *name; 41*68b2bbf2SGordon Ross int max_allowed; 42*68b2bbf2SGordon Ross int max_seen; 43*68b2bbf2SGordon Ross int current; 44*68b2bbf2SGordon Ross pthread_t tid; 45*68b2bbf2SGordon Ross }; 46*68b2bbf2SGordon Ross 47*68b2bbf2SGordon Ross static void *pipesvc_listener(void *); 48*68b2bbf2SGordon Ross static void *pipesvc_worker(void *); 49*68b2bbf2SGordon Ross static int pipe_send(ndr_pipe_t *, void *, size_t); 50*68b2bbf2SGordon Ross static int pipe_recv(ndr_pipe_t *, void *, size_t); 51*68b2bbf2SGordon Ross 52*68b2bbf2SGordon Ross mutex_t pipesvc_mutex = DEFAULTMUTEX; 53*68b2bbf2SGordon Ross int pipesvc_workers_max = 500; 54*68b2bbf2SGordon Ross int pipesvc_workers_cur = 0; 55*68b2bbf2SGordon Ross 56*68b2bbf2SGordon Ross uint16_t pipe_max_msgsize = SMB_PIPE_MAX_MSGSIZE; 57*68b2bbf2SGordon Ross 58*68b2bbf2SGordon Ross /* 59*68b2bbf2SGordon Ross * Allow more opens on SRVSVC because that's used by many clients 60*68b2bbf2SGordon Ross * to get the share list, etc. 61*68b2bbf2SGordon Ross */ 62*68b2bbf2SGordon Ross #define SRVSVC_MAX_OPENS 200 63*68b2bbf2SGordon Ross #define DEF_MAX_OPENS 50 64*68b2bbf2SGordon Ross 65*68b2bbf2SGordon Ross #define NLISTENERS 11 66*68b2bbf2SGordon Ross static struct pipe_listener 67*68b2bbf2SGordon Ross pipe_listeners[NLISTENERS] = { 68*68b2bbf2SGordon Ross { "eventlog", DEF_MAX_OPENS, 0, 0 }, 69*68b2bbf2SGordon Ross { "lsarpc", DEF_MAX_OPENS, 0, 0 }, 70*68b2bbf2SGordon Ross { "lsass", DEF_MAX_OPENS, 0, 0 }, 71*68b2bbf2SGordon Ross { "netdfs", DEF_MAX_OPENS, 0, 0 }, 72*68b2bbf2SGordon Ross { "netlogon", DEF_MAX_OPENS, 0, 0 }, 73*68b2bbf2SGordon Ross { "samr", DEF_MAX_OPENS, 0, 0 }, 74*68b2bbf2SGordon Ross { "spoolss", DEF_MAX_OPENS, 0, 0 }, 75*68b2bbf2SGordon Ross { "srvsvc", SRVSVC_MAX_OPENS, 0, 0 }, 76*68b2bbf2SGordon Ross { "svcctl", DEF_MAX_OPENS, 0, 0 }, 77*68b2bbf2SGordon Ross { "winreg", DEF_MAX_OPENS, 0, 0 }, 78*68b2bbf2SGordon Ross { "wkssvc", DEF_MAX_OPENS, 0, 0 }, 79*68b2bbf2SGordon Ross }; 80*68b2bbf2SGordon Ross 81*68b2bbf2SGordon Ross static ndr_pipe_t * 82*68b2bbf2SGordon Ross np_new(struct pipe_listener *pl, int fid) 83*68b2bbf2SGordon Ross { 84*68b2bbf2SGordon Ross ndr_pipe_t *np; 85*68b2bbf2SGordon Ross size_t len; 86*68b2bbf2SGordon Ross 87*68b2bbf2SGordon Ross /* 88*68b2bbf2SGordon Ross * Allocating ndr_pipe_t + smb_netuserinfo_t as one. 89*68b2bbf2SGordon Ross * We could just make that part of ndr_pipe_t, but 90*68b2bbf2SGordon Ross * that struct is opaque to libmlrpc. 91*68b2bbf2SGordon Ross */ 92*68b2bbf2SGordon Ross len = sizeof (*np) + sizeof (smb_netuserinfo_t); 93*68b2bbf2SGordon Ross np = malloc(len); 94*68b2bbf2SGordon Ross if (np == NULL) 95*68b2bbf2SGordon Ross return (NULL); 96*68b2bbf2SGordon Ross 97*68b2bbf2SGordon Ross bzero(np, len); 98*68b2bbf2SGordon Ross np->np_listener = pl; 99*68b2bbf2SGordon Ross np->np_endpoint = pl->name; 100*68b2bbf2SGordon Ross np->np_user = (void*)(np + 1); 101*68b2bbf2SGordon Ross np->np_send = pipe_send; 102*68b2bbf2SGordon Ross np->np_recv = pipe_recv; 103*68b2bbf2SGordon Ross np->np_fid = fid; 104*68b2bbf2SGordon Ross np->np_max_xmit_frag = pipe_max_msgsize; 105*68b2bbf2SGordon Ross np->np_max_recv_frag = pipe_max_msgsize; 106*68b2bbf2SGordon Ross 107*68b2bbf2SGordon Ross return (np); 108*68b2bbf2SGordon Ross } 109*68b2bbf2SGordon Ross 110*68b2bbf2SGordon Ross static void 111*68b2bbf2SGordon Ross np_free(ndr_pipe_t *np) 112*68b2bbf2SGordon Ross { 113*68b2bbf2SGordon Ross (void) close(np->np_fid); 114*68b2bbf2SGordon Ross free(np); 115*68b2bbf2SGordon Ross } 116*68b2bbf2SGordon Ross 117*68b2bbf2SGordon Ross /* 118*68b2bbf2SGordon Ross * Create the smbd opipe door service. 119*68b2bbf2SGordon Ross * Returns the door descriptor on success. Otherwise returns -1. 120*68b2bbf2SGordon Ross */ 121*68b2bbf2SGordon Ross int 122*68b2bbf2SGordon Ross smbd_pipesvc_start(void) 123*68b2bbf2SGordon Ross { 124*68b2bbf2SGordon Ross pthread_t tid; 125*68b2bbf2SGordon Ross pthread_attr_t tattr; 126*68b2bbf2SGordon Ross struct pipe_listener *pl; 127*68b2bbf2SGordon Ross int i, rc; 128*68b2bbf2SGordon Ross 129*68b2bbf2SGordon Ross if (mlsvc_init() != 0) { 130*68b2bbf2SGordon Ross smbd_report("msrpc initialization failed"); 131*68b2bbf2SGordon Ross return (-1); 132*68b2bbf2SGordon Ross } 133*68b2bbf2SGordon Ross 134*68b2bbf2SGordon Ross (void) pthread_attr_init(&tattr); 135*68b2bbf2SGordon Ross (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); 136*68b2bbf2SGordon Ross 137*68b2bbf2SGordon Ross for (i = 0; i < NLISTENERS; i++) { 138*68b2bbf2SGordon Ross pl = &pipe_listeners[i]; 139*68b2bbf2SGordon Ross pl->max_seen = 0; 140*68b2bbf2SGordon Ross 141*68b2bbf2SGordon Ross if (strcasecmp(pl->name, "spoolss") == 0 && 142*68b2bbf2SGordon Ross smb_config_getbool(SMB_CI_PRINT_ENABLE) == B_FALSE) 143*68b2bbf2SGordon Ross continue; 144*68b2bbf2SGordon Ross 145*68b2bbf2SGordon Ross rc = pthread_create(&tid, &tattr, pipesvc_listener, pl); 146*68b2bbf2SGordon Ross if (rc != 0) 147*68b2bbf2SGordon Ross break; 148*68b2bbf2SGordon Ross pipe_listeners[i].tid = tid; 149*68b2bbf2SGordon Ross } 150*68b2bbf2SGordon Ross 151*68b2bbf2SGordon Ross if (rc != 0) { 152*68b2bbf2SGordon Ross smbd_report("pipesvc pthread_create, %d", rc); 153*68b2bbf2SGordon Ross } 154*68b2bbf2SGordon Ross 155*68b2bbf2SGordon Ross (void) pthread_attr_destroy(&tattr); 156*68b2bbf2SGordon Ross 157*68b2bbf2SGordon Ross return (rc); 158*68b2bbf2SGordon Ross } 159*68b2bbf2SGordon Ross 160*68b2bbf2SGordon Ross void 161*68b2bbf2SGordon Ross smbd_pipesvc_stop(void) 162*68b2bbf2SGordon Ross { 163*68b2bbf2SGordon Ross int i; 164*68b2bbf2SGordon Ross 165*68b2bbf2SGordon Ross (void) mutex_lock(&pipesvc_mutex); 166*68b2bbf2SGordon Ross for (i = 0; i < NLISTENERS; i++) { 167*68b2bbf2SGordon Ross if (pipe_listeners[i].tid == 0) 168*68b2bbf2SGordon Ross continue; 169*68b2bbf2SGordon Ross (void) pthread_kill(pipe_listeners[i].tid, SIGTERM); 170*68b2bbf2SGordon Ross pipe_listeners[i].tid = 0; 171*68b2bbf2SGordon Ross } 172*68b2bbf2SGordon Ross (void) mutex_unlock(&pipesvc_mutex); 173*68b2bbf2SGordon Ross } 174*68b2bbf2SGordon Ross 175*68b2bbf2SGordon Ross static void * 176*68b2bbf2SGordon Ross pipesvc_listener(void *varg) 177*68b2bbf2SGordon Ross { 178*68b2bbf2SGordon Ross struct sockaddr_un sa; 179*68b2bbf2SGordon Ross int err, listen_fd, newfd, snlen; 180*68b2bbf2SGordon Ross struct pipe_listener *pl = varg; 181*68b2bbf2SGordon Ross ndr_pipe_t *np; 182*68b2bbf2SGordon Ross pthread_t tid; 183*68b2bbf2SGordon Ross int rc; 184*68b2bbf2SGordon Ross 185*68b2bbf2SGordon Ross listen_fd = socket(AF_UNIX, SOCK_STREAM, 0); 186*68b2bbf2SGordon Ross if (listen_fd < 0) { 187*68b2bbf2SGordon Ross smbd_report("pipesvc_listener, so_create: %d", errno); 188*68b2bbf2SGordon Ross return (NULL); 189*68b2bbf2SGordon Ross } 190*68b2bbf2SGordon Ross 191*68b2bbf2SGordon Ross bzero(&sa, sizeof (sa)); 192*68b2bbf2SGordon Ross sa.sun_family = AF_UNIX; 193*68b2bbf2SGordon Ross (void) snprintf(sa.sun_path, sizeof (sa.sun_path), 194*68b2bbf2SGordon Ross "%s/%s", SMB_PIPE_DIR, pl->name); 195*68b2bbf2SGordon Ross 196*68b2bbf2SGordon Ross /* Bind it to a listening name. */ 197*68b2bbf2SGordon Ross (void) unlink(sa.sun_path); 198*68b2bbf2SGordon Ross if (bind(listen_fd, (struct sockaddr *)&sa, sizeof (sa)) < 0) { 199*68b2bbf2SGordon Ross smbd_report("pipesvc_listener, so_bind: %d", errno); 200*68b2bbf2SGordon Ross (void) close(listen_fd); 201*68b2bbf2SGordon Ross return (NULL); 202*68b2bbf2SGordon Ross } 203*68b2bbf2SGordon Ross 204*68b2bbf2SGordon Ross if (listen(listen_fd, SOMAXCONN) < 0) { 205*68b2bbf2SGordon Ross smbd_report("pipesvc_listener, listen: %d", errno); 206*68b2bbf2SGordon Ross (void) close(listen_fd); 207*68b2bbf2SGordon Ross return (NULL); 208*68b2bbf2SGordon Ross } 209*68b2bbf2SGordon Ross 210*68b2bbf2SGordon Ross for (;;) { 211*68b2bbf2SGordon Ross 212*68b2bbf2SGordon Ross snlen = sizeof (sa); 213*68b2bbf2SGordon Ross newfd = accept(listen_fd, (struct sockaddr *)&sa, &snlen); 214*68b2bbf2SGordon Ross if (newfd < 0) { 215*68b2bbf2SGordon Ross err = errno; 216*68b2bbf2SGordon Ross switch (err) { 217*68b2bbf2SGordon Ross case ECONNABORTED: 218*68b2bbf2SGordon Ross continue; 219*68b2bbf2SGordon Ross case EINTR: 220*68b2bbf2SGordon Ross /* normal termination */ 221*68b2bbf2SGordon Ross goto out; 222*68b2bbf2SGordon Ross default: 223*68b2bbf2SGordon Ross smbd_report("pipesvc_listener, " 224*68b2bbf2SGordon Ross "accept failed: %d", errno); 225*68b2bbf2SGordon Ross } 226*68b2bbf2SGordon Ross smbd_report("pipesvc_listener, accept: %d", err); 227*68b2bbf2SGordon Ross break; 228*68b2bbf2SGordon Ross } 229*68b2bbf2SGordon Ross 230*68b2bbf2SGordon Ross np = np_new(pl, newfd); 231*68b2bbf2SGordon Ross if (np == NULL) { 232*68b2bbf2SGordon Ross smbd_report("pipesvc_listener, alloc1 failed"); 233*68b2bbf2SGordon Ross (void) close(newfd); 234*68b2bbf2SGordon Ross continue; 235*68b2bbf2SGordon Ross } 236*68b2bbf2SGordon Ross 237*68b2bbf2SGordon Ross rc = pthread_create(&tid, NULL, pipesvc_worker, np); 238*68b2bbf2SGordon Ross if (rc != 0) { 239*68b2bbf2SGordon Ross smbd_report("pipesvc_listener, pthread_create: %d", 240*68b2bbf2SGordon Ross errno); 241*68b2bbf2SGordon Ross np_free(np); 242*68b2bbf2SGordon Ross continue; 243*68b2bbf2SGordon Ross } 244*68b2bbf2SGordon Ross (void) pthread_detach(tid); 245*68b2bbf2SGordon Ross 246*68b2bbf2SGordon Ross /* Note: np_free in pipesvc_worker */ 247*68b2bbf2SGordon Ross np = NULL; 248*68b2bbf2SGordon Ross } 249*68b2bbf2SGordon Ross 250*68b2bbf2SGordon Ross out: 251*68b2bbf2SGordon Ross (void) close(listen_fd); 252*68b2bbf2SGordon Ross pl->tid = 0; 253*68b2bbf2SGordon Ross return (NULL); 254*68b2bbf2SGordon Ross } 255*68b2bbf2SGordon Ross 256*68b2bbf2SGordon Ross static void * 257*68b2bbf2SGordon Ross pipesvc_worker(void *varg) 258*68b2bbf2SGordon Ross { 259*68b2bbf2SGordon Ross XDR xdrs; 260*68b2bbf2SGordon Ross smb_pipehdr_t phdr; 261*68b2bbf2SGordon Ross ndr_pipe_t *np = varg; 262*68b2bbf2SGordon Ross struct pipe_listener *pl = np->np_listener; 263*68b2bbf2SGordon Ross void *buf = NULL; 264*68b2bbf2SGordon Ross uint32_t status; 265*68b2bbf2SGordon Ross ssize_t rc; 266*68b2bbf2SGordon Ross 267*68b2bbf2SGordon Ross (void) mutex_lock(&pipesvc_mutex); 268*68b2bbf2SGordon Ross if (pipesvc_workers_cur >= pipesvc_workers_max || 269*68b2bbf2SGordon Ross pl->current >= pl->max_allowed) { 270*68b2bbf2SGordon Ross (void) mutex_unlock(&pipesvc_mutex); 271*68b2bbf2SGordon Ross status = NT_STATUS_PIPE_NOT_AVAILABLE; 272*68b2bbf2SGordon Ross (void) send(np->np_fid, &status, sizeof (status), 0); 273*68b2bbf2SGordon Ross goto out_free_np; 274*68b2bbf2SGordon Ross } 275*68b2bbf2SGordon Ross pipesvc_workers_cur++; 276*68b2bbf2SGordon Ross pl->current++; 277*68b2bbf2SGordon Ross if (pl->max_seen < pl->current) 278*68b2bbf2SGordon Ross pl->max_seen = pl->current; 279*68b2bbf2SGordon Ross (void) mutex_unlock(&pipesvc_mutex); 280*68b2bbf2SGordon Ross 281*68b2bbf2SGordon Ross /* 282*68b2bbf2SGordon Ross * The smbsrv kmod sends us one initial message containing an 283*68b2bbf2SGordon Ross * XDR encoded smb_netuserinfo_t that we read and decode here, 284*68b2bbf2SGordon Ross * all unbeknownst to libmlrpc. 285*68b2bbf2SGordon Ross * 286*68b2bbf2SGordon Ross * Might be nice to enhance getpeerucred() so it can give us 287*68b2bbf2SGordon Ross * all the info smb_netuserinfo_t carries, and then use that, 288*68b2bbf2SGordon Ross * which would allow using a more generic RPC service. 289*68b2bbf2SGordon Ross */ 290*68b2bbf2SGordon Ross rc = pipe_recv(np, &phdr, sizeof (phdr)); 291*68b2bbf2SGordon Ross if (rc != 0) { 292*68b2bbf2SGordon Ross smbd_report("pipesvc_worker, recv1: %d", rc); 293*68b2bbf2SGordon Ross goto out_decr; 294*68b2bbf2SGordon Ross } 295*68b2bbf2SGordon Ross if (phdr.ph_magic != SMB_PIPE_HDR_MAGIC || 296*68b2bbf2SGordon Ross phdr.ph_uilen > 8192) { 297*68b2bbf2SGordon Ross smbd_report("pipesvc_worker, bad hdr"); 298*68b2bbf2SGordon Ross goto out_decr; 299*68b2bbf2SGordon Ross } 300*68b2bbf2SGordon Ross buf = malloc(phdr.ph_uilen); 301*68b2bbf2SGordon Ross if (buf == NULL) { 302*68b2bbf2SGordon Ross smbd_report("pipesvc_worker, alloc1 failed"); 303*68b2bbf2SGordon Ross goto out_decr; 304*68b2bbf2SGordon Ross } 305*68b2bbf2SGordon Ross rc = pipe_recv(np, buf, phdr.ph_uilen); 306*68b2bbf2SGordon Ross if (rc != 0) { 307*68b2bbf2SGordon Ross smbd_report("pipesvc_worker, recv2: %d", rc); 308*68b2bbf2SGordon Ross goto out_decr; 309*68b2bbf2SGordon Ross } 310*68b2bbf2SGordon Ross 311*68b2bbf2SGordon Ross xdrmem_create(&xdrs, buf, phdr.ph_uilen, XDR_DECODE); 312*68b2bbf2SGordon Ross if (!smb_netuserinfo_xdr(&xdrs, np->np_user)) { 313*68b2bbf2SGordon Ross smbd_report("pipesvc_worker, bad uinfo"); 314*68b2bbf2SGordon Ross goto out_free_buf; 315*68b2bbf2SGordon Ross } 316*68b2bbf2SGordon Ross 317*68b2bbf2SGordon Ross /* 318*68b2bbf2SGordon Ross * Later, could disallow opens of some pipes by 319*68b2bbf2SGordon Ross * anonymous users, etc. For now, reply "OK". 320*68b2bbf2SGordon Ross */ 321*68b2bbf2SGordon Ross status = 0; 322*68b2bbf2SGordon Ross rc = pipe_send(np, &status, sizeof (status)); 323*68b2bbf2SGordon Ross if (rc != 0) { 324*68b2bbf2SGordon Ross smbd_report("pipesvc_worker, send1: %d", rc); 325*68b2bbf2SGordon Ross goto out_free_buf; 326*68b2bbf2SGordon Ross } 327*68b2bbf2SGordon Ross 328*68b2bbf2SGordon Ross /* 329*68b2bbf2SGordon Ross * Run the RPC service loop worker, which 330*68b2bbf2SGordon Ross * returns when it sees the pipe close. 331*68b2bbf2SGordon Ross */ 332*68b2bbf2SGordon Ross ndr_pipe_worker(np); 333*68b2bbf2SGordon Ross 334*68b2bbf2SGordon Ross xdrs.x_op = XDR_FREE; 335*68b2bbf2SGordon Ross (void) smb_netuserinfo_xdr(&xdrs, np->np_user); 336*68b2bbf2SGordon Ross 337*68b2bbf2SGordon Ross out_free_buf: 338*68b2bbf2SGordon Ross free(buf); 339*68b2bbf2SGordon Ross xdr_destroy(&xdrs); 340*68b2bbf2SGordon Ross 341*68b2bbf2SGordon Ross out_decr: 342*68b2bbf2SGordon Ross (void) mutex_lock(&pipesvc_mutex); 343*68b2bbf2SGordon Ross pipesvc_workers_cur--; 344*68b2bbf2SGordon Ross pl->current--; 345*68b2bbf2SGordon Ross (void) mutex_unlock(&pipesvc_mutex); 346*68b2bbf2SGordon Ross 347*68b2bbf2SGordon Ross out_free_np: 348*68b2bbf2SGordon Ross /* Cleanup what came in by varg. */ 349*68b2bbf2SGordon Ross (void) shutdown(np->np_fid, SHUT_RDWR); 350*68b2bbf2SGordon Ross np_free(np); 351*68b2bbf2SGordon Ross return (NULL); 352*68b2bbf2SGordon Ross } 353*68b2bbf2SGordon Ross 354*68b2bbf2SGordon Ross /* 355*68b2bbf2SGordon Ross * These are the transport get/put callback functions provided 356*68b2bbf2SGordon Ross * via the ndr_pipe_t object to the libmlrpc`ndr_pipe_worker. 357*68b2bbf2SGordon Ross * These are called only with known PDU sizes and should 358*68b2bbf2SGordon Ross * loop as needed to transfer the entire message. 359*68b2bbf2SGordon Ross */ 360*68b2bbf2SGordon Ross static int 361*68b2bbf2SGordon Ross pipe_recv(ndr_pipe_t *np, void *buf, size_t len) 362*68b2bbf2SGordon Ross { 363*68b2bbf2SGordon Ross int x; 364*68b2bbf2SGordon Ross 365*68b2bbf2SGordon Ross while (len > 0) { 366*68b2bbf2SGordon Ross x = recv(np->np_fid, buf, len, 0); 367*68b2bbf2SGordon Ross if (x < 0) 368*68b2bbf2SGordon Ross return (errno); 369*68b2bbf2SGordon Ross if (x == 0) 370*68b2bbf2SGordon Ross return (EIO); 371*68b2bbf2SGordon Ross buf = (char *)buf + x; 372*68b2bbf2SGordon Ross len -= x; 373*68b2bbf2SGordon Ross } 374*68b2bbf2SGordon Ross 375*68b2bbf2SGordon Ross return (0); 376*68b2bbf2SGordon Ross } 377*68b2bbf2SGordon Ross 378*68b2bbf2SGordon Ross static int 379*68b2bbf2SGordon Ross pipe_send(ndr_pipe_t *np, void *buf, size_t len) 380*68b2bbf2SGordon Ross { 381*68b2bbf2SGordon Ross int x; 382*68b2bbf2SGordon Ross 383*68b2bbf2SGordon Ross while (len > 0) { 384*68b2bbf2SGordon Ross x = send(np->np_fid, buf, len, 0); 385*68b2bbf2SGordon Ross if (x < 0) 386*68b2bbf2SGordon Ross return (errno); 387*68b2bbf2SGordon Ross if (x == 0) 388*68b2bbf2SGordon Ross return (EIO); 389*68b2bbf2SGordon Ross buf = (char *)buf + x; 390*68b2bbf2SGordon Ross len -= x; 391*68b2bbf2SGordon Ross } 392*68b2bbf2SGordon Ross 393*68b2bbf2SGordon Ross return (0); 394*68b2bbf2SGordon Ross } 395