/* reserv.c - reserved and pseudo file descriptors
*
****************************************************************
* Copyright (C) 1998, 2000 Thomas Lord
*
* See the file "COPYING" for further information about
* the copyright and warranty status of this work.
*/
#include "hackerlab/os/errno.h"
#include "hackerlab/os/limits.h"
#include "hackerlab/bugs/panic.h"
#include "hackerlab/arrays/ar.h"
#include "hackerlab/vu/vu-bad-arg.h"
#include "hackerlab/vu/vu.h"
#include "hackerlab/vu/reserv.h"
#include "hackerlab/sort/qsort.h"
/************************************************************************
*(h0 "Reserved File Descriptors and Pseudo-Descriptors"
* :include ("vu/reserv.h"))
*
* The functions in this section allow programs to allocate file
* descriptors and pseudo-descriptors. A pseudo-descriptor is an
* integer outside of the range of descriptors used by the kernel
* (`0..(OPEN_MAX - 1)'). Pseudo-descriptors are densely packed
* (allocated sequentially beginning with `OPEN_MAX') and are suitable
* for use with the function `vu_set_fd_handler' (see xref:"A Virtual
* Unix File-System Interface").
*/
/* A dynamically sized array of available pseudo-descriptors.
*/
static t_converter free_reserved_pseudo = {0};
/* The lowest-numbered pseudo-descriptor (== OPEN_MAX).
*/
static int first_pseudo;
/* The lowest-numbered pseudo-descriptor that has never been allocated.
*/
static int next_pseudo;
/* VU functions
*/
static void * reserve_pseudo_make_closure (void * closure);
static void reserve_pseudo_free_closure (void * closure);
static int reserve_pseudo_close (int * errn, int fd, void * closure);
/* #define reserve_pseudo_make_closure (vu_make_closure_fn)vu_bad_arg */
/* #define reserve_pseudo_free_closure (vu_free_closure_fn)vu_bad_arg */
#define reserve_pseudo_access (vu_access_fn)vu_bad_arg
#define reserve_pseudo_chdir (vu_chdir_fn)vu_bad_arg
#define reserve_pseudo_chmod (vu_chmod_fn)vu_bad_arg
#define reserve_pseudo_chown (vu_chown_fn)vu_bad_arg
#define reserve_pseudo_chroot (vu_chroot_fn)vu_bad_arg
/* #define reserve_pseudo_close (vu_close_fn)vu_bad_arg */
#define reserve_pseudo_closedir (vu_closedir_fn)vu_bad_arg
#define reserve_pseudo_fchdir (vu_fchdir_fn)vu_bad_arg
#define reserve_pseudo_fchmod (vu_fchmod_fn)vu_bad_arg
#define reserve_pseudo_fchown (vu_fchown_fn)vu_bad_arg
#define reserve_pseudo_fstat (vu_fstat_fn)vu_bad_arg
#define reserve_pseudo_fsync (vu_fsync_fn)vu_bad_arg
#define reserve_pseudo_ftruncate (vu_ftruncate_fn)vu_bad_arg
#define reserve_pseudo_link (vu_link_fn)vu_bad_arg
#define reserve_pseudo_lseek (vu_lseek_fn)vu_bad_arg
#define reserve_pseudo_lstat (vu_lstat_fn)vu_bad_arg
#define reserve_pseudo_mkdir (vu_mkdir_fn)vu_bad_arg
#define reserve_pseudo_open (vu_open_fn)vu_bad_arg
#define reserve_pseudo_opendir (vu_opendir_fn)vu_bad_arg
#define reserve_pseudo_read (vu_read_fn)vu_bad_arg
#define reserve_pseudo_readdir (vu_readdir_fn)vu_bad_arg
#define reserve_pseudo_readlink (vu_readlink_fn)vu_bad_arg
#define reserve_pseudo_rename (vu_rename_fn)vu_bad_arg
#define reserve_pseudo_rmdir (vu_rmdir_fn)vu_bad_arg
#define reserve_pseudo_stat (vu_stat_fn)vu_bad_arg
#define reserve_pseudo_symlink (vu_symlink_fn)vu_bad_arg
#define reserve_pseudo_truncate (vu_truncate_fn)vu_bad_arg
#define reserve_pseudo_unlink (vu_unlink_fn)vu_bad_arg
#define reserve_pseudo_utime (vu_utime_fn)vu_bad_arg
#define reserve_pseudo_write (vu_write_fn)vu_bad_arg
#define reserve_pseudo_fcntl (vu_fcntl_fn)vu_bad_arg
#define reserve_pseudo_dup (vu_dup_fn)vu_bad_arg
#define reserve_pseudo_dup2 (vu_dup2_fn)vu_bad_arg
#define reserve_pseudo_move_state (vu_dup2_fn)vu_bad_arg
static struct vu_fs_discipline pseudo_fd_functions \
= { VU_FS_DISCIPLINE_INITIALIZERS (reserve_pseudo_) };
/* local prototypes */
static void init_reserv(void);
/*(c reserv)
* int reserv (int * errn, int flags);
*
* Allocate a file descriptor by opening `"/dev/null"' by using
* `vu_open' in the manner descibed by `flags' which may be one of
* `O_RDONLY', `O_RDWR' etc.
*
* (See xref:"vu_open".)
*/
int
reserv (int * errn, int flags)
{
return vu_open (errn, "/dev/null", flags, 0);
}
static void
init_reserv ()
{
static int initialized = 0;
if (!initialized)
{
/* Is there really a guarantee that descriptors
* are densly packed, starting at 0? If not,
* this is bogus.
*/
next_pseudo = sysconf (_SC_OPEN_MAX);
if (next_pseudo < 0)
panic ("I/O error calling sysconf (_SC_OPEN_MAX);\n");
/*
next_pseudo = getdtablesize ();
if (next_pseudo < 0)
next_pseudo = OPEN_MAX;
*/
first_pseudo = next_pseudo;
initialized = 1;
}
}
/*
* \brief sort ints
*/
static int
int_cmp (void * a, void * b, void * closure)
{
int *left = (int *)a;
int *right = (int *)b;
return *left - *right;
}
/*
* \brief trim the cache of reserved available pseudo fd's
*/
static void
trim_reserv_cache(void)
{
if (first_pseudo == next_pseudo)
/* we'll be allocating from the start anyway */
return;
/* sort the reserved fds */
/* TODO: reverse sort so that later allocations will take the lowest available
* once we've done the trimming.
*/
quicksort (free_reserved_pseudo.void_ptr,
ar_size (free_reserved_pseudo.void_ptr),
sizeof (int),
int_cmp,
NULL);
while (ar_size (free_reserved_pseudo.void_ptr))
{
if (*(int *)ar_ref (&free_reserved_pseudo.void_ptr,
ar_size (free_reserved_pseudo.void_ptr) - 1,
sizeof (int)) == next_pseudo)
{
--next_pseudo;
ar_pop_int(&free_reserved_pseudo.int_ptr);
}
else
{
/* not the same, we're done */
return;
}
}
ar_free (&free_reserved_pseudo.void_ptr);
}
/*(c reserv_pseudo)
* int reserv_pseudo (int * errn, int flags);
*
* Reserve a pseudo file descriptor suitable for use with
* `vu_set_fd_handler'. A pseudo file descriptor can be used with the
* vu file system functions and is guaranteed not to be the same as
* any real file descriptor. (see xref:"A Virtual Unix File-System
* Interface")
*/
int
reserv_pseudo (int * errn, int flags)
{
int answer;
init_reserv ();
if (ar_size (free_reserved_pseudo.void_ptr))
answer = ar_pop_int(&free_reserved_pseudo.int_ptr);
else
answer = ++next_pseudo;
vu_set_fd_handler (answer, &pseudo_fd_functions, 0);
return answer;
}
/*(c reserv_pseudo_ge_n)
* int reserv_pseudo_ge_n (int * errn, int n, int flags);
*
* Reserve a pseudo file descriptor greater than or equal to `n'
* suitable for use with `vu_set_fd_handler'. A pseudo file
* descriptor can be used with the vu file system functions and is
* guaranteed not to be the same as any real file descriptor. (see
* xref:"A Virtual Unix File-System Interface")
*
* This function is useful for implementing `vu_fcntl' with a `cmd'
* argument `F_DUPFD'.
*/
int
reserv_pseudo_ge_n (int * errn, int n, int flags)
{
int s;
int answer;
init_reserv ();
if (n < first_pseudo)
n = first_pseudo;
answer = -1;
s = ar_size (free_reserved_pseudo.void_ptr);
if (s)
{
int x;
for (x = 0; x < s; ++x)
if ( (free_reserved_pseudo.int_ptr[x] >= n)
&& (free_reserved_pseudo.int_ptr[x] < free_reserved_pseudo.int_ptr[s - 1]))
{
int tmp;
tmp = free_reserved_pseudo.int_ptr[x];
free_reserved_pseudo.int_ptr[x] = free_reserved_pseudo.int_ptr[s - 1];
free_reserved_pseudo.int_ptr[s - 1] = tmp;
}
}
if (!s || (free_reserved_pseudo.int_ptr[s - 1] < n))
{
while (next_pseudo < n)
{
ar_push_int(&free_reserved_pseudo.int_ptr, next_pseudo);
++next_pseudo;
}
}
return reserv_pseudo (errn, flags);
}
/* static void * reserve_pseudo_make_closure (void * closure);
*
* Do nothing. This is the `make_closure' function in the vtable
* `pseudo_fd_functions'.
*/
static void *
reserve_pseudo_make_closure (void * closure)
{
return closure;
}
/* static void reserve_pseudo_free_closure (void * closure);
*
* Do nothing. This is the `free_closure' function in the vtable
* `pseudo_fd_functions'.
*/
static void
reserve_pseudo_free_closure (void * closure)
{
}
/* static int reserve_pseudo_close (int * errn, int fd, void * closure);
*
* Close a pseudo-file descriptor. This is the `close' function in
* the vtable `pseudo_fd_functions'.
*/
static int
reserve_pseudo_close (int * errn, int fd, void * closure)
{
ar_push_int (&free_reserved_pseudo.int_ptr, fd);
trim_reserv_cache();
return 0;
}
/*(c unreserv_pseudo)
* void unreserv_pseudo (int fd);
*
* Release a pseudo-descriptor previously allocated by
* `reserved_pseudo'.
*/
void
unreserv_pseudo (int fd)
{
ar_push_int (&free_reserved_pseudo.int_ptr, fd);
trim_reserv_cache();
}
syntax highlighted by Code2HTML, v. 0.9.1