Files
ncpfs/util/ncopy.c
ncpfs archive import 82706139bf Import ncpfs 2.2.1
2026-04-28 20:39:59 +02:00

1194 lines
31 KiB
C

/*
ncopy.c - Copy file on a Netware server without Network Traffic
Copyright (C) 1996 by Brian Reid and Tom Henderson.
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
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)
Revision history:
0.01 1996 Brian Reid and Tom Henderson
Initial revision.
0.02 1999 Petr Vandrovec <vandrove@vc.cvut.cz>
Copy trustees and other Netware informations.
1.00 1999, November 20 Petr Vandrovec <vandrove@vc.cvut.cz>
Added license.
1.01 1999, November 21 Petr Vandrovec <vandrove@vc.cvut.cz>
Copy MAC resource fork.
1.02 2000, June 17 Petr Vandrovec <vandrove@vc.cvut.cz>
Recursive copy.
1.03 2001, August 9 Petr Vandrovec <vandrove@vc.cvut.cz>
Copy trustees and other Netware informations on directories.
Added -u option to copy different files only.
*/
#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 <errno.h>
#include <dirent.h>
#include <ncp/nwcalls.h>
#include "private/libintl.h"
#define _(X) gettext(X)
#define dfprintf(X...) fprintf(X)
/****************************************************************************
* Globals:
*
*/
const char *VersionStr = "1.02";
static char *ProgramName;
#define RSRCSUBDIR ".rsrc/"
/* (initialized) command options */
static int optVersion = 0; /* -V TRUE if just want version */
static int optVerbose = 0; /* -v TRUE if want verbose output */
static int optRecursive = 0; /* -r TRUE if want recursive copy */
static int optNice = 0; /* -n TRUE if we are cooperative (nice) */
static int optNiceFactorSel = 0; /* -s TRUE if we selected a nice factor */
static int optNiceFactor = 10; /* -s arg, number of 1M blocks to copy
before sleeping for a second */
static u_int32_t CopyBlockSize = 1048576; /* Size of the default block copy size */
static unsigned int NiceSleepTime = 1; /* Number of seconds to sleep in Nice Mode */
static int optOwner = 0; /* -pp */
static int optFlags = 0; /* -p */
static int optTimes = 0; /* -p too... Hack source... */
static int optTrustees = 0; /* -t */
static int optMAC = 0; /* -m */
static int optMACinRsrc = 0; /* -M */
static int optOnlyChanged = 0; /* -u */
static int BlocksCopied = 0; /* Number of blocks copied */
static unsigned int MaxNcopyRetries = 25; /* Maximum number of times to retry a failed
copy before giving up */
/* Globals needed for signal handlers */
static NWCONN_HANDLE CurrentConn = NULL; /* Connection of output file */
static u_int8_t *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(void) {
fprintf(stderr, _("usage: %s [-V]\n"), ProgramName);
fprintf(stderr, _(" %s [-vmMnppt] [-s amt] sourcefile destinationfile|directory\n"), ProgramName);
fprintf(stderr, _(" %s [-vmMnppt] [-s amt] sourcefile [...] directory\n"), ProgramName);
fprintf(stderr, _(" %s [-vmMnppt] [-s amt] -r sourcedir 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;
if (stat(path, &buf))
return strerror(errno); /* no permission? not exist? */
if (!S_ISDIR(buf.st_mode))
return _("not a directory"); /* not a directory */
return NULL; /* OK */
}
/****************************************************************************
*
*/
static int
handleOptions(const int argc, char *const argv[])
{
int opt;
while ((opt = getopt(argc, argv, "vVns:ptmMru")) != 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;
case 'p': /* preserve flags */
if (optTimes)
optOwner = 1; /* multiple 'p' */
optFlags = optTimes = 1;
break;
case 't':
optTrustees = 1;
break;
case 'm': /* copy MAC resource fork */
optMAC = 1;
break;
case 'M': /* MAC resource fork in .rsrc subdirectory */
optMACinRsrc = 1;
break;
case 'r':
optRecursive = 1;
break;
case 'u':
optOnlyChanged = 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 ((optRecursive || 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;
}
static int xlatid(NWCONN_HANDLE scon, NWObjectID sID,
NWCONN_HANDLE dcon, NWObjectID *dID) {
NWCCODE err;
char objectName[256];
NWObjectType objectType;
if (sID == 0) {
/* set zero owner returns 0x8901 error from ncp_modify... */
return 0;
}
if (scon == dcon) {
*dID = sID;
return 1;
}
err = NWGetObjectName(scon, sID, objectName, &objectType);
if (err) {
fprintf(stderr, _("Cannot convert ID %08X to name: %s\n"),
sID, strnwerror(err));
return 0;
}
err = NWGetObjectID(dcon, objectName, objectType, dID);
if (err) {
fprintf(stderr, _("Cannot convert name %s(%04X) to ID: %s\n"),
objectName, objectType, strnwerror(err));
return 0;
}
return 1;
}
/****************************************************************************
* Interfaces with the ncplib to do the netware copy of the file.
*/
static int
copyNWNW(
const char* header,
int sfd,
NWCONN_HANDLE scon,
const u_int8_t* sfhandle,
const char* source,
int dfd,
NWCONN_HANDLE dcon,
const u_int8_t* dfhandle,
const char* destination) {
ncp_off64_t totalSize;
ncp_off64_t sourceOff;
NWCCODE err;
unsigned int retryCount;
int failed;
int ncopy = scon && (scon == dcon);
int dolf = optVerbose;
if (scon) {
err = ncp_get_file_size(scon, sfhandle, &totalSize);
if (err)
totalSize = 0;
} else {
struct stat stats;
err = fstat(sfd, &stats);
if (err)
totalSize = 0;
else
totalSize = stats.st_size;
}
sourceOff = 0;
retryCount = 0;
if (optVerbose) {
if (totalSize)
printf(_("%s: %s -> %s %5.1f%%"), header, source, destination, 0.0);
else
printf(_("%s: %s -> %s %lld"), header, source, destination, 0LL);
fflush(stdout);
}
failed = -1;
while (retryCount < MaxNcopyRetries) {
if (ncopy) {
u_int32_t amountCopied;
/* 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;
}
err = ncp_copy_file(dcon, sfhandle,
dfhandle, sourceOff,
sourceOff,
CopyBlockSize, &amountCopied);
if (err) {
/* 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++;
} else {
if (!amountCopied) {
ncopy = 0;
}
sourceOff += amountCopied;
}
} else {
u_int8_t buffer[49152];
long bytes;
long written;
if (scon)
bytes = ncp_read(scon, sfhandle, sourceOff, sizeof(buffer), buffer);
else
bytes = read(sfd, buffer, sizeof(buffer));
if (bytes < 0) {
dolf = 0;
if (optVerbose)
putchar('\n');
fprintf(stderr, _("%s: Cannot read %s\n"),
ProgramName,
source);
break;
}
if (bytes == 0) {
/* whole file? */
if (sourceOff >= totalSize) {
failed = 0;
break;
}
sleep(1);
retryCount++;
} else {
if (dcon)
written = ncp_write(dcon, dfhandle, sourceOff, bytes, buffer);
else
written = write(dfd, buffer, bytes);
if (written != bytes) {
dolf = 0;
if (optVerbose)
putchar('\n');
fprintf(stderr, _("%s: Cannot write %s\n"),
ProgramName,
destination);
break;
}
sourceOff += bytes;
}
}
if (optVerbose)
{
putchar('\r');
if (totalSize && (totalSize >= sourceOff))
printf(_("%s: %s -> %s %5.1f%%"), header, source, destination,
((float) sourceOff) / totalSize * 100.0);
else
printf(_("%s: %s -> %s %lld"), header, source, destination,
sourceOff);
if (retryCount)
printf(_(" %d retries"), retryCount);
fflush(stdout);
}
}
if (dolf)
putchar('\n');
return failed;
}
static int
netwareCopyFile(int fdIn, int fdOut,
const char *source,
const char *destination,
mode_t sourceMode, int dofile)
{
NWCCODE err;
struct nw_info_struct2 sfi, dfi;
u_int8_t sfhandle[6];
u_int8_t dfhandle[6];
NWCONN_HANDLE scon, dcon;
struct NWCCRootEntry sentry, dentry;
int retValue = 2;
int ncopy = 0;
err = ncp_open_fd(fdIn, &scon);
if (err) {
dfprintf(stderr, _("%s: Unable to open source file: %s\n"),
ProgramName, strnwerror(err));
scon = NULL;
goto srcDetermined;
}
err = NWCCGetConnInfo(scon, NWCC_INFO_ROOT_ENTRY,
sizeof(sentry), &sentry);
if (err) {
dfprintf(stderr, _("%s: Unable to stat source file: %s\n"),
ProgramName, strnwerror(err));
ncp_close(scon);
scon = NULL;
goto srcDetermined;
}
if (dofile) {
err = ncp_ns_open_create_entry(scon, NW_NS_DOS, SA_ALL | 0x48,
1, sentry.volume, sentry.dirEnt, NULL, 0,
-1, OC_MODE_OPEN, 0, AR_READ_ONLY,
RIM_ALL,
&sfi, sizeof(sfi), NULL, NULL, sfhandle);
} else {
err = ncp_ns_obtain_entry_info(scon, NW_NS_DOS, SA_ALL | 0x48,
1, sentry.volume, sentry.dirEnt, NULL, 0,
NW_NS_DOS, RIM_ALL,
&sfi, sizeof(sfi));
}
if (err) {
dfprintf(stderr, _("%s: Unable to open source file: %s\n"),
ProgramName, strnwerror(err));
ncp_close(scon);
scon = NULL;
goto srcDetermined;
}
srcDetermined:;
{
/* Grrr... All kernels since 2.1.43 up to 2.2.10 & 2.3.9 corrupts
DosDirNum on file open :-( stat() fixes it, fstat does not */
struct stat statbuf;
stat(destination, &statbuf);
}
err = ncp_open_fd(fdOut, &dcon);
if (err) {
dfprintf(stderr, _("%s: Unable to open output file: %s\n"),
ProgramName, strnwerror(err));
dcon = NULL;
goto dstDetermined;
}
err = NWCCGetConnInfo(dcon, NWCC_INFO_ROOT_ENTRY,
sizeof(dentry), &dentry);
if (err) {
dfprintf(stderr, _("%s: Unable to stat destination file: %s\n"),
ProgramName, strnwerror(err));
ncp_close(dcon);
dcon = NULL;
goto dstDetermined;
}
if (dofile) {
err = ncp_ns_open_create_entry(dcon, NW_NS_DOS, SA_ALL,
1, dentry.volume, dentry.dirEnt, NULL, 0,
-1, OC_MODE_OPEN, 0, AR_WRITE_ONLY,
0,
&dfi, sizeof(dfi), NULL, NULL, dfhandle);
if (err) {
dfprintf(stderr, _("%s: Unable to open destination file: %s\n"),
ProgramName, strnwerror(err));
ncp_close(dcon);
dcon = NULL;
goto dstDetermined;
}
}
dstDetermined:;
if (scon && dcon) {
NWCONN_NUM snum, dnum;
if (!NWGetConnectionNumber(scon, &snum)
&& !NWGetConnectionNumber(dcon, &dnum)
&& (snum == dnum)) {
/* there is a chance that it is same connection */
unsigned char saddr[100];
unsigned char daddr[100];
NWCCTranAddr srec = {0, sizeof(saddr), saddr};
NWCCTranAddr drec = {0, sizeof(daddr), daddr};
err = NWCCGetConnInfo(scon, NWCC_INFO_TRAN_ADDR, sizeof(srec), &srec);
if (!err) err = NWCCGetConnInfo(dcon, NWCC_INFO_TRAN_ADDR, sizeof(drec), &drec);
if (!err) {
if ((srec.type == drec.type)
&& (srec.len == drec.len)
&& !memcpy(saddr, daddr, srec.len)) {
ncopy = 1;
}
} else {
err = NWCCGetConnInfo(scon, NWCC_INFO_SERVER_NAME, sizeof(saddr), saddr);
if (!err) err = NWCCGetConnInfo(dcon, NWCC_INFO_SERVER_NAME, sizeof(daddr), daddr);
if (!err) {
ncopy = !strcmp(saddr, daddr);
}
}
if (ncopy) {
ncp_close(scon);
scon = dcon;
}
}
}
if (dofile) {
/* Set globals in case a signal happens while copying */
CurrentConn = dcon;
CurrentFile = dfhandle;
retValue = copyNWNW(_("NetWare copy"),
fdIn, scon, sfhandle, source,
fdOut, dcon, dfhandle, destination);
if (dcon) {
if (ncp_close_file(dcon, dfhandle) != 0)
{
fprintf(stderr, _("%s: Close failed for %s\n"), ProgramName, destination);
retValue = 1;
}
CurrentFile = NULL;
}
if (scon) {
if (ncp_close_file(scon, sfhandle) != 0)
{
fprintf(stderr, _("%s: Close failed for %s\n"), ProgramName, source);
if (!retValue)
retValue = 1;
}
}
if ((optMAC || optMACinRsrc) && !retValue) {
NWCONN_HANDLE sMAC = NULL;
NWCONN_HANDLE dMAC = NULL;
int sFD = -1;
int dFD = -1;
const char* sourceRsrc = source;
const char* destinationRsrc = destination;
char sourceB[MAXPATHLEN+1];
char destinationB[MAXPATHLEN+1];
if (scon && optMAC) {
struct nw_info_struct2 srcMAC;
err = ncp_ns_obtain_entry_info(scon, NW_NS_DOS, SA_ALL,
1, sentry.volume, sentry.dirEnt, NULL, 0,
NW_NS_MAC, RIM_DIRECTORY,
&srcMAC, sizeof(srcMAC));
if (err) {
/* it is not fatal... it is user problem... */
dfprintf(stderr, _("MAC namespace is not supported on source %s: %s\n"), source, strnwerror(err));
/* look for .rsrc */
} else {
err = ncp_ns_open_create_entry(scon, NW_NS_MAC, SA_ALL,
1, srcMAC.Directory.volNumber, srcMAC.Directory.dirEntNum, NULL, 0,
1, OC_MODE_OPEN, 0, AR_READ_ONLY,
0,
NULL, 0, NULL, NULL, sfhandle);
if (err) {
/* NWE_SERVER_FAILURE is returned on NSS volume if there does not exist resource fork yet... */
/* ignore silently */
if (err != NWE_SERVER_FAILURE) {
fprintf(stderr, _("Unable to open MAC resource fork on source %s: %s\n"), source, strnwerror(err));
retValue = 1;
}
/* no MAC... */
goto nomac;
}
sMAC = scon;
}
}
if (!sMAC) {
const char* p;
char* d;
if (!optMACinRsrc)
goto nomac;
p = strrchr(source, '/');
if (p)
p = p + 1;
else
p = source;
memcpy(sourceB, source, p - source);
d = sourceB + (p - source);
d = stpcpy(d, RSRCSUBDIR);
strcpy(d, p);
sFD = open(sourceB, O_RDONLY);
if (sFD == -1)
goto nomac;
sourceRsrc = sourceB;
}
if (dcon && optMAC) {
struct nw_info_struct2 dstMAC;
err = ncp_ns_obtain_entry_info(dcon, NW_NS_DOS, SA_ALL,
1, dentry.volume, dentry.dirEnt, NULL, 0,
NW_NS_MAC, RIM_DIRECTORY,
&dstMAC, sizeof(dstMAC));
if (err) {
/* possible information loss... maybe fallback to create .rsrc/name */
fprintf(stderr, _("MAC namespace is not supported on destination %s: %s\n"), destination, strnwerror(err));
retValue = 1;
} else {
err = ncp_ns_open_create_entry(dcon, NW_NS_MAC, SA_ALL,
1, dstMAC.Directory.volNumber, dstMAC.Directory.dirEntNum, NULL, 0,
1, OC_MODE_OPEN, 0, AR_WRITE_ONLY,
0,
NULL, 0, NULL, NULL, dfhandle);
if (err) {
/* it is fatal... */
fprintf(stderr, _("Unable to create MAC resource fork on destination %s: %s\n"), destination, strnwerror(err));
retValue = 1;
goto nomac;
}
dMAC = dcon;
}
}
if (!dMAC) {
const char* p;
char* d;
if (!optMACinRsrc) {
/* try to create .rsrc/destination */
fprintf(stderr, _("Unable to copy MAC resource fork of %s because of %s does not support resource forks\n"),
source,
destination);
retValue = 1;
goto nomac;
}
p = strrchr(destination, '/');
if (p)
p = p + 1;
else
p = destination;
memcpy(destinationB, destination, p - destination);
d = destinationB + (p - destination);
d = stpcpy(d, RSRCSUBDIR);
strcpy(d, p);
dFD = open(destinationB, O_CREAT | O_TRUNC | O_WRONLY, sourceMode);
if (dFD == -1) {
if (errno == ENOENT) {
*(d-1) = 0;
mkdir(destinationB, sourceMode | ((sourceMode & 0444) >> 2) | ((sourceMode & 0222) >> 1));
*(d-1) = '/';
dFD = open(destinationB, O_CREAT | O_TRUNC | O_WRONLY, sourceMode);
}
if (dFD == -1) {
int err2 = errno;
fprintf(stderr, _("Unable to copy MAC resource fork: %s: %s\n"),
destinationB,
strerror(err2));
retValue = 1;
goto nomac;
}
}
destinationRsrc = destinationB;
}
retValue = copyNWNW(_("NetWare copy (resource fork)"),
sFD, sMAC, sfhandle, sourceRsrc,
dFD, dMAC, dfhandle, destinationRsrc);
nomac:;
if (dMAC)
ncp_close_file(dMAC, dfhandle);
if (dFD != -1)
close(dFD);
if (sMAC)
ncp_close_file(sMAC, sfhandle);
if (sFD != -1)
close(sFD);
}
} else {
retValue = 0;
}
if ((optTrustees) && (retValue == 0) && scon) {
u_int32_t iter = 0;
while (1) {
TRUSTEE_INFO ts[128];
unsigned int tstcount = 128;
unsigned int sptr;
unsigned int dptr;
if (ncp_ns_trustee_scan(scon, NW_NS_DOS, SA_ALL,
1, sentry.volume, sentry.dirEnt,
NULL, 0,
&iter, ts, &tstcount))
break;
if (tstcount == 0)
break;
if (!dcon) {
fprintf(stderr, _("Cannot set trustees on %s because of %s\n"),
destination,
_("not NetWare filesystem"));
break;
}
dptr = 0;
for (sptr = 0; sptr < tstcount; sptr++) {
if (xlatid(scon, ts[sptr].objectID, dcon, &ts[dptr].objectID)) {
ts[dptr].objectRights = ts[sptr].objectRights;
dptr++;
}
}
if (dptr) {
err = ncp_ns_trustee_add(dcon, NW_NS_DOS, 0x0016,
1, dentry.volume, dentry.dirEnt,
NULL, 0,
ts, dptr, 0xFFFF);
if (err) {
fprintf(stderr, _("Cannot set trustees on %s because of %s\n"),
destination,
strnwerror(err));
}
}
}
}
if ((optOwner || optFlags || optTimes) && (retValue == 0) && scon) {
if (!dcon) {
fprintf(stderr, _("Cannot set file attributes on %s because of %s\n"),
destination,
_("not NetWare filesystem"));
} else {
struct ncp_dos_info nwmd;
u_int32_t mask = 0;
memset(&nwmd, 0, sizeof(nwmd));
if (optOwner) {
if (xlatid(scon, sfi.Creation.ID, dcon, &nwmd.Creation.ID))
mask |= DM_CREATOR_ID;
if (xlatid(scon, sfi.Modify.ID, dcon, &nwmd.Modify.ID))
mask |= DM_MODIFIER_ID;
if (xlatid(scon, sfi.Archive.ID, dcon, &nwmd.Archive.ID))
mask |= DM_ARCHIVER_ID;
}
if (optFlags) {
nwmd.Attributes = sfi.Attributes.Attributes;
nwmd.Rights.Grant = sfi.Rights;
nwmd.Rights.Revoke = ~sfi.Rights;
mask |= DM_ATTRIBUTES | DM_INHERITED_RIGHTS_MASK;
}
if (optTimes) {
nwmd.Creation.Date = sfi.Creation.Date;
nwmd.Creation.Time = sfi.Creation.Time;
nwmd.Modify.Date = sfi.Modify.Date;
nwmd.Modify.Time = sfi.Modify.Time;
nwmd.Archive.Date = sfi.Archive.Date;
nwmd.Archive.Time = sfi.Archive.Time;
nwmd.LastAccess.Date = sfi.LastAccess.Date;
mask |= DM_CREATE_DATE | DM_CREATE_TIME |
DM_MODIFY_DATE | DM_MODIFY_TIME |
DM_ARCHIVE_DATE | DM_ARCHIVE_TIME |
DM_LAST_ACCESS_DATE;
}
if (mask) {
err = ncp_ns_modify_entry_dos_info(
dcon, NW_NS_DOS, SA_ALL,
1, dentry.volume, dentry.dirEnt, NULL, 0,
mask, &nwmd);
if (err) {
fprintf(stderr, _("Cannot set file attributes on %s because of %s\n"),
destination,
strnwerror(err));
}
}
}
}
/* Clear signal handling globals */
CurrentConn = NULL;
if (dcon)
ncp_close(dcon);
if (scon && !ncopy)
ncp_close(scon);
return retValue;
}
/****************************************************************************
* Decides whether to use the traditional file copy or the netware remote
* file copy.
*/
static int
selectFn(const struct dirent* entry) {
if (entry->d_name[0] == '.') {
if (entry->d_name[1] == 0)
return 0;
if (entry->d_name[1] == '.' && entry->d_name[2] == 0)
return 0;
}
return 1;
}
static char sourcePath[MAXPATHLEN * 2];
static char destinationPath[MAXPATHLEN * 2];
static int
copyFiles(void)
{
int oldUMask;
int retVal;
int fdIn, fdOut;
struct stat statBuf;
#ifdef NCOPY_DEBUG
printf(_("Param Src '%s'\n"
"Param Dest '%s'\n"), sourcePath, destinationPath);
#endif
fdIn = open(sourcePath, O_RDONLY);
if (fdIn == -1)
{
fprintf(stderr, _("%s: Cannot open %s, %s\n"), ProgramName, sourcePath,
strerror(errno));
return 1;
}
if (fstat(fdIn, &statBuf))
{
fprintf(stderr, _("%s: Cannot stat %s, %s\n"), ProgramName, sourcePath,
strerror(errno));
close(fdIn);
return 1;
}
if (S_ISDIR(statBuf.st_mode))
{
int ecode = 0;
if (optRecursive) {
struct stat dstBuf;
struct dirent** entries;
int rent;
int i;
char *dpx;
char *spx;
if (!stat(destinationPath, &dstBuf)) {
if (!S_ISDIR(dstBuf.st_mode)) {
fprintf(stderr, _("%s: %s: %s\n"), ProgramName, destinationPath, _("not a directory"));
close(fdIn);
return 0;
}
oldUMask = umask(0);
if (dstBuf.st_mode != statBuf.st_mode) {
if (chmod(destinationPath, statBuf.st_mode & 0777)) {
fprintf(stderr, _("%s: Cannot chmod %s: %s\n"), ProgramName,
destinationPath, strerror(errno));
}
}
} else {
oldUMask = umask(0);
if (mkdir(destinationPath, statBuf.st_mode & 0777)) {
fprintf(stderr, _("%s: Cannot create %s: %s\n"), ProgramName, destinationPath,
strerror(errno));
umask(oldUMask);
close(fdIn);
return 1;
}
}
fdOut = open(destinationPath, O_RDONLY);
if (fdOut == -1)
{
fprintf(stderr, _("%s: Cannot create %s, %s\n"), ProgramName, destinationPath,
strerror(errno));
umask(oldUMask);
close(fdIn);
return 1;
}
retVal = netwareCopyFile(fdIn, fdOut, sourcePath, destinationPath, statBuf.st_mode, 0);
close(fdOut);
umask(oldUMask);
close(fdIn);
rent = scandir(sourcePath, &entries, selectFn, alphasort);
if (rent < 0) {
fprintf(stderr, _("%s: Cannot read %s: %s\n"), ProgramName, sourcePath, strerror(errno));
umask(oldUMask);
close(fdIn);
return 1;
}
dpx = destinationPath + strlen(destinationPath);
*dpx = '/';
spx = sourcePath + strlen(sourcePath);
*spx = '/';
for (i = 0; i < rent; i++) {
int tmp;
strcpy(dpx + 1, entries[i]->d_name);
strcpy(spx + 1, entries[i]->d_name);
tmp = copyFiles();
if (tmp)
ecode = tmp;
if (tmp > 1)
break;
}
*spx = 0;
*dpx = 0;
umask(oldUMask);
close(fdIn);
} else {
fprintf(stderr, _("%s: %s: omitting directory\n"), ProgramName, sourcePath);
close(fdIn);
}
return ecode;
}
oldUMask = umask(0);
if (optOnlyChanged) {
struct stat statBufOut;
if (!stat(destinationPath, &statBufOut)) {
if (statBuf.st_size == statBufOut.st_size &&
statBuf.st_mtime == statBufOut.st_mtime) {
printf("%s and %s are same\n", sourcePath, destinationPath);
fdOut = open(destinationPath, O_RDONLY);
if (fdOut == -1) {
goto canopen;
}
retVal = netwareCopyFile(fdIn, fdOut, sourcePath, destinationPath, statBuf.st_mode, 0);
goto cpok;
}
}
}
fdOut = open(destinationPath, O_CREAT | O_TRUNC | O_WRONLY, statBuf.st_mode);
if (fdOut == -1)
{
canopen:;
fprintf(stderr, _("%s: Cannot create %s, %s\n"), ProgramName, destinationPath,
strerror(errno));
umask(oldUMask);
close(fdIn);
return 1;
}
retVal = netwareCopyFile(fdIn, fdOut, sourcePath, destinationPath, statBuf.st_mode, 1);
cpok:;
close(fdOut);
umask(oldUMask);
close(fdIn);
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...
d
* (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(void)
{
return copyFiles();
}
/****************************************************************************
* 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;
destination = argv[argc - 1]; /* get LAST argument */
for (loop = 0; loop < (argc - 1); loop++)
{ /* all file arguments, but last */
strncpy(destinationPath, destination, MAXPATHLEN);
destinationPath[MAXPATHLEN] = 0;
if ((argc > 2) || (!notDir(argv[argc - 1])))
{ /* destination is a dir */
if (*destinationPath != '/' || *(destinationPath + 1))
strcat(destinationPath, "/");
baseNamePtr = myBaseName(argv[loop]); /* get the file name */
strcat(destinationPath, baseNamePtr); /* add it on end of directory */
}
strncpy(sourcePath, argv[loop], MAXPATHLEN);
sourcePath[MAXPATHLEN] = 0;
copyStatus = copyRealPaths(); /* 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 (CurrentFile)
{
/* 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) != 0)
{
fprintf(stderr, _("%s: unclean close of output file"), ProgramName);
}
CurrentFile = NULL;
}
if (CurrentConn) {
ncp_close(CurrentConn);
CurrentConn = NULL;
}
exit(128 + sigNumber);
}
/****************************************************************************
* We'll trap Hangup, Interrupt, Quit or Terminate
*/
static int
trapSignals(void)
{
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 = strrchr(argv[0], '/');
if (ProgramName)
ProgramName++;
else
ProgramName = argv[0];
setlocale(LC_ALL, "");
bindtextdomain(NCPFS_PACKAGE, LOCALEDIR);
textdomain(NCPFS_PACKAGE);
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;
returnCode = handleFileArgs(argc - optind, argv + optind);
return returnCode;
}