/*
 * Copyright (c) 2011 The DragonFly Project.  All rights reserved.
 *
 * This code is derived from software contributed to The DragonFly Project
 * by Antonio Huete <tuxillo@quantumachine.net>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. Neither the name of The DragonFly Project nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific, prior written permission.
 *
 * 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 HOLDERS 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.
 */

#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <unistd.h>

#include "libhammer.h"

libhammer_fsinfo_t
libhammer_get_fsinfo(const char *path)
{
	struct hammer_pseudofs_data *pfs_od;
	struct hammer_ioc_pseudofs_rw pfs;
	struct hammer_ioc_info info;
	libhammer_pfsinfo_t pip;
	libhammer_fsinfo_t fip;
	int error = 0;
	int fd;

	if ((fd = open(path, O_RDONLY)) < 0)
		return NULL;

	fip = _libhammer_malloc(sizeof(*fip));
	TAILQ_INIT(&fip->list_pseudo);
	if ((ioctl(fd, HAMMERIOC_GET_INFO, &info)) < 0) {
		libhammer_free_fsinfo(fip);
		close(fd);
		return NULL;
	}

	/* Fill filesystem information */
	snprintf(fip->vol_name, TXTLEN, "%s", info.vol_label);
	fip->vol_fsid = info.vol_fsid;
	fip->version = info.version;
	fip->nvolumes = info.nvolumes;
	fip->rootvol = info.rootvol;
	fip->inodes = info.inodes;
	fip->bigblocks = info.bigblocks;
	fip->freebigblocks = info.freebigblocks;
	fip->rsvbigblocks = info.rsvbigblocks;

	bzero(&pfs, sizeof(pfs));
	pfs.pfs_id = 0;	/* start off with root PFS */
	pfs.ondisk = _libhammer_malloc(sizeof(*pfs_od));
	pfs.bytes = sizeof(struct hammer_pseudofs_data);

	while(error == 0) {
		error = ioctl(fd, HAMMERIOC_SCAN_PSEUDOFS, &pfs);
		if (error == 0 && !hammer_is_pfs_deleted(pfs.ondisk)) {
			pip = _libhammer_malloc(sizeof(*pip));
			pfs_od = pfs.ondisk;
			pip->ismaster = hammer_is_pfs_master(pfs_od);

			/*
			 * Fill in structs used in the library. We don't rely on
			 * HAMMER structs but we do fill our own.
			 */
			pip->version = fip->version;
			pip->pfs_id = pfs.pfs_id;
			pip->mirror_flags = pfs_od->mirror_flags;
			pip->beg_tid = pfs_od->sync_beg_tid;
			pip->end_tid = pfs_od->sync_end_tid;
			pip->mountedon =
			    libhammer_find_pfs_mount(&pfs_od->unique_uuid);
			if (fip->version < 3) {
				libhammer_compat_old_snapcount(pip);
			} else {
				TAILQ_INIT(&pip->list_snap);
				if (libhammer_pfs_get_snapshots(fip, pip) < 0)
					pip->snapcount = 0;
			}

			/*
			 * PFS retrieval goes 0..n so inserting in the tail
			 * leaves the TAILQ sorted by pfs_id.
			 * This is important since quickly getting PFS #0 is
			 * required for some functions so *DO NOT CHANGE IT*.
			 */
			TAILQ_INSERT_TAIL(&fip->list_pseudo, pip, entries);

		}
		pfs.pfs_id++;
	}
	free(pfs.ondisk);

	close (fd);

	return (fip);
}

void
libhammer_free_fsinfo(libhammer_fsinfo_t fip)
{
	struct libhammer_pfsinfo *pfstmp;

	while(!TAILQ_EMPTY(&fip->list_pseudo)) {
		pfstmp = TAILQ_FIRST(&fip->list_pseudo);
		libhammer_pfs_free_snapshots(pfstmp);
		if (pfstmp->mountedon)
			free(pfstmp->mountedon);
		TAILQ_REMOVE(&fip->list_pseudo, pfstmp, entries);
		free(pfstmp);
	}
	free(fip);
}
