/* * Copyright 2000, International Business Machines Corporation and others. * All Rights Reserved. * * This software has been released under the terms of the IBM Public * License. For details, see the LICENSE file in the top-level source * directory or online at http://www.openafs.org/dl/license10.html */ /* dump the database * Dump is made to a local file. Structures are dumped in network byte order * for transportability between hosts */ #include #include #include #include #include #ifdef AFS_PTHREAD_ENV # include #endif #include #include #include "database.h" #include "budb.h" #include "globals.h" #include "error_macros.h" #include "budb_errs.h" #include "budb_internal.h" /* dump ubik database - routines to scan the database and dump all * the information */ /* ----------------------- * synchronization on pipe * ----------------------- */ /* interlocking for database dump */ dumpSyncT dumpSync; dumpSyncP dumpSyncPtr = &dumpSync; /* canWrite * check if we should dump more of the database. Waits for the reader * to drain the information before allowing the writer to proceed. * exit: * 1 - ok to write */ afs_int32 canWrite(int fid) { #ifndef AFS_PTHREAD_ENV afs_int32 code = 0; #endif extern dumpSyncP dumpSyncPtr; ObtainWriteLock(&dumpSyncPtr->ds_lock); /* let the pipe drain */ while (dumpSyncPtr->ds_bytes > 0) { if (dumpSyncPtr->ds_readerStatus == DS_WAITING) { dumpSyncPtr->ds_readerStatus = 0; #ifdef AFS_PTHREAD_ENV opr_cv_broadcast(&dumpSyncPtr->ds_readerStatus_cond); #else code = LWP_SignalProcess(&dumpSyncPtr->ds_readerStatus); if (code) LogError(code, "canWrite: Signal delivery failed\n"); #endif } dumpSyncPtr->ds_writerStatus = DS_WAITING; ReleaseWriteLock(&dumpSyncPtr->ds_lock); #ifdef AFS_PTHREAD_ENV MUTEX_ENTER(&dumpSyncPtr->ds_writerStatus_mutex); opr_cv_wait(&dumpSyncPtr->ds_writerStatus_cond, &dumpSyncPtr->ds_writerStatus_mutex); MUTEX_EXIT(&dumpSyncPtr->ds_writerStatus_mutex); #else LWP_WaitProcess(&dumpSyncPtr->ds_writerStatus); #endif ObtainWriteLock(&dumpSyncPtr->ds_lock); } return (1); } /* haveWritten * record the fact that nbytes have been written. Signal the reader * to proceed, and unlock. * exit: * no return value */ void haveWritten(afs_int32 nbytes) { #ifndef AFS_PTHREAD_ENV afs_int32 code = 0; #endif extern dumpSyncP dumpSyncPtr; dumpSyncPtr->ds_bytes += nbytes; if (dumpSyncPtr->ds_readerStatus == DS_WAITING) { dumpSyncPtr->ds_readerStatus = 0; #ifdef AFS_PTHREAD_ENV opr_cv_broadcast(&dumpSyncPtr->ds_readerStatus_cond); #else code = LWP_SignalProcess(&dumpSyncPtr->ds_readerStatus); if (code) LogError(code, "haveWritten: Signal delivery failed\n"); #endif } ReleaseWriteLock(&dumpSyncPtr->ds_lock); } /* doneWriting * wait for the reader to drain all the information, and then set the * done flag. */ void doneWriting(afs_int32 error) { #ifndef AFS_PTHREAD_ENV afs_int32 code = 0; #endif /* wait for the reader */ ObtainWriteLock(&dumpSyncPtr->ds_lock); while (dumpSyncPtr->ds_readerStatus != DS_WAITING) { LogDebug(4, "doneWriting: waiting for Reader\n"); dumpSyncPtr->ds_writerStatus = DS_WAITING; ReleaseWriteLock(&dumpSyncPtr->ds_lock); #ifdef AFS_PTHREAD_ENV MUTEX_ENTER(&dumpSyncPtr->ds_writerStatus_mutex); opr_cv_wait(&dumpSyncPtr->ds_writerStatus_cond, &dumpSyncPtr->ds_writerStatus_mutex); MUTEX_EXIT(&dumpSyncPtr->ds_writerStatus_mutex); #else LWP_WaitProcess(&dumpSyncPtr->ds_writerStatus); #endif ObtainWriteLock(&dumpSyncPtr->ds_lock); } LogDebug(4, "doneWriting: setting done\n"); /* signal that we are done */ if (error) dumpSyncPtr->ds_writerStatus = DS_DONE_ERROR; else dumpSyncPtr->ds_writerStatus = DS_DONE; dumpSyncPtr->ds_readerStatus = 0; #ifdef AFS_PTHREAD_ENV opr_cv_broadcast(&dumpSyncPtr->ds_readerStatus_cond); #else code = LWP_NoYieldSignal(&dumpSyncPtr->ds_readerStatus); if (code) LogError(code, "doneWriting: Signal delivery failed\n"); #endif ReleaseWriteLock(&dumpSyncPtr->ds_lock); } /* notes: * ut - setup and pass down */ /* writeStructHeader * write header appropriate for requested structure type */ afs_int32 writeStructHeader(int fid, afs_int32 type) { struct structDumpHeader hostDumpHeader, netDumpHeader; hostDumpHeader.type = type; hostDumpHeader.structversion = 1; switch (type) { case SD_DBHEADER: hostDumpHeader.size = sizeof(struct DbHeader); break; case SD_DUMP: hostDumpHeader.size = sizeof(struct budb_dumpEntry); break; case SD_TAPE: hostDumpHeader.size = sizeof(struct budb_tapeEntry); break; case SD_VOLUME: hostDumpHeader.size = sizeof(struct budb_volumeEntry); break; case SD_END: hostDumpHeader.size = 0; break; default: LogError(0, "writeStructHeader: invalid type %d\n", type); BUDB_EXIT(1); } structDumpHeader_hton(&hostDumpHeader, &netDumpHeader); if (canWrite(fid) <= 0) return (BUDB_DUMPFAILED); if (write(fid, &netDumpHeader, sizeof(netDumpHeader)) != sizeof(netDumpHeader)) return (BUDB_DUMPFAILED); haveWritten(sizeof(netDumpHeader)); return (0); } /* writeTextHeader * write header appropriate for requested structure type */ afs_int32 writeTextHeader(int fid, afs_int32 type) { struct structDumpHeader hostDumpHeader, netDumpHeader; hostDumpHeader.structversion = 1; switch (type) { case TB_DUMPSCHEDULE: hostDumpHeader.type = SD_TEXT_DUMPSCHEDULE; break; case TB_VOLUMESET: hostDumpHeader.type = SD_TEXT_VOLUMESET; break; case TB_TAPEHOSTS: hostDumpHeader.type = SD_TEXT_TAPEHOSTS; break; default: LogError(0, "writeTextHeader: invalid type %d\n", type); BUDB_EXIT(1); } hostDumpHeader.size = ntohl(db.h.textBlock[type].size); structDumpHeader_hton(&hostDumpHeader, &netDumpHeader); if (canWrite(fid) <= 0) return (BUDB_DUMPFAILED); if (write(fid, &netDumpHeader, sizeof(netDumpHeader)) != sizeof(netDumpHeader)) return (BUDB_DUMPFAILED); haveWritten(sizeof(netDumpHeader)); return (0); } afs_int32 writeDbHeader(int fid) { struct DbHeader header; afs_int32 curtime; afs_int32 code = 0, tcode; extern struct memoryDB db; /* check the memory database header for integrity */ if (db.h.version != db.h.checkVersion) ERROR(BUDB_DATABASEINCONSISTENT); curtime = time(0); /* copy selected fields. Source is in xdr format. */ header.dbversion = db.h.version; header.created = htonl(curtime); strcpy(header.cell, ""); header.lastDumpId = db.h.lastDumpId; header.lastInstanceId = db.h.lastInstanceId; header.lastTapeId = db.h.lastTapeId; tcode = writeStructHeader(fid, SD_DBHEADER); if (tcode) ERROR(tcode); if (canWrite(fid) <= 0) ERROR(BUDB_DUMPFAILED); if (write(fid, &header, sizeof(header)) != sizeof(header)) ERROR(BUDB_DUMPFAILED); haveWritten(sizeof(header)); error_exit: return (code); } /* writeDump * write out a dump entry structure */ afs_int32 writeDump(int fid, dbDumpP dumpPtr) { struct budb_dumpEntry dumpEntry; afs_int32 code = 0, tcode; tcode = dumpToBudbDump(dumpPtr, &dumpEntry); if (tcode) ERROR(tcode); writeStructHeader(fid, SD_DUMP); if (canWrite(fid) <= 0) ERROR(BUDB_DUMPFAILED); if (write(fid, &dumpEntry, sizeof(dumpEntry)) != sizeof(dumpEntry)) ERROR(BUDB_DUMPFAILED); haveWritten(sizeof(dumpEntry)); error_exit: return (code); } afs_int32 writeTape(int fid, struct tape *tapePtr, afs_int32 dumpid) { struct budb_tapeEntry tapeEntry; afs_int32 code = 0, tcode; tcode = writeStructHeader(fid, SD_TAPE); if (tcode) ERROR(tcode); tapeToBudbTape(tapePtr, &tapeEntry); tapeEntry.dump = htonl(dumpid); if (canWrite(fid) <= 0) ERROR(BUDB_DUMPFAILED); if (write(fid, &tapeEntry, sizeof(tapeEntry)) != sizeof(tapeEntry)) ERROR(BUDB_DUMPFAILED); haveWritten(sizeof(tapeEntry)); error_exit: return (code); } /* combines volFragment and volInfo */ afs_int32 writeVolume(struct ubik_trans *ut, int fid, struct volFragment *volFragmentPtr, struct volInfo *volInfoPtr, afs_int32 dumpid, char *tapeName) { struct budb_volumeEntry budbVolume; afs_int32 code = 0; volsToBudbVol(volFragmentPtr, volInfoPtr, &budbVolume); budbVolume.dump = htonl(dumpid); strcpy(budbVolume.tape, tapeName); writeStructHeader(fid, SD_VOLUME); if (canWrite(fid) <= 0) ERROR(BUDB_DUMPFAILED); if (write(fid, &budbVolume, sizeof(budbVolume)) != sizeof(budbVolume)) ERROR(BUDB_DUMPFAILED); haveWritten(sizeof(budbVolume)); error_exit: return (code); } /* ------------------- * handlers for the text blocks * ------------------- */ /* checkLock * make sure a text lock is NOT held * exit: * 0 - not held * n - error */ afs_int32 checkLock(afs_int32 textType) { db_lockP lockPtr; if ((textType < 0) || (textType > TB_NUM - 1)) return (BUDB_BADARGUMENT); lockPtr = &db.h.textLocks[textType]; if (lockPtr->lockState != 0) return (BUDB_LOCKED); return (0); } /* checkText * check the integrity of the specified text type */ int checkText(struct ubik_trans *ut, afs_int32 textType) { struct textBlock *tbPtr; afs_int32 nBytes = 0; /* accumulated actual size */ afs_int32 size; struct block block; dbadr blockAddr; afs_int32 code = 0; tbPtr = &db.h.textBlock[textType]; blockAddr = ntohl(tbPtr->textAddr); size = ntohl(tbPtr->size); while (blockAddr != 0) { /* read the block */ code = cdbread(ut, text_BLOCK, blockAddr, (char *)&block, sizeof(block)); if (code) ERROR(code); /* check its type */ if (block.h.type != text_BLOCK) ERROR(BUDB_DATABASEINCONSISTENT); /* add up the size */ nBytes += BLOCK_DATA_SIZE; blockAddr = ntohl(block.h.next); } /* ensure that we have at least the expected amount of text */ if (nBytes < size) ERROR(BUDB_DATABASEINCONSISTENT); error_exit: return (code); } /* writeText * entry: * textType - type of text block, e.g. TB_DUMPSCHEDULE */ afs_int32 writeText(struct ubik_trans *ut, int fid, int textType) { struct textBlock *tbPtr; afs_int32 textSize, writeSize; dbadr dbAddr; struct block block; afs_int32 code = 0; /* check lock is free */ code = checkLock(textType); if (code) ERROR(code); /* ensure that this block has the correct type */ code = checkText(ut, textType); if (code) { LogError(0, "writeText: text type %d damaged\n", textType); ERROR(code); } tbPtr = &db.h.textBlock[textType]; textSize = ntohl(tbPtr->size); dbAddr = ntohl(tbPtr->textAddr); if (!dbAddr) goto error_exit; /* Don't save anything if no blocks */ writeTextHeader(fid, textType); while (dbAddr) { code = cdbread(ut, text_BLOCK, dbAddr, (char *)&block, sizeof(block)); if (code) ERROR(code); writeSize = min(textSize, BLOCK_DATA_SIZE); if (!writeSize) break; if (canWrite(fid) <= 0) ERROR(BUDB_DUMPFAILED); if (write(fid, &block.a[0], writeSize) != writeSize) ERROR(BUDB_IO); haveWritten(writeSize); textSize -= writeSize; dbAddr = ntohl(block.h.next); } error_exit: return (code); } #define MAXAPPENDS 200 afs_int32 writeDatabase(struct ubik_trans *ut, int fid) { dbadr dbAddr, dbAppAddr; struct dump diskDump, apDiskDump; dbadr tapeAddr; struct tape diskTape; dbadr volFragAddr; struct volFragment diskVolFragment; struct volInfo diskVolInfo; int length, hash; int old = 0; int entrySize; afs_int32 code = 0, tcode; afs_int32 appDumpAddrs[MAXAPPENDS], numaddrs, appcount, j; struct memoryHashTable *mht; LogDebug(4, "writeDatabase:\n"); /* write out a header identifying this database etc */ tcode = writeDbHeader(fid); if (tcode) { LogError(tcode, "writeDatabase: Can't write Header\n"); ERROR(tcode); } /* write out the tree of dump structures */ mht = ht_GetType(HT_dumpIden_FUNCTION, &entrySize); if (!mht) { LogError(tcode, "writeDatabase: Can't get dump type\n"); ERROR(BUDB_BADARGUMENT); } for (old = 0; old <= 1; old++) { /*oldnew */ /* only two states, old or not old */ length = (old ? mht->oldLength : mht->length); if (!length) continue; for (hash = 0; hash < length; hash++) { /*hashBuckets */ /* dump all the dumps in this hash bucket */ for (dbAddr = ht_LookupBucket(ut, mht, hash, old); dbAddr; dbAddr = ntohl(diskDump.idHashChain)) { /*initialDumps */ /* now check if this dump had any errors/inconsistencies. * If so, don't dump it */ if (badEntry(dbAddr)) { LogError(0, "writeDatabase: Damaged dump entry at addr 0x%x\n", dbAddr); Log(" Skipping remainder of dumps on hash chain %d\n", hash); break; } tcode = cdbread(ut, dump_BLOCK, dbAddr, &diskDump, sizeof(diskDump)); if (tcode) { LogError(tcode, "writeDatabase: Can't read dump entry (addr 0x%x)\n", dbAddr); Log(" Skipping remainder of dumps on hash chain %d\n", hash); break; } /* Skip appended dumps, only start with initial dumps */ if (diskDump.initialDumpID != 0) continue; /* Skip appended dumps, only start with initial dumps. Then * follow the appended dump chain so they are in order for restore. */ appcount = numaddrs = 0; for (dbAppAddr = dbAddr; dbAppAddr; dbAppAddr = ntohl(apDiskDump.appendedDumpChain)) { /*appendedDumps */ /* Check to see if we have a circular loop of appended dumps */ for (j = 0; j < numaddrs; j++) { if (appDumpAddrs[j] == dbAppAddr) break; /* circular loop */ } if (j < numaddrs) { /* circular loop */ Log("writeDatabase: Circular loop found in appended dumps\n"); Log("Skipping rest of appended dumps of dumpID %u\n", ntohl(diskDump.id)); break; } if (numaddrs >= MAXAPPENDS) numaddrs = MAXAPPENDS - 1; /* don't overflow */ appDumpAddrs[numaddrs] = dbAppAddr; numaddrs++; /* If we dump a 1000 appended dumps, assume a loop */ if (appcount >= 5 * MAXAPPENDS) { Log("writeDatabase: Potential circular loop of appended dumps\n"); Log("Skipping rest of appended dumps of dumpID %u. Dumped %d\n", ntohl(diskDump.id), appcount); break; } appcount++; /* Read the dump entry */ if (dbAddr == dbAppAddr) { /* First time through, don't need to read the dump entry again */ memcpy(&apDiskDump, &diskDump, sizeof(diskDump)); } else { if (badEntry(dbAppAddr)) { LogError(0, "writeDatabase: Damaged appended dump entry at addr 0x%x\n", dbAddr); Log(" Skipping this and remainder of appended dumps of initial DumpID %u\n", ntohl(diskDump.id)); break; } tcode = cdbread(ut, dump_BLOCK, dbAppAddr, &apDiskDump, sizeof(apDiskDump)); if (tcode) { LogError(tcode, "writeDatabase: Can't read appended dump entry (addr 0x%x)\n", dbAppAddr); Log(" Skipping this and remainder of appended dumps of initial DumpID %u\n", ntohl(diskDump.id)); break; } /* Verify that this appended dump points to the initial dump */ if (ntohl(apDiskDump.initialDumpID) != ntohl(diskDump.id)) { LogError(0, "writeDatabase: Appended dumpID %u does not reference initial dumpID %u\n", ntohl(apDiskDump.id), ntohl(diskDump.id)); Log(" Skipping this appended dump\n"); continue; } } /* Save the dump entry */ tcode = writeDump(fid, &apDiskDump); if (tcode) { LogError(tcode, "writeDatabase: Can't write dump entry\n"); ERROR(tcode); } /* For each tape on this dump */ for (tapeAddr = ntohl(apDiskDump.firstTape); tapeAddr; tapeAddr = ntohl(diskTape.nextTape)) { /*tapes */ /* read the tape entry */ tcode = cdbread(ut, tape_BLOCK, tapeAddr, &diskTape, sizeof(diskTape)); if (tcode) { LogError(tcode, "writeDatabase: Can't read tape entry (addr 0x%x) of dumpID %u\n", tapeAddr, ntohl(apDiskDump.id)); Log(" Skipping this and remaining tapes in the dump (and all their volumes)\n"); break; } /* Save the tape entry */ tcode = writeTape(fid, &diskTape, ntohl(apDiskDump.id)); if (tcode) { LogError(tcode, "writeDatabase: Can't write tape entry\n"); ERROR(tcode); } /* For each volume on this tape. */ for (volFragAddr = ntohl(diskTape.firstVol); volFragAddr; volFragAddr = ntohl(diskVolFragment.sameTapeChain)) { /*volumes */ /* Read the volume Fragment entry */ tcode = cdbread(ut, volFragment_BLOCK, volFragAddr, &diskVolFragment, sizeof(diskVolFragment)); if (tcode) { LogError(tcode, "writeDatabase: Can't read volfrag entry (addr 0x%x) of dumpID %u\n", volFragAddr, ntohl(apDiskDump.id)); Log(" Skipping this and remaining volumes on tape '%s'\n", diskTape.name); break; } /* Read the volume Info entry */ tcode = cdbread(ut, volInfo_BLOCK, ntohl(diskVolFragment.vol), &diskVolInfo, sizeof(diskVolInfo)); if (tcode) { LogError(tcode, "writeDatabase: Can't read volinfo entry (addr 0x%x) of dumpID %u\n", ntohl(diskVolFragment.vol), ntohl(apDiskDump.id)); Log(" Skipping volume on tape '%s'\n", diskTape.name); continue; } /* Save the volume entry */ tcode = writeVolume(ut, fid, &diskVolFragment, &diskVolInfo, ntohl(apDiskDump.id), diskTape.name); if (tcode) { LogError(tcode, "writeDatabase: Can't write volume entry\n"); ERROR(tcode); } } /*volumes */ } /*tapes */ } /*appendedDumps */ } /*initialDumps */ } /*hashBuckets */ } /*oldnew */ /* write out the textual configuration information */ tcode = writeText(ut, fid, TB_DUMPSCHEDULE); if (tcode) { LogError(tcode, "writeDatabase: Can't write dump schedule\n"); ERROR(tcode); } tcode = writeText(ut, fid, TB_VOLUMESET); if (tcode) { LogError(tcode, "writeDatabase: Can't write volume set\n"); ERROR(tcode); } tcode = writeText(ut, fid, TB_TAPEHOSTS); if (tcode) { LogError(tcode, "writeDatabase: Can't write tape hosts\n"); ERROR(tcode); } tcode = writeStructHeader(fid, SD_END); if (tcode) { LogError(tcode, "writeDatabase: Can't write end savedb\n"); ERROR(tcode); } error_exit: doneWriting(code); return (code); } #ifdef notdef afs_int32 canWrite(int fid) { afs_int32 in, out, except; struct timeval tp; afs_int32 code; tp.tv_sec = 0; tp.tv_usec = 0; out = (1 << fid); in = 0; except = 0; code = IOMGR_Select(32, &in, &out, &except, &tp); return (code); } #endif /* notdef */