/*

    File: rfs_dir.c

    Copyright (C) 1998-2007 Christophe GRENIER <grenier@cgsecurity.org>
    Some code from Yury Umanets <torque@ukrpost.net>
  
    This software 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 the Free Software Foundation, Inc., 51
    Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include "types.h"
#include "common.h"
#include "intrf.h"
#include "rfs.h"
#include "dir.h"
#include "rfs_dir.h"
#include "log.h"

#ifdef HAVE_LIBREISERFS
#include "dal/dal.h"
#ifdef HAVE_DAL_FILE_DAL_H
#include "dal/file_dal.h"
#endif
#ifdef HAVE_DAL_FILE_H
#include "dal/file.h"
#endif
#include "reiserfs/reiserfs.h"

struct rfs_dir_struct {
	file_data_t *dir_list;
	file_data_t *current_file;
	reiserfs_fs_t *current_fs;
	dal_t *dal;
	int flags;
};
static file_data_t *reiser_dir(disk_t *disk_car, const partition_t *partition, dir_data_t *dir_data, const unsigned long int cluster);
static void dir_partition_reiser_close(dir_data_t *dir_data);

#ifdef HAVE_STRUCT_DAL_OPS_DEV
dev_t dal_dev(dal_t *dal) {
    
    if (!dal)
	return 0;
    return (dev_t)dal->dev;
}
#endif

#ifdef HAVE_STRUCT_DAL_OPS_DEV
size_t dal_block_size(dal_t *dal) {
    if (!dal) return 0;
#ifdef HAVE_DAL_T_BLOCK_SIZE
    return dal->block_size;
#else
    return dal->blocksize;
#endif
}
#else
unsigned dal_get_blocksize(dal_t *dal) {
  if (!dal)
    return 0;
#ifdef HAVE_DAL_T_BLOCK_SIZE
    return dal->block_size;
#else
    return dal->blocksize;
#endif
}
#endif

/*
size_t dal_blocksize(dal_t *dal) {

	if (!dal) return 0;
#ifdef HAVE_DAL_T_BLOCK_SIZE
    return dal->block_size;
#else
    return dal->blocksize;
#endif

}
*/

char *dal_error(dal_t *dal) {
#ifdef HAVE_DAL_T_ERROR
    return dal->error;
#else
    return "";
#endif
}

static int file_read(dal_t *dal, void *buff, blk_t block, blk_t count) {
  uint64_t off;
  unsigned int blocklen;
  my_data_t *my_data=(my_data_t*)dal->data;
/* log_trace("reiser file_read(dal=%p,buff=%p,block=%ld, count=%ld)\n",dal,buff,block,count); */
  if (!dal || !buff)
    return 0;
#ifdef HAVE_DAL_T_BLOCK_SIZE
  off = (uint64_t)block * (uint64_t)dal->block_size;
  blocklen = count * dal->block_size;
#else
  off = (uint64_t)block * (uint64_t)dal->blocksize;
  blocklen = count * dal->blocksize;
#endif
/* log_debug("blocklen=%ld\n",blocklen); */
  if(my_data->disk_car->read(my_data->disk_car,blocklen,buff,my_data->partition->part_offset+off))
    return 0;
  return 1;
}

static int file_write(dal_t *dal, void *buff, blk_t block, blk_t count)
{
  uint64_t off, blocklen;
  if (!dal || !buff)
    return 0;
#ifdef HAVE_DAL_T_BLOCK_SIZE
  off = (uint64_t)block * (uint64_t)dal->block_size;
  blocklen = (uint64_t)count * (uint64_t)dal->block_size;
#else
  off = (uint64_t)block * (uint64_t)dal->blocksize;
  blocklen = (uint64_t)count * (uint64_t)dal->blocksize;
#endif
#ifdef ENABLE_REISERFS_WRITE
  if(my_data->disk_car->write(my_data->disk_car,blocklen,buff,my_data->partition->part_offset+off))
    return 0;
  return 1;
#else
  log_info("reiser file_write not implemented\n");
  return 0;
#endif
}

static int file_sync(dal_t *dal)
{
  if (!dal) return 0;
/* log_trace("reiser file_sync\n"); */
  return 1;
}

static int file_flags(dal_t *dal)
{
/* log_trace("reiser file_flags\n"); */
  if (!dal) return 0;
  return dal->flags;
}

static int file_equals(dal_t *dal1, dal_t *dal2)
{
/* log_trace("reiser file_equals\n"); */
  if (!dal1 || !dal2)
    return 0;
/* return !strcmp((char *)dal1->data, (char *)dal2->data); */
  {
    my_data_t *data1=(my_data_t*)dal1->data;
    my_data_t *data2=(my_data_t*)dal2->data;
    return (data1->disk_car==data2->disk_car) && (data1->partition==data2->partition) && (data1->offset==data2->offset);
  }
}

#ifdef HAVE_STRUCT_DAL_OPS_DEV
static int file_stat(dal_t *dal,struct stat *st)
{
/* log_trace("reiser file_stat\n"); */
  if (!dal)
    return 0;
/* return (unsigned int)st.st_dev; */
  return 1;
}
#else
static unsigned int file_stat(dal_t *dal)
{
/* log_trace("reiser file_stat\n"); */
  if (!dal)
    return 0;
/* return (unsigned int)st.st_dev; */
  return 1;
}
#endif

#ifdef HAVE_STRUCT_DAL_OPS_DEV
static blk_t   file_len(dal_t *dal) {
#else
static count_t file_len(dal_t *dal) {
#endif
  my_data_t *my_data=(my_data_t*)dal->data;
/* log_trace("reiser file_len\n"); */
  if (!dal) return 0;
#ifdef HAVE_DAL_T_BLOCK_SIZE
  return my_data->partition->part_size / dal->block_size;
#else
  return my_data->partition->part_size / dal->blocksize;
#endif
}

static struct dal_ops ops = {
	read: file_read, 
	write: file_write, 
	sync: file_sync, 
	flags: file_flags, 
	equals: file_equals, 
	stat: file_stat, 
	len: file_len,
#ifdef HAVE_STRUCT_DAL_OPS_DEV
	dev: dal_dev
#endif
};

void dal_close(dal_t *dal) {
/* log_trace("reiser dal_close\n"); */
	
	if (!dal) return;
	
	dal->ops = NULL;
	free(dal->data);
	dal->data = NULL;
	free(dal);
}

void file_close(dal_t *dal) {
/* log_trace("reiser file_close\n"); */
	if (!dal) return;
	dal_close(dal);
}

static int power_of_two(unsigned long value) {
	return (value & -value) == value;
}

#ifdef HAVE_STRUCT_DAL_OPS_DEV
dal_t *dal_open(struct dal_ops *myops, const void *dev, size_t blocksize, int flags, void *data)
#else
dal_t *dal_open(struct dal_ops *myops, unsigned blocksize, int flags, void *data) 
#endif
{
	dal_t *dal;
/* log_trace("reiser dal_open\n"); */
	
	if (!myops) return NULL;
	
	if (!power_of_two(blocksize)) {
		fprintf(stderr, "Block size isn't power of two.\n");
		return NULL;
	}	
	
	if (!(dal = (dal_t *)MALLOC(sizeof(*dal))))
		return NULL;

	memset(dal, 0, sizeof(*dal));
	
	dal->ops = myops;
#ifdef HAVE_DAL_T_BLOCK_SIZE
	dal->block_size = blocksize;
#else
	dal->blocksize = blocksize;
#endif
#ifdef HAVE_DAL_T_ENTITY
	dal->entity= NULL;
#endif
#ifdef HAVE_DAL_T_NAME
	strncpy(dal->name, "/dev/reiserfs",sizeof(dal->name));
#endif
#ifdef HAVE_DAL_T_ERROR
	dal->error[0]=0;
#endif
	dal->data = data;
	dal->flags = flags;
	return dal;
}

#ifdef HAVE_STRUCT_DAL_OPS_DEV
int dal_set_block_size(dal_t *dal, size_t blocksize) {

	if (!dal) return 0;
	
	if (!power_of_two(blocksize))
		return 0;
#ifdef HAVE_DAL_T_BLOCK_SIZE
	dal->block_size = blocksize;
#else
	dal->blocksize = blocksize;
#endif
	return 1;
}
#else
int dal_set_blocksize(dal_t *dal, unsigned blocksize) {

	if (!dal) return 0;
	
	if (!power_of_two(blocksize))
		return 0;
#ifdef HAVE_DAL_T_BLOCK_SIZE
	dal->block_size = blocksize;
#else
	dal->blocksize = blocksize;
#endif

	return 1;
}
#endif


int dal_read(dal_t *dal, void *buff, blk_t block, blk_t count) {

/* log_trace("reiser dal_read\n"); */
	if (!dal) return 0;

	if (dal->ops->read)
		return dal->ops->read(dal, buff, block, count);
	
	return 0;
}

int dal_write(dal_t *dal, void *buff, blk_t block, blk_t count) {

/* log_trace("reiser dal_write\n"); */
	if (!dal) return 0;
	
	if (dal->ops->write)
		return dal->ops->write(dal, buff, block, count);
		
	return 0;
}
	
int dal_sync(dal_t *dal) {

	if (!dal) return 0;

	if (dal->ops->sync)
		return dal->ops->sync(dal);
	
	return 0;	
}

int dal_flags(dal_t *dal) {

	if (!dal) return 0;

	if (dal->ops->flags)
		return dal->ops->flags(dal);
	
	return 0;
}

int dal_equals(dal_t *dal1, dal_t *dal2) {
/* log_trace("reiserfs dal_equals\n"); */
	
	if (!dal1 || !dal2) return 0;

	if (dal1->ops->equals)
		return dal1->ops->equals(dal1, dal2);
	
	return 0;	
}

#if defined(HAVE_STRUCT_DAL_OPS_DEV)
int dal_stat(dal_t *dal, struct stat *mystat)
{
/* log_trace("reiserfs dal_stat\n"); */
	if (!dal) return 0;
	if (dal->ops->stat)
		return dal->ops->stat(dal,mystat);
	return 0;
}
#else
unsigned int dal_stat(dal_t *dal) {
/* log_trace("reiserfs dal_stat\n"); */
	if (!dal) return 0;
	if (dal->ops->stat)
		return dal->ops->stat(dal);
	return 0;
}
#endif

blk_t dal_len(dal_t *dal) {
/* log_trace("reiserfs dal_len\n"); */
	
	if (!dal) 
		return 0;

	if (dal->ops->len)
		return dal->ops->len(dal);

	return 0;
}

static file_data_t *reiser_dir(disk_t *disk_car, const partition_t *partition, dir_data_t *dir_data, const unsigned long int cluster)
{
  struct rfs_dir_struct *ls=(struct rfs_dir_struct*)dir_data->private_dir_data;
  reiserfs_dir_t *dir;
  reiserfs_dir_entry_t entry;
  ls->dir_list=NULL;
  ls->current_file=NULL;
  if (!(dir = reiserfs_dir_open(ls->current_fs, dir_data->current_directory))) {
    aff_buffer(BUFFER_ADD,"Couldn't open dir\n");
    log_error("Couldn't open dir\n");
    return NULL;
  }
  while (reiserfs_dir_read(dir, &entry))
  {
    unsigned int thislen;
    char name[MAX_NAME_LEN(DEFAULT_BLOCK_SIZE)];
    reiserfs_object_t *entity;
    strncpy(name,dir_data->current_directory,sizeof(name));
    strcat(name,"/");
    strcat(name,entry.de_name);
    if((entity=reiserfs_object_create(ls->current_fs,name,1)))
    {
      file_data_t *new_file=MALLOC(sizeof(*new_file));
      thislen=(MAX_NAME_LEN(DEFAULT_BLOCK_SIZE)<DIR_NAME_LEN?MAX_NAME_LEN(DEFAULT_BLOCK_SIZE):DIR_NAME_LEN);
      memcpy(new_file->name,entry.de_name,thislen);
      new_file->name[thislen-1]='\0';

      new_file->prev=ls->current_file;
      new_file->next=NULL;
      new_file->filestat.st_size=entity->stat.st_size;
      memcpy(&new_file->filestat,&entity->stat,sizeof(new_file->filestat));
      reiserfs_object_free(entity);
      if(ls->current_file)
	ls->current_file->next=new_file;
      else
	ls->dir_list=new_file;
      ls->current_file=new_file;
    }
  }
  reiserfs_dir_close(dir);
  return ls->dir_list;
}

static void dir_partition_reiser_close(dir_data_t *dir_data)
{
  struct rfs_dir_struct *ls=(struct rfs_dir_struct*)dir_data->private_dir_data;
  reiserfs_fs_close(ls->current_fs);
  file_close(ls->dal);
  free(ls);
}
#endif

int dir_partition_reiser_init(disk_t *disk_car, const partition_t *partition, dir_data_t *dir_data, const int debug)
{
#ifdef HAVE_LIBREISERFS
  dal_t *dal;
  reiserfs_fs_t *fs;
  reiserfs_path_node_t *leaf;
  my_data_t *my_data;
  int32_t offset = 0;
  int error = 0;
  my_data=MALLOC(sizeof(*my_data));
  my_data->partition=partition;
  my_data->disk_car=disk_car;
  my_data->offset=0;
#ifdef HAVE_STRUCT_DAL_OPS_DEV
  dal=dal_open(&ops, "",DEFAULT_BLOCK_SIZE, O_RDONLY, my_data);
#else
  dal=dal_open(&ops, DEFAULT_BLOCK_SIZE, O_RDONLY, my_data);
#endif
  if (!dal)
  {
    log_error("Couldn't open device\n");
    free(my_data);
    return -1;
  }
  /* log_debug("file_open ok\n"); */

#ifdef HAVE_REISERFS_FS_OPEN_FAST
  if (!(fs = reiserfs_fs_open_fast(dal, dal)))
#else
    if (!(fs = reiserfs_fs_open(dal, dal)))
#endif
    {
      log_error("Couldn't open reiser filesystem %s\n",dal_error(dal));
      /* file_close call free(my_data) */
      file_close(dal);
      return -1;
    }
  /* log_debug("reiserfs_fs_open_fast ok\n"); */
  {
    struct rfs_dir_struct *ls=(struct rfs_dir_struct *)MALLOC(sizeof(*ls));
    ls->dir_list=NULL;
    ls->current_file=NULL;
    ls->current_fs=fs;
    ls->dal=dal;
    ls->flags = 0; /*DIRENT_FLAG_INCLUDE_EMPTY; */
    strncpy(dir_data->current_directory,"/",sizeof(dir_data->current_directory));
    dir_data->current_inode=2;
    dir_data->debug=debug;
    dir_data->get_dir=reiser_dir;
    dir_data->copy_file=NULL;
    dir_data->close=&dir_partition_reiser_close;
    dir_data->local_dir=NULL;
    dir_data->private_dir_data=ls;
  }
  return 0;
#else
  return -2;
#endif
}

const char*td_reiserfs_version(void)
{
#ifdef HAVE_LIBREISERFS
  return libreiserfs_get_version();
#else
  return "none";
#endif
}


syntax highlighted by Code2HTML, v. 0.9.1