#include "hammer_util.h"
#include <sys/diskslice.h>
#include <sys/diskmbr.h>
static void check_volume(volume_info_t volume);
static void get_buffer_readahead(buffer_info_t base);
static __inline int readhammervol(volume_info_t volume);
static __inline int readhammerbuf(buffer_info_t buffer);
static __inline int writehammervol(volume_info_t volume);
static __inline int writehammerbuf(buffer_info_t buffer);
hammer_uuid_t Hammer_FSType;
hammer_uuid_t Hammer_FSId;
int UseReadBehind = -4;
int UseReadAhead = 4;
int DebugOpt;
uint32_t HammerVersion = -1;
TAILQ_HEAD(volume_list, volume_info);
static struct volume_list VolList = TAILQ_HEAD_INITIALIZER(VolList);
static int valid_hammer_volumes;
static __inline
int
buffer_hash(hammer_off_t zone2_offset)
{
int hi;
hi = (int)(zone2_offset / HAMMER_BUFSIZE) & HAMMER_BUFLISTMASK;
return(hi);
}
static
buffer_info_t
find_buffer(hammer_off_t zone2_offset)
{
volume_info_t volume;
buffer_info_t buffer;
int hi;
volume = get_volume(HAMMER_VOL_DECODE(zone2_offset));
assert(volume);
hi = buffer_hash(zone2_offset);
TAILQ_FOREACH(buffer, &volume->buffer_lists[hi], entry) {
if (buffer->zone2_offset == zone2_offset)
return(buffer);
}
return(NULL);
}
static
volume_info_t
__alloc_volume(const char *volname, int oflags)
{
volume_info_t volume;
int i;
volume = calloc(1, sizeof(*volume));
volume->vol_no = -1;
volume->rdonly = (oflags == O_RDONLY);
volume->name = strdup(volname);
volume->fd = open(volume->name, oflags);
if (volume->fd < 0) {
err(1, "alloc_volume: Failed to open %s", volume->name);
}
check_volume(volume);
volume->ondisk = calloc(1, HAMMER_BUFSIZE);
for (i = 0; i < HAMMER_BUFLISTS; ++i)
TAILQ_INIT(&volume->buffer_lists[i]);
return(volume);
}
static
void
__add_volume(const volume_info_t volume)
{
volume_info_t scan;
struct stat st1, st2;
if (fstat(volume->fd, &st1) != 0) {
errx(1, "add_volume: %s: Failed to stat", volume->name);
}
TAILQ_FOREACH(scan, &VolList, entry) {
if (scan->vol_no == volume->vol_no) {
errx(1, "add_volume: %s: Duplicate volume number %d "
"against %s",
volume->name, volume->vol_no, scan->name);
}
if (fstat(scan->fd, &st2) != 0) {
errx(1, "add_volume: %s: Failed to stat %s",
volume->name, scan->name);
}
if ((st1.st_ino == st2.st_ino) && (st1.st_dev == st2.st_dev)) {
errx(1, "add_volume: %s: Specified more than once",
volume->name);
}
}
TAILQ_INSERT_TAIL(&VolList, volume, entry);
}
static
void
__verify_volume(const volume_info_t volume)
{
hammer_volume_ondisk_t ondisk = volume->ondisk;
char *fstype;
if (ondisk->vol_signature != HAMMER_FSBUF_VOLUME) {
errx(1, "verify_volume: Invalid volume signature %016jx",
ondisk->vol_signature);
}
if (ondisk->vol_rootvol != HAMMER_ROOT_VOLNO) {
errx(1, "verify_volume: Invalid root volume# %d",
ondisk->vol_rootvol);
}
hammer_uuid_to_string(&ondisk->vol_fstype, &fstype);
if (hammer_uuid_compare(&Hammer_FSType, &ondisk->vol_fstype)) {
errx(1, "verify_volume: %s: fstype %s does not indicate "
"this is a HAMMER volume", volume->name, fstype);
}
free(fstype);
if (hammer_uuid_compare(&Hammer_FSId, &ondisk->vol_fsid)) {
errx(1, "verify_volume: %s: fsid does not match other volumes!",
volume->name);
}
if (ondisk->vol_version < HAMMER_VOL_VERSION_MIN ||
ondisk->vol_version >= HAMMER_VOL_VERSION_WIP) {
errx(1, "verify_volume: %s: Invalid volume version %u",
volume->name, ondisk->vol_version);
}
}
volume_info_t
init_volume(const char *filename, int oflags, int32_t vol_no)
{
volume_info_t volume;
volume = __alloc_volume(filename, oflags);
volume->vol_no = volume->ondisk->vol_no = vol_no;
__add_volume(volume);
return(volume);
}
volume_info_t
load_volume(const char *filename, int oflags, int verify_volume)
{
volume_info_t volume;
int n;
volume = __alloc_volume(filename, oflags);
n = readhammervol(volume);
if (n == -1) {
err(1, "load_volume: %s: Read failed at offset 0",
volume->name);
}
volume->vol_no = volume->ondisk->vol_no;
if (volume->vol_no == HAMMER_ROOT_VOLNO)
HammerVersion = volume->ondisk->vol_version;
if (valid_hammer_volumes++ == 0)
Hammer_FSId = volume->ondisk->vol_fsid;
if (verify_volume)
__verify_volume(volume);
__add_volume(volume);
return(volume);
}
static
void
check_volume(volume_info_t volume)
{
struct partinfo pinfo;
struct stat st;
if (ioctl(volume->fd, DIOCGPART, &pinfo) < 0) {
if (fstat(volume->fd, &st) < 0) {
err(1, "Unable to stat %s", volume->name);
}
if (S_ISREG(st.st_mode)) {
volume->size = st.st_size;
volume->type = "REGFILE";
} else {
errx(1, "Unsupported file type for %s", volume->name);
}
} else {
if (pinfo.reserved_blocks) {
errx(1, "HAMMER cannot be placed in a partition "
"which overlaps the disklabel or MBR");
}
if (pinfo.media_blksize > HAMMER_BUFSIZE ||
HAMMER_BUFSIZE % pinfo.media_blksize) {
errx(1, "A media sector size of %d is not supported",
pinfo.media_blksize);
}
volume->size = pinfo.media_size;
volume->device_offset = pinfo.media_offset;
volume->type = "DEVICE";
}
}
int
is_regfile(const volume_info_t volume)
{
return(strcmp(volume->type, "REGFILE") ? 0 : 1);
}
void
assert_volume_offset(const volume_info_t volume)
{
assert(hammer_is_zone_raw_buffer(volume->vol_free_off));
assert(hammer_is_zone_raw_buffer(volume->vol_free_end));
if (volume->vol_free_off >= volume->vol_free_end) {
errx(1, "Ran out of room, filesystem too small");
}
}
volume_info_t
get_volume(int32_t vol_no)
{
volume_info_t volume;
TAILQ_FOREACH(volume, &VolList, entry) {
if (volume->vol_no == vol_no)
break;
}
return(volume);
}
volume_info_t
get_root_volume(void)
{
return(get_volume(HAMMER_ROOT_VOLNO));
}
static
hammer_off_t
__blockmap_xlate_to_zone2(hammer_off_t buf_offset)
{
hammer_off_t zone2_offset;
int error = 0;
if (hammer_is_zone_raw_buffer(buf_offset))
zone2_offset = buf_offset;
else
zone2_offset = blockmap_lookup(buf_offset, &error);
if (error)
return(HAMMER_OFF_BAD);
assert(hammer_is_zone_raw_buffer(zone2_offset));
return(zone2_offset);
}
static
buffer_info_t
__alloc_buffer(hammer_off_t zone2_offset, int isnew)
{
volume_info_t volume;
buffer_info_t buffer;
int hi;
volume = get_volume(HAMMER_VOL_DECODE(zone2_offset));
assert(volume != NULL);
buffer = calloc(1, sizeof(*buffer));
buffer->zone2_offset = zone2_offset;
buffer->raw_offset = hammer_xlate_to_phys(volume->ondisk, zone2_offset);
buffer->volume = volume;
buffer->ondisk = calloc(1, HAMMER_BUFSIZE);
if (isnew <= 0) {
if (readhammerbuf(buffer) == -1) {
err(1, "Failed to read %s:%016jx at %016jx",
volume->name,
(intmax_t)buffer->zone2_offset,
(intmax_t)buffer->raw_offset);
}
}
hi = buffer_hash(zone2_offset);
TAILQ_INSERT_TAIL(&volume->buffer_lists[hi], buffer, entry);
hammer_cache_add(&buffer->cache);
return(buffer);
}
static
buffer_info_t
get_buffer(hammer_off_t buf_offset, int isnew)
{
buffer_info_t buffer;
hammer_off_t zone2_offset;
int dora = 0;
zone2_offset = __blockmap_xlate_to_zone2(buf_offset);
if (zone2_offset == HAMMER_OFF_BAD)
return(NULL);
zone2_offset &= ~HAMMER_BUFMASK64;
buffer = find_buffer(zone2_offset);
if (buffer == NULL) {
buffer = __alloc_buffer(zone2_offset, isnew);
dora = (isnew == 0);
} else {
assert(isnew != -1);
hammer_cache_used(&buffer->cache);
}
assert(buffer->ondisk != NULL);
++buffer->cache.refs;
hammer_cache_flush();
if (isnew > 0) {
assert(buffer->cache.modified == 0);
bzero(buffer->ondisk, HAMMER_BUFSIZE);
buffer->cache.modified = 1;
}
if (dora)
get_buffer_readahead(buffer);
return(buffer);
}
static
void
get_buffer_readahead(const buffer_info_t base)
{
buffer_info_t buffer;
volume_info_t volume;
hammer_off_t zone2_offset;
int64_t raw_offset;
int ri = UseReadBehind;
int re = UseReadAhead;
raw_offset = base->raw_offset + ri * HAMMER_BUFSIZE;
volume = base->volume;
while (ri < re) {
if (raw_offset >= volume->ondisk->vol_buf_end)
break;
if (raw_offset < volume->ondisk->vol_buf_beg || ri == 0) {
++ri;
raw_offset += HAMMER_BUFSIZE;
continue;
}
zone2_offset = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no,
raw_offset - volume->ondisk->vol_buf_beg);
buffer = find_buffer(zone2_offset);
if (buffer == NULL) {
buffer = get_buffer(zone2_offset, -1);
rel_buffer(buffer);
}
++ri;
raw_offset += HAMMER_BUFSIZE;
}
}
void
rel_buffer(buffer_info_t buffer)
{
volume_info_t volume;
int hi;
if (buffer == NULL)
return;
assert(buffer->cache.refs > 0);
if (--buffer->cache.refs == 0) {
if (buffer->cache.delete) {
hi = buffer_hash(buffer->zone2_offset);
volume = buffer->volume;
if (buffer->cache.modified)
flush_buffer(buffer);
TAILQ_REMOVE(&volume->buffer_lists[hi], buffer, entry);
hammer_cache_del(&buffer->cache);
free(buffer->ondisk);
free(buffer);
}
}
}
void *
get_buffer_data(hammer_off_t buf_offset, buffer_info_t *bufferp, int isnew)
{
hammer_off_t xor = 0;
hammer_volume_ondisk_t ondisk;
if (*bufferp != NULL) {
if (hammer_is_zone_undo(buf_offset)) {
ondisk = (*bufferp)->volume->ondisk;
xor = hammer_xlate_to_undo(ondisk, buf_offset) ^
(*bufferp)->zone2_offset;
} else if (hammer_is_zone_direct_xlated(buf_offset)) {
xor = HAMMER_OFF_LONG_ENCODE(buf_offset) ^
HAMMER_OFF_LONG_ENCODE((*bufferp)->zone2_offset);
} else {
assert(0);
}
if (isnew > 0 || (xor & ~HAMMER_BUFMASK64)) {
rel_buffer(*bufferp);
*bufferp = NULL;
} else {
hammer_cache_used(&(*bufferp)->cache);
}
}
if (*bufferp == NULL) {
*bufferp = get_buffer(buf_offset, isnew);
if (*bufferp == NULL)
return(NULL);
}
return((char *)(*bufferp)->ondisk +
((int32_t)buf_offset & HAMMER_BUFMASK));
}
hammer_node_ondisk_t
alloc_btree_node(hammer_off_t *offp, buffer_info_t *data_bufferp)
{
hammer_node_ondisk_t node;
node = alloc_blockmap(HAMMER_ZONE_BTREE_INDEX, sizeof(*node),
offp, data_bufferp);
bzero(node, sizeof(*node));
return(node);
}
void *
alloc_meta_element(hammer_off_t *offp, int32_t data_len,
buffer_info_t *data_bufferp)
{
void *data;
data = alloc_blockmap(HAMMER_ZONE_META_INDEX, data_len,
offp, data_bufferp);
bzero(data, data_len);
return(data);
}
void
format_blockmap(volume_info_t root_vol, int zone, hammer_off_t offset)
{
hammer_blockmap_t blockmap;
hammer_off_t zone_base;
assert(root_vol->vol_no == HAMMER_ROOT_VOLNO);
assert(hammer_is_index_record(zone));
blockmap = &root_vol->ondisk->vol0_blockmap[zone];
zone_base = HAMMER_ZONE_ENCODE(zone, offset);
bzero(blockmap, sizeof(*blockmap));
blockmap->phys_offset = 0;
blockmap->first_offset = zone_base;
blockmap->next_offset = zone_base;
blockmap->alloc_offset = HAMMER_ENCODE(zone, 255, -1);
hammer_crc_set_blockmap(HammerVersion, blockmap);
}
void
format_freemap(volume_info_t root_vol)
{
buffer_info_t buffer = NULL;
hammer_off_t layer1_offset;
hammer_blockmap_t blockmap;
hammer_blockmap_layer1_t layer1;
int i, isnew;
assert(root_vol->vol_no == HAMMER_ROOT_VOLNO);
layer1_offset = bootstrap_bigblock(root_vol);
for (i = 0; i < HAMMER_BIGBLOCK_SIZE; i += sizeof(*layer1)) {
isnew = ((i % HAMMER_BUFSIZE) == 0);
layer1 = get_buffer_data(layer1_offset + i, &buffer, isnew);
bzero(layer1, sizeof(*layer1));
layer1->phys_offset = HAMMER_BLOCKMAP_UNAVAIL;
layer1->blocks_free = 0;
hammer_crc_set_layer1(HammerVersion, layer1);
}
assert(i == HAMMER_BIGBLOCK_SIZE);
rel_buffer(buffer);
blockmap = &root_vol->ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX];
bzero(blockmap, sizeof(*blockmap));
blockmap->phys_offset = layer1_offset;
blockmap->first_offset = 0;
blockmap->next_offset = HAMMER_ENCODE_RAW_BUFFER(0, 0);
blockmap->alloc_offset = HAMMER_ENCODE_RAW_BUFFER(255, -1);
hammer_crc_set_blockmap(HammerVersion, blockmap);
}
int64_t
initialize_freemap(volume_info_t volume)
{
volume_info_t root_vol;
buffer_info_t buffer1 = NULL;
buffer_info_t buffer2 = NULL;
hammer_blockmap_layer1_t layer1;
hammer_blockmap_layer2_t layer2;
hammer_off_t layer1_offset;
hammer_off_t layer2_offset;
hammer_off_t phys_offset;
hammer_off_t block_offset;
hammer_off_t aligned_vol_free_end;
hammer_blockmap_t freemap;
int64_t count = 0;
int64_t layer1_count = 0;
root_vol = get_root_volume();
assert_volume_offset(volume);
aligned_vol_free_end = HAMMER_BLOCKMAP_LAYER2_DOALIGN(volume->vol_free_end);
printf("initialize freemap volume %d\n", volume->vol_no);
freemap = &root_vol->ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX];
for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 0);
phys_offset < aligned_vol_free_end;
phys_offset += HAMMER_BLOCKMAP_LAYER2) {
layer1_offset = freemap->phys_offset +
HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset);
layer1 = get_buffer_data(layer1_offset, &buffer1, 0);
if (layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL) {
layer1->phys_offset = bootstrap_bigblock(volume);
layer1->blocks_free = 0;
buffer1->cache.modified = 1;
hammer_crc_set_layer1(HammerVersion, layer1);
}
}
for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 0);
phys_offset < aligned_vol_free_end;
phys_offset += HAMMER_BLOCKMAP_LAYER2) {
layer1_count = 0;
layer1_offset = freemap->phys_offset +
HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset);
layer1 = get_buffer_data(layer1_offset, &buffer1, 0);
assert(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL);
for (block_offset = 0;
block_offset < HAMMER_BLOCKMAP_LAYER2;
block_offset += HAMMER_BIGBLOCK_SIZE) {
layer2_offset = layer1->phys_offset +
HAMMER_BLOCKMAP_LAYER2_OFFSET(block_offset);
layer2 = get_buffer_data(layer2_offset, &buffer2, 0);
bzero(layer2, sizeof(*layer2));
if (phys_offset + block_offset < volume->vol_free_off) {
layer2->zone = HAMMER_ZONE_FREEMAP_INDEX;
layer2->append_off = HAMMER_BIGBLOCK_SIZE;
layer2->bytes_free = 0;
} else if (phys_offset + block_offset < volume->vol_free_end) {
layer2->zone = 0;
layer2->append_off = 0;
layer2->bytes_free = HAMMER_BIGBLOCK_SIZE;
++count;
++layer1_count;
} else {
layer2->zone = HAMMER_ZONE_UNAVAIL_INDEX;
layer2->append_off = HAMMER_BIGBLOCK_SIZE;
layer2->bytes_free = 0;
}
hammer_crc_set_layer2(HammerVersion, layer2);
buffer2->cache.modified = 1;
}
layer1->blocks_free += layer1_count;
hammer_crc_set_layer1(HammerVersion, layer1);
buffer1->cache.modified = 1;
}
rel_buffer(buffer1);
rel_buffer(buffer2);
return(count);
}
int64_t
count_freemap(const volume_info_t volume)
{
hammer_off_t phys_offset;
hammer_off_t vol_free_off;
hammer_off_t aligned_vol_free_end;
int64_t count = 0;
vol_free_off = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 0);
assert_volume_offset(volume);
aligned_vol_free_end = HAMMER_BLOCKMAP_LAYER2_DOALIGN(volume->vol_free_end);
if (volume->vol_no == HAMMER_ROOT_VOLNO)
vol_free_off += HAMMER_BIGBLOCK_SIZE;
for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 0);
phys_offset < aligned_vol_free_end;
phys_offset += HAMMER_BLOCKMAP_LAYER2) {
vol_free_off += HAMMER_BIGBLOCK_SIZE;
}
for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 0);
phys_offset < aligned_vol_free_end;
phys_offset += HAMMER_BIGBLOCK_SIZE) {
if (phys_offset < vol_free_off)
;
else if (phys_offset < volume->vol_free_end)
++count;
}
return(count);
}
void
format_undomap(volume_info_t root_vol, int64_t *undo_buffer_size)
{
hammer_off_t undo_limit;
hammer_blockmap_t blockmap;
hammer_volume_ondisk_t ondisk;
buffer_info_t buffer = NULL;
hammer_off_t scan;
int n;
int limit_index;
uint32_t seqno;
assert(root_vol->vol_no == HAMMER_ROOT_VOLNO);
ondisk = root_vol->ondisk;
undo_limit = *undo_buffer_size;
if (undo_limit == 0) {
undo_limit = HAMMER_VOL_BUF_SIZE(ondisk) / 1000;
if (undo_limit < HAMMER_BIGBLOCK_SIZE * HAMMER_MIN_UNDO_BIGBLOCKS)
undo_limit = HAMMER_BIGBLOCK_SIZE * HAMMER_MIN_UNDO_BIGBLOCKS;
}
undo_limit = HAMMER_BIGBLOCK_DOALIGN(undo_limit);
if (undo_limit < HAMMER_BIGBLOCK_SIZE)
undo_limit = HAMMER_BIGBLOCK_SIZE;
if (undo_limit > HAMMER_BIGBLOCK_SIZE * HAMMER_MAX_UNDO_BIGBLOCKS)
undo_limit = HAMMER_BIGBLOCK_SIZE * HAMMER_MAX_UNDO_BIGBLOCKS;
*undo_buffer_size = undo_limit;
blockmap = &ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
bzero(blockmap, sizeof(*blockmap));
blockmap->phys_offset = HAMMER_BLOCKMAP_UNAVAIL;
blockmap->first_offset = HAMMER_ENCODE_UNDO(0);
blockmap->next_offset = blockmap->first_offset;
blockmap->alloc_offset = HAMMER_ENCODE_UNDO(undo_limit);
hammer_crc_set_blockmap(HammerVersion, blockmap);
limit_index = undo_limit / HAMMER_BIGBLOCK_SIZE;
assert(limit_index <= HAMMER_MAX_UNDO_BIGBLOCKS);
for (n = 0; n < limit_index; ++n)
ondisk->vol0_undo_array[n] = alloc_undo_bigblock(root_vol);
while (n < HAMMER_MAX_UNDO_BIGBLOCKS)
ondisk->vol0_undo_array[n++] = HAMMER_BLOCKMAP_UNAVAIL;
printf("initializing the undo map (%jd MB)\n",
(intmax_t)HAMMER_OFF_LONG_ENCODE(blockmap->alloc_offset) /
(1024 * 1024));
scan = blockmap->first_offset;
seqno = 0;
while (scan < blockmap->alloc_offset) {
hammer_fifo_head_t head;
hammer_fifo_tail_t tail;
int bytes = HAMMER_UNDO_ALIGN;
int isnew = ((scan & HAMMER_BUFMASK64) == 0);
head = get_buffer_data(scan, &buffer, isnew);
buffer->cache.modified = 1;
tail = (void *)((char *)head + bytes - sizeof(*tail));
bzero(head, bytes);
head->hdr_signature = HAMMER_HEAD_SIGNATURE;
head->hdr_type = HAMMER_HEAD_TYPE_DUMMY;
head->hdr_size = bytes;
head->hdr_seq = seqno++;
tail->tail_signature = HAMMER_TAIL_SIGNATURE;
tail->tail_type = HAMMER_HEAD_TYPE_DUMMY;
tail->tail_size = bytes;
hammer_crc_set_fifo_head(HammerVersion, head, bytes);
scan += bytes;
}
rel_buffer(buffer);
}
const char *zone_labels[] = {
"",
"raw_volume",
"raw_buffer",
"undo",
"freemap",
"",
"",
"",
"btree",
"meta",
"large_data",
"small_data",
"",
"",
"",
"unavail",
};
void
print_blockmap(const volume_info_t volume)
{
hammer_blockmap_t blockmap;
hammer_volume_ondisk_t ondisk = volume->ondisk;
int64_t size, used;
int i;
char *fstype, *fsid;
#define INDENT ""
printf(INDENT"vol_label\t%s\n", ondisk->vol_label);
printf(INDENT"vol_count\t%d\n", ondisk->vol_count);
hammer_uuid_to_string(&ondisk->vol_fstype, &fstype);
hammer_uuid_to_string(&ondisk->vol_fsid, &fsid);
printf(INDENT"vol_fstype\t%s", fstype);
if (strcmp(fstype, "61dc63ac-6e38-11dc-8513-01301bb8a9f5") == 0)
printf(" \"%s\"\n", HAMMER_FSTYPE_STRING);
else
printf("\n");
printf(INDENT"vol_fsid\t%s\n", fsid);
free(fstype);
free(fsid);
printf(INDENT"vol_bot_beg\t%s\n", sizetostr(ondisk->vol_bot_beg));
printf(INDENT"vol_mem_beg\t%s\n", sizetostr(ondisk->vol_mem_beg));
printf(INDENT"vol_buf_beg\t%s\n", sizetostr(ondisk->vol_buf_beg));
printf(INDENT"vol_buf_end\t%s\n", sizetostr(ondisk->vol_buf_end));
printf(INDENT"vol0_next_tid\t%016jx\n",
(uintmax_t)ondisk->vol0_next_tid);
blockmap = &ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
size = HAMMER_OFF_LONG_ENCODE(blockmap->alloc_offset);
if (blockmap->first_offset <= blockmap->next_offset)
used = blockmap->next_offset - blockmap->first_offset;
else
used = blockmap->alloc_offset - blockmap->first_offset +
HAMMER_OFF_LONG_ENCODE(blockmap->next_offset);
printf(INDENT"undo_size\t%s\n", sizetostr(size));
printf(INDENT"undo_used\t%s\n", sizetostr(used));
printf(INDENT"zone # "
"phys first next alloc\n");
for (i = 0; i < HAMMER_MAX_ZONES; i++) {
blockmap = &ondisk->vol0_blockmap[i];
printf(INDENT"zone %-2d %-10s %016jx %016jx %016jx %016jx\n",
i, zone_labels[i],
(uintmax_t)blockmap->phys_offset,
(uintmax_t)blockmap->first_offset,
(uintmax_t)blockmap->next_offset,
(uintmax_t)blockmap->alloc_offset);
}
}
void
flush_all_volumes(void)
{
volume_info_t volume;
TAILQ_FOREACH(volume, &VolList, entry)
flush_volume(volume);
}
void
flush_volume(volume_info_t volume)
{
buffer_info_t buffer;
int i;
for (i = 0; i < HAMMER_BUFLISTS; ++i) {
TAILQ_FOREACH(buffer, &volume->buffer_lists[i], entry)
flush_buffer(buffer);
}
if (writehammervol(volume) == -1) {
err(1, "Write volume %d (%s)", volume->vol_no, volume->name);
}
}
void
flush_buffer(buffer_info_t buffer)
{
volume_info_t volume;
volume = buffer->volume;
if (writehammerbuf(buffer) == -1) {
err(1, "Write volume %d (%s)", volume->vol_no, volume->name);
}
buffer->cache.modified = 0;
}
static
int
__read(volume_info_t volume, void *data, int64_t offset, int size)
{
ssize_t n;
n = pread(volume->fd, data, size, offset);
if (n != size)
return(-1);
return(0);
}
static __inline
int
readhammervol(volume_info_t volume)
{
return(__read(volume, volume->ondisk, 0, HAMMER_BUFSIZE));
}
static __inline
int
readhammerbuf(buffer_info_t buffer)
{
return(__read(buffer->volume, buffer->ondisk, buffer->raw_offset,
HAMMER_BUFSIZE));
}
static
int
__write(volume_info_t volume, const void *data, int64_t offset, int size)
{
ssize_t n;
if (volume->rdonly)
return(0);
n = pwrite(volume->fd, data, size, offset);
if (n != size)
return(-1);
return(0);
}
static __inline
int
writehammervol(volume_info_t volume)
{
return(__write(volume, volume->ondisk, 0, HAMMER_BUFSIZE));
}
static __inline
int
writehammerbuf(buffer_info_t buffer)
{
return(__write(buffer->volume, buffer->ondisk, buffer->raw_offset,
HAMMER_BUFSIZE));
}
int64_t
init_boot_area_size(int64_t value, off_t avg_vol_size)
{
if (value == 0) {
value = HAMMER_BOOT_NOMBYTES;
while (value > avg_vol_size / HAMMER_MAX_VOLUMES)
value >>= 1;
}
if (value < HAMMER_BOOT_MINBYTES)
value = HAMMER_BOOT_MINBYTES;
else if (value > HAMMER_BOOT_MAXBYTES)
value = HAMMER_BOOT_MAXBYTES;
return(value);
}
int64_t
init_memory_log_size(int64_t value, off_t avg_vol_size)
{
if (value == 0) {
value = HAMMER_MEM_NOMBYTES;
while (value > avg_vol_size / HAMMER_MAX_VOLUMES)
value >>= 1;
}
if (value < HAMMER_MEM_MINBYTES)
value = HAMMER_MEM_MINBYTES;
else if (value > HAMMER_MEM_MAXBYTES)
value = HAMMER_MEM_MAXBYTES;
return(value);
}