+/* $Id$
+ * lufs interface module file objects implementation for libcaptive
+ * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
+ *
+ * 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; exactly version 2 of June 1991 is required
+ *
+ * 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
+ */
+
+
+#include "config.h"
+
+#include <glib/gmessages.h>
+#include "captivefs-misc.h"
+#include "captivefs-attr.h"
+#include <fcntl.h>
+
+#include <captive/client-vfs.h>
+#include <captive/client-file.h>
+
+#include <lufs/fs.h>
+
+
+/* Read a file/dir's attributes
+ * Fill all relevant data into the fattr structure.
+ * The uid/gid fields are just ownership hints hints:
+ * != 0 => we own the file
+ * == 0 => we don't own it
+ * The credentials structure (if applicable and saved from _init)
+ * can help determine ownership based on remote uids/gids.
+ *
+ * Notes:
+ * If your filesysem doesn't natively support '.' or '..',
+ * don't forget to special-case them here.
+ * It is best to assume that name is a relative path, not an
+ * absolute one. Thus, you need to either be keeping track of the
+ * last accessed directory in readdir, or, as this code does, changing
+ * to the current directory there.
+ */
+int captivefs_stat(CaptiveVfsObject *captive_vfs_object,const char *name,struct lufs_fattr *fattr)
+{
+GnomeVFSResult errvfsresult;
+CaptiveFileObject *captive_file_object;
+GnomeVFSFileInfo file_info;
+
+ g_return_val_if_fail(CAPTIVE_VFS_IS_OBJECT(captive_vfs_object),-1);
+ g_return_val_if_fail(name!=NULL,-1);
+ g_return_val_if_fail(fattr!=NULL,-1);
+
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_stat: name=%s",name);
+
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_new_open(&captive_file_object,captive_vfs_object,name,GNOME_VFS_OPEN_READ);
+ G_UNLOCK(libcaptive);
+ if (errvfsresult!=GNOME_VFS_OK)
+ goto fail;
+
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_file_info_get(captive_file_object,&file_info);
+ G_UNLOCK(libcaptive);
+ if (errvfsresult!=GNOME_VFS_OK)
+ goto fail_unref;
+
+ G_LOCK(libcaptive);
+ g_object_unref(captive_file_object);
+ G_UNLOCK(libcaptive);
+
+ if (!captivefs_GnomeVFSFileInfo_to_lufs_fattr(fattr,&file_info))
+ return -1;
+
+ return 0;
+
+fail_unref:
+ G_LOCK(libcaptive);
+ g_object_unref(captive_file_object);
+ G_UNLOCK(libcaptive);
+fail:
+ return -1;
+}
+
+
+/* Create a file
+ */
+int captivefs_create(CaptiveVfsObject *captive_vfs_object,const char *file,int mode)
+{
+GnomeVFSResult errvfsresult;
+CaptiveFileObject *captive_file_object;
+
+ g_return_val_if_fail(CAPTIVE_VFS_IS_OBJECT(captive_vfs_object),-1);
+ g_return_val_if_fail(file!=NULL,-1);
+
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_create: file=%s,mode=0x%X",file,mode);
+
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_new_create(&captive_file_object,captive_vfs_object,file,GNOME_VFS_OPEN_WRITE,
+ FALSE, /* exclusive */
+ mode); /* perm */
+ G_UNLOCK(libcaptive);
+ if (errvfsresult!=GNOME_VFS_OK)
+ return -1;
+
+ G_LOCK(libcaptive);
+ g_object_unref(captive_file_object);
+ G_UNLOCK(libcaptive);
+
+ return 0;
+}
+
+
+/* Delete a file
+ */
+int captivefs_unlink(CaptiveVfsObject *captive_vfs_object,const char *file)
+{
+GnomeVFSResult errvfsresult;
+CaptiveFileObject *captive_file_object;
+
+ g_return_val_if_fail(CAPTIVE_VFS_IS_OBJECT(captive_vfs_object),-1);
+ g_return_val_if_fail(file!=NULL,-1);
+
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_unlink: file=%s",file);
+
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_new_open(&captive_file_object,captive_vfs_object,file,GNOME_VFS_OPEN_WRITE);
+ G_UNLOCK(libcaptive);
+ if (errvfsresult!=GNOME_VFS_OK)
+ goto fail;
+
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_remove(captive_file_object);
+ G_UNLOCK(libcaptive);
+ if (errvfsresult!=GNOME_VFS_OK)
+ goto fail_unref;
+
+ G_LOCK(libcaptive);
+ g_object_unref(captive_file_object);
+ G_UNLOCK(libcaptive);
+
+ return 0;
+
+fail_unref:
+ G_LOCK(libcaptive);
+ g_object_unref(captive_file_object);
+ G_UNLOCK(libcaptive);
+fail:
+ return -1;
+}
+
+
+/* Rename a file/dir
+ */
+int captivefs_rename(CaptiveVfsObject *captive_vfs_object,const char *old_name,const char *new_name)
+{
+GnomeVFSResult errvfsresult;
+CaptiveFileObject *captive_file_object;
+
+ g_return_val_if_fail(CAPTIVE_VFS_IS_OBJECT(captive_vfs_object),-1);
+ g_return_val_if_fail(old_name!=NULL,-1);
+ g_return_val_if_fail(new_name!=NULL,-1);
+
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_rename: old_name=%s,new_name=%s",old_name,new_name);
+
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_new_open(&captive_file_object,captive_vfs_object,old_name,GNOME_VFS_OPEN_WRITE);
+ G_UNLOCK(libcaptive);
+ if (errvfsresult!=GNOME_VFS_OK)
+ goto fail;
+
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_move(captive_file_object,new_name,
+ FALSE); /* force_replace */
+ G_UNLOCK(libcaptive);
+ if (errvfsresult!=GNOME_VFS_OK)
+ goto fail_unref;
+
+ G_LOCK(libcaptive);
+ g_object_unref(captive_file_object);
+ G_UNLOCK(libcaptive);
+
+ return 0;
+
+fail_unref:
+ G_LOCK(libcaptive);
+ g_object_unref(captive_file_object);
+ G_UNLOCK(libcaptive);
+fail:
+ return -1;
+}
+
+
+/* map: (const gchar *) -> (CaptiveFileObject *) */
+static GHashTable *FileHandle_hash;
+
+static void FileHandle_hash_value_destroy_func(CaptiveFileObject *captive_file_object)
+{
+ g_return_if_fail(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
+
+ g_object_unref(captive_file_object);
+}
+
+static void FileHandle_hash_init(void)
+{
+ if (FileHandle_hash)
+ return;
+ FileHandle_hash=g_hash_table_new_full(g_str_hash,g_str_equal,
+ (GDestroyNotify)g_free, /* key_destroy_func */
+ (GDestroyNotify)FileHandle_hash_value_destroy_func); /* value_destroy_func */
+}
+
+static CaptiveFileObject *FileHandle_lookup(const char *name)
+{
+CaptiveFileObject *captive_file_object;
+
+ g_return_val_if_fail(name!=NULL,NULL);
+
+ FileHandle_hash_init();
+ if (!(captive_file_object=g_hash_table_lookup(FileHandle_hash,name))) {
+ g_warning("FileHandle_lookup: FileHandle not found of: %s",name);
+ return NULL;
+ }
+
+ g_assert(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
+
+ return captive_file_object;
+}
+
+
+/* Open a file
+ *
+ * Notes:
+ * By default, LUFS has no concept of file handles. To implement file
+ * handles, take a look at the atbl class in sshfs - it is easy to cut&paste
+ * for use, and can be easily adapted for whatever purpose you need handles
+ * for.
+ *
+ * Unlike the POSIX open command which has both a "mode" variable and
+ * a "flags" variable, this only has a "mode" variable. To convert to the
+ * POSIX version, ->flags=mode^O_ACCMODE and ->mode=mode&O_ACCMODE.
+ */
+int captivefs_open(CaptiveVfsObject *captive_vfs_object,const char *file,unsigned mode)
+{
+CaptiveFileObject *captive_file_object;
+GnomeVFSOpenMode gnome_vfs_open_mode;
+GnomeVFSResult errvfsresult;
+
+ g_return_val_if_fail(CAPTIVE_VFS_IS_OBJECT(captive_vfs_object),-1);
+ g_return_val_if_fail(file!=NULL,-1);
+
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_open: file=%s,mode=0x%X",file,mode);
+
+ FileHandle_hash_init();
+ if ((captive_file_object=g_hash_table_lookup(FileHandle_hash,file))) {
+ g_assert(CAPTIVE_FILE_IS_OBJECT(captive_file_object));
+ g_warning("captivefs_open: FileHandle already exists of: %s",file);
+ return -1;
+ }
+
+ if (mode & (O_RDONLY|O_WRONLY|O_RDWR)) {
+ g_warning("captivefs_open: Unrecognized 'mode' 0x%X",mode);
+ return -1;
+ }
+ switch (mode & (O_RDONLY|O_WRONLY|O_RDWR)) {
+ case O_RDONLY: gnome_vfs_open_mode=GNOME_VFS_OPEN_READ; break;
+ case O_WRONLY: gnome_vfs_open_mode=GNOME_VFS_OPEN_WRITE; break;
+ case O_RDWR: gnome_vfs_open_mode=GNOME_VFS_OPEN_READ|GNOME_VFS_OPEN_WRITE; break;
+ default:
+ g_warning("captivefs_open: Unrecognized 'mode' 0x%X",mode);
+ return -1;
+ }
+
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_new_open(&captive_file_object,captive_vfs_object,file,
+ gnome_vfs_open_mode|GNOME_VFS_OPEN_RANDOM);
+ G_UNLOCK(libcaptive);
+ if (errvfsresult!=GNOME_VFS_OK)
+ return -1;
+
+ g_hash_table_insert(FileHandle_hash,g_strdup(file),captive_file_object);
+
+ return 0;
+}
+
+
+int captivefs_release(CaptiveVfsObject *captive_vfs_object,const char *file)
+{
+ g_return_val_if_fail(CAPTIVE_VFS_IS_OBJECT(captive_vfs_object),-1);
+ g_return_val_if_fail(file!=NULL,-1);
+
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_release: file=%s",file);
+
+ FileHandle_hash_init();
+ if (!g_hash_table_remove(FileHandle_hash,file)) {
+ g_warning("captivefs_release: FileHandle not found of: %s",file);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* Read from a file. Changed to use the (2) routines not for efficiency,
+ * but to make it work with 64-bit offsets :-(.
+ */
+int captivefs_read(CaptiveVfsObject *captive_vfs_object,const char *file,long long offset,unsigned long count,void *buf)
+{
+CaptiveFileObject *captive_file_object;
+GnomeVFSFileSize bytes_read;
+GnomeVFSResult errvfsresult;
+
+ g_return_val_if_fail(CAPTIVE_VFS_IS_OBJECT(captive_vfs_object),-1);
+ g_return_val_if_fail(file!=NULL,-1);
+ g_return_val_if_fail(buf!=NULL || count==0,-1);
+
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_read: file=%s,offset=%" G_GINT64_FORMAT ",count=0x%lX",
+ file,(gint64)offset,count);
+
+ if (!(captive_file_object=FileHandle_lookup(file)))
+ return -1;
+
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_seek(captive_file_object,GNOME_VFS_SEEK_START,offset);
+ G_UNLOCK(libcaptive);
+ if (errvfsresult!=GNOME_VFS_OK)
+ return -1;
+
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_read(captive_file_object,buf,count,&bytes_read);
+ G_UNLOCK(libcaptive);
+ if (errvfsresult!=GNOME_VFS_OK)
+ return -1;
+
+ if (bytes_read>INT_MAX) {
+ g_warning("captivefs_read: Read %" G_GUINT64_FORMAT " bytes > INT_MAX=%d; dropped data",
+ (guint64)bytes_read,INT_MAX);
+ bytes_read=INT_MAX;
+ }
+
+ return bytes_read;
+}
+
+
+/* Write to a file
+ */
+int captivefs_write(CaptiveVfsObject *captive_vfs_object,const char *file,long long offset,unsigned long count,const void *buf)
+{
+CaptiveFileObject *captive_file_object;
+GnomeVFSFileSize bytes_written;
+GnomeVFSResult errvfsresult;
+
+ g_return_val_if_fail(CAPTIVE_VFS_IS_OBJECT(captive_vfs_object),-1);
+ g_return_val_if_fail(file!=NULL,-1);
+ g_return_val_if_fail(buf!=NULL || count==0,-1);
+
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_write: file=%s,offset=%" G_GINT64_FORMAT ",count=0x%lX",
+ file,(gint64)offset,count);
+
+ if (!(captive_file_object=FileHandle_lookup(file)))
+ return -1;
+
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_seek(captive_file_object,GNOME_VFS_SEEK_START,offset);
+ G_UNLOCK(libcaptive);
+ if (errvfsresult!=GNOME_VFS_OK)
+ return -1;
+
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_write(captive_file_object,buf,count,&bytes_written);
+ G_UNLOCK(libcaptive);
+ if (errvfsresult!=GNOME_VFS_OK)
+ return -1;
+
+ if (bytes_written>INT_MAX) {
+ g_warning("captivefs_written: Written %" G_GUINT64_FORMAT " bytes > INT_MAX=%d; dropped data",
+ (guint64)bytes_written,INT_MAX);
+ bytes_written=INT_MAX;
+ }
+
+ return bytes_written;
+}
+
+
+/* Change a file/dir's attributes
+ */
+int captivefs_setattr(CaptiveVfsObject *captive_vfs_object,const char *file,const struct lufs_fattr *fattr)
+{
+GnomeVFSFileInfo file_info;
+GnomeVFSResult errvfsresult;
+CaptiveFileObject *captive_file_object;
+
+ g_return_val_if_fail(CAPTIVE_VFS_IS_OBJECT(captive_vfs_object),-1);
+ g_return_val_if_fail(file!=NULL,-1);
+ g_return_val_if_fail(fattr!=NULL,-1);
+
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_setattr: file=%s",file);
+
+ if (!captivefs_lufs_fattr_to_GnomeVFSFileInfo(&file_info,fattr))
+ return -1;
+
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_new_open(&captive_file_object,captive_vfs_object,file,GNOME_VFS_OPEN_WRITE);
+ G_UNLOCK(libcaptive);
+ if (errvfsresult!=GNOME_VFS_OK)
+ goto fail;
+
+ G_LOCK(libcaptive);
+ errvfsresult=captive_file_file_info_set(captive_file_object,&file_info,file_info.valid_fields);
+ G_UNLOCK(libcaptive);
+ if (errvfsresult!=GNOME_VFS_OK)
+ goto fail_unref;
+
+ G_LOCK(libcaptive);
+ g_object_unref(captive_file_object);
+ G_UNLOCK(libcaptive);
+
+ return 0;
+
+fail_unref:
+ G_LOCK(libcaptive);
+ g_object_unref(captive_file_object);
+ G_UNLOCK(libcaptive);
+fail:
+ return -1;
+}