#include "hammer2.h"
typedef void (*cmd_callback)(const void *, hammer2_blockref_t *, int);
static void h2disk_check(const char *devpath, cmd_callback callback1);
static void h2pfs_check(int fd, hammer2_blockref_t *bref,
cmd_callback callback2);
static void info_callback1(const void *, hammer2_blockref_t *, int);
static void info_callback2(const void *, hammer2_blockref_t *, int);
static
void
h2disk_check_serno(cmd_callback fn)
{
DIR *dir;
if ((dir = opendir("/dev/serno")) != NULL) {
struct dirent *den;
while ((den = readdir(dir)) != NULL) {
const char *ptr;
int slice;
char part;
char *devpath;
if (!strcmp(den->d_name, ".") ||
!strcmp(den->d_name, ".."))
continue;
ptr = strrchr(den->d_name, '.');
if (ptr && sscanf(ptr, ".s%d%c", &slice, &part) == 2) {
asprintf(&devpath, "/dev/serno/%s",
den->d_name);
h2disk_check(devpath, fn);
free(devpath);
}
}
closedir(dir);
}
}
static
void
h2disk_check_dm(cmd_callback fn)
{
DIR *dir;
if ((dir = opendir("/dev/mapper")) != NULL) {
struct dirent *den;
while ((den = readdir(dir)) != NULL) {
char *devpath;
if (!strcmp(den->d_name, ".") ||
!strcmp(den->d_name, "..") ||
!strcmp(den->d_name, "control"))
continue;
asprintf(&devpath, "/dev/mapper/%s",
den->d_name);
h2disk_check(devpath, fn);
free(devpath);
}
closedir(dir);
}
}
static
void
h2disk_check_misc(cmd_callback fn)
{
DIR *dir;
if ((dir = opendir("/dev")) != NULL) {
struct dirent *den;
while ((den = readdir(dir)) != NULL) {
char *devpath;
if (!strcmp(den->d_name, ".") ||
!strcmp(den->d_name, ".."))
continue;
if (strncmp(den->d_name, "ad", 2) &&
strncmp(den->d_name, "vn", 2))
continue;
if (!strcmp(den->d_name, "vn"))
continue;
if (!strncmp(den->d_name, "ad", 2) &&
den->d_namlen <= 3)
continue;
asprintf(&devpath, "/dev/%s", den->d_name);
h2disk_check(devpath, fn);
free(devpath);
}
closedir(dir);
}
}
int
cmd_info(int ac, const char **av)
{
int i;
for (i = 0; i < ac; ++i)
h2disk_check(av[i], info_callback1);
if (ac == 0) {
h2disk_check_serno(info_callback1);
h2disk_check_dm(info_callback1);
h2disk_check_misc(info_callback1);
}
return 0;
}
struct pfs_entry {
TAILQ_ENTRY(pfs_entry) entry;
char name[NAME_MAX+1];
char s[NAME_MAX+1];
};
static TAILQ_HEAD(, pfs_entry) head;
static
void
info_callback1(const void *path, hammer2_blockref_t *bref, int fd)
{
struct pfs_entry *p;
printf("%s:\n", (const char*)path);
TAILQ_INIT(&head);
h2pfs_check(fd, bref, info_callback2);
printf(" Type "
"ClusterId (pfs_clid) "
"Label\n");
while ((p = TAILQ_FIRST(&head)) != NULL) {
printf(" %s %s\n", p->s, p->name);
TAILQ_REMOVE(&head, p, entry);
free(p);
}
}
static
void
info_callback2(const void *data,
hammer2_blockref_t *bref __unused, int fd __unused)
{
const hammer2_inode_data_t *ipdata = data;
const hammer2_inode_meta_t *meta = &ipdata->meta;
char *pfs_id_str = NULL;
const char *type_str;
struct pfs_entry *p, *e;
uuid_t uuid;
uuid = meta->pfs_clid;
hammer2_uuid_to_str(&uuid, &pfs_id_str);
if (meta->pfs_type == HAMMER2_PFSTYPE_MASTER) {
if (meta->pfs_subtype == HAMMER2_PFSSUBTYPE_NONE)
type_str = "MASTER";
else
type_str = hammer2_pfssubtype_to_str(meta->pfs_subtype);
} else {
type_str = hammer2_pfstype_to_str(meta->pfs_type);
}
e = calloc(1, sizeof(*e));
snprintf(e->name, sizeof(e->name), "%s", ipdata->filename);
snprintf(e->s, sizeof(e->s), "%-11s %s", type_str, pfs_id_str);
free(pfs_id_str);
p = TAILQ_FIRST(&head);
while (p) {
if (strcmp(e->name, p->name) <= 0) {
TAILQ_INSERT_BEFORE(p, e, entry);
break;
}
p = TAILQ_NEXT(p, entry);
}
if (!p)
TAILQ_INSERT_TAIL(&head, e, entry);
}
static void mount_callback1(const void *, hammer2_blockref_t *, int);
static void mount_callback2(const void *, hammer2_blockref_t *, int);
static void cmd_mountall_alarm(int signo);
static volatile sig_atomic_t DidAlarm;
int
cmd_mountall(int ac, const char **av)
{
int i;
pid_t pid;
for (i = 0; i < ac; ++i)
h2disk_check(av[i], mount_callback1);
if (ac == 0) {
h2disk_check_serno(mount_callback1);
h2disk_check_dm(mount_callback1);
h2disk_check_misc(mount_callback1);
}
signal(SIGALRM, cmd_mountall_alarm);
for (;;) {
alarm(15);
pid = wait3(NULL, 0, NULL);
if (pid < 0 && errno == ECHILD)
break;
if (pid < 0 && DidAlarm) {
printf("Timeout waiting for mounts to complete\n");
break;
}
}
alarm(0);
return 0;
}
static
void
cmd_mountall_alarm(int signo __unused)
{
DidAlarm = 1;
}
static const char *mount_path;
static const char *mount_comp;
static
void
mount_callback1(const void *devpath, hammer2_blockref_t *bref, int fd)
{
mount_path = devpath;
mount_comp = strrchr(devpath, '/');
if (mount_comp) {
++mount_comp;
h2pfs_check(fd, bref, mount_callback2);
}
}
static
void
mount_callback2(const void *data,
hammer2_blockref_t *bref __unused, int fd)
{
const hammer2_inode_data_t *ipdata = data;
char *tmp_path;
char *label;
int tfd;
if (strcmp(ipdata->filename, "LOCAL") == 0) {
if ((tfd = open("/dev/null", O_RDONLY)) >= 0) {
dup2(tfd, fd);
close(tfd);
} else {
perror("open(/dev/null)");
exit(1);
}
asprintf(&tmp_path, "/var/hammer2/LOCAL.%s", mount_comp);
asprintf(&label, "%s@LOCAL", mount_path);
mkdir("/var/hammer2", 0700);
mkdir(tmp_path, 0700);
printf("mount %s\n", tmp_path);
if (fork() == 0) {
execl("/sbin/mount_hammer2",
"mount",
label,
tmp_path,
NULL);
}
free(label);
free(tmp_path);
}
}
static
void
h2disk_check(const char *devpath, cmd_callback callback1)
{
hammer2_blockref_t broot;
hammer2_blockref_t best;
hammer2_media_data_t media;
hammer2_volume_data_t *voldata;
struct partinfo partinfo;
int fd;
int i;
int best_i;
fd = open(devpath, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Unable to open \"%s\"\n", devpath);
return;
}
if (ioctl(fd, DIOCGPART, &partinfo) == -1) {
fprintf(stderr, "DIOCGPART failed on \"%s\"\n", devpath);
goto done;
}
if (partinfo.fstype != FS_HAMMER2) {
uint32_t status;
uuid_t h2uuid;
int is_nil = uuid_is_nil(&partinfo.fstype_uuid, NULL);
uuid_from_string(HAMMER2_UUID_STRING, &h2uuid, &status);
if (!is_nil && (status != uuid_s_ok ||
uuid_compare(&partinfo.fstype_uuid, &h2uuid, NULL) != 0)) {
goto done;
}
}
best_i = -1;
bzero(&best, sizeof(best));
for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
bzero(&broot, sizeof(broot));
broot.type = HAMMER2_BREF_TYPE_VOLUME;
broot.data_off = (i * HAMMER2_ZONE_BYTES64) |
HAMMER2_PBUFRADIX;
lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET);
if (read(fd, &media, HAMMER2_PBUFSIZE) !=
(ssize_t)HAMMER2_PBUFSIZE)
continue;
voldata = &media.voldata;
if (voldata->magic != HAMMER2_VOLUME_ID_HBO)
continue;
if (voldata->nvolumes > 1)
break;
broot.mirror_tid = voldata->mirror_tid;
if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
best_i = i;
best = broot;
}
}
if (best_i >= 0)
callback1(devpath, &best, fd);
done:
close(fd);
}
static
void
h2pfs_check(int fd, hammer2_blockref_t *bref, cmd_callback callback2)
{
hammer2_media_data_t media;
hammer2_blockref_t *bscan;
int bcount;
int i;
size_t bytes;
size_t io_bytes;
size_t boff;
uint32_t cv;
uint64_t cv64;
hammer2_off_t io_off;
hammer2_off_t io_base;
bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
if (bytes)
bytes = (size_t)1 << bytes;
io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
io_bytes = bytes;
boff = io_off - io_base;
io_bytes = HAMMER2_LBUFSIZE;
while (io_bytes + boff < bytes)
io_bytes <<= 1;
if (io_bytes > sizeof(media)) {
printf("(bad block size %zu)\n", bytes);
return;
}
if (bref->type != HAMMER2_BREF_TYPE_DATA) {
lseek(fd, io_base, SEEK_SET);
if (read(fd, &media, io_bytes) != (ssize_t)io_bytes) {
printf("(media read failed)\n");
return;
}
if (boff)
bcopy((char *)&media + boff, &media, bytes);
}
bscan = NULL;
bcount = 0;
if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) {
switch(HAMMER2_DEC_CHECK(bref->methods)) {
case HAMMER2_CHECK_NONE:
break;
case HAMMER2_CHECK_DISABLED:
break;
case HAMMER2_CHECK_ISCSI32:
cv = hammer2_icrc32(&media, bytes);
if (bref->check.iscsi32.value != cv) {
printf("\t(icrc failed %02x:%08x/%08x)\n",
bref->methods,
bref->check.iscsi32.value,
cv);
}
break;
case HAMMER2_CHECK_XXHASH64:
cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED);
if (bref->check.xxhash64.value != cv64) {
printf("\t(xxhash failed %02x:%016jx/%016jx)\n",
bref->methods,
bref->check.xxhash64.value,
cv64);
}
break;
case HAMMER2_CHECK_SHA192:
break;
case HAMMER2_CHECK_FREEMAP:
cv = hammer2_icrc32(&media, bytes);
if (bref->check.freemap.icrc32 != cv) {
printf("\t(fcrc %02x:%08x/%08x)\n",
bref->methods,
bref->check.freemap.icrc32,
cv);
}
break;
}
}
switch(bref->type) {
case HAMMER2_BREF_TYPE_INODE:
if (media.ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
if ((media.ipdata.meta.op_flags &
HAMMER2_OPFLAG_DIRECTDATA) == 0) {
bscan = &media.ipdata.u.blockset.blockref[0];
bcount = HAMMER2_SET_COUNT;
}
} else if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
callback2(&media.ipdata, bref, fd);
bscan = NULL;
bcount = 0;
} else {
bscan = NULL;
bcount = 0;
}
break;
case HAMMER2_BREF_TYPE_INDIRECT:
bscan = &media.npdata[0];
bcount = bytes / sizeof(hammer2_blockref_t);
break;
case HAMMER2_BREF_TYPE_VOLUME:
bscan = &media.voldata.sroot_blockset.blockref[0];
bcount = HAMMER2_SET_COUNT;
break;
default:
break;
}
for (i = 0; i < bcount; ++i) {
if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY)
h2pfs_check(fd, &bscan[i], callback2);
}
}