Browse files

merged 'storage' from main-dev (needs 'util' and 'node' to compile all)

  • Loading branch information...
1 parent db090d5 commit 06e1de7dc6548996e1e361402b622ab9bcb7fd14 Dmitrii Zagorodnov dmitrii@eucalyptus.com committed Apr 27, 2011
Showing with 1,415 additions and 239 deletions.
  1. +720 −0 storage/backing.c
  2. +7 −20 storage/{test.c → backing.h}
  3. +397 −170 storage/blobstore.c
  4. +71 −4 storage/blobstore.h
  5. +0 −3 storage/diskfile.c
  6. +97 −9 storage/diskutil.c
  7. +66 −1 storage/diskutil.h
  8. +56 −32 storage/walrus.c
  9. +1 −0 storage/walrus.h
View
720 storage/backing.c
@@ -0,0 +1,720 @@
+// -*- mode: C; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-
+// vim: set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
+
+/*
+ Copyright (c) 2009 Eucalyptus Systems, Inc.
+
+ 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, only version 3 of the License.
+
+ This file 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, see <http://www.gnu.org/licenses/>.
+
+ Please contact Eucalyptus Systems, Inc., 130 Castilian
+ Dr., Goleta, CA 93101 USA or visit <http://www.eucalyptus.com/licenses/>
+ if you need additional information or have any questions.
+
+ This file may incorporate work covered under the following copyright and
+ permission notice:
+
+ Software License Agreement (BSD License)
+
+ Copyright (c) 2008, Regents of the University of California
+
+
+ Redistribution and use of this software in source and binary forms, with
+ or without modification, are permitted provided that the following
+ conditions are met:
+
+ Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. USERS OF
+ THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE
+ LICENSED MATERIAL, COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS
+ SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
+ IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
+ BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
+ THE REGENTS’ DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
+ OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
+ WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
+ ANY SUCH LICENSES OR RIGHTS.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#include <assert.h>
+#include <dirent.h>
+#include "misc.h" // logprintfl, ensure_...
+#include "data.h" // ncInstance
+#include "diskutil.h"
+#include "eucalyptus.h"
+#include "blobstore.h"
+#include "walrus.h"
+#include "backing.h"
+
+#define TIMEOUT 100*60*60*2 // TODO: change the timeout?
+
+static char instances_path [MAX_PATH];
+static blobstore * cache_bs = NULL;
+static blobstore * work_bs;
+
+static void bs_errors (const char * msg)
+{
+ logprintfl (EUCAERROR, "blobstore: %s", msg);
+}
+
+int init_backing_store (const char * conf_instances_path, unsigned int conf_work_size_mb, unsigned int conf_cache_size_mb)
+{
+ logprintfl (EUCAINFO, "initializing backing store...\n");
+
+ if (conf_instances_path == NULL) {
+ logprintfl (EUCAERROR, "error: INSTANCE_PATH not specified\n");
+ return ERROR;
+ }
+ strncpy (instances_path, conf_instances_path, sizeof (instances_path));
+ if (check_directory (instances_path)) {
+ logprintfl (EUCAERROR, "error: INSTANCE_PATH (%s) does not exist!\n", instances_path);
+ return ERROR;
+ }
+ char cache_path [MAX_PATH]; snprintf (cache_path, sizeof (cache_path), "%s/cache", instances_path);
+ if (ensure_directories_exist (cache_path, 0, 0700) == -1) return ERROR;
+ char work_path [MAX_PATH]; snprintf (work_path, sizeof (work_path), "%s/work", instances_path);
+ if (ensure_directories_exist (work_path, 0, 0700) == -1) return ERROR;
+ unsigned long long cache_limit_blocks = conf_cache_size_mb * 2048; // convert MB to blocks
+ unsigned long long work_limit_blocks = conf_work_size_mb * 2048;
+ if (work_limit_blocks==0) { // we take 0 as unlimited
+ work_limit_blocks = ULLONG_MAX;
+ }
+
+ blobstore_set_error_function ( &bs_errors );
+ if (cache_limit_blocks) {
+ cache_bs = blobstore_open (cache_path, cache_limit_blocks, BLOBSTORE_FORMAT_DIRECTORY, BLOBSTORE_REVOCATION_LRU, BLOBSTORE_SNAPSHOT_ANY);
+ if (cache_bs==NULL) {
+ logprintfl (EUCAERROR, "ERROR: %s\n", blobstore_get_error_str(blobstore_get_error()));
+ return ERROR;
+ }
+ // TODO: run through cache and verify checksums?
+ // TODO: run through cache and work and clean up unused stuff?
+ }
+ work_bs = blobstore_open (work_path, work_limit_blocks, BLOBSTORE_FORMAT_FILES, BLOBSTORE_REVOCATION_NONE, BLOBSTORE_SNAPSHOT_ANY);
+ if (work_bs==NULL) {
+ logprintfl (EUCAERROR, "ERROR: %s\n", blobstore_get_error_str(blobstore_get_error()));
+ blobstore_close (cache_bs);
+ return ERROR;
+ }
+
+ logprintfl (EUCADEBUG, "initialized backing store\n");
+ return OK;
+}
+
+static void set_backing (virtualBootRecord * vbr, blockblob * bb, int allow_block_dev)
+{
+ if (allow_block_dev && strlen (blockblob_get_dev (bb))) {
+ strncpy (vbr->backingPath, blockblob_get_dev (bb), sizeof (vbr->backingPath));
+ vbr->backingType = SOURCE_TYPE_BLOCK;
+ } else {
+ strncpy (vbr->backingPath, blockblob_get_file (bb), sizeof (vbr->backingPath));
+ vbr->backingType = SOURCE_TYPE_FILE;
+ }
+ logprintfl (EUCAINFO, "prepared backing of type %s\n", vbr->typeName);
+}
+
+// sets path to
+// - the path of a file in an instance directory
+// - the path of the instance directory
+// - the path where all instance directories are kept
+// this function must be kept consistent with set_id() below
+static void set_path (char * path, unsigned int path_size, const ncInstance * instance, const char * filename)
+{
+ if (instance) {
+ if (filename) {
+ snprintf (path, path_size, "%s/work/%s/%s/%s", instances_path, instance->userId, instance->instanceId, filename);
+ } else {
+ snprintf (path, path_size, "%s/work/%s/%s", instances_path, instance->userId, instance->instanceId);
+ }
+ } else {
+ snprintf (path, path_size, "%s/work", instances_path);
+ }
+}
+
+// sets id to the blobblock ID of a component, specified by the vbr, of an instance
+// this value must be kept consistent with path generated by set_path() above
+static void set_id (ncInstance * instance, virtualBootRecord * vbr, char * id, unsigned int id_size)
+{
+
+ snprintf (id, id_size, "%s/%s/blob-%s-%s",
+ instance->userId,
+ instance->instanceId,
+ vbr->typeName,
+ (vbr->type==NC_RESOURCE_KERNEL||vbr->type==NC_RESOURCE_RAMDISK)?(vbr->id):(vbr->guestDeviceName));
+}
+
+static int create_vbr_backing (ncInstance * instance, virtualBootRecord * vbr, int allow_block_dev)
+{
+ logprintfl (EUCAINFO, "preparing backing of type %s (pulled from '%s')...\n", vbr->typeName, vbr->resourceLocation);
+ int ret = ERROR;
+
+ // construct the blob IDs for this resource
+ char * cache_id = vbr->id;
+ char work_id [EUCA_MAX_PATH];
+ set_id (instance, vbr, work_id, sizeof (work_id));
+
+ // download data or prepare remote device or create local data
+ switch (vbr->locationType) {
+ case NC_LOCATION_URL:
+ case NC_LOCATION_CLC:
+ case NC_LOCATION_SC:
+ logprintfl (EUCAERROR, "error: backing of type %s is NOT IMPLEMENTED\n", vbr->typeName);
+ // TODO
+ break;
+
+ case NC_LOCATION_WALRUS: {
+
+ // get the digest
+ char * blob_sig = walrus_get_digest (vbr->preparedResourceLocation);
+ if (blob_sig==NULL) goto w_error;
+ long long bb_size_bytes = str2longlong (blob_sig, "<size>", "</size>"); // pull size from the digest
+ if (bb_size_bytes < 1) goto w_error;
+ vbr->size = bb_size_bytes; // record size now that we know it
+
+ // get a reference to a cached blob, if possible
+ blockblob * cache_bb = NULL;
+ int in_cache = 0;
+ if (cache_bs) { // we have a cache store
+ int flags = 0; // first we'll try opening as a reader
+ while ((cache_bb = blockblob_open (cache_bs, cache_id, bb_size_bytes, flags, blob_sig, TIMEOUT)) == NULL) {
+ int err = blobstore_get_error();
+
+ if (err==BLOBSTORE_ERROR_NOENT) { // cache entry does not exist
+ flags = BLOBSTORE_FLAG_CREAT | BLOBSTORE_FLAG_EXCL; // try creating
+
+ } else if (err==BLOBSTORE_ERROR_SIGNATURE) { // wrong signature or length
+ // open with any signature and delete the old one
+ cache_bb = blockblob_open (cache_bs, cache_id, 0, 0, NULL, 0);
+ if (cache_bb && blockblob_delete (cache_bb, 0) == 0) {
+ flags = BLOBSTORE_FLAG_CREAT | BLOBSTORE_FLAG_EXCL; // try creating
+ cache_bb = NULL; // (delete frees the handle)
+ } else {
+ break;
+ }
+ } else if (err==BLOBSTORE_ERROR_NOSPC) { // no space in the cache
+ break; // give up
+
+ } else if (err==BLOBSTORE_ERROR_AGAIN) { // timed out waiting
+ break; // give up
+
+ } else {
+ logprintfl (EUCAWARN, "unkown error while preparing cache entry, skipping cache for %s\n", instance->instanceId);
+ break;
+ }
+ }
+ if (cache_bb && !flags) {
+ in_cache = 1; // we have a valid entry in the cache
+ }
+ }
+
+ logprintfl (EUCAINFO, "allocating work blob %s of size %lld bytes\n", work_id, bb_size_bytes);
+
+ // allocate the work entry
+ blockblob * work_bb = blockblob_open (work_bs, work_id, bb_size_bytes, BLOBSTORE_FLAG_CREAT | BLOBSTORE_FLAG_EXCL, blob_sig, 1000); // TODO: figure out the timeout
+ if (work_bb==NULL)
+ goto w_error;
+
+ if (!in_cache) {
+ const char * dest_path; // decide where the download will go
+ if (cache_bb) {
+ dest_path = blockblob_get_file (cache_bb);
+ } else {
+ dest_path = blockblob_get_file (work_bb);
+ }
+ if (walrus_image_by_manifest_url (vbr->preparedResourceLocation, dest_path, 1) != OK) {
+ logprintfl (EUCAERROR, "error: failed to download for instance %s component %s\n", instance->instanceId, vbr->preparedResourceLocation);
+ goto w_error;
+ }
+ }
+
+ if (cache_bb) {
+ if (allow_block_dev) {
+ blockmap map [] = {
+ {BLOBSTORE_SNAPSHOT, BLOBSTORE_BLOCKBLOB, {blob:cache_bb}, 0, 0, round_up_sec (bb_size_bytes) / 512}
+ };
+ if (blockblob_clone (work_bb, map, 1)==-1) {
+ logprintfl (EUCAERROR, "error: failed to clone cached blob %s to work blob %s\n", cache_bb->id, work_bb->id);
+ goto w_error;
+ }
+ } else {
+ if (blockblob_copy (cache_bb, 0L, work_bb, 0L, 0L)==-1) {
+ logprintfl (EUCAERROR, "error: failed to copy cached blob %s to work blob %s\n", cache_bb->id, work_bb->id);
+ goto w_error;
+ }
+ }
+ }
+
+ if (instance->params.image == vbr) { // this is a root image
+ const char * dev = blockblob_get_dev (work_bb);
+
+ // tune file system, which is needed to boot EMIs fscked long ago
+ logprintfl (EUCAINFO, "tuning root file system\n");
+ if (diskutil_tune (dev) == ERROR) {
+ logprintfl (EUCAERROR, "error: failed to tune root file system\n");
+ goto w_error;
+ }
+
+ logprintfl (EUCAINFO, "injecting the ssh key\n");
+
+ // mount the partition
+ char mnt_pt [EUCA_MAX_PATH];
+ set_path (mnt_pt, sizeof (mnt_pt), instance, "euca-mount-XXXXXX");
+ if (mkdtemp (mnt_pt)==NULL) {
+ logprintfl (EUCAINFO, "error: mkdtemp() failed: %s\n", strerror (errno));
+ goto w_error;
+ }
+ if (diskutil_mount (dev, mnt_pt) != OK) {
+ logprintfl (EUCAINFO, "error: failed to mount '%s' on '%s'\n", dev, mnt_pt);
+ goto w_error;
+ }
+
+ // save the SSH key, with the right permissions
+ int injection_failed = 0;
+ char path [EUCA_MAX_PATH];
+ snprintf (path, sizeof (path), "%s/root/.ssh", mnt_pt);
+ if (diskutil_mkdir (path) == -1) {
+ logprintfl (EUCAINFO, "error: failed to create path '%s'\n", path);
+ injection_failed = 1;
+ goto unmount;
+ }
+ if (diskutil_ch (path, "root", 0700) != OK) {
+ logprintfl (EUCAINFO, "error: failed to change user and/or permissions for '%s'\n", path);
+ injection_failed = 1;
+ goto unmount;
+ }
+ snprintf (path, sizeof (path), "%s/root/.ssh/authorized_keys", mnt_pt);
+ if (diskutil_write2file (path, instance->keyName) != OK) { // TODO: maybe append the key instead of overwriting?
+ logprintfl (EUCAINFO, "error: failed to save key in '%s'\n", path);
+ injection_failed = 1;
+ goto unmount;
+ }
+ if (diskutil_ch (path, "root", 0600) != OK) {
+ logprintfl (EUCAINFO, "error: failed to change user and/or permissions for '%s'\n", path);
+ injection_failed = 1;
+ goto unmount;
+ }
+
+ unmount:
+
+ // unmount partition and delete the mount point
+ if (diskutil_umount (mnt_pt) != OK) {
+ logprintfl (EUCAINFO, "error: failed to unmount %s (there may be a resource leak)\n", mnt_pt);
+ injection_failed = 1;
+ }
+ if (rmdir (mnt_pt) != 0) {
+ logprintfl (EUCAINFO, "error: failed to remove %s (there may be a resource leak): %s\n", mnt_pt, strerror(errno));
+ injection_failed = 1;
+ }
+
+ if (injection_failed)
+ goto w_error;
+ }
+
+ set_backing (vbr, work_bb, allow_block_dev);
+ ret = OK;
+
+ w_error:
+
+ if (work_bb) blockblob_close (work_bb);
+ if (cache_bb) blockblob_close (cache_bb);
+ if (blob_sig) free (blob_sig);
+ break;
+ }
+
+ case NC_LOCATION_IQN:
+ logprintfl (EUCAERROR, "error: backing of type %s is NOT IMPLEMENTED\n", vbr->typeName);
+ // TODO:
+ break;
+
+ case NC_LOCATION_AOE:
+ logprintfl (EUCAERROR, "error: backing of type %s is NOT IMPLEMENTED\n", vbr->typeName);
+ // TODO:
+ break;
+
+ case NC_LOCATION_NONE: {
+ // allocate the work entry
+ logprintfl (EUCAINFO, "allocating work blob %s of size %lld bytes\n", work_id, vbr->size);
+
+ blockblob * work_bb = blockblob_open (work_bs, work_id, vbr->size, BLOBSTORE_FLAG_CREAT | BLOBSTORE_FLAG_EXCL, NULL, 1000); // TODO: figure out the timeout
+ if (work_bb==NULL)
+ break;
+
+ int format = ERROR;
+ switch (vbr->format) {
+ case NC_FORMAT_NONE:
+ format = OK;
+ break;
+ case NC_FORMAT_EXT2: // TODO: distinguish ext2 and ext3!
+ case NC_FORMAT_EXT3:
+ logprintfl (EUCAINFO, "formatting blob %s as ext3\n", work_id);
+ format = diskutil_mkfs (blockblob_get_dev (work_bb), vbr->size);
+ break;
+ case NC_FORMAT_SWAP:
+ logprintfl (EUCAINFO, "formatting blob %s as swap\n", work_id);
+ format = diskutil_mkswap (blockblob_get_dev (work_bb), vbr->size);
+ break;
+ default:
+ logprintfl (EUCAERROR, "error: format of type %s is NOT IMPLEMENTED\n", vbr->formatName);
+ }
+
+ if (format == OK) {
+ set_backing (vbr, work_bb, allow_block_dev);
+ ret = OK;
+ }
+
+ if (work_bb) blockblob_close (work_bb);
+ break;
+ }
+
+ default:
+ logprintfl (EUCAERROR, "error: unrecognized locationType %d\n", vbr->locationType);
+ }
+
+ return ret;
+}
+
+static blockblob * open_blob (ncInstance * instance, virtualBootRecord * vbr)
+{
+ char id [EUCA_MAX_PATH];
+ set_id (instance, vbr, id, sizeof (id));
+ logprintfl (EUCADEBUG, "opening blob %s\n", id);
+ return blockblob_open (work_bs, id, vbr->size, 0, NULL, 1000); // TODO: figure out the timeout
+}
+
+// sets vbr->guestDeviceName based on other entries in the struct
+// (guestDevice{Type|Bus}, {disk|partition}Number}
+static void set_disk_dev (virtualBootRecord * vbr)
+{
+ char type [3] = "\0\0\0";
+ if (vbr->guestDeviceType==DEV_TYPE_FLOPPY) {
+ type [0] = 'f';
+ } else { // a disk
+ switch (vbr->guestDeviceBus) {
+ case BUS_TYPE_IDE: type [0] = 'h'; break;
+ case BUS_TYPE_SCSI: type [0] = 's'; break;
+ case BUS_TYPE_VIRTIO: type [0] = 'v'; break;
+ case BUS_TYPE_XEN: type [0] = 'x'; type [1] = 'v'; break;
+ case BUS_TYPES_TOTAL:
+ default:
+ type [0] = '?'; // error
+ }
+ }
+
+ char disk;
+ if (vbr->guestDeviceType==DEV_TYPE_FLOPPY) {
+ assert (vbr->diskNumber >=0 && vbr->diskNumber <= 9);
+ disk = '0' + vbr->diskNumber;
+ } else { // a disk
+ assert (vbr->diskNumber >=0 && vbr->diskNumber <= 26);
+ disk = 'a' + vbr->diskNumber;
+ }
+
+ char part [3] = "\0";
+ if (vbr->partitionNumber) {
+ snprintf (part, sizeof(part), "%d", vbr->partitionNumber);
+ }
+
+ snprintf (vbr->guestDeviceName, sizeof (vbr->guestDeviceName), "%sd%c%s", type, disk, part);
+}
+
+static int create_disk (ncInstance * instance, virtualBootRecord * disk, virtualBootRecord ** parts, int partitions)
+{
+ logprintfl (EUCAINFO, "composing a disk from supplied partitions...\n");
+
+ int ret = ERROR;
+#define MBR_BLOCKS (62 + 4)
+ disk->size = 512 * MBR_BLOCKS;
+ blockblob * pbbs [EUCA_MAX_PARTITIONS];
+ blockmap map [EUCA_MAX_PARTITIONS] = { {BLOBSTORE_SNAPSHOT, BLOBSTORE_ZERO, {blob:NULL}, 0, 0, MBR_BLOCKS} };
+
+ // run through partitions and add their sizes
+ for (int i=0; i<partitions; i++) {
+ virtualBootRecord * p = * (parts + i);
+
+ if (p->size < 1) {
+ logprintfl (EUCAERROR, "error: unknown size for partition %d\n", i);
+ goto cleanup;
+ }
+
+ if (p->size % 512) {
+ logprintfl (EUCAERROR, "error: size for partition %d is not a multiple of 512\n", i);
+ goto cleanup;
+ }
+
+ pbbs [i] = open_blob (instance, p);
+ if (pbbs [i] == NULL) {
+ logprintfl (EUCAERROR, "error: failed to open blob for partition %d\n", i);
+ goto cleanup;
+ }
+
+ int m = i + 1; // first map entry is for MBR
+ map [m].relation_type = BLOBSTORE_MAP;
+ map [m].source_type = BLOBSTORE_BLOCKBLOB;
+ map [m].source.blob = pbbs [i];
+ map [m].first_block_src = 0;
+ map [m].first_block_dst = (disk->size / 512);
+ map [m].len_blocks = (p->size / 512);
+ disk->size += p->size;
+ }
+
+ // set *some* of the fields in vbr:
+ // - ones needed for set_id() below to work
+ // - ones needed for xml.c:gen_instance_xml() to generate correct disk entries
+ virtualBootRecord * p1 = * parts; // first partition is representative of others
+ strncpy (disk->typeName, "root", sizeof (disk->typeName)); // for id
+ disk->type = NC_RESOURCE_IMAGE; // for id
+ disk->guestDeviceType = p1->guestDeviceType; // for xml
+ disk->guestDeviceBus = p1->guestDeviceBus; // for xml
+ disk->diskNumber = p1->diskNumber;
+ set_disk_dev (disk);
+
+ // generate the id and create the blob
+ char disk_id [EUCA_MAX_PATH];
+ set_id (instance, disk, disk_id, sizeof (disk_id));
+ blockblob * dbb = blockblob_open (work_bs, disk_id, disk->size, BLOBSTORE_FLAG_CREAT | BLOBSTORE_FLAG_EXCL, NULL, 1000); // TODO: figure out the timeout
+ if (dbb == NULL) {
+ goto cleanup;
+ }
+
+ // map the partitions to the disk
+ if (blockblob_clone (dbb, map, partitions + 1)==-1) {
+ logprintfl (EUCAERROR, "error: failed to clone partitions to created disk\n");
+ goto cleanup;
+ }
+ set_backing (disk, dbb, TRUE);
+
+ // create MBR
+ logprintfl (EUCAINFO, "creating MBR\n");
+ if (diskutil_mbr (blockblob_get_dev (dbb), "msdos") == ERROR) { // issues `parted mklabel`
+ logprintfl (EUCAERROR, "error: failed to add MBR to disk\n");
+ goto cleanup;
+ }
+ for (int i=0; i<partitions; i++) {
+ int m = i + 1; // first map entry is for MBR
+ logprintfl (EUCAINFO, "adding partition %d to partition table\n", i);
+ if (diskutil_part (blockblob_get_dev (dbb), // issues `parted mkpart`
+ "primary", // TODO: make this work with more than 4 partitions
+ NULL, // do not create file system
+ map [m].first_block_dst, // first sector
+ map [m].first_block_dst + map [m].len_blocks - 1) == ERROR) {
+ logprintfl (EUCAERROR, "error: failed to add partition %d to disk\n", i);
+ goto cleanup;
+ }
+
+ /*
+ virtualBootRecord * p = * (parts + i);
+ blockblob * pbb = pbbs [i];
+ if (instance->params.image == p) {
+ */
+
+ }
+
+ // TODO: make disk bootable if kernel/ramdisk are present
+
+ ret = OK;
+ cleanup:
+
+ for (int i=0; i<partitions; i++) {
+ if (pbbs [i]) {
+ blockblob_close (pbbs [i]);
+ }
+ }
+
+ return ret;
+}
+
+int create_instance_backing (ncInstance * instance)
+{
+ int ret = ERROR;
+ int total_prereqs = 0;
+ virtualMachine * vm = &(instance->params);
+
+ char instance_path [MAX_PATH];
+ set_path (instance_path, sizeof (instance_path), instance, NULL);
+ ensure_directories_exist (instance_path, 0, 0700);
+
+ // sort vbrs into prereqs[] and parts[] so they can be approached in the right order
+ // (first the prereqs, then disks and partitions, in increasing order)
+ virtualBootRecord * prereq [EUCA_MAX_VBRS];
+ virtualBootRecord * parts [BUS_TYPES_TOTAL][EUCA_MAX_DISKS][EUCA_MAX_PARTITIONS];
+ bzero (parts, sizeof (parts));
+ for (int i=0; i<EUCA_MAX_VBRS && i<vm->virtualBootRecordLen; i++) {
+ virtualBootRecord * vbr = &(vm->virtualBootRecord[i]);
+ if (vbr->type==NC_RESOURCE_KERNEL || vbr->type==NC_RESOURCE_RAMDISK) {
+ prereq [total_prereqs++] = vbr;
+ } else {
+ parts [vbr->guestDeviceBus][vbr->diskNumber][vbr->partitionNumber] = vbr;
+ }
+ }
+
+ // first download the prerequisites
+ for (int i=0; i<total_prereqs; i++) {
+ virtualBootRecord * vbr = prereq [i];
+ if (create_vbr_backing (instance, vbr, FALSE)) { // libvirt wants a file not a block device for kernel and ramdisk
+ logprintfl (EUCAERROR, "Error: failed to obtain prerequisites needed by instance %s\n", instance->instanceId);
+ goto out;
+ }
+ }
+
+ // then create disks and partitions
+ for (int i=0; i<BUS_TYPES_TOTAL; i++) {
+ for (int j=0; j<EUCA_MAX_DISKS; j++) {
+ int partitions = 0;
+ for (int k=0; k<EUCA_MAX_PARTITIONS; k++) {
+ virtualBootRecord * vbr = parts [i][j][k];
+ if (vbr) {
+ if (create_vbr_backing (instance, vbr, TRUE)) { // libvirt can use either a file or block device for disks
+ logprintfl (EUCAERROR, "Error: failed to create backing (bus %d, disk %d, part %d) for instance %s\n", i, j, k, instance->instanceId);
+ goto out;
+ }
+ if (k>0)
+ partitions++;
+
+ } else if (partitions) { // there were partitions and we saw them all
+ if (vm->virtualBootRecordLen==EUCA_MAX_VBRS) {
+ logprintfl (EUCAERROR, "error: out of room in the virtual boot record while adding disk %d on bus %d\n", j, i);
+ goto out;
+ }
+ if (create_disk (instance, &(vm->virtualBootRecord [vm->virtualBootRecordLen++]), &(parts [i][j][1]), partitions)) {
+ logprintfl (EUCAERROR, "error: failed to create disk %d on bus %d from %d partitions\n", j, i, partitions);
+ vm->virtualBootRecordLen--;
+ goto out;
+ }
+ partitions = 0;
+ }
+ }
+ }
+ }
+
+ set_path (instance->instancePath, sizeof (instance->instancePath), instance, NULL);
+ set_path (instance->xmlFilePath, sizeof (instance->xmlFilePath), instance, "instance.xml");
+ set_path (instance->libvirtFilePath, sizeof (instance->libvirtFilePath), instance, "libvirt.xml");
+ set_path (instance->consoleFilePath, sizeof (instance->consoleFilePath), instance, "console.log");
+
+ ret = OK;
+ out:
+ return ret;
+}
+
+int save_instance_struct (const ncInstance * instance)
+{
+ if (instance==NULL) {
+ logprintfl(EUCADEBUG, "save_instance_struct: NULL instance!\n");
+ return ERROR;
+ }
+
+ char checkpoint_path [MAX_PATH];
+ set_path (checkpoint_path, sizeof (checkpoint_path), instance, "instance.checkpoint");
+
+ int fd;
+ if ((fd = open (checkpoint_path, O_CREAT | O_WRONLY, 0600)) < 0) {
+ logprintfl(EUCADEBUG, "save_instance_struct: failed to create instance checkpoint at %s\n", checkpoint_path);
+ return ERROR;
+ }
+
+ if (write (fd, (char *)instance, sizeof(struct ncInstance_t)) != sizeof (struct ncInstance_t)) {
+ logprintfl(EUCADEBUG, "save_instance_struct: failed to write instance checkpoint at %s\n", checkpoint_path);
+ close (fd);
+ return ERROR;
+ }
+ close (fd);
+
+ return OK;
+}
+
+ncInstance * load_instance_struct (const char * instanceId)
+{
+ const int meta_size = sizeof (struct ncInstance_t);
+ ncInstance * instance = calloc (1, meta_size);
+ if (instance==NULL) {
+ logprintfl (EUCADEBUG, "load_instance_struct: out of memory for instance struct\n");
+ return NULL;
+ }
+ strncpy (instance->instanceId, instanceId, sizeof (instance->instanceId));
+
+ // we don't know userId, so we'll look for instanceId in every user's
+ // directory (we're assuming that instanceIds are unique in the system)
+ char user_paths [MAX_PATH];
+ set_path (user_paths, sizeof (user_paths), NULL, NULL);
+ DIR * insts_dir = opendir(user_paths);
+ if (insts_dir == NULL) {
+ logprintfl (EUCADEBUG, "load_instance_struct: failed to open %s\n", user_paths);
+ goto free;
+ }
+
+ struct dirent * dir_entry;
+ while ((dir_entry = readdir (insts_dir)) != NULL) {
+ char tmp_path [MAX_PATH];
+ struct stat mystat;
+
+ snprintf(tmp_path, sizeof (tmp_path), "%s/%s/%s", user_paths, dir_entry->d_name, instance->instanceId);
+ if (stat(tmp_path, &mystat)==0) {
+ strncpy (instance->userId, dir_entry->d_name, sizeof (instance->userId));
+ break; // found it
+ }
+ }
+ closedir (insts_dir);
+
+ if (strlen(instance->userId)<1) {
+ logprintfl (EUCADEBUG, "load_instance_struct: didn't find instance %s\n", instance->instanceId);
+ goto free;
+ }
+
+ int fd;
+ char checkpoint_path [MAX_PATH];
+ set_path (checkpoint_path, sizeof (checkpoint_path), instance, "instance.checkpoint");
+ if ((fd = open(checkpoint_path, O_RDONLY)) < 0
+ || read (fd, instance, meta_size) < meta_size) {
+ logprintfl(EUCADEBUG, "load_instance_struct: failed to load metadata for %s from %s: %s\n", instance->instanceId, checkpoint_path, strerror (errno));
+ close (fd);
+ goto free;
+ }
+ close (fd);
+ instance->stateCode = NO_STATE;
+ return instance;
+
+ free:
+ if (instance) free (instance);
+ return NULL;
+}
+
+int destroy_instance_backing (ncInstance * instance)
+{
+ // TODO: implement cleanup
+ return ERROR;
+}
View
27 storage/test.c → storage/backing.h
@@ -55,29 +55,16 @@
SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
- THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
+ THE REGENTS DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
ANY SUCH LICENSES OR RIGHTS.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include "storage.h"
-#include "http.h"
-int main (int argc, char **argv)
-{
- printf ("argc=%d\n", argc);
+#include "data.h" // ncInstance
- if (argc==5) {
- return http_put (argv[1], argv[2], argv[3], argv[4]);
-
- } else {
- printf ("=====> testing storage.c\n");
- int err = test_cache ();
- printf (" error=%d\n", err);
- return err;
- }
-}
+int init_backing_store (const char * conf_instances_path, unsigned int conf_work_size_mb, unsigned int conf_cache_size_mb);
+int create_instance_backing (ncInstance * instance);
+int save_instance_struct (const ncInstance * instance);
+ncInstance * load_instance_struct (const char * instanceId);
+int destroy_instance_backing (ncInstance * instance);
View
567 storage/blobstore.c
@@ -2,6 +2,66 @@
// vim: set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
/*
+ Copyright (c) 2009 Eucalyptus Systems, Inc.
+
+ 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, only version 3 of the License.
+
+ This file 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, see <http://www.gnu.org/licenses/>.
+
+ Please contact Eucalyptus Systems, Inc., 130 Castilian
+ Dr., Goleta, CA 93101 USA or visit <http://www.eucalyptus.com/licenses/>
+ if you need additional information or have any questions.
+
+ This file may incorporate work covered under the following copyright and
+ permission notice:
+
+ Software License Agreement (BSD License)
+
+ Copyright (c) 2008, Regents of the University of California
+
+
+ Redistribution and use of this software in source and binary forms, with
+ or without modification, are permitted provided that the following
+ conditions are met:
+
+ Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. USERS OF
+ THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE
+ LICENSED MATERIAL, COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS
+ SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
+ IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
+ BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
+ THE REGENTS’ DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
+ OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
+ WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
+ ANY SUCH LICENSES OR RIGHTS.
+*/
+
+/*
* blobstore.c
*/
@@ -23,6 +83,7 @@
#include <pthread.h>
#include "blobstore.h"
#include "diskutil.h"
+#include "misc.h" // ensure_...
#define BLOBSTORE_METADATA_FILE ".blobstore"
#define BLOBSTORE_DEFAULT_UMASK 0700
@@ -32,12 +93,11 @@
#define BLOBSTORE_NO_TIMEOUT -1L
#define DM_PATH "/dev/mapper/"
#define DM_FORMAT DM_PATH "%s" // TODO: do not hardcode?
-#define DMSETUP "/sbin/dmsetup" // TODO: do not hardcode?
#define MIN_BLOCKS_SNAPSHOT 32 // otherwise dmsetup fails with
+ // device-mapper: reload ioctl failed: Cannot allocate memory OR
+ // device-mapper: reload ioctl failed: Input/output error
#define EUCA_ZERO "euca-zero"
-#define EUCA_ZERO_SIZE "2199023255552" // is one a petabyte enough?
-// device-mapper: reload ioctl failed: Cannot allocate memory OR
-// device-mapper: reload ioctl failed: Input/output error
+#define EUCA_ZERO_SIZE "2199023255552" // is one petabyte enough?
typedef enum { // paths to files containing...
BLOCKBLOB_PATH_NONE = 0, // sentinel for identifying files that are not blockblob related
@@ -73,11 +133,28 @@ typedef struct _blobstore_filelock {
} blobstore_filelock;
__thread blobstore_error_t _blobstore_errno = BLOBSTORE_ERROR_OK; // thread-local errno
+static void (* err_fn) (const char * msg) = NULL;
static unsigned char _do_print_errors = 1;
+static unsigned char _do_print_trace = 0;
static pthread_mutex_t _blobstore_mutex = PTHREAD_MUTEX_INITIALIZER; // process-global mutex
static blobstore_filelock * locks_list = NULL; // process-global LL head (TODO: replace this with a hash table)
static char zero_buf [1] = "\0";
+static void myprintf (const char * format, ...)
+{
+ char buf [1024];
+
+ va_list ap;
+ va_start (ap, format);
+ vsnprintf (buf, sizeof (buf), format, ap);
+ va_end (ap);
+
+ if (err_fn)
+ err_fn (buf);
+ else
+ puts (buf);
+}
+
static void print_trace (void)
{
void *array[64];
@@ -89,7 +166,7 @@ static void print_trace (void)
strings = backtrace_symbols (array, size);
for (i = 0; i < size; i++)
- printf ("\t%s\n", strings[i]);
+ myprintf ("\t%s\n", strings[i]);
free (strings);
}
@@ -110,19 +187,13 @@ static void err (blobstore_error_t error, const char * custom_msg)
msg = blobstore_get_error_str (error);
}
if (_do_print_errors) {
- printf ("error: %s\n", msg); // TODO: add logging hooks
- //print_trace ();
+ myprintf ("error: %s\n", msg);
+ if (_do_print_trace)
+ print_trace ();
}
_blobstore_errno = error;
}
-/*
-static void logg (unsigned int level, const char * msg)
-{
- printf ("%s\n", msg);
-}
-*/
-
__INLINE__ static void propagate_system_errno (blobstore_error_t default_errno)
{
switch (errno) {
@@ -134,12 +205,17 @@ __INLINE__ static void propagate_system_errno (blobstore_error_t default_errno)
case ENOSPC: _blobstore_errno = BLOBSTORE_ERROR_NOSPC; break;
case EAGAIN: _blobstore_errno = BLOBSTORE_ERROR_AGAIN; break;
default:
- perror ("blobstore"); // TODO: remove?
+ //perror ("blobstore");
_blobstore_errno = default_errno;
}
err (_blobstore_errno, NULL); // print the message
}
+void blobstore_set_error_function ( void (* fn) (const char * msg) )
+{
+ err_fn = fn;
+}
+
static void gen_id (char * str, unsigned int size)
{
struct timeval tv;
@@ -458,13 +534,37 @@ static int write_store_metadata (blobstore * bs)
return 0;
}
+enum {
+ DMSETUP,
+ ROOTWRAP,
+ LASTHELPER
+};
+
+static char * helpers [LASTHELPER] = {
+ "dmsetup",
+ "euca_rootwrap",
+};
+
+static char * helpers_path [LASTHELPER];
+static int initialized = 0;
+
static int blobstore_init (void)
{
- logfile (NULL, EUCAWARN);
- int ret = diskutil_init();
- if (ret) {
- err (BLOBSTORE_ERROR_UNKNOWN, "failed to initialize blobstore library");
+ int ret = 0;
+
+ if (!initialized) {
+ ret = diskutil_init();
+ if (ret) {
+ err (BLOBSTORE_ERROR_UNKNOWN, "failed to initialize diskutil library");
+ } else {
+ ret = verify_helpers (helpers, helpers_path, LASTHELPER);
+ if (ret) {
+ err (BLOBSTORE_ERROR_UNKNOWN, "failed to initialize blobstore library");
+ }
+ }
+ initialized = 1;
}
+
return ret;
}
@@ -913,55 +1013,6 @@ static int delete_blockblob_files (const blobstore * bs, const char * bb_id)
return count;
}
-// given path=A/B/C and only A existing, create A/B and, unless
-// is_file_path==1, also create A/B/C directory
-// returns: 0 = path already existed, 1 = created OK, -1 = error
-static int ensure_directories_exist (const char * path, int is_file_path, mode_t mode)
-{
- int len = strlen (path);
- char * path_copy = NULL;
- int ret = 0;
- int i;
-
- if (len>0)
- path_copy = strdup (path);
-
- if (path_copy==NULL)
- return -1;
-
- for (i=0; i<len; i++) {
- struct stat buf;
- int try_dir = 0;
-
- if (path[i]=='/' && i>0) { // dir path, not root
- path_copy[i] = '\0';
- try_dir = 1;
-
- } else if (path[i]!='/' && i+1==len) { // last one
- if (!is_file_path)
- try_dir = 1;
- }
-
- if ( try_dir ) {
- if ( stat (path_copy, &buf) == -1 ) {
- logprintfl (EUCAINFO, "creating path %s\n", path_copy);
-
- if ( mkdir (path_copy, mode) == -1) {
- logprintfl (EUCAERROR, "error: failed to create path %s: %s\n", path_copy, strerror (errno));
-
- free (path_copy);
- return -1;
- }
- ret = 1; // we created a directory
- }
- path_copy[i] = '/'; // restore the slash
- }
- }
-
- free (path_copy);
- return ret;
-}
-
// helper for ensuring a directory required by blob exists
// returns: 0 = already existed, 1 = created OK, -1 = error
static int ensure_blockblob_metadata_path (const blobstore * bs, const char * bb_id)
@@ -1080,10 +1131,10 @@ static blockblob ** walk_bs (blobstore * bs, const char * dir_path, blockblob **
strncpy (bb->id, blob_id, sizeof(bb->id));
strncpy (bb->blocks_path, entry_path, sizeof(bb->blocks_path));
set_device_path (bb); // read .dm and .loopback and set bb->device_path accordingly
- bb->size_blocks = (sb.st_size/512);
+ bb->size_bytes = sb.st_size;
bb->last_accessed = sb.st_atime;
bb->last_modified = sb.st_mtime;
- bb->snapshot_type = BLOBSTORE_FORMAT_ANY;
+ bb->snapshot_type = BLOBSTORE_FORMAT_ANY; // it is not necessary to know whether this is a snapshot
bb->in_use = check_in_use (bs, bb->id, 0);
}
@@ -1135,8 +1186,8 @@ static long long purge_blockblobs_lru ( blobstore * bs, blockblob * bb_list, lon
bb = bb_array [i];
if (! (bb->in_use & ~BLOCKBLOB_STATUS_BACKED) ) {
if (delete_blockblob_files (bs, bb->id)>0) {
- purged += bb->size_blocks;
- printf ("purged from blobstore %s blockblob %s of size %lld (total purged in this sweep %lld)\n", bs->id, bb->id, bb->size_blocks, purged);
+ purged += round_up_sec (bb->size_bytes) / 512;
+ myprintf ("purged from blobstore %s blockblob %s (total blocks purged in this sweep %lld)\n", bs->id, bb->id, purged);
}
}
if (purged>=need_blocks)
@@ -1150,11 +1201,13 @@ static long long purge_blockblobs_lru ( blobstore * bs, blockblob * bb_list, lon
blockblob * blockblob_open ( blobstore * bs,
const char * id, // can be NULL if creating, in which case blobstore will pick a random ID
- unsigned long long size_blocks, // on create: reserve this size; on open: verify the size, unless set to 0
+ unsigned long long size_bytes, // on create: reserve this size; on open: verify the size, unless set to 0
unsigned int flags, // BLOBSTORE_FLAG_CREAT | BLOBSTORE_FLAG_EXCL - same semantcs as for open() flags
const char * sig, // if non-NULL, on create sig is recorded, on open it is verified
unsigned long long timeout ) // maximum wait, in milliseconds
{
+ long long size_blocks = round_up_sec (size_bytes) / 512;
+
if (flags & ~(BLOBSTORE_FLAG_CREAT | BLOBSTORE_FLAG_EXCL)) {
err (BLOBSTORE_ERROR_INVAL, "only _CREAT and _EXCL flags are allowed");
return NULL;
@@ -1169,7 +1222,7 @@ blockblob * blockblob_open ( blobstore * bs,
}
if (size_blocks!=0 && (flags & BLOBSTORE_FLAG_CREAT) && (size_blocks > bs->limit_blocks)) {
err (BLOBSTORE_ERROR_NOSPC, NULL);
- return NULL;
+ return NULL;
}
blockblob * bbs = NULL; // a temp LL of blockblobs, used for computing free space and for purging
@@ -1185,7 +1238,7 @@ blockblob * blockblob_open ( blobstore * bs,
} else {
gen_id (bb->id, sizeof(bb->id));
}
- bb->size_blocks = size_blocks;
+ bb->size_bytes = size_bytes;
set_blockblob_metadata_path (BLOCKBLOB_PATH_BLOCKS, bs, bb->id, bb->blocks_path, sizeof (bb->blocks_path));
if (blobstore_lock(bs, timeout)==-1) { // lock it so we can traverse it (TODO: move this into creation-only section?)
@@ -1224,23 +1277,24 @@ blockblob * blockblob_open ( blobstore * bs,
long long blocks_inuse = 0;
unsigned int blobs_total = 0;
for (blockblob * abb = bbs; abb; abb=abb->next) {
+ long long abb_size_blocks = round_up_sec (abb->size_bytes) / 512;
if (abb->in_use & ~BLOCKBLOB_STATUS_BACKED) {
- blocks_inuse += abb->size_blocks; // these can't be purged if we need space (TODO: look into recursive purging of unused references?)
+ blocks_inuse += abb_size_blocks; // these can't be purged if we need space (TODO: look into recursive purging of unused references?)
} else {
- blocks_allocated += abb->size_blocks; // these can be purged
+ blocks_allocated += abb_size_blocks; // these can be purged
}
blobs_total++;
}
long long blocks_free = bs->limit_blocks - (blocks_allocated + blocks_inuse);
- if (blocks_free < bb->size_blocks) {
+ if (blocks_free < size_blocks) {
if (!(bs->revocation_policy==BLOBSTORE_REVOCATION_LRU) // not allowed to purge
||
- (blocks_free+blocks_allocated) < bb->size_blocks) { // not enough purgeable material
+ (blocks_free+blocks_allocated) < size_blocks) { // not enough purgeable material
err (BLOBSTORE_ERROR_NOSPC, NULL);
goto clean;
}
- long long blocks_needed = bb->size_blocks-blocks_free;
+ long long blocks_needed = size_blocks-blocks_free;
_err_off(); // do not care about errors duing purging
long long blocks_freed = purge_blockblobs_lru (bs, bbs, blocks_needed);
_err_on();
@@ -1250,27 +1304,39 @@ blockblob * blockblob_open ( blobstore * bs,
}
}
- if (lseek (bb->fd, (bb->size_blocks*512)-1, SEEK_CUR)==(off_t) -1) { // create a file with a hole
+ if (lseek (bb->fd, size_bytes - 1, SEEK_CUR) == (off_t)-1) { // create a file with a hole
propagate_system_errno (BLOBSTORE_ERROR_UNKNOWN);
goto clean;
}
- if (write (bb->fd, zero_buf, 1)!=(ssize_t)1) {
+ if (write (bb->fd, zero_buf, 1) != (ssize_t)1) {
propagate_system_errno (BLOBSTORE_ERROR_UNKNOWN);
goto clean;
}
if (sig)
if (write_blockblob_metadata_path (BLOCKBLOB_PATH_SIG, bs, bb->id, sig))
goto clean;
+ bb->snapshot_type = BLOBSTORE_SNAPSHOT_NONE; // just created, so not a snapshot
} else { // blob existed
char buf [1024];
- if (bb->size_blocks==0) {
- bb->size_blocks = (sb.st_size/512);
- } else if (bb->size_blocks != (sb.st_size/512)) { // check the size
- err (BLOBSTORE_ERROR_INVAL, "size of the existing blockblob does not match");
+
+ if (bb->size_bytes==0) { // find out the size from the file size
+ bb->size_bytes = sb.st_size;
+ } else if (bb->size_bytes != sb.st_size) { // verify the size specified by the user
+ err (BLOBSTORE_ERROR_SIGNATURE, "size of the existing blockblob does not match");
goto clean;
}
+
+ // determine whether this blob is a map of another,
+ // in which case the blocks are backing and should
+ // not be accessed directly
+ if (read_blockblob_metadata_path (BLOCKBLOB_PATH_DM, bs, bb->id, buf, sizeof (buf)) > 0) {
+ bb->snapshot_type = BLOBSTORE_SNAPSHOT_DM;
+ } else {
+ bb->snapshot_type = BLOBSTORE_SNAPSHOT_NONE;
+ }
+
if (sig) { // check the signature, if there
int sig_size;
if ((sig_size=read_blockblob_metadata_path (BLOCKBLOB_PATH_SIG, bs, bb->id, buf, sizeof (buf)))!=strlen(sig)
@@ -1395,13 +1461,13 @@ static int dm_suspend_resume (const char * dev_name)
{
char cmd [1024];
- snprintf (cmd, sizeof (cmd), "%s suspend %s", DMSETUP, dev_name);
+ snprintf (cmd, sizeof (cmd), "%s %s suspend %s", helpers_path [ROOTWRAP], helpers_path [DMSETUP], dev_name);
int status = system (cmd);
if (status == -1 || WEXITSTATUS(status) != 0) {
err (BLOBSTORE_ERROR_UNKNOWN, "failed to suspend device with 'dmsetup'");
return -1;
}
- snprintf (cmd, sizeof (cmd), "%s resume %s", DMSETUP, dev_name);
+ snprintf (cmd, sizeof (cmd), "%s %s resume %s", helpers_path [ROOTWRAP], helpers_path [DMSETUP], dev_name);
status = system (cmd);
if (status == -1 || WEXITSTATUS(status) != 0) {
err (BLOBSTORE_ERROR_UNKNOWN, "failed to resume device with 'dmsetup'");
@@ -1440,7 +1506,7 @@ static int dm_delete_devices (char * dev_names[], int size)
char cmd [1024];
int retries = 1;
try_again:
- snprintf (cmd, sizeof (cmd), "%s remove %s", DMSETUP, dev_names_removable [i]);
+ snprintf (cmd, sizeof (cmd), "%s %s remove %s", helpers_path [ROOTWRAP], helpers_path [DMSETUP], dev_names_removable [i]);
int status = system (cmd);
if (status == -1 || WEXITSTATUS(status) != 0) {
if (retries--) {
@@ -1462,7 +1528,8 @@ static int dm_create_devices (char * dev_names[], char * dm_tables[], int size)
for (i=0; i<size; i++) {
int pipefds [2];
-
+ myprintf ("creating device %s\n", dev_names [i]);
+
if (pipe (pipefds) == -1) {
propagate_system_errno (BLOBSTORE_ERROR_UNKNOWN);
goto cleanup;
@@ -1480,25 +1547,22 @@ static int dm_create_devices (char * dev_names[], char * dm_tables[], int size)
propagate_system_errno (BLOBSTORE_ERROR_UNKNOWN);
_exit (1);
}
- _exit (execl (DMSETUP, DMSETUP, "create", dev_names[i], NULL));
+ _exit (execl (helpers_path [ROOTWRAP], helpers_path [ROOTWRAP], helpers_path [DMSETUP], "create", dev_names[i], NULL));
} else { // parent
close (pipefds [0]);
write (pipefds [1], dm_tables [i], strlen (dm_tables [i]));
close (pipefds [1]);
- /*
- printf ("%s create %s\n===\n", DMSETUP, dev_names[i]);
- write (1, dm_tables [i], strlen (dm_tables [i]));
- printf ("===\n");
- fsync (1);
- */
+
int status;
if (waitpid (cpid, &status, 0) == -1) {
propagate_system_errno (BLOBSTORE_ERROR_UNKNOWN);
goto cleanup;
}
if (WEXITSTATUS(status) != 0) {
err (BLOBSTORE_ERROR_UNKNOWN, "failed to set up device mapper table with 'dmsetup'");
+ myprintf ("command: %s %s create %s\n", helpers_path [ROOTWRAP], helpers_path [DMSETUP], dev_names[i]);
+ myprintf ("input: %s", dm_tables [i]);
goto cleanup;
}
@@ -1646,6 +1710,81 @@ int blockblob_delete ( blockblob * bb, long long timeout_usec )
return ret;
}
+static int verify_bb ( const blockblob * bb, unsigned long long min_size_bytes )
+{
+ if (bb->fd==-1) {
+ err (BLOBSTORE_ERROR_INVAL, "blockblob involved in operation is not open");
+ return -1;
+ }
+ struct stat sb;
+ if (fstat (bb->fd, &sb)==-1) {
+ propagate_system_errno (BLOBSTORE_ERROR_NOENT);
+ return -1;
+ }
+ if (sb.st_size < bb->size_bytes) {
+ err (BLOBSTORE_ERROR_UNKNOWN, "blockblob involved in operation has backing of unexpected size");
+ return -1;
+ }
+ if (sb.st_size < min_size_bytes) {
+ err (BLOBSTORE_ERROR_INVAL, "blockblob involved in operation has backing that is too small");
+ return -1;
+ }
+ if (stat (bb->device_path, &sb)==-1) {
+ propagate_system_errno (BLOBSTORE_ERROR_NOENT);
+ return -1;
+ }
+ if (!S_ISBLK(sb.st_mode)) {
+ err (BLOBSTORE_ERROR_INVAL, "blockblob involved in operation is missing a loopback block device");
+ return -1;
+ }
+ return 0;
+}
+
+int blockblob_copy ( blockblob * src_bb, // source blob to copy data from
+ unsigned long long src_offset_bytes, // start offset in source
+ blockblob * dst_bb, // destination blob to copy data to
+ unsigned long long dst_offset_bytes, // start offset in destination
+ unsigned long long len_bytes) // 0 = copy until EOF of source
+{
+ int ret = 0;
+
+ if (src_bb==NULL || dst_bb==NULL) {
+ err (BLOBSTORE_ERROR_INVAL, "blockblob pointer is NULL");
+ return -1;
+ }
+
+ long long copy_len_bytes = len_bytes;
+ if (copy_len_bytes==0) {
+ copy_len_bytes = src_bb->size_bytes - src_offset_bytes;
+ }
+ if (copy_len_bytes<1) {
+ err (BLOBSTORE_ERROR_INVAL, "copy source offset outside of range");
+ return -1;
+ }
+
+ // make sure both source and destination blobs are in good shape and big enough
+ if (verify_bb (src_bb, src_offset_bytes + copy_len_bytes) ||
+ verify_bb (dst_bb, dst_offset_bytes + copy_len_bytes)) {
+ return -1;
+ }
+
+ // determine the largest acceptable block size for dd, all the way down to a byte possibly
+ int granularity = 4096;
+ while (src_offset_bytes % granularity || dst_offset_bytes % granularity || copy_len_bytes % granularity) {
+ granularity /= 2;
+ }
+
+ // do the copy (with block devices dd will silently omit to copy bytes outside the block boundary, so we use paths for uncloned blobs)
+ const char * src_path = (src_bb->snapshot_type == BLOBSTORE_SNAPSHOT_DM)?(blockblob_get_dev (src_bb)):(blockblob_get_file (src_bb));
+ const char * dst_path = (dst_bb->snapshot_type == BLOBSTORE_SNAPSHOT_DM)?(blockblob_get_dev (dst_bb)):(blockblob_get_file (dst_bb));
+ if (diskutil_dd2 (src_path, dst_path, granularity, copy_len_bytes/granularity, dst_offset_bytes/granularity, src_offset_bytes/granularity)) {
+ err (BLOBSTORE_ERROR_INVAL, "failed to copy a section");
+ return -1;
+ }
+
+ return ret;
+}
+
int blockblob_clone ( blockblob * bb, // destination blob, which blocks may be used as backing
const blockmap * map, // map of blocks from other blobs/devices to be copied/mapped/snapshotted
unsigned int map_size ) // size of the map []
@@ -1660,6 +1799,7 @@ int blockblob_clone ( blockblob * bb, // destination blob, which blocks may be u
err (BLOBSTORE_ERROR_INVAL, "invalid blockbmap or its size");
return -1;
}
+ long long bb_size_blocks = round_down_sec (bb->size_bytes) / 512; // dmsetup will not map partial blocks, so we conservatively round down
// verify dependencies (block devices present, blob sizes make sense, zero device present)
char * zero_dev = NULL;
@@ -1695,36 +1835,20 @@ int blockblob_clone ( blockblob * bb, // destination blob, which blocks may be u
err (BLOBSTORE_ERROR_INVAL, "one of the source blockblob pointers is NULL");
return -1;
}
- if (sbb->fd==-1) {
- err (BLOBSTORE_ERROR_INVAL, "one of the source blockblobs is not open");
- return -1;
- }
- struct stat sb;
- if (fstat (sbb->fd, &sb)==-1) {
- propagate_system_errno (BLOBSTORE_ERROR_NOENT);
- return -1;
- }
- if (sb.st_size/512 < sbb->size_blocks) {
- err (BLOBSTORE_ERROR_INVAL, "one of the source blockblobs has backing that is too small");
- return -1;
- }
- if (stat (sbb->device_path, &sb)==-1) {
- propagate_system_errno (BLOBSTORE_ERROR_NOENT);
+ long long sbb_size_blocks = round_down_sec (sbb->size_bytes) / 512; // dmsetup will not map partial blocks, so we conservatively round down
+ if (verify_bb (sbb, sbb_size_blocks)) {
return -1;
}
- if (!S_ISBLK(sb.st_mode)) {
- err (BLOBSTORE_ERROR_INVAL, "one of the source blockblobs is missing a loopback block device");
- return -1;
- }
- if (sbb->size_blocks < (m->first_block_src + m->len_blocks)) {
+ if (sbb_size_blocks < (m->first_block_src + m->len_blocks)) {
err (BLOBSTORE_ERROR_INVAL, "one of the source blockblobs is too small for the map");
return -1;
}
- if (bb->size_blocks < (m->first_block_dst + m->len_blocks)) {
+ if (bb_size_blocks < (m->first_block_dst + m->len_blocks)) {
err (BLOBSTORE_ERROR_INVAL, "the destination blockblob is too small for the map");
return -1;
}
if (m->relation_type==BLOBSTORE_SNAPSHOT && m->len_blocks < MIN_BLOCKS_SNAPSHOT) {
+ printf ("len_blocks = %lld\n", m->len_blocks);
err (BLOBSTORE_ERROR_INVAL, "snapshot size is too small");
return -1;
}
@@ -1872,7 +1996,8 @@ int blockblob_clone ( blockblob * bb, // destination blob, which blocks may be u
ret = -1;
goto cleanup;
}
-
+ bb->snapshot_type = BLOBSTORE_SNAPSHOT_DM; // remember that blobstore uses device mapper
+
// update .refs on dependencies and create .deps for this blob
char my_ref [BLOBSTORE_MAX_PATH+MAX_DM_NAME+1];
snprintf (my_ref, sizeof (my_ref), "%s %s", bb->store->path, bb->id); // TODO: use store ID to proof against moving blobstore?
@@ -1929,28 +2054,38 @@ const char * blockblob_get_dev ( blockblob * bb )
return bb->device_path;
}
-// returns a path to the file containg the blob, but only if snapshot_type={ANY|NONE}
+// returns a path to the file containg the blob, but only if snapshot_type!=DM
const char * blockblob_get_file ( blockblob * bb )
{
if (bb==NULL) {
err (BLOBSTORE_ERROR_INVAL,NULL);
return NULL;
}
- if (bb->snapshot_type!=BLOBSTORE_SNAPSHOT_ANY && bb->snapshot_type!=BLOBSTORE_SNAPSHOT_DM) {
- err (BLOBSTORE_ERROR_INVAL,"device paths only supported for blockblobs with snapshots");
+ if (bb->snapshot_type==BLOBSTORE_SNAPSHOT_DM) {
+ err (BLOBSTORE_ERROR_INVAL, "file access only supported for uncloned blockblobs");
return NULL;
}
return bb->blocks_path;
}
// size of blob in blocks
-unsigned long long blockblob_get_size ( blockblob * bb)
+unsigned long long blockblob_get_size_blocks ( blockblob * bb)
+{
+ if (bb==NULL) {
+ err (BLOBSTORE_ERROR_INVAL,NULL);
+ return 0;
+ }
+ return round_up_sec (bb->size_bytes) / 512;
+}
+
+// size of blob in bytes
+unsigned long long blockblob_get_size_bytes ( blockblob * bb)
{
if (bb==NULL) {
err (BLOBSTORE_ERROR_INVAL,NULL);
return 0;
}
- return bb->size_blocks;
+ return bb->size_bytes;
}
/////////////////////////////////////////////// unit testing code ///////////////////////////////////////////////////
@@ -1989,14 +2124,24 @@ unsigned long long blockblob_get_size ( blockblob * bb)
#define _CBB BLOBSTORE_FLAG_CREAT|BLOBSTORE_FLAG_EXCL
#define B1 "BLOCKBLOB-01"
-#define B2 "FOO/BLOCKBLOB-02"
-#define B3 "FOO/BAR/BLOCKBLOB-03"
-#define B4 "FOO/BAR/BAZ/BLOCKBLOB-04"
+#define B2 "BLOCKBLOB-02"
+#define B3 "BLOCKBLOB-03"
+#define B4 "BLOCKBLOB-04"
+//#define B2 "FOO/BLOCKBLOB-02"
+//#define B3 "FOO/BAR/BLOCKBLOB-03"
+//#define B4 "FOO/BAR/BAZ/BLOCKBLOB-04"
#define B5 "BLOCKBLOB-05"
#define B6 "BLOCKBLOB-06"
#define _OPENBB(BB,ID,SI,SG,FL,TI,RE) _blobstore_errno=0; \
printf ("%d: bb_open (%s size=%d flags=%d timeout=%d)", getpid(), (ID==NULL)?("null"):(ID), SI, FL, TI); \
+ BB=blockblob_open (bs, ID, (SI)*512, FL, SG, TI); \
+ printf ("=%s errno=%d '%s'\n", (BB==NULL)?("NULL"):("OK"), _blobstore_errno, blobstore_get_error_str(_blobstore_errno)); \
+ if ((BB==NULL) && (_blobstore_errno==0)) printf ("======================> UNSET errno ON ERROR (errors=%d)!!!\n", ++errors); \
+ else if ((RE==-1 && BB!=NULL) || (RE==0 && BB==NULL)) _UNEXPECTED;
+// same as _OPENBB but accepts bytes rather than blocks
+#define _OPENBBb(BB,ID,SI,SG,FL,TI,RE) _blobstore_errno=0; \
+ printf ("%d: bb_open (%s size=%d flags=%d timeout=%d)", getpid(), (ID==NULL)?("null"):(ID), SI, FL, TI); \
BB=blockblob_open (bs, ID, SI, FL, SG, TI); \
printf ("=%s errno=%d '%s'\n", (BB==NULL)?("NULL"):("OK"), _blobstore_errno, blobstore_get_error_str(_blobstore_errno)); \
if ((BB==NULL) && (_blobstore_errno==0)) printf ("======================> UNSET errno ON ERROR (errors=%d)!!!\n", ++errors); \
@@ -2015,31 +2160,50 @@ unsigned long long blockblob_get_size ( blockblob * bb)
printf ("=%d errno=%d '%s'\n", ret, _blobstore_errno, blobstore_get_error_str(_blobstore_errno)); \
if ((ret==-1) && (_blobstore_errno==0)) printf ("======================> UNSET errno ON ERROR (errors=%d)!!!\n", ++errors); \
else if (RE!=ret) _UNEXPECTED;
-
+
+#define _COPYBB(SBB,SO,DBB,DO,LEN,RE) _blobstore_errno=0; \
+ printf ("%d: bb_copy (%s to %s)", getpid(), SBB->id, DBB->id); \
+ ret=blockblob_copy(SBB,SO,DBB,DO,LEN); \
+ printf ("=%d errno=%d '%s'\n", ret, _blobstore_errno, blobstore_get_error_str(_blobstore_errno)); \
+ if ((ret==-1) && (_blobstore_errno==0)) printf ("======================> UNSET errno ON ERROR (errors=%d)!!!\n", ++errors); \
+ else if (RE!=ret) _UNEXPECTED;
+
#define BS_SIZE 30
#define BB_SIZE 10
#define CBB_SIZE 32
-#define STRESS_BS_SIZE 1000000
+#define STRESS_BS_SIZE 100000
#define STRESS_MIN_BB 64
-#define STRESS_BLOBS 80
+#define STRESS_BLOBS 10
-static void _fill_blob (blockblob * bb, char c)
+static void _fill_blob (blockblob * bb, char c, int use_file)
{
- const char * path = blockblob_get_dev (bb);
+ const char * path;
+ if (use_file) {
+ path = blockblob_get_file (bb);
+ } else {
+ path = blockblob_get_dev (bb);
+ }
+
char buf [1];
buf [0] = c;
+ printf ("filling out with dummy data %s\n", path);
int fd = open (path, O_WRONLY);
+ int failed_bytes = 0;
if (fd!=-1) {
- for (int i=0; i<bb->size_blocks*512; i++) {
- write (fd, buf, 1);
+ for (int i=0; i<bb->size_bytes; i++) {
+ if (write (fd, buf, 1)!=1)
+ failed_bytes++;
}
}
+ if (failed_bytes) {
+ printf ("WARNING: failed to fill %d byte(s) to path %s\n", failed_bytes, path);
+ }
fsync (fd);
close (fd);
}
-static blobstore * create_teststore (int size, const char * base, const char * name, blobstore_format_t format, blobstore_revocation_t revocation, blobstore_snapshot_t snapshot)
+static blobstore * create_teststore (int size_blocks, const char * base, const char * name, blobstore_format_t format, blobstore_revocation_t revocation, blobstore_snapshot_t snapshot)
{
static int ts = 0;
static int counter = 0;
@@ -2056,7 +2220,7 @@ static blobstore * create_teststore (int size, const char * base, const char * n
return NULL;
}
printf ("created %s\n", bs_path);
- blobstore * bs = blobstore_open (bs_path, size, format, revocation, snapshot);
+ blobstore * bs = blobstore_open (bs_path, size_blocks, format, revocation, snapshot);
if (bs==NULL) {
printf ("ERROR: %s\n", blobstore_get_error_str(blobstore_get_error()));
return NULL;
@@ -2169,7 +2333,7 @@ static int do_clone_stresstest (const char * base, const char * name, blobstore_
// fill the stores
for (int i=0; i<STRESS_BLOBS; i++) {
#define _OPENERR(BS,BB,BBSIZE) \
- BB = blockblob_open (BS, NULL, BBSIZE, BLOBSTORE_FLAG_CREAT | BLOBSTORE_FLAG_EXCL, NULL, 1000); \
+ BB = blockblob_open (BS, NULL, BBSIZE*512, BLOBSTORE_FLAG_CREAT | BLOBSTORE_FLAG_EXCL, NULL, 1000); \
if (BB == NULL) { \
printf ("ERROR: failed to create blockblob i=%d\n", i); \
errors++; \
@@ -2270,35 +2434,9 @@ static int do_clone_stresstest (const char * base, const char * name, blobstore_
return errors;
}
-static int do_clone_test (const char * base, const char * name, blobstore_format_t format, blobstore_revocation_t revocation, blobstore_snapshot_t snapshot)
+static int check_destination (blockblob * bb4, char * op)
{
- int ret;
int errors = 0;
- printf ("commencing cloning test\n");
-
- blobstore * bs = create_teststore (CBB_SIZE*6, base, name, BLOBSTORE_FORMAT_DIRECTORY, BLOBSTORE_REVOCATION_ANY, BLOBSTORE_SNAPSHOT_ANY);
- if (bs==NULL) { errors++; goto done; }
-
- blockblob * bb1, * bb2, * bb3, * bb4, * bb5, * bb6;
-
- // these are to be mapped to others
- _OPENBB(bb1,B1,CBB_SIZE,NULL,_CBB,0,0); // bs size: 1
- _fill_blob (bb1, '1');
- _OPENBB(bb2,B2,CBB_SIZE,NULL,_CBB,0,0); // bs size: 2
- _fill_blob (bb2, '2');
- _OPENBB(bb3,B3,CBB_SIZE,NULL,_CBB,0,0); // bs size: 3
- _fill_blob (bb3, '3');
-
- // these are to be clones
- _OPENBB(bb4,B4,CBB_SIZE*3,NULL,_CBB,0,0); // bs size: 6
- blockmap bm1 [] = {
- {BLOBSTORE_MAP, BLOBSTORE_BLOCKBLOB, {blob:bb1}, 0, 0, CBB_SIZE},
- {BLOBSTORE_COPY, BLOBSTORE_BLOCKBLOB, {blob:bb2}, 0, CBB_SIZE, CBB_SIZE},
- {BLOBSTORE_SNAPSHOT, BLOBSTORE_BLOCKBLOB, {blob:bb3}, 0, CBB_SIZE*2, CBB_SIZE},
- };
- _CLONBB(bb4,B4,bm1,0);
-
- // see if cloning worked
const char * dev = blockblob_get_dev (bb4);
if (dev!=NULL) {
int fd = open (dev, O_RDONLY);
@@ -2322,14 +2460,93 @@ static int do_clone_test (const char * base, const char * name, blobstore_format
stop_comparing:
close (fd);
} else {
- printf ("ERROR: failed to open block device %s for the clone\n", dev);
+ printf ("ERROR: failed to open block device %s for the %s\n", dev, op);
errors++;
}
} else {
- printf ("ERROR: failed to get a block device for the clone\n");
+ printf ("ERROR: failed to get a block device for the %s\n", op);
errors++;
}
+ return errors;
+}
+
+static int do_copy_test (const char * base, const char * name)
+{
+ int ret;
+ int errors = 0;
+ printf ("commencing copy test\n");
+
+ blobstore * bs = create_teststore (CBB_SIZE*7, base, name, BLOBSTORE_FORMAT_DIRECTORY, BLOBSTORE_REVOCATION_ANY, BLOBSTORE_SNAPSHOT_ANY);
+ if (bs==NULL) { errors++; goto done; }
+
+ blockblob * bb1, * bb2, * bb3, * bb4;
+
+ // these are to be copied to another
+ _OPENBBb(bb1,B1,CBB_SIZE*512*7+1,NULL,_CBB,0,-1); // too big for bs
+ if (errors) goto done;
+ _OPENBBb(bb1,B1,CBB_SIZE*512,NULL,_CBB,0,0); // bs size: 1
+ _fill_blob (bb1, '1', TRUE);
+ _OPENBBb(bb2,B2,CBB_SIZE*512+1,NULL,_CBB,0,0); // bs size: 3
+ _fill_blob (bb2, '2', TRUE);
+ _OPENBBb(bb3,B3,CBB_SIZE*512-2,NULL,_CBB,0,0); // bs size: 4
+ _fill_blob (bb3, '3', TRUE);
+
+ // this is to be the destination of the copy
+ _OPENBB(bb4,B4,CBB_SIZE*3,NULL,_CBB,0,0); // bs size: 7
+ _COPYBB(bb1,0,bb4,0,0,0); // check that len=0 works and that right block size is chosen
+ _COPYBB(bb2,0,bb4,CBB_SIZE*512,CBB_SIZE*512+1,0);
+ _COPYBB(bb3,0,bb4,CBB_SIZE*512*2,CBB_SIZE*512-2,0);
+ _COPYBB(bb3,0,bb4,CBB_SIZE*512*3-2,2,0);
+ _COPYBB(bb3,0,bb4,CBB_SIZE*512*2,CBB_SIZE*512,-1); // source is too small
+ _COPYBB(bb3,2,bb4,CBB_SIZE*512*2,CBB_SIZE*512,-1); // source is too small
+ _COPYBB(bb3,0,bb4,CBB_SIZE*512*3-1,2,-1); // destination is too small
+
+ // see if copy worked
+ errors += check_destination (bb4, "copy");
+
+ _DELEBB(bb1,B1,0);
+ _DELEBB(bb2,B2,0);
+ _DELEBB(bb3,B3,0);
+ _DELEBB(bb4,B4,0);
+ blobstore_close (bs);
+
+ printf ("completed copy test\n");
+ done:
+ return errors;
+}
+
+static int do_clone_test (const char * base, const char * name, blobstore_format_t format, blobstore_revocation_t revocation, blobstore_snapshot_t snapshot)
+{
+ int ret;
+ int errors = 0;
+ printf ("commencing cloning test\n");
+
+ blobstore * bs = create_teststore (CBB_SIZE*6, base, name, BLOBSTORE_FORMAT_DIRECTORY, BLOBSTORE_REVOCATION_ANY, BLOBSTORE_SNAPSHOT_ANY);
+ if (bs==NULL) { errors++; goto done; }
+
+ blockblob * bb1, * bb2, * bb3, * bb4, * bb5, * bb6;
+
+ // these are to be mapped to others
+ _OPENBB(bb1,B1,CBB_SIZE,NULL,_CBB,0,0); // bs size: 1
+ _fill_blob (bb1, '1', FALSE);
+ _OPENBB(bb2,B2,CBB_SIZE,NULL,_CBB,0,0); // bs size: 2
+ _fill_blob (bb2, '2', FALSE);
+ _OPENBB(bb3,B3,CBB_SIZE,NULL,_CBB,0,0); // bs size: 3
+ _fill_blob (bb3, '3', FALSE);
+
+ // these are to be clones
+ _OPENBB(bb4,B4,CBB_SIZE*3,NULL,_CBB,0,0); // bs size: 6
+ blockmap bm1 [] = {
+ {BLOBSTORE_MAP, BLOBSTORE_BLOCKBLOB, {blob:bb1}, 0, 0, CBB_SIZE},
+ {BLOBSTORE_COPY, BLOBSTORE_BLOCKBLOB, {blob:bb2}, 0, CBB_SIZE, CBB_SIZE},
+ {BLOBSTORE_SNAPSHOT, BLOBSTORE_BLOCKBLOB, {blob:bb3}, 0, CBB_SIZE*2, CBB_SIZE},
+ };
+ _CLONBB(bb4,B4,bm1,0);
+
+ // see if cloning worked
+ errors += check_destination (bb4, "clone");
+
_DELEBB(bb1,B1,-1); // referenced, not deletable
_DELEBB(bb2,B2,0); // not referenced, deletable
_DELEBB(bb3,B3,-1); // referenced, not deletable
@@ -2347,7 +2564,7 @@ static int do_clone_test (const char * base, const char * name, blobstore_format
errors++;
goto done;
}
- bb5 = blockblob_open (bs2, B5, CBB_SIZE*3, BLOBSTORE_FLAG_CREAT, NULL, 0);
+ bb5 = blockblob_open (bs2, B5, CBB_SIZE*3*512, BLOBSTORE_FLAG_CREAT, NULL, 0);
if (bb5==NULL) {
errors++;
goto done;
@@ -2674,25 +2891,35 @@ int do_file_lock_test (void)
int main (int argc, char ** argv)
{
int errors = 0;
+ char cwd [1024];
+ getcwd (cwd, sizeof (cwd));
printf ("testing blobstore.c\n");
errors += do_file_lock_test ();
if (errors) goto done; // no point in doing blobstore test if above isn't working
- char cwd [1024];
- getcwd (cwd, sizeof (cwd));
-
errors += do_metadata_test (cwd, "directory-meta");
if (errors) goto done; // no point in doing blobstore test if above isn't working
errors += do_blobstore_test (cwd, "directory-norevoc", BLOBSTORE_FORMAT_DIRECTORY, BLOBSTORE_REVOCATION_NONE);
+ if (errors) goto done; // no point in doing blobstore test if above isn't working
+
errors += do_blobstore_test (cwd, "lru-directory", BLOBSTORE_FORMAT_DIRECTORY, BLOBSTORE_REVOCATION_LRU);
+ if (errors) goto done; // no point in doing blobstore test if above isn't working
+
errors += do_blobstore_test (cwd, "lru-visible", BLOBSTORE_FORMAT_FILES, BLOBSTORE_REVOCATION_LRU);
+ if (errors) goto done; // no point in doing blobstore test if above isn't working
+
+ errors += do_copy_test (cwd, "copy");
+ if (errors) goto done; // no point in doing blobstore test if above isn't working
errors += do_clone_test (cwd, "clone", BLOBSTORE_FORMAT_DIRECTORY, BLOBSTORE_REVOCATION_LRU, BLOBSTORE_SNAPSHOT_DM);
+ if (errors) goto done; // no point in doing blobstore test if above isn't working
errors += do_clone_stresstest (cwd, "clonestress", BLOBSTORE_FORMAT_DIRECTORY, BLOBSTORE_REVOCATION_LRU, BLOBSTORE_SNAPSHOT_DM);
+ if (errors) goto done; // no point in doing blobstore test if above isn't working
+
done:
printf ("done testing blobstore.c (errors=%d)\n", errors);
blobstore_cleanup();
View
75 storage/blobstore.h
@@ -2,6 +2,66 @@
// vim: set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
/*
+ Copyright (c) 2009 Eucalyptus Systems, Inc.
+
+ 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, only version 3 of the License.
+
+ This file 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, see <http://www.gnu.org/licenses/>.
+
+ Please contact Eucalyptus Systems, Inc., 130 Castilian
+ Dr., Goleta, CA 93101 USA or visit <http://www.eucalyptus.com/licenses/>
+ if you need additional information or have any questions.
+
+ This file may incorporate work covered under the following copyright and
+ permission notice:
+
+ Software License Agreement (BSD License)
+
+ Copyright (c) 2008, Regents of the University of California
+
+
+ Redistribution and use of this software in source and binary forms, with
+ or without modification, are permitted provided that the following
+ conditions are met:
+
+ Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. USERS OF
+ THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE
+ LICENSED MATERIAL, COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS
+ SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
+ IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
+ BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
+ THE REGENTS’ DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
+ OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
+ WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
+ ANY SUCH LICENSES OR RIGHTS.
+*/
+
+/*
* blobstore.h
*/
@@ -96,7 +156,7 @@ typedef struct _blockblob {
char blocks_path [BLOBSTORE_MAX_PATH]; // full path of the content or snapshot backing file
char device_path [BLOBSTORE_MAX_PATH]; // full path of a block device on which blob can be accessed
char dm_name [MAX_DM_NAME]; // name of the main device mapper device if this is a clone
- unsigned long long size_blocks; // size of the blob, in 512-byte blocks
+ unsigned long long size_bytes; // size of the blob in bytes
blobstore_snapshot_t snapshot_type; // ANY = not initialized/known, NONE = not a snapshot, DM = DM-based snapshot
unsigned int in_use; // flags showing how the blockblob is being used (OPENED, LOCKED, LINKED)
time_t last_accessed; // timestamp of last access
@@ -132,22 +192,29 @@ int blobstore_close ( blobstore * bs ); // releases a reference, allowing others
int blobstore_delete ( blobstore * bs ); // if no outside references to store or blobs exist, and no blobs are protected, deletes the blobs, the store metadata, and frees the blobstore handle
int blobstore_get_error ( void ); // returns code of the last error
const char * blobstore_get_error_str ( blobstore_error_t error ); // description of the error
+void blobstore_set_error_function ( void (* fn) (const char * msg)); // sets the function that will be handed error messages (instead of sending them to stdout)
// blockblob operations
blockblob * blockblob_open ( blobstore * bs,
const char * id, // can be NULL if creating, in which case blobstore will pick a random ID
- unsigned long long size_blocks, // on create: reserve this size; on open: verify the size, unless set to 0
+ unsigned long long size_bytes, // on create: reserve this size; on open: verify the size, unless set to 0
unsigned int flags, // BLOBSTORE_FLAG_CREAT | BLOBSTORE_FLAG_EXCL - same semantcs as for open() flags
const char * sig, // if non-NULL, on create sig is recorded, on open it is verified
unsigned long long timeout ); // maximum wait, in milliseconds, for a lock (0 = no blocking)
int blockblob_close ( blockblob * bb ); // releases the blob locks, allowing others to open() it, and frees the blockblob handle
int blockblob_delete ( blockblob * bb, long long timeout ); // if no outside references to the blob exist, and blob is not protected, deletes the blob, its metadata, and frees the blockblob handle
+int blockblob_copy ( blockblob * src_bb, // source blob to copy data from
+ unsigned long long src_offset_bytes, // start offset in source
+ blockblob * dst_bb, // destination blob to copy data to
+ unsigned long long dst_offset_bytes, // start offset in destination
+ unsigned long long len_bytes); // length of region to copy, 0 = copy until EOF of source
int blockblob_clone ( blockblob * bb, // destination blob
- const blockmap * map, // map of blocks from other blobs to be copied/snapshotted
+ const blockmap * map, // map of blocks from other blobs to be copied/snapshotted (NOTE: all blocks in map must have sizes that are multiples of 512)
unsigned int map_size ); // length of the map []
const char * blockblob_get_dev ( blockblob * bb ); // returns a block device pointing to the blob
const char * blockblob_get_file ( blockblob * bb ); // returns a path to the file containg the blob, but only if snapshot_type={ANY|NONE}
-unsigned long long blockblob_get_size ( blockblob * bb); // size of blob in blocks
+unsigned long long blockblob_get_size_blocks ( blockblob * bb); // size of blob in blocks
+unsigned long long blockblob_get_size_bytes ( blockblob * bb); // size of blob in bytes
#endif // _BLOBSTORE_H
View
3 storage/diskfile.c
@@ -21,9 +21,6 @@
static boolean diskutil_initialized = FALSE;
-// round up or down to sector size
-long long round_up_sec (long long bytes) { return ((bytes % SECTOR_SIZE) ? (((bytes / SECTOR_SIZE) + 1) * SECTOR_SIZE) : bytes); }
-long long round_down_sec (long long bytes) { return ((bytes % SECTOR_SIZE) ? (((bytes / SECTOR_SIZE)) * SECTOR_SIZE) : bytes); }
long long mbr_size_bytes (void) { return SECTOR_SIZE * (62 + 4); } // TODO: figure out why extra couple sectors seem needed
// allocates a diskfile object and creates an empty files of size at most limit_bytes
View
106 storage/diskutil.c
@@ -1,6 +1,66 @@
// -*- mode: C; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-
// vim: set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
+/*
+ Copyright (c) 2009 Eucalyptus Systems, Inc.
+
+ 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, only version 3 of the License.
+
+ This file 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, see <http://www.gnu.org/licenses/>.
+
+ Please contact Eucalyptus Systems, Inc., 130 Castilian
+ Dr., Goleta, CA 93101 USA or visit <http://www.eucalyptus.com/licenses/>
+ if you need additional information or have any questions.
+
+ This file may incorporate work covered under the following copyright and
+ permission notice:
+
+ Software License Agreement (BSD License)
+
+ Copyright (c) 2008, Regents of the University of California
+
+
+ Redistribution and use of this software in source and binary forms, with
+ or without modification, are permitted provided that the following
+ conditions are met:
+
+ Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. USERS OF
+ THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE
+ LICENSED MATERIAL, COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS
+ SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
+ IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
+ BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
+ THE REGENTS’ DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
+ OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
+ WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
+ ANY SUCH LICENSES OR RIGHTS.
+*/
+
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -13,11 +73,11 @@
#include "misc.h" // logprintfl
#include "diskutil.h"
#include "eucalyptus.h"
-#include "diskfile.h"
enum {
MKSWAP=0,
- MKEXT3,
+ MKEXT3,
+ TUNE2FS,
FILECMD,
LOSETUP,
MOUNT,
@@ -41,6 +101,7 @@ enum {
static char * helpers [LASTHELPER] = {
"mkswap",
"mkfs.ext3",
+ "tune2fs",
"file",
"losetup",
"mount",
@@ -61,12 +122,19 @@ static char * helpers [LASTHELPER] = {
};
static char * helpers_path [LASTHELPER];
-
static char * pruntf (char *format, ...);
+static int initialized = 0;
int diskutil_init (void)
{
- return verify_helpers (helpers, helpers_path, LASTHELPER);
+ int ret = 0;
+
+ if (!initialized) {
+ ret = verify_helpers (helpers, helpers_path, LASTHELPER);
+ initialized = 1;
+ }
+
+ return ret;
}
int diskutil_cleanup (void)
@@ -89,7 +157,7 @@ int diskutil_ddzero (const char * path, const long long sectors, boolean zero_fi
seek = 0;
}
- output = pruntf ("%s if=/dev/zero of=%s bs=512 seek=%lld count=%lld", helpers_path[DD], path, seek, count);
+ output = pruntf ("%s %s if=/dev/zero of=%s bs=512 seek=%lld count=%lld", helpers_path[ROOTWRAP], helpers_path[DD], path, seek, count);
if (!output) {
logprintfl (EUCAINFO, "ERROR: cannot create disk file %s\n", path);
ret = ERROR;
@@ -122,8 +190,8 @@ int diskutil_dd2 (const char * in, const char * out, const int bs, const long lo
int ret = OK;
char * output;
- logprintfl (EUCAINFO, "copying data from %s to %s of %lld blocks, seeking %lld, skipping %lld\n", in, out, count, seek, skip);
- output = pruntf("%s %s if=%s of=%s bs=%d count=%lld seek=%lld skip=%lld", helpers_path[ROOTWRAP], helpers_path[DD], in, out, bs, count, seek, skip);
+ logprintfl (EUCAINFO, "copying data from %s to %s of %lld blocks (bs=%d), seeking %lld, skipping %lld\n", in, out, count, bs, seek, skip);
+ output = pruntf("%s %s if=%s of=%s bs=%d count=%lld seek=%lld skip=%lld conv=notrunc,fsync", helpers_path[ROOTWRAP], helpers_path[DD], in, out, bs, count, seek, skip);
if (!output) {
logprintfl (EUCAINFO, "ERROR: cannot copy '%s' to '%s'\n", in, out);
ret = ERROR;
@@ -139,7 +207,7 @@ int diskutil_mbr (const char * path, const char * type)
int ret = OK;
char * output;
- output = pruntf ("LD_PRELOAD='' %s --script %s mklabel %s", helpers_path[PARTED], path, type);
+ output = pruntf ("LD_PRELOAD='' %s %s --script %s mklabel %s", helpers_path[ROOTWRAP], helpers_path[PARTED], path, type);
if (!output) {
logprintfl (EUCAINFO, "ERROR: cannot create an MBR\n");
ret = ERROR;
@@ -155,7 +223,7 @@ int diskutil_part (const char * path, char * part_type, const char * fs_type, co
int ret = OK;
char * output;
- output = pruntf ("%s --script %s mkpart %s %s %llds %llds", helpers_path[PARTED], path, part_type, fs_type, first_sector, last_sector);
+ output = pruntf ("LD_PRELOAD='' %s %s --script %s mkpart %s %s %llds %llds", helpers_path[ROOTWRAP], helpers_path[PARTED], path, part_type, (fs_type)?(fs_type):(""), first_sector, last_sector);
if (!output) {
logprintfl (EUCAINFO, "ERROR: cannot add a partition\n");
ret = ERROR;
@@ -263,6 +331,22 @@ int diskutil_mkfs (const char * lodev, const long long size_bytes)
return ret;
}
+int diskutil_tune (const char * lodev)
+{
+ int ret = OK;
+ char * output;
+
+ output = pruntf ("%s %s %s -c 0 -i 0", helpers_path[ROOTWRAP], helpers_path[TUNE2FS], lodev);
+ if (!output) {
+ logprintfl (EUCAINFO, "ERROR: cannot tune file system on '%s'\n", lodev);
+ ret = ERROR;
+ } else {
+ free (output);
+ }
+
+ return ret;
+}
+
int diskutil_sectors (const char * path, const int part, long long * first, long long * last)
{
int ret = ERROR;
@@ -567,3 +651,7 @@ static char * pruntf (char *format, ...)
}
return(output);
}
+
+// round up or down to sector size
+long long round_up_sec (long long bytes) { return ((bytes % SECTOR_SIZE) ? (((bytes / SECTOR_SIZE) + 1) * SECTOR_SIZE) : bytes); }
+long long round_down_sec (long long bytes) { return ((bytes % SECTOR_SIZE) ? (((bytes / SECTOR_SIZE)) * SECTOR_SIZE) : bytes); }
View
67 storage/diskutil.h
@@ -1,11 +1,73 @@
// -*- mode: C; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-
// vim: set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
+/*
+ Copyright (c) 2009 Eucalyptus Systems, Inc.
+
+ 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, only version 3 of the License.
+
+ This file 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, see <http://www.gnu.org/licenses/>.
+
+ Please contact Eucalyptus Systems, Inc., 130 Castilian
+ Dr., Goleta, CA 93101 USA or visit <http://www.eucalyptus.com/licenses/>
+ if you need additional information or have any questions.
+
+ This file may incorporate work covered under the following copyright and
+ permission notice:
+
+ Software License Agreement (BSD License)
+
+ Copyright (c) 2008, Regents of the University of California
+
+
+ Redistribution and use of this software in source and binary forms, with
+ or without modification, are permitted provided that the following
+ conditions are met:
+
+ Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. USERS OF
+ THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE
+ LICENSED MATERIAL, COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS
+ SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
+ IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
+ BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
+ THE REGENTS’ DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
+ OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
+ WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
+ ANY SUCH LICENSES OR RIGHTS.
+*/
+
#ifndef HELPERS_H
#define HELPERS_H
#include "misc.h" // bolean
+#define SECTOR_SIZE 512
+
int diskutil_init (void);
int diskutil_cleanup (void);
int diskutil_ddzero (const char * path, const long long sectors, boolean zero_fill);
@@ -17,13 +79,16 @@ int diskutil_loop (const char * path, const long long offset, char * lodev, int
int diskutil_unloop (const char * lodev);
int diskutil_mkswap (const char * lodev, const long long size_bytes);
int diskutil_mkfs (const char * lodev, const long long size_bytes);
+int diskutil_tune (const char * lodev);
int diskutil_sectors (const char * path, const int part, long long * first, long long * last);
int diskutil_mount (const char * dev, const char * mnt_pt);
int diskutil_umount (const char * dev);
+int diskutil_write2file (const char * file, const char * str);
int diskutil_grub_files (const char * mnt_pt, const int part, const char * kernel, const char * ramdisk);
int diskutil_grub_mbr (const char * path, const int part);
int diskutil_ch (const char * path, const char * user, const int perms);
int diskutil_mkdir (const char * path);
int diskutil_cp (const char * from, const char * to);
-
+long long round_up_sec (long long bytes);
+long long round_down_sec (long long bytes);
#endif
View
88 storage/walrus.c
@@ -55,7 +55,7 @@
SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
- THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
+ THE REGENTS DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
ANY SUCH LICENSES OR RIGHTS.
@@ -78,7 +78,6 @@
#include "eucalyptus.h"
#include "misc.h"
#include "walrus.h"
-#include <http.h>
#define TOTAL_RETRIES 10 /* download is retried in case of connection problems */
#define FIRST_TIMEOUT 4 /* in seconds, goes in powers of two afterwards */
@@ -99,7 +98,7 @@ static void zerr (int ret, char * where);
#endif
struct request {
- FILE * fp; /* output file pointer to be used by curl WRITERs */
+ int fd; /* output file descriptor to be used by curl WRITERs */
long long total_wrote; /* bytes written during the operation */
long long total_calls; /* write calls made during the operation */
#if defined (CAN_GZIP)
@@ -139,8 +138,8 @@ static int walrus_request (const char * walrus_op, const char * verb, const char
return code;
}
- FILE * fp = fopen64 (outfile, "w");
- if (fp==NULL) {
+ int fd = open (outfile, O_CREAT | O_WRONLY); // we do not truncate the file
+ if (fd==-1 || lseek (fd, 0, SEEK_SET)==-1) {
logprintfl (EUCAERROR, "walrus_request(): failed to open %s for writing\n", outfile);
return code;
}
@@ -150,7 +149,7 @@ static int walrus_request (const char * walrus_op, const char * verb, const char
curl = curl_easy_init ();
if (curl==NULL) {
logprintfl (EUCAERROR, "walrus_request(): could not initialize libcurl\n");
- fclose(fp);
+ close(fd);
return code;
}
@@ -165,15 +164,15 @@ static int walrus_request (const char * walrus_op, const char * verb, const char
/* TODO: HEAD isn't very useful atm since we don't look at headers */
curl_easy_setopt (curl, CURLOPT_NOBODY, 1L);
} else {
- fclose(fp);
+ close(fd);
logprintfl (EUCAERROR, "walrus_request(): invalid HTTP verb %s\n", verb);
return ERROR; /* TODO: dealloc structs before returning! */
}
/* set up the default write function, but possibly override
* it below, if compression is desired and possible */
struct request params;
- params.fp = fp;
+ params.fd = fd;
curl_easy_setopt (curl, CURLOPT_WRITEDATA, &params);
curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_data);
#if defined(CAN_GZIP)
@@ -194,7 +193,7 @@ static int walrus_request (const char * walrus_op, const char * verb, const char
time_t t = time(NULL);
char date_str [26];
if (ctime_r(&t, date_str)==NULL) {
- fclose(fp);
+ close(fd);
return ERROR;
}
assert (strlen(date_str)+7<=STRSIZE);
@@ -206,7 +205,7 @@ static int walrus_request (const char * walrus_op, const char * verb, const char
char * cert_str = euca_get_cert (0); /* read the cloud-wide cert */
if (cert_str==NULL) {