Permalink
Fetching contributors…
Cannot retrieve contributors at this time
544 lines (452 sloc) 13 KB
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <vhd/libvhd.h>
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/alloc.h>
#include <caml/custom.h>
#include <caml/fail.h>
#include <caml/signals.h>
#include "syslog.h"
static struct custom_operations vhd_ops = {
"com.citrix.dci.vhd",
custom_finalize_default,
custom_compare_default,
custom_hash_default,
custom_serialize_default,
custom_deserialize_default
};
#define Vhd_val(v) (*((vhd_context_t **) Data_custom_val(v)))
static value alloc_vhd(vhd_context_t *t)
{
value v = alloc_custom(&vhd_ops, sizeof(vhd_context_t *), 0, 1);
Vhd_val(v)=t;
return v;
}
value stub_vhd_open(value name, value flags)
{
CAMLparam2(name,flags);
CAMLlocal1(vhd);
vhd_context_t *context = (vhd_context_t *)malloc(sizeof(vhd_context_t));
int ret = vhd_open(context,String_val(name),Int_val(flags));
if(ret!=0) {
caml_failwith("Failed to open VHD");
}
vhd=alloc_vhd(context);
CAMLreturn (vhd);
}
value stub_vhd_close(value vhd)
{
CAMLparam1(vhd);
vhd_context_t *context=Vhd_val(vhd);
vhd_close(context);
free(context);
CAMLreturn (Val_unit);
}
value stub_vhd_get_phys_size(value vhd)
{
CAMLparam1(vhd);
vhd_context_t *context=Vhd_val(vhd);
off64_t phys_size;
vhd_get_phys_size(context, &phys_size);
CAMLreturn(caml_copy_int64(phys_size));
}
value stub_vhd_create(value name, value size, value type, value mbytes, value flags)
{
CAMLparam5(name,size,type,mbytes,flags);
int ret = vhd_create(String_val(name),Int64_val(size),Int_val(type),Int64_val(mbytes),Int_val(flags));
if(ret != 0) {
caml_failwith("Failed to create VHD");
}
CAMLreturn (Val_unit);
}
value stub_vhd_snapshot(value snapshot, value size, value parent, value mbytes, value flags)
{
CAMLparam5(snapshot,size,parent,mbytes,flags);
int ret = vhd_snapshot(String_val(snapshot),Int64_val(size),String_val(parent),Int64_val(mbytes),Int_val(flags));
if(ret != 0) {
caml_failwith("Failed to snapshot VHD");
}
CAMLreturn (Val_unit);
}
value stub_vhd_get_parent(value vhd)
{
CAMLparam1(vhd);
char *parent=NULL;
int n,i,err;
vhd_parent_locator_t *loc;
vhd_context_t *context = Vhd_val(vhd);
if(context->footer.type != HD_TYPE_DIFF) {
caml_failwith("Disk is not a differencing disk");
}
n = vhd_parent_locator_count(context);
for (i = 0; i < n; i++) {
loc = context->header.loc + i;
if(loc->code == PLAT_CODE_MACX) {
err = vhd_parent_locator_read(context, loc, &parent);
if (err)
caml_failwith("vhd_parent_locator_read failed");
}
}
if(parent==NULL) {
caml_failwith("Failed to find a parent!");
}
CAMLreturn(caml_copy_string(parent));
}
value stub_vhd_get_uid(value vhd)
{
CAMLparam1(vhd);
vhd_context_t *context = Vhd_val(vhd);
char uuid[256];
uuid_unparse_lower(context->footer.uuid,uuid);
CAMLreturn(caml_copy_string(uuid));
}
value stub_vhd_get_max_bat_size(value vhd)
{
CAMLparam1(vhd);
vhd_context_t *context = Vhd_val(vhd);
CAMLreturn(caml_copy_int64(context->header.max_bat_size));
}
value stub_vhd_get_parent_uid(value vhd)
{
CAMLparam1(vhd);
vhd_context_t *context = Vhd_val(vhd);
char uuid[256];
if(context->footer.type != HD_TYPE_DIFF) {
caml_failwith("Not a differencing disk");
}
uuid_unparse_lower(context->header.prt_uuid,uuid);
CAMLreturn(caml_copy_string(uuid));
}
value stub_vhd_get_type(value vhd)
{
CAMLparam1(vhd);
vhd_context_t *context = Vhd_val(vhd);
CAMLreturn(Val_int(context->footer.type));
}
value stub_vhd_get_creator(value vhd)
{
CAMLparam1(vhd);
vhd_context_t *context = Vhd_val(vhd);
char creator[5];
strncpy(creator,context->footer.crtr_app,4);
creator[4]='\0';
CAMLreturn(caml_copy_string(creator));
}
value stub_vhd_get_virtual_size(value vhd)
{
CAMLparam1(vhd);
vhd_context_t *context = Vhd_val(vhd);
CAMLreturn(caml_copy_int64(context->footer.curr_size));
}
value stub_vhd_get_hidden(value vhd)
{
CAMLparam1(vhd);
int hidden;
vhd_context_t *context = Vhd_val(vhd);
vhd_hidden(context,&hidden);
CAMLreturn(Val_int(hidden));
}
value stub_vhd_set_hidden(value vhd, value hidden)
{
CAMLparam2(vhd,hidden);
vhd_context_t *context = Vhd_val(vhd);
context->footer.hidden = (char)Int_val(hidden);
int err = vhd_write_footer(context, &context->footer);
if(err) {
syslog(LOG_DAEMON | LOG_ERR, "set hidden failed: %d", err);
caml_failwith("Set hidden failed!");
}
CAMLreturn(Val_unit);
}
value stub_vhd_set_phys_size(value vhd, value ml_newsize)
{
CAMLparam2(vhd,ml_newsize);
int err;
vhd_context_t *context = Vhd_val(vhd);
uint64_t newsize=Int64_val(ml_newsize);
err=vhd_set_phys_size(context, newsize);
if(err) {
syslog(LOG_DAEMON | LOG_ERR, "vhd_set_phys_size failed: %d", err);
caml_failwith("Set phys_size failed");
}
CAMLreturn(Val_unit);
}
value stub_vhd_set_virt_size(value vhd, value ml_newsize)
{
CAMLparam2(vhd,ml_newsize);
int err;
vhd_context_t *context = Vhd_val(vhd);
uint64_t newsize=Int64_val(ml_newsize);
err=vhd_set_virt_size(context, newsize);
if(err) {
syslog(LOG_DAEMON | LOG_ERR, "vhd_set_virt_size failed: %d", err);
caml_failwith("Set virt_size failed");
}
CAMLreturn(Val_unit);
}
static int
__raw_io_write(int fd, char* buf, uint64_t sec, uint32_t secs)
{
off64_t off;
size_t ret;
errno = 0;
off = lseek64(fd, vhd_sectors_to_bytes(sec), SEEK_SET);
if (off == (off64_t)-1) {
printf("raw parent: seek(0x%08"PRIx64") failed: %d\n",
vhd_sectors_to_bytes(sec), -errno);
return -errno;
}
ret = write(fd, buf, vhd_sectors_to_bytes(secs));
if (ret == vhd_sectors_to_bytes(secs))
return 0;
printf("raw parent: write of 0x%"PRIx64" returned %zd, errno: %d\n",
vhd_sectors_to_bytes(secs), ret, -errno);
return (errno ? -errno : -EIO);
}
/*
* Use 'parent' if the parent is VHD, and 'parent_fd' if the parent is raw
*/
static int
vhd_util_coalesce_block(vhd_context_t *vhd, vhd_context_t *parent,
int parent_fd, uint64_t block)
{
int i, err;
char *buf, *map;
uint64_t sec, secs;
buf = NULL;
map = NULL;
sec = block * vhd->spb;
if (vhd->bat.bat[block] == DD_BLK_UNUSED)
return 0;
err = posix_memalign((void **)&buf, 4096, vhd->header.block_size);
if (err)
return -err;
err = vhd_io_read(vhd, buf, sec, vhd->spb);
if (err)
goto done;
if (vhd_has_batmap(vhd) && vhd_batmap_test(vhd, &vhd->batmap, block)) {
if (parent->file)
err = vhd_io_write(parent, buf, sec, vhd->spb);
else
err = __raw_io_write(parent_fd, buf, sec, vhd->spb);
goto done;
}
err = vhd_read_bitmap(vhd, block, &map);
if (err)
goto done;
for (i = 0; i < vhd->spb; i++) {
if (!vhd_bitmap_test(vhd, map, i))
continue;
for (secs = 0; i + secs < vhd->spb; secs++)
if (!vhd_bitmap_test(vhd, map, i + secs))
break;
if (parent->file)
err = vhd_io_write(parent,
buf + vhd_sectors_to_bytes(i),
sec + i, secs);
else
err = __raw_io_write(parent_fd,
buf + vhd_sectors_to_bytes(i),
sec + i, secs);
if (err)
goto done;
i += secs;
}
err = 0;
done:
free(buf);
free(map);
return err;
}
value stub_vhd_coalesce(value vhd)
{
CAMLparam1(vhd);
vhd_context_t *context = Vhd_val(vhd);
vhd_context_t parent;
char uuid[37];
char *pname;
int err,i;
int parent_fd=-1;
parent.file = NULL;
uuid_unparse(context->footer.uuid,uuid);
caml_enter_blocking_section();
err=vhd_parent_locator_get(context, &pname);
if(err) {
syslog(LOG_DAEMON | LOG_ERR, "Error finding %s parent: %d", uuid, err);
caml_failwith("Couldn't find parent");
}
if( vhd_parent_raw(context)) {
parent_fd = open(pname, O_RDWR | O_DIRECT | O_LARGEFILE, 0644);
if(parent_fd == -1) {
err = - errno;
syslog(LOG_DAEMON | LOG_ERR, "Failed to open raw parent %s: %d",pname,err);
free(pname);
caml_failwith("Couldn't open parent");
}
} else {
err = vhd_open(&parent,pname,VHD_OPEN_RDWR);
if(err) {
syslog(LOG_DAEMON | LOG_ERR, "Failed to open vhd parent %s: %d",pname,err);
free(pname);
caml_failwith("Couldn't open parent");
}
}
err=vhd_get_bat(context);
if(err)
goto done;
if(vhd_has_batmap(context)) {
err = vhd_get_batmap(context);
if (err)
goto done;
}
for(i=0; i<context->bat.entries; i++) {
err=vhd_util_coalesce_block(context,&parent,parent_fd,i);
if(err)
goto done;
}
err=0;
done:
free(pname);
if(parent.file)
vhd_close(&parent);
else
close(parent_fd);
caml_leave_blocking_section();
CAMLreturn (Val_unit);
}
value stub_vhd_write_sector(value vhd, value ml_sectorno, value ml_string)
{
CAMLparam3(vhd, ml_sectorno, ml_string);
uint64_t sectorno=Int64_val(ml_sectorno);
if(caml_string_length(ml_string)!=512)
caml_failwith("Require string to be of length 512");
vhd_context_t *context = Vhd_val(vhd);
char *buf;
int err;
err = posix_memalign((void **)&buf, 4096, context->header.block_size);
if(err) {
syslog(LOG_INFO, "error with the posix_memalign: %d", err);
caml_failwith("Error with the posix memalign");
}
memcpy(buf,String_val(ml_string),512);
caml_enter_blocking_section();
err = vhd_get_bat(context);
if(err) {
syslog(LOG_INFO, "error getting bat: %d", err);
caml_leave_blocking_section();
caml_failwith("Error getting BAT");
}
err = vhd_io_write(context, buf, sectorno, 1);
if(err) {
syslog(LOG_INFO, "error performing write: %d", err);
caml_leave_blocking_section();
caml_failwith("Error performing write");
}
syslog(LOG_INFO, "string='%s', sectorno=%Ld, err=%d", buf, sectorno, err);
caml_leave_blocking_section();
CAMLreturn(Val_int(err));
}
value stub_vhd_read_sector(value vhd, value ml_sectorno)
{
CAMLparam2(vhd,ml_sectorno);
CAMLlocal1(returnstr);
char buf[512];
uint64_t sectorno=Int64_val(ml_sectorno);
vhd_context_t *context = Vhd_val(vhd);
int err;
caml_enter_blocking_section();
err = vhd_io_read(context, buf, sectorno, 1);
caml_leave_blocking_section();
returnstr=caml_alloc_string(512);
memcpy(String_val(returnstr),buf,512);
CAMLreturn(returnstr);
}
value stub_vhd_set_log_level(value level)
{
CAMLparam1(level);
libvhd_set_log_level(Int_val(level));
CAMLreturn(Val_unit);
}
value stub_vhd_set_parent(value vhd, value ml_new_parent, value ml_new_parent_is_raw)
{
CAMLparam3(vhd, ml_new_parent, ml_new_parent_is_raw);
char *new_parent = strdup(String_val(ml_new_parent));
int new_parent_is_raw = 0;
vhd_context_t *context = Vhd_val(vhd);
int err;
if(Bool_val(ml_new_parent_is_raw))
new_parent_is_raw=1;
caml_enter_blocking_section();
err=vhd_change_parent(context, new_parent, new_parent_is_raw);
if(err) {
syslog(LOG_INFO, "error performing setting parent: %d", err);
}
caml_leave_blocking_section();
CAMLreturn(Val_unit);
}
/* Return a run-length encoded list of allocated blocks */
value stub_vhd_get_bat(value vhd)
{
CAMLparam1(vhd);
CAMLlocal3(list,tmp,pair);
vhd_context_t *context = Vhd_val(vhd);
int state=0;
int len=0;
int i;
int max = context->footer.curr_size >> 21;
int err = vhd_get_bat(context);
syslog(LOG_DAEMON | LOG_ERR, "stub_vhd_get_bat: max=%d",max);
if(err != 0) {
caml_failwith("Failed to get BAT");
}
list = Val_int(0);
for(i=0; i<max; i++) {
if(state==0) {
if(context->bat.bat[i] != DD_BLK_UNUSED) {
state=1;
pair = caml_alloc(2,0);
Store_field(pair,0,Val_int(i));
len=1;
}
} else if(state==1) {
if(context->bat.bat[i] == DD_BLK_UNUSED) {
Store_field(pair,1,Val_int(len));
tmp = caml_alloc(2,0);
Store_field(tmp,0,pair);
Store_field(tmp,1,list);
list=tmp;
state=0;
len=0;
} else {
len++;
}
}
}
if(state==1) {
Store_field(pair,1,Val_int(len));
tmp = caml_alloc(2,0);
Store_field(tmp,0,pair);
Store_field(tmp,1,list);
list=tmp;
}
CAMLreturn(list);
}
value stub_vhd_get_first_allocated_block(value vhd)
{
CAMLparam1(vhd);
vhd_context_t *context = Vhd_val(vhd);
uint64_t firstblock=DD_BLK_UNUSED;
int i,max,err;
max = context->footer.curr_size >> 21;
err = vhd_get_bat(context);
for(i=0; i<max; i++) {
if(context->bat.bat[i]<firstblock) {
firstblock=context->bat.bat[i];
}
}
CAMLreturn(caml_copy_int64(firstblock));
}