regexps.com
VU provides a virtual file-system interface -- a way for programs to redefine the meaning of the basic file-system functions for a particular file descriptor or for a particular part of the file-system namespace. When a process defines a virtual file-system, its definitions apply to that process only.
For the purposes of this section, the unix file system interface is defined as these functions:
access chdir chmod chown chroot close closedir fchdir fchmod fchown fstat fsync ftruncate link lseek lstat mkdir open opendir read readdir readlink rename rmdir stat symlink truncate unlink utime write fcntl dup dup2
For each of those functions, there is a corresponding vu_
function:
vu_access vu_chdir vu_chmod ...
The function prototypes of the vu_
functions are slightly
different from the traditional unix functions. For one thing, all
vu_
functions accept an output paramter, errn
, as the first
argument:
int vu_open (int * errn, char * path, int flags, int mode)
That parameter takes the place of the global variable errno
.
When an error occurs in a vu_
function, the error number is
returned in *errn
.
The other interface difference is the functions for reading
directories. The vu_
functions return an integer to indicate
success (0)
or failure (-1)
and return other information through
output parameters:
int vu_opendir (int * errn, DIR ** retv, char * path) int vu_readdir (int * errn, struct alloc_limits * limits, char ** file_ret, DIR * dir)
Aside from those difference, vu_
functions can be substituted
freely for their unix counterparts. If no other changes are made,
the modified program will run just the same as the original.
There are two additional vu_
functions:
vu_read_retry vu_write_retry
which are similar to read
and write
but which
automatically restart after a EINTR
or EAGAIN
.
Programs can provide their own definitions of the vu_
file-system
functions. These definitions can be made to apply to specific file
descriptors or to a subset of the names in the file-system
namespace of the process.
To define a set of virtual functions, a program defines functions
having the same return types and almost the same parameters as the
vu_
virtual functions. The virtual functions take one extra
(final) argument, void * closure
. For example:
int my_virtual_open (int * errn, char * path, int flags, int mode, void * closure)
All of these functions must be named consistently by adding a prefix to the root name of the file system functions:
my_virtual_access my_virtual_chdir my_virtual_chmod ...
Two additional functions must be provided:
void * my_virtual_make_closure (void * closure) void my_virtual_free_closure (void * closure)
These are explained below.
Having defined a set of virtual file system functions, a program must declare a vtable for the functions:
#include "hackerlab/vu/vu.h" ...
struct vu_fs_discipline my_fs_vtable = { VU_FS_DISCIPLINE_INITIALIZERS (my_virtual_) };
Note that the prefix used to name virtual functions is used by
the macro VU_FS_DISCIPLINE_INITIALIZERS
to initialize the vtable.
Finally, the functions may be applied to either a portion of the file-system namespace or to a particular descriptor:
// Applying `my_virtual_' functions to a particular // descriptor: // vu_set_fd_handler (fd, &my_fs_vtable, closure);
// Applying `my_virtual_' functions to a part // of the file-system namespace. Note that // preg is a `regex_t' -- a regular expression // compiled by `regcomp'. `eflags' is like the // parameter of the same name to `regexec': // vu_push_name_handler (name, doc, preg, eflags, &my_fs_vtable, closure, 0);
After those calls, a call to a vu_
file-system using descriptor
fd
will do its work by calling a my_virtual_
function (e.g.
vu_read
will call my_virtual_read
to read from fd
).
A call to a vu_
function with a path that matches the regexp
preg
will also do its work by calling a my_virtual_
function
(e.g. vu_open
will call my_virtual_open
to open a matching
file-name). If a new file is successfully opened this way, VU
automatically calls vu_set_fd_handler
to establish the same
vtable as the handler for the new descriptor.
Name-space handlers are kept on a stack and searched from the top of stack down. Each handler has a name and an array of strings which are its documentation.
When a set of virtual file system functions is installed by
vu_set_fd_handler
or vu_push_name_handler
the caller provides a
closure. This closure is an opaque value to vu
itself. A copy
of the closure is passed as the final parameter to the caller's
virtual functions. For example, to open a file that has matched a
regular expression passed to vu_push_name_handler
, vu_open
uses
the sequence:
fd = handler->vtable->open (errn, path, flags, mode, handler->closure);
VU doesn't save a copy of the closure directly. Instead, it calls
the make_closure
function from the vtable to create the value
to save and the free_closure
function when that copy is being
discarded. make_closure
is called once when
vu_push_name_handler
is called, and once each time
vu_set_fd_handler
is called. free_closure
is called each time
vu_close
or vu_closedir
is called.
struct vu_handler;
For each VU namespace handler and file-descriptor handler, there
is a struct vu_handler
that indicates the vtable
and closure
to use for that portion of the file-system.
struct vu_handler { struct vu_fs_discipline * vtable; void * closure; };
The vu_
functions can operate on file descriptors which are
created by the program itself without the knowledge of the
operating system kernel. The advantage of such descriptors is that
they take up no kernel resources so it is practical to create a
large number of them.
Pseudo-descriptors which are guaranteed to be distinct from all
kernel descriptors can be created using the function
reserv_pseudo
and destroyed using the function unreserv_pseudo
.
For example:
fd = reserv_pseudo (); vu_set_fd_handler (fd, &my_fs_vtable, 0);
and:
int my_virtual_close (int * errn, int fd, void * closure) { unreserv_pseudo (fd); return 0; }
One way to use a pseudo-descriptor is to combine it with buffered-I/O to create a file that corresponds to a string in the program's address space. (See Buffered File Descriptors.)
void vu_push_name_handler (t_uchar * name, t_uchar ** doc, regex_t * preg, int eflags, struct vu_fs_discipline * vtable, void * closure, int is_optional);
vu_push_name_handler
establishs a vtable of virtual file system
functions for a portion of the file-system namespace.
name
is the name for the handler recognized by
vu_enable_optional_name_handler
. Conventionally,
this name may be used as an option argument to the
command line options -N
or --namespace
. For optimal
help message formatting, name
should be no longer than
30
characters. A pointer to name
is kept by this function.
doc
is a documentation string for the handler, printed in the
outpupt of vu_help_for_optional_handlers
. For optimal help
message formatting, each line of doc
should be no longer than 40
characters. The last element of the array doc
must be 0
.
A pointer to doc
is kept by this function.
File-names matching preg
are handled by the functions in
vtable
. Matching is performed by regexec
:
regexec (preg, path, 0, 0, eflags)
If a matching file is successfully opened, vu_open
and
vu_opendir
call:
vu_set_fd_handler (new_fd, vtable, closure)
If is_optional
is not 0
, the file-name handler is recorded
but not enabled. It can be made active by a call to
vu_enable_optional_name_handler
.
Function
vu_enable_optional_name_handler
int vu_enable_optional_name_handler (t_uchar * name);
Push the named namespace handler on the VU namespace stack.
The named handler must have previously been established by a
call to vu_push_optional_name_handler
.
Return 0
on success, -1
if the named handler was not found.
void vu_set_fd_handler (int fd, struct vu_fs_discipline * vtable, void * closure);
Establish a vtable of virtual file system functions for a particular descriptor or pseudo-descriptor.
The handler is automatically removed by vu_close
.
int vu_move_state (int * errn, int fd, int newfd);
Move the VU handler for fd
to newfd
.
struct vu_handler * vu_path_dispatch (char * path);
Return the vtable and closure that handle the file-name path
.
(See vu_handler.)
struct vu_handler * vu_fd_dispatch (int fd);
Return the vtable and closure that handle the descriptor fd
.
(See vu_handler.)
struct vu_handler * vu_dir_dispatch (DIR * dir);
Return the vtable and closure that handle the directory dir
.
(See vu_handler.)
The function vu_fd_dispatch
returns a pointer to a struct
vu_handler
which in turn holds a pointer to the vtable and closure
used to handle vu_
functions for a particular descriptor (see
vu_handler).
Using vu_fd_dispatch
, descriptor handlers can be stacked. For
example, the buffered-I/O functions (vfdbuf_
) work by imposing a
file-system vtable that maintains a buffer but that performs actual
I/O by calling functions from an underlying vtable. To establish
the buffering vtable, the function vfdbuf_buffer_fd
uses a
sequence of operations like:
int vfdbuf_buffer_fd (int * errn, int fd, long bufsize, int flags, int zero_buffer) { struct vu_handler * sub_handler;
... sub_handler = vu_fd_dispatch (fd);
... remember that sub_handler does I/O for fd: bufs[fd].sub_handler = sub_handler; ...
... Establish the buffering functions as the new vtable for `fd'.
vu_set_fd_handler (fd, &vfdbuf_vtable, 0); }
The vfdbuf_
file-system functions follow this example:
int vfdbuf_fsync (int * errn, int fd, void * closure) { ... Empty the buffer.
if (vfdbuf_flush (errn, fd) < 0) return -1;
... Perform the actual `fsync' using the vtable set aside in vfdbuf_buffer_fd:
return bufs[fd].sub_handler.vtable->fsync (errn, fd, bufs[fd].sub_handler.closure); }
Note that when closing a file, it is the responsibility of the
vfdbuf_
functions to free the closure for the underlying vtable:
int vfdbuf_close (int * errn, int fd, void * closure) { int errn int got; int ign;
... got = bufs[fd].sub_handler.vtable->close (errn, fd, bufs[fd].sub_handler.closure);
bufs[fd].sub_handler.vtable->free_closure (bufs[fd].sub_handler.closure); ... }
These functions approximately mirror the traditional unix system call interface, but have these improvments:
1. Error numbers are not stored in a global, but in a return value.
2. Functions dispatch on file names and descriptors, permitting these functions to work on objects other than ordinary files and sockets and to work on ordinary files and sockets in unusual ways.
int vu_access (int * errn, char * path, int mode);
See the manual page for access
.
int vu_chdir (int * errn, char * path);
See the manual page for chdir
.
int vu_chmod (int * errn, char * path, int mode);
See the manual page for chmod
.
int vu_chown (int * errn, char * path, int owner, int group);
See the manual page for chown
.
int vu_chroot (int * errn, char * path);
See the manual page for chroot
.
int vu_close (int * errn, int fd);
See the manual page for close
.
int vu_closedir (int * errn, DIR * dir);
See the manual page for closedir
.
int vu_fchdir (int * errn, int fd);
See the manual page for fchdir
.
int vu_fchmod (int * errn, int fd, int mode);
See the manual page for fchmod
.
int vu_fchown (int * errn, int fd, int owner, int group);
See the manual page for fchown
.
int vu_fstat (int * errn, int fd, struct stat * buf);
See the manual page for fstat
.
int vu_fsync (int * errn, int fd);
See the manual page for fsync
.
int vu_ftruncate (int * errn, int fd, off_t where);
See the manual page for ftruncate
.
int vu_link (int * errn, char * from, char * to);
See the manual page for link
.
off_t vu_lseek (int * errn, int fd, off_t offset, int whence);
See the manual page for lseek
.
int vu_lstat (int * errn, char * path, struct stat * buf);
See the manual page for lstat
.
int vu_mkdir (int * errn, char * path, int mode);
See the manual page for mkdir
.
int vu_open (int * errn, char * path, int flags, int mode);
See the manual page for open
.
int vu_dir_fd (DIR * dir);
Return the pseudo descriptor associated with DIR.
int vu_opendir (int * errn, DIR ** retv, char * path);
See the manual page for opendir
.
ssize_t vu_read (int * errn, int fd, char * buf, size_t count);
See the manual page for read
.
ssize_t vu_read_retry (int * errn, int fd, char * buf, size_t count);
Use vu_read
to read from fd
. Read repeatedly (even if a read
returns early from EINTR or EAGAIN) until count
characters are
read, or the end-of-file is reached.
Return the number of characters read or -1
on error.
int vu_readdir (int * errn, struct alloc_limits * limits, char ** file_ret, DIR * dir);
See the manual page for readdir
.
Note that in vu
, the file name is returned in *file_ret
and is dynamically allocated using limits
. It is up to the
caller to free the file name.
int vu_readlink (int * errn, char * path, char * buf, int bufsize);
See the manual page for readlink
.
int vu_rename (int * errn, char * from, char * to);
See the manual page for rename
.
int vu_rmdir (int * errn, char * path);
See the manual page for rmdir
.
int vu_stat (int * errn, char * path, struct stat * buf);
See the manual page for stat
.
int vu_symlink (int * errn, char * from, char * to);
See the manual page for symlink
.
int vu_truncate (int * errn, char * path, off_t where);
See the manual page for truncate
.
int vu_unlink (int * errn, char * path);
See the manual page for unlink
.
int vu_utime (int * errn, char * path, struct utimbuf *times);
See the manual page for utime
.
ssize_t vu_write (int * errn, int fd, char * buf, size_t count);
See the manual page for write
.
ssize_t vu_write_retry (int * errn, int fd, char * buf, size_t count);
Use vu_write
to write to fd
. Write repeatedly (even if a write
returns early from EINTR or EAGAIN) until count
characters are
written.
Return the number of characters written or -1
on error.
int vu_fcntl (int * errn, int fd, int cmd, long arg);
See the manual page for fcntl
.
int vu_dup (int * errn, int fd);
See the manual page for dup
.
int vu_dup2 (int * errn, int fd, int newfd);
See the manual page for dup2
.
regexps.com