* Copyright (c) 2009 The DragonFly Project. All rights reserved.
*
* This code is derived from software contributed to The DragonFly Project
- * by Michael Neumann <mneumann@ntecs.de>
+ * by Matthew Dillon <dillon@backplane.com> and
+ * Michael Neumann <mneumann@ntecs.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#include "hammer.h"
#include <string.h>
+static uint64_t check_volume(const char *vol_name);
static void expand_usage(int exit_code);
/*
bzero(&expand, sizeof(expand));
strncpy(expand.device_name, av[1], MAXPATHLEN);
+ expand.vol_size = check_volume(av[1]);
+ expand.boot_area_size = 0; // XXX
+ expand.mem_area_size = 0; // XXX
if (ioctl(fd, HAMMERIOC_EXPAND, &expand) < 0) {
fprintf(stderr, "hammer expand ioctl: %s\n", strerror(errno));
fprintf(stderr, "hammer expand <filesystem> <device>\n");
exit(exit_code);
}
+
+/*
+ * Check basic volume characteristics. HAMMER filesystems use a minimum
+ * of a 16KB filesystem buffer size.
+ *
+ * Returns the size of the device.
+ *
+ * From newfs_hammer.c
+ */
+static
+uint64_t
+check_volume(const char *vol_name)
+{
+ struct partinfo pinfo;
+ int fd;
+
+ /*
+ * Get basic information about the volume
+ */
+ fd = open(vol_name, O_RDWR);
+ if (fd < 0)
+ errx(1, "Unable to open %s R+W", vol_name);
+
+ if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
+ errx(1, "No block device: %s", vol_name);
+ }
+ /*
+ * When formatting a block device as a HAMMER volume the
+ * sector size must be compatible. HAMMER uses 16384 byte
+ * filesystem buffers.
+ */
+ if (pinfo.reserved_blocks) {
+ errx(1, "HAMMER cannot be placed in a partition "
+ "which overlaps the disklabel or MBR");
+ }
+ if (pinfo.media_blksize > 16384 ||
+ 16384 % pinfo.media_blksize) {
+ errx(1, "A media sector size of %d is not supported",
+ pinfo.media_blksize);
+ }
+
+ close(fd);
+ return pinfo.media_size;
+}
* Copyright (c) 2009 The DragonFly Project. All rights reserved.
*
* This code is derived from software contributed to The DragonFly Project
- * by Michael Neumann <mneumann@ntecs.de>
+ * by Matthew Dillon <dillon@backplane.com> and
+ * Michael Neumann <mneumann@ntecs.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
*/
#include "hammer.h"
+#include <sys/fcntl.h>
+#include <sys/nlookup.h>
+#include <sys/buf.h>
+
+static int
+hammer_format_volume_header(struct hammer_mount *hmp, const char *vol_name,
+ int vol_no, int vol_count,
+ int64_t vol_size, int64_t boot_area_size, int64_t mem_area_size);
+
int
hammer_ioc_expand(hammer_transaction_t trans, hammer_inode_t ip,
struct hammer_ioc_expand *expand)
{
- kprintf("hammer expand, device: %s\n", expand->device_name);
- return 0;
+ struct hammer_mount *hmp = trans->hmp;
+ struct mount *mp = hmp->mp;
+ int error;
+
+ if (mp->mnt_flag & MNT_RDONLY) {
+ kprintf("Cannot expand read-only HAMMER filesystem\n");
+ return (EINVAL);
+ }
+
+ if (hmp->nvolumes + 1 >= HAMMER_MAX_VOLUMES) {
+ kprintf("Max number of HAMMER volumes exceeded\n");
+ return (EINVAL);
+ }
+
+ error = hammer_format_volume_header(
+ hmp,
+ hmp->rootvol->ondisk->vol_name,
+ hmp->nvolumes,
+ hmp->nvolumes+1,
+ expand->vol_size,
+ expand->boot_area_size,
+ expand->mem_area_size);
+
+ if (error == 0) {
+ error = hammer_install_volume(hmp, expand->device_name, NULL);
+ }
+
+ if (error == 0) {
+ ++hmp->nvolumes;
+ hammer_sync_lock_sh(trans);
+ hammer_lock_ex(&hmp->blkmap_lock);
+
+ /*
+ * Set each volumes new value of the vol_count field.
+ */
+ for (int vol_no = 0; vol_no < hmp->nvolumes; ++vol_no) {
+ hammer_volume_t volume;
+ volume = hammer_get_volume(hmp, vol_no, &error);
+ KKASSERT(error == 0);
+ hammer_modify_volume_field(trans, volume, vol_count);
+ volume->ondisk->vol_count = hmp->nvolumes;
+ hammer_modify_volume_done(volume);
+ hammer_rel_volume(volume, 0);
+ }
+
+ hammer_unlock(&hmp->blkmap_lock);
+ hammer_sync_unlock(trans);
+ }
+
+ if (error) {
+ kprintf("An error occured: %d\n", error);
+ }
+
+ return (error);
+}
+
+static int
+hammer_format_volume_header(struct hammer_mount *hmp, const char *vol_name,
+ int vol_no, int vol_count,
+ int64_t vol_size, int64_t boot_area_size, int64_t mem_area_size)
+{
+ struct vnode *devvp = NULL;
+ struct buf *bp = NULL;
+ struct nlookupdata nd;
+ struct hammer_volume_ondisk *ondisk;
+ int error;
+
+ /*
+ * Get the device vnode
+ */
+ error = nlookup_init(&nd, vol_name, UIO_SYSSPACE, NLC_FOLLOW);
+ if (error == 0)
+ error = nlookup(&nd);
+ if (error == 0)
+ error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp);
+ nlookup_done(&nd);
+
+ if (error == 0) {
+ if (vn_isdisk(devvp, &error)) {
+ error = vfs_mountedon(devvp);
+ }
+ }
+ if (error == 0 &&
+ count_udev(devvp->v_umajor, devvp->v_uminor) > 0) {
+ error = EBUSY;
+ }
+ if (error == 0) {
+ vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
+ error = vinvalbuf(devvp, V_SAVE, 0, 0);
+ if (error == 0) {
+ error = VOP_OPEN(devvp, FREAD|FWRITE, FSCRED, NULL);
+ }
+ vn_unlock(devvp);
+ }
+ if (error) {
+ if (devvp)
+ vrele(devvp);
+ return (error);
+ }
+
+ /*
+ * Extract the volume number from the volume header and do various
+ * sanity checks.
+ */
+ KKASSERT(HAMMER_BUFSIZE >= sizeof(struct hammer_volume_ondisk));
+ error = bread(devvp, 0LL, HAMMER_BUFSIZE, &bp);
+ if (error || bp->b_bcount < sizeof(struct hammer_volume_ondisk))
+ goto late_failure;
+
+ ondisk = (struct hammer_volume_ondisk*) bp->b_data;
+
+ /*
+ * Note that we do NOT allow to use a device that contains
+ * a valid HAMMER signature. It has to be cleaned up with dd
+ * before.
+ */
+ if (ondisk->vol_signature == HAMMER_FSBUF_VOLUME) {
+ kprintf("hammer_expand: Formatting of valid HAMMER volume "
+ "%s denied. Erase with dd!\n", vol_name);
+ error = EFTYPE;
+ goto late_failure;
+ }
+
+ bzero(ondisk, sizeof(struct hammer_volume_ondisk));
+ ksnprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", vol_name);
+ ondisk->vol_fstype = hmp->rootvol->ondisk->vol_fstype;
+ ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
+ ondisk->vol_fsid = hmp->fsid;
+ ondisk->vol_rootvol = hmp->rootvol->vol_no;
+ ondisk->vol_no = vol_no;
+ ondisk->vol_count = vol_count;
+ ondisk->vol_version = hmp->version;
+
+ /*
+ * Reserve space for (future) header junk, setup our poor-man's
+ * bigblock allocator.
+ */
+ int64_t vol_alloc = HAMMER_BUFSIZE * 16;
+
+ ondisk->vol_bot_beg = vol_alloc;
+ vol_alloc += boot_area_size;
+ ondisk->vol_mem_beg = vol_alloc;
+ vol_alloc += mem_area_size;
+
+ /*
+ * The remaining area is the zone 2 buffer allocation area. These
+ * buffers
+ */
+ ondisk->vol_buf_beg = vol_alloc;
+ ondisk->vol_buf_end = vol_size & ~(int64_t)HAMMER_BUFMASK;
+
+ if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
+ kprintf("volume %d %s is too small to hold the volume header",
+ ondisk->vol_no, ondisk->vol_name);
+ error = EFTYPE;
+ goto late_failure;
+ }
+
+ ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
+ HAMMER_BUFSIZE;
+ ondisk->vol_blocksize = HAMMER_BUFSIZE;
+
+ /*
+ * Write volume header to disk
+ */
+ error = bwrite(bp);
+ bp = NULL;
+
+late_failure:
+ if (bp)
+ brelse(bp);
+ VOP_CLOSE(devvp, FREAD|FWRITE);
+ if (devvp)
+ vrele(devvp);
+ return (error);
}