/* wrapper.c - file and directory wrapper functions
Copyright (C) 2000 Maurer IT Systemlösungen KEG
The Gnome Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The Gnome Library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the Gnome Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
Author: Dietmar Maurer <dietmar@maurer-it.com>
*/
#include <string.h>
#include "efs_internal.h"
#if MAX_MEM_LEVEL >= 8
# define DEF_MEM_LEVEL 8
#else
# define DEF_MEM_LEVEL MAX_MEM_LEVEL
#endif
static gint
gzstream_get_byte (EFSFile *file)
{
GZStream *s = file->gzstream;
if (s->eof) return EOF;
if (s->stream.avail_in == 0) {
gint32 res;
res = file->efs->driver->fops->
file_read (file, s->inbuf, Z_BUFSIZE,
&s->stream.avail_in);
if (!res && !s->stream.avail_in) s->eof = 1;
else if (res) { s->err = Z_ERRNO; return EOF; }
s->stream.next_in = s->inbuf;
}
s->stream.avail_in--;
return *(s->stream.next_in)++;
}
static void
gzstream_put_long (EFSFile *file, guint32 x)
{
x = GUINT32_TO_LE(x);
file->efs->driver->fops->file_write (file, &x, 4);
}
static guint32
gzstream_get_long (EFSFile *file)
{
guint32 x = (guint32)gzstream_get_byte(file);
int c;
x += ((guint32)gzstream_get_byte(file))<<8;
x += ((guint32)gzstream_get_byte(file))<<16;
c = gzstream_get_byte(file);
if (c == EOF) file->gzstream->err = Z_DATA_ERROR;
x += ((guint32)c)<<24;
return x;
}
static gint
gzstream_flush (EFSFile *file, gint flush)
{
GZStream *s = file->gzstream;
guint len;
gint done = 0;
if (!(file->mode&EFS_WRITE)) return Z_STREAM_ERROR;
s->stream.avail_in = 0; /* should be zero already anyway */
for (;;) {
len = Z_BUFSIZE - s->stream.avail_out;
if (len != 0) {
if (file->efs->driver->fops->
file_write (file, s->outbuf, len))
return s->err = Z_ERRNO;
s->stream.next_out = s->outbuf;
s->stream.avail_out = Z_BUFSIZE;
}
if (done) break;
s->err = deflate(&(s->stream), flush);
/* Ignore the second of two consecutive flushes: */
if (len == 0 && s->err == Z_BUF_ERROR) s->err = Z_OK;
/* deflate has finished flushing only when it hasn't used up
* all the available space in the output buffer:
*/
done = (s->stream.avail_out != 0 || s->err == Z_STREAM_END);
if (s->err != Z_OK && s->err != Z_STREAM_END) break;
}
return s->err == Z_STREAM_END ? Z_OK : s->err;
}
static void
efs_destroy_file (EFSFile *file)
{
if (!file) return;
if (file->gzstream) {
if (file->gzstream->stream.state != NULL) {
if (file->mode&EFS_WRITE) {
deflateEnd(&(file->gzstream->stream));
} else {
inflateEnd(&(file->gzstream->stream));
}
}
if (file->gzstream->inbuf) g_free (file->gzstream->inbuf);
if (file->gzstream->outbuf) g_free (file->gzstream->outbuf);
if (file->gzstream) g_free (file->gzstream);
}
g_free (file);
}
static gint
gzstream_rewind (EFSFile *file)
{
GZStream *s = file->gzstream;
gint32 p;
s->err = Z_OK;
s->eof = 0;
s->stream.avail_in = 0;
s->stream.next_in = s->inbuf;
s->crc = crc32(0L, Z_NULL, 0);
inflateReset(&s->stream);
return file->efs->driver->fops->file_seek (file, 0, EFS_SEEK_SET, &p);
}
/**
* efs_node_open:
* @node: return value
* @parent: parent directory
* @path: filesystem path
* @flags: access flags
* @type: prefered node type (EFS_FILE or EFS_DIR or zero)
*
* Description: Opens or creates a new node. @path
* is simply the name of the node like "test.c". "./test.c" is not valid
* and will always return NULL.
*
* @flags is %EFS_READ or %EFS_RDWR which request opening the
* #EFSFile read-only or read/write. flags may also be bitwise-or'd
* with one or more of the following:
*
* %EFS_CREATE If the #EFSFile does not exist it will be created.
*
* %EFS_EXCL When used with %EFS_CREATE, if the #EFSFile already
* exists it is an error and the open will fail.
*
* %EFS_COMP Creates a compressed file when used with %EFS_CREATE. Compressed
* files are not seekable.
*
* %EFS_APPEND The file is opened in append mode. Before each write, the
* file pointer is positioned at the end of the file.
*
* Returns:
*/
EFSResult
efs_node_open (EFSNode **node, EFSDir *parent, const char *path,
gint flags, gint type)
{
EFSResult res;
g_return_val_if_fail (node != NULL, EFS_ERR_INVAL);
*node = NULL;
g_return_val_if_fail (parent != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (parent->mode & EFS_DIR, EFS_ERR_INVAL);
g_return_val_if_fail (path != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (!(flags&(~(EFS_RDWR|EFS_CREATE|EFS_COMP|
EFS_EXCL|EFS_APPEND))),
EFS_ERR_INVAL);
g_return_val_if_fail (!((flags&EFS_COMP)&&(flags&EFS_APPEND)),
EFS_ERR_INVAL);
g_return_val_if_fail (!(type&(~(EFS_FILE|EFS_DIR))),
EFS_ERR_INVAL);
if (!(parent->mode&EFS_WRITE)) flags &= ~(EFS_CREATE|EFS_WRITE);
if (flags&EFS_CREATE) flags |= EFS_WRITE;
if (flags&EFS_WRITE) flags |= EFS_READ;
if (!(flags&EFS_RDWR)) flags |= EFS_READ;
if ((flags&EFS_WRITE)&&(!(parent->efs->mode&EFS_WRITE)))
return EFS_ERR_PERM;
res = parent->efs->driver->fops->node_open (node, parent, path, flags,
type);
if (res) return res;
if (!(*node)) return EFS_ERR_INT;
if (type & EFS_DIR) return EFS_ERR_OK;
if ((*node)->mode&EFS_COMP) {
(*node)->gzstream = g_new0 (GZStream, 1);
(*node)->gzstream->err = Z_OK;
(*node)->gzstream->crc = crc32(0L, Z_NULL, 0);
if ((*node)->mode&EFS_WRITE) {
if (deflateInit2(&((*node)->gzstream->stream),
Z_DEFAULT_COMPRESSION,
Z_DEFLATED, -MAX_WBITS,
DEF_MEM_LEVEL,
Z_DEFAULT_STRATEGY) != Z_OK) {
efs_destroy_file (*node);
return EFS_ERR_INT;
}
(*node)->gzstream->stream.next_out =
(*node)->gzstream->outbuf =
g_malloc (Z_BUFSIZE);
} else {
(*node)->gzstream->stream.next_in =
(*node)->gzstream->inbuf =
g_malloc (Z_BUFSIZE);
if (inflateInit2(&((*node)->gzstream->stream),
-MAX_WBITS) != Z_OK) {
efs_destroy_file (*node);
return EFS_ERR_INT;
}
}
(*node)->gzstream->stream.avail_out = Z_BUFSIZE;
}
return EFS_ERR_OK;
}
/**
* efs_file_close
* @file: file descriptor
*
* Description: Closes the file.
*
* Returns: 0 upon success. -1 upon failure.
*/
EFSResult
efs_file_close (EFSFile *file)
{
EFSResult res;
g_return_val_if_fail (file != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (file->mode&EFS_FILE, EFS_ERR_INVAL);
if ((file->mode&EFS_COMP)&&(file->mode&EFS_WRITE)) {
GZStream *s = file->gzstream;
if (gzstream_flush (file, Z_FINISH) != Z_OK) {
file->efs->driver->fops->node_close (file);
efs_destroy_file (file);
return EFS_ERR_INT;
}
gzstream_put_long (file, s->crc);
}
res = file->efs->driver->fops->node_close (file);
efs_destroy_file (file);
return res;
}
/**
* efs_file_seek
* @file: file descriptor
* @offset:
* @whence:
*
* Description: This function repositions the offset of the @file
* to the argument @offset according to the directive whence as follows:
*
* %EFS_SEEK_SET The offset is set to offset bytes.
*
* %EFS_SEEK_CUR The offset is set to its current location plus offset bytes.
*
* %EFS_SEEK_END The offset is set to the size of the file plus offset bytes.
*
* Returns: Upon successful completion, lseek returns the resulting
* offset location as measured in bytes from the beginning of the file.
* Otherwise, a value of -1 is returned.
*/
EFSResult
efs_file_seek (EFSFile *file, gint32 offset, gint whence, guint32 *pos)
{
g_return_val_if_fail (file != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (file->mode&EFS_FILE, EFS_ERR_INVAL);
if (pos) *pos = 0;
if (file->mode&EFS_COMP) {
if (!((offset==0)&&(whence==EFS_SEEK_SET)))
return EFS_ERR_NOSEEK;
if (file->mode&EFS_WRITE) return EFS_ERR_NOSEEK;
return gzstream_rewind (file);
} else {
guint32 *p, tmp_pos;
if (pos) p = pos; else p = &tmp_pos;
return file->efs->driver->fops->
file_seek (file, offset, whence, p);
}
}
/**
* efs_file_tell
* @file: file descriptor
* @pos: return value
*
* Description: This function obtains the current value of the file
* position indicator for @file.
*
* Returns: the current position indicator, or -1 on error.
*/
EFSResult
efs_file_tell (EFSFile *file, guint32 *pos)
{
g_return_val_if_fail (file != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (file->mode&EFS_FILE, EFS_ERR_INVAL);
g_return_val_if_fail (pos != NULL, EFS_ERR_INVAL);
if (file->mode&EFS_COMP) {
if (file->mode&EFS_WRITE)
*pos = file->gzstream->stream.total_in;
else
*pos = file->gzstream->stream.total_out;
} else *pos = file->pos;
return EFS_ERR_OK;
}
/**
* efs_file_read
* @file: file descriptor
* @buf: pointer to a buffer to store the result
* @count: bytes to read
*
* Description: This function attempts to read up to @count bytes
* from @file into the buffer starting at @buf. If @count is zero it returns
* zero and has no other results.
*
* Returns: On success, the number of bytes read is returned (zero
* indicates end of file), and the file position is advanced by this number.
* On error, -1 is returned.
*/
EFSResult
efs_file_read (EFSFile *file, gpointer buf, gint32 count, guint32 *bytes_read)
{
gint32 br;
*bytes_read = 0;
g_return_val_if_fail (file != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (file->mode&EFS_FILE, EFS_ERR_INVAL);
g_return_val_if_fail (buf != NULL, EFS_ERR_INVAL);
if (!count) return EFS_ERR_OK;
if (file->mode&EFS_COMP) {
EFSResult res;
gpointer next_out;
gpointer start;
GZStream *s = file->gzstream;
s->stream.next_out = start = next_out = buf;
s->stream.avail_out = count;
while (s->stream.avail_out != 0) {
if (s->stream.avail_in == 0 && !s->eof) {
if (!(res = file->efs->driver->fops->
file_read(file,s->inbuf,Z_BUFSIZE,&br))){
s->stream.avail_in = br;
if (!br) s->eof = 1;
} else {
s->err = Z_ERRNO; break;
}
s->stream.next_in = s->inbuf;
}
s->err = inflate(&(s->stream), Z_NO_FLUSH);
if (s->err == Z_STREAM_END) {
/* Check CRC */
s->crc = crc32(s->crc, start, (uInt)
((guchar *) s->stream.next_out -
(guchar *) start));
start = s->stream.next_out;
if (gzstream_get_long(file) != s->crc)
s->err = Z_DATA_ERROR;
}
if (s->err != Z_OK || s->eof) break;
}
s->crc = crc32(s->crc, start, (uInt)
((guchar *) s->stream.next_out - (guchar *) start));
*bytes_read = count - s->stream.avail_out;
return EFS_ERR_OK;
} else
return file->efs->driver->fops->
file_read (file, buf, count, bytes_read);
}
/**
* efs_file_write
* @file: file descriptor
* @buf: pointer to the data
* @count: bytes to write
*
* Description: Writes up to @count bytes to @file from the buffer starting
* at @buf.
*
* Returns: On success, the number of bytes written are returned (zero
* indicates nothing was written). On error, -1 is returned.
*/
EFSResult
efs_file_write (EFSFile *file, gpointer buf, gint32 count)
{
g_return_val_if_fail (file != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (file->mode&EFS_FILE, EFS_ERR_INVAL);
g_return_val_if_fail (buf != NULL, EFS_ERR_INVAL);
if (!(file->mode&EFS_WRITE)) return EFS_ERR_PERM;
if (!(file->efs->mode&EFS_WRITE)) return EFS_ERR_PERM;
if (!count) return EFS_ERR_OK;
if (file->mode&EFS_COMP) {
EFSResult res = EFS_ERR_OK;
GZStream *s = file->gzstream;
s->stream.next_in = buf;
s->stream.avail_in = count;
while (s->stream.avail_in != 0) {
if (s->stream.avail_out == 0) {
s->stream.next_out = s->outbuf;
if ((res = file->efs->driver->fops->
file_write(file, s->outbuf,
Z_BUFSIZE))) {
s->err = Z_ERRNO;
break;
}
s->stream.avail_out = Z_BUFSIZE;
}
s->err = deflate(&(s->stream), Z_NO_FLUSH);
if (s->err != Z_OK) break;
}
s->crc = crc32(s->crc, (const Bytef *)buf, count);
if (s->stream.avail_in) return EFS_ERR_INT;
return EFS_ERR_OK;
} else
return file->efs->driver->fops->file_write (file, buf, count);
}
/**
* efs_file_trunc
* @file: file descriptor
* @size: new size
*
* Description: Causes the @file to be truncated at @size bytes.
* If the file previously was larger than this size, the extra data
* is lost. If the file previously was shorter, the file is left
* unchanged.
*
* Returns: On success the new file size is returned. On error, -1 is returned.
*/
EFSResult
efs_file_trunc (EFSFile *file, guint32 size)
{
EFSResult res;
g_return_val_if_fail (file != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (file->mode&EFS_FILE, EFS_ERR_INVAL);
if (!(file->mode&EFS_WRITE)) return EFS_ERR_PERM;
if (!(file->efs->mode&EFS_WRITE)) return EFS_ERR_PERM;
if (file->mode&EFS_COMP) {
if (size) return EFS_ERR_NOSEEK;
if ((res = file->efs->driver->fops->file_trunc (file, 0)))
return res;
return gzstream_rewind (file);
} else
return file->efs->driver->fops->file_trunc (file, size);
}
/**
* efs_type_set
* @file: file descriptor
* @type:
*
* Description: Set the type code of @file
*
* Returns:
*/
EFSResult
efs_type_set (EFSNode *node, guint32 type)
{
g_return_val_if_fail (node != NULL, EFS_ERR_INVAL);
if (!(node->mode&EFS_WRITE)) return EFS_ERR_PERM;
if (!(node->efs->mode&EFS_WRITE)) return EFS_ERR_PERM;
if (node->mode&EFS_ROOT) {
node->efs->type = type;
return EFS_ERR_OK;
} else {
return node->efs->driver->fops->type_set (node, type);
}
}
/**
* efs_type_get
* @file: file descriptor
* @type: return value
*
* Description: Get the type code of @file
*
* Returns:
*/
EFSResult
efs_type_get (EFSNode *node, guint32 *type)
{
g_return_val_if_fail (node != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (type != NULL, EFS_ERR_INVAL);
if (node->mode&EFS_ROOT) {
*type = node->efs->type;
return EFS_ERR_OK;
} else {
return node->efs->driver->fops->type_get (node, type);
}
}
/**
* efs_stat
* @dir: reference to a #EFS directory
* @path: file or directory name
* @stat: buffer to store the result
*
* Description: This functions return information about the specified
* file or directory. The result is stored in the @stat buffer.
*
* Returns: On success, zero is returned. On error, -1 is returned.
*/
EFSResult
efs_stat (EFSDir *dir, const char *path, EFSStat *stat)
{
EFSNode *node;
EFSResult res;
g_return_val_if_fail (dir != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (dir->mode&EFS_DIR, EFS_ERR_INVAL);
g_return_val_if_fail (path != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (stat != NULL, EFS_ERR_INVAL);
if ((res = dir->efs->driver->fops->node_open
(&node, dir, path, EFS_READ, 0))) return res;
res = dir->efs->driver->fops->node_stat (node, stat);
dir->efs->driver->fops->node_close (node);
return res;
}
/**
* efs_node_stat
* @node: reference to a file or directory
* @stat: buffer to store the result
*
* Description: This functions return information about the specified
* file or directory. The result is stored in the @stat buffer.
*
* Returns:
*/
EFSResult
efs_node_stat (EFSNode *node, EFSStat *stat)
{
g_return_val_if_fail (node != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (stat != NULL, EFS_ERR_INVAL);
return node->efs->driver->fops->node_stat (node, stat);
}
/**
* efs_erase:
* @dir: reference to a #EFS directory
* @path: file or directory name
*
* Description: Delete the file/directory it refers to.
*
* Returns: zero on success, or -1 if an error occurred.
*/
EFSResult
efs_erase (EFSDir *dir, const char *path)
{
g_return_val_if_fail (dir != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (dir->mode&EFS_DIR, EFS_ERR_INVAL);
g_return_val_if_fail (path != NULL, EFS_ERR_INVAL);
if (!(dir->mode&EFS_WRITE)) return EFS_ERR_PERM;
if (!(dir->efs->mode&EFS_WRITE)) return EFS_ERR_PERM;
return dir->efs->driver->fops->erase (dir, path);
}
/**
* efs_dir_close
* @dir: the directory to close.
*
* Description: Closes @dir. This function
* recurses its opened files/subdirs. This
* function can be called on the root #EFS
* after having called efs_commit.
*
* Returns: 0 upon success. -1 upon failure.
*/
EFSResult
efs_dir_close (EFSDir *dir)
{
g_return_val_if_fail (dir != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (dir->mode&EFS_DIR, EFS_ERR_INVAL);
g_return_val_if_fail (!(dir->mode&EFS_ROOT), EFS_ERR_INVAL);
return dir->efs->driver->fops->node_close (dir);
}
/**
* efs_dir_seek
* @dir: the directory to seek
* @offset:
*
* Description: This function sets the location in the directory
* stream from which the next efs_dir_read() call will start.
* efs_dir_seek() should be used with an offset returned by
* efs_dir_tell() or zero.
*
* Returns: 0 upon success. -1 upon failure.
*/
EFSResult
efs_dir_seek (EFSDir *dir, guint32 offset)
{
g_return_val_if_fail (dir != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (dir->mode&EFS_DIR, EFS_ERR_INVAL);
return dir->efs->driver->fops->dir_seek (dir, offset);
}
/**
* efs_dir_tell
* @dir: directory descriptor
* @pos: return value
*
* Description: This function obtains the current value of the file
* position indicator for @dir.
*
* Returns: the current position indicator, or -1 on error.
*/
EFSResult
efs_dir_tell (EFSDir *dir, guint32 *pos)
{
g_return_val_if_fail (dir != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (dir->mode&EFS_DIR, EFS_ERR_INVAL);
g_return_val_if_fail (pos != NULL, EFS_ERR_INVAL);
*pos = dir->pos;
return EFS_ERR_OK;
}
/**
* efs_dir_read
* @dir: the directory to close.
* @de: a place to store the result
*
* Description: allows you to browse @dir content.
*
* Returns: it will return a positive value and store the next
* #EFSDirEntry in @de till all of them have been returned. It will
* return zero after the last #EFSDirEntry has been returned, or -1 on error.
*/
EFSResult
efs_dir_read (EFSDir *dir, EFSDirEntry *de)
{
g_return_val_if_fail (dir != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (dir->mode&EFS_DIR, EFS_ERR_INVAL);
g_return_val_if_fail (de != NULL, EFS_ERR_INVAL);
return dir->efs->driver->fops->dir_read (dir, de);
}
/**
* efs_rename:
* @dir: reference to a #EFS directory
* @old_path: file or directory name
* @new_path: new name
*
* Description: Rename a file or directory.
*
* Returns: zero on success, or -1 if an error occurred.
*/
EFSResult
efs_rename (EFSDir *dir, const char *old_path, const char *new_path)
{
g_return_val_if_fail (dir != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (dir->mode&EFS_DIR, EFS_ERR_INVAL);
g_return_val_if_fail (old_path != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (new_path != NULL, EFS_ERR_INVAL);
if (!(dir->mode&EFS_WRITE)) return EFS_ERR_PERM;
if (!(dir->efs->mode&EFS_WRITE)) return EFS_ERR_PERM;
if (!strcmp (old_path, new_path)) return EFS_ERR_OK;
return dir->efs->driver->fops->rename (dir, old_path, new_path);
}
/**
* efs_node_equal:
* @node1: reference to a node
* @node2: reference to a node
*
* Description: Check if the nodes are equal.
*
* Returns: TRUE if the nodes are equal, FALSE otherwise.
*/
gboolean
efs_node_equal (EFSNode *node1, EFSNode *node2)
{
g_return_val_if_fail (node1 != NULL, FALSE);
g_return_val_if_fail (node2 != NULL, FALSE);
if (node1->efs != node2->efs) return FALSE;
return node1->efs->driver->fops->node_equal (node1, node2);
}
syntax highlighted by Code2HTML, v. 0.9.1