947 lines
24 KiB
C
947 lines
24 KiB
C
/****************************************************************************
|
|
* ncopy.c
|
|
*
|
|
* Copy file on a Netware server without Network Traffic
|
|
*
|
|
* Copyright (C) 1996 by Brian Reid and Tom Henderson.
|
|
*
|
|
* Send bug reports for ncopy to "breid@tim.com"
|
|
*
|
|
* Still to do: support recursive copy with two arguments
|
|
* Both must be directories. (similar to rcp -r)
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/param.h>
|
|
#include <ctype.h>
|
|
#include <mntent.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include "ncplib.h"
|
|
|
|
|
|
struct NCPMountRec
|
|
{
|
|
char *mountDir;
|
|
char *server;
|
|
struct ncp_conn *conn;
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Globals:
|
|
*
|
|
*/
|
|
const char *VersionStr = "0.1";
|
|
char *ProgramName;
|
|
|
|
struct NCPMountRec *NcpMountTable = NULL;
|
|
int ncpCount = 0;
|
|
|
|
/* (initialized) command options */
|
|
|
|
int optVersion = 0; /* -V TRUE if just want version */
|
|
int optVerbose = 0; /* -v TRUE if want verbose output */
|
|
int optNice = 0; /* -n TRUE if we are cooperative (nice) */
|
|
int optNiceFactorSel = 0; /* -s TRUE if we selected a nice factor */
|
|
int optNiceFactor = 10; /* -s arg, number of 100K blocks to copy
|
|
before sleeping for a second */
|
|
__u32 CopyBlockSize = 100000; /* Size of the default block copy size */
|
|
unsigned int NiceSleepTime = 1; /* Number of seconds to sleep in Nice Mode */
|
|
|
|
int BlocksCopied = 0; /* Number of blocks copied */
|
|
int MaxNcopyRetries = 25; /* Maximum number of times to retry a failed
|
|
copy before giving up */
|
|
|
|
/* Globals needed for signal handlers */
|
|
int OutputOpen = 0; /* True if the ncp output file is open */
|
|
struct ncp_conn *CurrentConn = NULL; /* Connection of output file */
|
|
struct ncp_file_info *CurrentFile = NULL; /* File info of output file */
|
|
|
|
/* Signal control structures */
|
|
static struct sigaction sHangupSig;
|
|
static struct sigaction sInterruptSig;
|
|
static struct sigaction sQuitSig;
|
|
static struct sigaction sTermSig;
|
|
|
|
/****************************************************************************
|
|
*
|
|
*/
|
|
static void
|
|
usage()
|
|
{
|
|
fprintf(stderr, "usage: %s [-V]\n", ProgramName);
|
|
fprintf(stderr, " %s [-vn] [-s amt] sourcefile destinationfile|directory\n", ProgramName);
|
|
fprintf(stderr, " %s [-vn] [-s amt] sourcefile [...] directory\n", ProgramName);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Return pointer to last component of the path.
|
|
* Returned string may have one or more "/" left on the end.
|
|
* ("/" returns pointer to "/", null returns pointer to null)
|
|
* Return pointer to original string if no "/" in string. (except at end)
|
|
*/
|
|
static const char *
|
|
myBaseName(const char *path)
|
|
{
|
|
const char *p;
|
|
|
|
for (p = &path[strlen(path)]; p != path; p--)
|
|
{ /* skip ENDING "/" chars */
|
|
if (*p && *p != '/')
|
|
break;
|
|
}
|
|
if (p == path)
|
|
return p;
|
|
for (; p != path || *p == '/'; p--)
|
|
{
|
|
if (*p == '/')
|
|
return ++p;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
*/
|
|
static const char *
|
|
notDir(const char *path)
|
|
{
|
|
struct stat buf;
|
|
static const char *notDirectory = "not a directory";
|
|
|
|
if (stat(path, &buf))
|
|
return strerror(errno); /* no permission? not exist? */
|
|
if (!S_ISDIR(buf.st_mode))
|
|
return notDirectory; /* not a directory */
|
|
return (char *) 0; /* OK */
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
*/
|
|
static int
|
|
handleOptions(const int argc, char *const argv[])
|
|
{
|
|
int opt;
|
|
|
|
while ((opt = getopt(argc, argv, "vVns:")) != EOF)
|
|
{
|
|
switch (opt)
|
|
{
|
|
|
|
case 'V': /* Version */
|
|
optVersion = 1;
|
|
break;
|
|
|
|
case 'v': /* Verbose output */
|
|
optVerbose = 1;
|
|
break;
|
|
|
|
case 'n': /* Nice, cooperative copy */
|
|
optNice = 1;
|
|
break;
|
|
|
|
case 's': /* Nice Factor */
|
|
optNiceFactorSel = 1;
|
|
optNiceFactor = atoi(optarg);
|
|
if (optNiceFactor < 1)
|
|
{
|
|
fprintf(stderr, "%s: -s option requires positive numeric argument > 0\n",
|
|
ProgramName);
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
default: /* invalid options or options without required arguments */
|
|
return 1;
|
|
}
|
|
continue;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* TODO: if recursive flag last MUST be a directory, even if only 2 args.
|
|
*/
|
|
static int
|
|
validateFileArgs(const int argc, char *const argv[])
|
|
{
|
|
const char *p;
|
|
if (argc == 0)
|
|
{
|
|
fprintf(stderr, "%s: No arguments specified.\n", ProgramName);
|
|
return 1;
|
|
}
|
|
if (argc == 1)
|
|
{
|
|
fprintf(stderr, "%s: No destination specified.\n", ProgramName);
|
|
return 1;
|
|
}
|
|
if ((argc > 2) && (p = notDir(argv[argc - 1])))
|
|
{ /* last arg MUST be dir */
|
|
fprintf(stderr, "%s: %s: %s\n", ProgramName, argv[argc - 1], p);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Duplicate a string.
|
|
*/
|
|
char *
|
|
duplicateStr(const char *InStr)
|
|
{
|
|
char *dup;
|
|
if (!InStr)
|
|
return NULL;
|
|
dup = (char *) malloc(strlen(InStr) + 1);
|
|
if (dup)
|
|
strcpy(dup, InStr);
|
|
return dup;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* load a table of ncpfs mount points.
|
|
*/
|
|
int
|
|
loadMountTable()
|
|
{
|
|
FILE *mountedFile;
|
|
struct mntent *mountEntry = NULL;
|
|
ncpCount = 0;
|
|
if ((mountedFile = fopen(MOUNTED, "r")) == NULL)
|
|
{
|
|
fprintf(stderr, "ncopy: cannot open %s, %s\n", MOUNTED, strerror(errno));
|
|
return 1;
|
|
}
|
|
while ((mountEntry = getmntent(mountedFile)) != NULL)
|
|
{
|
|
if (!strcmp(mountEntry->mnt_type, "ncpfs"))
|
|
ncpCount++;
|
|
}
|
|
if (ncpCount)
|
|
{
|
|
NcpMountTable = (struct NCPMountRec *)
|
|
malloc(ncpCount * sizeof(struct NCPMountRec));
|
|
if (!NcpMountTable)
|
|
{
|
|
fprintf(stderr, "Out of memory\n");
|
|
fclose(mountedFile);
|
|
return 1;
|
|
}
|
|
fseek(mountedFile, 0, SEEK_SET);
|
|
ncpCount = 0;
|
|
while ((mountEntry = getmntent(mountedFile)) != NULL)
|
|
{
|
|
if (!strcmp(mountEntry->mnt_type, "ncpfs"))
|
|
{
|
|
NcpMountTable[ncpCount].mountDir = duplicateStr(mountEntry->mnt_dir);
|
|
NcpMountTable[ncpCount].server =
|
|
duplicateStr(mountEntry->mnt_fsname);
|
|
NcpMountTable[ncpCount].conn = NULL;
|
|
ncpCount++;
|
|
}
|
|
}
|
|
}
|
|
fclose(mountedFile);
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Releases the table of ncpfs mount points.
|
|
*/
|
|
void
|
|
releaseMountTable()
|
|
{
|
|
int loop;
|
|
if (!ncpCount)
|
|
return;
|
|
for (loop = ncpCount; loop; loop--, ncpCount--)
|
|
{
|
|
if (NcpMountTable[loop - 1].conn)
|
|
{
|
|
ncp_close(NcpMountTable[loop - 1].conn);
|
|
NcpMountTable[loop - 1].conn = NULL;
|
|
}
|
|
free(NcpMountTable[loop - 1].server);
|
|
free(NcpMountTable[loop - 1].mountDir);
|
|
}
|
|
free(NcpMountTable);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Finds the index into the mount point table that enables ncp copy for
|
|
* the file.
|
|
* Returns -1 if the files do not reference the same server.
|
|
*/
|
|
int
|
|
ncpIndex(const char *InputFile, const char *OutputFile)
|
|
{
|
|
int loop;
|
|
char *mountDir;
|
|
if (!ncpCount)
|
|
return -1;
|
|
|
|
for (loop = 0; loop < ncpCount; loop++)
|
|
{
|
|
mountDir = NcpMountTable[loop].mountDir;
|
|
if (!strncmp(mountDir, InputFile, strlen(mountDir)) &&
|
|
!strncmp(mountDir, OutputFile, strlen(mountDir)))
|
|
return loop;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* Does a regular buffered file copy.
|
|
* This is used if we cannot use the Netware file copy.
|
|
*/
|
|
int
|
|
normalFileCopy(const char *InputFile, const char *OutputFile,
|
|
char *Buffer, int BufferSize,
|
|
const char *paramInputFile,
|
|
const char *paramOutputFile)
|
|
{
|
|
int fdIn, fdOut;
|
|
long fileSize, totalSize;
|
|
struct stat statBuf;
|
|
fdIn = open(InputFile, O_RDONLY);
|
|
if (fdIn == -1)
|
|
{
|
|
fprintf(stderr, "%s: Cannot open %s, %s\n", ProgramName, paramInputFile,
|
|
strerror(errno));
|
|
return 1;
|
|
}
|
|
if (fstat(fdIn, &statBuf))
|
|
{
|
|
fprintf(stderr, "%s: Cannot stat %s, %s\n", ProgramName, paramInputFile,
|
|
strerror(errno));
|
|
close(fdIn);
|
|
return 1;
|
|
}
|
|
if (S_ISDIR(statBuf.st_mode))
|
|
{
|
|
close(fdIn);
|
|
fprintf(stderr, "%s: %s: omitting directory\n", ProgramName, paramInputFile);
|
|
return 0; /* At this point, don't consider this a fatal error */
|
|
}
|
|
fdOut = open(OutputFile, O_CREAT | O_TRUNC | O_WRONLY, statBuf.st_mode);
|
|
if (fdOut == -1)
|
|
{
|
|
fprintf(stderr, "%s: Cannot create %s, %s\n", ProgramName, paramOutputFile,
|
|
strerror(errno));
|
|
close(fdIn);
|
|
return 1;
|
|
}
|
|
fileSize = lseek(fdIn, 0, SEEK_END);
|
|
if (fileSize < 0)
|
|
{
|
|
fprintf(stderr, "%s: lseek error on %s, %s\n", ProgramName, paramInputFile,
|
|
strerror(errno));
|
|
close(fdOut);
|
|
close(fdIn);
|
|
return 1;
|
|
}
|
|
lseek(fdIn, 0, SEEK_SET);
|
|
if (optVerbose)
|
|
{
|
|
printf("Normal copy: %s -> %s 0%%", paramInputFile, paramOutputFile);
|
|
fflush(stdout);
|
|
}
|
|
totalSize = fileSize;
|
|
while (fileSize)
|
|
{
|
|
int currentMove;
|
|
int writeAmt;
|
|
currentMove = (fileSize > BufferSize) ? BufferSize : fileSize;
|
|
if (read(fdIn, Buffer, currentMove) != currentMove)
|
|
{
|
|
fprintf(stderr, "%s: Error reading %s, %s\n", ProgramName, paramInputFile,
|
|
strerror(errno));
|
|
close(fdIn);
|
|
close(fdOut);
|
|
return 1;
|
|
}
|
|
writeAmt = write(fdOut, Buffer, currentMove);
|
|
if (writeAmt < 0)
|
|
{
|
|
fprintf(stderr, "%s: Error writing %s, %s\n", ProgramName, paramOutputFile,
|
|
strerror(errno));
|
|
close(fdIn);
|
|
close(fdOut);
|
|
return 1;
|
|
} else if (writeAmt == 0)
|
|
{
|
|
fprintf(stderr, "%s: Out of space on destination device writing %s\n",
|
|
ProgramName, OutputFile);
|
|
close(fdIn);
|
|
close(fdOut);
|
|
return 1;
|
|
}
|
|
fileSize -= currentMove;
|
|
if (optVerbose)
|
|
{
|
|
printf("\rNormal copy: %s -> %s %ld%%", paramInputFile, paramOutputFile, (100 - (fileSize * 100 / totalSize)));
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
close(fdOut);
|
|
close(fdIn);
|
|
if (optVerbose)
|
|
printf("\n");
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Converts a string to upper case.
|
|
* Netware file names need to be all upper case.
|
|
*/
|
|
char *
|
|
upString(char *str)
|
|
{
|
|
char *alias = str;
|
|
while (*alias)
|
|
{
|
|
*alias = toupper(*alias);
|
|
++alias;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Locates the first occurrance of a single character in the input string.
|
|
* returns -1 if the character is not found.
|
|
*/
|
|
int
|
|
stringPosition(const char *str, char token)
|
|
{
|
|
const char *alias = str;
|
|
while (*alias)
|
|
{
|
|
if (*alias == token)
|
|
return alias - str;
|
|
alias++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Walks up the directory path building info structures along the way
|
|
* in order to get a dir_handle.
|
|
* This will mangle the input "FileString", leaving just the file name
|
|
* component in it when it is finished.
|
|
*/
|
|
int
|
|
getDirHandle(struct ncp_conn *conn, char *FileString, __u8 * NewDirHandle)
|
|
{
|
|
struct nw_info_struct info1, info2;
|
|
int currentLevel = 0;
|
|
int k;
|
|
struct nw_info_struct *parentInfo = NULL;
|
|
struct nw_info_struct *currentInfo = NULL;
|
|
|
|
while ((k = stringPosition(FileString, '/')) >= 0)
|
|
{
|
|
FileString[k] = 0;
|
|
if (!currentLevel)
|
|
{
|
|
parentInfo = NULL;
|
|
currentInfo = &info1;
|
|
} else if (currentLevel % 2)
|
|
{
|
|
parentInfo = &info1;
|
|
currentInfo = &info2;
|
|
} else
|
|
{
|
|
parentInfo = &info2;
|
|
currentInfo = &info1;
|
|
}
|
|
if (ncp_do_lookup(conn, parentInfo, FileString,
|
|
currentInfo) != 0)
|
|
{
|
|
fprintf(stderr, "%s: Ncp lookup failed on directory %s--%s\n",
|
|
ProgramName, FileString, strerror(errno));
|
|
return 1;
|
|
}
|
|
++currentLevel;
|
|
memmove(FileString, FileString + k + 1, strlen(FileString + k + 1) + 1);
|
|
}
|
|
|
|
if (ncp_alloc_short_dir_handle(conn, currentInfo, NCP_ALLOC_TEMPORARY,
|
|
NewDirHandle) != 0)
|
|
{
|
|
fprintf(stderr, "%s: Ncp alloc dir handle failed--%s\n",
|
|
ProgramName, strerror(errno));
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* Interfaces with the ncplib to do the netware copy of the file.
|
|
*/
|
|
int
|
|
netwareCopyFile(int ncpMountIndex, const char *sourcefile,
|
|
const char *destfile,
|
|
const char *paramInputFile,
|
|
const char *paramOutputFile)
|
|
{
|
|
__u8 source_dir_handle;
|
|
__u8 dest_dir_handle;
|
|
struct ncp_file_info source_file;
|
|
struct ncp_file_info dest_file;
|
|
__u32 amountCopied;
|
|
__u32 amtLeft;
|
|
__u32 totalSize;
|
|
__u32 sourceOff;
|
|
__u32 thisMove;
|
|
int stroffset;
|
|
int retValue;
|
|
char *sourceDup;
|
|
char *destDup;
|
|
struct ncp_conn *sourceconn;
|
|
int retryCount;
|
|
long err = 0;
|
|
|
|
/* Establish a connection to a Netware mount point if
|
|
one is not already established. */
|
|
if (!NcpMountTable[ncpMountIndex].conn)
|
|
{
|
|
NcpMountTable[ncpMountIndex].conn =
|
|
ncp_open_mount(NcpMountTable[ncpMountIndex].mountDir, &err);
|
|
if (err)
|
|
{
|
|
com_err(ProgramName, err, "opening ncp connection on mount point %s",
|
|
NcpMountTable[ncpMountIndex].mountDir);
|
|
return 2;
|
|
}
|
|
}
|
|
sourceconn = NcpMountTable[ncpMountIndex].conn;
|
|
|
|
/* Duplicate and upper case the file names so we do not trample
|
|
on the input strings */
|
|
stroffset = strlen(NcpMountTable[ncpMountIndex].mountDir) + 1;
|
|
sourceDup = duplicateStr(sourcefile + stroffset);
|
|
destDup = duplicateStr(destfile + stroffset);
|
|
if (!sourceDup || !destDup)
|
|
{
|
|
fprintf(stderr, "%s: Malloc failed duplicating file names\n",
|
|
ProgramName);
|
|
return 2;
|
|
}
|
|
upString(sourceDup);
|
|
upString(destDup);
|
|
|
|
/* Get Handles to the input and output directories */
|
|
if (getDirHandle(sourceconn, sourceDup, &source_dir_handle) ||
|
|
getDirHandle(sourceconn, destDup, &dest_dir_handle))
|
|
{
|
|
free(sourceDup);
|
|
free(destDup);
|
|
return 1;
|
|
}
|
|
/* Open the input and output files. */
|
|
if (ncp_open_file(sourceconn, source_dir_handle, sourceDup, 0, AR_READ,
|
|
&source_file) != 0)
|
|
{
|
|
fprintf(stderr, "%s: Cannot open %s--%s\n",
|
|
ProgramName, paramInputFile, strerror(errno));
|
|
free(sourceDup);
|
|
free(destDup);
|
|
return 1;
|
|
}
|
|
if (ncp_create_file(sourceconn, dest_dir_handle, destDup,
|
|
source_file.file_attributes, &dest_file) != 0)
|
|
{
|
|
fprintf(stderr, "%s: Cannot create %s--%s\n", ProgramName, paramOutputFile,
|
|
strerror(errno));
|
|
ncp_close_file(sourceconn, source_file.file_id);
|
|
free(sourceDup);
|
|
free(destDup);
|
|
return 1;
|
|
}
|
|
/* Set globals in case a signal happens while copying */
|
|
CurrentConn = sourceconn;
|
|
CurrentFile = &dest_file;
|
|
OutputOpen = 1;
|
|
|
|
free(sourceDup);
|
|
free(destDup);
|
|
|
|
retValue = 0;
|
|
if (optVerbose)
|
|
{
|
|
printf("NetWare copy: %s -> %s 0%%", paramInputFile, paramOutputFile);
|
|
fflush(stdout);
|
|
}
|
|
/* The main copy loop. */
|
|
|
|
amtLeft = totalSize = source_file.file_length;
|
|
sourceOff = 0;
|
|
retryCount = 0;
|
|
|
|
while (amtLeft && retryCount < MaxNcopyRetries)
|
|
{
|
|
int ncopyRetValue;
|
|
if (amtLeft > CopyBlockSize)
|
|
thisMove = CopyBlockSize;
|
|
else
|
|
thisMove = amtLeft;
|
|
/* If we are being nice and we've copied enough blocks, go to sleep */
|
|
if (optNice)
|
|
{
|
|
if (BlocksCopied == optNiceFactor)
|
|
{
|
|
sleep(NiceSleepTime);
|
|
BlocksCopied = 0;
|
|
} else
|
|
++BlocksCopied;
|
|
}
|
|
ncopyRetValue = ncp_copy_file(sourceconn, source_file.file_id,
|
|
dest_file.file_id, sourceOff, sourceOff,
|
|
thisMove, &amountCopied);
|
|
if (ncopyRetValue != 0)
|
|
{
|
|
/* In my testing this only happens when you run out of space
|
|
on the server.
|
|
Netware seems to wait a bit before reporting space recently
|
|
free'd. I will just wait a bit before bombin */
|
|
sleep(1); /* Sleep for a second and try again */
|
|
retryCount++;
|
|
amountCopied = thisMove = 0;
|
|
}
|
|
if (amountCopied != thisMove)
|
|
{
|
|
fprintf(stderr, "%s: Warning, amountCopied (%u) != thisMove (%u)\n",
|
|
ProgramName, (unsigned int) amountCopied, (unsigned int) thisMove);
|
|
}
|
|
#ifdef NCOPY_DEBUG
|
|
fprintf(stderr, "Copied %u (actual %u)\n", (unsigned int) thisMove,
|
|
(unsigned int) amountCopied);
|
|
#endif
|
|
amtLeft -= amountCopied;
|
|
sourceOff += amountCopied;
|
|
if (optVerbose)
|
|
{
|
|
printf("\rNetWare copy: %s -> %s %ld%%", paramInputFile, paramOutputFile,
|
|
(100 - (long) ((float) amtLeft / (float) totalSize * 100.0)));
|
|
if (retryCount)
|
|
printf(" %d retries", retryCount);
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
if (retryCount >= MaxNcopyRetries)
|
|
retValue = 1;
|
|
if (optVerbose)
|
|
printf("\n");
|
|
if (ncp_close_file(sourceconn, dest_file.file_id) != 0)
|
|
{
|
|
fprintf(stderr, "%s: Close failed for %s\n", ProgramName, paramOutputFile);
|
|
retValue = 1;
|
|
}
|
|
/* Clear signal handling globals */
|
|
OutputOpen = 0;
|
|
CurrentConn = NULL;
|
|
CurrentFile = NULL;
|
|
|
|
if (ncp_close_file(sourceconn, source_file.file_id) != 0)
|
|
{
|
|
fprintf(stderr, "%s: Close failed for %s\n", ProgramName, paramInputFile);
|
|
retValue = 1;
|
|
}
|
|
if (ncp_dealloc_dir_handle(sourceconn, dest_dir_handle) != 0)
|
|
{
|
|
fprintf(stderr, "%s: Dealloc dir handle error for %s\n", ProgramName,
|
|
paramOutputFile);
|
|
retValue = 1;
|
|
}
|
|
if (ncp_dealloc_dir_handle(sourceconn, source_dir_handle) != 0)
|
|
{
|
|
fprintf(stderr, "%s: Dealloc dir handle error for %s\n", ProgramName,
|
|
paramInputFile);
|
|
retValue = 1;
|
|
}
|
|
return retValue;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* Decides whether to use the traditional file copy or the netware remote
|
|
* file copy.
|
|
*/
|
|
int
|
|
copyFiles(const char *realsource, const char *realdestination,
|
|
const char *paraminputfile, const char *paramoutputfile)
|
|
{
|
|
int oldUMask;
|
|
char fileBuffer[24000];
|
|
int retVal = 0;
|
|
int ncpMountIndex = ncpIndex(realsource, realdestination);
|
|
#ifdef NCOPY_DEBUG
|
|
printf("Real Source '%s'\n"
|
|
"Real Dest '%s'\n"
|
|
"Param Src '%s'\n"
|
|
"Param Dest '%s'\n", realsource, realdestination, paraminputfile,
|
|
paramoutputfile);
|
|
#endif
|
|
|
|
oldUMask = umask(0);
|
|
if (ncpMountIndex < 0)
|
|
retVal = normalFileCopy(realsource, realdestination, fileBuffer,
|
|
sizeof(fileBuffer),
|
|
paraminputfile, paramoutputfile);
|
|
else
|
|
retVal = netwareCopyFile(ncpMountIndex, realsource, realdestination,
|
|
paraminputfile, paramoutputfile);
|
|
umask(oldUMask);
|
|
return retVal;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* HERE
|
|
*
|
|
* Brian may NEED "fake" path if he prints error messages?
|
|
* or I may need a way to get his error messages so I can
|
|
* print them with the "fake" path.
|
|
* My current error messages are on the REAL path, which would be confusing...
|
|
*
|
|
* (1-source problem, 2-destination problem, 3-other fatal)
|
|
* We need to decide when to exit or continue the loop,
|
|
* and what to return when we do exit the loop.
|
|
* Is it failure if 3 files are to be copied, and 1 fails?
|
|
* If one copy fails, we stay in the loop, right?
|
|
* Is it failure if destination fails?
|
|
* Do we Stay in the loop?
|
|
*/
|
|
static int
|
|
copyRealPaths(const char *source, const char *destination)
|
|
{
|
|
char realsource[MAXPATHLEN * 2];
|
|
char realdestination[MAXPATHLEN * 2];
|
|
char dirPart[MAXPATHLEN + 1];
|
|
char filePart[MAXPATHLEN + 1];
|
|
const char *p;
|
|
|
|
if (realpath(source, realsource) == 0)
|
|
{ /* the source must at least exist */
|
|
fprintf(stderr, "%s: %s: %s\n",
|
|
ProgramName, source, strerror(errno));
|
|
return 1; /* indicate a "source" problem */
|
|
}
|
|
if (realpath(destination, realdestination) == 0)
|
|
{ /* dest file missing? OK */
|
|
strncpy(dirPart, destination, MAXPATHLEN); /* but "dirpart" must work */
|
|
dirPart[MAXPATHLEN] = 0;
|
|
p = myBaseName(dirPart);
|
|
strcpy(filePart, p);
|
|
dirPart[p - dirPart] = 0; /* isolates "directory" part from "file part" */
|
|
if (realpath(dirPart, realdestination) == 0)
|
|
{
|
|
fprintf(stderr, "%s: %s: %s\n",
|
|
ProgramName, dirPart, strerror(errno));
|
|
return 2; /* indicate a "destination" problem */
|
|
}
|
|
if (*realdestination != '/' || *(realdestination + 1))
|
|
strcat(realdestination, "/");
|
|
strcat(realdestination, filePart);
|
|
}
|
|
/* becomes prog exit code */
|
|
|
|
/* Test Cases: (Where file/dir may or may not exist)
|
|
* "", file, file/, dir, dir/
|
|
* /, //, /dir, /dir/, /file, /file/,
|
|
* /tmp/file, /tmp/file/, tmp/file, tmp/file/,
|
|
* /tmp/dir, /tmp/dir/, tmp/dir, tmp/dir/
|
|
*/
|
|
return copyFiles(realsource, realdestination, source, destination);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* guaranteed argc is at least 2 and
|
|
* if argc > 2 last parameter is a directory
|
|
* by validateFileArgs()
|
|
*/
|
|
static int
|
|
handleFileArgs(int argc, char *const argv[])
|
|
{
|
|
int loop;
|
|
const char *destination;
|
|
int copyStatus;
|
|
int returnCode = 0; /* default program exit code */
|
|
const char *baseNamePtr;
|
|
char destinationfile[MAXPATHLEN * 2];
|
|
|
|
destination = argv[argc - 1]; /* get LAST argument */
|
|
for (loop = 0; loop < (argc - 1); loop++)
|
|
{ /* all file arguments, but last */
|
|
strncpy(destinationfile, destination, MAXPATHLEN);
|
|
destinationfile[MAXPATHLEN] = 0;
|
|
if ((argc > 2) || (!notDir(argv[argc - 1])))
|
|
{ /* destination is a dir */
|
|
if (*destinationfile != '/' || *(destinationfile + 1))
|
|
strcat(destinationfile, "/");
|
|
baseNamePtr = myBaseName(argv[loop]); /* get the file name */
|
|
strcat(destinationfile, baseNamePtr); /* add it on end of directory */
|
|
}
|
|
copyStatus = copyRealPaths(argv[loop], destinationfile); /* do the copy */
|
|
if (copyStatus > 1)
|
|
return copyStatus; /* fatal failure? bye */
|
|
if (copyStatus == 1)
|
|
returnCode = 1; /* a partial failure? we can continue */
|
|
}
|
|
return returnCode; /* return what will be the program exit code */
|
|
}
|
|
/****************************************************************************
|
|
*
|
|
*/
|
|
static void
|
|
handleSignals(int sigNumber)
|
|
{
|
|
/* Ignore Signal Handling while cleaning up */
|
|
|
|
/* SIGHUP */
|
|
sHangupSig.sa_handler = SIG_IGN;
|
|
if (sigaction(SIGHUP, &sHangupSig, NULL) == -1)
|
|
{
|
|
fprintf(stderr, "%s: Reset to ignore SIGHUP signal failed: %s",
|
|
ProgramName, strerror(errno));
|
|
}
|
|
/* SIGINT */
|
|
sInterruptSig.sa_handler = SIG_IGN;
|
|
if (sigaction(SIGINT, &sInterruptSig, NULL) == -1)
|
|
{
|
|
fprintf(stderr, "%s: Reset to ignore SIGINT signal failed: %s",
|
|
ProgramName, strerror(errno));
|
|
}
|
|
/* SIGQUIT */
|
|
sQuitSig.sa_handler = SIG_IGN;
|
|
if (sigaction(SIGQUIT, &sQuitSig, NULL) == -1)
|
|
{
|
|
fprintf(stderr, "%s: Reset to ignore SIGQUIT signal failed: %s",
|
|
ProgramName, strerror(errno));
|
|
}
|
|
/* SIGTERM */
|
|
sTermSig.sa_handler = SIG_IGN;
|
|
if (sigaction(SIGTERM, &sTermSig, NULL) == -1)
|
|
{
|
|
fprintf(stderr, "%s: Reset to ignore SIGTERM signal failed: %s",
|
|
ProgramName, strerror(errno));
|
|
}
|
|
/* If we don't close the ncp output file, we have to ncpumount and
|
|
ncpmount before we can get rid of it. */
|
|
if (OutputOpen)
|
|
{
|
|
/* Issue a warning if we cannot close the file */
|
|
/* If an error occurs we probably have to umount/mount to
|
|
remove the file */
|
|
if (ncp_close_file(CurrentConn, CurrentFile->file_id) != 0)
|
|
{
|
|
fprintf(stderr, "%s: unclean close of output file", ProgramName);
|
|
}
|
|
OutputOpen = 0;
|
|
}
|
|
exit(128 + sigNumber);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* We'll trap Hangup, Interrupt, Quit or Terminate
|
|
*/
|
|
static int
|
|
trapSignals()
|
|
{
|
|
if (sigaction(SIGHUP, NULL, &sHangupSig))
|
|
{ /* init structure fields */
|
|
fprintf(stderr, "%s: Get HANGUP signal action failed: %s",
|
|
ProgramName, strerror(errno));
|
|
return 1;
|
|
}
|
|
sHangupSig.sa_handler = handleSignals;
|
|
if (sigaction(SIGHUP, &sHangupSig, NULL) == -1)
|
|
{
|
|
fprintf(stderr, "%s: Reset HANGUP signal action failed: %s",
|
|
ProgramName, strerror(errno));
|
|
return 1;
|
|
}
|
|
if (sigaction(SIGINT, NULL, &sInterruptSig))
|
|
{ /* init structure fields */
|
|
fprintf(stderr, "%s: Get INTERRUPT signal action failed: %s",
|
|
ProgramName, strerror(errno));
|
|
return 1;
|
|
}
|
|
sInterruptSig.sa_handler = handleSignals;
|
|
if (sigaction(SIGINT, &sInterruptSig, NULL) == -1)
|
|
{
|
|
fprintf(stderr, "%s: Reset INTERRUPT signal action failed: %s",
|
|
ProgramName, strerror(errno));
|
|
return 1;
|
|
}
|
|
if (sigaction(SIGQUIT, NULL, &sQuitSig))
|
|
{ /* init structure fields */
|
|
fprintf(stderr, "%s: Get QUIT signal action failed: %s",
|
|
ProgramName, strerror(errno));
|
|
return 1;
|
|
}
|
|
sQuitSig.sa_handler = handleSignals;
|
|
if (sigaction(SIGQUIT, &sQuitSig, NULL) == -1)
|
|
{
|
|
fprintf(stderr, "%s: Reset QUIT signal action failed: %s",
|
|
ProgramName, strerror(errno));
|
|
return 1;
|
|
}
|
|
if (sigaction(SIGTERM, NULL, &sTermSig))
|
|
{ /* init structure fields */
|
|
fprintf(stderr, "%s: Get TERMINATE signal action failed: %s",
|
|
ProgramName, strerror(errno));
|
|
return 1;
|
|
}
|
|
sTermSig.sa_handler = handleSignals;
|
|
if (sigaction(SIGTERM, &sTermSig, NULL) == -1)
|
|
{
|
|
fprintf(stderr, "%s: Reset TERMINATE signal action failed: %s",
|
|
ProgramName, strerror(errno));
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
*/
|
|
int
|
|
main(int argc, char *const argv[])
|
|
{
|
|
int returnCode;
|
|
ProgramName = argv[0];
|
|
|
|
if (handleOptions(argc, argv))
|
|
{ /* bad option, missing option parameter */
|
|
usage();
|
|
return 1;
|
|
}
|
|
if (optVersion)
|
|
{ /* only option not requiring any arguments */
|
|
printf("%s version %s\n", ProgramName, VersionStr);
|
|
return 0;
|
|
}
|
|
if (validateFileArgs(argc - optind, argv + optind))
|
|
{
|
|
usage();
|
|
return 1;
|
|
}
|
|
if (trapSignals())
|
|
return 1;
|
|
loadMountTable();
|
|
returnCode = handleFileArgs(argc - optind, argv + optind);
|
|
releaseMountTable();
|
|
return returnCode;
|
|
}
|