/* * Copyright (c) 2015 Jiri Svoboda * All rights reserved. * * Redistribution and use 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. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ /** @addtogroup vbd * @{ */ /** * @file */ #include #include #include #include #include #include #include #include #include #include #include "disk.h" #include "types/vbd.h" static fibril_mutex_t vbds_disks_lock; static list_t vbds_disks; /* of vbds_disk_t */ static list_t vbds_parts; /* of vbds_part_t */ static category_id_t part_cid; static int vbds_disk_parts_add(vbds_disk_t *, label_t *); static int vbds_disk_parts_remove(vbds_disk_t *); static int vbds_bd_open(bd_srvs_t *, bd_srv_t *); static int vbds_bd_close(bd_srv_t *); static int vbds_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t); static int vbds_bd_sync_cache(bd_srv_t *, aoff64_t, size_t); static int vbds_bd_write_blocks(bd_srv_t *, aoff64_t, size_t, const void *, size_t); static int vbds_bd_get_block_size(bd_srv_t *, size_t *); static int vbds_bd_get_num_blocks(bd_srv_t *, aoff64_t *); static int vbds_bsa_translate(vbds_part_t *, aoff64_t, size_t, aoff64_t *); static int vbds_part_svc_register(vbds_part_t *); static int vbds_part_svc_unregister(vbds_part_t *); static int vbds_part_indices_update(vbds_disk_t *); static vbd_part_id_t vbds_part_id = 1; static bd_ops_t vbds_bd_ops = { .open = vbds_bd_open, .close = vbds_bd_close, .read_blocks = vbds_bd_read_blocks, .sync_cache = vbds_bd_sync_cache, .write_blocks = vbds_bd_write_blocks, .get_block_size = vbds_bd_get_block_size, .get_num_blocks = vbds_bd_get_num_blocks }; static vbds_part_t *bd_srv_part(bd_srv_t *bd) { return (vbds_part_t *)bd->srvs->sarg; } int vbds_disks_init(void) { int rc; fibril_mutex_initialize(&vbds_disks_lock); list_initialize(&vbds_disks); list_initialize(&vbds_parts); rc = loc_category_get_id("partition", &part_cid, 0); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Error looking up partition " "category."); return EIO; } return EOK; } /** Check for new disk devices */ static int vbds_disks_check_new(void) { bool already_known; category_id_t disk_cat; service_id_t *svcs; size_t count, i; int rc; fibril_mutex_lock(&vbds_disks_lock); rc = loc_category_get_id("disk", &disk_cat, IPC_FLAG_BLOCKING); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed resolving category 'disk'."); fibril_mutex_unlock(&vbds_disks_lock); return ENOENT; } rc = loc_category_get_svcs(disk_cat, &svcs, &count); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting list of disk " "devices."); fibril_mutex_unlock(&vbds_disks_lock); return EIO; } for (i = 0; i < count; i++) { already_known = false; list_foreach(vbds_disks, ldisks, vbds_disk_t, disk) { if (disk->svc_id == svcs[i]) { already_known = true; break; } } if (!already_known) { log_msg(LOG_DEFAULT, LVL_NOTE, "Found disk '%lu'", (unsigned long) svcs[i]); rc = vbds_disk_add(svcs[i]); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Could not add " "disk."); } } } fibril_mutex_unlock(&vbds_disks_lock); return EOK; } static int vbds_disk_by_svcid(service_id_t sid, vbds_disk_t **rdisk) { list_foreach(vbds_disks, ldisks, vbds_disk_t, disk) { if (disk->svc_id == sid) { *rdisk = disk; return EOK; } } return ENOENT; } static int vbds_part_by_pid(vbds_part_id_t partid, vbds_part_t **rpart) { log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_by_pid(%zu)", partid); list_foreach(vbds_parts, lparts, vbds_part_t, part) { log_msg(LOG_DEFAULT, LVL_DEBUG, "%zu == %zu ?", part->pid, partid); if (part->pid == partid) { log_msg(LOG_DEFAULT, LVL_DEBUG, "Found match."); *rpart = part; return EOK; } } log_msg(LOG_DEFAULT, LVL_DEBUG, "No match."); return ENOENT; } static int vbds_part_by_svcid(service_id_t svcid, vbds_part_t **rpart) { log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_by_svcid(%zu)", svcid); list_foreach(vbds_parts, lparts, vbds_part_t, part) { log_msg(LOG_DEFAULT, LVL_DEBUG, "%zu == %zu ?", part->svc_id, svcid); if (part->svc_id == svcid) { log_msg(LOG_DEFAULT, LVL_DEBUG, "Found match."); *rpart = part; return EOK; } } log_msg(LOG_DEFAULT, LVL_DEBUG, "No match."); return ENOENT; } /** Add partition to our inventory based on liblabel partition structure */ static int vbds_part_add(vbds_disk_t *disk, label_part_t *lpart, vbds_part_t **rpart) { vbds_part_t *part; service_id_t psid = 0; label_part_info_t lpinfo; int rc; log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_add(%s, %p)", disk->svc_name, lpart); label_part_get_info(lpart, &lpinfo); part = calloc(1, sizeof(vbds_part_t)); if (part == NULL) { log_msg(LOG_DEFAULT, LVL_ERROR, "Out of memory."); return ENOMEM; } part->lpart = lpart; part->disk = disk; part->pid = ++vbds_part_id; part->svc_id = psid; part->block0 = lpinfo.block0; part->nblocks = lpinfo.nblocks; bd_srvs_init(&part->bds); part->bds.ops = &vbds_bd_ops; part->bds.sarg = part; if (lpinfo.pkind != lpk_extended) { rc = vbds_part_svc_register(part); if (rc != EOK) return EIO; } list_append(&part->ldisk, &disk->parts); list_append(&part->lparts, &vbds_parts); log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_add -> %p", part); if (rpart != NULL) *rpart = part; return EOK; } /** Remove partition from our inventory leaving only the underlying liblabel * partition structure. */ static int vbds_part_remove(vbds_part_t *part, label_part_t **rlpart) { label_part_t *lpart; int rc; log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_remove(%p)", part); lpart = part->lpart; if (part->open_cnt > 0) { log_msg(LOG_DEFAULT, LVL_DEBUG, "part->open_cnt = %d", part->open_cnt); return EBUSY; } if (part->svc_id != 0) { rc = vbds_part_svc_unregister(part); if (rc != EOK) return EIO; } list_remove(&part->ldisk); list_remove(&part->lparts); free(part); if (rlpart != NULL) *rlpart = lpart; return EOK; } /** Remove all disk partitions from our inventory leaving only the underlying * liblabel partition structures. */ static int vbds_disk_parts_add(vbds_disk_t *disk, label_t *label) { label_part_t *part; int rc; part = label_part_first(label); while (part != NULL) { rc = vbds_part_add(disk, part, NULL); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding partition " "(disk %s)", disk->svc_name); return rc; } part = label_part_next(part); } return EOK; } /** Remove all disk partitions from our inventory leaving only the underlying * liblabel partition structures. */ static int vbds_disk_parts_remove(vbds_disk_t *disk) { link_t *link; vbds_part_t *part; int rc; link = list_first(&disk->parts); while (link != NULL) { part = list_get_instance(link, vbds_part_t, ldisk); rc = vbds_part_remove(part, NULL); if (rc != EOK) return rc; link = list_first(&disk->parts); } return EOK; } static void vbds_disk_cat_change_cb(void) { (void) vbds_disks_check_new(); } int vbds_disk_discovery_start(void) { int rc; rc = loc_register_cat_change_cb(vbds_disk_cat_change_cb); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering callback " "for disk discovery (%d).", rc); return rc; } return vbds_disks_check_new(); } int vbds_disk_add(service_id_t sid) { label_t *label = NULL; vbds_disk_t *disk = NULL; bool block_inited = false; size_t block_size; int rc; log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_disk_add(%zu)", sid); /* Check for duplicates */ rc = vbds_disk_by_svcid(sid, &disk); if (rc == EOK) return EEXIST; disk = calloc(1, sizeof(vbds_disk_t)); if (disk == NULL) return ENOMEM; rc = loc_service_get_name(sid, &disk->svc_name); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting disk service name."); rc = EIO; goto error; } log_msg(LOG_DEFAULT, LVL_DEBUG, "block_init(%zu)", sid); rc = block_init(sid, 2048); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed opening block device %s.", disk->svc_name); rc = EIO; goto error; } rc = block_get_bsize(sid, &block_size); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting block size of %s.", disk->svc_name); rc = EIO; goto error; } block_inited = true; rc = label_open(sid, &label); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to open label in disk %s.", disk->svc_name); rc = EIO; goto error; } disk->svc_id = sid; disk->label = label; disk->block_size = block_size; list_initialize(&disk->parts); list_append(&disk->ldisks, &vbds_disks); log_msg(LOG_DEFAULT, LVL_NOTE, "Recognized disk label. Adding partitions."); (void) vbds_disk_parts_add(disk, label); return EOK; error: label_close(label); if (block_inited) { log_msg(LOG_DEFAULT, LVL_DEBUG, "block_fini(%zu)", sid); block_fini(sid); } if (disk != NULL) free(disk->svc_name); free(disk); return rc; } int vbds_disk_remove(service_id_t sid) { vbds_disk_t *disk; int rc; log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_disk_remove(%zu)", sid); rc = vbds_disk_by_svcid(sid, &disk); if (rc != EOK) return rc; rc = vbds_disk_parts_remove(disk); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed removing disk."); return rc; } list_remove(&disk->ldisks); label_close(disk->label); log_msg(LOG_DEFAULT, LVL_DEBUG, "block_fini(%zu)", sid); block_fini(sid); free(disk); return EOK; } /** Get list of disks as array of service IDs. */ int vbds_disk_get_ids(service_id_t *id_buf, size_t buf_size, size_t *act_size) { size_t act_cnt; size_t buf_cnt; fibril_mutex_lock(&vbds_disks_lock); buf_cnt = buf_size / sizeof(service_id_t); act_cnt = list_count(&vbds_disks); *act_size = act_cnt * sizeof(service_id_t); if (buf_size % sizeof(service_id_t) != 0) { fibril_mutex_unlock(&vbds_disks_lock); return EINVAL; } size_t pos = 0; list_foreach(vbds_disks, ldisks, vbds_disk_t, disk) { if (pos < buf_cnt) id_buf[pos] = disk->svc_id; pos++; } fibril_mutex_unlock(&vbds_disks_lock); return EOK; } int vbds_disk_info(service_id_t sid, vbd_disk_info_t *info) { vbds_disk_t *disk; label_info_t linfo; int rc; log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_disk_info(%zu)", sid); rc = vbds_disk_by_svcid(sid, &disk); if (rc != EOK) return rc; rc = label_get_info(disk->label, &linfo); if (rc != EOK) return rc; info->ltype = linfo.ltype; info->flags = linfo.flags; info->ablock0 = linfo.ablock0; info->anblocks = linfo.anblocks; info->block_size = disk->block_size; log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_disk_info - block_size=%zu", info->block_size); return EOK; } int vbds_get_parts(service_id_t sid, service_id_t *id_buf, size_t buf_size, size_t *act_size) { vbds_disk_t *disk; size_t act_cnt; size_t buf_cnt; int rc; rc = vbds_disk_by_svcid(sid, &disk); if (rc != EOK) return rc; buf_cnt = buf_size / sizeof(service_id_t); act_cnt = list_count(&disk->parts); *act_size = act_cnt * sizeof(service_id_t); if (buf_size % sizeof(service_id_t) != 0) return EINVAL; size_t pos = 0; list_foreach(disk->parts, ldisk, vbds_part_t, part) { if (pos < buf_cnt) id_buf[pos] = part->pid; pos++; } return EOK; } int vbds_label_create(service_id_t sid, label_type_t ltype) { label_t *label; label_info_t linfo; vbds_disk_t *disk; int rc; log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_label_create(%zu)", sid); /* Find disk */ rc = vbds_disk_by_svcid(sid, &disk); if (rc != EOK) return rc; log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_label_create(%zu) - label_close", sid); /* Verify that current label is a dummy label */ rc = label_get_info(disk->label, &linfo); if (rc != EOK) return rc; if (linfo.ltype != lt_none) { log_msg(LOG_DEFAULT, LVL_ERROR, "Label already exists."); return EEXIST; } /* Close dummy label first */ rc = vbds_disk_parts_remove(disk); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed removing disk."); return rc; } label_close(disk->label); disk->label = NULL; log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_label_create(%zu) - label_create", sid); rc = label_create(sid, ltype, &label); if (rc != EOK) goto error; (void) vbds_disk_parts_add(disk, label); disk->label = label; log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_label_create(%zu) - success", sid); return EOK; error: log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_label_create(%zu) - failure", sid); if (disk->label == NULL) { rc = label_open(sid, &label); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to open label in disk %s.", disk->svc_name); } } return rc; } int vbds_label_delete(service_id_t sid) { vbds_disk_t *disk; label_t *label; int rc; log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_label_delete(%zu)", sid); rc = vbds_disk_by_svcid(sid, &disk); if (rc != EOK) return rc; rc = vbds_disk_parts_remove(disk); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed deleting label."); return rc; } rc = label_destroy(disk->label); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed deleting label."); return rc; } disk->label = NULL; rc = label_open(disk->svc_id, &label); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to open label in disk %s.", disk->svc_name); return EIO; } (void) vbds_disk_parts_add(disk, label); disk->label = label; return EOK; } int vbds_part_get_info(vbds_part_id_t partid, vbd_part_info_t *pinfo) { vbds_part_t *part; label_part_info_t lpinfo; int rc; rc = vbds_part_by_pid(partid, &part); if (rc != EOK) return rc; label_part_get_info(part->lpart, &lpinfo); pinfo->index = lpinfo.index; pinfo->pkind = lpinfo.pkind; pinfo->block0 = lpinfo.block0; pinfo->nblocks = lpinfo.nblocks; pinfo->svc_id = part->svc_id; return EOK; } int vbds_part_create(service_id_t sid, vbd_part_spec_t *pspec, vbds_part_id_t *rpart) { vbds_disk_t *disk; vbds_part_t *part; label_part_spec_t lpspec; label_part_t *lpart; int rc; log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_create(%zu)", sid); rc = vbds_disk_by_svcid(sid, &disk); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Disk %zu not found", sid); goto error; } log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_crate(%zu): " "index=%d block0=%" PRIu64 " nblocks=%" PRIu64 " hdr_blocks=%" PRIu64 " pkind=%d", sid, pspec->index, pspec->block0, pspec->nblocks, pspec->hdr_blocks, pspec->pkind); label_pspec_init(&lpspec); lpspec.index = pspec->index; lpspec.block0 = pspec->block0; lpspec.nblocks = pspec->nblocks; lpspec.hdr_blocks = pspec->hdr_blocks; lpspec.pkind = pspec->pkind; lpspec.ptype = pspec->ptype; rc = label_part_create(disk->label, &lpspec, &lpart); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Error creating partition."); goto error; } rc = vbds_part_indices_update(disk); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Error updating partition indices"); goto error; } rc = vbds_part_add(disk, lpart, &part); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed while creating " "partition."); rc = label_part_destroy(lpart); if (rc != EOK) log_msg(LOG_DEFAULT, LVL_ERROR, "Cannot roll back partition creation."); rc = EIO; goto error; } if (rpart != NULL) *rpart = part->pid; return EOK; error: return rc; } int vbds_part_delete(vbds_part_id_t partid) { vbds_part_t *part; vbds_disk_t *disk; label_part_t *lpart; int rc; rc = vbds_part_by_pid(partid, &part); if (rc != EOK) return rc; disk = part->disk; rc = vbds_part_remove(part, &lpart); if (rc != EOK) return rc; rc = label_part_destroy(lpart); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed deleting partition"); /* Try rolling back */ rc = vbds_part_add(disk, lpart, NULL); if (rc != EOK) log_msg(LOG_DEFAULT, LVL_ERROR, "Failed rolling back."); return EIO; } rc = vbds_part_indices_update(disk); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Error updating partition indices"); return EIO; } return EOK; } int vbds_suggest_ptype(service_id_t sid, label_pcnt_t pcnt, label_ptype_t *ptype) { vbds_disk_t *disk; int rc; rc = vbds_disk_by_svcid(sid, &disk); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_DEBUG, "Disk %zu not found", sid); goto error; } rc = label_suggest_ptype(disk->label, pcnt, ptype); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_DEBUG, "label_suggest_ptype() failed"); goto error; } return EOK; error: return rc; } static int vbds_bd_open(bd_srvs_t *bds, bd_srv_t *bd) { vbds_part_t *part = bd_srv_part(bd); log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_bd_open()"); part->open_cnt++; return EOK; } static int vbds_bd_close(bd_srv_t *bd) { vbds_part_t *part = bd_srv_part(bd); log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_bd_close()"); part->open_cnt--; return EOK; } static int vbds_bd_read_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt, void *buf, size_t size) { vbds_part_t *part = bd_srv_part(bd); aoff64_t gba; log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_bd_read_blocks()"); if (cnt * part->disk->block_size < size) return EINVAL; if (vbds_bsa_translate(part, ba, cnt, &gba) != EOK) return ELIMIT; return block_read_direct(part->disk->svc_id, gba, cnt, buf); } static int vbds_bd_sync_cache(bd_srv_t *bd, aoff64_t ba, size_t cnt) { vbds_part_t *part = bd_srv_part(bd); aoff64_t gba; log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_bd_sync_cache()"); /* XXX Allow full-disk sync? */ if (ba != 0 || cnt != 0) { if (vbds_bsa_translate(part, ba, cnt, &gba) != EOK) return ELIMIT; } else { gba = 0; } return block_sync_cache(part->disk->svc_id, gba, cnt); } static int vbds_bd_write_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt, const void *buf, size_t size) { vbds_part_t *part = bd_srv_part(bd); aoff64_t gba; log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_bd_write_blocks()"); if (cnt * part->disk->block_size < size) return EINVAL; if (vbds_bsa_translate(part, ba, cnt, &gba) != EOK) return ELIMIT; return block_write_direct(part->disk->svc_id, gba, cnt, buf); } static int vbds_bd_get_block_size(bd_srv_t *bd, size_t *rsize) { vbds_part_t *part = bd_srv_part(bd); log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_bd_get_block_size()"); *rsize = part->disk->block_size; return EOK; } static int vbds_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb) { vbds_part_t *part = bd_srv_part(bd); log_msg(LOG_DEFAULT, LVL_DEBUG2, "vbds_bd_get_num_blocks()"); *rnb = part->nblocks; return EOK; } void vbds_bd_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg) { vbds_part_t *part; int rc; service_id_t svcid; log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_bd_conn()"); svcid = IPC_GET_ARG2(*icall); log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_bd_conn() - svcid=%zu", svcid); rc = vbds_part_by_svcid(svcid, &part); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_DEBUG, "vbd_bd_conn() - partition " "not found."); async_answer_0(iid, EINVAL); return; } log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_bd_conn() - call bd_conn"); bd_conn(iid, icall, &part->bds); } /** Translate block segment address with range checking. */ static int vbds_bsa_translate(vbds_part_t *part, aoff64_t ba, size_t cnt, aoff64_t *gba) { if (ba + cnt > part->nblocks) return ELIMIT; *gba = part->block0 + ba; return EOK; } /** Register service for partition */ static int vbds_part_svc_register(vbds_part_t *part) { char *name; service_id_t psid; int idx; int rc; idx = part->lpart->index; rc = asprintf(&name, "%sp%u", part->disk->svc_name, idx); if (rc < 0) { log_msg(LOG_DEFAULT, LVL_ERROR, "Out of memory."); return ENOMEM; } log_msg(LOG_DEFAULT, LVL_DEBUG, "loc_service_register('%s')", name); rc = loc_service_register(name, &psid); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering " "service %s (%d).", name, rc); free(name); free(part); return EIO; } rc = loc_service_add_to_cat(psid, part_cid); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Failled adding partition " "service %s to partition category.", name); free(name); free(part); rc = loc_service_unregister(psid); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Error unregistering " "service. Rollback failed."); } return EIO; } free(name); part->svc_id = psid; part->reg_idx = idx; return EOK; } /** Unregister service for partition */ static int vbds_part_svc_unregister(vbds_part_t *part) { int rc; log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_svc_unregister(" "disk->svc_name='%s', id=%zu)", part->disk->svc_name, part->svc_id); rc = loc_service_unregister(part->svc_id); if (rc != EOK) return EIO; part->svc_id = 0; part->reg_idx = 0; return EOK; } /** Update service names for any partition whose index has changed. */ static int vbds_part_indices_update(vbds_disk_t *disk) { label_part_info_t lpinfo; int rc; log_msg(LOG_DEFAULT, LVL_DEBUG, "vbds_part_indices_update()"); /* First unregister services for partitions whose index has changed */ list_foreach(disk->parts, ldisk, vbds_part_t, part) { if (part->svc_id != 0 && part->lpart->index != part->reg_idx) { rc = vbds_part_svc_unregister(part); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Error " "un-registering partition."); return EIO; } } } /* Now re-register those services under the new indices */ list_foreach(disk->parts, ldisk, vbds_part_t, part) { label_part_get_info(part->lpart, &lpinfo); if (part->svc_id == 0 && lpinfo.pkind != lpk_extended) { rc = vbds_part_svc_register(part); if (rc != EOK) { log_msg(LOG_DEFAULT, LVL_ERROR, "Error " "re-registering partition."); return EIO; } } } return EOK; } /** @} */