FILE * fdopen(int fd, const char *mode) { FILE *fp; int flags, oflags, fdflags, tmp; /* * File descriptors are a full int, but _file is only a short. * If we get a valid file descriptor that is greater than * SHRT_MAX, then the fd will get sign-extended into an * invalid file descriptor. Handle this case by failing the * open. */ if (fd > SHRT_MAX) { errno = EMFILE; return (NULL); } if ((flags = __sflags(mode, &oflags)) == 0) return (NULL); /* Make sure the mode the user wants is a subset of the actual mode. */ if ((fdflags = _fcntl(fd, F_GETFL, 0)) < 0) return (NULL); /* Work around incorrect O_ACCMODE. */ tmp = fdflags & (O_ACCMODE | O_EXEC); if (tmp != O_RDWR && (tmp != (oflags & O_ACCMODE))) { errno = EINVAL; return (NULL); } if ((fp = __sfp()) == NULL) return (NULL); if ((oflags & O_CLOEXEC) && _fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { fp->_flags = 0; return (NULL); } fp->_flags = flags; /* * If opened for appending, but underlying descriptor does not have * O_APPEND bit set, assert __SAPP so that __swrite() caller * will _sseek() to the end before write. */ /* XXX: Reuse __SALC for O_APPEND. */ if (fdflags & O_APPEND) fp->_flags |= __SALC; else if (oflags & O_APPEND) fp->_flags |= __SAPP; fp->_file = fd; fp->_cookie = fp; fp->_read = __sread; fp->_write = __swrite; fp->_seek = __sseek; fp->_close = __sclose; return (fp); }
/* * Open a directory with existing file descriptor. */ DIR * fdopendir(int fd) { if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) return (NULL); return (__opendir_common(fd, DTF_HIDEW|DTF_NODUP, true)); }
short _isatty(int fd) { register short lub; lub = (int) _fcntl(&stdin[fd], 5, (size_t) 0); return (lub >= CONIN && lub <= CONOUT) || (lub >= COM1 && lub <= COM4) || (lub >= COM5 && lub <= COM16); }
static int register_to_daemon(void) { int flags; char test = 't'; daemon_fd = connect_to_daemon(LISTEN_SOCKET_FILE); flags = _fcntl(daemon_fd, F_GETFD, 0); if (_fcntl(daemon_fd, F_SETFD, flags & FD_CLOEXEC) == -1) { sysv_print_err("fcntl error\n"); return (-1); } /* Send a message such that daemon can obtain process credentials.*/ send_msg_with_cred(daemon_fd, &test, sizeof(test)); sysv_print("register to daemon: sock fd = %d\n", daemon_fd); return (0); }
int lockf(int filedes, int function, off_t size) { struct flock fl; int cmd; fl.l_start = 0; fl.l_len = size; fl.l_whence = SEEK_CUR; switch (function) { case F_ULOCK: cmd = F_SETLK; fl.l_type = F_UNLCK; break; case F_LOCK: cmd = F_SETLKW; fl.l_type = F_WRLCK; break; case F_TLOCK: cmd = F_SETLK; fl.l_type = F_WRLCK; break; case F_TEST: fl.l_type = F_WRLCK; if (_fcntl(filedes, F_GETLK, &fl) == -1) return (-1); if (fl.l_type == F_UNLCK || (fl.l_sysid == 0 && fl.l_pid == getpid())) return (0); errno = EAGAIN; return (-1); /* NOTREACHED */ default: errno = EINVAL; return (-1); /* NOTREACHED */ } return (_fcntl(filedes, cmd, &fl)); }
FILE *_fdopen_r(struct _reent *ptr, int fd, const char *mode) { register FILE *fp; int flags, oflags; #ifdef F_GETFL int fdflags, fdmode; #endif if ((flags = __sflags(ptr, mode, &oflags)) == 0) return 0; /* make sure the mode the user wants is a subset of the actual mode */ #ifdef F_GETFL if ((fdflags = _fcntl(fd, F_GETFL, 0)) < 0) return 0; fdmode = fdflags & O_ACCMODE; if (fdmode != O_RDWR && (fdmode != (oflags & O_ACCMODE))) { ptr->_errno = EBADF; return 0; } #endif if ((fp = __sfp(ptr)) == 0) return 0; fp->_flags = flags; /* * If opened for appending, but underlying descriptor * does not have O_APPEND bit set, assert __SAPP so that * __swrite() will lseek to end before each write. */ if ((oflags & O_APPEND) #ifdef F_GETFL && !(fdflags & O_APPEND) #endif ) fp->_flags |= __SAPP; fp->_file = fd; fp->_cookie = (void *)fp; #undef _read #undef _write #undef _seek #undef _close fp->_read = __sread; fp->_write = __swrite; fp->_seek = __sseek; fp->_close = __sclose; return fp; }
/* Should be called with mutex acquired */ static void connectlog(void) { struct sockaddr_un SyslogAddr; /* AF_UNIX address of local logger */ if (LogFile == -1) { if ((LogFile = _socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) return; (void)_fcntl(LogFile, F_SETFD, 1); } if (LogFile != -1 && status == NOCONN) { SyslogAddr.sun_len = sizeof(SyslogAddr); SyslogAddr.sun_family = AF_UNIX; /* * First try priveleged socket. If no success, * then try default socket. */ (void)strncpy(SyslogAddr.sun_path, _PATH_LOG_PRIV, sizeof SyslogAddr.sun_path); if (_connect(LogFile, (struct sockaddr *)&SyslogAddr, sizeof(SyslogAddr)) != -1) status = CONNPRIV; if (status == NOCONN) { (void)strncpy(SyslogAddr.sun_path, _PATH_LOG, sizeof SyslogAddr.sun_path); if (_connect(LogFile, (struct sockaddr *)&SyslogAddr, sizeof(SyslogAddr)) != -1) status = CONNDEF; } if (status == NOCONN) { /* * Try the old "/dev/log" path, for backward * compatibility. */ (void)strncpy(SyslogAddr.sun_path, _PATH_OLDLOG, sizeof SyslogAddr.sun_path); if (_connect(LogFile, (struct sockaddr *)&SyslogAddr, sizeof(SyslogAddr)) != -1) status = CONNDEF; } if (status == NOCONN) { (void)_close(LogFile); LogFile = -1; } } }
/* * Open a directory with existing file descriptor. */ DIR * fdopendir(int fd) { struct stat statb; /* Check that fd is associated with a directory. */ if (_fstat(fd, &statb) != 0) return (NULL); if (!S_ISDIR(statb.st_mode)) { errno = ENOTDIR; return (NULL); } if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) return (NULL); return (__opendir_common(fd, NULL, DTF_HIDEW|DTF_NODUP)); }
/* * Opens the connection with the specified params. Initializes all kqueues. */ struct cached_connection_ * __open_cached_connection(struct cached_connection_params const *params) { struct cached_connection_ *retval; struct kevent eventlist; struct sockaddr_un client_address; int client_address_len, client_socket; int res; assert(params != NULL); client_socket = _socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); client_address.sun_family = PF_LOCAL; strncpy(client_address.sun_path, params->socket_path, sizeof(client_address.sun_path)); client_address_len = sizeof(client_address.sun_family) + strlen(client_address.sun_path) + 1; res = _connect(client_socket, (struct sockaddr *)&client_address, client_address_len); if (res == -1) { _close(client_socket); return (NULL); } _fcntl(client_socket, F_SETFL, O_NONBLOCK); retval = malloc(sizeof(struct cached_connection_)); assert(retval != NULL); memset(retval, 0, sizeof(struct cached_connection_)); retval->sockfd = client_socket; retval->write_queue = kqueue(); assert(retval->write_queue != -1); EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); res = _kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL); retval->read_queue = kqueue(); assert(retval->read_queue != -1); EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL); res = _kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL); return (retval); }
int __dup3(int oldfd, int newfd, int flags) { int how; if (oldfd == newfd) { errno = EINVAL; return (-1); } if (flags & ~O_CLOEXEC) { errno = EINVAL; return (-1); } how = (flags & O_CLOEXEC) ? F_DUP2FD_CLOEXEC : F_DUP2FD; return (_fcntl(oldfd, how, newfd)); }
int __rpc_raise_fd(int fd) { int nfd; if (fd >= __rpc_minfd) return (fd); if ((nfd = _fcntl(fd, F_DUPFD, __rpc_minfd)) == -1) return (fd); if (_fsync(nfd) == -1) { _close(nfd); return (fd); } if (_close(fd) == -1) { /* this is okay, we will syslog an error, then use the new fd */ syslog(LOG_ERR, "could not close() fd %d; mem & fd leak", fd); } return (nfd); }
FILE * fdopen(int fd, const char *mode) { FILE *fp; int flags, oflags, fdflags, tmp; if ((flags = __sflags(mode, &oflags)) == 0) return (NULL); /* Make sure the mode the user wants is a subset of the actual mode. */ if ((fdflags = _fcntl(fd, F_GETFL, 0)) < 0) return (NULL); tmp = fdflags & O_ACCMODE; if (tmp != O_RDWR && (tmp != (oflags & O_ACCMODE))) { errno = EINVAL; return (NULL); } if ((fp = __sfp()) == NULL) return (NULL); fp->pub._flags = flags; /* * If opened for appending, but underlying descriptor does not have * O_APPEND bit set, assert __SAPP so that __swrite() caller * will _sseek() to the end before write. */ if ((oflags & O_APPEND) && !(fdflags & O_APPEND)) fp->pub._flags |= __SAPP; fp->pub._fileno = fd; fp->_cookie = fp; fp->_read = __sread; fp->_write = __swrite; fp->_seek = __sseek; fp->_close = __sclose; return (fp); }
int shm_open(const char *path, int flags, mode_t mode) { int fd; struct stat stab; if ((flags & O_ACCMODE) == O_WRONLY) return (EINVAL); fd = _open(path, flags, mode); if (fd != -1) { if (_fstat(fd, &stab) != 0 || !S_ISREG(stab.st_mode)) { _close(fd); errno = EINVAL; return (-1); } if (_fcntl(fd, F_SETFL, (int)FPOSIXSHM) != 0) { _close(fd); return (-1); } } return (fd); }
/* * This is the simplified interface to the client rpc layer. * The client handle is not destroyed here and is reused for * the future calls to same prog, vers, host and nettype combination. * * The total time available is 25 seconds. */ enum clnt_stat rpc_call(const char *host, /* host name */ rpcprog_t prognum, /* program number */ rpcvers_t versnum, /* version number */ rpcproc_t procnum, /* procedure number */ xdrproc_t inproc, const char *in, xdrproc_t outproc, /* in/out XDR procedures */ char *out, /* recv/send data */ const char *nettype) /* nettype */ { struct rpc_call_private *rcp = NULL; enum clnt_stat clnt_stat; struct timeval timeout, tottimeout; static thread_key_t rpc_call_key; int main_thread = 1; if ((main_thread = thr_main())) { rcp = rpc_call_private_main; } else { if (rpc_call_key == 0) { mutex_lock(&tsd_lock); if (rpc_call_key == 0) thr_keycreate(&rpc_call_key, rpc_call_destroy); mutex_unlock(&tsd_lock); } rcp = (struct rpc_call_private *)thr_getspecific(rpc_call_key); } if (rcp == NULL) { rcp = malloc(sizeof (*rcp)); if (rcp == NULL) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; return (rpc_createerr.cf_stat); } if (main_thread) rpc_call_private_main = rcp; else thr_setspecific(rpc_call_key, (void *) rcp); rcp->valid = 0; rcp->client = NULL; } if ((nettype == NULL) || (nettype[0] == 0)) nettype = "netpath"; if (!(rcp->valid && rcp->pid == getpid() && (rcp->prognum == prognum) && (rcp->versnum == versnum) && (!strcmp(rcp->host, host)) && (!strcmp(rcp->nettype, nettype)))) { int fd; rcp->valid = 0; if (rcp->client) CLNT_DESTROY(rcp->client); /* * Using the first successful transport for that type */ rcp->client = clnt_create(host, prognum, versnum, nettype); rcp->pid = getpid(); if (rcp->client == NULL) { return (rpc_createerr.cf_stat); } /* * Set time outs for connectionless case. Do it * unconditionally. Faster than doing a t_getinfo() * and then doing the right thing. */ timeout.tv_usec = 0; timeout.tv_sec = 5; CLNT_CONTROL(rcp->client, CLSET_RETRY_TIMEOUT, (char *)(void *)&timeout); if (CLNT_CONTROL(rcp->client, CLGET_FD, (char *)(void *)&fd)) _fcntl(fd, F_SETFD, 1); /* make it "close on exec" */ rcp->prognum = prognum; rcp->versnum = versnum; if ((strlen(host) < (size_t)MAXHOSTNAMELEN) && (strlen(nettype) < (size_t)NETIDLEN)) { strcpy(rcp->host, host); strcpy(rcp->nettype, nettype); rcp->valid = 1; } else { rcp->valid = 0; } } /* else reuse old client */ tottimeout.tv_sec = 25; tottimeout.tv_usec = 0; /*LINTED const castaway*/ clnt_stat = CLNT_CALL(rcp->client, procnum, inproc, (char *) in, outproc, out, tottimeout); /* * if call failed, empty cache */ if (clnt_stat != RPC_SUCCESS) rcp->valid = 0; return (clnt_stat); }
int rcmd_af(char **ahost, int rport, const char *locuser, const char *remuser, const char *cmd, int *fd2p, int af) { struct addrinfo hints, *res, *ai; struct sockaddr_storage from; fd_set reads; sigset_t oldmask, newmask; pid_t pid; int s, aport, lport, timo, error; char c, *p; int refused, nres; char num[8]; static char canonnamebuf[MAXDNAME]; /* is it proper here? */ /* call rcmdsh() with specified remote shell if appropriate. */ if (!issetugid() && (p = getenv("RSH"))) { struct servent *sp = getservbyname("shell", "tcp"); if (sp && sp->s_port == rport) return (rcmdsh(ahost, rport, locuser, remuser, cmd, p)); } /* use rsh(1) if non-root and remote port is shell. */ if (geteuid()) { struct servent *sp = getservbyname("shell", "tcp"); if (sp && sp->s_port == rport) return (rcmdsh(ahost, rport, locuser, remuser, cmd, NULL)); } pid = getpid(); memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_CANONNAME; hints.ai_family = af; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; snprintf(num, sizeof(num), "%d", ntohs(rport)); error = getaddrinfo(*ahost, num, &hints, &res); if (error) { fprintf(stderr, "rcmd: getaddrinfo: %s\n", gai_strerror(error)); if (error == EAI_SYSTEM) fprintf(stderr, "rcmd: getaddrinfo: %s\n", strerror(errno)); return (-1); } if (res->ai_canonname && strlen(res->ai_canonname) + 1 < sizeof(canonnamebuf)) { strncpy(canonnamebuf, res->ai_canonname, sizeof(canonnamebuf)); *ahost = canonnamebuf; } nres = 0; for (ai = res; ai; ai = ai->ai_next) nres++; ai = res; refused = 0; sigemptyset(&newmask); sigaddset(&newmask, SIGURG); _sigprocmask(SIG_BLOCK, (const sigset_t *)&newmask, &oldmask); for (timo = 1, lport = IPPORT_RESERVED - 1;;) { s = rresvport_af(&lport, ai->ai_family); if (s < 0) { if (errno != EAGAIN && ai->ai_next) { ai = ai->ai_next; continue; } if (errno == EAGAIN) fprintf(stderr, "rcmd: socket: All ports in use\n"); else fprintf(stderr, "rcmd: socket: %s\n", strerror(errno)); freeaddrinfo(res); _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL); return (-1); } _fcntl(s, F_SETOWN, pid); if (_connect(s, ai->ai_addr, ai->ai_addrlen) >= 0) break; _close(s); if (errno == EADDRINUSE) { lport--; continue; } if (errno == ECONNREFUSED) refused = 1; if (ai->ai_next == NULL && (!refused || timo > 16)) { fprintf(stderr, "%s: %s\n", *ahost, strerror(errno)); freeaddrinfo(res); _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL); return (-1); } if (nres > 1) { int oerrno = errno; getnameinfo(ai->ai_addr, ai->ai_addrlen, paddr, sizeof(paddr), NULL, 0, NI_NUMERICHOST); fprintf(stderr, "connect to address %s: ", paddr); errno = oerrno; perror(0); } if ((ai = ai->ai_next) == NULL) { /* refused && timo <= 16 */ struct timespec time_to_sleep, time_remaining; time_to_sleep.tv_sec = timo; time_to_sleep.tv_nsec = 0; _nanosleep(&time_to_sleep, &time_remaining); timo *= 2; ai = res; refused = 0; } if (nres > 1) { getnameinfo(ai->ai_addr, ai->ai_addrlen, paddr, sizeof(paddr), NULL, 0, NI_NUMERICHOST); fprintf(stderr, "Trying %s...\n", paddr); } } lport--; if (fd2p == 0) { _write(s, "", 1); lport = 0; } else { int s2 = rresvport_af(&lport, ai->ai_family), s3; socklen_t len = ai->ai_addrlen; int nfds; if (s2 < 0) goto bad; _listen(s2, 1); snprintf(num, sizeof(num), "%d", lport); if (_write(s, num, strlen(num)+1) != strlen(num)+1) { fprintf(stderr, "rcmd: write (setting up stderr): %s\n", strerror(errno)); _close(s2); goto bad; } nfds = max(s, s2)+1; if(nfds > FD_SETSIZE) { fprintf(stderr, "rcmd: too many files\n"); _close(s2); goto bad; } again: FD_ZERO(&reads); FD_SET(s, &reads); FD_SET(s2, &reads); errno = 0; if (_select(nfds, &reads, 0, 0, 0) < 1 || !FD_ISSET(s2, &reads)){ if (errno != 0) fprintf(stderr, "rcmd: select (setting up stderr): %s\n", strerror(errno)); else fprintf(stderr, "select: protocol failure in circuit setup\n"); _close(s2); goto bad; } s3 = _accept(s2, (struct sockaddr *)&from, &len); switch (from.ss_family) { case AF_INET: aport = ntohs(((struct sockaddr_in *)&from)->sin_port); break; #ifdef INET6 case AF_INET6: aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port); break; #endif default: aport = 0; /* error */ break; } /* * XXX careful for ftp bounce attacks. If discovered, shut them * down and check for the real auxiliary channel to connect. */ if (aport == 20) { _close(s3); goto again; } _close(s2); if (s3 < 0) { fprintf(stderr, "rcmd: accept: %s\n", strerror(errno)); lport = 0; goto bad; } *fd2p = s3; if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) { fprintf(stderr, "socket: protocol failure in circuit setup.\n"); goto bad2; } } _write(s, locuser, strlen(locuser)+1); _write(s, remuser, strlen(remuser)+1); _write(s, cmd, strlen(cmd)+1); if (_read(s, &c, 1) != 1) { fprintf(stderr, "rcmd: %s: %s\n", *ahost, strerror(errno)); goto bad2; } if (c != 0) { while (_read(s, &c, 1) == 1) { _write(STDERR_FILENO, &c, 1); if (c == '\n') break; } goto bad2; } _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL); freeaddrinfo(res); return (s); bad2: if (lport) _close(*fd2p); bad: _close(s); _sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL); freeaddrinfo(res); return (-1); }
/* * Common routine for opendir(3), __opendir2(3) and fdopendir(3). */ static DIR * __opendir_common(int fd, const char *name, int flags) { DIR *dirp; int incr; int saved_errno; int unionstack; struct stat statb; dirp = NULL; /* _fstat() the open handler because the file may have changed. */ if (_fstat(fd, &statb) != 0) goto fail; if (!S_ISDIR(statb.st_mode)) { errno = ENOTDIR; goto fail; } if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || (dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) goto fail; dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); LIST_INIT(&dirp->dd_td->td_locq); dirp->dd_td->td_loccnt = 0; /* * Use the system page size if that is a multiple of DIRBLKSIZ. * Hopefully this can be a big win someday by allowing page * trades to user space to be done by _getdirentries(). */ incr = getpagesize(); if ((incr % DIRBLKSIZ) != 0) incr = DIRBLKSIZ; /* * Determine whether this directory is the top of a union stack. */ if (flags & DTF_NODUP) { struct statfs sfb; if (_fstatfs(fd, &sfb) < 0) goto fail; unionstack = !strcmp(sfb.f_fstypename, "unionfs") || (sfb.f_flags & MNT_UNION); } else { unionstack = 0; } if (unionstack) { int len = 0; int space = 0; char *buf = 0; char *ddptr = 0; char *ddeptr; int n; struct dirent **dpv; /* * The strategy here is to read all the directory * entries into a buffer, sort the buffer, and * remove duplicate entries by setting the inode * number to zero. */ do { /* * Always make at least DIRBLKSIZ bytes * available to _getdirentries */ if (space < DIRBLKSIZ) { space += incr; len += incr; buf = reallocf(buf, len); if (buf == NULL) goto fail; ddptr = buf + (len - space); } n = _getdirentries(fd, ddptr, space, &dirp->dd_seek); if (n > 0) { ddptr += n; space -= n; } } while (n > 0); ddeptr = ddptr; flags |= __DTF_READALL; /* * Re-open the directory. * This has the effect of rewinding back to the * top of the union stack and is needed by * programs which plan to fchdir to a descriptor * which has also been read -- see fts.c. */ if (flags & DTF_REWIND) { (void)_close(fd); if ((fd = _open(name, O_RDONLY | O_DIRECTORY)) == -1) { saved_errno = errno; free(buf); free(dirp); errno = saved_errno; return (NULL); } } /* * There is now a buffer full of (possibly) duplicate * names. */ dirp->dd_buf = buf; /* * Go round this loop twice... * * Scan through the buffer, counting entries. * On the second pass, save pointers to each one. * Then sort the pointers and remove duplicate names. */ for (dpv = 0;;) { n = 0; ddptr = buf; while (ddptr < ddeptr) { struct dirent *dp; dp = (struct dirent *) ddptr; if ((long)dp & 03L) break; if ((dp->d_reclen <= 0) || (dp->d_reclen > (ddeptr + 1 - ddptr))) break; ddptr += dp->d_reclen; if (dp->d_fileno) { if (dpv) dpv[n] = dp; n++; } } if (dpv) { struct dirent *xp; /* * This sort must be stable. */ mergesort(dpv, n, sizeof(*dpv), opendir_compar); dpv[n] = NULL; xp = NULL; /* * Scan through the buffer in sort order, * zapping the inode number of any * duplicate names. */ for (n = 0; dpv[n]; n++) { struct dirent *dp = dpv[n]; if ((xp == NULL) || strcmp(dp->d_name, xp->d_name)) { xp = dp; } else { dp->d_fileno = 0; } if (dp->d_type == DT_WHT && (flags & DTF_HIDEW)) dp->d_fileno = 0; } free(dpv); break; } else { dpv = malloc((n+1) * sizeof(struct dirent *)); if (dpv == NULL) break; } } dirp->dd_len = len; dirp->dd_size = ddptr - dirp->dd_buf; } else { dirp->dd_len = incr; dirp->dd_size = 0; dirp->dd_buf = malloc(dirp->dd_len); if (dirp->dd_buf == NULL) goto fail; dirp->dd_seek = 0; flags &= ~DTF_REWIND; } dirp->dd_loc = 0; dirp->dd_fd = fd; dirp->dd_flags = flags; dirp->dd_lock = NULL; /* * Set up seek point for rewinddir. */ dirp->dd_rewind = telldir(dirp); return (dirp); fail: saved_errno = errno; free(dirp); (void)_close(fd); errno = saved_errno; return (NULL); }
/*ARGSUSED*/ static bool_t rendezvous_request(SVCXPRT *xprt, struct rpc_msg *msg) { int sock, flags; struct cf_rendezvous *r; struct cf_conn *cd; struct sockaddr_storage addr; socklen_t len; struct __rpc_sockinfo si; SVCXPRT *newxprt; fd_set cleanfds; assert(xprt != NULL); assert(msg != NULL); r = (struct cf_rendezvous *)xprt->xp_p1; again: len = sizeof addr; if ((sock = _accept(xprt->xp_fd, (struct sockaddr *)(void *)&addr, &len)) < 0) { if (errno == EINTR) goto again; /* * Clean out the most idle file descriptor when we're * running out. */ if (errno == EMFILE || errno == ENFILE) { cleanfds = svc_fdset; __svc_clean_idle(&cleanfds, 0, FALSE); goto again; } return (FALSE); } /* * make a new transporter (re-uses xprt) */ newxprt = makefd_xprt(sock, r->sendsize, r->recvsize); newxprt->xp_rtaddr.buf = mem_alloc(len); if (newxprt->xp_rtaddr.buf == NULL) return (FALSE); memcpy(newxprt->xp_rtaddr.buf, &addr, len); newxprt->xp_rtaddr.len = len; #ifdef PORTMAP if (addr.ss_family == AF_INET || addr.ss_family == AF_LOCAL) { newxprt->xp_raddr = *(struct sockaddr_in *)newxprt->xp_rtaddr.buf; newxprt->xp_addrlen = sizeof (struct sockaddr_in); } #endif /* PORTMAP */ if (__rpc_fd2sockinfo(sock, &si) && si.si_proto == IPPROTO_TCP) { len = 1; /* XXX fvdl - is this useful? */ _setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &len, sizeof (len)); } cd = (struct cf_conn *)newxprt->xp_p1; cd->recvsize = r->recvsize; cd->sendsize = r->sendsize; cd->maxrec = r->maxrec; if (cd->maxrec != 0) { flags = _fcntl(sock, F_GETFL, 0); if (flags == -1) return (FALSE); if (_fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) return (FALSE); if (cd->recvsize > cd->maxrec) cd->recvsize = cd->maxrec; cd->nonblock = TRUE; __xdrrec_setnonblock(&cd->xdrs, cd->maxrec); } else cd->nonblock = FALSE; gettimeofday(&cd->last_recv_time, NULL); return (FALSE); /* there is never an rpc msg to be processed */ }
int _ftello(FILE *fp, fpos_t *offset) { fpos_t pos; size_t n; int dflags; if (fp->_seek == NULL) { errno = ESPIPE; /* historic practice */ return (1); } /* * Find offset of underlying I/O object, then * adjust for buffered bytes. */ if (fp->_flags & __SOFF) pos = fp->_offset; else { pos = _sseek(fp, (fpos_t)0, SEEK_CUR); if (pos == -1) return (1); } if (fp->_flags & __SRD) { /* * Reading. Any unread characters (including * those from ungetc) cause the position to be * smaller than that in the underlying object. */ if ((pos -= (HASUB(fp) ? fp->_ur : fp->_r)) < 0) { fp->_flags |= __SERR; errno = EIO; return (1); } if (HASUB(fp)) pos -= fp->_r; /* Can be negative at this point. */ } else if ((fp->_flags & __SWR) && fp->_p != NULL) { dflags = 0; if (fp->_flags & __SAPP) dflags = O_APPEND; else if (fp->_file != -1 && (dflags = _fcntl(fp->_file, F_GETFL)) < 0) return (1); if ((dflags & O_APPEND) && (pos = _sseek(fp, (fpos_t)0, SEEK_END)) == -1) { if ((fp->_flags & __SOPT) || __sflush(fp) || (pos = _sseek(fp, (fpos_t)0, SEEK_CUR)) == -1) return (1); else { *offset = pos; return (0); } } /* * Writing. Any buffered characters cause the * position to be greater than that in the * underlying object. */ n = fp->_p - fp->_bf._base; if (pos > OFF_MAX - n) { errno = EOVERFLOW; return (1); } pos += n; } *offset = pos; return (0); }
DIR * __fdopendir2(int fd, int flags) { DIR *dirp; int incr; int saved_errno; struct stat statb; dirp = NULL; if (_fstat(fd, &statb) != 0) goto fail; if (!S_ISDIR(statb.st_mode)) { errno = ENOTDIR; goto fail; } if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || (dirp = malloc(sizeof(DIR))) == NULL) goto fail; /* * Use the system page size if that is a multiple of DIRBLKSIZ. * Hopefully this can be a big win someday by allowing page * trades to user space to be done by _getdirentries(). */ incr = getpagesize(); if ((incr % DIRBLKSIZ) != 0) incr = DIRBLKSIZ; dirp->dd_len = incr; dirp->dd_buf = malloc(dirp->dd_len); if (dirp->dd_buf == NULL) goto fail; flags &= ~DTF_REWIND; dirp->dd_loc = 0; dirp->dd_fd = fd; dirp->dd_flags = flags; dirp->dd_lock = NULL; /* * Set up seek point for rewinddir. */ dirp->dd_seek = 0; dirp->dd_rewind = telldir(dirp); /* * The file offset of the fd passed to fdopendir() determines the * initial entry returned by readdir(). Save this offset so that * telldir() right after fdopendir() returns a value pointing to this * initial entry. * We don't have to worry about misaligned file offsets. The kernel * deals with these. */ if ((dirp->dd_seek = lseek(fd, 0, SEEK_CUR)) < 0) goto fail; return (dirp); fail: saved_errno = errno; if (dirp != NULL) { _reclaim_telldir(dirp); free(dirp->dd_buf); } free(dirp); errno = saved_errno; return (NULL); }
/* * Keep the handle cached. This call may be made quite often. */ static CLIENT * getkeyserv_handle(int vers) { void *localhandle; struct netconfig *nconf; struct netconfig *tpconf; struct key_call_private *kcp = key_call_private_main; struct timeval wait_time; struct utsname u; int main_thread; int fd; static thread_key_t key_call_key; #define TOTAL_TIMEOUT 30 /* total timeout talking to keyserver */ #define TOTAL_TRIES 5 /* Number of tries */ if ((main_thread = thr_main())) { kcp = key_call_private_main; } else { if (key_call_key == 0) { mutex_lock(&tsd_lock); if (key_call_key == 0) thr_keycreate(&key_call_key, key_call_destroy); mutex_unlock(&tsd_lock); } kcp = (struct key_call_private *)thr_getspecific(key_call_key); } if (kcp == NULL) { kcp = (struct key_call_private *)malloc(sizeof (*kcp)); if (kcp == NULL) { return (NULL); } if (main_thread) key_call_private_main = kcp; else thr_setspecific(key_call_key, (void *) kcp); kcp->client = NULL; } /* if pid has changed, destroy client and rebuild */ if (kcp->client != NULL && kcp->pid != getpid()) { clnt_destroy(kcp->client); kcp->client = NULL; } if (kcp->client != NULL) { /* if uid has changed, build client handle again */ if (kcp->uid != geteuid()) { kcp->uid = geteuid(); auth_destroy(kcp->client->cl_auth); kcp->client->cl_auth = authsys_create("", kcp->uid, 0, 0, NULL); if (kcp->client->cl_auth == NULL) { clnt_destroy(kcp->client); kcp->client = NULL; return (NULL); } } /* Change the version number to the new one */ clnt_control(kcp->client, CLSET_VERS, (void *)&vers); return (kcp->client); } if (!(localhandle = setnetconfig())) { return (NULL); } tpconf = NULL; if (uname(&u) == -1) { endnetconfig(localhandle); return (NULL); } while ((nconf = getnetconfig(localhandle)) != NULL) { if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) { /* * We use COTS_ORD here so that the caller can * find out immediately if the server is dead. */ if (nconf->nc_semantics == NC_TPI_COTS_ORD) { kcp->client = clnt_tp_create(u.nodename, KEY_PROG, vers, nconf); if (kcp->client) break; } else { tpconf = nconf; } } } if ((kcp->client == NULL) && (tpconf)) /* Now, try the CLTS or COTS loopback transport */ kcp->client = clnt_tp_create(u.nodename, KEY_PROG, vers, tpconf); endnetconfig(localhandle); if (kcp->client == NULL) { return (NULL); } kcp->uid = geteuid(); kcp->pid = getpid(); kcp->client->cl_auth = authsys_create("", kcp->uid, 0, 0, NULL); if (kcp->client->cl_auth == NULL) { clnt_destroy(kcp->client); kcp->client = NULL; return (NULL); } wait_time.tv_sec = TOTAL_TIMEOUT/TOTAL_TRIES; wait_time.tv_usec = 0; clnt_control(kcp->client, CLSET_RETRY_TIMEOUT, (char *)&wait_time); if (clnt_control(kcp->client, CLGET_FD, (char *)&fd)) _fcntl(fd, F_SETFD, 1); /* make it "close on exec" */ return (kcp->client); }
int rcmd_af(char **ahost, unsigned short rport, const char *locuser, const char *remuser, const char *cmd, int *fd2p, int af) { int s, timo = 1; ssize_t retval; pid_t pid; struct sockaddr_storage caddr, faddr; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; struct addrinfo hints; struct addrinfo *res, *resp; size_t addrlen; int rc; #define MAX_SHORTSTRLEN 6 char aport[MAX_SHORTSTRLEN]; char c; int lport = 0; #ifdef SYSV sigset_t oldmask; sigset_t newmask; struct sigaction oldaction; struct sigaction newaction; #else int oldmask; #endif /* SYSV */ fd_set fdset; int selret; char *addr; static char hostname[MAXHOSTNAMELEN]; socklen_t len; char abuf[INET6_ADDRSTRLEN]; if (!(af == AF_INET || af == AF_INET6 || af == AF_UNSPEC)) { errno = EAFNOSUPPORT; return (-1); } pid = getpid(); memset(&hints, 0, sizeof (hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_CANONNAME; if (af == AF_INET6) { hints.ai_flags |= AI_V4MAPPED; hints.ai_family = AF_UNSPEC; } else { hints.ai_family = af; } (void) snprintf(aport, MAX_SHORTSTRLEN, "%u", ntohs(rport)); rc = getaddrinfo(*ahost, aport, &hints, &res); if (rc != 0) { (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "%s: unknown host%s\n"), *ahost, rc == EAI_AGAIN ? " (try again later)" : ""); return (-1); } resp = res; (void) strlcpy(hostname, res->ai_canonname, MAXHOSTNAMELEN); *ahost = hostname; #ifdef SYSV /* ignore SIGPIPE */ bzero((char *)&newaction, sizeof (newaction)); newaction.sa_handler = SIG_IGN; (void) _sigaction(SIGPIPE, &newaction, &oldaction); /* block SIGURG */ bzero((char *)&newmask, sizeof (newmask)); (void) _sigaddset(&newmask, SIGURG); (void) _sigprocmask(SIG_BLOCK, &newmask, &oldmask); #else oldmask = _sigblock(sigmask(SIGURG)); #endif /* SYSV */ for (;;) { s = rresvport_af(&lport, res->ai_family); if (s < 0) { int af = res->ai_family; /* * See if we have any addresses of a different type * to try. */ while (res != NULL && res->ai_family == af) res = res->ai_next; if (res != NULL) continue; if (errno == EAGAIN) (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "socket: All ports in use\n")); else perror("rcmd: socket"); #ifdef SYSV /* restore original SIGPIPE handler */ (void) _sigaction(SIGPIPE, &oldaction, (struct sigaction *)0); /* restore original signal mask */ (void) _sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0); #else sigsetmask(oldmask); #endif /* SYSV */ freeaddrinfo(resp); return (-1); } bzero((char *)&caddr, sizeof (caddr)); bcopy(res->ai_addr, &caddr, res->ai_addrlen); addrlen = res->ai_addrlen; if (af == AF_INET6 && res->ai_addr->sa_family == AF_INET) { struct in6_addr ia6; struct sockaddr_in6 *in6addr; IN6_INADDR_TO_V4MAPPED(&((struct sockaddr_in *) res->ai_addr)->sin_addr, &ia6); in6addr = (struct sockaddr_in6 *)&caddr; in6addr->sin6_addr = ia6; in6addr->sin6_family = AF_INET6; addrlen = sizeof (struct sockaddr_in6); } (void) _fcntl(s, F_SETOWN, pid); if (connect(s, (struct sockaddr *)&caddr, addrlen) >= 0) break; (void) close(s); if (errno == EADDRINUSE) { lport = 0; continue; } if (errno == ECONNREFUSED && timo <= 16) { (void) sleep(timo); timo *= 2; continue; } if (res->ai_next != NULL) { int oerrno = errno; if (res->ai_addr->sa_family == AF_INET6) addr = (char *)&((struct sockaddr_in6 *) res->ai_addr)->sin6_addr; else addr = (char *)&((struct sockaddr_in *) res->ai_addr)->sin_addr; (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "connect to address %s: "), inet_ntop(res->ai_addr->sa_family, addr, abuf, sizeof (abuf))); errno = oerrno; perror(0); res = res->ai_next; if (res->ai_addr->sa_family == AF_INET6) addr = (char *)&((struct sockaddr_in6 *) res->ai_addr)->sin6_addr; else addr = (char *)&((struct sockaddr_in *) res->ai_addr)->sin_addr; (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "Trying %s...\n"), inet_ntop(res->ai_addr->sa_family, addr, abuf, sizeof (abuf))); continue; } perror(*ahost); freeaddrinfo(resp); #ifdef SYSV /* restore original SIGPIPE handler */ (void) _sigaction(SIGPIPE, &oldaction, (struct sigaction *)0); /* restore original signal mask */ (void) _sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0); #else sigsetmask(oldmask); #endif /* SYSV */ return (-1); } lport = 0; if (fd2p == 0) { (void) write(s, "", 1); } else { int s2 = rresvport_af(&lport, res->ai_family), s3; len = (socklen_t)sizeof (faddr); if (s2 < 0) goto bad; (void) listen(s2, 1); (void) snprintf(aport, MAX_SHORTSTRLEN, "%d", lport); if (write(s, aport, strlen(aport)+1) != strlen(aport)+1) { perror(_dgettext(TEXT_DOMAIN, "write: setting up stderr")); (void) close(s2); goto bad; } FD_ZERO(&fdset); FD_SET(s, &fdset); FD_SET(s2, &fdset); while ((selret = select(FD_SETSIZE, &fdset, (fd_set *)0, (fd_set *)0, (struct timeval *)0)) > 0) { if (FD_ISSET(s, &fdset)) { /* * Something's wrong: we should get no * data on this connection at this point, * so we assume that the connection has * gone away. */ (void) close(s2); goto bad; } if (FD_ISSET(s2, &fdset)) { /* * We assume this is an incoming connect * request and proceed normally. */ s3 = accept(s2, (struct sockaddr *)&faddr, &len); FD_CLR(s2, &fdset); (void) close(s2); if (s3 < 0) { perror("accept"); lport = 0; goto bad; } else break; } } if (selret == -1) { /* * This should not happen, and we treat it as * a fatal error. */ (void) close(s2); goto bad; } *fd2p = s3; switch (faddr.ss_family) { case AF_INET: sin = (struct sockaddr_in *)&faddr; if (ntohs(sin->sin_port) >= IPPORT_RESERVED) { (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "socket: protocol failure in circuit " "setup.\n")); goto bad2; } break; case AF_INET6: sin6 = (struct sockaddr_in6 *)&faddr; if (ntohs(sin6->sin6_port) >= IPPORT_RESERVED) { (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "socket: protocol failure in circuit " "setup.\n")); goto bad2; } break; default: (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "socket: protocol failure in circuit setup.\n")); goto bad2; } } (void) write(s, locuser, strlen(locuser)+1); (void) write(s, remuser, strlen(remuser)+1); (void) write(s, cmd, strlen(cmd)+1); retval = read(s, &c, 1); if (retval != 1) { if (retval == 0) { (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "Protocol error, %s closed connection\n"), *ahost); } else if (retval < 0) { perror(*ahost); } else { (void) fprintf(stderr, _dgettext(TEXT_DOMAIN, "Protocol error, %s sent %d bytes\n"), *ahost, retval); } goto bad2; } if (c != 0) { while (read(s, &c, 1) == 1) { (void) write(2, &c, 1); if (c == '\n') break; } goto bad2; } #ifdef SYSV /* restore original SIGPIPE handler */ (void) _sigaction(SIGPIPE, &oldaction, (struct sigaction *)0); /* restore original signal mask */ (void) _sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0); #else sigsetmask(oldmask); #endif /* SYSV */ freeaddrinfo(resp); return (s); bad2: if (lport) (void) close(*fd2p); bad: (void) close(s); #ifdef SYSV /* restore original SIGPIPE handler */ (void) _sigaction(SIGPIPE, &oldaction, (struct sigaction *)0); /* restore original signal mask */ (void) _sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0); #else sigsetmask(oldmask); #endif /* SYSV */ freeaddrinfo(resp); return (-1); }
extern DB * __hash_open(const char *file, int flags, int mode, const HASHINFO * info, int dflags __unused) { HTAB *hashp; struct stat statbuf; DB *dbp; int bpages, hdrsize, new_table, nsegs, save_errno; if ((flags & O_ACCMODE) == O_WRONLY) { errno = EINVAL; return (NULL); } if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB)))) return (NULL); hashp->fp = -1; /* * Even if user wants write only, we need to be able to read * the actual file, so we need to open it read/write. But, the * field in the hashp structure needs to be accurate so that * we can check accesses. */ hashp->flags = flags; new_table = 0; if (!file || (flags & O_TRUNC) || (stat(file, &statbuf) && (errno == ENOENT))) { if (errno == ENOENT) errno = 0; /* Just in case someone looks at errno */ new_table = 1; } if (file) { if ((hashp->fp = _open(file, flags, mode)) == -1) RETURN_ERROR(errno, error0); /* if the .db file is empty, and we had permission to create a new .db file, then reinitialize the database */ if ((flags & O_CREAT) && _fstat(hashp->fp, &statbuf) == 0 && statbuf.st_size == 0) new_table = 1; _fcntl(hashp->fp, F_SETFD, 1); } if (new_table) { if (!(hashp = init_hash(hashp, file, info))) RETURN_ERROR(errno, error1); } else { /* Table already exists */ if (info && info->hash) hashp->hash = info->hash; else hashp->hash = __default_hash; hdrsize = _read(hashp->fp, &hashp->hdr, sizeof(HASHHDR)); #if BYTE_ORDER == LITTLE_ENDIAN swap_header(hashp); #endif if (hdrsize == -1) RETURN_ERROR(errno, error1); if (hdrsize != sizeof(HASHHDR)) RETURN_ERROR(EFTYPE, error1); /* Verify file type, versions and hash function */ if (hashp->MAGIC != HASHMAGIC) RETURN_ERROR(EFTYPE, error1); #define OLDHASHVERSION 1 if (hashp->VERSION != HASHVERSION && hashp->VERSION != OLDHASHVERSION) RETURN_ERROR(EFTYPE, error1); if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY) RETURN_ERROR(EFTYPE, error1); /* * Figure out how many segments we need. Max_Bucket is the * maximum bucket number, so the number of buckets is * max_bucket + 1. */ nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) / hashp->SGSIZE; hashp->nsegs = 0; if (alloc_segs(hashp, nsegs)) /* * If alloc_segs fails, table will have been destroyed * and errno will have been set. */ return (NULL); /* Read in bitmaps */ bpages = (hashp->SPARES[hashp->OVFL_POINT] + (hashp->BSIZE << BYTE_SHIFT) - 1) >> (hashp->BSHIFT + BYTE_SHIFT); hashp->nmaps = bpages; memset(&hashp->mapp[0], 0, bpages * sizeof(u_int32_t *)); } /* Initialize Buffer Manager */ if (info && info->cachesize) __buf_init(hashp, info->cachesize); else __buf_init(hashp, DEF_BUFSIZE); hashp->new_file = new_table; hashp->save_file = file && (hashp->flags & O_RDWR); hashp->cbucket = -1; if (!(dbp = (DB *)malloc(sizeof(DB)))) { save_errno = errno; hdestroy(hashp); errno = save_errno; return (NULL); } dbp->internal = hashp; dbp->close = hash_close; dbp->del = hash_delete; dbp->fd = hash_fd; dbp->get = hash_get; dbp->put = hash_put; dbp->seq = hash_seq; dbp->sync = hash_sync; dbp->type = DB_HASH; #ifdef DEBUG fprintf(stderr, "%s\n%s%p\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n", "init_htab:", "TABLE POINTER ", hashp, "BUCKET SIZE ", hashp->BSIZE, "BUCKET SHIFT ", hashp->BSHIFT, "DIRECTORY SIZE ", hashp->DSIZE, "SEGMENT SIZE ", hashp->SGSIZE, "SEGMENT SHIFT ", hashp->SSHIFT, "FILL FACTOR ", hashp->FFACTOR, "MAX BUCKET ", hashp->MAX_BUCKET, "OVFL POINT ", hashp->OVFL_POINT, "LAST FREED ", hashp->LAST_FREED, "HIGH MASK ", hashp->HIGH_MASK, "LOW MASK ", hashp->LOW_MASK, "NSEGS ", hashp->nsegs, "NKEYS ", hashp->NKEYS); #endif #ifdef HASH_STATISTICS hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0; #endif return (dbp); error1: if (hashp != NULL) _close(hashp->fp); error0: free(hashp); errno = save_errno; return (NULL); }
/* * __BT_OPEN -- Open a btree. * * Creates and fills a DB struct, and calls the routine that actually * opens the btree. * * Parameters: * fname: filename (NULL for in-memory trees) * flags: open flag bits * mode: open permission bits * b: BTREEINFO pointer * * Returns: * NULL on failure, pointer to DB on success. * */ DB * __bt_open(const char *fname, int flags, mode_t mode, const BTREEINFO *openinfo, int dflags) { struct stat sb; BTMETA m; BTREE *t; BTREEINFO b; DB *dbp; pgno_t ncache; ssize_t nr; int machine_lorder, saved_errno; t = NULL; /* * Intention is to make sure all of the user's selections are okay * here and then use them without checking. Can't be complete, since * we don't know the right page size, lorder or flags until the backing * file is opened. Also, the file's page size can cause the cachesize * to change. */ machine_lorder = byteorder(); if (openinfo) { b = *openinfo; /* Flags: R_DUP. */ if (b.flags & ~(R_DUP)) goto einval; /* * Page size must be indx_t aligned and >= MINPSIZE. Default * page size is set farther on, based on the underlying file * transfer size. */ if (b.psize && (b.psize < MINPSIZE || b.psize > MAX_PAGE_OFFSET + 1 || b.psize & (sizeof(indx_t) - 1) )) goto einval; /* Minimum number of keys per page; absolute minimum is 2. */ if (b.minkeypage) { if (b.minkeypage < 2) goto einval; } else b.minkeypage = DEFMINKEYPAGE; /* If no comparison, use default comparison and prefix. */ if (b.compare == NULL) { b.compare = __bt_defcmp; if (b.prefix == NULL) b.prefix = __bt_defpfx; } if (b.lorder == 0) b.lorder = machine_lorder; } else { b.compare = __bt_defcmp; b.cachesize = 0; b.flags = 0; b.lorder = machine_lorder; b.minkeypage = DEFMINKEYPAGE; b.prefix = __bt_defpfx; b.psize = 0; } /* Check for the ubiquitous PDP-11. */ if (b.lorder != BIG_ENDIAN && b.lorder != LITTLE_ENDIAN) goto einval; /* Allocate and initialize DB and BTREE structures. */ if ((t = (BTREE *)calloc(1, sizeof(BTREE))) == NULL) goto err; t->bt_fd = -1; /* Don't close unopened fd on error. */ t->bt_lorder = b.lorder; t->bt_order = NOT; t->bt_cmp = b.compare; t->bt_pfx = b.prefix; t->bt_rfd = -1; if ((t->bt_dbp = dbp = (DB *)calloc(1, sizeof(DB))) == NULL) goto err; if (t->bt_lorder != machine_lorder) F_SET(t, B_NEEDSWAP); dbp->type = DB_BTREE; dbp->internal = t; dbp->close = __bt_close; dbp->del = __bt_delete; dbp->fd = __bt_fd; dbp->get = __bt_get; dbp->put = __bt_put; dbp->seq = __bt_seq; dbp->sync = __bt_sync; /* * If no file name was supplied, this is an in-memory btree and we * open a backing temporary file. Otherwise, it's a disk-based tree. */ if (fname) { switch (flags & O_ACCMODE) { case O_RDONLY: F_SET(t, B_RDONLY); break; case O_RDWR: break; case O_WRONLY: default: goto einval; } if ((t->bt_fd = _open(fname, flags, mode)) < 0) goto err; } else { if ((flags & O_ACCMODE) != O_RDWR) goto einval; if ((t->bt_fd = tmp()) == -1) goto err; F_SET(t, B_INMEM); } if (_fcntl(t->bt_fd, F_SETFD, 1) == -1) goto err; if (_fstat(t->bt_fd, &sb)) goto err; if (sb.st_size) { if ((nr = _read(t->bt_fd, &m, sizeof(BTMETA))) < 0) goto err; if (nr != sizeof(BTMETA)) goto eftype; /* * Read in the meta-data. This can change the notion of what * the lorder, page size and flags are, and, when the page size * changes, the cachesize value can change too. If the user * specified the wrong byte order for an existing database, we * don't bother to return an error, we just clear the NEEDSWAP * bit. */ if (m.magic == BTREEMAGIC) F_CLR(t, B_NEEDSWAP); else { F_SET(t, B_NEEDSWAP); M_32_SWAP(m.magic); M_32_SWAP(m.version); M_32_SWAP(m.psize); M_32_SWAP(m.free); M_32_SWAP(m.nrecs); M_32_SWAP(m.flags); } if (m.magic != BTREEMAGIC || m.version != BTREEVERSION) goto eftype; if (m.psize < MINPSIZE || m.psize > MAX_PAGE_OFFSET + 1 || m.psize & (sizeof(indx_t) - 1) ) goto eftype; if (m.flags & ~SAVEMETA) goto eftype; b.psize = m.psize; F_SET(t, m.flags); t->bt_free = m.free; t->bt_nrecs = m.nrecs; } else { /* * Set the page size to the best value for I/O to this file. * Don't overflow the page offset type. */ if (b.psize == 0) { b.psize = sb.st_blksize; if (b.psize < MINPSIZE) b.psize = MINPSIZE; if (b.psize > MAX_PAGE_OFFSET + 1) b.psize = MAX_PAGE_OFFSET + 1; } /* Set flag if duplicates permitted. */ if (!(b.flags & R_DUP)) F_SET(t, B_NODUPS); t->bt_free = P_INVALID; t->bt_nrecs = 0; F_SET(t, B_METADIRTY); } t->bt_psize = b.psize; /* Set the cache size; must be a multiple of the page size. */ if (b.cachesize && b.cachesize & (b.psize - 1) ) b.cachesize += (~b.cachesize & (b.psize - 1) ) + 1; if (b.cachesize < b.psize * MINCACHE) b.cachesize = b.psize * MINCACHE; /* Calculate number of pages to cache. */ ncache = (b.cachesize + t->bt_psize - 1) / t->bt_psize; /* * The btree data structure requires that at least two keys can fit on * a page, but other than that there's no fixed requirement. The user * specified a minimum number per page, and we translated that into the * number of bytes a key/data pair can use before being placed on an * overflow page. This calculation includes the page header, the size * of the index referencing the leaf item and the size of the leaf item * structure. Also, don't let the user specify a minkeypage such that * a key/data pair won't fit even if both key and data are on overflow * pages. */ t->bt_ovflsize = (t->bt_psize - BTDATAOFF) / b.minkeypage - (sizeof(indx_t) + NBLEAFDBT(0, 0)); if (t->bt_ovflsize < NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t)) t->bt_ovflsize = NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t); /* Initialize the buffer pool. */ if ((t->bt_mp = mpool_open(NULL, t->bt_fd, t->bt_psize, ncache)) == NULL) goto err; if (!F_ISSET(t, B_INMEM)) mpool_filter(t->bt_mp, __bt_pgin, __bt_pgout, t); /* Create a root page if new tree. */ if (nroot(t) == RET_ERROR) goto err; /* Global flags. */ if (dflags & DB_LOCK) F_SET(t, B_DB_LOCK); if (dflags & DB_SHMEM) F_SET(t, B_DB_SHMEM); if (dflags & DB_TXN) F_SET(t, B_DB_TXN); return (dbp); einval: errno = EINVAL; goto err; eftype: errno = EFTYPE; goto err; err: saved_errno = errno; if (t) { if (t->bt_dbp) free(t->bt_dbp); if (t->bt_fd != -1) _close(t->bt_fd); free(t); } errno = saved_errno; return (NULL); }