[Using an external tar program instead of implementing something similar in CVS (or a general-purpose hook) is not a good idea, IMHO. Needs test cases. Haven't looked at it in much detail. -kingdon] To: bug-cvs@gnu.org Subject: [Patches (again): Client/server wrapper support] Cc: bbum@codefab.com Date: Mon, 13 Apr 98 18:20:02 -0700 From: Wilfredo Sanchez Add client/server wrapper support. This uses tar to do the transfer. Code is based on patches from Bill Bumgarner; I did a lot of cleanup in hopes that it would make the code easier to accept. Still needs some work, but it is functional, which beats not at all. This is important to Rhapsody users, since CVS otherwise doesn't handle certain types of data, which we use commonly. -Fred wsanchez@apple.com diff -u -r cvs.Cyclic/ChangeLog cvs.wrappers/ChangeLog --- cvs.Cyclic/ChangeLog Mon Apr 6 22:02:57 1998 +++ cvs.wrappers/ChangeLog Mon Apr 13 17:52:43 1998 @@ -14,6 +14,10 @@ * TODO (190): Remove "failed to check out" from commit.c from lists of error messages suppressed by -q; it no longer is. +Wed Feb 11 23:32:45 PST 1998 Wilfredo Sanchez + + * configure.in: Add path check for tar. + 4 Feb 1998 Jim Kingdon * cvsnt.mak: The usual "because Visual C++ feels like it" diff -u -r cvs.Cyclic/configure.in cvs.wrappers/configure.in --- cvs.Cyclic/configure.in Mon Apr 6 22:03:00 1998 +++ cvs.wrappers/configure.in Mon Apr 13 17:53:11 1998 @@ -41,6 +41,9 @@ AC_PATH_PROG(perl_path, perl, no) AC_PATH_PROG(csh_path, csh, no) +AC_PATH_PROGS(tar, gnutar gtar tar, tar) +AC_DEFINE_UNQUOTED(TAR_PROGRAM, "${tar}") + AC_SYS_INTERPRETER if test X"$ac_cv_sys_interpreter" != X"yes" ; then # silly trick to avoid problems in AC macros... diff -u -r cvs.Cyclic/src/ChangeLog cvs.wrappers/src/ChangeLog --- cvs.Cyclic/src/ChangeLog Mon Apr 6 22:03:28 1998 +++ cvs.wrappers/src/ChangeLog Mon Apr 13 17:53:44 1998 @@ -364,6 +364,33 @@ * server.c (cvs_output_binary): Use OPEN_BINARY not _O_BINARY. +Wed Feb 11 23:32:45 PST 1998 Wilfredo Sanchez + + * Makefile.in: Add TAR = @tar@ + + * client.c: Added b.bum's client/server wrapper code, and then + did come cleanup on it. The code is not 100% there yet, but it's a + good start, and I think we're not doing anything bogus like hardcoding + paths, etc. Note that in the actual transfer code, it's wrapped with + noexec=0 (and reverts afterwards) so that the tar to allow cvs -nq update + to pass the tar file back and forth for comparison. I think some minor + tweaking can get this functionality to be happy for once. + + * cvs.h: add define for CVSWRAPPERTMP. + + * options.h.in: add #define WRAPPERS_USE_TAR_FOR_TRANSFER + + * options.h.in: add define for TAR_PROGRAM. + + * server.c: Added b.bum's client/server wrapper code, and then did come + cleanup as above with client.c. + + * server.h: Moved server_active outside of #ifdef SERVER_SUPPORT because + it is used outside of SERVER_SUPPORT in server.c. + + * wrapper.c: Added b.bum's client/server wrapper code, and then did come + cleanup as above with client.c. + Mon Feb 9 18:34:39 1998 Jim Kingdon Tweaks to Ian's checkin: diff -u -r cvs.Cyclic/src/Makefile.in cvs.wrappers/src/Makefile.in --- cvs.Cyclic/src/Makefile.in Mon Apr 6 22:03:26 1998 +++ cvs.wrappers/src/Makefile.in Mon Apr 13 17:54:31 1998 @@ -35,6 +35,9 @@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ +# Path to tar +TAR = @tar@ + LIBS = @LIBS@ SOURCES = add.c admin.c buffer.c checkin.c checkout.c classify.c client.c \ diff -u -r cvs.Cyclic/src/client.c cvs.wrappers/src/client.c --- cvs.Cyclic/src/client.c Mon Apr 6 22:03:52 1998 +++ cvs.wrappers/src/client.c Mon Apr 13 17:55:11 1998 @@ -1458,6 +1458,10 @@ char *scratch_entries = NULL; int bin; +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER + int tarred = 0; +#endif /* WRAPPERS_USE_TAR_FOR_TRANSFER */ + #ifdef UTIME_EXPECTS_WRITABLE int change_it_back = 0; #endif @@ -1515,12 +1519,24 @@ read_line (&mode_string); +read_line: read_line (&size_string); if (size_string[0] == 'z') { use_gzip = 1; size = atoi (size_string+1); } +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER + else if (size_string[0] == 'E') { + goto read_line; + } + else if (size_string[0] == 'T') + { + use_gzip = 0; + tarred = 1; + size = atoi (size_string+1); + } +#endif /* WRAPPERS_USE_TAR_FOR_TRANSFER */ else { use_gzip = 0; @@ -1712,7 +1728,49 @@ if (data->contents == UPDATE_ENTRIES_UPDATE) { +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER + if (tarred) { + char *tardir = CVSWRAPPERTMP; + char untarred_file[PATH_MAX]; + int retcode; + + int global_noexec = noexec; + + /* This is a hack to work around the fact that noexec is used + in places that I can't override. (eg. make_directory). When + the -n global flag is used in cvs, this stuff still needs to + happen, so let's override the flag temporarily. */ + noexec = 0; + + if (isdir(tardir)) + unlink_file_dir(tardir); + + make_directory(tardir); + + run_setup (TAR_PROGRAM); + run_arg ("-xf"); + run_arg (temp_filename); + run_arg ("-C"); + run_arg (tardir); + retcode = run_exec (DEVNULL, RUN_TTY, RUN_TTY, RUN_REALLY); + + strcpy(untarred_file, tardir); + strcat(untarred_file, "/"); + strcat(untarred_file, filename); + + unlink_file_dir(filename); /* remove existing directory */ + rename_file(untarred_file, filename); + unlink_file_dir(temp_filename); /* get rid of tarfile */ + unlink_file_dir(tardir); /* ... and output file */ + + /* Return to status quo. */ + noexec = global_noexec; + } else { +#endif /* WRAPPERS_USE_TAR_FOR_TRANSFER */ rename_file (temp_filename, filename); +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER + } +#endif /* WRAPPERS_USE_TAR_FOR_TRANSFER */ } else if (data->contents == UPDATE_ENTRIES_PATCH) { @@ -1944,6 +2002,21 @@ change_mode (filename, stored_mode, 1); stored_mode_valid = 0; +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER +#warning This is bogus. +/* 1) this variable will not be set unless the 'if (data->contents == + * UPDATE_ENTRIES_UPDATE || data->contents == UPDATE_ENTRIES_PATCH || + * data->contents == UPDATE_ENTRIES_RCS_DIFF)' evaluate to true and + * executes... + * + * 2) if tarred is set, then utime() is never executed... how does + * this affect the operation of cvs? + * + * 3) what else does this break? + */ + + if (!tarred) { +#endif if (stored_modtime_valid) { struct utimbuf t; @@ -1974,6 +2047,9 @@ stored_modtime_valid = 0; } +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER + } +#endif /* * Process the entries line. Do this after we've written the file, @@ -4585,6 +4661,76 @@ error (1, errno, "reading %s", short_pathname); mode_string = mode_to_string (sb.st_mode); + +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER + if ((sb.st_mode & S_IFMT) & S_IFDIR) { + /* file is a directory; send it over as a tarfile... */ + char *tarfile = CVSWRAPPERTMP; + struct stat tarfile_sb; + int retcode; + int newsize; + char *bufp; + int len; + char tmp[80]; + + int global_noexec = noexec; + + /* This is a hack to work around the fact that noexec is used + in places that I can't override. (eg. make_directory). When + the -n global flag is used in cvs, this stuff still needs to + happen, so let's override the flag temporarily. */ + noexec = 0; + + if (isfile(tarfile)) + unlink_file(tarfile); + + run_setup (TAR_PROGRAM); + run_arg ("-cf"); + run_arg (tarfile); + run_arg (file); + retcode = run_exec (DEVNULL, RUN_TTY, RUN_TTY, RUN_REALLY); + + if (stat(tarfile, &tarfile_sb) < 0) { + error(1, errno, "stat failed for %s", tarfile); + } + bufsize = tarfile_sb.st_size; + buf = xmalloc (bufsize); + + fd = open (tarfile, (O_RDONLY | OPEN_BINARY) ); + if (fd < 0) + error (1, errno, "openning %s", tarfile); + + bufp = buf; + while ((len = read (fd, bufp, (buf + bufsize) - bufp)) > 0) + bufp += len; + + if (len < 0) + error (1, errno, "reading %s", tarfile); + + newsize = bufp - buf; + (void)close(fd); + unlink_file(tarfile); + + send_to_server ("Modified ", 0); + send_to_server (file, 0); + send_to_server ("\012", 1); + send_to_server (mode_string, 0); + send_to_server ("\012T", 2); /* 'T' flag indicates a tarfile */ + sprintf (tmp, "%lu\012", (unsigned long) newsize); + send_to_server (tmp, 0); + + if (newsize > 0) + send_to_server (buf, newsize); + + free (buf); + free (mode_string); + + /* Return to status quo. */ + noexec = global_noexec; + + return; + } +#endif /* WRAPPERS_USE_TAR_FOR_TRANSFER */ /* Beware: on systems using CRLF line termination conventions, the read and write functions will convert CRLF to LF, so the diff -u -r cvs.Cyclic/src/commit.c cvs.wrappers/src/commit.c --- cvs.Cyclic/src/commit.c Mon Apr 6 22:03:54 1998 +++ cvs.wrappers/src/commit.c Mon Apr 13 17:55:34 1998 @@ -440,6 +440,8 @@ message[n] = '\0'; } + wrap_setup (); + #ifdef CLIENT_SUPPORT if (client_active) { @@ -616,8 +618,6 @@ /* XXX - this is not the perfect check for this */ if (argc <= 0) write_dirtag = tag; - - wrap_setup (); lock_tree_for_write (argc, argv, local, aflag); diff -u -r cvs.Cyclic/src/cvs.h cvs.wrappers/src/cvs.h --- cvs.Cyclic/src/cvs.h Mon Apr 6 22:03:36 1998 +++ cvs.wrappers/src/cvs.h Mon Apr 13 17:55:51 1998 @@ -207,6 +207,9 @@ #define CVSPREFIX ",," #define CVSDOTIGNORE ".cvsignore" #define CVSDOTWRAPPER ".cvswrappers" +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER +#define CVSWRAPPERTMP "#cvs.wrap-tmp" +#endif /* Command attributes -- see function lookup_command_attribute(). */ #define CVS_CMD_IGNORE_ADMROOT 1 diff -u -r cvs.Cyclic/src/options.h.in cvs.wrappers/src/options.h.in --- cvs.Cyclic/src/options.h.in Tue Jan 6 10:16:44 1998 +++ cvs.wrappers/src/options.h.in Mon Apr 13 17:56:01 1998 @@ -191,6 +191,21 @@ #define SERVER_HI_WATER (2 * 1024 * 1024) #define SERVER_LO_WATER (1 * 1024 * 1024) +/* When defined, the client/server code will use tar to wrap and transfer + * any directory that is marked as needing to be wrapped ('.nib' files, + * for example). + */ +#define WRAPPERS_USE_TAR_FOR_TRANSFER + +/* + * The "tar" program to run when using the CVS server and accepting + * tar files across the network. Specify a full pathname if your site + * wants to use a particular tar. + */ +#ifndef TAR_PROGRAM +#define TAR_PROGRAM "tar" +#endif + /* End of CVS configuration section */ /* diff -u -r cvs.Cyclic/src/server.c cvs.wrappers/src/server.c --- cvs.Cyclic/src/server.c Mon Apr 6 22:04:27 1998 +++ cvs.wrappers/src/server.c Mon Apr 13 17:56:28 1998 @@ -1101,6 +1101,9 @@ char *mode_text; int gzipped = 0; +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER + int tarred = 0; +#endif /* WRAPPERS_USE_TAR_FOR_TRANSFER */ /* * This used to return immediately if error_pending () was true. @@ -1167,6 +1170,13 @@ gzipped = 1; size = atoi (size_text + 1); } +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER + else if (size_text[0] == 'T') + { + tarred = 1; + size = atoi (size_text + 1); + } +#endif /* WRAPPERS_USE_TAR_FOR_TRANSFER */ else size = atoi (size_text); free (size_text); @@ -1189,8 +1199,52 @@ if (size >= 0) { +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER + if (tarred) { + char *tardir = CVSWRAPPERTMP; + char *tarfile = "data"; + char untarred_file[PATH_MAX]; + char *filename = arg; + int retcode; + + int global_noexec = noexec; + + /* This is a hack to work around the fact that noexec is used + in places that I can't override. (eg. make_directory). When + the -n global flag is used in cvs, this stuff still needs to + happen, so let's override the flag temporarily. */ + noexec = 0; + + receive_file (size, tarfile, 0); + if (error_pending ()) return; + + make_directory(tardir); + + run_setup (TAR_PROGRAM); + run_arg ("-xf"); + run_arg (tarfile); + run_arg ("-C"); + run_arg (tardir); + retcode = run_exec (DEVNULL, RUN_TTY, RUN_TTY, RUN_REALLY); + + strcpy(untarred_file, tardir); + strcat(untarred_file, "/"); + strcat(untarred_file, filename); + + unlink_file_dir(filename); /* remove existing directory */ + rename_file(untarred_file, filename); + unlink_file_dir(tarfile); /* get rid of tarfile */ + unlink_file_dir(tardir); /* ... and output file */ + + /* Return to status quo. */ + noexec = global_noexec; + } else { +#endif /* WRAPPERS_USE_TAR_FOR_TRANSFER */ receive_file (size, arg, gzipped); if (error_pending ()) return; +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER + } +#endif /* WRAPPERS_USE_TAR_FOR_TRANSFER */ } { @@ -3544,6 +3598,55 @@ { long status; +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER + if ((mode & S_IFMT) & S_IFDIR) { + /* file is a directory; send it over as a tarfile... */ + char *tarfile = CVSWRAPPERTMP; + struct stat tarfile_sb; + int retcode; + + int global_noexec = noexec; + + /* This is a hack to work around the fact that noexec is used + in places that I can't override. (eg. make_directory). When + the -n global flag is used in cvs, this stuff still needs to + happen, so let's override the flag temporarily. */ + noexec = 0; + + if (isfile(tarfile)) + unlink_file(tarfile); + + run_setup (TAR_PROGRAM); + run_arg ("-cf"); + run_arg (tarfile); + run_arg (finfo->file); + retcode = run_exec (DEVNULL, RUN_TTY, RUN_TTY, RUN_REALLY); + + if (stat(tarfile, &tarfile_sb) < 0) { + error(1, errno, "stat failed for %s", tarfile); + } + + size = tarfile_sb.st_size; + + f = fopen (tarfile, "r"); + if (f == NULL) + error (1, errno, "reading %s", tarfile); + status = buf_read_file (f, size, &list, &last); + if (status == -2) + (*protocol->memory_error) (&protocol); + else if (status != 0) + error (1, ferror (f) ? errno : 0, "reading %s", + tarfile); + if (fclose (f) == EOF) + error (1, errno, "reading %s", tarfile); + unlink_file(tarfile); + /* prepend size message with "T" flag for tarfile */ + buf_output0 (protocol, "T"); + + /* Return to status quo. */ + noexec = global_noexec; + } else { +#endif /* WRAPPERS_USE_TAR_FOR_TRANSFER */ f = CVS_FOPEN (finfo->file, "rb"); if (f == NULL) error (1, errno, "reading %s", finfo->fullname); @@ -3555,6 +3658,9 @@ finfo->fullname); if (fclose (f) == EOF) error (1, errno, "reading %s", finfo->fullname); +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER + } +#endif /* WRAPPERS_USE_TAR_FOR_TRANSFER */ } } @@ -4333,7 +4439,7 @@ (void) buf_shutdown (buf_to_net); } -int server_active = 0; +int server_active = 0; int server_expanding = 0; int diff -u -r cvs.Cyclic/src/server.h cvs.wrappers/src/server.h --- cvs.Cyclic/src/server.h Mon Apr 6 22:03:40 1998 +++ cvs.wrappers/src/server.h Mon Apr 13 17:56:41 1998 @@ -7,13 +7,13 @@ #define STDERR_FILENO 2 #endif +extern int server_active; #ifdef SERVER_SUPPORT /* * Nonzero if we are using the server. Used by various places to call * server-specific functions. */ -extern int server_active; extern int server_expanding; /* Server functions exported to the rest of CVS. */ diff -u -r cvs.Cyclic/src/wrapper.c cvs.wrappers/src/wrapper.c --- cvs.Cyclic/src/wrapper.c Mon Apr 6 22:04:33 1998 +++ cvs.wrappers/src/wrapper.c Mon Apr 13 17:56:51 1998 @@ -14,6 +14,7 @@ /* Original Author: athan@morgan.com 2/1/94 Modified By: vdemarco@bou.shl.com + bbum@friday.com This package was written to support the NEXTSTEP concept of "wrappers." These are essentially directories that are to be @@ -90,6 +91,10 @@ static int wrap_setup_already_done = 0; char *homedir; +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER + char *wfile; +#endif /* WRAPPERS_USE_TAR_FOR_TRANSFER */ + if (wrap_setup_already_done != 0) return; else @@ -131,6 +136,12 @@ free (file); } +#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER + if (wfile = getenv("CVS_CLIENT_WRAPPER_FILE")) { + wrap_add_file(wfile, 0); + } +#endif /* WRAPPERS_USE_TAR_FOR_TRANSFER */ + /* FIXME: calling wrap_add() below implies that the CVSWRAPPERS * environment variable contains exactly one "wrapper" -- a line * of the form @@ -162,25 +173,35 @@ for (i = 0; i < wrap_count + wrap_tempcount; ++i) { - if (wrap_list[i]->tocvsFilter != NULL - || wrap_list[i]->fromcvsFilter != NULL) - /* For greater studliness we would print the offending option - and (more importantly) where we found it. */ - error (0, 0, "\ --t and -f wrapper options are not supported remotely; ignored"); + send_to_server ("Argument -W\012Argument ", 0); + send_to_server (wrap_list[i]->wildCard, 0); + + if (wrap_list[i]->tocvsFilter != NULL) + { + send_to_server (" -t '", 0); + send_to_server (wrap_list[i]->tocvsFilter, 0); + send_to_server ("' ", 0); + } + + if (wrap_list[i]->fromcvsFilter != NULL) + { + send_to_server (" -f '", 0); + send_to_server (wrap_list[i]->fromcvsFilter, 0); + send_to_server ("' ", 0); + } + if (wrap_list[i]->mergeMethod == WRAP_COPY) - /* For greater studliness we would print the offending option - and (more importantly) where we found it. */ - error (0, 0, "\ --m wrapper option is not supported remotely; ignored"); + { + send_to_server (" -m 'COPY' ", 0); + } + if (wrap_list[i]->rcsOption != NULL) { - send_to_server ("Argument -W\012Argument ", 0); - send_to_server (wrap_list[i]->wildCard, 0); send_to_server (" -k '", 0); send_to_server (wrap_list[i]->rcsOption, 0); - send_to_server ("'\012", 0); + send_to_server ("' ", 0); } + send_to_server ("\012", 0); } } #endif /* CLIENT_SUPPORT */