638 lines
16 KiB
C
638 lines
16 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU Lesser General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with these libraries and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
//%% (c) Copyright 1993, 1994 Hewlett-Packard Company
|
|
//%% (c) Copyright 1993, 1994 International Business Machines Corp.
|
|
//%% (c) Copyright 1993, 1994 Sun Microsystems, Inc.
|
|
//%% (c) Copyright 1993, 1994 Novell, Inc.
|
|
//%% $XConsortium: ttdbck.C /main/3 1995/10/20 16:34:00 rswiston $
|
|
/*
|
|
*
|
|
* ttdbck.cc
|
|
* @(#)ttdbck.C 1.31 93/09/07
|
|
*
|
|
* ToolTalk 1.0 spec data base inspect and repair tool
|
|
*
|
|
* Copyright (c) 1990 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <isam.h>
|
|
#include <locale.h>
|
|
#include <memory.h>
|
|
#include "util/tt_port.h"
|
|
#include "util/tt_gettext.h"
|
|
#include "util/tt_global_env.h"
|
|
#include "ttdbck.h"
|
|
#include "dbck.h"
|
|
#include "options_tt.h"
|
|
#include "binkey.h"
|
|
#include "spec.h"
|
|
#include "util/copyright.h"
|
|
#include "dm/dm_recfmts.h"
|
|
#include "mp/mp_mp.h"
|
|
#include "mp_s_mp.h"
|
|
#include "tt_db_server_consts.h"
|
|
#include "tt_isstrerror.h"
|
|
|
|
TT_INSERT_COPYRIGHT
|
|
|
|
#ifdef OPT_PATCH
|
|
static char PatchID[] = "Patch Id: 100626_03.";
|
|
static int Patch_ID100626_03;
|
|
#endif
|
|
|
|
char *progname; // from argv[0]
|
|
Dbck_specoptions *opts;
|
|
_Tt_typedb_ptr *tdb_ptr;
|
|
FILE *tstream; // for calling print() methods in dbx
|
|
|
|
// Global variables controlling state of main merge loop.
|
|
|
|
Table_oid_prop oid_prop_record;
|
|
Table_oid_access oid_access_record;
|
|
Table_oid_container oid_container_record;
|
|
Table_docoid_path docoid_path_record;
|
|
|
|
struct keydesc docoid_path_keydesc;
|
|
|
|
int oid_prop_fd, oid_access_fd, oid_container_fd, docoid_path_fd;
|
|
|
|
Binkey this_min_key;
|
|
Binkey oid_prop_key;
|
|
Binkey oid_access_key;
|
|
Binkey oid_container_key;
|
|
Binkey last_min_key;
|
|
|
|
int oid_access_inspected;
|
|
int oid_container_inspected;
|
|
|
|
_Tt_string oid_prop_value;
|
|
_Tt_string oid_prop_name;
|
|
|
|
_Tt_string oid_prop_rootname;
|
|
_Tt_string oid_access_rootname;
|
|
_Tt_string oid_container_rootname;
|
|
_Tt_string docoid_path_rootname;
|
|
|
|
Spec_list_ptr specs_to_repair;
|
|
|
|
|
|
// TOC
|
|
|
|
int main(int argc, char **argv);
|
|
int process_directory(_Tt_string dirname);
|
|
void process_spec(Spec_ptr s);
|
|
void advance_oid_prop();
|
|
void advance_oid_container();
|
|
void advance_oid_access();
|
|
void closeall();
|
|
Binkey compute_min_key(Binkey a, Binkey b, Binkey c);
|
|
void inspect_docoid_path(Spec_ptr p);
|
|
void pisamerr(const char *func, const char *name);
|
|
void check_if_file(Spec_ptr p);
|
|
|
|
// isam.h does not include function headers at all!!
|
|
|
|
extern "C" {
|
|
int isaddindex(int, struct keydesc*);
|
|
int isbuild(char*, int, struct keydesc*, int);
|
|
int isclose(int);
|
|
int iscntl(int, int, char*);
|
|
int isdelrec(int, long);
|
|
int iserase(char*);
|
|
int isopen(char*, int);
|
|
int isread(int, char*, int);
|
|
int isrepair(const char *, int);
|
|
int isrewrec(int, int, char*);
|
|
int isstart(int, struct keydesc*, int, char*, int);
|
|
int iswrite(int, char*);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int status;
|
|
|
|
tstream = stderr;
|
|
opts = new Dbck_specoptions;
|
|
progname = argv[0];
|
|
setlocale( LC_ALL, "" );
|
|
|
|
if (!opts->set_opts(argc, argv))
|
|
{
|
|
fprintf(stderr, "%s", catgets(_ttcatd, 6, 2,
|
|
"Usage:\n"
|
|
"ttdbck [-f file] [-k objkey] [-t type] [-bx] \n"
|
|
"[-impa] [-IZ] [-F newfilename] [-T newtype] [mountpoints]\n"));
|
|
exit (1);
|
|
}
|
|
|
|
if (DBCK_DEBUG(1)) {
|
|
opts->print(stderr);
|
|
}
|
|
|
|
if (opts->repairing_p() &&
|
|
!opts->selecting_p() &&
|
|
!opts->diagnosing_p()) {
|
|
fprintf(stderr, "%s",
|
|
catgets(_ttcatd, 6, 3,
|
|
"ttdbck: you must specify a selection "
|
|
"[-fkt] option or a diagnosis [-bx] option\n"
|
|
"if a repair [-FTZ] option is specified\n"));
|
|
exit (1);
|
|
}
|
|
|
|
if (opts->diag_badform_p()) {
|
|
// We have to initialize just enough of the ttsession
|
|
// server state to make the _Tt_typedb class work.
|
|
// XXX: really the _Tt_typedb class should be independent
|
|
// of the server.
|
|
_tt_global = new _Tt_global;
|
|
_tt_s_mp = new _Tt_s_mp;
|
|
_tt_mp = (_Tt_mp *)new _Tt_s_mp;
|
|
tdb_ptr = new _Tt_typedb_ptr;
|
|
(*tdb_ptr) = new _Tt_typedb;
|
|
|
|
Tt_status err;
|
|
// Load the xdr types database
|
|
err = (*tdb_ptr)->init_xdr();
|
|
if (err == TT_ERR_NO_MATCH) {
|
|
fprintf(stderr, "ttdbck: %s\n",
|
|
catgets(_ttcatd, 6, 4,
|
|
"Version mismatch in compiled types"));
|
|
exit (1);
|
|
} else if (err != TT_OK) {
|
|
fprintf(stderr, "ttdbck: %s\n",
|
|
catgets(_ttcatd, 6, 5,
|
|
"Cannot read types in database"));
|
|
exit (1);
|
|
}
|
|
}
|
|
|
|
status = do_directories(opts->dbdirectories(),process_directory);
|
|
|
|
// Normally UNIX programs don't make noise if nothing is wrong, but
|
|
// in the case of ttdbck it's unlikely the admin running it is
|
|
// familiar with it, and it's only being run if the database is
|
|
// suspected of damage already. So we print a reassuring message
|
|
// if there are no errors. If there are errors, there has
|
|
// already been output.
|
|
|
|
if (status==0) {
|
|
fprintf(stderr, "%s",
|
|
catgets(_ttcatd, 6, 25,
|
|
"ttdbck: no errors found.\n"));
|
|
}
|
|
|
|
exit(status);
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Main guts of ttdbck. Called once for each directory named on the
|
|
* command line. Returns 1 if the directory was processed without
|
|
* detecting any errors, else 0.
|
|
*/
|
|
int
|
|
process_directory(_Tt_string dirname)
|
|
{
|
|
struct keydesc oid_prop_keydesc;
|
|
struct keydesc oid_access_keydesc;
|
|
struct keydesc oid_container_keydesc;
|
|
|
|
Spec_ptr this_spec;
|
|
|
|
// Initialize for main loop.
|
|
|
|
specs_to_repair = new Spec_list;
|
|
|
|
oid_prop_rootname = dirname.cat(TT_DB_PROPERTY_TABLE_FILE);
|
|
oid_prop_keydesc.k_flags = ISDUPS;
|
|
oid_prop_keydesc.k_nparts = 2;
|
|
oid_prop_keydesc.k_part[0].kp_start =
|
|
offsetof(Table_oid_prop,objkey);
|
|
oid_prop_keydesc.k_part[0].kp_leng =
|
|
sizeof(oid_prop_record.objkey);
|
|
oid_prop_keydesc.k_part[0].kp_type = BINTYPE;
|
|
oid_prop_keydesc.k_part[1].kp_start =
|
|
offsetof(Table_oid_prop,propname);
|
|
oid_prop_keydesc.k_part[1].kp_leng =
|
|
sizeof(oid_prop_record.propname);
|
|
oid_prop_keydesc.k_part[1].kp_type = CHARTYPE;
|
|
|
|
oid_access_rootname = dirname.cat(TT_DB_ACCESS_TABLE_FILE);
|
|
oid_access_keydesc.k_flags = ISNODUPS;
|
|
oid_access_keydesc.k_nparts = 1;
|
|
oid_access_keydesc.k_part[0].kp_start =
|
|
offsetof(Table_oid_access,objkey);
|
|
oid_access_keydesc.k_part[0].kp_leng =
|
|
sizeof(oid_access_record.objkey);
|
|
oid_access_keydesc.k_part[0].kp_type = BINTYPE;
|
|
|
|
oid_container_rootname = dirname.cat(TT_DB_FILE_OBJECT_MAP_FILE);
|
|
oid_container_keydesc.k_flags = ISNODUPS;
|
|
oid_container_keydesc.k_nparts = 1;
|
|
oid_container_keydesc.k_part[0].kp_start =
|
|
offsetof(Table_oid_container,objkey);
|
|
oid_container_keydesc.k_part[0].kp_leng =
|
|
sizeof(oid_container_record.objkey);
|
|
oid_container_keydesc.k_part[0].kp_type = BINTYPE;
|
|
|
|
docoid_path_rootname = dirname.cat(TT_DB_FILE_TABLE_FILE);
|
|
docoid_path_keydesc.k_flags = ISNODUPS;
|
|
docoid_path_keydesc.k_nparts = 1;
|
|
docoid_path_keydesc.k_part[0].kp_start =
|
|
offsetof(Table_docoid_path, dockey);
|
|
docoid_path_keydesc.k_part[0].kp_leng =
|
|
sizeof(docoid_path_record.dockey);
|
|
docoid_path_keydesc.k_part[0].kp_type = BINTYPE;
|
|
|
|
last_min_key = Binkey::smallest;
|
|
|
|
oid_prop_fd = -1;
|
|
oid_access_fd = -1;
|
|
oid_container_fd = -1;
|
|
docoid_path_fd = -1;
|
|
|
|
if (opts->repair_netisam_p()) {
|
|
if (-1==isrepair(oid_prop_rootname, 1)) {
|
|
pisamerr("isrepair", oid_prop_rootname);
|
|
return 0;
|
|
}
|
|
if (-1==isrepair(oid_access_rootname, 1)) {
|
|
pisamerr("isrepair", oid_access_rootname);
|
|
return 0;
|
|
}
|
|
if (-1==isrepair(oid_container_rootname, 1)) {
|
|
pisamerr("isrepair", oid_container_rootname);
|
|
return 0;
|
|
}
|
|
if (-1==isrepair(docoid_path_rootname, 1)) {
|
|
pisamerr("isrepair", docoid_path_rootname);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
oid_prop_fd = isopen(oid_prop_rootname, ISVARLEN+ISINPUT+ISMANULOCK);
|
|
if (oid_prop_fd==-1) {
|
|
pisamerr("isopen",oid_prop_rootname);
|
|
closeall();
|
|
return 0;
|
|
}
|
|
oid_access_fd = isopen(oid_access_rootname,
|
|
ISVARLEN+ISINPUT+ISMANULOCK);
|
|
if (oid_access_fd==-1) {
|
|
pisamerr("isopen",oid_access_rootname);
|
|
closeall();
|
|
return 0;
|
|
}
|
|
oid_container_fd = isopen(oid_container_rootname,
|
|
ISVARLEN+ISINPUT+ISMANULOCK);
|
|
if (oid_container_fd==-1) {
|
|
pisamerr("isopen",oid_container_rootname);
|
|
closeall();
|
|
return 0;
|
|
}
|
|
docoid_path_fd = isopen(docoid_path_rootname,
|
|
ISVARLEN+ISINPUT+ISMANULOCK);
|
|
if (docoid_path_fd==-1) {
|
|
pisamerr("isopen",docoid_path_rootname);
|
|
closeall();
|
|
return 0;
|
|
}
|
|
if (-1==isstart(oid_prop_fd, &oid_prop_keydesc,
|
|
0,
|
|
(char *)&oid_prop_record,
|
|
ISFIRST)) {
|
|
pisamerr("isstart",oid_prop_rootname);
|
|
closeall();
|
|
return 0;
|
|
}
|
|
if (-1==isstart(oid_access_fd, &oid_access_keydesc,
|
|
0,
|
|
(char *)&oid_access_record,
|
|
ISFIRST)) {
|
|
pisamerr("isstart", oid_access_rootname);
|
|
closeall();
|
|
return 0;
|
|
}
|
|
if (-1==isstart(oid_container_fd, &oid_container_keydesc,
|
|
0,
|
|
(char *)&oid_container_record,
|
|
ISFIRST)){
|
|
pisamerr("isstart", oid_container_rootname);
|
|
closeall();
|
|
return 0;
|
|
}
|
|
if (-1==isstart(docoid_path_fd, &docoid_path_keydesc,
|
|
0,
|
|
(char *)&docoid_path_record,
|
|
ISFIRST)){
|
|
pisamerr("isstart", docoid_path_rootname);
|
|
closeall();
|
|
return 0;
|
|
}
|
|
|
|
advance_oid_prop();
|
|
advance_oid_access();
|
|
advance_oid_container();
|
|
this_min_key = compute_min_key(oid_prop_key,
|
|
oid_access_key,
|
|
oid_container_key);
|
|
while (this_min_key<Binkey::largest) {
|
|
if (last_min_key<this_min_key) {
|
|
// At this point, we have read all
|
|
// information about the previous spec.
|
|
// Analyze it, list it if called for,
|
|
// queue it for later repair if called
|
|
// for.
|
|
|
|
process_spec(this_spec);
|
|
|
|
// Complain about any missing required props
|
|
// in previous spec, and reset the required property
|
|
// list.
|
|
|
|
// start accumulating a new spec
|
|
|
|
this_spec = new Spec;
|
|
this_spec->key =
|
|
new Binkey((unsigned char *)(char *)this_min_key);
|
|
check_if_file(this_spec);
|
|
|
|
if (oid_access_key>this_min_key) {
|
|
// mark spec as no access rec
|
|
}
|
|
|
|
if (oid_container_key>this_min_key) {
|
|
// mark spec as no container rec
|
|
}
|
|
}
|
|
|
|
if (!oid_container_inspected &&
|
|
oid_container_key==this_min_key) {
|
|
oid_container_inspected = 1;
|
|
inspect_docoid_path(this_spec);
|
|
}
|
|
|
|
if (oid_prop_key==this_min_key) {
|
|
|
|
// Accumulate property
|
|
|
|
this_spec->add_prop_and_value(oid_prop_name,
|
|
oid_prop_value);
|
|
}
|
|
|
|
// Advance to next records.
|
|
|
|
if (oid_prop_key==this_min_key) {
|
|
advance_oid_prop();
|
|
} else {
|
|
if (oid_container_key==this_min_key) {
|
|
advance_oid_container();
|
|
}
|
|
if (oid_access_key==this_min_key) {
|
|
advance_oid_access();
|
|
}
|
|
}
|
|
last_min_key = this_min_key;
|
|
this_min_key = compute_min_key(oid_prop_key,
|
|
oid_access_key,
|
|
oid_container_key);
|
|
}
|
|
// And remember to process the last spec.
|
|
|
|
process_spec(this_spec);
|
|
|
|
closeall();
|
|
|
|
if (opts->repairing_p()) {
|
|
Spec_list_cursor c(specs_to_repair);
|
|
while(c.next()) {
|
|
c->repair_spec();
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// the advance_*() routines try to read the next record in the table;
|
|
// when eof is hit, they set the record key to the largest possible
|
|
// key. This makes all the merge comparisons work right.
|
|
|
|
void
|
|
advance_oid_prop()
|
|
{
|
|
if (-1==isread(oid_prop_fd, (char *)&oid_prop_record, ISNEXT)) {
|
|
switch (iserrno) {
|
|
case EENDFILE:
|
|
break;
|
|
default:
|
|
pisamerr("isread", oid_prop_rootname);
|
|
break;
|
|
}
|
|
oid_prop_key = Binkey::largest;
|
|
} else {
|
|
int l;
|
|
oid_prop_key = oid_prop_record.objkey;
|
|
l = sizeof(oid_prop_record.propname);
|
|
if (oid_prop_record.propname[l-1]==NULL_CHAR) {
|
|
// strip nulls
|
|
l = strlen(oid_prop_record.propname);
|
|
}
|
|
oid_prop_name.set((unsigned char *)oid_prop_record.propname,l);
|
|
oid_prop_value.set((unsigned char *)oid_prop_record.propval,
|
|
isreclen-offsetof(Table_oid_prop,propval));
|
|
}
|
|
}
|
|
void
|
|
advance_oid_access()
|
|
{
|
|
if (-1==isread(oid_access_fd, (char *)&oid_access_record, ISNEXT)) {
|
|
switch (iserrno) {
|
|
case EENDFILE:
|
|
break;
|
|
default:
|
|
pisamerr("isread", oid_access_rootname);
|
|
break;
|
|
}
|
|
oid_access_key = Binkey::largest;
|
|
} else {
|
|
oid_access_key = oid_access_record.objkey;
|
|
}
|
|
oid_access_inspected = 0;
|
|
}
|
|
void
|
|
advance_oid_container()
|
|
{
|
|
if (-1==isread(oid_container_fd,
|
|
(char *)&oid_container_record, ISNEXT)) {
|
|
switch (iserrno) {
|
|
case EENDFILE:
|
|
break;
|
|
default:
|
|
pisamerr("isread", oid_container_rootname);
|
|
break;
|
|
}
|
|
oid_container_key = Binkey::largest;
|
|
} else {
|
|
oid_container_key = oid_container_record.objkey;
|
|
}
|
|
oid_container_inspected = 0;
|
|
}
|
|
|
|
void
|
|
closeall()
|
|
{
|
|
if (oid_container_fd!=-1) isclose(oid_container_fd);
|
|
if (oid_access_fd!=-1) isclose(oid_access_fd);
|
|
if (oid_prop_fd!=-1) isclose(oid_prop_fd);
|
|
if (docoid_path_fd!=-1) isclose(docoid_path_fd);
|
|
}
|
|
|
|
// compute_min_key finds the least key
|
|
|
|
Binkey
|
|
compute_min_key(Binkey a, Binkey b, Binkey c)
|
|
{
|
|
Binkey result = a;
|
|
if (b<result) result = b;
|
|
if (c<result) result = c;
|
|
return result;
|
|
}
|
|
|
|
// process spec is called when the next key is encountered, or at
|
|
// end of file. The spec data accumulated in spec s is inspected,
|
|
// printed, and/or queued for later repair.
|
|
|
|
void
|
|
process_spec(Spec_ptr s)
|
|
{
|
|
// The first time through the loop there is no accumulated spec
|
|
// so s will be null.
|
|
|
|
if (!s.is_null()) {
|
|
s->process_spec();
|
|
}
|
|
}
|
|
|
|
|
|
// inspect_docoid_path retrieves the docoid_path record for the current
|
|
// spec and fills the file name into this_spec.
|
|
|
|
void
|
|
inspect_docoid_path(Spec_ptr this_spec)
|
|
{
|
|
memset((char *)&docoid_path_record, 0, sizeof(docoid_path_record));
|
|
memcpy(docoid_path_record.dockey, oid_container_record.dockey,
|
|
sizeof(docoid_path_record.dockey));
|
|
|
|
if (-1==isread(docoid_path_fd, (char *)&docoid_path_record, ISEQUAL)) {
|
|
switch (iserrno) {
|
|
case ENOREC:
|
|
// oid doesn't have a file!
|
|
this_spec->filename = "";
|
|
return;
|
|
default:
|
|
pisamerr("isread", oid_container_rootname);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (docoid_path_record.filepath[MAX_KEY_LEN-1] == '\0') {
|
|
// strip padding.
|
|
this_spec->filename = (char *)docoid_path_record.filepath;
|
|
} else {
|
|
this_spec->filename.set((unsigned char *)
|
|
docoid_path_record.filepath,
|
|
isreclen-offsetof(Table_docoid_path,
|
|
filepath));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Print ISAM error message. Some error codes (well, one)
|
|
* are special cased to give more "helpful" messages.
|
|
*/
|
|
|
|
void
|
|
pisamerr(const char *func, const char *name)
|
|
{
|
|
const char *msg = _tt_isstrerror(iserrno);
|
|
if (msg) {
|
|
fprintf(stderr,"ttdbck: %s(\"%s\"): %s\n", func, name,
|
|
msg);
|
|
} else {
|
|
fprintf(stderr,"ttdbck: %s(\"%s\"): %d\n", func, name,
|
|
iserrno);
|
|
}
|
|
switch (iserrno) {
|
|
case EBADFILE:
|
|
fprintf(stderr, "%s",
|
|
catgets(_ttcatd, 6, 6,
|
|
"ttdbck: try 'ttdbck -I'.\n"));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
check_if_file(Spec_ptr this_spec)
|
|
{
|
|
memset((char *)&docoid_path_record, '\0', sizeof(docoid_path_record));
|
|
memcpy((char *)docoid_path_record.dockey,
|
|
(char *)(*this_spec->key), OID_KEY_LENGTH);
|
|
|
|
if (-1==isread(docoid_path_fd, (char *)&docoid_path_record, ISEQUAL)) {
|
|
switch (iserrno) {
|
|
case ENOREC:
|
|
break;
|
|
default:
|
|
pisamerr("isread", oid_container_rootname);
|
|
break;
|
|
}
|
|
this_spec->is_filespec = 0;
|
|
return;
|
|
}
|
|
|
|
// This is the docoid for a file. Set the filename in the spec
|
|
// so the -f option will select the docoid for the file as well.
|
|
|
|
this_spec->is_filespec = 1;
|
|
|
|
if (docoid_path_record.filepath[MAX_KEY_LEN-1] == '\0') {
|
|
// strip padding.
|
|
this_spec->filename = (char *)docoid_path_record.filepath;
|
|
} else {
|
|
this_spec->filename.set((unsigned char *)
|
|
docoid_path_record.filepath,
|
|
isreclen-offsetof(Table_docoid_path,
|
|
filepath));
|
|
}
|
|
}
|