/* Filename: restore.c * Author: Mark R. Rinfret * Date: 08/02/87 * Description: Restore processing module for MRBackup * * History: (most recent change first) * * 12/18/87 -MRR- Restore was using function IsCompressed() to determine * candidates for decompression. Unfortunately, it also * tried to decompress .ARC and .ZOO files. * * Restore now requests the next disk to be restored from. * * 11/22/87 -MRR- Modifications for version 2.0. * * 09/19/87 -MRR- Added NewHomeDir() which creates subdirectories as * necessary when the initial backup path specifies a * subdirectory. */ #include "MRBackup.h" #include ":src/lib/DiskMisc.h" char fullBackPath[PATH_MAX+1], fullHomePath[PATH_MAX+1]; static BOOL bigFileSeqNbr; /* >0 => big file sequence number */ BOOL homeIsDevice; /* true => home is "DH:" */ ^L /* Create a new directory on the home device. * Called with: * name: directory pathname * Returns: * false => success * true => failure */ int NewHomeDir(name) char *name; { char c; struct Lock *dirLock; int dirLeng; int errnum; char dirname[256]; int nameindx = 0, nameleng; *dirname = '\0'; dirLeng = 0; nameleng = strlen(name); /* Parse the pathname, one directory node at a time, creating * directories as needed. */ while (nameindx < nameleng) { if (nameindx) /* 2nd - nth pass? */ dirname[dirLeng++] = '/'; /* directory separator */ while ((c = name[nameindx++]) && c != '/') dirname[dirLeng++] = c; dirname[dirLeng] = '\0'; /* terminate with null */ if (dirLock = (struct Lock *) Lock(dirname,SHARED_LOCK)) /* subdir exists? */ UnLock(dirLock); else { /* create subdirectory */ if ((dirLock = (struct Lock *) CreateDir(dirname))== NULL){ if ((errnum = IoErr())== ERROR_DIRECTORY_NOT_EMPTY){ sprintf(conmsg, "Directory %s already exists!\n",dirname); TypeAndSpeak(conmsg); } else { sprintf(conmsg, "ERROR %d: Unable to create directory %s\n", errnum,dirname); TypeAndSpeak(conmsg); return errnum; } } else UnLock(dirLock); } } /* endwhile */ return 0; } /* Restore files from floppy disk. */ int Restore() { int disks=0, status = 0; char volumeName[31]; Speak("And away we go!"); BreakPath(backPath, srcVol, srcPath); strcat(srcVol, ":"); homeIsDevice = (homePath[strlen(homePath)-1] == ':'); BreakPath(homePath, destVol, destPath); strcat(destVol, ":"); while ( !status ) { TypeAndSpeak("I am ready to read the next disk.\n"); if (!RequestDisk(mainWindow, srcVol, "Insert the next disk to be restored in ")) break; if (!IsDir(backPath)) { TypeAndSpeak( "Backup path must be a device or directory name!\n"); return ERR_ABORT; } GetVolumeName(srcVol, volumeName); SetCurVolumeGadget(volumeName); if ( (totalSize = TotalDiskBlocks(destVol)) < 0) status = -totalSize; else if ((bigFileSeqNbr = GetBigFileInfo(srcVol)) < 0) status = -bigFileSeqNbr; else { status = RestoreFile(srcPath); } if (bigFileSeqNbr) { /* we have a big file to restore? */ status = RestoreBigFile(); } /*!!! Need GetErrOpt right here... */ ++disks; } if (status == 0) { if (disks) TypeAndSpeak("Your restoration project is completed.\n"); else TypeAndSpeak("Maybe you will let me do it next time.\n"); } else { sprintf(conmsg,"Restore terminated with status %d.\n",status); TypeAndSpeak(conmsg); TypeAndSpeak( "Perhaps you should check things out and try it again.\n"); } SetCurVolumeGadget(""); return status; } ^L /* Restore all the files in a directory. * Called with: * lock: lock on the directory * fib: pointer to file info block * path: directory pathname (without volume) * Returns: * status (0 => success) */ int RestoreDir(lock, fib, path) struct Lock *lock; struct FileInfoBlock *fib; char *path; { struct Lock *dirLock = NULL, *filelock = NULL; char newpath[256]; int status = 0; strcpy(temp, homePath); if (*path) { if (!homeIsDevice) strcat(temp, "/"); strcat(temp, path); } #ifdef DEBUG sprintf(debugMsg,"Checking for directory %s\n",temp); DebugWrite(debugMsg); #endif if (!(dirLock = (struct Lock *) Lock(temp, SHARED_LOCK))) { if ((status = IoErr()) == ERROR_OBJECT_NOT_FOUND) { #ifdef DEBUG sprintf(debugMsg,"Creating directory %s\n",temp); DebugWrite(debugMsg); #endif if (status = NewHomeDir(temp)) return status; } else { sprintf(conmsg,"RestoreDir cannot lock %s: %d\n",temp, status); TypeAndSpeak(conmsg); return status; } } if (dirLock) UnLock(dirLock); while (ExNext(lock,fib)) { strcpy(newpath, path); if (*newpath) strcat(newpath, "/"); strcat(newpath, fib->fib_FileName); if (status = RestoreFile(newpath)) { /* filter out "permissable:" errors */ if (status == ERROR_OBJECT_IN_USE || status == ERROR_WRITE_PROTECTED) status = 0; else break; } } done: return status; } ^L /* Restore one or more files according to the calling pathname. * The path argument does not contain the backup volume name. */ int RestoreFile(path) char *path; { struct FileInfoBlock *fib = NULL, *fib2 = NULL; UBYTE exists = FALSE, ignore = FALSE; unsigned isCompressed; struct Lock *lock = NULL; USHORT nameLength; char *s; UBYTE savechar; int status = 0; if (status = CheckStop()) return status; /* Don't restore the big file here...it's done last. */ if (ThisIsBigFile(path)) return status; if (!(fib = (struct FileInfoBlock *) AllocMem((long) sizeof (struct FileInfoBlock), MEMF_PUBLIC | MEMF_CHIP))) { TypeAndSpeak("RestoreFile could not allocate FIB!\n"); return ERROR_NO_FREE_STORE; } sprintf(fullBackPath, "%s%s",srcVol,path); strcpy(fullHomePath, homePath); if (*path) { if (!homeIsDevice) strcat(fullHomePath, "/"); strcat(fullHomePath, path); } #ifdef DEBUG sprintf(conmsg,"fullBackPath = %s\n",fullBackPath); DebugWrite(conmsg); sprintf(conmsg,"fullHomePath = %s\n",fullHomePath); DebugWrite(conmsg); #endif if ((sizeLeft = DiskBlocksLeft(destVol)) < 0) { status = -sizeLeft; sprintf(conmsg, "Can't determine disk blocks left on %s; error %d.\n", destVol, status); TypeAndSpeak(conmsg); goto done; } SetGauge(sizeLeft, totalSize); do { status = 0; if (!(lock = (struct Lock *) Lock(fullBackPath, SHARED_LOCK))) { status = IoErr(); sprintf(conmsg, "RestoreFile can't lock %s; error %d\n", fullBackPath, status); TypeAndSpeak(conmsg); goto checkStatus; } if (!Examine(lock, fib)) { status = IoErr(); sprintf(conmsg, "RestoreFile can't examine %s; error %d\n", fullBackPath, status); TypeAndSpeak(conmsg); goto checkStatus; } if (fib->fib_DirEntryType > 0) { /* path is a directory */ status = RestoreDir(lock, fib, path); UnLock(lock); lock = NULL; } else { UnLock(lock); lock = NULL; /* Note: though we can use the function IsCompressed to test * for potentially compressed files in Backup(), ONLY files * compressed with LZW (ending in .z) are candidates for * decompression. The following test looks for these files * only. */ isCompressed = false; if (doCompress && (s = rindex(fullHomePath,'.'))) { /* look for ".z" ONLY! */ if (!strcmpc(s, ".z")) { isCompressed = true; /* truncate the destination pathname (remove ".z") */ nameLength = strlen(fullHomePath); fullHomePath[nameLength-2] = '\0'; } } /*#define NOCOPY*/ #ifndef NOCOPY /* If this file exists, then check its modification date. If * it's newer than the backup, don't replace it. */ if ((lock = (struct Lock *) Lock(fullHomePath, SHARED_LOCK))) { if (!(fib2 = (struct FileInfoBlock *) AllocMem((long) sizeof (struct FileInfoBlock), MEMF_PUBLIC | MEMF_CHIP))) { TypeAndSpeak("RestoreFile could not allocate FIB!\n"); status = ERROR_NO_FREE_STORE; goto done; } Examine(lock, fib2); UnLock(lock); lock = NULL; if (CompareDS(&fib2->fib_Date, &fib->fib_Date) >= 0) ignore = TRUE; } if (ignore) { sprintf(conmsg, "Skipping %s, since home file is current.\n", path); WriteConsole(conmsg); } else { if (! (doCompress && isCompressed) ) { copyfile: sprintf(conmsg,"Copying %s\n", fullBackPath); WriteConsole(conmsg); status = CopyFile(fullBackPath, fullHomePath); } else { sprintf(conmsg, "Decompressing %s\n", fullBackPath); WriteConsole(conmsg); if (status = decompress(fullBackPath, fullHomePath)) { sprintf(conmsg, "Decompression of %s failed; status is %d.\n", fullBackPath, status); TypeAndSpeak(conmsg); TypeAndSpeak( "I will try to copy the file, instead.\n"); /* restore ".z" to name */ fullHomePath[nameLength-2] = '.'; goto copyfile; } CopyFileDate(fullBackPath, fullHomePath); } } #endif } checkStatus: if (status && status != ERR_ABORT) { ++errorCount; SetErrorGadget(); if (status == ERROR_DISK_FULL) { TypeAndSpeak("The destination disk is full.\n"); /* The following test is pretty kludgy. It boils down to * "If the drive is a DF device, it's removable, therefore the * user can recover from this error by inserting a new diskette." */ if (toupper(destVol[0]) == 'D' && toupper(destVol[1]) == 'F') { TypeAndSpeak( "Put a new disk in the destination drive and try again.\n"); } else { TypeAndSpeak( "You may have to delete some files to continue."); } } status = GetErrOpt(ERR_ABORT|ERR_RETRY_FILE|ERR_IGNORE); if (status == ERR_IGNORE) status = 0; } } while (status == ERR_RETRY_FILE); done: if (lock) UnLock(lock); if (fib) FreeMem(fib, (long) sizeof(struct FileInfoBlock)); if (fib2) FreeMem(fib2, (long) sizeof(struct FileInfoBlock)); return status; }