/* efs.c - efs file access 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 <config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <ctype.h>
#include <sys/file.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif
#include "efs_internal.h"
#include "ib1_driver.h"
gint efs_errno;
static EFSDriver *efs_driver_list[] = {
&efs_driver_ib1,
&efs_driver_ib1_enc,
NULL
};
static gboolean
free_hash_node (gpointer key, gpointer value, gpointer user_data)
{
if (key) g_free (key);
return TRUE;
}
static void
efs_typehash_clear (EFS *efs)
{
if (efs->s2i_hash) {
g_hash_table_foreach_remove (efs->s2i_hash, free_hash_node,
NULL);
g_hash_table_destroy (efs->s2i_hash);
efs->s2i_hash = NULL;
}
if (efs->i2s_hash) {
g_hash_table_destroy (efs->i2s_hash);
efs->i2s_hash = NULL;
}
}
gchar*
efs_strerror(EFSResult errnum)
{
switch (errnum) {
case EFS_ERR_NOSEEK: return "seek not possible";
case EFS_ERR_PERM: return "operation not permitted";
case EFS_ERR_NODRIVER: return "no driver found";
case EFS_ERR_NOENT: return "no such file or directory";
case EFS_ERR_LOCKED: return "file is locked";
case EFS_ERR_INVPASS: return "invalid password";
case EFS_ERR_FORMAT: return "wrong header format";
case EFS_ERR_ERRNO: return "unix system IO error";
case EFS_ERR_NOTFILE: return "not a file";
case EFS_ERR_NOTDIR: return "not a directory";
case EFS_ERR_NOTEMPTY: return "directory not empty";
case EFS_ERR_EXISTS: return "file exists";
case EFS_ERR_INVAL: return "invalid argument";
case EFS_ERR_OK: return "everithing is ok";
case EFS_ERR_INT: return "internal error";
case EFS_ERR_NOTYPE: return "no such type";
default: return "unknown error";
}
}
EFSDriver*
efs_find_driver (gchar *drivername)
{
EFSDriver **h;
gint i;
h = efs_driver_list;
i = 0;
while (h[i] && g_strcasecmp(drivername, h[i]->drivername)) i++;
return h[i];
}
gboolean
efs_passwd_compare (EFSHeader *head, gchar *passwd)
{
gchar *epw;
epw = crypt (passwd, head->epw);
if (strncmp(epw, head->epw, sizeof (head->epw))) return FALSE;
return TRUE;
}
void
efs_passwd_set (EFSHeader *head, gchar *passwd)
{
gchar *epw;
gint i;
guint32 seed[2];
gchar salt[] = "$1$........";
const char *const seedchars =
"./0123456789ABCDEFGHIJKLMNOPQRST"
"UVWXYZabcdefghijklmnopqrstuvwxyz";
/* copied from the crypt manual */
/* Generate a (not very) random seed. */
seed[0] = time(NULL);
seed[1] = getpid() ^ (seed[0] >> 14 & 0x30000);
/* Turn it into printable characters from `seedchars'. */
for (i = 0; i < 8; i++)
salt[3+i] = seedchars[(seed[i/5] >> (i%5)*6) & 0x3f];
epw = crypt (passwd, salt);
strncpy (head->epw, epw, sizeof (head->epw));
}
EFSResult
efs_open_full (EFSDir **dir, const char *path, gint flags, gint mode,
EFSPassFunc pass_func, gchar *passwd)
{
EFSResult res;
EFSHeader *head;
EFSDriver *driver;
gint fd, i, j;
struct stat sb;
gchar buf[512], drivername[1024], lockname[1024];
const gchar *filename;
gchar *pw = passwd;
g_return_val_if_fail (dir != NULL, EFS_ERR_INVAL);
*dir = NULL;
g_return_val_if_fail (path != NULL, EFS_ERR_INVAL);
if (flags&EFS_CREATE) flags |= EFS_RDWR;
if (flags&EFS_WRITE) flags |= EFS_READ;
/* extract driver and filename */
i = 0; while ((i<1024)&&path[i]&&isalnum((int)path[i])) i++;
filename = path;
drivername[0] = 0;
if (path[i]==':') {
j=i; while (path[j]==':') j++;
strncpy (drivername, path, i);
drivername[i] = 0;
filename = &path[j];
}
strcpy (lockname, filename);
strcpy (&lockname[strlen(filename)], ".WRITELOCK");
driver = NULL;
if (!(stat (filename, &sb))) { /* exists */
if ((flags&EFS_CREATE)&&(flags&EFS_EXCL))
return EFS_ERR_EXISTS;
if (!S_ISREG(sb.st_mode)) return EFS_ERR_NOTFILE;
/* detect driver */
if (!(fd = open(filename, O_RDONLY))) return EFS_ERR_ERRNO;
if (!((read (fd, buf, 512))==512)) {
close(fd);
return EFS_ERR_ERRNO;
}
close(fd);
head = (EFSHeader *)buf;
if (strncmp(head->efs_id, EFS_FILE_ID, 4))
return EFS_ERR_FORMAT;
strncpy (drivername, head->drivername, 12);
drivername[12] = 0;
if (!(driver = efs_find_driver(drivername)))
return EFS_ERR_NODRIVER;
if (head->protected & !pw) {
if (!pass_func) return EFS_ERR_INVPASS;
if (!(pw = pass_func (filename, FALSE)))
return EFS_ERR_INVPASS;
}
if (head->protected && !driver->encrypted &&
!efs_passwd_compare(head, pw)) return EFS_ERR_INVPASS;
if ((flags&EFS_WRITE)&& (res = efs_lock_create(lockname)))
return res;
res = driver->sops->open (dir, driver, filename, flags, pw);
} else { /* file does not exist */
if (!(flags&EFS_CREATE)) return EFS_ERR_NOENT;
if (errno != ENOENT) return EFS_ERR_ERRNO;
if (drivername[0] &&
(!(driver = efs_find_driver(drivername)))) {
return EFS_ERR_NODRIVER;
}
if (!driver) driver = efs_driver_list [0]; /* default */
if ((flags&EFS_PROT) && !pw) {
if (!pass_func) return EFS_ERR_INVPASS;
if (!(pw = pass_func (filename, TRUE)))
return EFS_ERR_INVPASS;
}
if ((flags&EFS_WRITE) && (res = efs_lock_create(lockname)))
return res;
res = driver->sops->create(dir,driver,filename,flags,mode,pw);
}
if (*dir) {
if (flags&EFS_WRITE) {
(*dir)->efs->lockname = g_strdup(lockname);
(*dir)->efs->mode = EFS_RDWR;
} else (*dir)->efs->mode = EFS_READ;
(*dir)->mode |= EFS_DIR|EFS_ROOT|(flags&EFS_RDWR);
(*dir)->efs->root = *dir;
} else if (flags&EFS_WRITE) efs_lock_remove (lockname);
return res;
}
/**
* efs_open:
* @dir: return value
* @path: filesystem path
* @flags: access flags
* @mode: file permissions
* @passwd: a password for protected files
*
* Description: Opens or creates a new #EFS within file @path.
*
* @flags is %EFS_READ or %EFS_RDWR which request opening the #EFS read-only
* or read/write. flags may also be bitwise-or'd with one or more of the
* following:
*
* %EFS_CREATE If the #EFS does not exist it will be created.
*
* %EFS_EXCL When used with %EFS_CREATE, if the #EFS already exists it is an
* error and the open will fail.
*
* @mode specifies the permissions to use if a new file is created.
* You can specify them in octal with 0700 which gives 700 permission
* to the file created.
*
* Returns: EFSResult
*/
EFSResult
efs_open (EFSDir **dir, const char *path, gint flags, gint mode,
gchar *passwd)
{
return efs_open_full (dir, path, flags, mode, NULL, passwd);
}
EFSResult
efs_open_cb (EFSDir **dir, const char *path, gint flags, gint mode,
EFSPassFunc pass_func)
{
return efs_open_full (dir, path, flags, mode, pass_func, NULL);
}
/**
* efs_close:
* @root: reference to the #EFS root directory
*
* Description: Closes the #EFS and frees all resources.
*
* Returns: zero on success, or -1 if an error occurred.
*/
EFSResult
efs_close (EFSDir *root)
{
EFSResult res;
g_return_val_if_fail (root != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (root->mode&EFS_ROOT, EFS_ERR_INVAL);
efs_typehash_clear (root->efs);
if (root->efs->lockname) {
efs_lock_remove (root->efs->lockname);
g_free (root->efs->lockname);
root->efs->lockname = NULL;
}
if (!(res = root->efs->driver->sops->close (root))) g_free (root);
return res;
}
/**
* efs_commit:
* @root: reference to the #EFS root directory
*
* Description: Synchronize data on disk with memory.
*
* Returns: zero on success, or -1 if an error occurred.
*/
EFSResult
efs_commit (EFSDir *root)
{
g_return_val_if_fail (root != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (root->mode&EFS_ROOT, EFS_ERR_INVAL);
if (!(root->efs->mode&EFS_WRITE)) return EFS_ERR_PERM;
efs_typehash_clear (root->efs);
return root->efs->driver->sops->commit (root);
}
/**
* efs_revert:
* @root: reference to the #EFS root directory
*
* Description: undo all changes since last commit
*
* Returns: zero on success, or -1 if an error occurred.
*/
EFSResult
efs_revert (EFSDir *root)
{
g_return_val_if_fail (root != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (root->mode&EFS_ROOT, EFS_ERR_INVAL);
if (!(root->efs->mode&EFS_WRITE)) return EFS_ERR_PERM;
efs_typehash_clear (root->efs);
return root->efs->driver->sops->revert (root);
}
/**
* efs_fsstat:
* @root: reference to the #EFS root directory
* @fsstat: a buffer to store the results
*
* Description: get information about the #EFS file system.
*
* Returns: zero on success, or -1 on failure.
*/
EFSResult
efs_fsstat (EFSDir *root, EFSFSStat *fsstat)
{
g_return_val_if_fail (root != NULL, EFS_ERR_INVAL);
g_return_val_if_fail (root->mode&EFS_ROOT, EFS_ERR_INVAL);
g_return_val_if_fail (fsstat != NULL, EFS_ERR_INVAL);
return root->efs->driver->sops->fsstat (root, fsstat);
}
/**
* efs_node_close
* @node: file or directory descriptor
*
* Description: closes the file or directory descriptor.
*
*/
EFSResult
efs_node_close (EFSNode *node)
{
g_return_val_if_fail (node != NULL, EFS_ERR_INVAL);
if (node->mode & EFS_FILE)
return efs_file_close (node);
else
return efs_dir_close (node);
}
/**
* efs_file_open:
*
* see efs_node_open()
*/
EFSResult
efs_file_open (EFSFile **file, EFSDir *dir, const char *path, gint flags)
{
return efs_node_open (file, dir, path, flags, EFS_FILE);
}
/**
* efs_dir_open:
*
* see efs_node_open()
*/
EFSResult
efs_dir_open (EFSDir **dir, EFSDir *parent, const char *path, gint flags)
{
return efs_node_open (dir, parent, path, flags, EFS_DIR);
}
syntax highlighted by Code2HTML, v. 0.9.1