Source: file.h


Annotated List
Files
Globals
Hierarchy
Index
// Copyright (C) 1999-2000 Open Source Telecom Corporation.
//  
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software 
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// 
// As a special exception to the GNU General Public License, permission is 
// granted for additional uses of the text contained in its release 
// of APE.
// 
// The exception is that, if you link the APE library with other files
// to produce an executable, this does not by itself cause the
// resulting executable to be covered by the GNU General Public License.
// Your use of that executable is in no way restricted on account of
// linking the APE library code into it.
// 
// This exception does not however invalidate any other reasons why
// the executable file might be covered by the GNU General Public License.
// 
// This exception applies only to the code released under the 
// name APE.  If you copy code from other releases into a copy of
// APE, as the General Public License permits, the exception does
// not apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
// 
// If you write modifications of your own for APE, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.  

#ifndef	__APE_FILE_H__
#define	__APE_FILE_H__

#ifndef	__APE_THREAD_H__
#include <APE/thread.h>
#endif

#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>

typedef	unsigned long pos_t;

enum
{
	FILE_OPEN_READONLY = O_RDONLY,
	FILE_OPEN_WRITEONLY = O_WRONLY,
	FILE_OPEN_READWRITE = O_RDWR,
	FILE_OPEN_APPEND = O_WRONLY | O_APPEND,

#ifdef	O_SYNC
	FILE_OPEN_SYNC = O_RDWR | O_SYNC,
#else
	FILE_OPEN_SYNC = O_RDWR,
#endif
	FILE_OPEN_TRUNCATE = O_RDWR | O_TRUNC
};

enum
{
	FILE_MAPPED_READ = O_RDONLY,
	FILE_MAPPED_WRITE = O_WRONLY,
	FILE_MAPPED_RDWR = O_RDWR
};

enum
{
	FILE_PERM_PRIVATE = S_IRUSR | S_IWUSR,
	FILE_PERM_GROUP = FILE_PERM_PRIVATE | S_IRGRP | S_IWGRP,
	FILE_PERM_PUBLIC = FILE_PERM_GROUP | S_IROTH | S_IWOTH
};

/**
 * The purpose of this class is to define a low level file access
 * class that is portable between Win32 and Posix systems.  In fact
 * win32 lacks certain critical concepts related to multi-user file
 * ownership and yet introduces some very odd concepts of cache control
 * and configuration based on assumed application usage.  These combine
 * to make this and the other related portable file access classes a 
 * little odd, and certainly in need of further work.
 * 
 * @author David Sugar <dyfet@ostel.com>
 * @short Portable disk file access.
 */
class File
{
protected:
	int _fd;
	
public:
	/**
	 * Open an existing file of the given name with the given access
	 * mode.  Valid access modes include FILE_OPEN_READONLY, FILE_OPEN_WRITEONLY,
	 * FILE_OPEN_APPEND, FILE_OPEN_READWRITE, FILE_OPEN_SYNC, and FILE_OPEN_TRUNCATE.
	 * A File "exception" is thrown if the file open fails.
	 * 
	 * @param fname name of file path to open.
	 * @param access mode for access to the given file.
	 */
	File(const char *fname, int access);
	/**
	 * Create a new file and access for use by the File class.  Valid modes
	 * include FILE_OPEN_WRITEONLY, FILE_OPEN_APPEND, FILE_OPEN_SYNC,
	 * and FILE_OPEN_READWRITE.  The most generic permissions for your
	 * newly created file include FILE_PERM_PRIVATE, FILE_PERM_GROUP, and
	 * FILE_PERM_PUBIC.  On Posix systems, the standard chmod() permission
	 * values may be directly used, as well as the stat.h defines.  As
	 * with all APE classes, an exception is thrown if the create fails.
	 * 
	 * @param fname name of file path to create and open.
	 */
	File(const char *fname, int access, int perm);
	/**
	 * Open a copy of an existing "File" through a duplicate object.  This 
	 * essentially uses the Posix dup() call to duplicate the file
	 * descriptor involved.
	 * 
	 * @param f an existing File class to duplicate from.
	 */
	File(const File &f);
	/**
	 * Create a File "class" to reference an existing and already open
	 * file descriptor.  This is kind of like "fdopen" in purpose, and
	 * simply allows any existing file descriptor to be manipulated
	 * by the File methods.
	 * 
	 * @param fd an existing and already open file descriptor.
	 */
	File(int fd);

	/**
	 * The destructor normally closes the File descriptor and should
	 * also release any allocated resources in a derived class.
	 */
	virtual ~File()
		{close(_fd);};

	/**
	 * Read an arbitrary number of bytes from the File object.
	 * 
	 * @return number of bytes read with success, or -1 on error.
	 * @param buf pointer to hold read data.
	 * @param len number of bytes to read.
	 */
	int Read(void *buf, size_t len)
		{return read(_fd, (char *)buf, len);};

	/**
	 * Write an arbitrary number of bytes to the File object.
	 * 
	 * @return number of bytes written on success, -1 on error.
	 * @param buf pointer to data that will be written.
	 * @param len number of butes to write.
	 */
	int Write(void *buf, size_t len)
		{return write(_fd, (char *)buf, len);};

	/**
	 * A quick and easy way to write a null terminated C string to
	 * a file.
	 * 
	 * @return number of bytes written on success, -1 on error.
	 * @param pointer to null terminated string to write to the file.
	 */
	int Write(char *buf)
		{return write(_fd, buf, strlen(buf));};

	/**
	 * Set the current file position.  This is an absolute position
	 * from the start of the file.
	 * 
	 * @return current file position after operation.
	 * @param pos new file position to set file to.
	 */
	pos_t Position(pos_t pos)
		{return (pos_t)lseek(_fd, pos, SEEK_SET);}; 

	/**
	 * Set the current file position to the end of the file.
	 * 
	 * @return file position (length) of the end of the file.
	 */
	pos_t Append(void)
		{return (pos_t)lseek(_fd, 0l, SEEK_END);}; 

	File &operator=(const File &f);

	friend int read(File &f, void *buf, size_t len)
		{return read(f._fd, (char *)buf, len);};

	friend int write(File &f, void *buf, size_t len)
		{return write(f._fd, (char *)buf, len);};

	friend int write(File &f, char *buf)
		{return write(f._fd, buf, strlen(buf));};

	friend pos_t append(File &f)
		{return (pos_t)lseek(f._fd, 0l, SEEK_END);};

	friend pos_t position(File &f, pos_t pos)
		{return (pos_t)lseek(f._fd, pos, SEEK_SET);};
};

/**
 * Create a new file always on the specified path.  If a file already
 * exists on the specified path name, it is removed.
 * 
 * @author David Sugar <dyfet@ostel.com>
 * @short create a new file pathname.
 */
class NewFile : public File
{
protected:
	void Initial(char *fname, int perm)
		{remove(fname);};

public:
	NewFile(const char *fname, int access, int perms);
};

/**
 * Create a new file as a FIFO device (named pipe) on the current
 * file system and then opens it.
 * 
 * @author David Sugar <dyfet@ostel.com>
 * @short Create and open a named pipe.
 */
class NamedPipe : public File
{
protected:
	void Initial(char *fname, int perm);

public:
	NamedPipe(const char *fname, int perms);
};	

/**
 * Create and map a disk file into memory.  This portable class works
 * under both Posix via mmap and under the win32 API.   A mapped file
 * can be referenced directly by it's memory segment.
 * 
 * @author David Sugar
 * @short map a named disk file into memory.
 */
class MappedFile : protected File
{
private:
	size_t _len;
	char *_mem;

public:
	/**
	 * Map a portion or all of a specified file in the specified
	 * shared memory access mode.  Valid mapping modes include
	 * FILE_MAPPED_READ, FILE_MAPPED_WRITE, and FILE_MAPPED_RDWR.
	 * 
	 * @param fname pathname of file to map into memory.
	 * @param offset from start of file to begin mapping in bytes.
	 * @param size of mapped area in bytes.
	 * @param mode to map file.
	 */
	MappedFile(const char *fname, pos_t offset, size_t size, int mode);	
	
	/**
	 * Release a mapped section of memory associated with a file.  The
	 * mapped area is updated back to disk.
	 */
	virtual ~MappedFile();

	/**
	 * Synchronize the contents of the mapped portion of memory with
	 * the disk file and wait for completion.  This assures the memory
	 * mapped from the file is written back.
	 */
	inline void Sync(void)
		{msync(_mem, _len, MS_SYNC);};

	/**
	 * Map a portion of the memory mapped from the file back to the
	 * file and do not wait for completion.  This is useful when mapping
	 * a database file and updating a single record.
	 * 
	 * @param offset into the mapped region of memory.
	 * @param len of partial region (example, record length).
	 */
	inline void Update(unsigned offset, size_t len)
		{msync(_mem + offset, len, MS_ASYNC);};

	/**
	 * Fetch a pointer to an offset within the memory mapped portion
	 * of the disk file.  This really is used for convience of matching
	 * operations between Update and Fetch, as one could simply have
	 * accessed the base pointer where the file was mapped directly.
	 * 
	 * @param offset from start of mapped memory.
	 */
	inline void *Fetch(unsigned offset)
		{return _mem + offset;};

	friend inline void sync(MappedFile &m)
		{msync(m._mem, m._len, MS_SYNC);};

	friend inline void update(MappedFile &m, unsigned offset, size_t len)
		{msync(m._mem + offset, len, MS_ASYNC);};

	friend inline void *fetch(MappedFile &m, unsigned offset = 0)
		{return m._mem + offset;};
};

/**
 * Locked files are presumed to be shared between different processes.
 * File locks are used to protect file access and specifically defined 
 * operations which must occur exclusivily by a single process, such as
 * for logical record locks in a database.  Record locking is assumed
 * to be a blocking operation.
 * 
 * @author David Sugar <dyfet@ostel.com>
 * @short shared file access between different processes.
 */
class LockedFile : protected File
{
public:
	/**
	 * Open an existing and named data file that may be shared by
	 * multiple processes.  Throw an exception if the file doesn't
	 * exist or cannot be accessed.
	 * 
	 * @param fname path name of disk file to open.
	 * @param access access mode (read, write, or rdwr) to use.
	 * @see File#File
	 */
	LockedFile(const char *fname, int access);
	/**
	 * Duplicate an existing locked file's descriptor, but do not
	 * duplicate any outstanding locks.
	 * 
	 * @param f reference to another LockedFile.
	 */
	LockedFile(const LockedFile &f);

	/**
	 * Release the locked file and any locks held by this object.
	 */
	~LockedFile()
		{close(_fd);};

	/**
	 * Recieve a lock from the end of the current file to inf and
	 * when successful write the specified data to the end.  Upon
	 * completion, release the file lock.
	 * 
	 * @return number of bytes written on success, -1 on error.
	 * @param buf pointer to data to write to the file.
	 * @param len number of bytes to write.
	 */
	int Append(void *buf, size_t len);
	/**
	 * Request a lock for a portion of the datafile and then read
	 * the exclusivily locked portion of the file into memory.
	 * 
	 * @return number of bytes read on success, -1 on error.
	 * @param pos offset to portion of file to read.
	 * @param buf pointer to memory to read data into.
	 * @param len number of bytes to read.
	 */
	int Request(pos_t pos, void *buf, size_t len);
	/**
	 * Copy a presumably modified memory block back to a locked
	 * portion of a file as retrieved from a previous request, and
	 * then clear the file lock so that another process may now
	 * access that region.  No check is made to assure the Update
	 * actually matches a previous Request.
	 * 
	 * @return number of bytes written on success, -1 on error.
	 * @param pos offset to portion of file to write.
	 * @param buf pointer to memory to write data from.
	 * @param len number of bytes to write.
	 */
	int Update(pos_t pos, void *buf, size_t len);
	/**
	 * Release a lock held from a request without modifying any
	 * data in the file so that another process may now access the
	 * locked region.  No check is made to assure the Clear actually
	 * matches an existing Request.
	 * 
	 * @return 0 on success, -1 on error.
	 * @param pos offset to portion of file that was locked.
	 * @param len number of bytes originally requested.
	 */
	int Clear(pos_t pos, size_t len);
};

/**
 * Locked files provide clean access to a database file that is shared
 * between multiple processes via file locking.  A seperate and more 
 * efficient methodology is provided here to enable multiple threads to
 * access the same file descriptor concurrently.  While shared files do
 * not include thread exclusive file region locking, such locking can be 
 * established with in memory based tables rather than kernel file locks 
 * for speed if needed.
 * 
 * @author David Sugar <dyfet@ostel.com>
 * @short Shared concurrent file access from multiple threads.
 */
class SharedFile : public File, public Mutex
{
public:
	/**
	 * Create a new disk file that will be shared between multiple
	 * threads.  A file name, access mode, and file permissions to
	 * use when creating a new file are specified.  On failure an
	 * exception is thrown.
	 * 
	 * @param fname path name of disk file to open or create.
	 * @param access file access mode.
	 * @param perm file permission to assign when creating a new file.
	 * @see File#File
	 */
	SharedFile(const char *fname, int access, int perm);
	/**
	 * Open an existing disk file for shared access between multiple
	 * threads.  A file name and file access mode must be specified.
	 * If the file does not exist or cannot be opened, an exception is
	 * thrown.
	 * 
	 * @param fname path name of disk file to open.
	 * @param access file access mode.
	 */
	SharedFile(const char *fname, int access);
	/**
	 * Open a copy of an existing "File" through a duplicate object for
	 * shared access from multiple threads.  This essentially uses the 
	 * Posix dup() call to duplicate the file descriptor involved.
	 * 
	 * @param f an existing File class to duplicate from.
	 */
	SharedFile(const SharedFile &f);
	/**
	 * Create a shared file "class" to reference an existing and already 
	 * open file descriptor from multiple threads.  This is kind of like 
	 * "fdopen" in purpose, and simply allows any existing file descriptor 
	 * to be manipulated by the thread shared methods.
	 * 
	 * @param fd an existing and already open file descriptor.
	 */
	SharedFile(int fd);
	/**
	 * Close the existing shared file object and release any used
	 * resources.
	 */
	~SharedFile()
		{close(_fd);};

	/**
	 * Write data to the end of a specified share file exclusivily.
	 * The mutex lock is used to assure the write operation is not
	 * interfered with by other threads until it completes.
	 * 
	 * @return number of bytes written on success, -1 on failure.
	 * @param buf pointer to data to write to the file.
	 * @param len number of bytes to write.
	 */
	int Append(void *buf, size_t len);
	/**
	 * Read data from a specified file offset exclusivily.  The Mutex
	 * lock is used to assure the read operation can complete without
	 * interference by other threads.  While the current file offset
	 * must be specified and tracked seperately by each thread on it's
	 * own, an enhanced version may soon use a ThreadKey for this purpose.
	 * 
	 * @return number of bytes read on success, -1 on error.
	 * @param pos file offset to start read from file.
	 * @param buf pointer to store data read from file.
	 * @param len number of bytes to read.
	 */
	int Read(pos_t pos, void *buf, size_t len);
	/**
	 * Write data to a specified file offset exclusivily.  The Mutex
	 * lock is used to assure the write operation can complete without
	 * interference by other threads.  While the current file offset
	 * must be specified and tracked seperately by each thread on it's
	 * own, an enhanced version may soon use a ThreadKey for this purpose.
	 * 
	 * @return number of bytes written on success, -1 on error.
	 * @param pos file offset to start file write.
	 * @param buf pointer to data to write to the file.
	 * @param len number of bytes to write.
	 */
	int Write(pos_t pos, void *buf, size_t len);

	SharedFile &operator=(SharedFile &f);
	
	friend inline int read(SharedFile &f, pos_t pos, void *buf, size_t len)
		{return f.Read(pos, buf, len);};

 	friend inline int write(SharedFile &f, pos_t pos, void *buf, size_t len)
 		{return f.Write(pos, buf, len);};

	friend inline int append(SharedFile &f, void *buf, size_t len)
		{return f.Append(buf, len);};
};

/**
 * The Pipe uses system kernel buffering to hold data being passed either
 * between two execution contexts within the same process, or between
 * different processes.  Unlike thread's "Buffer", Pipe uses system
 * descriptors and kernel memory.  Under Posix, the size of the pipe and
 * associated kernel memory is always a fixed constant as defined by
 * _PC_PIPE_BUF.  The APE "pipe" class primarily deals with "atomic"
 * transfers of fixed sized objects through pipes.  Pipes may pass
 * data arbitrarily and can also be used through the "pipestream" class.
 *
 * The "Pipe" class is not meant to be a true "public" class, but as a
 * builder class for deriving other classes.
 *
 * @author David Sugar <dyfet@ostel.com>.
 * @short kernel buffering between processes and/or threads.
 */
class Pipe
{
private:
	int fd[2];
	int objcount;
	int objsize;

protected:
	/**
	 * Get the object size for atomic operations.
	 *
	 * @return size of atomic operations.
	 */
	inline int getSize(void)
		{return objsize;};

	/**
	 * Sender is often used for implementing a fork()'d message port
	 * between processes.  By stopping the sender, the current pipe
	 * can then only be used for receiving, and the sender is presumed
	 * to be in the other half of a fork()'d process.
	 * 
	 * @see #endReceiver
	 */
	inline void endSender(void)
		{close(fd[1]);};
	/**
	 * Receiver is often used for implementing a fork()'d message port
	 * between processes.  By stopping the receiver, the current pipe
	 * can then only be used for sending, and the receiver is presumed
	 * to be in the other half of a fork()'d process.
	 * 
	 * @see #endSender
	 */
	inline void endReceiver(void)
		{close(fd[0]);};

	/**
	 * Create a kernel pipe descriptor set using pipe().  On systems
	 * which allow the pipe kernel buffer to be defined, a size for
	 * aligned atomic transfers can be set, as well as the number of
	 * atomic objects the kernel will buffer.  On Posix systems, these
	 * options are ignored.
	 *
	 * @param objsize of atomic objects to send.
	 * @param count of atomic objects to kernel buffer.
	 */
	Pipe(int size = 512, int count = 1);

	/**
	 * Destroy the pipe and kernel descriptor resources.
	 */
	~Pipe();

	/**
	 * Create a pipe as a duplicate of an existing pipe.
	 * 
	 * @param orig pipe to duplicate.
	 */
	Pipe(const Pipe &orig);

	Pipe &operator=(const Pipe &orig);
	
public:
	/**
	 * Used to see if the pipe has any open entities.
	 */
	bool operator!();

	/**
	 * Perform an object atomic transfer of data from a pipe.
	 * 
	 * @return number of bytes actually read if successful.
	 * @param addr pointer to store read data.
	 * @param len number of bytes to read.
	 */
	inline int Recv(void *addr)
		{return ::read(fd[0], (char *)addr, objsize);};

	/**
	 * Perform an object atomic transfer of data to a pipe.
	 * 
	 * @return number of bytes written if successful.
	 * @param addr pointer to write data from.
	 * @param len number of butes to write.
	 */
	inline int Send(void *addr)
		{return ::write(fd[1], (char *)addr, objsize);};
};

/**
 * The DSO dynamic loader class is used to load object files.  On
 * elf based systems this is typically done with dlopen.  A dummy
 * stub class is generated for non-dl capable systems.
 *
 * @author David Sugar <dyfet@ostel.com>
 * @short Dynamic class file loader.
 */

class DSO 
{
private:
#ifdef	HAVE_MODULES
	static Mutex mutex;
	static DSO *first, *last;
	DSO *next, *prev;
	void *image;
#endif

public:
	/**
	 * Construct and load a DSO object file.
	 *
	 * @param pathname of object file to load.
	 */ 
#ifdef	HAVE_MODULES
	DSO(char *filename);
#else
	DSO(char *filename)
		{throw this;};
#endif

	/**
	 * Detach a DSO object from running memory.
	 */
#ifdef	HAVE_MODULES
	~DSO();
#endif

	/**
	 * Lookup a symbol in the loaded file.
	 */
#ifdef	HAVE_MODULES
	void *operator[](const char *);
#else
	void *operator[](const char *)
		{return NULL;};
#endif	

#ifdef	HAVE_MODULES
	friend void dynunload(void);
#else
	friend void dynunload(void)
		{return;};
#endif
};

inline int clear(LockedFile &f, pos_t pos, size_t len)
	{return f.Clear(pos, len);};

inline int append(LockedFile &f, void *buf, size_t len)
	{return f.Append(buf, len);};

inline int request(LockedFile &f, pos_t pos, void *buf, size_t len)
	{return f.Request(pos, buf, len);};

inline int update(LockedFile &f, pos_t pos, void *buf, size_t len)
	{return f.Update(pos, buf, len);};

#endif


Generated by: dyfet@home.sys on Tue Mar 7 09:18:03 200.