/* -*- Mode: C++; tab-width: 8; c-basic-offset: 8 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#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;
}

