1197 lines
31 KiB
C
1197 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 {
|
|
char saddrs[100];
|
|
char daddrs[100];
|
|
|
|
err = NWCCGetConnInfo(scon, NWCC_INFO_SERVER_NAME, sizeof(saddrs), saddrs);
|
|
if (!err) err = NWCCGetConnInfo(dcon, NWCC_INFO_SERVER_NAME, sizeof(daddrs), daddrs);
|
|
if (!err) {
|
|
ncopy = !strcmp(saddrs, daddrs);
|
|
}
|
|
}
|
|
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 __attribute__((noreturn))
|
|
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;
|
|
}
|