diff --git a/.downloads/ncpfs-0.20.tgz b/.downloads/ncpfs-0.20.tgz new file mode 100644 index 0000000..03532ed Binary files /dev/null and b/.downloads/ncpfs-0.20.tgz differ diff --git a/Changes b/Changes index 9af8080..ab46edc 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,19 @@ I only began this file with ncpfs-0.12. If you're interested in older -versions, you can find them on linux01.gwdg.de:/pub/ncpfs/old. +versions, you can find them on ftp.gwdg.de:/pub/linux/misc/ncpfs/old. + +ncpfs-0.19 -> ncpfs-0.20 +- Changed the home site for ncpfs from linux01.gwdg.de:/pub/ncpfs + to ftp.gwdg.de:/pub/linux/misc/ncpfs. linux01 will remain available, + but we would like to reduce the load on that machine. Sites + mirroring linux01 please redirect your mirror software to + ftp.gwdg.de. Thanks. +- Removed a bug in ncplib.c that made slist require a full + login. Thanks to ndt1001@chu.cam.ac.uk (Neil Turton) for the hint. +- The first real user contribution: ncopy by Brian G. Reid + (breid@tim.com) and Tom C. Henderson (thenderson@tim.com). Many + thanks to you! If you find bugs in ncopy, tell them, not me ;-) +- Handle expired passwords. Thanks to "Mathew Lim" + for the hint. ncpfs-0.18 -> ncpfs-0.19 diff --git a/Makefile b/Makefile index bfc215e..2200c63 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # Makefile for the linux ncp-filesystem routines. # -VERSION = 0.19 +VERSION = 0.20 TOPDIR = $(shell pwd) BINDIR = /usr/local/bin diff --git a/ipxdump/ipxparse.c b/ipxdump/ipxparse.c index 837a2a2..7d3dd5c 100644 --- a/ipxdump/ipxparse.c +++ b/ipxdump/ipxparse.c @@ -275,6 +275,124 @@ void handle_ncp (struct sockaddr_ipx *source, switch(rq->function) { + case 20: + printf("fn: %-3d\n", rq->function); + printf("Get File Server Date and Time\n"); + break; + case 21: + printf("fn: %-3d, subfn: %-3d\n", + rq->function, data[2]); + switch(data[2]) + { + case 0: + printf("Send Broadcast Message\n"); + break; + case 1: + printf("Get Broadcast Message\n"); + break; + } + data += 3; + data_length -= 3; + break; + case 22: + printf("fn: %-3d, subfn: %-3d\n", + rq->function, data[2]); + switch(data[2]) + { + case 00: + printf("Set Directory Handle\n"); + break; + case 01: + printf("Get Directory Path\n"); + break; + case 18: + printf("Allocate Permanent Dir Handle\n"); + break; + case 20: + printf("Deallocate Directory Handle\n"); + break; + case 21: + printf("Get Volume Info with handle\n"); + break; + } + data += 3; + data_length -= 3; + break; + case 23: + printf("fn: %-3d, subfn: %-3d\n", rq->function, + data[2]); + switch(data[2]) + { + case 17: + printf("Get Fileserver Information\n"); + break; + case 23: + printf("Get Crypt Key\n"); + break; + case 24: + printf("Encrypted Login\n"); + break; + case 28: + printf("Get Connection Information\n"); + break; + case 53: + printf("Get Bindery Object ID\n"); + break; + case 55: + printf("Scan Bindery Object\n"); + break; + case 61: + printf("Read Property Value\n"); + break; + case 62: + printf("Write Property Value\n"); + break; + case 70: + printf("Get Bindery Access Level\n"); + break; + } + + data += 3; + data_length -= 3; + break; + case 24: + printf("fn: %-3d\n", rq->function); + printf("End of Job\n"); + break; + case 34: + printf("fn: %-3d, subfn: %-3d\n", rq->function, + data[2]); + data += 3; + data_length -= 3; + break; + case 62: + printf("fn: %-3d\n", rq->function); + printf("File Search Initialize\n"); + break; + case 63: + printf("fn: %-3d\n", rq->function); + printf("File Search Continue\n"); + break; + case 64: + printf("fn: %-3d\n", rq->function); + printf("Search for a file\n"); + break; + case 66: + printf("fn: %-3d\n", rq->function); + printf("Close File\n"); + break; + case 72: + printf("fn: %-3d\n", rq->function); + printf("Read from File\n"); + break; + case 73: + printf("fn: %-3d\n", rq->function); + printf("Write to File\n"); + break; + case 75: + printf("fn: %-3d\n", rq->function); + printf("Set File Time Date Stamp\n"); + break; case 87: printf("fn: %-3d, subfn: %-3d\n", rq->function, data[0]); @@ -311,86 +429,6 @@ void handle_ncp (struct sockaddr_ipx *source, data += 1; data_length -= 1; break; - case 21: - printf("fn: %-3d, subfn: %-3d\n", - rq->function, data[2]); - switch(data[2]) - { - case 0: - printf("Send Broadcast Message\n"); - break; - case 1: - printf("Get Broadcast Message\n"); - break; - } - data += 3; - data_length -= 3; - break; - case 22: - printf("fn: %-3d, subfn: %-3d\n", - rq->function, data[2]); - switch(data[2]) - { - case 21: - printf("Get Volume Info with handle\n"); - break; - } - data += 3; - data_length -= 3; - break; - case 23: - printf("fn: %-3d, subfn: %-3d\n", rq->function, - data[2]); - switch(data[2]) - { - case 17: - printf("Get Fileserver Information\n"); - break; - case 28: - printf("Get Connection Information\n"); - break; - case 55: - printf("Scan Bindery Object\n"); - break; - } - - data += 3; - data_length -= 3; - break; - case 24: - printf("fn: %-3d\n", rq->function); - printf("End of Job\n"); - break; - case 34: - printf("fn: %-3d, subfn: %-3d\n", rq->function, - data[2]); - data += 3; - data_length -= 3; - break; - case 62: - printf("fn: %-3d\n", rq->function); - printf("File Search Initialize\n"); - break; - case 63: - printf("fn: %-3d\n", rq->function); - printf("File Search Continue\n"); - break; - case 64: - printf("fn: %-3d\n", rq->function); - printf("Search for a file\n"); - break; - case 66: - printf("fn: %-3d\n", rq->function); - printf("Close File\n"); - break; - case 73: - printf("fn: %-3d\n", rq->function); - printf("Write to File\n"); - break; - case 75: - printf("fn: %-3d\n", rq->function); - printf("Set File Time Date Stamp\n"); - break; default: printf("fn: %-3d\n", rq->function); } diff --git a/man/Makefile b/man/Makefile index 10ecff5..474ad75 100644 --- a/man/Makefile +++ b/man/Makefile @@ -1,4 +1,4 @@ -MAN1= slist nprint pqlist nsend pserver +MAN1= slist nprint pqlist nsend pserver ncopy MAN5= nwclient MAN8= ncpmount ncpumount ipx_configure ipx_interface ipx_internal_net \ ipx_route nwmsg diff --git a/man/ncopy.1 b/man/ncopy.1 new file mode 100644 index 0000000..b1b99c6 --- /dev/null +++ b/man/ncopy.1 @@ -0,0 +1,96 @@ +.\" +.\" Man page for the ncopy program +.\" +.TH NCOPY 1 17/03/1996 ncopy ncopy +.SH NAME +ncopy \- NetWare file copy + +.SH SYNOPSIS +.B ncopy -V + +.B ncopy +[ +.B -vn +] +[ +.B -s +.I amount +] +.B file destinationfile|directory + +.B ncopy +[ +.B -vn +] +[ +.B -s +.I amount +] +.B file1 +[ +.B file2 ... +] +.B directory + +.SH DESCRIPTION +With +.B ncopy +you can copy files to different locations on a single NetWare file +server without generating excess network traffic. The program uses +a NetWare function to do the copy rather than transferring the file +across the network for both the read and write. + +If the last argument is a directory, +.B ncopy +will copy the source file(s) into the directory. If only two files +are given and the last argument is not a directory, it will copy the +source file to the destination file. + +If the source and destination files are not on the same NetWare server +(or are not on NetWare servers at all), +.B ncopy +will do a normal file copy. +.SH OPTIONS +.B -V +.RS 3 +Show version number and exit +.RE + +.B -v +.RS 3 +Verbose copy. Will show current file and percentage completion. +.RE + +.B -n +.RS 3 +Nice NetWare copy. Will sleep for a second between copying blocks on +the NetWare server. Gives other people a chance to do some work on +the NetWare server when you are copying large files. This has no +effect if you are not copying on a NetWare server. +.RE + +.B -s +.I amount +.RS 3 +Nice time slice factor. Used in conjunction with the +.B -n +option, this specifies the number of 100K blocks to copy before sleeping. +Default is 10. (1 Megabyte) +.RE + +.SH BUGS +.B ncopy +cannot recurse into directories. +Does not work on NetWare volumes mounted with the +.I -V +option to +.B ncpmount. + +.SH "SEE ALSO" +.B ncpmount(8), ncpumount(8) + +.SH CREDITS +ncopy was written by Brian G. Reid (breid@tim.com) and +Tom C. Henderson (thenderson@tim.com). +Many thanks to Volker Lendecke (lendecke@namu01.gwdg.de) for the ncpfs +and ncplib which made ncopy possible. diff --git a/ncpfs-0.19.lsm b/ncpfs-0.20.lsm similarity index 77% rename from ncpfs-0.19.lsm rename to ncpfs-0.20.lsm index 7917950..f99ed77 100644 --- a/ncpfs-0.19.lsm +++ b/ncpfs-0.20.lsm @@ -1,7 +1,7 @@ Begin3 Title: ncpfs -Version: 0.19 -Entered-date: 22. March 1996 +Version: 0.20 +Entered-date: 25. March 1996 Description: With ncpfs you can mount volumes of your netware server under Linux. You can also print to netware print queues and spool netware print queues to the @@ -11,9 +11,9 @@ Description: With ncpfs you can mount volumes of your netware Keywords: filesystem ncp novell netware printing Author: lendecke@namu01.gwdg.de (Volker Lendecke) Maintained-by: lendecke@namu01.gwdg.de (Volker Lendecke) -Primary-site: linux01.gwdg.de:/pub/ncpfs +Primary-site: ftp.gwdg.de:/pub/linux/misc/ncpfs Alternate-site: sunsite.unc.edu:/pub/system/Filesystems/ - ~103k ncpfs-0.19.tgz - ~ 1k ncpfs-0.19.lsm + ~111k ncpfs-0.20.tgz + ~ 1k ncpfs-0.20.lsm Copying-policy: GPL End diff --git a/util/Makefile b/util/Makefile index 1a5af00..4c856b7 100644 --- a/util/Makefile +++ b/util/Makefile @@ -2,7 +2,7 @@ # Makefile for the linux ncp-filesystem routines. # -USERUTILS = slist pqlist nwfsinfo pserver nprint nsend +USERUTILS = slist pqlist nwfsinfo pserver nprint nsend ncopy UIDUTILS = ncpmount ncpumount SBINUTILS = nwmsg diff --git a/util/ncopy.c b/util/ncopy.c new file mode 100644 index 0000000..918c36d --- /dev/null +++ b/util/ncopy.c @@ -0,0 +1,849 @@ +/**************************************************************************** + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/util/ncplib.c b/util/ncplib.c index 596eb30..11c02cc 100644 --- a/util/ncplib.c +++ b/util/ncplib.c @@ -591,6 +591,8 @@ ncp_mount_request(struct ncp_conn *conn, int function) return result; } + conn->completion = reply->completion_code; + conn->conn_status = reply->connection_state; conn->ncp_reply_size = result - sizeof(struct ncp_reply_header); if ((reply->completion_code != 0) && (conn->verbose != 0)) @@ -837,9 +839,6 @@ ncp_find_fileserver(const char *server_name, long *err) return &result; } - - - static long ncp_open_temporary(struct ncp_conn *conn, const struct ncp_conn_spec *spec) @@ -887,6 +886,8 @@ ncp_find_permanent(const struct ncp_conn_spec *spec) int mount_fid; struct ncp_fs_info i; + initialize_NCPL_error_table(); + if ((mtab = fopen(MOUNTED, "r")) == NULL) { return NULL; @@ -972,6 +973,8 @@ ncp_open(const struct ncp_conn_spec *spec, long *err) { struct ncp_conn *result; + initialize_NCPL_error_table(); + result = malloc(sizeof(struct ncp_conn)); if (result == NULL) @@ -1001,6 +1004,8 @@ ncp_open_mount(const char *mount_point, long *err) { struct ncp_conn *result; + initialize_NCPL_error_table(); + if (strlen(mount_point) >= sizeof(result->mount_point)) { *err = ENAMETOOLONG; @@ -1297,6 +1302,8 @@ ncp_find_conn_spec(const char *server, const char *user, const char *password, FILE *nwc; struct ncp_conn_spec *nwc_ent; + initialize_NCPL_error_table(); + *err = 0; memzero(spec); spec.uid = getuid(); @@ -1500,12 +1507,20 @@ ncp_initialize_as(int *argc, char **argv, i += 1; } - if ((spec = ncp_find_conn_spec(server, user, password, - getuid(), err)) == NULL) - { - return NULL; - } + spec = ncp_find_conn_spec(server, user, password, getuid(), err); + if (spec == NULL) + { + if (login_necessary != 0) + { + return NULL; + } + else + { + return ncp_open(NULL, err); + } + } + spec->login_type = login_type; if (login_necessary == 0) @@ -1952,8 +1967,27 @@ ncp_login_object(struct ncp_conn *conn, } if ((result = ncp_login_encrypted(conn, &user, - ncp_key, password)) != 0) { - return result; + ncp_key, password)) != 0) + { + struct nw_property p; + struct ncp_prop_login_control *l + = (struct ncp_prop_login_control *)&p; + + if (conn->completion != NCP_GRACE_PERIOD) + { + return result; + } + + fprintf(stderr, "Your password has expired\n"); + + if ((result = ncp_read_property_value(conn, NCP_BINDERY_USER, + username, 1, + "LOGIN_CONTROL", + &p)) == 0) + { + fprintf(stderr, "You have %d login attempts left\n", + l->GraceLogins); + } } return 0; } @@ -2858,9 +2892,9 @@ ncp_copy_file(struct ncp_conn *conn, ncp_add_byte(conn, 0); /* reserved */ ncp_add_mem(conn, source_file, 6); ncp_add_mem(conn, target_file, 6); - ncp_add_dword(conn, source_offset); - ncp_add_dword(conn, target_offset); - ncp_add_dword(conn, count); + ncp_add_dword(conn, htonl(source_offset)); + ncp_add_dword(conn, htonl(target_offset)); + ncp_add_dword(conn, htonl(count)); if ((result = ncp_request(conn, 74)) != 0) { @@ -2868,7 +2902,7 @@ ncp_copy_file(struct ncp_conn *conn, return result; } - *copied_count = ncp_reply_dword(conn, 0); + *copied_count = ntohl(ncp_reply_dword(conn, 0)); ncp_unlock_conn(conn); return 0; } diff --git a/util/ncplib.h b/util/ncplib.h index 1115237..7a3633d 100644 --- a/util/ncplib.h +++ b/util/ncplib.h @@ -168,6 +168,26 @@ long ncp_scan_bindery_object(struct ncp_conn *conn, __u32 last_id, __u16 object_type, char *search_string, struct ncp_bindery_object *target); + +struct ncp_prop_login_control { + __u8 AccountExpireDate[3] __attribute__ ((packed)); + __u8 Disabled __attribute__ ((packed)); + __u8 PasswordExpireDate[3] __attribute__ ((packed)); + __u8 GraceLogins __attribute__ ((packed)); + __u16 PasswordExpireInterval __attribute__ ((packed)); + __u8 MaxGraceLogins __attribute__ ((packed)); + __u8 MinPasswordLength __attribute__ ((packed)); + __u16 MaxConnections __attribute__ ((packed)); + __u8 ConnectionTimeMask[42] __attribute__ ((packed)); + __u8 LastLogin[6] __attribute__ ((packed)); + __u8 RestrictionMask __attribute__ ((packed)); + __u8 reserved __attribute__ ((packed)); + __u32 MaxDiskUsage __attribute__ ((packed)); + __u16 BadLoginCount __attribute__ ((packed)); + __u32 BadLoginCountDown __attribute__ ((packed)); + __u8 LastIntruder[8] __attribute__ ((packed)); +}; + long ncp_read_property_value(struct ncp_conn *conn, int object_type, const char *object_name, @@ -178,6 +198,9 @@ ncp_login_encrypted(struct ncp_conn *conn, const struct ncp_bindery_object *object, const unsigned char *key, const unsigned char *passwd); + +#define NCP_GRACE_PERIOD (0xdf) + long ncp_login_user(struct ncp_conn *conn, const unsigned char *username, diff --git a/util/ncpmount.c b/util/ncpmount.c index 16c60c3..89a5586 100644 --- a/util/ncpmount.c +++ b/util/ncpmount.c @@ -484,12 +484,42 @@ main(int argc, char *argv[]) exit(1); } - if ( ((conn = ncp_open_mount(mount_point, &err)) == NULL) - || ((err = ncp_login_user(conn, spec->user, spec->password)) != 0) - || ((err = ioctl(conn->mount_fid, NCP_IOC_CONN_LOGGED_IN, - NULL)) != 0)) + if ((conn = ncp_open_mount(mount_point, &err)) == NULL) { - com_err("ncpmount", err, "in login"); + com_err("ncpmount", err, "attempt to open mount point"); + umount(mount_point); + exit(1); + } + + if ((err = ncp_login_user(conn, spec->user, spec->password)) != 0) + { + struct nw_property p; + struct ncp_prop_login_control *l + = (struct ncp_prop_login_control *)&p; + + if (conn->completion != NCP_GRACE_PERIOD) + { + com_err("ncpmount", err, "in login"); + fprintf(stderr, "Login denied\n"); + ncp_close(conn); + umount(mount_point); + exit(1); + } + + fprintf(stderr, "Your password has expired\n"); + + if ((err = ncp_read_property_value(conn, NCP_BINDERY_USER, + spec->user, 1, + "LOGIN_CONTROL", &p)) == 0) + { + fprintf(stderr, "You have %d login attempts left\n", + l->GraceLogins); + } + } + + if ((err = ioctl(conn->mount_fid, NCP_IOC_CONN_LOGGED_IN, NULL)) != 0) + { + com_err("ncpmount", err, "in logged_indication"); ncp_close(conn); umount(mount_point); exit(1);