/*
    file.c -- standard file dal abstraction.
    Copyright (C) 2001, 2002 Yury Umanets.
*/

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

#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#if defined(__freebsd__)
#  define O_LARGEFILE 0
#  include <sys/disk.h>
#endif

#ifndef DJGPP
#  include <sys/stat.h>
#endif

#include <dal/dal.h>

static void file_save_error(dal_t *dal) {
    char *error;

    memset(dal->error, 0, sizeof(dal->error));
    
    if ((error = strerror(errno)))
	memcpy(dal->error, error, strlen(error));
}

static int file_read(dal_t *dal, void *buff, blk_t block, count_t count) {
    off_t off, len;
	
    if (!dal || !buff)
    	return 0;
	
    off = (off_t)block * (off_t)dal->blocksize;
    if (lseek(*((int *)dal->entity), off, SEEK_SET) == (off_t)-1) {
	file_save_error(dal);
	return 0;
    }

    len = (off_t)(count * dal->blocksize);
    if (read(*((int *)dal->entity), buff, len) <= 0) {
	file_save_error(dal);
	return 0;
    }
    
    return 1;
}

static int file_write(dal_t *dal, void *buff, blk_t block, count_t count) {
    off_t off, len;
	
    if (!dal || !buff)
	return 0;
	
    off = (off_t)block * (off_t)dal->blocksize;
    
    if (lseek(*((int *)dal->entity), off, SEEK_SET) == (off_t)-1) {
	file_save_error(dal);
	return 0;
    }
    
    len = (off_t)count * (off_t)dal->blocksize;
    if (write((*(int *)dal->entity), buff, len) <= 0) {
	file_save_error(dal);
	return 0;
    }
    
    return 1;
}

static int file_sync(dal_t *dal) {

    if (!dal) 
	return 0;
	
    if (fsync(*((int *)dal->entity))) {
	file_save_error(dal);
	return 0;
    }
    
    return 1;
}

static int file_flags(dal_t *dal) {

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

static int file_equals(dal_t *dal1, dal_t *dal2) {

    if (!dal1 || !dal2)
	return 0;
	  
    return !strcmp((char *)dal1->data, (char *)dal2->data);
}

static unsigned int file_stat(dal_t *dal) {
#ifdef DJGPP
    if (!dal) 
	return 0;
    
    return 1;
#else
    struct stat st;
	
    if (!dal)
	return 0;
	
    if (stat((char *)dal->data, &st))
	return 0;

    return (unsigned int)st.st_rdev;
#endif
}

#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64)
#   define BLKGETSIZE64 _IOR(0x12, 114, sizeof(unsigned long long))
#endif

/*
    Handler for "len" operation for use with file device. See bellow for 
    understanding where it is used.
*/
static count_t file_len(dal_t *dal) {
    unsigned long long size;
    off_t max_off = 0;

    if (!dal) return 0;
    
#ifdef BLKGETSIZE64
    
    if (ioctl(*((int *)dal->entity), BLKGETSIZE64, &size) >= 0)
        return (count_t)(size / dal->blocksize);
    
    file_save_error(dal);
    
#endif

#ifdef BLKGETSIZE    
    
    if (ioctl(*((int *)dal->entity), BLKGETSIZE, &size) >= 0)
        return (count_t)(size / (dal->blocksize / 512));

    file_save_error(dal);
    
#endif
    
#ifdef DIOCGMEDIASIZE

    if (ioctl(*((int *)dal->entity), DIOCGMEDIASIZE, &size) >= 0)
        return (count_t)(size / dal->blocksize);

    file_save_error(dal);
    
#endif

    if ((max_off = lseek(*((int *)dal->entity), 0, SEEK_END)) == (off_t)-1) {
	file_save_error(dal);
	return 0;
    }
    
    return (count_t)(max_off / dal->blocksize);
}

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
};

dal_t *file_open(const char *file, unsigned blocksize, int flags) {
    int fd;
    dal_t *dal;
	
    if (!file) 
	return NULL;

#if defined(O_LARGEFILE)
    if ((fd = open(file, flags | O_LARGEFILE)) == -1)
#else
    if ((fd = open(file, flags)) == -1)
#endif
	return NULL;
	
    dal = dal_open(&ops, blocksize, flags, (void *)file);
    strncpy(dal->name, file, strlen(file));

    if (!(dal->entity = libdal_calloc(sizeof(int), 0)))
	goto error_free_dal;

    *((int *)dal->entity) = fd;
    
    return dal;
    
error_free_dal:
    dal_close(dal);
error:
    return NULL;    
}

int file_reopen(dal_t *dal, int flags) {
    int fd;
	
    if (!dal) 
	return 0;

    close(*((int *)dal->entity));
	
#if defined(O_LARGEFILE)
    if ((fd = open((char *)dal->data, flags | O_LARGEFILE)) == -1)
#else
    if ((fd = open((char *)dal->data, flags)) == -1)
#endif
	return 0;
	
    *((int *)dal->entity) = fd;
    dal->flags = flags;
	
    return 1;
}

void file_close(dal_t *dal) {

    if (!dal) 
	return;

    close(*((int *)dal->entity));
    libdal_free(dal->entity);
    dal_close(dal);
}



syntax highlighted by Code2HTML, v. 0.9.1