/*
 *   Copyright (c) International Business Machines Corp., 2000-2002
 *
 *   This program is free software;  you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program;  if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
/*
 *   COMPONENT_NAME: jfs_fscklog (formerly xchklog)
 *
 *      The jfs_fscklog tool provides 2 basic functions - fsck.jfs
 *      service log extraction and display.  The code here extracts
 *      the log.
 *
 */
#include <config.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

#include "devices.h"
#include "diskmap.h"
#include "fsckwsp.h"
#include "fsck_message.h"
#include "jfs_endian.h"
#include "jfs_fscklog.h"
#include "super.h"
#include "utilsubs.h"
#include "xfsck.h"

#define fsck_READ  1
#define fsck_WRITE 2

extern char file_name[128];

FILE *outfp;

extern struct fscklog_record fscklog_record;
extern struct fscklog_record *local_recptr;

 /* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  *
  * superblock I/O buffer and pointer
  *
  */
struct superblock aggr_superblock;
struct superblock *sb_ptr;

 /* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  *
  * input:  fsck service log I/O buffer
  * output: fsck extracted service log I/O buffer
  *
  */
char fscklog_buffer[FSCKLOG_BUFSIZE];
char xchklog_buffer[XCHKLOG_BUFSIZE];

 /* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  *
  * For message processing
  *
  *    values are assigned by the main jfs_fscklog routine
  */
extern char *Vol_Label;

 /* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  *
  * Device information.
  *
  *     values are assigned when (if) the device is opened.
  */
FILE *Dev_IOPort;
unsigned Dev_blksize;
int Dev_SectorSize;

/* VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
 *
 * The following are internal to this file
 *
 */
void extract_service_log(void);

int xchklog_final_processing(void);

int xchklog_fscklog_fill_buffer(void);

int xchklog_initial_processing(struct fscklog_record *);

int open_device_read(const char *);

int open_outfile(void);

int readwrite_device(int64_t, unsigned, unsigned *, void *, int);

void record_msg(char *);

int validate_super(int);

int validate_superblock(void);

/*****************************************************************************
 * NAME: xchklog
 *
 * FUNCTION: Entry point for jfs read aggregate fsck service log
 *
 * PARAMETERS:
 *           local_recptr - fscklog record
 *
 * RETURNS:
 *      success: 0
 *      failure: something else
 */
int xchklog(struct fscklog_record *local_recptr)
{
	int rc = 0;

	/*
	 * some basic initializations
	 */
	sb_ptr = &aggr_superblock;

	rc = xchklog_initial_processing(local_recptr);

	if (rc == 0) {
		/* we have a buffer and the device is open */
		extract_service_log();
	}

	if (rc == 0) {
		rc = xchklog_final_processing();
	} else {
		xchklog_final_processing();
	}

	return (rc);
}

/***************************************************************************
 * NAME: extract_service_log
 *
 * FUNCTION:
 *
 * PARAMETERS:  none
 *
 * RETURNS:
 *      success: 0
 *      failure: something else
 */
void extract_service_log()
{
	char *msg_txt;
	int dsl_rc = 0;
	char *log_entry;
	int log_entry_pos;
	int bytes_left;
	struct fscklog_entry_hdr *hdptr;

	local_recptr->fscklog_last_read = 0;

	while ((!local_recptr->fscklog_last_read) && (dsl_rc == 0)) {

		dsl_rc = xchklog_fscklog_fill_buffer();
		local_recptr->this_buffer_last_message = 0;
		while ((!local_recptr->this_buffer_last_message)
		       && (dsl_rc == 0)) {

			hdptr = (struct fscklog_entry_hdr *)
			    (local_recptr->infile_buf_ptr +
			     local_recptr->infile_buf_data_len);
			if (hdptr->entry_length == 0) {
				/* no more entries in this buffer */
				local_recptr->this_buffer_last_message = -1;
			} else {
				/* this entry_length != 0 */

				/* swap if on big endian machine */
				ujfs_swap_fscklog_entry_hdr(hdptr);

				log_entry = (char *) hdptr;
				log_entry_pos =
				    sizeof (struct fscklog_entry_hdr);

				/*
				 * Each FSCKLOG_BUFSIZE boundary starts a new
				 * log record.  Otherwise, we depend on a valid
				 * entry_length to locate the next message.
				 */
				bytes_left = local_recptr->infile_buf_length -
				    local_recptr->infile_buf_data_len;
				if ((bytes_left < 0)
				    || (hdptr->entry_length < 0)
				    || (hdptr->entry_length > bytes_left)) {
					/* this isn't a valid log record length */
					send_msg(fsck_CHKLOGINVALRCD);
					local_recptr->this_buffer_last_message =
					    -1;
				} else {
					/* the log record length looks ok */
					msg_txt = &log_entry[log_entry_pos];

					/*
					 * record the message in the output file
					 */
					record_msg(msg_txt);

					/*
					 * set up for the next record
					 */
					local_recptr->infile_buf_data_len +=
					    hdptr->entry_length;
					/*if (hdptr->msg_num == fsck_SESSEND) {
						local_recptr->
						    this_buffer_last_message =
						    -1;
						    }*/
					if (local_recptr->infile_buf_data_len >=
					    local_recptr->infile_buf_length) {
						local_recptr->
						    this_buffer_last_message =
						    -1;
					}
				}
			}
		}
	}

	return;
}

/***************************************************************************
 * NAME: xchklog_final_processing
 *
 * FUNCTION:  If processing read/write, replicate the superblock and the
 *            aggregate inode structures (i.e., the Aggregate Inode Map
 *            and the Aggregate Inode Table).
 *
 *            Notify the user about various things.
 *
 * PARAMETERS:  none
 *
 * RETURNS:
 *      success: 0
 *      failure: something else
 */
int xchklog_final_processing()
{
	int pf_rc = 0;

	/*
	 * Close the device
	 */
	if (local_recptr->device_is_open) {
		pf_rc = fclose(Dev_IOPort);
	}

	/*
	 * Close the output file
	 */
	if (local_recptr->outfile_is_open) {
		/*
		 * flush the buffer if necessary
		 */
		if (local_recptr->outfile_buf_data_len != 0) {
			fwrite((const void *) (local_recptr->outfile_buf_ptr),
			       sizeof (char), local_recptr->outfile_buf_length,
			       outfp);
		}
		fclose(outfp);
	}

	return (pf_rc);
}

/*****************************************************************************
 * NAME: fscklog_fill_buffer
 *
 * FUNCTION:  If the current fsck session has write access to the aggregate,
 *            and if the in-aggregate fsck log is not full, write the
 *            contents of the current fscklog buffer into the in-aggregate
 *            fsck log.
 *
 * PARAMETERS:  none
 *
 * NOTES:  o Unlike most _put_ routines in this module, fscklog_put_buffer
 *           actually writes to the device.  This is done because the fsck
 *           log contains information which provides crucial serviceability
 *           should the fsck session be interrupted.
 *
 *         o Errors here are recorded in the control page of the fsck
 *           in-aggregate workspace but never affect other fsck processing.
 *
 * RETURNS:
 *      success: 0
 *      failure: something else
 */
int xchklog_fscklog_fill_buffer()
{
	int flfb_rc = 0;
	int io_rc = 0;
	unsigned bytes_read = 0;
	unsigned log_bytes_left;

	io_rc = readwrite_device(local_recptr->infile_agg_offset,
				 local_recptr->infile_buf_length,
				 &bytes_read,
				 (void *) local_recptr->infile_buf_ptr,
				 fsck_READ);

	if ((io_rc != 0)
	    || (bytes_read != (unsigned) local_recptr->infile_buf_length)) {
		/*
		 * write failed or didn't read
		 * correct number of bytes
		 */
		send_msg(fsck_URCVWRT, fsck_ref_msg(fsck_metadata), Vol_Label);

		send_msg(fsck_ERRONLOG, FSCK_BADREAD_FSCKLOG, io_rc, fsck_READ,
			 (long long) local_recptr->infile_agg_offset,
			 (long int) local_recptr->infile_buf_length,
			 (long int) bytes_read);
	}
	/*
	 * We want to reset the buffer no matter what.  If is useful
	 * to keep going because the next section may be readable.
	 */
	local_recptr->infile_agg_offset += local_recptr->infile_buf_length;
	local_recptr->infile_log_offset += local_recptr->infile_buf_length;
	local_recptr->infile_buf_data_len = 0;

	log_bytes_left = (local_recptr->ondev_fscklog_byte_length / 2) -
	    local_recptr->infile_log_offset;
	if (log_bytes_left < local_recptr->infile_buf_length) {
		/* this is the last one */
		local_recptr->fscklog_last_read = -1;
	}
	return (flfb_rc);
}

/*****************************************************************************
 * NAME: xchklog_initial_processing
 *
 * FUNCTION: Parse and verify invocation parameters.
 *           Open the device and verify that it contains a JFS file system.
 *           Read the chkdsk workspace control page.
 *           Calculate interesting aggregate offsets.
 *
 *
 * PARAMETERS:  as specified to main()
 *
 * NOTES:
 *
 * RETURNS:
 *      success: 0
 *      failure: something else
 */
int xchklog_initial_processing(struct fscklog_record *local_recptr)
{
	int pi_rc = 0;

	/*
	 * Initialize the fscklog control block
	 */
	local_recptr->infile_buf_length = FSCKLOG_BUFSIZE;
	local_recptr->infile_buf_ptr = fscklog_buffer;
	local_recptr->outfile_buf_length = XCHKLOG_BUFSIZE;
	local_recptr->outfile_buf_ptr = xchklog_buffer;

	/*
	 * Open the device and verify that it contains a valid JFS aggregate
	 * If it does, check/repair the superblock.
	 */
	pi_rc = open_device_read(Vol_Label);

	if (pi_rc != 0) {
		/*device open failed */
		send_msg(fsck_CNTRESUPB);
	} else {
		/* device is open */
		local_recptr->device_is_open = 1;
		pi_rc = validate_superblock();
		if (pi_rc == 0) {
			/* a valid superblock */

			/*
			 * add some stuff to the local record which is based on
			 * superblock fields
			 */

			/* length of the on-device fsck service log */
			local_recptr->ondev_fscklog_byte_length =
			    sb_ptr->s_fsckloglen * sb_ptr->s_bsize;
			/* length of the on-device fsck service log */
			local_recptr->ondev_fscklog_fsblk_length =
			    sb_ptr->s_fsckloglen;
			/* length of the on-device fsck workspace */
			local_recptr->ondev_wsp_fsblk_length =
			    lengthPXD(&(sb_ptr->s_fsckpxd)) -
			    local_recptr->ondev_fscklog_fsblk_length;
			/* length of the on-device fsck workspace */
			local_recptr->ondev_wsp_byte_length =
			    local_recptr->ondev_wsp_fsblk_length *
			    sb_ptr->s_bsize;
			/* aggregate block offset of the on-device fsck workspace */
			local_recptr->ondev_wsp_fsblk_offset =
			    addressPXD(&(sb_ptr->s_fsckpxd));
			/* byte offset of the on-device fsck workspace */
			local_recptr->ondev_wsp_byte_offset =
			    local_recptr->ondev_wsp_fsblk_offset *
			    sb_ptr->s_bsize;
			/* aggregate block offset of the on-device fsck workspace */
			local_recptr->ondev_fscklog_fsblk_offset =
			    local_recptr->ondev_wsp_fsblk_offset +
			    local_recptr->ondev_wsp_fsblk_length;
			/* byte offset of the on-device fsck workspace */
			local_recptr->ondev_fscklog_byte_offset =
			    local_recptr->ondev_wsp_byte_offset +
			    local_recptr->ondev_wsp_byte_length;
			/*
			 * The offsets now assume the most recent log is 1st in the
			 * aggregate fsck service log space.  Adjust if needed.
			 */
			if (local_recptr->which_log == NEWLOG) {
				/* most recent wanted */
				if (sb_ptr->s_fscklog == 2) {
					/* the 2nd is most recent */
					local_recptr->
					    ondev_fscklog_fsblk_offset +=
					    local_recptr->
					    ondev_fscklog_fsblk_length / 2;
					local_recptr->
					    ondev_fscklog_byte_offset +=
					    local_recptr->
					    ondev_fscklog_byte_length / 2;
				}
			} else {
				/* previous log wanted */
				if (sb_ptr->s_fscklog != 2) {
					/* the 2nd is not most recent */
					local_recptr->
					    ondev_fscklog_fsblk_offset +=
					    local_recptr->
					    ondev_fscklog_fsblk_length / 2;
					local_recptr->
					    ondev_fscklog_byte_offset +=
					    local_recptr->
					    ondev_fscklog_byte_length / 2;
				}
			}
			local_recptr->infile_agg_offset =
			    local_recptr->ondev_fscklog_byte_offset;

			pi_rc = open_outfile();
		}
	}

	if (local_recptr->which_log == NEWLOG) {
		send_msg(fsck_CHKLOGNEW);
	} else {
		send_msg(fsck_CHKLOGOLD);
	}

	return (pi_rc);
}

/*****************************************************************************
 * NAME: open_device_read
 *
 * FUNCTION:  Open the specified device for read access.
 *
 * PARAMETERS:
 *      Device  - input - the device specification
 *
 * NOTES:
 *
 * RETURNS:
 *      success: 0
 *      failure: something else
 */
int open_device_read(const char *Device)
{
	Dev_IOPort = fopen(Device, "r");
	if (Dev_IOPort == NULL) {
		send_msg(fsck_DEVOPENRDRC, ERROR_FILE_NOT_FOUND);
		return ERROR_FILE_NOT_FOUND;
	}

	Dev_blksize = Dev_SectorSize = PBSIZE;
	return 0;
}

/*****************************************************************************
 * NAME: open_outfile
 *
 * FUNCTION:  Open the output file.
 *
 * PARAMETERS:
 *      Device  - input - the device specification
 *
 * RETURNS:
 *      success: 0
 *      failure: something else
 */
int open_outfile()
{
	int openof_rc = 0;

	if (!local_recptr->file_name_specified) {
		/* no output file name given */
		if (local_recptr->which_log == NEWLOG) {
			/* most recent wanted */
			memcpy((void *) file_name, (void *) default_logfile_new,
			       default_logfile_name_len);
		} else {
			/* prior log wanted */
			memcpy((void *) file_name, (void *) default_logfile_old,
			       default_logfile_name_len);
		}
	}

	outfp = fopen(file_name, "w");
	if (outfp == NULL) {
		/* output file open failed */
		openof_rc = XCHKLOG_CANTOPENOUTFILE;
		send_msg(fsck_XCHKLOGOPNFAIL, file_name);
	} else {
		/* output file is open */
		local_recptr->outfile_is_open = -1;

		/*
		 * write the eyecatcher into the output buffer
		 */
		memcpy((void *) (local_recptr->outfile_buf_ptr),
		       (void *) jfs_chklog_eyecatcher, 16);
		local_recptr->outfile_buf_data_len = 16;

		/*
		 * announce the output file name
		 */
		if (local_recptr->which_log == NEWLOG) {
			/* most recent */
			send_msg(fsck_XCHKLOGNEW, file_name);
		} else {
			/* previous */
			send_msg(fsck_XCHKLOGOLD, file_name);
		}
	}
	return (openof_rc);
}

/*****************************************************************************
 * NAME: readwrite_device
 *
 * FUNCTION:  Read data from or write data to the device on which the
 *            aggregate resides.
 *
 * PARAMETERS:
 *      dev_offset           - input - the offset, in bytes, into the aggregate
 *                                     of the data to read or to which to write
 *                                     the data.
 *      requested_data_size  - input - the number of bytes requested
 *      actual_data_size     - input - pointer to a variable in which to return
 *                                     the number of bytes actually read or
 *                                     written
 *      data_buffer          - input - the address of the buffer in which to
 *                                     put the data or from which to write
 *                                     the data
 *      mode                 - input - { fsck_READ | fsck_WRITE }
 *
 * NOTES:  This routine also exists in fsck code.  When there is time,
 *	   examine both copies for differences and put the combined
 *         copy in libfs to eliminate extra code.
 *
 * RETURNS:
 *      success: 0
 *      failure: something else
 */
int readwrite_device(int64_t dev_offset,
		     unsigned requested_data_size,
		     unsigned *actual_data_size, void *data_buffer, int mode)
{
	int rwdb_rc = 0;

	if ((dev_offset % Dev_SectorSize)
	    || (requested_data_size % Dev_SectorSize)) {
		rwdb_rc = FSCK_FAILED_SEEK;
	} else {
		switch (mode) {
		case fsck_READ:
			rwdb_rc =
			    ujfs_rw_diskblocks(Dev_IOPort, dev_offset,
					       requested_data_size, data_buffer,
					       GET);
			break;
		case fsck_WRITE:
			rwdb_rc =
			    ujfs_rw_diskblocks(Dev_IOPort, dev_offset,
					       requested_data_size, data_buffer,
					       PUT);
			break;
		default:
			rwdb_rc = FSCK_INTERNAL_ERROR_3;
			break;
		}
	}

	if (rwdb_rc == 0)
		*actual_data_size = requested_data_size;
	else
		*actual_data_size = 0;

	return (rwdb_rc);
}

/*****************************************************************************
 * NAME: record_msg
 *
 * FUNCTION: Record an fsck service log message in the output file,
 *           formatted for the common fsck service log display tool.
 *
 * PARAMETERS:
 *      ?                 - input -
 *      ?                 - returned -
 *
 * NOTES:  Any message designated fsck_debug is english-only, and
 *         is issued in english, even if it is sent to stdout.
 *
 *         All other messages are local-language (in the nls context)
 *         and, when sent to stdout or stderr, are issued in the
 *         local language.
 *
 *         Regardless of the language used to send a message to
 *         stdout and stderr, messages are ALWAYS LOGGED IN ENGLISH.
 *
 * RETURNS:
 *	nothing
 */
void record_msg(char *msg_txt)
{
	char *msg;
	char log_entry[4096];
	int entry_length = sizeof (struct chklog_entry_hdr);
	struct chklog_entry_hdr *hdptr;
	int buffer_bytes_left;

	hdptr = (struct chklog_entry_hdr *) log_entry;
	msg = &log_entry[entry_length];
	strcpy(msg, msg_txt);
	entry_length += strlen(msg_txt);
	/* add null terminator to string */
	log_entry[entry_length++] = '\0';

	/*
	 * round the length up so the next entry will
	 * start on a doubleword (4 byte) boundary
	 */
	entry_length = ((entry_length + 3) / 4) * 4;

	hdptr->entry_length = entry_length;

	buffer_bytes_left =
	    local_recptr->outfile_buf_length -
	    local_recptr->outfile_buf_data_len;
	if (buffer_bytes_left < entry_length) {
		/* won't fit */
		local_recptr->last_msghdr->entry_length += buffer_bytes_left;
		fwrite((const void *) (local_recptr->outfile_buf_ptr),
		       sizeof (char), local_recptr->outfile_buf_length, outfp);
		/* clear the buffer */
		memset((void *) (local_recptr->outfile_buf_ptr), 0,
		       local_recptr->outfile_buf_length);
		local_recptr->outfile_buf_data_len = 0;
	}
	local_recptr->last_msghdr = (struct fscklog_entry_hdr *)
	    (local_recptr->outfile_buf_ptr +
	     local_recptr->outfile_buf_data_len);
	memcpy((void *) local_recptr->last_msghdr, (void *) hdptr,
	       entry_length);

	local_recptr->outfile_buf_data_len += entry_length;

	return;
}

/*****************************************************************************
 * NAME: validate_super
 *
 * FUNCTION:  This routine validates the JFS superblock currently in the
 *            buffer.  If any problem is detected, the which_superblock
 *            input parm is used to tailor the message issued to notify
 *            the user.
 *
 * PARAMETERS:
 *      which_super  - input - specifies the superblock on which is in the
 *                             buffer { fsck_primary | fsck_secondary }
 *
 * NOTES:  This routine also exists in fsck code.  When there is time,
 *	   examine both copies for differences and put the combined
 *         copy in libfs to eliminate extra code.
 *
 * RETURNS:
 *      success: 0
 *      failure: something else
 */
int validate_super(int which_super)
{
	int vs_rc = 0;		/* assume the superblock is ok */
	int64_t bytes_on_device;
	int64_t agg_blks_in_aggreg = 0, agg_blks_on_device =
	    0, dev_blks_on_device;
	int64_t fsck_start_from_pxd, fsck_blkmap_start_blks;
	uint32_t fsck_length_from_pxd, fsck_blkmap_size_blks,
	    fsck_blkmap_size_pages;
	int64_t jlog_start_from_pxd;
	uint32_t jlog_length_from_pxd;
	int agl2size;
	unsigned expected_flag = JFS_GROUPCOMMIT;
	unsigned agsize;
	int bad_bsize = 0;

	if (memcmp(sb_ptr->s_magic, JFS_MAGIC, sizeof (sb_ptr->s_magic)) != 0) {
		vs_rc = FSCK_BADSBMGC;
		send_msg(fsck_BADSBMGC, fsck_ref_msg(which_super));
	} else if (sb_ptr->s_version > JFS_VERSION) {
		vs_rc = FSCK_BADSBVRSN;
		send_msg(fsck_BADSBVRSN, fsck_ref_msg(which_super));
	} else {
		/* the magic number and version number are correct so it
		 * probably is a JFS superblock with the format we are expecting
		 */

		/* get physical device size */
		ujfs_get_dev_size(Dev_IOPort, &bytes_on_device);

		dev_blks_on_device = bytes_on_device / Dev_blksize;

		if (sb_ptr->s_pbsize != Dev_blksize) {
			vs_rc = FSCK_BADSBOTHR1;
			send_msg(fsck_BADSBOTHR, "1", fsck_ref_msg(which_super));
		}
		if (sb_ptr->s_l2pbsize != log2shift(Dev_blksize)) {
			vs_rc = FSCK_BADSBOTHR2;
			send_msg(fsck_BADSBOTHR, "2", fsck_ref_msg(which_super));
		}
		if (!inrange(sb_ptr->s_bsize, 512, 4096)) {
			bad_bsize = -1;
			vs_rc = FSCK_BADSBOTHR3;
			send_msg(fsck_BADSBBLSIZ, fsck_ref_msg(which_super));
		} else {
			/* else the filesystem block size is a legal value */
			if (sb_ptr->s_l2bsize != log2shift(sb_ptr->s_bsize)) {
				vs_rc = FSCK_BADSBOTHR4;
				send_msg(fsck_BADSBOTHR, "4",
					 fsck_ref_msg(which_super));
			}
			if (sb_ptr->s_l2bfactor !=
			    log2shift(sb_ptr->s_bsize / Dev_blksize)) {
				vs_rc = FSCK_BADSBOTHR5;
				send_msg(fsck_BADSBOTHR, "5",
					 fsck_ref_msg(which_super));
			}
			if (sb_ptr->s_bsize < Dev_blksize) {
				bad_bsize = -1;
				vs_rc = FSCK_BLSIZLTLVBLSIZ;
				send_msg(fsck_BLSIZLTLVBLSIZ,
					 fsck_ref_msg(which_super));
			}
		}

		if (!bad_bsize) {
			agg_blks_on_device = bytes_on_device / sb_ptr->s_bsize;
		}

		if (sb_ptr->s_size > dev_blks_on_device) {
			vs_rc = FSCK_BADSBFSSIZ;
			send_msg(fsck_BADSBFSSIZ, fsck_ref_msg(which_super));
		}
#ifdef	_JFS_DFS_LFS
		s_size_inbytes = sb_ptr->s_size * Dev_blksize;
		sum_inbytes = (int64_t) (sb_ptr->totalUsable * 1024) +
		    (int64_t) (sb_ptr->minFree * 1024);
		if ((sum_inbytes > s_size_inbytes)
		    || ((s_size_inbytes - sum_inbytes) >= 1024)) {
			/* the sum is greater or the difference is at least 1K */
			vs_rc = FSCK_BADBLKCTTTL;
			send_msg(fsck_BADBLKCTTTL, fsck_ref_msg(which_super));
		}
#endif
		/* must have JFS_OS2 or JFS_LINUX */
		if (!(((sb_ptr->s_flag & JFS_OS2) == JFS_OS2)
		      || ((sb_ptr->s_flag & JFS_LINUX) == JFS_LINUX))) {
			vs_rc = FSCK_BADSBOTHR6;
			send_msg(fsck_BADSBOTHR, "6", fsck_ref_msg(which_super));
		}

		if ((sb_ptr->s_flag & expected_flag) != expected_flag) {
			vs_rc = FSCK_BADSBOTHR6;
			send_msg(fsck_BADSBOTHR, "6", fsck_ref_msg(which_super));
		}
		if (sb_ptr->s_agsize < (1 << L2BPERDMAP)) {
			vs_rc = FSCK_BADSBAGSIZ;
			send_msg(fsck_BADSBAGSIZ, fsck_ref_msg(which_super));
		} else {
			/* else the alloc group size is possibly correct */
			agg_blks_in_aggreg =
			    sb_ptr->s_size * sb_ptr->s_pbsize / sb_ptr->s_bsize;
			agl2size =
			    ujfs_getagl2size(agg_blks_in_aggreg,
					     sb_ptr->s_bsize);
			/* get the allocation group size */
			agsize = (int64_t) 1 << agl2size;
			if (sb_ptr->s_agsize != agsize) {
				vs_rc = FSCK_BADAGFSSIZ;
				send_msg(fsck_BADSBAGSIZ, fsck_ref_msg(which_super));
			}
		}

		if (!vs_rc) {
			/*
			 * check out the fsck in-aggregate workspace
			 */
			fsck_length_from_pxd = lengthPXD(&(sb_ptr->s_fsckpxd));
			fsck_start_from_pxd = addressPXD(&(sb_ptr->s_fsckpxd));
			agg_blks_in_aggreg = fsck_length_from_pxd +
			    (sb_ptr->s_size * sb_ptr->s_pbsize /
			     sb_ptr->s_bsize);

			if (agg_blks_in_aggreg > agg_blks_on_device) {
				/* wsp length is bad */
				vs_rc = FSCK_BADSBFWSL1;
				send_msg(fsck_BADSBFWSL1, fsck_ref_msg(which_super));
			} else {
				fsck_blkmap_size_pages =
				    ((agg_blks_in_aggreg +
				      (BITSPERPAGE - 1)) / BITSPERPAGE) + 1 +
				    50;
				/* size in aggregate blocks */
				fsck_blkmap_size_blks =
				    (fsck_blkmap_size_pages << L2PSIZE) /
				    sb_ptr->s_bsize;
				/*
				 * aggregate block offset of the
				 * fsck workspace in the aggregate.
				 */
				fsck_blkmap_start_blks =
				    agg_blks_in_aggreg - fsck_blkmap_size_blks;
				if (fsck_length_from_pxd !=
				    fsck_blkmap_size_blks) {
					/*
					 * length of fsck in-aggregate
					 * workspace is incorrect
					 */
					vs_rc = FSCK_BADSBFWSL;
					send_msg(fsck_BADSBFWSL,
						 fsck_ref_msg(which_super));
				}
				if (fsck_start_from_pxd !=
				    fsck_blkmap_start_blks) {
					/*
					 * address of fsck in-aggregate
					 * workspace is incorrect
					 */
					vs_rc = FSCK_BADSBFWSA;
					send_msg(fsck_BADSBFWSA,
						 fsck_ref_msg(which_super));
				}
			}
		}
		if (!vs_rc) {
			/*
			 * check out the in-aggregate journal log
			 *
			 * if there is one it starts at the end of the fsck
			 * in-aggregate workspace.
			 */
			jlog_length_from_pxd = lengthPXD(&(sb_ptr->s_logpxd));
			jlog_start_from_pxd = addressPXD(&(sb_ptr->s_logpxd));

			if (jlog_start_from_pxd != 0) {
				/* there's one in there */
				if (jlog_start_from_pxd != agg_blks_in_aggreg) {
					/*
					 * address of in-aggregate
					 * journal log is incorrect
					 */
					vs_rc = FSCK_BADSBFJLA;
					send_msg(fsck_BADSBFJLA,
						 fsck_ref_msg(which_super));
				}
				agg_blks_in_aggreg += jlog_length_from_pxd;
				if (agg_blks_in_aggreg > agg_blks_on_device) {
					/* log length is bad */
					vs_rc = FSCK_BADSBFJLL;
					send_msg(fsck_BADSBFJLL,
						 fsck_ref_msg(which_super));
				}
			}
		}
	}
	return (vs_rc);
}

/*****************************************************************************
 * NAME: validate_superblock
 *
 * FUNCTION:  Verify that the primary superblock is valid.
 *            If not, verify that the secondary superblock is valid.
 *
 * PARAMETERS:  none
 *
 * NOTES:  If this routine returns 0 then the superblock
 *         I/O buffer contains a valid superblock.
 *
 *	   This routine also exists in fsck code.  When there is time,
 *	   examine both copies for differences and put the combined
 *         copy in libfs to eliminate extra code.
 *
 * RETURNS:
 *      success: 0
 *      failure: something else
 */
int validate_superblock()
{
	int vsb_rc = 0;
	int primary_sb_bad = 1;
	int secondary_sb_bad = 1;
	int which_sb = 0;

	/* get primary  */
	vsb_rc = ujfs_get_superblk(Dev_IOPort, sb_ptr, 1);

	if (vsb_rc != 0) {
		/* if read primary fails */
		send_msg(fsck_CNTRESUPP);
	} else {
		/* got primary superblock */
		which_sb = fsck_primary;
		primary_sb_bad = validate_super(fsck_primary);
	}

	if (primary_sb_bad) {
		/* can't use the primary superblock */
		send_msg(fsck_SBBADP);

		/* get 2ndary */
		vsb_rc = ujfs_get_superblk(Dev_IOPort, sb_ptr, 0);

		if (vsb_rc != 0) {
			send_msg(fsck_CNTRESUPS);
		} else {
			/* got secondary superblock */
			which_sb = fsck_secondary;
			secondary_sb_bad = validate_super(fsck_secondary);
		}

		if (!secondary_sb_bad) {
			/* secondary is ok */
			vsb_rc = 0;
		} else {
			send_msg(fsck_SBBADS);
		}
	}
	if ((!primary_sb_bad) || (!secondary_sb_bad)) {
		/* the buffer holds a valid superblock */

		/* aggregate block size */
		local_recptr->ag_blk_size = sb_ptr->s_bsize;

		send_msg(fsck_XCHKLOGSBOK, fsck_ref_msg(which_sb));
	}

	return (vsb_rc);
}
