blob: 4ab02d06700eeea0a734190b30f61cc618548e13 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; c-basic-offset: 8 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Netscape Portable Runtime (NSPR).
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "primpl.h"
/*
** Global lock variable used to bracket calls into rusty libraries that
** aren't thread safe (like libc, libX, etc).
*/
static PRLock *_pr_rename_lock = NULL;
void
_MD_InitIO (void)
{
}
PRStatus
_MD_open_dir (_MDDir *md,const char *name)
{
int err;
md->d = opendir(name);
if (!md->d) {
err = _MD_ERRNO();
_PR_MD_MAP_OPENDIR_ERROR(err);
return PR_FAILURE;
}
return PR_SUCCESS;
}
char*
_MD_read_dir (_MDDir *md, PRIntn flags)
{
struct dirent *de;
int err;
for (;;) {
/*
* XXX: readdir() is not MT-safe
*/
_MD_ERRNO() = 0;
de = readdir(md->d);
if (!de) {
err = _MD_ERRNO();
_PR_MD_MAP_READDIR_ERROR(err);
return 0;
}
if ((flags & PR_SKIP_DOT) &&
(de->d_name[0] == '.') && (de->d_name[1] == 0))
continue;
if ((flags & PR_SKIP_DOT_DOT) &&
(de->d_name[0] == '.') && (de->d_name[1] == '.') &&
(de->d_name[2] == 0))
continue;
if ((flags & PR_SKIP_HIDDEN) && (de->d_name[1] == '.'))
continue;
break;
}
return de->d_name;
}
PRInt32
_MD_close_dir (_MDDir *md)
{
int rv = 0, err;
if (md->d) {
rv = closedir(md->d);
if (rv == -1) {
err = _MD_ERRNO();
_PR_MD_MAP_CLOSEDIR_ERROR(err);
}
}
return(rv);
}
void
_MD_make_nonblock (PRFileDesc *fd)
{
int blocking = 1;
setsockopt(fd->secret->md.osfd, SOL_SOCKET, SO_NONBLOCK, &blocking, sizeof(blocking));
}
PRStatus
_MD_set_fd_inheritable (PRFileDesc *fd, PRBool inheritable)
{
int rv;
rv = fcntl(fd->secret->md.osfd, F_SETFD, inheritable ? 0 : FD_CLOEXEC);
if (-1 == rv) {
PR_SetError(PR_UNKNOWN_ERROR, _MD_ERRNO());
return PR_FAILURE;
}
return PR_SUCCESS;
}
void
_MD_init_fd_inheritable (PRFileDesc *fd, PRBool imported)
{
if (imported) {
fd->secret->inheritable = _PR_TRI_UNKNOWN;
} else {
int flags = fcntl(fd->secret->md.osfd, F_GETFD, 0);
if (flags == -1) {
PR_SetError(PR_UNKNOWN_ERROR, _MD_ERRNO());
return;
}
fd->secret->inheritable = (flags & FD_CLOEXEC) ?
_PR_TRI_TRUE : _PR_TRI_FALSE;
}
}
void
_MD_query_fd_inheritable (PRFileDesc *fd)
{
int flags;
PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable);
flags = fcntl(fd->secret->md.osfd, F_GETFD, 0);
PR_ASSERT(-1 != flags);
fd->secret->inheritable = (flags & FD_CLOEXEC) ?
_PR_TRI_FALSE : _PR_TRI_TRUE;
}
PRInt32
_MD_open (const char *name, PRIntn flags, PRIntn mode)
{
PRInt32 osflags;
PRInt32 rv, err;
if (flags & PR_RDWR) {
osflags = O_RDWR;
} else if (flags & PR_WRONLY) {
osflags = O_WRONLY;
} else {
osflags = O_RDONLY;
}
if (flags & PR_EXCL)
osflags |= O_EXCL;
if (flags & PR_APPEND)
osflags |= O_APPEND;
if (flags & PR_TRUNCATE)
osflags |= O_TRUNC;
if (flags & PR_SYNC) {
/* Ummmm. BeOS doesn't appear to
support sync in any way shape or
form. */
return PR_NOT_IMPLEMENTED_ERROR;
}
/*
** On creations we hold the 'create' lock in order to enforce
** the semantics of PR_Rename. (see the latter for more details)
*/
if (flags & PR_CREATE_FILE)
{
osflags |= O_CREAT ;
if (NULL !=_pr_rename_lock)
PR_Lock(_pr_rename_lock);
}
rv = open(name, osflags, mode);
if (rv < 0) {
err = _MD_ERRNO();
_PR_MD_MAP_OPEN_ERROR(err);
}
if ((flags & PR_CREATE_FILE) && (NULL !=_pr_rename_lock))
PR_Unlock(_pr_rename_lock);
return rv;
}
PRInt32
_MD_close_file (PRInt32 osfd)
{
PRInt32 rv, err;
rv = close(osfd);
if (rv == -1) {
err = _MD_ERRNO();
_PR_MD_MAP_CLOSE_ERROR(err);
}
return(rv);
}
PRInt32
_MD_read (PRFileDesc *fd, void *buf, PRInt32 amount)
{
PRInt32 rv, err;
PRInt32 osfd = fd->secret->md.osfd;
rv = read( osfd, buf, amount );
if (rv < 0) {
err = _MD_ERRNO();
_PR_MD_MAP_READ_ERROR(err);
}
return(rv);
}
PRInt32
_MD_write (PRFileDesc *fd, const void *buf, PRInt32 amount)
{
PRInt32 rv, err;
PRInt32 osfd = fd->secret->md.osfd;
rv = write( osfd, buf, amount );
if( rv < 0 ) {
err = _MD_ERRNO();
_PR_MD_MAP_WRITE_ERROR(err);
}
return( rv );
}
#ifndef BONE_VERSION /* Writev moves to bnet.c with BONE */
PRInt32
_MD_writev (PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size,
PRIntervalTime timeout)
{
return PR_NOT_IMPLEMENTED_ERROR;
}
#endif
PRInt32
_MD_lseek (PRFileDesc *fd, PRInt32 offset, int whence)
{
PRInt32 rv, err;
rv = lseek (fd->secret->md.osfd, offset, whence);
if (rv == -1) {
err = _MD_ERRNO();
_PR_MD_MAP_LSEEK_ERROR(err);
}
return( rv );
}
PRInt64
_MD_lseek64 (PRFileDesc *fd, PRInt64 offset, int whence)
{
PRInt32 rv, err;
/* According to the BeOS headers, lseek accepts a
* variable of type off_t for the offset, and off_t
* is defined to be a 64-bit value. So no special
* cracking needs to be done on "offset".
*/
rv = lseek (fd->secret->md.osfd, offset, whence);
if (rv == -1) {
err = _MD_ERRNO();
_PR_MD_MAP_LSEEK_ERROR(err);
}
return( rv );
}
PRInt32
_MD_fsync (PRFileDesc *fd)
{
PRInt32 rv, err;
rv = fsync(fd->secret->md.osfd);
if (rv == -1) {
err = _MD_ERRNO();
_PR_MD_MAP_FSYNC_ERROR(err);
}
return(rv);
}
PRInt32
_MD_delete (const char *name)
{
PRInt32 rv, err;
rv = unlink(name);
if (rv == -1)
{
err = _MD_ERRNO();
_PR_MD_MAP_UNLINK_ERROR(err);
}
return (rv);
}
PRInt32
_MD_getfileinfo (const char *fn, PRFileInfo *info)
{
struct stat sb;
PRInt32 rv, err;
PRInt64 s, s2us;
rv = stat(fn, &sb);
if (rv < 0) {
err = _MD_ERRNO();
_PR_MD_MAP_STAT_ERROR(err);
} else if (info) {
if (S_IFREG & sb.st_mode)
info->type = PR_FILE_FILE;
else if (S_IFDIR & sb.st_mode)
info->type = PR_FILE_DIRECTORY;
else
info->type = PR_FILE_OTHER;
/* Must truncate file size for the 32 bit
version */
info->size = (sb.st_size & 0xffffffff);
LL_I2L(s, sb.st_mtime);
LL_I2L(s2us, PR_USEC_PER_SEC);
LL_MUL(s, s, s2us);
info->modifyTime = s;
LL_I2L(s, sb.st_ctime);
LL_MUL(s, s, s2us);
info->creationTime = s;
}
return rv;
}
PRInt32
_MD_getfileinfo64 (const char *fn, PRFileInfo64 *info)
{
struct stat sb;
PRInt32 rv, err;
PRInt64 s, s2us;
rv = stat(fn, &sb);
if (rv < 0) {
err = _MD_ERRNO();
_PR_MD_MAP_STAT_ERROR(err);
} else if (info) {
if (S_IFREG & sb.st_mode)
info->type = PR_FILE_FILE;
else if (S_IFDIR & sb.st_mode)
info->type = PR_FILE_DIRECTORY;
else
info->type = PR_FILE_OTHER;
/* For the 64 bit version we can use
* the native st_size without modification
*/
info->size = sb.st_size;
LL_I2L(s, sb.st_mtime);
LL_I2L(s2us, PR_USEC_PER_SEC);
LL_MUL(s, s, s2us);
info->modifyTime = s;
LL_I2L(s, sb.st_ctime);
LL_MUL(s, s, s2us);
info->creationTime = s;
}
return rv;
}
PRInt32
_MD_getopenfileinfo (const PRFileDesc *fd, PRFileInfo *info)
{
struct stat sb;
PRInt64 s, s2us;
PRInt32 rv, err;
rv = fstat(fd->secret->md.osfd, &sb);
if (rv < 0) {
err = _MD_ERRNO();
_PR_MD_MAP_FSTAT_ERROR(err);
} else if (info) {
if (info) {
if (S_IFREG & sb.st_mode)
info->type = PR_FILE_FILE ;
else if (S_IFDIR & sb.st_mode)
info->type = PR_FILE_DIRECTORY;
else
info->type = PR_FILE_OTHER;
/* Use lower 32 bits of file size */
info->size = ( sb.st_size & 0xffffffff);
LL_I2L(s, sb.st_mtime);
LL_I2L(s2us, PR_USEC_PER_SEC);
LL_MUL(s, s, s2us);
info->modifyTime = s;
LL_I2L(s, sb.st_ctime);
LL_MUL(s, s, s2us);
info->creationTime = s;
}
}
return rv;
}
PRInt32
_MD_getopenfileinfo64 (const PRFileDesc *fd, PRFileInfo64 *info)
{
struct stat sb;
PRInt64 s, s2us;
PRInt32 rv, err;
rv = fstat(fd->secret->md.osfd, &sb);
if (rv < 0) {
err = _MD_ERRNO();
_PR_MD_MAP_FSTAT_ERROR(err);
} else if (info) {
if (info) {
if (S_IFREG & sb.st_mode)
info->type = PR_FILE_FILE ;
else if (S_IFDIR & sb.st_mode)
info->type = PR_FILE_DIRECTORY;
else
info->type = PR_FILE_OTHER;
info->size = sb.st_size;
LL_I2L(s, sb.st_mtime);
LL_I2L(s2us, PR_USEC_PER_SEC);
LL_MUL(s, s, s2us);
info->modifyTime = s;
LL_I2L(s, sb.st_ctime);
LL_MUL(s, s, s2us);
info->creationTime = s;
}
}
return rv;
}
PRInt32
_MD_rename (const char *from, const char *to)
{
PRInt32 rv = -1, err;
/*
** This is trying to enforce the semantics of WINDOZE' rename
** operation. That means one is not allowed to rename over top
** of an existing file. Holding a lock across these two function
** and the open function is known to be a bad idea, but ....
*/
if (NULL != _pr_rename_lock)
PR_Lock(_pr_rename_lock);
if (0 == access(to, F_OK))
PR_SetError(PR_FILE_EXISTS_ERROR, 0);
else
{
rv = rename(from, to);
if (rv < 0) {
err = _MD_ERRNO();
_PR_MD_MAP_RENAME_ERROR(err);
}
}
if (NULL != _pr_rename_lock)
PR_Unlock(_pr_rename_lock);
return rv;
}
PRInt32
_MD_access (const char *name, PRIntn how)
{
PRInt32 rv, err;
int checkFlags;
struct stat buf;
switch (how) {
case PR_ACCESS_WRITE_OK:
checkFlags = S_IWUSR | S_IWGRP | S_IWOTH;
break;
case PR_ACCESS_READ_OK:
checkFlags = S_IRUSR | S_IRGRP | S_IROTH;
break;
case PR_ACCESS_EXISTS:
/* we don't need to examine st_mode. */
break;
default:
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
return -1;
}
rv = stat(name, &buf);
if (rv == 0 && how != PR_ACCESS_EXISTS && (!(buf.st_mode & checkFlags))) {
PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, 0);
return -1;
}
if (rv < 0) {
err = _MD_ERRNO();
_PR_MD_MAP_STAT_ERROR(err);
}
return(rv);
}
PRInt32
_MD_stat (const char *name, struct stat *buf)
{
return PR_NOT_IMPLEMENTED_ERROR;
}
PRInt32
_MD_mkdir (const char *name, PRIntn mode)
{
status_t rv;
int err;
/*
** This lock is used to enforce rename semantics as described
** in PR_Rename. Look there for more fun details.
*/
if (NULL !=_pr_rename_lock)
PR_Lock(_pr_rename_lock);
rv = mkdir(name, mode);
if (rv < 0) {
err = _MD_ERRNO();
_PR_MD_MAP_MKDIR_ERROR(err);
}
if (NULL !=_pr_rename_lock)
PR_Unlock(_pr_rename_lock);
return rv;
}
PRInt32
_MD_rmdir (const char *name)
{
int rv, err;
rv = rmdir(name);
if (rv == -1) {
err = _MD_ERRNO();
_PR_MD_MAP_RMDIR_ERROR(err);
}
return rv;
}
PRInt32
_MD_pr_poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
{
PRInt32 rv = 0;
PRThread *me = _PR_MD_CURRENT_THREAD();
/*
* This code is almost a duplicate of w32poll.c's _PR_MD_PR_POLL().
*/
fd_set rd, wt, ex;
PRFileDesc *bottom;
PRPollDesc *pd, *epd;
PRInt32 maxfd = -1, ready, err;
PRIntervalTime remaining, elapsed, start;
struct timeval tv, *tvp = NULL;
if (_PR_PENDING_INTERRUPT(me))
{
me->flags &= ~_PR_INTERRUPT;
PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
return -1;
}
if (0 == npds) {
PR_Sleep(timeout);
return rv;
}
FD_ZERO(&rd);
FD_ZERO(&wt);
FD_ZERO(&ex);
ready = 0;
for (pd = pds, epd = pd + npds; pd < epd; pd++)
{
PRInt16 in_flags_read = 0, in_flags_write = 0;
PRInt16 out_flags_read = 0, out_flags_write = 0;
if ((NULL != pd->fd) && (0 != pd->in_flags))
{
if (pd->in_flags & PR_POLL_READ)
{
in_flags_read = (pd->fd->methods->poll)(pd->fd, pd->in_flags & ~PR_POLL_WRITE, &out_flags_read);
}
if (pd->in_flags & PR_POLL_WRITE)
{
in_flags_write = (pd->fd->methods->poll)(pd->fd, pd->in_flags & ~PR_POLL_READ, &out_flags_write);
}
if ((0 != (in_flags_read & out_flags_read))
|| (0 != (in_flags_write & out_flags_write)))
{
/* this one's ready right now */
if (0 == ready)
{
/*
* We will have to return without calling the
* system poll/select function. So zero the
* out_flags fields of all the poll descriptors
* before this one.
*/
PRPollDesc *prev;
for (prev = pds; prev < pd; prev++)
{
prev->out_flags = 0;
}
}
ready += 1;
pd->out_flags = out_flags_read | out_flags_write;
}
else
{
pd->out_flags = 0; /* pre-condition */
/* make sure this is an NSPR supported stack */
bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
PR_ASSERT(NULL != bottom); /* what to do about that? */
if ((NULL != bottom)
&& (_PR_FILEDESC_OPEN == bottom->secret->state))
{
if (0 == ready)
{
PRInt32 osfd = bottom->secret->md.osfd;
if (osfd > maxfd) maxfd = osfd;
if (in_flags_read & PR_POLL_READ)
{
pd->out_flags |= _PR_POLL_READ_SYS_READ;
FD_SET(osfd, &rd);
}
if (in_flags_read & PR_POLL_WRITE)
{
pd->out_flags |= _PR_POLL_READ_SYS_WRITE;
FD_SET(osfd, &wt);
}
if (in_flags_write & PR_POLL_READ)
{
pd->out_flags |= _PR_POLL_WRITE_SYS_READ;
FD_SET(osfd, &rd);
}
if (in_flags_write & PR_POLL_WRITE)
{
pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE;
FD_SET(osfd, &wt);
}
if (pd->in_flags & PR_POLL_EXCEPT) FD_SET(osfd, &ex);
}
}
else
{
if (0 == ready)
{
PRPollDesc *prev;
for (prev = pds; prev < pd; prev++)
{
prev->out_flags = 0;
}
}
ready += 1; /* this will cause an abrupt return */
pd->out_flags = PR_POLL_NVAL; /* bogii */
}
}
}
else
{
pd->out_flags = 0;
}
}
if (0 != ready) return ready; /* no need to block */
remaining = timeout;
start = PR_IntervalNow();
retry:
if (timeout != PR_INTERVAL_NO_TIMEOUT)
{
PRInt32 ticksPerSecond = PR_TicksPerSecond();
tv.tv_sec = remaining / ticksPerSecond;
tv.tv_usec = PR_IntervalToMicroseconds( remaining % ticksPerSecond );
tvp = &tv;
}
ready = _MD_SELECT(maxfd + 1, &rd, &wt, &ex, tvp);
if (ready == -1 && errno == EINTR)
{
if (timeout == PR_INTERVAL_NO_TIMEOUT) goto retry;
else
{
elapsed = (PRIntervalTime) (PR_IntervalNow() - start);
if (elapsed > timeout) ready = 0; /* timed out */
else
{
remaining = timeout - elapsed;
goto retry;
}
}
}
/*
** Now to unravel the select sets back into the client's poll
** descriptor list. Is this possibly an area for pissing away
** a few cycles or what?
*/
if (ready > 0)
{
ready = 0;
for (pd = pds, epd = pd + npds; pd < epd; pd++)
{
PRInt16 out_flags = 0;
if ((NULL != pd->fd) && (0 != pd->in_flags))
{
PRInt32 osfd;
bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
PR_ASSERT(NULL != bottom);
osfd = bottom->secret->md.osfd;
if (FD_ISSET(osfd, &rd))
{
if (pd->out_flags & _PR_POLL_READ_SYS_READ)
out_flags |= PR_POLL_READ;
if (pd->out_flags & _PR_POLL_WRITE_SYS_READ)
out_flags |= PR_POLL_WRITE;
}
if (FD_ISSET(osfd, &wt))
{
if (pd->out_flags & _PR_POLL_READ_SYS_WRITE)
out_flags |= PR_POLL_READ;
if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE)
out_flags |= PR_POLL_WRITE;
}
if (FD_ISSET(osfd, &ex)) out_flags |= PR_POLL_EXCEPT;
/* Workaround for nonblocking connects under net_server */
#ifndef BONE_VERSION
if (out_flags)
{
/* check if it is a pending connect */
int i = 0, j = 0;
PR_Lock( _connectLock );
for( i = 0; i < connectCount; i++ )
{
if(connectList[i].osfd == osfd)
{
int connectError;
int connectResult;
connectResult = connect(connectList[i].osfd,
&connectList[i].addr,
connectList[i].addrlen);
connectError = errno;
if(connectResult < 0 )
{
if(connectError == EINTR || connectError == EWOULDBLOCK ||
connectError == EINPROGRESS || connectError == EALREADY)
{
break;
}
}
if(i == (connectCount - 1))
{
connectList[i].osfd = -1;
} else {
for(j = i; j < connectCount; j++ )
{
memcpy( &connectList[j], &connectList[j+1],
sizeof(connectList[j]));
}
}
connectCount--;
bottom->secret->md.connectReturnValue = connectResult;
bottom->secret->md.connectReturnError = connectError;
bottom->secret->md.connectValueValid = PR_TRUE;
break;
}
}
PR_Unlock( _connectLock );
}
#endif
}
pd->out_flags = out_flags;
if (out_flags) ready++;
}
PR_ASSERT(ready > 0);
}
else if (ready < 0)
{
err = _MD_ERRNO();
if (err == EBADF)
{
/* Find the bad fds */
ready = 0;
for (pd = pds, epd = pd + npds; pd < epd; pd++)
{
pd->out_flags = 0;
if ((NULL != pd->fd) && (0 != pd->in_flags))
{
bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
if (fcntl(bottom->secret->md.osfd, F_GETFL, 0) == -1)
{
pd->out_flags = PR_POLL_NVAL;
ready++;
}
}
}
PR_ASSERT(ready > 0);
}
else _PR_MD_MAP_SELECT_ERROR(err);
}
return ready;
} /* _MD_pr_poll */
/*
* File locking.
*/
PRStatus
_MD_lockfile (PRInt32 osfd)
{
PRInt32 rv;
struct flock linfo;
linfo.l_type =
linfo.l_whence = SEEK_SET;
linfo.l_start = 0;
linfo.l_len = 0;
rv = fcntl(osfd, F_SETLKW, &linfo);
if (rv == 0)
return PR_SUCCESS;
_PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
return PR_FAILURE;
}
PRStatus
_MD_tlockfile (PRInt32 osfd)
{
PRInt32 rv;
struct flock linfo;
linfo.l_type =
linfo.l_whence = SEEK_SET;
linfo.l_start = 0;
linfo.l_len = 0;
rv = fcntl(osfd, F_SETLK, &linfo);
if (rv == 0)
return PR_SUCCESS;
_PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
return PR_FAILURE;
}
PRStatus
_MD_unlockfile (PRInt32 osfd)
{
PRInt32 rv;
struct flock linfo;
linfo.l_type =
linfo.l_whence = SEEK_SET;
linfo.l_start = 0;
linfo.l_len = 0;
rv = fcntl(osfd, F_UNLCK, &linfo);
if (rv == 0)
return PR_SUCCESS;
_PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
return PR_FAILURE;
}