summaryrefslogtreecommitdiffstats
path: root/contrib/cvs/src
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>1999-12-11 12:50:10 +0000
committerpeter <peter@FreeBSD.org>1999-12-11 12:50:10 +0000
commit206fbe27a15175665fddfb75d6e456be523b4ab2 (patch)
treeab9a84f957ba62ca61273ac92b92d0a1bf428bc6 /contrib/cvs/src
parent474a3106d4cfb9005960e108e8fdb84c778d1d94 (diff)
downloadFreeBSD-src-206fbe27a15175665fddfb75d6e456be523b4ab2.zip
FreeBSD-src-206fbe27a15175665fddfb75d6e456be523b4ab2.tar.gz
Merge cyclic changes for 1.10.7 only our mainline.
Diffstat (limited to 'contrib/cvs/src')
-rw-r--r--contrib/cvs/src/commit.c278
-rw-r--r--contrib/cvs/src/cvs.h31
-rw-r--r--contrib/cvs/src/diff.c32
-rw-r--r--contrib/cvs/src/filesubr.c47
-rw-r--r--contrib/cvs/src/import.c126
-rw-r--r--contrib/cvs/src/lock.c185
-rw-r--r--contrib/cvs/src/login.c22
-rw-r--r--contrib/cvs/src/logmsg.c8
-rw-r--r--contrib/cvs/src/mkmodules.c154
-rw-r--r--contrib/cvs/src/rcs.c416
-rw-r--r--contrib/cvs/src/rcs.h4
-rw-r--r--contrib/cvs/src/rcscmds.c5
-rw-r--r--contrib/cvs/src/recurse.c348
-rw-r--r--contrib/cvs/src/server.c860
-rw-r--r--contrib/cvs/src/update.c122
15 files changed, 1974 insertions, 664 deletions
diff --git a/contrib/cvs/src/commit.c b/contrib/cvs/src/commit.c
index 6b674e1..6dfd4a8 100644
--- a/contrib/cvs/src/commit.c
+++ b/contrib/cvs/src/commit.c
@@ -50,7 +50,6 @@ static int precommit_list_proc PROTO((Node * p, void *closure));
static int precommit_proc PROTO((char *repository, char *filter));
static int remove_file PROTO ((struct file_info *finfo, char *tag,
char *message));
-static void fix_rcs_modes PROTO((char *rcs, char *user));
static void fixaddfile PROTO((char *file, char *repository));
static void fixbranch PROTO((RCSNode *, char *branch));
static void unlockrcs PROTO((RCSNode *rcs));
@@ -342,10 +341,11 @@ commit (argc, argv)
readonly user stuff (CVSROOT/readers, &c). That is, why should
root be able to "cvs init", "cvs import", &c, but not "cvs ci"? */
if (geteuid () == (uid_t) 0
-#ifdef CLIENT_SUPPORT
+# ifdef CLIENT_SUPPORT
+ /* Who we are on the client side doesn't affect logging. */
&& !client_active
-#endif
- )
+# endif
+ )
{
struct passwd *pw;
@@ -411,7 +411,7 @@ commit (argc, argv)
argv += optind;
/* numeric specified revision means we ignore sticky tags... */
- if (saved_tag && isdigit (*saved_tag))
+ if (saved_tag && isdigit ((unsigned char) *saved_tag))
{
aflag = 1;
/* strip trailing dots */
@@ -566,13 +566,6 @@ commit (argc, argv)
send_arg("-n");
option_with_arg ("-r", saved_tag);
- /* Sending only the names of the files which were modified, added,
- or removed means that the server will only do an up-to-date
- check on those files. This is different from local CVS and
- previous versions of client/server CVS, but it probably is a Good
- Thing, or at least Not Such A Bad Thing. */
- send_file_names (find_args.argc, find_args.argv, 0);
-
/* FIXME: This whole find_args.force/SEND_FORCE business is a
kludge. It would seem to be a server bug that we have to
say that files are modified when they are not. This makes
@@ -585,6 +578,13 @@ commit (argc, argv)
send_files (find_args.argc, find_args.argv, local, 0,
find_args.force ? SEND_FORCE : 0);
+ /* Sending only the names of the files which were modified, added,
+ or removed means that the server will only do an up-to-date
+ check on those files. This is different from local CVS and
+ previous versions of client/server CVS, but it probably is a Good
+ Thing, or at least Not Such A Bad Thing. */
+ send_file_names (find_args.argc, find_args.argv, 0);
+
send_to_server ("ci\012", 0);
err = get_responses_and_close ();
if (err != 0 && use_editor && saved_message != NULL)
@@ -677,9 +677,10 @@ commit (argc, argv)
{
time_t now;
- (void) time (&now);
- if (now == last_register_time)
+ for (;;)
{
+ (void) time (&now);
+ if (now != last_register_time) break;
sleep (1); /* to avoid time-stamp races */
}
}
@@ -708,7 +709,7 @@ classify_file_internal (finfo, vers)
noexec = quiet = really_quiet = 1;
/* handle specified numeric revision specially */
- if (saved_tag && isdigit (*saved_tag))
+ if (saved_tag && isdigit ((unsigned char) *saved_tag))
{
/* If the tag is for the trunk, make sure we're at the head */
if (numdots (saved_tag) < 2)
@@ -792,6 +793,19 @@ check_fileproc (callerdat, finfo)
struct commit_info *ci;
struct logfile_info *li;
+ size_t cvsroot_len = strlen (CVSroot_directory);
+
+ if (strncmp (finfo->repository, CVSroot_directory, cvsroot_len) == 0
+ && ISDIRSEP (finfo->repository[cvsroot_len])
+ && strncmp (finfo->repository + cvsroot_len + 1,
+ CVSROOTADM,
+ sizeof (CVSROOTADM) - 1) == 0
+ && ISDIRSEP (finfo->repository[cvsroot_len + sizeof (CVSROOTADM)])
+ && strcmp (finfo->repository + cvsroot_len + sizeof (CVSROOTADM) + 1,
+ CVSNULLREPOS) == 0
+ )
+ error (1, 0, "cannot check in to %s", finfo->repository);
+
status = classify_file_internal (finfo, &vers);
/*
@@ -830,7 +844,7 @@ check_fileproc (callerdat, finfo)
* allow the commit if timestamp is identical or if we find
* an RCS_MERGE_PAT in the file.
*/
- if (!saved_tag || !isdigit (*saved_tag))
+ if (!saved_tag || !isdigit ((unsigned char) *saved_tag))
{
if (vers->date)
{
@@ -902,7 +916,9 @@ warning: file `%s' seems to still contain conflict indicators",
}
}
- if (status == T_REMOVED && vers->tag && isdigit (*vers->tag))
+ if (status == T_REMOVED
+ && vers->tag
+ && isdigit ((unsigned char) *vers->tag))
{
/* Remove also tries to forbid this, but we should check
here. I'm only _sure_ about somewhat obscure cases
@@ -941,7 +957,7 @@ warning: file `%s' seems to still contain conflict indicators",
}
free (rcs);
}
- if (vers->tag && isdigit (*vers->tag) &&
+ if (vers->tag && isdigit ((unsigned char) *vers->tag) &&
numdots (vers->tag) > 1)
{
error (0, 0,
@@ -1001,7 +1017,7 @@ warning: file `%s' seems to still contain conflict indicators",
ci = (struct commit_info *) xmalloc (sizeof (struct commit_info));
ci->status = status;
if (vers->tag)
- if (isdigit (*vers->tag))
+ if (isdigit ((unsigned char) *vers->tag))
ci->rev = xstrdup (vers->tag);
else
ci->rev = RCS_whatbranch (finfo->rcs, vers->tag);
@@ -1120,7 +1136,7 @@ precommit_proc (repository, filter)
s = xstrdup (filter);
for (cp = s; *cp; cp++)
- if (isspace (*cp))
+ if (isspace ((unsigned char) *cp))
{
*cp = '\0';
break;
@@ -1267,7 +1283,11 @@ commit_fileproc (callerdat, finfo)
Since the branch test was done in check_fileproc for
modified files, we need to stub it in again here. */
- if (ci->tag)
+ if (ci->tag
+
+ /* If numeric, it is on the trunk; check_fileproc enforced
+ this. */
+ && !isdigit ((unsigned char) ci->tag[0]))
{
if (finfo->rcs == NULL)
error (1, 0, "internal error: no parsed RCS file");
@@ -1601,16 +1621,16 @@ findmaxrev (p, closure)
* XXX - if removing a ,v file that is a relative symbolic link to
* another ,v file, we probably should add a ".." component to the
* link to keep it relative after we move it into the attic.
- */
+
+ Return value is 0 on success, or >0 on error (in which case we have
+ printed an error message). */
static int
remove_file (finfo, tag, message)
struct file_info *finfo;
char *tag;
char *message;
{
- mode_t omask;
int retcode;
- char *tmp;
int branch;
int lockflag;
@@ -1700,16 +1720,6 @@ remove_file (finfo, tag, message)
RCS_rewrite (finfo->rcs, NULL, NULL);
}
-#ifdef SERVER_SUPPORT
- if (server_active) {
- /* If this is the server, there will be a file sitting in the
- temp directory which is the kludgy way in which server.c
- tells time_stamp that the file is no longer around. Remove
- it so we can create temp files with that name (ignore errors). */
- unlink_file (finfo->file);
- }
-#endif
-
/* check something out. Generally this is the head. If we have a
particular rev, then name it. */
retcode = RCS_checkout (finfo->rcs, finfo->file, rev ? corev : NULL,
@@ -1746,34 +1756,9 @@ remove_file (finfo, tag, message)
if (rev != NULL)
free (rev);
- old_path = finfo->rcs->path;
+ old_path = xstrdup (finfo->rcs->path);
if (!branch)
- {
- /* this was the head; really move it into the Attic */
- tmp = xmalloc(strlen(finfo->repository) +
- sizeof('/') +
- sizeof(CVSATTIC) +
- sizeof('/') +
- strlen(finfo->file) +
- sizeof(RCSEXT) + 1);
- (void) sprintf (tmp, "%s/%s", finfo->repository, CVSATTIC);
- omask = umask (cvsumask);
- (void) CVS_MKDIR (tmp, 0777);
- (void) umask (omask);
- (void) sprintf (tmp, "%s/%s/%s%s", finfo->repository, CVSATTIC,
- finfo->file, RCSEXT);
-
- if (strcmp (finfo->rcs->path, tmp) != 0
- && CVS_RENAME (finfo->rcs->path, tmp) == -1
- && (isreadable (finfo->rcs->path) || !isreadable (tmp)))
- {
- free(tmp);
- return (1);
- }
- /* The old value of finfo->rcs->path is in old_path, and is
- freed below. */
- finfo->rcs->path = tmp;
- }
+ RCS_setattic (finfo->rcs, 1);
/* Print message that file was removed. */
cvs_output (old_path, 0);
@@ -1784,8 +1769,7 @@ remove_file (finfo, tag, message)
cvs_output ("\ndone\n", 0);
free(prev_rev);
- if (old_path != finfo->rcs->path)
- free (old_path);
+ free (old_path);
Scratch_Entry (finfo->entries, finfo->file);
return (0);
@@ -1811,7 +1795,9 @@ finaladd (finfo, rev, tag, options)
char *tmp = xmalloc (strlen (finfo->file) + sizeof (CVSADM)
+ sizeof (CVSEXT_LOG) + 10);
(void) sprintf (tmp, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG);
- (void) unlink_file (tmp);
+ if (unlink_file (tmp) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", tmp);
free (tmp);
}
else
@@ -1855,7 +1841,10 @@ fixaddfile (file, repository)
save_really_quiet = really_quiet;
really_quiet = 1;
if ((rcsfile = RCS_parsercsfile (rcs)) == NULL)
- (void) unlink_file (rcs);
+ {
+ if (unlink_file (rcs) < 0)
+ error (0, errno, "cannot remove %s", rcs);
+ }
else
freercsnode (&rcsfile);
really_quiet = save_really_quiet;
@@ -1902,8 +1891,20 @@ checkaddfile (file, repository, tag, options, rcsnode)
int newfile = 0;
RCSNode *rcsfile = NULL;
int retval;
+ int adding_on_branch;
+
+ /* Callers expect to be able to use either "" or NULL to mean the
+ default keyword expansion. */
+ if (options != NULL && options[0] == '\0')
+ options = NULL;
+ if (options != NULL)
+ assert (options[0] == '-' && options[1] == 'k');
+
+ /* If numeric, it is on the trunk; check_fileproc enforced
+ this. */
+ adding_on_branch = tag != NULL && !isdigit ((unsigned char) tag[0]);
- if (tag)
+ if (adding_on_branch)
{
rcs = xmalloc (strlen (repository) + strlen (file)
+ sizeof (RCSEXT) + sizeof (CVSATTIC) + 10);
@@ -1926,6 +1927,7 @@ checkaddfile (file, repository, tag, options, rcsnode)
{
/* file has existed in the past. Prepare to resurrect. */
char *rev;
+ char *oldexpand;
if ((rcsfile = *rcsnode) == NULL)
{
@@ -1934,41 +1936,38 @@ checkaddfile (file, repository, tag, options, rcsnode)
goto out;
}
- if (tag == NULL)
+ oldexpand = RCS_getexpand (rcsfile);
+ if ((oldexpand != NULL
+ && options != NULL
+ && strcmp (options + 2, oldexpand) != 0)
+ || (oldexpand == NULL && options != NULL))
{
- char *oldfile;
-
- /* we are adding on the trunk, so move the file out of the
- Attic. */
- oldfile = xstrdup (rcs);
- sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
+ /* We tell the user about this, because it means that the
+ old revisions will no longer retrieve the way that they
+ used to. */
+ error (0, 0, "changing keyword expansion mode to %s", options);
+ RCS_setexpand (rcsfile, options + 2);
+ }
- if (strcmp (oldfile, rcs) == 0)
+ if (!adding_on_branch)
+ {
+ /* We are adding on the trunk, so move the file out of the
+ Attic. */
+ if (!(rcsfile->flags & INATTIC))
{
error (0, 0, "internal error: confused about attic for %s",
- oldfile);
- out1:
- free (oldfile);
+ rcsfile->path);
retval = 1;
goto out;
}
- if (CVS_RENAME (oldfile, rcs) != 0)
- {
- error (0, errno, "failed to move `%s' out of the attic",
- oldfile);
- goto out1;
- }
- if (isreadable (oldfile)
- || !isreadable (rcs))
+
+ sprintf (rcs, "%s/%s%s", repository, file, RCSEXT);
+
+ if (RCS_setattic (rcsfile, 0))
{
- error (0, 0, "\
-internal error: `%s' didn't move out of the attic",
- oldfile);
- goto out1;
+ retval = 1;
+ goto out;
}
- free (oldfile);
- free (rcsfile->path);
- rcsfile->path = xstrdup (rcs);
}
rev = RCS_getversion (rcsfile, tag, NULL, 1, (int *) NULL);
@@ -2021,7 +2020,7 @@ internal error: `%s' didn't move out of the attic",
}
/* Set RCS keyword expansion options. */
- if (options && options[0] == '-' && options[1] == 'k')
+ if (options != NULL)
opt = options + 2;
else
opt = NULL;
@@ -2050,7 +2049,7 @@ internal error: `%s' didn't move out of the attic",
/* when adding a file for the first time, and using a tag, we need
to create a dead revision on the trunk. */
- if (tag && newfile)
+ if (adding_on_branch && newfile)
{
char *tmp;
FILE *fp;
@@ -2112,7 +2111,7 @@ internal error: `%s' didn't move out of the attic",
}
}
- if (tag != NULL)
+ if (adding_on_branch)
{
/* when adding with a tag, we need to stub a branch, if it
doesn't already exist. */
@@ -2178,13 +2177,22 @@ internal error: `%s' didn't move out of the attic",
fileattr_newfile (file);
- /* I don't think fix_rcs_modes is needed any more. In the
- add_rcs_file case, the algorithms used by add_rcs_file and
- fix_rcs_modes are the same, so there is no need to go through
- it all twice. In the other cases, I think we want to just
- preserve the mode that the file had before we started. That is
- a behavior change, but I would think a desirable one. */
- fix_rcs_modes (rcs, file);
+ /* At this point, we used to set the file mode of the RCS file
+ based on the mode of the file in the working directory. If we
+ are creating the RCS file for the first time, add_rcs_file does
+ this already. If we are re-adding the file, then perhaps it is
+ consistent to preserve the old file mode, just as we preserve
+ the old keyword expansion mode.
+
+ If we decide that we should change the modes, then we can't do
+ it here anyhow. At this point, the RCS file may be owned by
+ somebody else, so a chmod will fail. We need to instead do the
+ chmod after rewriting it.
+
+ FIXME: In general, I think the file mode (and the keyword
+ expansion mode) should be associated with a particular revision
+ of the file, so that it is possible to have different revisions
+ of a file have different modes. */
retval = 0;
@@ -2218,7 +2226,8 @@ lock_RCS (user, rcs, rev, repository)
* the head points to the trunk, not a branch... and as such, it's not
* necessary to move the head in this case.
*/
- if (rev == NULL || (rev && isdigit (*rev) && numdots (rev) < 2))
+ if (rev == NULL
+ || (rev && isdigit ((unsigned char) *rev) && numdots (rev) < 2))
{
branch = xstrdup (rcs->branch);
if (branch != NULL)
@@ -2275,69 +2284,6 @@ lock_RCS (user, rcs, rev, repository)
return (1);
}
-/* Called when "add"ing files to the RCS respository. It doesn't seem to
- be possible to get RCS to use the right mode, so we change it after
- the fact. TODO: now that RCS has been librarified, we have the power
- to change this. */
-
-static void
-fix_rcs_modes (rcs, user)
- char *rcs;
- char *user;
-{
- struct stat sb;
- mode_t rcs_mode;
-
-#ifdef PRESERVE_PERMISSIONS_SUPPORT
- /* Do ye nothing to the modes on a symbolic link. */
- if (preserve_perms && islink (user))
- return;
-#endif
-
- if (CVS_STAT (user, &sb) < 0)
- {
- /* FIXME: Should be ->fullname. */
- error (0, errno, "warning: cannot stat %s", user);
- return;
- }
-
- /* Now we compute the new mode.
-
- TODO: decide whether this whole thing can/should be skipped
- when `preserve_perms' is set. Almost certainly so. -twp
-
- The algorithm that we use is:
-
- Write permission is always off (this is what RCS and CVS have always
- done).
-
- If S_IRUSR is on (user read), then the read permission of
- the RCS file will be on. It would seem that if this is off,
- then other users can't do "cvs update" and such, so perhaps this
- should be hardcoded to being on (it is a strange case, though--the
- case in which a user file doesn't have user read permission on).
-
- If S_IXUSR is on (user execute), then set execute permission
- on the RCS file. This allows other users who check out the file
- to get the right setting for whether a shell script (for example)
- has the executable bit set.
-
- The result of that calculation is modified by CVSUMASK. The
- reason, of course, that the read and execute settings take the
- user bit and copy it to all three bits (user, group, other), is
- that it should be CVSUMASK, not the umask of individual users,
- which is the sole determiner of modes in the repository. */
-
- rcs_mode = 0;
- if (sb.st_mode & S_IRUSR)
- rcs_mode |= S_IRUSR | S_IRGRP | S_IROTH;
- if (sb.st_mode & S_IXUSR)
- rcs_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
- rcs_mode &= ~cvsumask;
- if (chmod (rcs, rcs_mode) < 0)
- error (0, errno, "warning: cannot change mode of %s", rcs);
-}
-
/*
* free an UPDATE node's data
*/
diff --git a/contrib/cvs/src/cvs.h b/contrib/cvs/src/cvs.h
index 9e0b057..b889c9a 100644
--- a/contrib/cvs/src/cvs.h
+++ b/contrib/cvs/src/cvs.h
@@ -9,6 +9,7 @@
/*
* basic information used in all source files
*
+ * $FreeBSD$
*/
@@ -371,11 +372,16 @@ extern char *RCS_citag;
/* Access method specified in CVSroot. */
typedef enum {
local_method, server_method, pserver_method, kserver_method, gserver_method,
- ext_method
+ ext_method, fork_method
} CVSmethod;
extern char *method_names[]; /* change this in root.c if you change
the enum above */
+/* This global variable holds the global -d option. It is NULL if -d
+ was not used, which means that we must get the CVSroot information
+ from the CVSROOT environment variable or from a CVS/Root file. */
+extern char *CVSroot_cmdline;
+
extern char *CVSroot_original; /* the active, complete CVSroot string */
extern int client_active; /* nonzero if we are doing remote access */
extern CVSmethod CVSroot_method; /* one of the enum values above */
@@ -383,6 +389,11 @@ extern char *CVSroot_username; /* the username or NULL if method == local */
extern char *CVSroot_hostname; /* the hostname or NULL if method == local */
extern char *CVSroot_directory; /* the directory name */
+/* These variables keep track of all of the CVSROOT directories that
+ have been seen by the client and the current one of those selected. */
+extern List *root_directories;
+extern char *current_root;
+
extern char *emptydir_name PROTO ((void));
extern int trace; /* Show all commands */
@@ -393,11 +404,11 @@ extern int require_real_user; /* skip CVSROOT/passwd, /etc/passwd users only*/
extern int top_level_admin;
-#ifdef AUTH_SERVER_SUPPORT
-extern char *Pserver_Repos; /* used to check that same repos is
- transmitted in pserver auth and in
- CVS protocol. */
-#endif /* AUTH_SERVER_SUPPORT */
+#ifdef CLIENT_SUPPORT
+extern List *dirs_sent_to_server; /* used to decide which "Argument
+ xxx" commands to send to each
+ server in multiroot mode. */
+#endif
extern char hostname[];
@@ -438,6 +449,7 @@ void Subdir_Deregister PROTO((List *, const char *, const char *));
char *Make_Date PROTO((char *rawdate));
char *date_from_time_t PROTO ((time_t));
+void date_to_internet PROTO ((char *, char *));
char *Name_Repository PROTO((char *dir, char *update_dir));
char *Short_Repository PROTO((char *repository));
@@ -456,7 +468,7 @@ extern void check_numeric PROTO ((const char *, int, char **));
char *getcaller PROTO((void));
char *time_stamp PROTO((char *file));
-char *xmalloc PROTO((size_t bytes));
+void *xmalloc PROTO((size_t bytes));
void *xrealloc PROTO((void *ptr, size_t bytes));
void expand_string PROTO ((char **, size_t *, size_t));
char *xstrdup PROTO((const char *str));
@@ -509,6 +521,9 @@ void lock_tree_for_write PROTO ((int argc, char **argv, int local, int aflag));
/* See lock.c for description. */
extern void lock_dir_for_write PROTO ((char *));
+
+/* LockDir setting from CVSROOT/config. */
+extern char *lock_dir;
void Scratch_Entry PROTO((List * list, char *fname));
void ParseTag PROTO((char **tagp, char **datep, int *nonbranchp));
@@ -635,6 +650,7 @@ char *make_message_rcslegal PROTO((char *message));
extern int file_has_markers PROTO ((const struct file_info *));
extern void get_file PROTO ((const char *, const char *, const char *,
char **, size_t *, size_t *));
+extern void resolve_symlink PROTO ((char **filename));
/* flags for run_exec(), the fast system() for CVS */
#define RUN_NORMAL 0x0000 /* no special behaviour */
@@ -655,7 +671,6 @@ int run_exec PROTO((const char *stin, const char *stout, const char *sterr,
FILE *run_popen PROTO((const char *, const char *));
int piped_child PROTO((char **, int *, int *));
void close_on_exec PROTO((int));
-int filter_stream_through_program PROTO((int, int, char **, pid_t *));
pid_t waitpid PROTO((pid_t, int *, int));
diff --git a/contrib/cvs/src/diff.c b/contrib/cvs/src/diff.c
index d5e3a7c..15353e63 100644
--- a/contrib/cvs/src/diff.c
+++ b/contrib/cvs/src/diff.c
@@ -12,6 +12,8 @@
*
* Without any file arguments, runs diff against all the currently modified
* files.
+ *
+ * $FreeBSD$
*/
#include "cvs.h"
@@ -40,7 +42,13 @@ static enum diff_file diff_file_nodiff PROTO ((struct file_info *finfo,
static int diff_fileproc PROTO ((void *callerdat, struct file_info *finfo));
static void diff_mark_errors PROTO((int err));
+
+/* Global variables. Would be cleaner if we just put this stuff in a
+ struct like log.c does. */
+
+/* Command line tags, from -r option. Points into argv. */
static char *diff_rev1, *diff_rev2;
+/* Command line dates, from -D option. Malloc'd. */
static char *diff_date1, *diff_date2;
static char *use_rev1, *use_rev2;
static int have_rev1_label, have_rev2_label;
@@ -224,15 +232,19 @@ diff (argc, argv)
* non-recursive/recursive diff.
*/
- /* For server, need to be able to do this command more than once
- (according to the protocol spec, even if the current client
- doesn't use it). */
+ /* Clean out our global variables (multiroot can call us multiple
+ times and the server can too, if the client sends several
+ diff commands). */
if (opts == NULL)
{
opts_allocated = 1;
opts = xmalloc (opts_allocated);
}
opts[0] = '\0';
+ diff_rev1 = NULL;
+ diff_rev2 = NULL;
+ diff_date1 = NULL;
+ diff_date2 = NULL;
optind = 0;
while ((c = getopt_long (argc, argv,
@@ -267,7 +279,7 @@ diff (argc, argv)
break;
case 131:
/* --ifdef. */
- strcat_and_allocate (&opts, &opts_allocated, " -D");
+ strcat_and_allocate (&opts, &opts_allocated, " --ifdef=");
strcat_and_allocate (&opts, &opts_allocated, optarg);
break;
case 129: case 130: case 132: case 133: case 134:
@@ -353,17 +365,18 @@ diff (argc, argv)
if (diff_date2)
client_senddate (diff_date2);
- send_file_names (argc, argv, SEND_EXPAND_WILD);
-
/* Send the current files unless diffing two revs from the archive */
if (diff_rev2 == NULL && diff_date2 == NULL)
send_files (argc, argv, local, 0, 0);
else
send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
+
send_to_server ("diff\012", 0);
err = get_responses_and_close ();
free (options);
+ options = NULL;
return (err);
}
#endif
@@ -386,6 +399,13 @@ diff (argc, argv)
/* clean up */
free (options);
+ options = NULL;
+
+ if (diff_date1 != NULL)
+ free (diff_date1);
+ if (diff_date2 != NULL)
+ free (diff_date2);
+
return (err);
}
diff --git a/contrib/cvs/src/filesubr.c b/contrib/cvs/src/filesubr.c
index 91af048..ae64460 100644
--- a/contrib/cvs/src/filesubr.c
+++ b/contrib/cvs/src/filesubr.c
@@ -17,6 +17,10 @@
definitions under operating systems (like, say, Windows NT) with different
file system semantics. */
+/*
+ * $FreeBSD$
+ */
+
#include "cvs.h"
static int deep_remove_dir PROTO((const char *path));
@@ -34,12 +38,8 @@ copy_file (from, to)
int fdin, fdout;
if (trace)
-#ifdef SERVER_SUPPORT
- (void) fprintf (stderr, "%c-> copy(%s,%s)\n",
- (server_active) ? 'S' : ' ', from, to);
-#else
- (void) fprintf (stderr, "-> copy(%s,%s)\n", from, to);
-#endif
+ (void) fprintf (stderr, "%s-> copy(%s,%s)\n",
+ CLIENT_SERVER_STR, from, to);
if (noexec)
return;
@@ -377,14 +377,9 @@ xchmod (fname, writable)
}
if (trace)
-#ifdef SERVER_SUPPORT
- (void) fprintf (stderr, "%c-> chmod(%s,%o)\n",
- (server_active) ? 'S' : ' ', fname,
+ (void) fprintf (stderr, "%s-> chmod(%s,%o)\n",
+ CLIENT_SERVER_STR, fname,
(unsigned int) mode);
-#else
- (void) fprintf (stderr, "-> chmod(%s,%o)\n", fname,
- (unsigned int) mode);
-#endif
if (noexec)
return;
@@ -401,12 +396,8 @@ rename_file (from, to)
const char *to;
{
if (trace)
-#ifdef SERVER_SUPPORT
- (void) fprintf (stderr, "%c-> rename(%s,%s)\n",
- (server_active) ? 'S' : ' ', from, to);
-#else
- (void) fprintf (stderr, "-> rename(%s,%s)\n", from, to);
-#endif
+ (void) fprintf (stderr, "%s-> rename(%s,%s)\n",
+ CLIENT_SERVER_STR, from, to);
if (noexec)
return;
@@ -422,12 +413,8 @@ unlink_file (f)
const char *f;
{
if (trace)
-#ifdef SERVER_SUPPORT
- (void) fprintf (stderr, "%c-> unlink(%s)\n",
- (server_active) ? 'S' : ' ', f);
-#else
- (void) fprintf (stderr, "-> unlink(%s)\n", f);
-#endif
+ (void) fprintf (stderr, "%s-> unlink(%s)\n",
+ CLIENT_SERVER_STR, f);
if (noexec)
return (0);
@@ -506,6 +493,7 @@ deep_remove_dir (path)
*/
return -1;
+ errno = 0;
while ((dp = readdir (dirp)) != NULL)
{
char *buf;
@@ -539,6 +527,15 @@ deep_remove_dir (path)
}
}
free (buf);
+
+ errno = 0;
+ }
+ if (errno != 0)
+ {
+ int save_errno = errno;
+ closedir (dirp);
+ errno = save_errno;
+ return -1;
}
closedir (dirp);
return rmdir (path);
diff --git a/contrib/cvs/src/import.c b/contrib/cvs/src/import.c
index cc84258..a449842 100644
--- a/contrib/cvs/src/import.c
+++ b/contrib/cvs/src/import.c
@@ -14,6 +14,8 @@
* VendorReleTag Tag for this particular release
*
* Additional arguments specify more Vendor Release Tags.
+ *
+ * $FreeBSD$
*/
#include "cvs.h"
@@ -94,6 +96,17 @@ import (argc, argv)
command_name);
break;
case 'd':
+#ifdef SERVER_SUPPORT
+ if (server_active)
+ {
+ /* CVS 1.10 and older clients will send this, but it
+ doesn't do any good. So tell the user we can't
+ cope, rather than silently losing. */
+ error (0, 0,
+ "warning: not setting the time of import from the file");
+ error (0, 0, "due to client limitations");
+ }
+#endif
use_file_modtime = 1;
break;
case 'b':
@@ -132,6 +145,20 @@ import (argc, argv)
if (argc < 3)
usage (import_usage);
+#ifdef SERVER_SUPPORT
+ /* This is for handling the Checkin-time request. It might seem a
+ bit odd to enable the use_file_modtime code even in the case
+ where Checkin-time was not sent for a particular file. The
+ effect is that we use the time of upload, rather than the time
+ when we call RCS_checkin. Since those times are both during
+ CVS's run, that seems OK, and it is easier to implement than
+ putting the "was Checkin-time sent" flag in CVS/Entries or some
+ such place. */
+
+ if (server_active)
+ use_file_modtime = 1;
+#endif
+
for (i = 1; i < argc; i++) /* check the tags for validity */
{
int j;
@@ -143,7 +170,8 @@ import (argc, argv)
}
/* XXX - this should be a module, not just a pathname */
- if (! isabsolute (argv[0]))
+ if (! isabsolute (argv[0])
+ && pathname_levels (argv[0]) == 0)
{
if (CVSroot_directory == NULL)
{
@@ -158,9 +186,11 @@ import (argc, argv)
}
else
{
- repository = xmalloc (strlen (argv[0]) + 5);
- (void) strcpy (repository, argv[0]);
- repos_len = 0;
+ /* It is somewhere between a security hole and "unexpected" to
+ let the client start mucking around outside the cvsroot
+ (wouldn't get the right CVSROOT configuration, &c). */
+ error (1, 0, "directory %s not relative within the repository",
+ argv[0]);
}
/*
@@ -170,7 +200,7 @@ import (argc, argv)
* must only have two dots in it (like "1.1.1").
*/
for (cp = vbranch; *cp != '\0'; cp++)
- if (!isdigit (*cp) && *cp != '.')
+ if (!isdigit ((unsigned char) *cp) && *cp != '.')
error (1, 0, "%s is not a numeric branch", vbranch);
if (numdots (vbranch) != 2)
error (1, 0, "Only branches with two dots are supported: %s", vbranch);
@@ -212,9 +242,6 @@ import (argc, argv)
{
int err;
- if (use_file_modtime)
- send_arg("-d");
-
if (vbranch[0] != '\0')
option_with_arg ("-b", vbranch);
if (message)
@@ -275,29 +302,52 @@ import (argc, argv)
{
if (!really_quiet)
{
- char buf[80];
- sprintf (buf, "\n%d conflicts created by this import.\n",
- conflicts);
- cvs_output (buf, 0);
- cvs_output ("Use the following command to help the merge:\n\n",
- 0);
- cvs_output ("\t", 1);
- cvs_output (program_name, 0);
- cvs_output (" checkout -j", 0);
- cvs_output (argv[1], 0);
- cvs_output (":yesterday -j", 0);
- cvs_output (argv[1], 0);
- cvs_output (" ", 1);
- cvs_output (argv[0], 0);
- cvs_output ("\n\n", 0);
+ char buf[20];
+ char *buf2;
+
+ cvs_output_tagged ("+importmergecmd", NULL);
+ cvs_output_tagged ("newline", NULL);
+ sprintf (buf, "%d", conflicts);
+ cvs_output_tagged ("conflicts", buf);
+ cvs_output_tagged ("text", " conflicts created by this import.");
+ cvs_output_tagged ("newline", NULL);
+ cvs_output_tagged ("text",
+ "Use the following command to help the merge:");
+ cvs_output_tagged ("newline", NULL);
+ cvs_output_tagged ("newline", NULL);
+ cvs_output_tagged ("text", "\t");
+ cvs_output_tagged ("text", program_name);
+ if (CVSroot_cmdline != NULL)
+ {
+ cvs_output_tagged ("text", " -d ");
+ cvs_output_tagged ("text", CVSroot_cmdline);
+ }
+ cvs_output_tagged ("text", " checkout -j");
+ buf2 = xmalloc (strlen (argv[1]) + 20);
+ sprintf (buf2, "%s:yesterday", argv[1]);
+ cvs_output_tagged ("mergetag1", buf2);
+ free (buf2);
+ cvs_output_tagged ("text", " -j");
+ cvs_output_tagged ("mergetag2", argv[1]);
+ cvs_output_tagged ("text", " ");
+ cvs_output_tagged ("repository", argv[0]);
+ cvs_output_tagged ("newline", NULL);
+ cvs_output_tagged ("newline", NULL);
+ cvs_output_tagged ("-importmergecmd", NULL);
}
+ /* FIXME: I'm not sure whether we need to put this information
+ into the loginfo. If we do, then note that it does not
+ report any required -d option. There is no particularly
+ clean way to tell the server about the -d option used by
+ the client. */
(void) fprintf (logfp, "\n%d conflicts created by this import.\n",
conflicts);
(void) fprintf (logfp,
"Use the following command to help the merge:\n\n");
- (void) fprintf (logfp, "\t%s checkout -j%s:yesterday -j%s %s\n\n",
- program_name, argv[1], argv[1], argv[0]);
+ (void) fprintf (logfp, "\t%s checkout ", program_name);
+ (void) fprintf (logfp, "-j%s:yesterday -j%s %s\n\n",
+ argv[1], argv[1], argv[0]);
}
else
{
@@ -340,9 +390,9 @@ import (argc, argv)
return (err);
}
-/*
- * process all the files in ".", then descend into other directories.
- */
+/* Process all the files in ".", then descend into other directories.
+ Returns 0 for success, or >0 on error (in which case a message
+ will have been printed). */
static int
import_descend (message, vtag, targc, targv)
char *message;
@@ -361,25 +411,27 @@ import_descend (message, vtag, targc, targv)
if ((dirp = CVS_OPENDIR (".")) == NULL)
{
+ error (0, errno, "cannot open directory");
err++;
}
else
{
+ errno = 0;
while ((dp = readdir (dirp)) != NULL)
{
if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
- continue;
+ goto one_more_time_boys;
#ifdef SERVER_SUPPORT
/* CVS directories are created in the temp directory by
server.c because it doesn't special-case import. So
don't print a message about them, regardless of -I!. */
if (server_active && strcmp (dp->d_name, CVSADM) == 0)
- continue;
+ goto one_more_time_boys;
#endif
if (ign_name (dp->d_name))
{
add_log ('I', dp->d_name);
- continue;
+ goto one_more_time_boys;
}
if (
@@ -418,12 +470,20 @@ import_descend (message, vtag, targc, targv)
vtag, targc, targv,
repository,
keyword_opt != NULL &&
- keyword_opt[0] == 'b');
+ keyword_opt[0] == 'b',
+ use_file_modtime);
else
#endif
err += process_import_file (message, dp->d_name,
vtag, targc, targv);
}
+ one_more_time_boys:
+ errno = 0;
+ }
+ if (errno != 0)
+ {
+ error (0, errno, "cannot read directory");
+ ++err;
}
(void) closedir (dirp);
}
@@ -874,7 +934,7 @@ get_comment (user)
*/
(void) strcpy (suffix_path, cp);
for (cp = suffix_path; *cp; cp++)
- if (isupper (*cp))
+ if (isupper ((unsigned char) *cp))
*cp = tolower (*cp);
suffix = suffix_path;
}
diff --git a/contrib/cvs/src/lock.c b/contrib/cvs/src/lock.c
index 1f2ccad..a0c0a34 100644
--- a/contrib/cvs/src/lock.c
+++ b/contrib/cvs/src/lock.c
@@ -8,6 +8,8 @@
* Set Lock
*
* Lock file support for CVS.
+ *
+ * $FreeBSD$
*/
/* The node Concurrency in doc/cvs.texinfo has a brief introduction to
@@ -73,6 +75,7 @@
unneeded complication although it presumably would be faster). */
#include "cvs.h"
+#include <assert.h>
struct lock {
/* This is the directory in which we may have a lock named by the
@@ -134,12 +137,161 @@ static List *lock_tree_list;
static char *locked_dir;
static List *locked_list;
+/* LockDir from CVSROOT/config. */
+char *lock_dir;
+
+static char *lock_name PROTO ((char *repository, char *name));
+
+/* Return a newly malloc'd string containing the name of the lock for the
+ repository REPOSITORY and the lock file name within that directory
+ NAME. Also create the directories in which to put the lock file
+ if needed (if we need to, could save system call(s) by doing
+ that only if the actual operation fails. But for now we'll keep
+ things simple). */
+static char *
+lock_name (repository, name)
+ char *repository;
+ char *name;
+{
+ char *retval;
+ char *p;
+ char *q;
+ char *short_repos;
+ mode_t save_umask;
+ int saved_umask = 0;
+
+ if (lock_dir == NULL)
+ {
+ /* This is the easy case. Because the lock files go directly
+ in the repository, no need to create directories or anything. */
+ retval = xmalloc (strlen (repository) + strlen (name) + 10);
+ (void) sprintf (retval, "%s/%s", repository, name);
+ }
+ else
+ {
+ struct stat sb;
+ mode_t new_mode = 0;
+
+ /* The interesting part of the repository is the part relative
+ to CVSROOT. */
+ assert (CVSroot_directory != NULL);
+ assert (strncmp (repository, CVSroot_directory,
+ strlen (CVSroot_directory)) == 0);
+ short_repos = repository + strlen (CVSroot_directory);
+ assert (*short_repos++ == '/');
+
+ retval = xmalloc (strlen (lock_dir)
+ + strlen (short_repos)
+ + strlen (name)
+ + 10);
+ strcpy (retval, lock_dir);
+ q = retval + strlen (retval);
+ *q++ = '/';
+
+ strcpy (q, short_repos);
+
+ /* In the common case, where the directory already exists, let's
+ keep it to one system call. */
+ if (CVS_STAT (retval, &sb) < 0)
+ {
+ /* If we need to be creating more than one directory, we'll
+ get the existence_error here. */
+ if (!existence_error (errno))
+ error (1, errno, "cannot stat directory %s", retval);
+ }
+ else
+ {
+ if (S_ISDIR (sb.st_mode))
+ goto created;
+ else
+ error (1, 0, "%s is not a directory", retval);
+ }
+
+ /* Now add the directories one at a time, so we can create
+ them if needed.
+
+ The idea behind the new_mode stuff is that the directory we
+ end up creating will inherit permissions from its parent
+ directory (we re-set new_mode with each EEXIST). CVSUMASK
+ isn't right, because typically the reason for LockDir is to
+ use a different set of permissions. We probably want to
+ inherit group ownership also (but we don't try to deal with
+ that, some systems do it for us either always or when g+s is on).
+
+ We don't try to do anything about the permissions on the lock
+ files themselves. The permissions don't really matter so much
+ because the locks will generally be removed by the process
+ which created them. */
+
+ if (CVS_STAT (lock_dir, &sb) < 0)
+ error (1, errno, "cannot stat %s", lock_dir);
+ new_mode = sb.st_mode;
+ save_umask = umask (0000);
+ saved_umask = 1;
+
+ p = short_repos;
+ while (1)
+ {
+ while (!ISDIRSEP (*p) && *p != '\0')
+ ++p;
+ if (ISDIRSEP (*p))
+ {
+ strncpy (q, short_repos, p - short_repos);
+ q[p - short_repos] = '\0';
+ if (!ISDIRSEP (q[p - short_repos - 1])
+ && CVS_MKDIR (retval, new_mode) < 0)
+ {
+ int saved_errno = errno;
+ if (saved_errno != EEXIST)
+ error (1, errno, "cannot make directory %s", retval);
+ else
+ {
+ if (CVS_STAT (retval, &sb) < 0)
+ error (1, errno, "cannot stat %s", retval);
+ new_mode = sb.st_mode;
+ }
+ }
+ ++p;
+ }
+ else
+ {
+ strcpy (q, short_repos);
+ if (CVS_MKDIR (retval, new_mode) < 0
+ && errno != EEXIST)
+ error (1, errno, "cannot make directory %s", retval);
+ goto created;
+ }
+ }
+ created:;
+
+ strcat (retval, "/");
+ strcat (retval, name);
+
+ if (saved_umask)
+ {
+ assert (umask (save_umask) == 0000);
+ saved_umask = 0;
+ }
+ }
+ return retval;
+}
+
/*
* Clean up all outstanding locks
*/
void
Lock_Cleanup ()
{
+ /* FIXME: error handling here is kind of bogus; we sometimes will call
+ error, which in turn can call us again. For the moment work around
+ this by refusing to reenter this function (this is a kludge). */
+ /* FIXME-reentrancy: the workaround isn't reentrant. */
+ static int in_lock_cleanup = 0;
+
+ if (in_lock_cleanup)
+ return;
+ in_lock_cleanup = 1;
+
remove_locks ();
dellist (&lock_tree_list);
@@ -151,6 +303,7 @@ Lock_Cleanup ()
locked_dir = NULL;
locked_list = NULL;
}
+ in_lock_cleanup = 0;
}
/*
@@ -199,8 +352,7 @@ lock_simple_remove (lock)
existence_error here. */
if (readlock != NULL)
{
- tmp = xmalloc (strlen (lock->repository) + strlen (readlock) + 10);
- (void) sprintf (tmp, "%s/%s", lock->repository, readlock);
+ tmp = lock_name (lock->repository, readlock);
if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
error (0, errno, "failed to remove lock %s", tmp);
free (tmp);
@@ -212,8 +364,7 @@ lock_simple_remove (lock)
existence_error here. */
if (writelock != NULL)
{
- tmp = xmalloc (strlen (lock->repository) + strlen (writelock) + 10);
- (void) sprintf (tmp, "%s/%s", lock->repository, writelock);
+ tmp = lock_name (lock->repository, writelock);
if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
error (0, errno, "failed to remove lock %s", tmp);
free (tmp);
@@ -221,8 +372,7 @@ lock_simple_remove (lock)
if (lock->have_lckdir)
{
- tmp = xmalloc (strlen (lock->repository) + sizeof (CVSLCK) + 10);
- (void) sprintf (tmp, "%s/%s", lock->repository, CVSLCK);
+ tmp = lock_name (lock->repository, CVSLCK);
SIG_beginCrSect ();
if (CVS_RMDIR (tmp) < 0)
error (0, errno, "failed to remove lock dir %s", tmp);
@@ -283,8 +433,7 @@ Reader_Lock (xrepository)
}
/* write a read-lock */
- tmp = xmalloc (strlen (xrepository) + strlen (readlock) + 10);
- (void) sprintf (tmp, "%s/%s", xrepository, readlock);
+ tmp = lock_name (xrepository, readlock);
if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF)
{
error (0, errno, "cannot create read lock in repository `%s'",
@@ -437,8 +586,7 @@ write_lock (lock)
}
/* write the write-lock file */
- tmp = xmalloc (strlen (lock->repository) + strlen (writelock) + 10);
- (void) sprintf (tmp, "%s/%s", lock->repository, writelock);
+ tmp = lock_name (lock->repository, writelock);
if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF)
{
int xerrno = errno;
@@ -582,8 +730,7 @@ set_lock (lock, will_wait)
if (masterlock != NULL)
free (masterlock);
- masterlock = xmalloc (strlen (lock->repository) + sizeof (CVSLCK) + 10);
- (void) sprintf (masterlock, "%s/%s", lock->repository, CVSLCK);
+ masterlock = lock_name (lock->repository, CVSLCK);
/*
* Note that it is up to the callers of set_lock() to arrange for signal
@@ -680,13 +827,17 @@ lock_wait (repos)
char *repos;
{
time_t now;
+ char *msg;
(void) time (&now);
- error (0, 0, "[%8.8s] waiting for %s's lock in %s", ctime (&now) + 11,
- lockers_name, repos);
+ msg = xmalloc (100 + strlen (lockers_name) + strlen (repos));
+ sprintf (msg, "[%8.8s] waiting for %s's lock in %s", ctime (&now) + 11,
+ lockers_name, repos);
+ error (0, 0, "%s", msg);
/* Call cvs_flusherr to ensure that the user sees this message as
soon as possible. */
cvs_flusherr ();
+ free (msg);
(void) sleep (CVSLCKSLEEP);
}
@@ -698,12 +849,16 @@ lock_obtained (repos)
char *repos;
{
time_t now;
+ char *msg;
(void) time (&now);
- error (0, 0, "[%8.8s] obtained lock in %s", ctime (&now) + 11, repos);
+ msg = xmalloc (100 + strlen (repos));
+ sprintf (msg, "[%8.8s] obtained lock in %s", ctime (&now) + 11, repos);
+ error (0, 0, "%s", msg);
/* Call cvs_flusherr to ensure that the user sees this message as
soon as possible. */
cvs_flusherr ();
+ free (msg);
}
static int lock_filesdoneproc PROTO ((void *callerdat, int err,
diff --git a/contrib/cvs/src/login.c b/contrib/cvs/src/login.c
index 0a74f37..708dfc8 100644
--- a/contrib/cvs/src/login.c
+++ b/contrib/cvs/src/login.c
@@ -5,6 +5,8 @@
* specified in the README file that comes with CVS.
*
* Allow user to log in for an authenticating server.
+ *
+ * $FreeBSD$
*/
#include "cvs.h"
@@ -48,7 +50,14 @@ construct_cvspass_filename ()
homedir = get_homedir ();
if (! homedir)
{
- error (1, errno, "could not find out home directory");
+ /* FIXME? This message confuses a lot of users, at least
+ on Win95 (which doesn't set HOMEDRIVE and HOMEPATH like
+ NT does). I suppose the answer for Win95 is to store the
+ passwords in the registry or something (??). And .cvsrc
+ and such too? Wonder what WinCVS does (about .cvsrc, the
+ right thing for a GUI is to just store the password in
+ memory only)... */
+ error (1, 0, "could not find out home directory");
return (char *) NULL;
}
@@ -246,7 +255,8 @@ login (argc, argv)
/* FIXME: rename_file would make more sense (e.g. almost
always faster). */
copy_file (tmp_name, passfile);
- unlink_file (tmp_name);
+ if (unlink_file (tmp_name) < 0)
+ error (0, errno, "cannot remove %s", tmp_name);
chmod (passfile, 0600);
free (tmp_name);
@@ -447,6 +457,8 @@ logout (argc, argv)
*/
passfile = construct_cvspass_filename ();
+ /* FIXME: This should not be in /tmp; that is almost surely a security
+ hole. Probably should just keep it in memory. */
tmp_name = cvs_temp_name ();
if ((tmp_fp = CVS_FOPEN (tmp_name, "w")) == NULL)
{
@@ -486,14 +498,16 @@ logout (argc, argv)
if (! found)
{
printf ("Entry not found for %s\n", CVSroot_original);
- unlink_file (tmp_name);
+ if (unlink_file (tmp_name) < 0)
+ error (0, errno, "cannot remove %s", tmp_name);
}
else
{
/* FIXME: rename_file would make more sense (e.g. almost
always faster). */
copy_file (tmp_name, passfile);
- unlink_file (tmp_name);
+ if (unlink_file (tmp_name) < 0)
+ error (0, errno, "cannot remove %s", tmp_name);
chmod (passfile, 0600);
}
return 0;
diff --git a/contrib/cvs/src/logmsg.c b/contrib/cvs/src/logmsg.c
index 6807937..e1b8ed0 100644
--- a/contrib/cvs/src/logmsg.c
+++ b/contrib/cvs/src/logmsg.c
@@ -4,6 +4,8 @@
*
* You may distribute under the terms of the GNU General Public License as
* specified in the README file that comes with the CVS source distribution.
+ *
+ * $FreeBSD$
*/
#include "cvs.h"
@@ -452,7 +454,8 @@ do_verify (messagep, repository)
{
/* Since following error() exits, delete the temp file
now. */
- unlink_file (fname);
+ if (unlink_file (fname) < 0)
+ error (0, errno, "cannot remove %s", fname);
error (1, retcode == -1 ? errno : 0,
"Message verification failed");
@@ -510,7 +513,8 @@ do_verify (messagep, repository)
/* Delete the temp file */
- unlink_file (fname);
+ if (unlink_file (fname) < 0)
+ error (0, errno, "cannot remove %s", fname);
free (fname);
}
}
diff --git a/contrib/cvs/src/mkmodules.c b/contrib/cvs/src/mkmodules.c
index e716222..966fb328 100644
--- a/contrib/cvs/src/mkmodules.c
+++ b/contrib/cvs/src/mkmodules.c
@@ -3,7 +3,10 @@
* Copyright (c) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License as
- * specified in the README file that comes with the CVS kit. */
+ * specified in the README file that comes with the CVS kit.
+ *
+ * $FreeBSD$
+ */
#include "cvs.h"
#include "savecwd.h"
@@ -353,7 +356,7 @@ static const struct admin_file filelist[] = {
{CVSROOTADM_CONFIG,
"a %s file configures various behaviors",
config_contents},
- {NULL, NULL}
+ {NULL, NULL, NULL}
};
/* Rebuild the checked out administrative files in directory DIR. */
@@ -397,11 +400,6 @@ mkmodules (dir)
rename_rcsfile (temp, CVSROOTADM_MODULES);
break;
- case -1: /* fork failed */
- (void) unlink_file (temp);
- error (1, errno, "cannot check out %s", CVSROOTADM_MODULES);
- /* NOTREACHED */
-
default:
error (0, 0,
"'cvs checkout' is less functional without a %s file",
@@ -409,7 +407,9 @@ mkmodules (dir)
break;
} /* switch on checkout_file() */
- (void) unlink_file (temp);
+ if (unlink_file (temp) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", temp);
free (temp);
/* Checkout the files that need it in CVSROOT dir */
@@ -430,7 +430,9 @@ mkmodules (dir)
else if (fileptr->errormsg)
error (0, 0, fileptr->errormsg, fileptr->filename);
#endif
- (void) unlink_file (temp);
+ if (unlink_file (temp) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", temp);
free (temp);
}
@@ -453,11 +455,13 @@ mkmodules (dir)
*last = '\0'; /* strip the newline */
/* Skip leading white space. */
- for (fname = line; *fname && isspace(*fname); fname++)
+ for (fname = line;
+ *fname && isspace ((unsigned char) *fname);
+ fname++)
;
/* Find end of filename. */
- for (cp = fname; *cp && !isspace(*cp); cp++)
+ for (cp = fname; *cp && !isspace ((unsigned char) *cp); cp++)
;
*cp = '\0';
@@ -468,11 +472,16 @@ mkmodules (dir)
}
else
{
- for (cp++; cp < last && *last && isspace(*last); cp++)
+ for (cp++;
+ cp < last && *last && isspace ((unsigned char) *last);
+ cp++)
;
if (cp < last && *cp)
error (0, 0, cp, fname);
}
+ if (unlink_file (temp) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", temp);
free (temp);
}
if (line)
@@ -522,6 +531,10 @@ make_tempfile ()
return temp;
}
+/* Get a file. If the file does not exist, return 1 silently. If
+ there is an error, print a message and return 1 (FIXME: probably
+ not a very clean convention). On success, return 0. */
+
static int
checkout_file (file, temp)
char *file;
@@ -547,6 +560,8 @@ checkout_file (file, temp)
(RCSCHECKOUTPROC) NULL, (void *) NULL);
if (retcode != 0)
{
+ /* Probably not necessary (?); RCS_checkout already printed a
+ message. */
error (0, 0, "failed to check out %s file",
file);
}
@@ -607,7 +622,7 @@ write_dbmfile (temp)
if (value[0] == '#')
continue; /* comment line */
vp = value;
- while (*vp && isspace (*vp))
+ while (*vp && isspace ((unsigned char) *vp))
vp++;
if (*vp == '\0')
continue; /* empty line */
@@ -618,11 +633,11 @@ write_dbmfile (temp)
if (!cont)
{
key.dptr = vp;
- while (*vp && !isspace (*vp))
+ while (*vp && !isspace ((unsigned char) *vp))
vp++;
key.dsize = vp - key.dptr;
*vp++ = '\0'; /* NULL terminate the key */
- while (*vp && isspace (*vp))
+ while (*vp && isspace ((unsigned char) *vp))
vp++; /* skip whitespace to value */
if (*vp == '\0')
{
@@ -639,17 +654,28 @@ write_dbmfile (temp)
}
}
dbm_close (db);
- (void) fclose (fp);
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", temp);
if (err)
{
+ /* I think that the size of the buffer needed here is
+ just determined by sizeof (CVSROOTADM_MODULES), the
+ filenames created by make_tempfile, and other things that won't
+ overflow. */
char dotdir[50], dotpag[50], dotdb[50];
(void) sprintf (dotdir, "%s.dir", temp);
(void) sprintf (dotpag, "%s.pag", temp);
(void) sprintf (dotdb, "%s.db", temp);
- (void) unlink_file (dotdir);
- (void) unlink_file (dotpag);
- (void) unlink_file (dotdb);
+ if (unlink_file (dotdir) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", dotdir);
+ if (unlink_file (dotpag) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", dotpag);
+ if (unlink_file (dotdb) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", dotdb);
error (1, 0, "DBM creation failed; correct above errors");
}
}
@@ -658,10 +684,18 @@ static void
rename_dbmfile (temp)
char *temp;
{
+ /* I think that the size of the buffer needed here is
+ just determined by sizeof (CVSROOTADM_MODULES), the
+ filenames created by make_tempfile, and other things that won't
+ overflow. */
char newdir[50], newpag[50], newdb[50];
char dotdir[50], dotpag[50], dotdb[50];
char bakdir[50], bakpag[50], bakdb[50];
+ int dir1_errno = 0, pag1_errno = 0, db1_errno = 0;
+ int dir2_errno = 0, pag2_errno = 0, db2_errno = 0;
+ int dir3_errno = 0, pag3_errno = 0, db3_errno = 0;
+
(void) sprintf (dotdir, "%s.dir", CVSROOTADM_MODULES);
(void) sprintf (dotpag, "%s.pag", CVSROOTADM_MODULES);
(void) sprintf (dotdb, "%s.db", CVSROOTADM_MODULES);
@@ -679,18 +713,59 @@ rename_dbmfile (temp)
/* don't mess with me */
SIG_beginCrSect ();
- (void) unlink_file (bakdir); /* rm .#modules.dir .#modules.pag */
- (void) unlink_file (bakpag);
- (void) unlink_file (bakdb);
- (void) CVS_RENAME (dotdir, bakdir); /* mv modules.dir .#modules.dir */
- (void) CVS_RENAME (dotpag, bakpag); /* mv modules.pag .#modules.pag */
- (void) CVS_RENAME (dotdb, bakdb); /* mv modules.db .#modules.db */
- (void) CVS_RENAME (newdir, dotdir); /* mv "temp".dir modules.dir */
- (void) CVS_RENAME (newpag, dotpag); /* mv "temp".pag modules.pag */
- (void) CVS_RENAME (newdb, dotdb); /* mv "temp".db modules.db */
+ /* rm .#modules.dir .#modules.pag */
+ if (unlink_file (bakdir) < 0)
+ dir1_errno = errno;
+ if (unlink_file (bakpag) < 0)
+ pag1_errno = errno;
+ if (unlink_file (bakdb) < 0)
+ db1_errno = errno;
+
+ /* mv modules.dir .#modules.dir */
+ if (CVS_RENAME (dotdir, bakdir) < 0)
+ dir2_errno = errno;
+ /* mv modules.pag .#modules.pag */
+ if (CVS_RENAME (dotpag, bakpag) < 0)
+ pag2_errno = errno;
+ /* mv modules.db .#modules.db */
+ if (CVS_RENAME (dotdb, bakdb) < 0)
+ db2_errno = errno;
+
+ /* mv "temp".dir modules.dir */
+ if (CVS_RENAME (newdir, dotdir) < 0)
+ dir3_errno = errno;
+ /* mv "temp".pag modules.pag */
+ if (CVS_RENAME (newpag, dotpag) < 0)
+ pag3_errno = errno;
+ /* mv "temp".db modules.db */
+ if (CVS_RENAME (newdb, dotdb) < 0)
+ db3_errno = errno;
/* OK -- make my day */
SIG_endCrSect ();
+
+ /* I didn't want to call error() when we had signals blocked
+ (unnecessary?), but do it now. */
+ if (dir1_errno && !existence_error (dir1_errno))
+ error (0, dir1_errno, "cannot remove %s", bakdir);
+ if (pag1_errno && !existence_error (pag1_errno))
+ error (0, pag1_errno, "cannot remove %s", bakpag);
+ if (db1_errno && !existence_error (db1_errno))
+ error (0, db1_errno, "cannot remove %s", bakdb);
+
+ if (dir2_errno && !existence_error (dir2_errno))
+ error (0, dir2_errno, "cannot remove %s", bakdir);
+ if (pag2_errno && !existence_error (pag2_errno))
+ error (0, pag2_errno, "cannot remove %s", bakpag);
+ if (db2_errno && !existence_error (db2_errno))
+ error (0, db2_errno, "cannot remove %s", bakdb);
+
+ if (dir3_errno && !existence_error (dir3_errno))
+ error (0, dir3_errno, "cannot remove %s", bakdir);
+ if (pag3_errno && !existence_error (pag3_errno))
+ error (0, pag3_errno, "cannot remove %s", bakpag);
+ if (db3_errno && !existence_error (db3_errno))
+ error (0, db3_errno, "cannot remove %s", bakdb);
}
#endif /* !MY_NDBM */
@@ -708,16 +783,31 @@ rename_rcsfile (temp, real)
rcs = xmalloc (strlen (real) + sizeof (RCSEXT) + 10);
(void) sprintf (rcs, "%s%s", real, RCSEXT);
statbuf.st_mode = 0; /* in case rcs file doesn't exist, but it should... */
- (void) CVS_STAT (rcs, &statbuf);
+ if (CVS_STAT (rcs, &statbuf) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot stat %s", rcs);
free (rcs);
if (chmod (temp, 0444 | (statbuf.st_mode & 0111)) < 0)
error (0, errno, "warning: cannot chmod %s", temp);
bak = xmalloc (strlen (real) + sizeof (BAKPREFIX) + 10);
(void) sprintf (bak, "%s%s", BAKPREFIX, real);
- (void) unlink_file (bak); /* rm .#loginfo */
- (void) CVS_RENAME (real, bak); /* mv loginfo .#loginfo */
- (void) CVS_RENAME (temp, real); /* mv "temp" loginfo */
+
+ /* rm .#loginfo */
+ if (unlink_file (bak) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", bak);
+
+ /* mv loginfo .#loginfo */
+ if (CVS_RENAME (real, bak) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot rename %s to %s", real, bak);
+
+ /* mv "temp" loginfo */
+ if (CVS_RENAME (temp, real) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot rename %s to %s", temp, real);
+
free (bak);
}
diff --git a/contrib/cvs/src/rcs.c b/contrib/cvs/src/rcs.c
index d5a17a2..ff38a48 100644
--- a/contrib/cvs/src/rcs.c
+++ b/contrib/cvs/src/rcs.c
@@ -6,6 +6,8 @@
*
* The routines contained in this file do all the rcs file parsing and
* manipulation
+ *
+ * $FreeBSD$
*/
#include <assert.h>
@@ -155,6 +157,26 @@ static const char spacetab[] = {
#define whitespace(c) (spacetab[(unsigned char)c] != 0)
+static char *rcs_lockfile;
+
+/* A few generic thoughts on error handling, in particular the
+ printing of unexpected characters that we find in the RCS file
+ (that is, why we use '\x%x' rather than %c or some such).
+
+ * Avoiding %c means we don't have to worry about what is printable
+ and other such stuff. In error handling, often better to keep it
+ simple.
+
+ * Hex rather than decimal or octal because character set standards
+ tend to use hex.
+
+ * Saying "character 0x%x" might make it sound like we are printing
+ a file offset. So we use '\x%x'.
+
+ * Would be nice to print the offset within the file, but I can
+ imagine various portability hassles (in particular, whether
+ unsigned long is always big enough to hold file offsets). */
+
/* Parse an rcsfile given a user file name and a repository. If there is
an error, we print an error message and return NULL. If the file
does not exist, we return NULL without printing anything (I'm not
@@ -366,7 +388,9 @@ RCS_parsercsfile_i (fp, rcsfile)
break;
}
- for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++)
+ for (cp = key;
+ (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
+ cp++)
/* do nothing */ ;
if (*cp == '\0')
break;
@@ -500,7 +524,9 @@ RCS_reparsercsfile (rdata, pfp, rcsbufp)
* revision or `desc', we are done with the headers and are down to the
* revision deltas, so we break out of the loop
*/
- for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++)
+ for (cp = key;
+ (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
+ cp++)
/* do nothing */ ;
/* Note that when comparing with RCSDATE, we are not massaging
VALUE from the string found in the RCS file. This is OK
@@ -585,6 +611,98 @@ RCS_reparsercsfile (rdata, pfp, rcsbufp)
rdata->flags &= ~PARTIAL;
}
+/* Move RCS into or out of the Attic, depending on TOATTIC. If the
+ file is already in the desired place, return without doing
+ anything. At some point may want to think about how this relates
+ to RCS_rewrite but that is a bit hairy (if one wants renames to be
+ atomic, or that kind of thing). If there is an error, print a message
+ and return 1. On success, return 0. */
+int
+RCS_setattic (rcs, toattic)
+ RCSNode *rcs;
+ int toattic;
+{
+ char *newpath;
+ char *p;
+ char *q;
+
+ /* Some systems aren't going to let us rename an open file. */
+ rcsbuf_cache_close ();
+
+ /* Could make the pathname computations in this file, and probably
+ in other parts of rcs.c too, easier if the REPOS and FILE
+ arguments to RCS_parse got stashed in the RCSNode. */
+
+ if (toattic)
+ {
+ mode_t omask;
+
+ if (rcs->flags & INATTIC)
+ return 0;
+
+ /* Example: rcs->path is "/foo/bar/baz,v". */
+ newpath = xmalloc (strlen (rcs->path) + sizeof CVSATTIC + 5);
+ p = last_component (rcs->path);
+ strncpy (newpath, rcs->path, p - rcs->path);
+ strcpy (newpath + (p - rcs->path), CVSATTIC);
+
+ /* Create the Attic directory if it doesn't exist. */
+ omask = umask (cvsumask);
+ if (CVS_MKDIR (newpath, 0777) < 0 && errno != EEXIST)
+ error (0, errno, "cannot make directory %s", newpath);
+ (void) umask (omask);
+
+ strcat (newpath, "/");
+ strcat (newpath, p);
+
+ if (CVS_RENAME (rcs->path, newpath) < 0)
+ {
+ int save_errno = errno;
+
+ /* The checks for isreadable look awfully fishy, but
+ I'm going to leave them here for now until I
+ can think harder about whether they take care of
+ some cases which should be handled somehow. */
+
+ if (isreadable (rcs->path) || !isreadable (newpath))
+ {
+ error (0, save_errno, "cannot rename %s to %s",
+ rcs->path, newpath);
+ free (newpath);
+ return 1;
+ }
+ }
+ }
+ else
+ {
+ if (!(rcs->flags & INATTIC))
+ return 0;
+
+ newpath = xmalloc (strlen (rcs->path));
+
+ /* Example: rcs->path is "/foo/bar/Attic/baz,v". */
+ p = last_component (rcs->path);
+ strncpy (newpath, rcs->path, p - rcs->path - 1);
+ newpath[p - rcs->path - 1] = '\0';
+ q = newpath + (p - rcs->path - 1) - (sizeof CVSATTIC - 1);
+ assert (strncmp (q, CVSATTIC, sizeof CVSATTIC - 1) == 0);
+ strcpy (q, p);
+
+ if (CVS_RENAME (rcs->path, newpath) < 0)
+ {
+ error (0, errno, "failed to move `%s' out of the attic",
+ rcs->path);
+ free (newpath);
+ return 1;
+ }
+ }
+
+ free (rcs->path);
+ rcs->path = newpath;
+
+ return 0;
+}
+
/*
* Fully parse the RCS file. Store all keyword/value pairs, fetch the
* log messages for each revision, and fetch add and delete counts for
@@ -673,7 +791,8 @@ warning: duplicate key `%s' in version `%s' of RCS file `%s'",
op = *cp++;
if (op != 'a' && op != 'd')
- error (1, 0, "unrecognized operation '%c' in %s",
+ error (1, 0, "\
+unrecognized operation '\\x%x' in %s",
op, rcs->path);
(void) strtoul (cp, (char **) &cp, 10);
if (*cp++ != ' ')
@@ -1481,7 +1600,8 @@ rcsbuf_getstring (rcsbuf, strp)
/* PTR should now point to the start of a string. */
if (c != '@')
- error (1, 0, "expected @-string at `%c' in %s", c, rcsbuf->filename);
+ error (1, 0, "expected @-string at '\\x%x' in %s",
+ c, rcsbuf->filename);
/* Optimize the common case of a value composed of a single
'@' string. */
@@ -1738,7 +1858,7 @@ rcsbuf_getword (rcsbuf, wordp)
printing character that is not a special.' This test ought
to do the trick. */
c = *ptr;
- if (isprint (c) &&
+ if (isprint ((unsigned char) c) &&
c != ';' && c != '$' && c != ',' && c != '@' && c != ':')
{
++ptr;
@@ -1806,9 +1926,10 @@ rcsbuf_getrevnum (rcsbuf, revp)
++ptr;
}
- if (! isdigit (c) && c != '.')
+ if (! isdigit ((unsigned char) c) && c != '.')
error (1, 0,
- "unexpected `%c' reading revision number in RCS file %s",
+ "\
+unexpected '\\x%x' reading revision number in RCS file %s",
c, rcsbuf->filename);
*revp = ptr;
@@ -1828,10 +1949,11 @@ rcsbuf_getrevnum (rcsbuf, revp)
c = *ptr;
}
- while (isdigit (c) || c == '.');
+ while (isdigit ((unsigned char) c) || c == '.');
if (! whitespace (c))
- error (1, 0, "unexpected `%c' reading revision number in RCS file %s",
+ error (1, 0, "\
+unexpected '\\x%x' reading revision number in RCS file %s",
c, rcsbuf->filename);
*ptr = '\0';
@@ -2339,7 +2461,7 @@ RCS_getversion (rcs, tag, date, force_tag_match, simple_tag)
}
/* Work out the branch. */
- if (! isdigit (tag[0]))
+ if (! isdigit ((unsigned char) tag[0]))
branch = RCS_whatbranch (rcs, tag);
else
branch = xstrdup (tag);
@@ -2404,6 +2526,16 @@ RCS_tag2rev (rcs, tag)
}
}
+ /* Try for a real (that is, exists in the RCS deltas) branch
+ (RCS_exist_rev just checks for real revisions and revisions
+ which have tags pointing to them). */
+ pa = RCS_getbranch (rcs, rev, 1);
+ if (pa != NULL)
+ {
+ free (pa);
+ return rev;
+ }
+
/* Tag is branch, but does not exist, try corresponding
* magic branch tag.
*
@@ -2427,7 +2559,7 @@ RCS_tag2rev (rcs, tag)
RCS_check_tag (tag); /* exit if not a valid tag */
/* If tag is "HEAD", special case to get head RCS revision */
- if (tag && (strcmp (tag, TAG_HEAD) == 0))
+ if (tag && STREQ (tag, TAG_HEAD))
return (RCS_head (rcs));
/* If valid tag let translate_symtag say yea or nay. */
@@ -2480,7 +2612,7 @@ RCS_gettag (rcs, symtag, force_tag_match, simple_tag)
#endif
return (RCS_head (rcs));
- if (!isdigit (tag[0]))
+ if (!isdigit ((unsigned char) tag[0]))
{
char *version;
@@ -2689,7 +2821,7 @@ RCS_isbranch (rcs, rev)
const char *rev;
{
/* numeric revisions are easy -- even number of dots is a branch */
- if (isdigit (*rev))
+ if (isdigit ((unsigned char) *rev))
return ((numdots (rev) & 1) == 0);
/* assume a revision if you can't find the RCS info */
@@ -2716,7 +2848,7 @@ RCS_nodeisbranch (rcs, rev)
assert (rcs != NULL);
/* numeric revisions are easy -- even number of dots is a branch */
- if (isdigit (*rev))
+ if (isdigit ((unsigned char) *rev))
return ((numdots (rev) & 1) == 0);
version = translate_symtag (rcs, rev);
@@ -2943,7 +3075,7 @@ RCS_branch_head (rcs, rev)
if (RCS_nodeisbranch (rcs, rev))
return RCS_getbranch (rcs, rev, 1);
- if (isdigit (*rev))
+ if (isdigit ((unsigned char) *rev))
num = xstrdup (rev);
else
{
@@ -3115,8 +3247,23 @@ RCS_getdate (rcs, date, force_tag_match)
*/
/* if we found what we're looking for, and it's not 1.1 return it */
- if (cur_rev != NULL && ! STREQ (cur_rev, "1.1"))
- return (xstrdup (cur_rev));
+ if (cur_rev != NULL)
+ {
+ if (! STREQ (cur_rev, "1.1"))
+ return (xstrdup (cur_rev));
+
+ /* This is 1.1; if the date of 1.1 is not the same as that for the
+ 1.1.1.1 version, then return 1.1. This happens when the first
+ version of a file is created by a regular cvs add and commit,
+ and there is a subsequent cvs import of the same file. */
+ p = findnode (rcs->versions, "1.1.1.1");
+ if (p)
+ {
+ vers = (RCSVers *) p->data;
+ if (RCS_datecmp (vers->date, date) != 0)
+ return xstrdup ("1.1");
+ }
+ }
/* look on the vendor branch */
retval = RCS_getdatebranch (rcs, date, CVSBRANCH);
@@ -3468,11 +3615,11 @@ RCS_check_tag (tag)
* characters cannot be non-visible graphic characters, and must not be
* in the set of "invalid" RCS identifier characters.
*/
- if (isalpha (*tag))
+ if (isalpha ((unsigned char) *tag))
{
for (cp = tag; *cp; cp++)
{
- if (!isgraph (*cp))
+ if (!isgraph ((unsigned char) *cp))
error (1, 0, "tag `%s' has non-visible graphic characters",
tag);
if (strchr (invalid, *cp))
@@ -3499,7 +3646,7 @@ RCS_valid_rev (rev)
{
char last, c;
last = *rev++;
- if (!isdigit (last))
+ if (!isdigit ((unsigned char) last))
return 0;
while ((c = *rev++)) /* Extra parens placate -Wall gcc option */
{
@@ -3510,10 +3657,10 @@ RCS_valid_rev (rev)
continue;
}
last = c;
- if (!isdigit (c))
+ if (!isdigit ((unsigned char) c))
return 0;
}
- if (!isdigit (last))
+ if (!isdigit ((unsigned char) last))
return 0;
return 1;
}
@@ -3549,10 +3696,26 @@ char *
RCS_getexpand (rcs)
RCSNode *rcs;
{
+ /* Since RCS_parsercsfile_i now reads expand, don't need to worry
+ about RCS_reparsercsfile. */
assert (rcs != NULL);
return rcs->expand;
}
+/* Set keyword expansion mode to EXPAND. For example "b" for binary. */
+void
+RCS_setexpand (rcs, expand)
+ RCSNode *rcs;
+ char *expand;
+{
+ /* Since RCS_parsercsfile_i now reads expand, don't need to worry
+ about RCS_reparsercsfile. */
+ assert (rcs != NULL);
+ if (rcs->expand != NULL)
+ free (rcs->expand);
+ rcs->expand = xstrdup (expand);
+}
+
/* RCS keywords, and a matching enum. */
struct rcs_keyword
{
@@ -3760,7 +3923,7 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen)
/* Look for the first non alphabetic character after the '$'. */
send = srch + srch_len;
for (s = srch; s < send; s++)
- if (! isalpha (*s))
+ if (! isalpha ((unsigned char) *s))
break;
/* If the first non alphabetic character is not '$' or ':',
@@ -3876,7 +4039,7 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen)
break;
case KEYWORD_NAME:
- if (name != NULL && ! isdigit (*name))
+ if (name != NULL && ! isdigit ((unsigned char) *name))
value = (char *) name;
else
value = NULL;
@@ -4219,7 +4382,7 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat)
: (sout != RUN_TTY ? sout : "(stdout)"))));
}
- assert (rev == NULL || isdigit (*rev));
+ assert (rev == NULL || isdigit ((unsigned char) *rev));
if (noexec && workfile != NULL)
return 0;
@@ -4467,9 +4630,9 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat)
error (1, 0, "%s:%s has bad `special' newphrase %s",
workfile, vers->version, info->data);
devnum = devnum_long;
- if (strcmp (devtype, "character") == 0)
+ if (STREQ (devtype, "character"))
special_file = S_IFCHR;
- else if (strcmp (devtype, "block") == 0)
+ else if (STREQ (devtype, "block"))
special_file = S_IFBLK;
else
error (0, 0, "%s is a special file of unsupported type `%s'",
@@ -5030,6 +5193,9 @@ RCS_checkin (rcs, workfile, message, rev, flags)
struct tm *ftm;
time_t modtime;
int adding_branch = 0;
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ struct stat sb;
+#endif
commitpt = NULL;
@@ -5050,40 +5216,11 @@ RCS_checkin (rcs, workfile, message, rev, flags)
allocated_workfile = 1;
}
- /* Is the backend file a symbolic link? Follow it and replace the
- filename with the destination of the link. */
-
- while (islink (rcs->path))
- {
- char *newname;
-#ifdef HAVE_READLINK
- /* The clean thing to do is probably to have each filesubr.c
- implement this (with an error if not supported by the
- platform, in which case islink would presumably return 0).
- But that would require editing each filesubr.c and so the
- expedient hack seems to be looking at HAVE_READLINK. */
- newname = xreadlink (rcs->path);
-#else
- error (1, 0, "internal error: islink doesn't like readlink");
-#endif
-
- if (isabsolute (newname))
- {
- free (rcs->path);
- rcs->path = newname;
- }
- else
- {
- char *oldname = last_component (rcs->path);
- int dirlen = oldname - rcs->path;
- char *fullnewname = xmalloc (dirlen + strlen (newname) + 1);
- strncpy (fullnewname, rcs->path, dirlen);
- strcpy (fullnewname + dirlen, newname);
- free (newname);
- free (rcs->path);
- rcs->path = fullnewname;
- }
- }
+ /* If the filename is a symbolic link, follow it and replace it
+ with the destination of the link. We need to do this before
+ calling rcs_internal_lockfile, or else we won't put the lock in
+ the right place. */
+ resolve_symlink (&(rcs->path));
checkin_quiet = flags & RCS_FLAGS_QUIET;
if (!checkin_quiet)
@@ -5129,7 +5266,6 @@ RCS_checkin (rcs, workfile, message, rev, flags)
if (preserve_perms)
{
Node *np;
- struct stat sb;
char buf[64]; /* static buffer should be safe: see usage. -twp */
delta->other_delta = getlist();
@@ -5228,6 +5364,12 @@ RCS_checkin (rcs, workfile, message, rev, flags)
dtext->version = xstrdup (newrev);
bufsize = 0;
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ if (preserve_perms && !S_ISREG (sb.st_mode))
+ /* Pretend file is empty. */
+ bufsize = 0;
+ else
+#endif
get_file (workfile, workfile,
rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
&dtext->text, &bufsize, &dtext->len);
@@ -5310,7 +5452,7 @@ RCS_checkin (rcs, workfile, message, rev, flags)
char *branch, *tip, *newrev, *p;
int dots, isrevnum;
- assert (isdigit(*rev));
+ assert (isdigit ((unsigned char) *rev));
newrev = xstrdup (rev);
dots = numdots (newrev);
@@ -5465,6 +5607,12 @@ RCS_checkin (rcs, workfile, message, rev, flags)
/* If this revision is being inserted on the trunk, the change text
for the new delta should be the contents of the working file ... */
bufsize = 0;
+#ifdef PRESERVE_PERMISSIONS_SUPPORT
+ if (preserve_perms && !S_ISREG (sb.st_mode))
+ /* Pretend file is empty. */
+ ;
+ else
+#endif
get_file (workfile, workfile,
rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
&dtext->text, &bufsize, &dtext->len);
@@ -6539,8 +6687,23 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive)
char *diffbuf;
size_t bufsize, len;
+#if defined (__CYGWIN32__) || defined (_WIN32)
+ /* FIXME: This is an awful kludge, but at least until I have
+ time to work on it a little more and test it, I'd rather
+ give a fatal error than corrupt the file. I think that we
+ need to use "-kb" and "--binary" and "rb" to get_file
+ (probably can do it always, not just for binary files, if
+ we are consistent between the RCS_checkout and the diff). */
+ {
+ char *expand = RCS_getexpand (rcs);
+ if (expand != NULL && STREQ (expand, "b"))
+ error (1, 0,
+ "admin -o not implemented yet for binary on this system");
+ }
+#endif
+
afterfile = cvs_temp_name();
- status = RCS_checkout (rcs, NULL, after, NULL, NULL, afterfile,
+ status = RCS_checkout (rcs, NULL, after, NULL, "-ko", afterfile,
(RCSCHECKOUTPROC)0, NULL);
if (status > 0)
goto delrev_done;
@@ -6568,13 +6731,13 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive)
else
{
beforefile = cvs_temp_name();
- status = RCS_checkout (rcs, NULL, before, NULL, NULL, beforefile,
+ status = RCS_checkout (rcs, NULL, before, NULL, "-ko", beforefile,
(RCSCHECKOUTPROC)0, NULL);
if (status > 0)
goto delrev_done;
outfile = cvs_temp_name();
- status = diff_exec (beforefile, afterfile, "-n", outfile);
+ status = diff_exec (beforefile, afterfile, "-an", outfile);
if (status == 2)
{
@@ -7024,7 +7187,7 @@ apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers)
we define a deltafrag as an add or a delete) need to be applied
in reverse order. So we stick them into a linked list. */
struct deltafrag {
- enum {ADD, DELETE} type;
+ enum {FRAG_ADD, FRAG_DELETE} type;
unsigned long pos;
unsigned long nlines;
const char *new_lines;
@@ -7041,7 +7204,8 @@ apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers)
if (op != 'a' && op != 'd')
/* Can't just skip over the deltafrag, because the value
of op determines the syntax. */
- error (1, 0, "unrecognized operation '%c' in %s", op, name);
+ error (1, 0, "unrecognized operation '\\x%x' in %s",
+ op, name);
df = (struct deltafrag *) xmalloc (sizeof (struct deltafrag));
df->next = dfhead;
dfhead = df;
@@ -7063,7 +7227,7 @@ apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers)
{
unsigned int i;
- df->type = ADD;
+ df->type = FRAG_ADD;
i = df->nlines;
/* The text we want is the number of lines specified, or
until the end of the value, whichever comes first (it
@@ -7093,7 +7257,7 @@ apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers)
--df->pos;
assert (op == 'd');
- df->type = DELETE;
+ df->type = FRAG_DELETE;
}
}
@@ -7103,12 +7267,12 @@ apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers)
switch (df->type)
{
- case ADD:
+ case FRAG_ADD:
if (! linevector_add (lines, df->new_lines, df->len, addvers,
df->pos))
return 0;
break;
- case DELETE:
+ case FRAG_DELETE:
if (df->pos > lines->nlines
|| df->pos + df->nlines > lines->nlines)
return 0;
@@ -7567,7 +7731,9 @@ getdelta (rcsbuf, rcsfile, keyp, valp)
/* Make sure that it is a revision number and not a cabbage
or something. */
- for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++)
+ for (cp = key;
+ (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
+ cp++)
/* do nothing */ ;
/* Note that when comparing with RCSDATE, we are not massaging
VALUE from the string found in the RCS file. This is OK since
@@ -7751,7 +7917,9 @@ unable to parse %s; `state' not in the expected place", rcsfile);
continue;
}
/* if we have a new revision number, we're done with this delta */
- for (cp = key; (isdigit (*cp) || *cp == '.') && *cp != '\0'; cp++)
+ for (cp = key;
+ (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
+ cp++)
/* do nothing */ ;
/* Note that when comparing with RCSDATE, we are not massaging
VALUE from the string found in the RCS file. This is OK
@@ -8333,6 +8501,30 @@ count_delta_actions (np, ignore)
return 0;
}
+/*
+ * Clean up temporary files
+ */
+static RETSIGTYPE
+rcs_cleanup ()
+{
+ /* Note that the checks for existence_error are because we are
+ called from a signal handler, so we don't know whether the
+ files got created. */
+
+ /* FIXME: Do not perform buffered I/O from an interrupt handler like
+ this (via error). However, I'm leaving the error-calling code there
+ in the hope that on the rare occasion the error call is actually made
+ (e.g., a fluky I/O error or permissions problem prevents the deletion
+ of a just-created file) reentrancy won't be an issue. */
+ if (rcs_lockfile != NULL)
+ {
+ if (unlink_file (rcs_lockfile) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", rcs_lockfile);
+ }
+ rcs_lockfile = NULL;
+}
+
/* RCS_internal_lockfile and RCS_internal_unlockfile perform RCS-style
locking on the specified RCSFILE: for a file called `foo,v', open
for writing a file called `,foo,'.
@@ -8357,10 +8549,6 @@ count_delta_actions (np, ignore)
processes from stomping all over each other's laundry. Hence,
they are `internal' locking functions.
- Note that we don't clean up the ,foo, file on ^C. We probably should.
- I'm not completely sure whether RCS does or not (I looked at the code
- a little, and didn't find it).
-
If there is an error, give a fatal error; if we return we always
return a non-NULL value. */
@@ -8368,13 +8556,35 @@ static FILE *
rcs_internal_lockfile (rcsfile)
char *rcsfile;
{
- char *lockfile;
int fd;
struct stat rstat;
FILE *fp;
+ static int first_call = 1;
+
+ if (first_call)
+ {
+ first_call = 0;
+ /* clean up if we get a signal */
+#ifdef SIGHUP
+ (void) SIG_register (SIGHUP, rcs_cleanup);
+#endif
+#ifdef SIGINT
+ (void) SIG_register (SIGINT, rcs_cleanup);
+#endif
+#ifdef SIGQUIT
+ (void) SIG_register (SIGQUIT, rcs_cleanup);
+#endif
+#ifdef SIGPIPE
+ (void) SIG_register (SIGPIPE, rcs_cleanup);
+#endif
+#ifdef SIGTERM
+ (void) SIG_register (SIGTERM, rcs_cleanup);
+#endif
+ }
/* Get the lock file name: `,file,' for RCS file `file,v'. */
- lockfile = rcs_lockfilename (rcsfile);
+ assert (rcs_lockfile == NULL);
+ rcs_lockfile = rcs_lockfilename (rcsfile);
/* Use the existing RCS file mode, or read-only if this is a new
file. (Really, this is a lie -- if this is a new file,
@@ -8400,12 +8610,13 @@ rcs_internal_lockfile (rcsfile)
rely on O_EXCL these days. This might be true for unix (I
don't really know), but I am still pretty skeptical in the case
of the non-unix systems. */
- fd = open (lockfile, OPEN_BINARY | O_WRONLY | O_CREAT | O_EXCL | O_TRUNC,
+ fd = open (rcs_lockfile,
+ OPEN_BINARY | O_WRONLY | O_CREAT | O_EXCL | O_TRUNC,
S_IRUSR | S_IRGRP | S_IROTH);
if (fd < 0)
{
- error (1, errno, "could not open lock file `%s'", lockfile);
+ error (1, errno, "could not open lock file `%s'", rcs_lockfile);
}
/* Force the file permissions, and return a stream object. */
@@ -8413,11 +8624,11 @@ rcs_internal_lockfile (rcsfile)
this in the non-HAVE_FCHMOD case. */
#ifdef HAVE_FCHMOD
if (fchmod (fd, rstat.st_mode) < 0)
- error (1, errno, "cannot change mode for %s", lockfile);
+ error (1, errno, "cannot change mode for %s", rcs_lockfile);
#endif
fp = fdopen (fd, FOPEN_BINARY_WRITE);
if (fp == NULL)
- error (1, errno, "cannot fdopen %s", lockfile);
+ error (1, errno, "cannot fdopen %s", rcs_lockfile);
free (lockfile);
@@ -8429,10 +8640,7 @@ rcs_internal_unlockfile (fp, rcsfile)
FILE *fp;
char *rcsfile;
{
- char *lockfile;
-
- /* Get the lock file name: `,file,' for RCS file `file,v'. */
- lockfile = rcs_lockfilename (rcsfile);
+ assert (rcs_lockfile != NULL);
/* Abort if we could not write everything successfully to LOCKFILE.
This is not a great error-handling mechanism, but should prevent
@@ -8445,12 +8653,21 @@ rcs_internal_unlockfile (fp, rcsfile)
fragile even if it happens to sometimes be true. The real
solution is to check each call to fprintf rather than waiting
until the end like this. */
- error (1, 0, "error writing to lock file %s", lockfile);
+ error (1, 0, "error writing to lock file %s", rcs_lockfile);
if (fclose (fp) == EOF)
- error (1, errno, "error closing lock file %s", lockfile);
+ error (1, errno, "error closing lock file %s", rcs_lockfile);
- rename_file (lockfile, rcsfile);
- free (lockfile);
+ rename_file (rcs_lockfile, rcsfile);
+
+ {
+ /* Use a temporary to make sure there's no interval
+ (after rcs_lockfile has been freed but before it's set to NULL)
+ during which the signal handler's use of rcs_lockfile would
+ reference freed memory. */
+ char *tmp = rcs_lockfile;
+ rcs_lockfile = NULL;
+ free (tmp);
+ }
}
static char *
@@ -8493,6 +8710,9 @@ RCS_rewrite (rcs, newdtext, insertpt)
if (noexec)
return;
+ /* Make sure we're operating on an actual file and not a symlink. */
+ resolve_symlink (&(rcs->path));
+
fout = rcs_internal_lockfile (rcs->path);
RCS_putadmin (rcs, fout);
@@ -8571,8 +8791,8 @@ annotate_fileproc (callerdat, finfo)
cvs_outerr (finfo->fullname, 0);
cvs_outerr ("\n***************\n", 0);
- RCS_deltas (finfo->rcs, fp, rcsbufp, version, RCS_ANNOTATE, (char **) NULL,
- (size_t) NULL, (char **) NULL, (size_t *) NULL);
+ RCS_deltas (finfo->rcs, fp, rcsbufp, version, RCS_ANNOTATE, NULL,
+ NULL, NULL, NULL);
free (version);
return 0;
}
@@ -8645,8 +8865,8 @@ annotate (argc, argv)
option_with_arg ("-r", tag);
if (date)
client_senddate (date);
- send_file_names (argc, argv, SEND_EXPAND_WILD);
send_files (argc, argv, local, 0, SEND_NO_CONTENTS);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
send_to_server ("annotate\012", 0);
return get_responses_and_close ();
}
@@ -8682,7 +8902,7 @@ make_file_label (path, rev, rcs)
char *file;
file = last_component (path);
- label = (char *) xmalloc (strlen (file)
+ label = (char *) xmalloc (strlen (path)
+ (rev == NULL ? 0 : strlen (rev))
+ 50);
@@ -8691,7 +8911,7 @@ make_file_label (path, rev, rcs)
char *date;
RCS_getrevtime (rcs, rev, datebuf, 0);
date = printable_date (datebuf);
- (void) sprintf (label, "-L%s\t%s\t%s", file, date, rev);
+ (void) sprintf (label, "-L%s\t%s\t%s", path, date, rev);
free (date);
}
else
@@ -8708,7 +8928,7 @@ make_file_label (path, rev, rcs)
wm->tm_year + 1900, wm->tm_mon + 1,
wm->tm_mday, wm->tm_hour,
wm->tm_min, wm->tm_sec);
- (void) sprintf (label, "-L%s\t%s", file, datebuf);
+ (void) sprintf (label, "-L%s\t%s", path, datebuf);
}
}
return label;
diff --git a/contrib/cvs/src/rcs.h b/contrib/cvs/src/rcs.h
index dc0c7be..7aee90a 100644
--- a/contrib/cvs/src/rcs.h
+++ b/contrib/cvs/src/rcs.h
@@ -6,6 +6,8 @@
* specified in the README file that comes with the CVS source distribution.
*
* RCS source control definitions needed by rcs.c and friends
+ *
+ * $FreeBSD$
*/
/* String which indicates a conflict if it occurs at the start of a line. */
@@ -184,6 +186,7 @@ RCSNode *RCS_parse PROTO((const char *file, const char *repos));
RCSNode *RCS_parsercsfile PROTO((char *rcsfile));
void RCS_fully_parse PROTO((RCSNode *));
void RCS_reparsercsfile PROTO((RCSNode *, FILE **, struct rcsbuffer *));
+extern int RCS_setattic PROTO ((RCSNode *, int));
char *RCS_check_kflag PROTO((const char *arg));
char *RCS_getdate PROTO((RCSNode * rcs, char *date, int force_tag_match));
@@ -211,6 +214,7 @@ char *RCS_branch_head PROTO ((RCSNode *rcs, char *rev));
int RCS_isdead PROTO((RCSNode *, const char *));
char *RCS_getexpand PROTO ((RCSNode *));
+void RCS_setexpand PROTO ((RCSNode *, char *));
int RCS_checkout PROTO ((RCSNode *, char *, char *, char *, char *, char *,
RCSCHECKOUTPROC, void *));
int RCS_checkin PROTO ((RCSNode *rcs, char *workfile, char *message,
diff --git a/contrib/cvs/src/rcscmds.c b/contrib/cvs/src/rcscmds.c
index f889233..bcdffd5 100644
--- a/contrib/cvs/src/rcscmds.c
+++ b/contrib/cvs/src/rcscmds.c
@@ -7,6 +7,8 @@
*
* The functions in this file provide an interface for performing
* operations directly on RCS files.
+ *
+ * $FreeBSD$
*/
#include "cvs.h"
@@ -147,7 +149,8 @@ call_diff_write_output (text, len)
const char *text;
size_t len;
{
- cvs_output (text, len);
+ if (len > 0)
+ cvs_output (text, len);
}
/* Call back function for the diff library to flush the output file.
diff --git a/contrib/cvs/src/recurse.c b/contrib/cvs/src/recurse.c
index d88bf2b..209d324 100644
--- a/contrib/cvs/src/recurse.c
+++ b/contrib/cvs/src/recurse.c
@@ -6,6 +6,7 @@
*
* General recursion handler
*
+ * $FreeBSD$
*/
#include "cvs.h"
@@ -13,9 +14,6 @@
#include "fileattr.h"
#include "edit.h"
-#ifdef CLIENT_SUPPORT
-static int do_argument_proc PROTO((Node * p, void *closure));
-#endif
static int do_dir_proc PROTO((Node * p, void *closure));
static int do_file_proc PROTO((Node * p, void *closure));
static void addlist PROTO((List ** listp, char *key));
@@ -61,23 +59,6 @@ struct frame_and_entries {
List *entries;
};
-#ifdef CLIENT_SUPPORT
-/* This is a callback to send "Argument" commands to the server in the
- case we've done a "cvs update" or "cvs commit" in a top-level
- directory where there is no CVSADM directory. */
-
-static int
-do_argument_proc (p, closure)
- Node *p;
- void *closure;
-{
- char *dir = p->key;
- send_to_server ("Argument ", 0);
- send_to_server (dir, 0);
- send_to_server ("\012", 1);
- return 0;
-}
-#endif
/* Start a recursive command.
@@ -127,6 +108,9 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat,
int dosrcs;
{
int i, err = 0;
+#ifdef CLIENT_SUPPORT
+ List *args_to_send_when_finished = NULL;
+#endif
List *files_by_dir = NULL;
struct recursion_frame frame;
@@ -169,6 +153,29 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat,
if (argc == 0)
{
+ int just_subdirs = (which & W_LOCAL) && !isdir (CVSADM);
+
+#ifdef CLIENT_SUPPORT
+ if (!just_subdirs
+ && CVSroot_cmdline == NULL
+ && client_active)
+ {
+ char *root = Name_Root (NULL, update_dir);
+ if (root && strcmp (root, current_root) != 0)
+ /* We're skipping this directory because it is for
+ a different root. Therefore, we just want to
+ do the subdirectories only. Processing files would
+ cause a working directory from one repository to be
+ processed against a different repository, which could
+ cause all kinds of spurious conflicts and such.
+
+ Question: what about the case of "cvs update foo"
+ where we process foo/bar and not foo itself? That
+ seems to be handled somewhere (else) but why should
+ it be a separate case? Needs investigation... */
+ just_subdirs = 1;
+ }
+#endif
/*
* There were no arguments, so we'll probably just recurse. The
@@ -177,7 +184,7 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat,
* process each of the sub-directories, so we pretend like we were
* called with the list of sub-dirs of the current dir as args
*/
- if ((which & W_LOCAL) && !isdir (CVSADM))
+ if (just_subdirs)
{
dirlist = Find_Directories ((char *) NULL, W_LOCAL, (List *) NULL);
/* If there are no sub-directories, there is a certain logic in
@@ -204,17 +211,21 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat,
appropriate "Argument" commands to the server. In
this case, that won't have happened, so we need to
do it here. While this example uses "update", this
- generalizes to other commands. */
-
- err += walklist (dirlist, do_argument_proc, NULL);
+ generalizes to other commands. */
+
+ /* This is the same call to Find_Directories as above.
+ FIXME: perhaps it would be better to write a
+ function that duplicates a list. */
+ args_to_send_when_finished = Find_Directories ((char *) NULL,
+ W_LOCAL,
+ (List *) NULL);
}
#endif
}
else
addlist (&dirlist, ".");
- err += do_recursion (&frame);
- goto out;
+ goto do_the_work;
}
@@ -335,14 +346,146 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat,
/* then do_recursion on the dirlist. */
if (dirlist != NULL)
+ {
+ do_the_work:
err += do_recursion (&frame);
-
+ }
+
/* Free the data which expand_wild allocated. */
free_names (&argc, argv);
- out:
free (update_dir);
update_dir = NULL;
+
+#ifdef CLIENT_SUPPORT
+ if (args_to_send_when_finished != NULL)
+ {
+ /* FIXME (njc): in the multiroot case, we don't want to send
+ argument commands for those top-level directories which do
+ not contain any subdirectories which have files checked out
+ from current_root. If we do, and two repositories have a
+ module with the same name, nasty things could happen.
+
+ This is hard. Perhaps we should send the Argument commands
+ later in this procedure, after we've had a chance to notice
+ which directores we're using (after do_recursion has been
+ called once). This means a _lot_ of rewriting, however.
+
+ What we need to do for that to happen is descend the tree
+ and construct a list of directories which are checked out
+ from current_cvsroot. Now, we eliminate from the list all
+ of those directories which are immediate subdirectories of
+ another directory in the list. To say that the opposite
+ way, we keep the directories which are not immediate
+ subdirectories of any other in the list. Here's a picture:
+
+ a
+ / \
+ B C
+ / \
+ D e
+ / \
+ F G
+ / \
+ H I
+
+ The node in capitals are those directories which are
+ checked out from current_cvsroot. We want the list to
+ contain B, C, F, and G. D, H, and I are not included,
+ because their parents are also checked out from
+ current_cvsroot.
+
+ The algorithm should be:
+
+ 1) construct a tree of all directory names where each
+ element contains a directory name and a flag which notes if
+ that directory is checked out from current_cvsroot
+
+ a0
+ / \
+ B1 C1
+ / \
+ D1 e0
+ / \
+ F1 G1
+ / \
+ H1 I1
+
+ 2) Recursively descend the tree. For each node, recurse
+ before processing the node. If the flag is zero, do
+ nothing. If the flag is 1, check the node's parent. If
+ the parent's flag is one, change the current entry's flag
+ to zero.
+
+ a0
+ / \
+ B1 C1
+ / \
+ D0 e0
+ / \
+ F1 G1
+ / \
+ H0 I0
+
+ 3) Walk the tree and spit out "Argument" commands to tell
+ the server which directories to munge.
+
+ Yuck. It's not clear this is worth spending time on, since
+ we might want to disable cvs commands entirely from
+ directories that do not have CVSADM files...
+
+ Anyways, the solution as it stands has modified server.c
+ (dirswitch) to create admin files [via server.c
+ (create_adm_p)] in all path elements for a client's
+ "Directory xxx" command, which forces the server to descend
+ and serve the files there. client.c (send_file_names) has
+ also been modified to send only those arguments which are
+ appropriate to current_root.
+
+ */
+
+ /* Construct a fake argc/argv pair. */
+
+ int our_argc = 0, i;
+ char **our_argv = NULL;
+
+ if (! list_isempty (args_to_send_when_finished))
+ {
+ Node *head, *p;
+
+ head = args_to_send_when_finished->list;
+
+ /* count the number of nodes */
+ i = 0;
+ for (p = head->next; p != head; p = p->next)
+ i++;
+ our_argc = i;
+
+ /* create the argument vector */
+ our_argv = (char **) xmalloc (sizeof (char *) * our_argc);
+
+ /* populate it */
+ i = 0;
+ for (p = head->next; p != head; p = p->next)
+ our_argv[i++] = xstrdup (p->key);
+ }
+
+ /* We don't want to expand widcards, since we've just created
+ a list of directories directly from the filesystem. */
+ send_file_names (our_argc, our_argv, 0);
+
+ /* Free our argc/argv. */
+ if (our_argv != NULL)
+ {
+ for (i = 0; i < our_argc; i++)
+ free (our_argv[i]);
+ free (our_argv);
+ }
+
+ dellist (&args_to_send_when_finished);
+ }
+#endif
+
return (err);
}
@@ -359,6 +502,7 @@ do_recursion (frame)
char *srepository;
List *entries = NULL;
int should_readlock;
+ int process_this_directory = 1;
/* do nothing if told */
if (frame->flags == R_SKIP_ALL)
@@ -410,6 +554,57 @@ do_recursion (frame)
server_pause_check();
#endif
+ /* Check the value in CVSADM_ROOT and see if it's in the list. If
+ not, add it to our lists of CVS/Root directories and do not
+ process the files in this directory. Otherwise, continue as
+ usual. THIS_ROOT might be NULL if we're doing an initial
+ checkout -- check before using it. The default should be that
+ we process a directory's contents and only skip those contents
+ if a CVS/Root file exists.
+
+ If we're running the server, we want to process all
+ directories, since we're guaranteed to have only one CVSROOT --
+ our own. */
+
+ if (
+ /* If -d was specified, it should override CVS/Root.
+
+ In the single-repository case, it is long-standing CVS behavior
+ and makes sense - the user might want another access method,
+ another server (which mounts the same repository), &c.
+
+ In the multiple-repository case, -d overrides all CVS/Root
+ files. That is the only plausible generalization I can
+ think of. */
+ CVSroot_cmdline == NULL
+
+#ifdef SERVER_SUPPORT
+ && ! server_active
+#endif
+ )
+ {
+ char *this_root = Name_Root ((char *) NULL, update_dir);
+ if (this_root != NULL)
+ {
+ if (findnode (root_directories, this_root) == NULL)
+ {
+ /* Add it to our list. */
+
+ Node *n = getnode ();
+ n->type = UNKNOWN;
+ n->key = xstrdup (this_root);
+
+ if (addnode (root_directories, n))
+ error (1, 0, "cannot add new CVSROOT %s", this_root);
+
+ }
+
+ process_this_directory = (strcmp (current_root, this_root) == 0);
+
+ free (this_root);
+ }
+ }
+
/*
* Fill in repository with the current repository
*/
@@ -468,12 +663,26 @@ do_recursion (frame)
repository = Name_Repository ((char *) NULL, update_dir);
/* find the files and fill in entries if appropriate */
- filelist = Find_Names (repository, lwhich, frame->aflag, &entries);
+ if (process_this_directory)
+ {
+ filelist = Find_Names (repository, lwhich, frame->aflag,
+ &entries);
+ if (filelist == NULL)
+ {
+ error (0, 0, "skipping directory %s", update_dir);
+ /* Note that Find_Directories and the filesdoneproc
+ in particular would do bad things ("? foo.c" in
+ the case of some filesdoneproc's). */
+ goto skip_directory;
+ }
+ }
}
/* find sub-directories if we will recurse */
if (frame->flags != R_SKIP_DIRS)
- dirlist = Find_Directories (repository, frame->which, entries);
+ dirlist = Find_Directories (
+ process_this_directory ? repository : NULL,
+ frame->which, entries);
}
else
{
@@ -487,7 +696,7 @@ do_recursion (frame)
}
/* process the files (if any) */
- if (filelist != NULL && frame->fileproc)
+ if (process_this_directory && filelist != NULL && frame->fileproc)
{
struct file_info finfo_struct;
struct frame_and_file frfile;
@@ -525,11 +734,12 @@ do_recursion (frame)
}
/* call-back files done proc (if any) */
- if (dodoneproc && frame->filesdoneproc != NULL)
+ if (process_this_directory && dodoneproc && frame->filesdoneproc != NULL)
err = frame->filesdoneproc (frame->callerdat, err, repository,
update_dir[0] ? update_dir : ".",
entries);
+ skip_directory:
fileattr_write ();
fileattr_free ();
@@ -589,7 +799,24 @@ do_file_proc (p, closure)
strcat (finfo->fullname, finfo->file);
if (frfile->frame->dosrcs && repository)
+ {
finfo->rcs = RCS_parse (finfo->file, repository);
+
+ /* OK, without W_LOCAL the error handling becomes relatively
+ simple. The file names came from readdir() on the
+ repository and so we know any ENOENT is an error
+ (e.g. symlink pointing to nothing). Now, the logic could
+ be simpler - since we got the name from readdir, we could
+ just be calling RCS_parsercsfile. */
+ if (finfo->rcs == NULL
+ && !(frfile->frame->which & W_LOCAL))
+ {
+ error (0, 0, "could not read RCS file for %s", finfo->fullname);
+ free (finfo->fullname);
+ cvs_flushout ();
+ return 0;
+ }
+ }
else
finfo->rcs = (RCSNode *) NULL;
ret = frfile->frame->fileproc (frfile->frame->callerdat, finfo);
@@ -625,6 +852,7 @@ do_dir_proc (p, closure)
int err = 0;
struct saved_cwd cwd;
char *saved_update_dir;
+ int process_this_directory = 1;
if (fncmp (dir, CVSADM) == 0)
{
@@ -760,12 +988,62 @@ but CVS uses %s for its own purposes; skipping %s directory",
free (cvsadmdir);
}
+ /* Only process this directory if the root matches. This nearly
+ duplicates code in do_recursion. */
+
+ if (
+ /* If -d was specified, it should override CVS/Root.
+
+ In the single-repository case, it is long-standing CVS behavior
+ and makes sense - the user might want another access method,
+ another server (which mounts the same repository), &c.
+
+ In the multiple-repository case, -d overrides all CVS/Root
+ files. That is the only plausible generalization I can
+ think of. */
+ CVSroot_cmdline == NULL
+
+#ifdef SERVER_SUPPORT
+ && ! server_active
+#endif
+ )
+ {
+ char *this_root = Name_Root (dir, update_dir);
+ if (this_root != NULL)
+ {
+ if (findnode (root_directories, this_root) == NULL)
+ {
+ /* Add it to our list. */
+
+ Node *n = getnode ();
+ n->type = UNKNOWN;
+ n->key = xstrdup (this_root);
+
+ if (addnode (root_directories, n))
+ error (1, 0, "cannot add new CVSROOT %s", this_root);
+
+ }
+
+ process_this_directory = (strcmp (current_root, this_root) == 0);
+ free (this_root);
+ }
+ }
+
/* call-back dir entry proc (if any) */
if (dir_return == R_SKIP_ALL)
;
else if (frame->direntproc != NULL)
- dir_return = frame->direntproc (frame->callerdat, dir, newrepos,
- update_dir, frent->entries);
+ {
+ /* If we're doing the actual processing, call direntproc.
+ Otherwise, assume that we need to process this directory
+ and recurse. FIXME. */
+
+ if (process_this_directory)
+ dir_return = frame->direntproc (frame->callerdat, dir, newrepos,
+ update_dir, frent->entries);
+ else
+ dir_return = R_PROCESS;
+ }
else
{
/* Generic behavior. I don't see a reason to make the caller specify
@@ -811,7 +1089,7 @@ but CVS uses %s for its own purposes; skipping %s directory",
(void) strcpy (update_dir, ".");
/* call-back dir leave proc (if any) */
- if (frame->dirleaveproc != NULL)
+ if (process_this_directory && frame->dirleaveproc != NULL)
err = frame->dirleaveproc (frame->callerdat, dir, err, update_dir,
frent->entries);
diff --git a/contrib/cvs/src/server.c b/contrib/cvs/src/server.c
index 4c18fc9..63fef8c 100644
--- a/contrib/cvs/src/server.c
+++ b/contrib/cvs/src/server.c
@@ -8,6 +8,10 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. */
+/*
+ * $FreeBSD$
+ */
+
#include <assert.h>
#include "cvs.h"
#include "watch.h"
@@ -133,7 +137,7 @@ char *CVS_Username = NULL;
/* Used to check that same repos is transmitted in pserver auth and in
later CVS protocol. Exported because root.c also uses. */
-char *Pserver_Repos = NULL;
+static char *Pserver_Repos = NULL;
/* Should we check for system usernames/passwords? Can be changed by
CVSROOT/config. */
@@ -341,6 +345,182 @@ fd_buffer_block (closure, block)
return 0;
}
+/* Populate all of the directories between BASE_DIR and its relative
+ subdirectory DIR with CVSADM directories. Return 0 for success or
+ errno value. */
+static int create_adm_p PROTO((char *, char *));
+
+static int
+create_adm_p (base_dir, dir)
+ char *base_dir;
+ char *dir;
+{
+ char *dir_where_cvsadm_lives, *dir_to_register, *p, *tmp;
+ int retval, done;
+ FILE *f;
+
+ if (strcmp (dir, ".") == 0)
+ return 0; /* nothing to do */
+
+ /* Allocate some space for our directory-munging string. */
+ p = malloc (strlen (dir) + 1);
+ if (p == NULL)
+ return ENOMEM;
+
+ dir_where_cvsadm_lives = malloc (strlen (base_dir) + strlen (dir) + 100);
+ if (dir_where_cvsadm_lives == NULL)
+ return ENOMEM;
+
+ /* Allocate some space for the temporary string in which we will
+ construct filenames. */
+ tmp = malloc (strlen (base_dir) + strlen (dir) + 100);
+ if (tmp == NULL)
+ return ENOMEM;
+
+
+ /* We make several passes through this loop. On the first pass,
+ we simply create the CVSADM directory in the deepest directory.
+ For each subsequent pass, we try to remove the last path
+ element from DIR, create the CVSADM directory in the remaining
+ pathname, and register the subdirectory in the newly created
+ CVSADM directory. */
+
+ retval = done = 0;
+
+ strcpy (p, dir);
+ strcpy (dir_where_cvsadm_lives, base_dir);
+ strcat (dir_where_cvsadm_lives, "/");
+ strcat (dir_where_cvsadm_lives, p);
+ dir_to_register = NULL;
+
+ while (1)
+ {
+ /* Create CVSADM. */
+ (void) sprintf (tmp, "%s/%s", dir_where_cvsadm_lives, CVSADM);
+ if ((CVS_MKDIR (tmp, 0777) < 0) && (errno != EEXIST))
+ {
+ retval = errno;
+ goto finish;
+ }
+
+ /* Create CVSADM_REP. */
+ (void) sprintf (tmp, "%s/%s", dir_where_cvsadm_lives, CVSADM_REP);
+ if (! isfile (tmp))
+ {
+ /* Use Emptydir as the placeholder until the client sends
+ us the real value. This code is similar to checkout.c
+ (emptydir_name), but the code below returns errors
+ differently. */
+
+ char *empty;
+ empty = malloc (strlen (CVSroot_directory)
+ + sizeof (CVSROOTADM)
+ + sizeof (CVSNULLREPOS)
+ + 10);
+ if (! empty)
+ {
+ retval = ENOMEM;
+ goto finish;
+ }
+
+ /* Create the directory name. */
+ (void) sprintf (empty, "%s/%s/%s", CVSroot_directory,
+ CVSROOTADM, CVSNULLREPOS);
+
+ /* Create the directory if it doesn't exist. */
+ if (! isfile (empty))
+ {
+ mode_t omask;
+ omask = umask (cvsumask);
+ if (CVS_MKDIR (empty, 0777) < 0)
+ {
+ retval = errno;
+ free (empty);
+ goto finish;
+ }
+ (void) umask (omask);
+ }
+
+
+ f = CVS_FOPEN (tmp, "w");
+ if (f == NULL)
+ {
+ retval = errno;
+ free (empty);
+ goto finish;
+ }
+ /* Write the directory name to CVSADM_REP. */
+ if (fprintf (f, "%s\n", empty) < 0)
+ {
+ retval = errno;
+ fclose (f);
+ free (empty);
+ goto finish;
+ }
+ if (fclose (f) == EOF)
+ {
+ retval = errno;
+ free (empty);
+ goto finish;
+ }
+
+ /* Clean up after ourselves. */
+ free (empty);
+ }
+
+ /* Create CVSADM_ENT. We open in append mode because we
+ don't want to clobber an existing Entries file. */
+ (void) sprintf (tmp, "%s/%s", dir_where_cvsadm_lives, CVSADM_ENT);
+ f = CVS_FOPEN (tmp, "a");
+ if (f == NULL)
+ {
+ retval = errno;
+ goto finish;
+ }
+ if (fclose (f) == EOF)
+ {
+ retval = errno;
+ goto finish;
+ }
+
+ if (dir_to_register != NULL)
+ {
+ /* FIXME: Yes, this results in duplicate entries in the
+ Entries.Log file, but it doesn't currently matter. We
+ might need to change this later on to make sure that we
+ only write one entry. */
+
+ Subdir_Register ((List *) NULL, dir_where_cvsadm_lives,
+ dir_to_register);
+ }
+
+ if (done)
+ break;
+
+ dir_to_register = strrchr (p, '/');
+ if (dir_to_register == NULL)
+ {
+ dir_to_register = p;
+ strcpy (dir_where_cvsadm_lives, base_dir);
+ done = 1;
+ }
+ else
+ {
+ *dir_to_register = '\0';
+ dir_to_register++;
+ strcpy (dir_where_cvsadm_lives, base_dir);
+ strcat (dir_where_cvsadm_lives, "/");
+ strcat (dir_where_cvsadm_lives, p);
+ }
+ }
+
+ finish:
+ free (tmp);
+ free (dir_where_cvsadm_lives);
+ free (p);
+ return retval;
+}
+
/*
* Make directory DIR, including all intermediate directories if necessary.
* Returns 0 for success or errno code.
@@ -563,6 +743,7 @@ serve_root (arg)
char *env;
char *path;
int save_errno;
+ char *arg_dup;
if (error_pending()) return;
@@ -574,9 +755,7 @@ serve_root (arg)
return;
}
- /* Sending "Root" twice is illegal. It would also be nice to
- check for the other case, in which there is no Root request
- prior to a request which requires one.
+ /* Sending "Root" twice is illegal.
The other way to handle a duplicate Root requests would be as a
request to clear out all state and start over as if it was a
@@ -591,7 +770,29 @@ serve_root (arg)
return;
}
- set_local_cvsroot (arg);
+#ifdef AUTH_SERVER_SUPPORT
+ if (Pserver_Repos != NULL)
+ {
+ if (strcmp (Pserver_Repos, arg) != 0)
+ {
+ if (alloc_pending (80 + strlen (Pserver_Repos) + strlen (arg)))
+ /* The explicitness is to aid people who are writing clients.
+ I don't see how this information could help an
+ attacker. */
+ sprintf (pending_error_text, "\
+E Protocol error: Root says \"%s\" but pserver says \"%s\"",
+ arg, Pserver_Repos);
+ }
+ }
+#endif
+ arg_dup = malloc (strlen (arg) + 1);
+ if (arg_dup == NULL)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
+ strcpy (arg_dup, arg);
+ set_local_cvsroot (arg_dup);
/* For pserver, this will already have happened, and the call will do
nothing. But for rsh, we need to do it now. */
@@ -600,10 +801,15 @@ serve_root (arg)
/* Now is a good time to read CVSROOT/options too. */
parseopts(CVSroot_directory);
- path = xmalloc (strlen (CVSroot_directory)
- + sizeof (CVSROOTADM)
- + sizeof (CVSROOTADM_HISTORY)
- + 10);
+ path = malloc (strlen (CVSroot_directory)
+ + sizeof (CVSROOTADM)
+ + sizeof (CVSROOTADM_HISTORY)
+ + 10);
+ if (path == NULL)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
(void) sprintf (path, "%s/%s", CVSroot_directory, CVSROOTADM);
if (!isaccessible (path, R_OK | X_OK))
{
@@ -673,6 +879,73 @@ server_pathname_check (path)
}
}
+static int outside_root PROTO ((char *));
+
+/* Is file or directory REPOS an absolute pathname within the
+ CVSroot_directory? If yes, return 0. If no, set pending_error
+ and return 1. */
+static int
+outside_root (repos)
+ char *repos;
+{
+ size_t repos_len = strlen (repos);
+ size_t root_len = strlen (CVSroot_directory);
+
+ /* I think isabsolute (repos) should always be true, and that
+ any RELATIVE_REPOS stuff should only be in CVS/Repository
+ files, not the protocol (for compatibility), but I'm putting
+ in the isabsolute check just in case. */
+ if (!isabsolute (repos))
+ {
+ if (alloc_pending (repos_len + 80))
+ sprintf (pending_error_text, "\
+E protocol error: %s is not absolute", repos);
+ return 1;
+ }
+
+ if (repos_len < root_len
+ || strncmp (CVSroot_directory, repos, root_len) != 0)
+ {
+ not_within:
+ if (alloc_pending (strlen (CVSroot_directory)
+ + strlen (repos)
+ + 80))
+ sprintf (pending_error_text, "\
+E protocol error: directory '%s' not within root '%s'",
+ repos, CVSroot_directory);
+ return 1;
+ }
+ if (repos_len > root_len)
+ {
+ if (repos[root_len] != '/')
+ goto not_within;
+ if (pathname_levels (repos + root_len + 1) > 0)
+ goto not_within;
+ }
+ return 0;
+}
+
+static int outside_dir PROTO ((char *));
+
+/* Is file or directory FILE outside the current directory (that is, does
+ it contain '/')? If no, return 0. If yes, set pending_error
+ and return 1. */
+static int
+outside_dir (file)
+ char *file;
+{
+ if (strchr (file, '/') != NULL)
+ {
+ if (alloc_pending (strlen (file)
+ + 80))
+ sprintf (pending_error_text, "\
+E protocol error: directory '%s' not within current directory",
+ file);
+ return 1;
+ }
+ return 0;
+}
+
/*
* Add as many directories to the temp directory as the client tells us it
* will use "..", so we never try to access something outside the temp
@@ -712,13 +985,31 @@ dirswitch (dir, repos)
{
int status;
FILE *f;
- char *b;
size_t dir_len;
server_write_entries ();
if (error_pending()) return;
+ /* Check for bad directory name.
+
+ FIXME: could/should unify these checks with server_pathname_check
+ except they need to report errors differently. */
+ if (isabsolute (dir))
+ {
+ if (alloc_pending (80 + strlen (dir)))
+ sprintf (pending_error_text,
+ "E absolute pathname `%s' illegal for server", dir);
+ return;
+ }
+ if (pathname_levels (dir) > max_dotdot_limit)
+ {
+ if (alloc_pending (80 + strlen (dir)))
+ sprintf (pending_error_text,
+ "E protocol error: `%s' has too many ..", dir);
+ return;
+ }
+
if (dir_name != NULL)
free (dir_name);
@@ -748,7 +1039,7 @@ dirswitch (dir, repos)
strcat (dir_name, "/");
strcat (dir_name, dir);
- status = mkdir_p (dir_name);
+ status = mkdir_p (dir_name);
if (status != 0
&& status != EEXIST)
{
@@ -758,16 +1049,20 @@ dirswitch (dir, repos)
return;
}
- /* Note that this call to Subdir_Register will be a noop if the parent
- directory does not yet exist (for example, if the client sends
- "Directory foo" followed by "Directory .", then the subdirectory does
- not get registered, but if the client sends "Directory ." followed
- by "Directory foo", then the subdirectory does get registered.
- This seems pretty fishy, but maybe it is the way it needs to work. */
- b = strrchr (dir_name, '/');
- *b = '\0';
- Subdir_Register ((List *) NULL, dir_name, b + 1);
- *b = '/';
+ /* We need to create adm directories in all path elements because
+ we want the server to descend them, even if the client hasn't
+ sent the appropriate "Argument xxx" command to match the
+ already-sent "Directory xxx" command. See recurse.c
+ (start_recursion) for a big discussion of this. */
+
+ status = create_adm_p (server_temp_dir, dir);
+ if (status != 0)
+ {
+ pending_error = status;
+ if (alloc_pending (80 + strlen (dir_name)))
+ sprintf (pending_error_text, "E cannot create_adm_p %s", dir_name);
+ return;
+ }
if ( CVS_CHDIR (dir_name) < 0)
{
@@ -780,14 +1075,17 @@ dirswitch (dir, repos)
* This is pretty much like calling Create_Admin, but Create_Admin doesn't
* report errors in the right way for us.
*/
- if (CVS_MKDIR (CVSADM, 0777) < 0)
+ if ((CVS_MKDIR (CVSADM, 0777) < 0) && (errno != EEXIST))
{
- if (errno == EEXIST)
- /* Don't create the files again. */
- return;
pending_error = errno;
return;
}
+
+ /* The following will overwrite the contents of CVSADM_REP. This
+ is the correct behavior -- mkdir_p may have written a
+ placeholder value to this file and we need to insert the
+ correct value. */
+
f = CVS_FOPEN (CVSADM_REP, "w");
if (f == NULL)
{
@@ -867,24 +1165,8 @@ serve_directory (arg)
status = buf_read_line (buf_from_net, &repos, (int *) NULL);
if (status == 0)
{
- /* I think isabsolute (repos) should always be true, and that
- any RELATIVE_REPOS stuff should only be in CVS/Repository
- files, not the protocol (for compatibility), but I'm putting
- in the in isabsolute check just in case. */
- if (isabsolute (repos)
- && strncmp (CVSroot_directory,
- repos,
- strlen (CVSroot_directory)) != 0)
- {
- if (alloc_pending (strlen (CVSroot_directory)
- + strlen (repos)
- + 80))
- sprintf (pending_error_text, "\
-E protocol error: directory '%s' not within root '%s'",
- repos, CVSroot_directory);
+ if (outside_root (repos))
return;
- }
-
dirswitch (arg, repos);
free (repos);
}
@@ -1058,8 +1340,6 @@ receive_file (size, file, gzipped)
{
int fd;
char *arg = file;
- pid_t gzip_pid = 0;
- int gzip_status;
/* Write the file. */
fd = CVS_OPEN (arg, O_WRONLY | O_CREAT | O_TRUNC, 0600);
@@ -1072,15 +1352,78 @@ receive_file (size, file, gzipped)
return;
}
- /*
- * FIXME: This doesn't do anything reasonable with gunzip's stderr, which
- * means that if gunzip writes to stderr, it will cause all manner of
- * protocol violations.
- */
if (gzipped)
- fd = filter_through_gunzip (fd, 0, &gzip_pid);
+ {
+ /* Using gunzip_and_write isn't really a high-performance
+ approach, because it keeps the whole thing in memory
+ (contiguous memory, worse yet). But it seems easier to
+ code than the alternative (and less vulnerable to subtle
+ bugs). Given that this feature is mainly for
+ compatibility, that is the better tradeoff. */
+
+ int toread = size;
+ char *filebuf;
+ char *p;
- receive_partial_file (size, fd);
+ filebuf = malloc (size);
+ p = filebuf;
+ /* If NULL, we still want to read the data and discard it. */
+
+ while (toread > 0)
+ {
+ int status, nread;
+ char *data;
+
+ status = buf_read_data (buf_from_net, toread, &data, &nread);
+ if (status != 0)
+ {
+ if (status == -2)
+ pending_error = ENOMEM;
+ else
+ {
+ pending_error_text = malloc (80);
+ if (pending_error_text == NULL)
+ pending_error = ENOMEM;
+ else if (status == -1)
+ {
+ sprintf (pending_error_text,
+ "E premature end of file from client");
+ pending_error = 0;
+ }
+ else
+ {
+ sprintf (pending_error_text,
+ "E error reading from client");
+ pending_error = status;
+ }
+ }
+ return;
+ }
+
+ toread -= nread;
+
+ if (filebuf != NULL)
+ {
+ memcpy (p, data, nread);
+ p += nread;
+ }
+ }
+ if (filebuf == NULL)
+ {
+ pending_error = ENOMEM;
+ goto out;
+ }
+
+ if (gunzip_and_write (fd, file, filebuf, size))
+ {
+ if (alloc_pending (80))
+ sprintf (pending_error_text,
+ "E aborting due to compression error");
+ }
+ free (filebuf);
+ }
+ else
+ receive_partial_file (size, fd);
if (pending_error_text)
{
@@ -1094,30 +1437,25 @@ receive_file (size, file, gzipped)
/* else original string is supposed to be unchanged */
}
+ out:
if (close (fd) < 0 && !error_pending ())
{
pending_error_text = malloc (40 + strlen (arg));
if (pending_error_text)
sprintf (pending_error_text, "E cannot close %s", arg);
pending_error = errno;
- if (gzip_pid)
- waitpid (gzip_pid, (int *) 0, 0);
return;
}
-
- if (gzip_pid)
- {
- if (waitpid (gzip_pid, &gzip_status, 0) != gzip_pid)
- error (1, errno, "waiting for gunzip process %ld",
- (long) gzip_pid);
- else if (gzip_status != 0)
- error (1, 0, "gunzip exited %d", gzip_status);
- }
}
/* Kopt for the next file sent in Modified or Is-modified. */
static char *kopt;
+/* Timestamp (Checkin-time) for next file sent in Modified or
+ Is-modified. */
+static int checkin_time_valid;
+static time_t checkin_time;
+
static void serve_modified PROTO ((char *));
static void
@@ -1215,12 +1553,31 @@ serve_modified (arg)
return;
}
+ if (outside_dir (arg))
+ return;
+
if (size >= 0)
{
receive_file (size, arg, gzipped);
if (error_pending ()) return;
}
+ if (checkin_time_valid)
+ {
+ struct utimbuf t;
+
+ memset (&t, 0, sizeof (t));
+ t.modtime = t.actime = checkin_time;
+ if (utime (arg, &t) < 0)
+ {
+ pending_error = errno;
+ if (alloc_pending (80 + strlen (arg)))
+ sprintf (pending_error_text, "E cannot utime %s", arg);
+ return;
+ }
+ checkin_time_valid = 0;
+ }
+
{
int status = change_mode (arg, mode_text, 0);
free (mode_text);
@@ -1271,6 +1628,9 @@ serve_unchanged (arg)
if (error_pending ())
return;
+ if (outside_dir (arg))
+ return;
+
/* Rewrite entries file to have `=' in timestamp field. */
for (p = entries; p != NULL; p = p->next)
{
@@ -1311,6 +1671,9 @@ serve_is_modified (arg)
if (error_pending ())
return;
+ if (outside_dir (arg))
+ return;
+
/* Rewrite entries file to have `M' in timestamp field. */
found = 0;
for (p = entries; p != NULL; p = p->next)
@@ -1446,6 +1809,34 @@ serve_kopt (arg)
strcpy (kopt, arg);
}
+static void serve_checkin_time PROTO ((char *));
+
+static void
+serve_checkin_time (arg)
+ char *arg;
+{
+ if (error_pending ())
+ return;
+
+ if (checkin_time_valid)
+ {
+ if (alloc_pending (80 + strlen (arg)))
+ sprintf (pending_error_text,
+ "E protocol error: duplicate Checkin-time request: %s",
+ arg);
+ return;
+ }
+
+ checkin_time = get_date (arg, NULL);
+ if (checkin_time == (time_t)-1)
+ {
+ if (alloc_pending (80 + strlen (arg)))
+ sprintf (pending_error_text, "E cannot parse date %s", arg);
+ return;
+ }
+ checkin_time_valid = 1;
+}
+
static void
server_write_entries ()
{
@@ -1533,6 +1924,9 @@ serve_notify (arg)
if (error_pending ()) return;
+ if (outside_dir (arg))
+ return;
+
new = (struct notify_note *) malloc (sizeof (struct notify_note));
if (new == NULL)
{
@@ -1584,6 +1978,9 @@ serve_notify (arg)
{
char *cp;
+ if (strchr (data, '+'))
+ goto error;
+
new->type = data;
if (data[1] != '\t')
goto error;
@@ -1623,7 +2020,7 @@ serve_notify (arg)
}
return;
error:
- pending_error_text = malloc (40);
+ pending_error_text = malloc (80);
if (pending_error_text)
strcpy (pending_error_text,
"E Protocol error; misformed Notify request");
@@ -1684,6 +2081,8 @@ server_notify ()
Lock_Cleanup ();
}
+ last_node = NULL;
+
/* The code used to call fflush (stdout) here, but that is no
longer necessary. The data is now buffered in buf_to_net,
which will be flushed by the caller, do_cvs_command. */
@@ -1929,6 +2328,9 @@ serve_questionable (arg)
return;
}
+ if (outside_dir (arg))
+ return;
+
if (!ign_name (arg))
{
char *update_dir;
@@ -2882,8 +3284,8 @@ server_register (name, version, timestamp, options, tag, date, conflict)
if (trace)
{
(void) fprintf (stderr,
- "%c-> server_register(%s, %s, %s, %s, %s, %s, %s)\n",
- (server_active) ? 'S' : ' ', /* silly */
+ "%s-> server_register(%s, %s, %s, %s, %s, %s, %s)\n",
+ CLIENT_SERVER_STR,
name, version, timestamp ? timestamp : "", options,
tag ? tag : "", date ? date : "",
conflict ? conflict : "");
@@ -3349,6 +3751,12 @@ server_copy_file (file, update_dir, repository, newfile)
char *repository;
char *newfile;
{
+ /* At least for now, our practice is to have the server enforce
+ noexec for the repository and the client enforce it for the
+ working directory. This might want more thought, and/or
+ documentation in cvsclient.texi (other responses do it
+ differently). */
+
if (!supported_response ("Copy-file"))
return;
buf_output0 (protocol, "Copy-file ");
@@ -3367,33 +3775,21 @@ server_modtime (finfo, vers_ts)
Vers_TS *vers_ts;
{
char date[MAXDATELEN];
- int year, month, day, hour, minute, second;
- /* Note that these strings are specified in RFC822 and do not vary
- according to locale. */
- static const char *const month_names[] =
- {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+ char outdate[MAXDATELEN];
assert (vers_ts->vn_rcs != NULL);
if (!supported_response ("Mod-time"))
return;
- /* The only hard part about this routine is converting the date
- formats. In terms of functionality it all boils down to the
- call to RCS_getrevtime. */
if (RCS_getrevtime (finfo->rcs, vers_ts->vn_rcs, date, 0) == (time_t) -1)
/* FIXME? should we be printing some kind of warning? For one
thing I'm not 100% sure whether this happens in non-error
circumstances. */
return;
-
- sscanf (date, SDATEFORM, &year, &month, &day, &hour, &minute, &second);
- sprintf (date, "%d %s %d %d:%d:%d -0000", day,
- month < 1 || month > 12 ? "???" : month_names[month - 1],
- year, hour, minute, second);
+ date_to_internet (outdate, date);
buf_output0 (protocol, "Mod-time ");
- buf_output0 (protocol, date);
+ buf_output0 (protocol, outdate);
buf_output0 (protocol, "\n");
}
@@ -3441,6 +3837,12 @@ server_updated (finfo, vers, updated, mode, checksum, filebuf)
unsigned long size;
char size_text[80];
+ /* The contents of the file will be in one of filebuf,
+ list/last, or here. */
+ unsigned char *file;
+ size_t file_allocated;
+ size_t file_used;
+
if (filebuf != NULL)
{
size = buf_length (filebuf);
@@ -3549,6 +3951,11 @@ CVS server internal error: no mode in server_updated");
}
list = last = NULL;
+
+ file = NULL;
+ file_allocated = 0;
+ file_used = 0;
+
if (size > 0)
{
/* Throughout this section we use binary mode to read the
@@ -3564,8 +3971,14 @@ CVS server internal error: no mode in server_updated");
*/
&& size > 100)
{
- int status, fd, gzip_status;
- pid_t gzip_pid;
+ /* Basing this routine on read_and_gzip is not a
+ high-performance approach. But it seems easier
+ to code than the alternative (and less
+ vulnerable to subtle bugs). Given that this feature
+ is mainly for compatibility, that is the better
+ tradeoff. */
+
+ int fd;
/* Callers must avoid passing us a buffer if
file_gzip_level is set. We could handle this case,
@@ -3578,22 +3991,13 @@ CVS server internal error: unhandled case in server_updated");
fd = CVS_OPEN (finfo->file, O_RDONLY | OPEN_BINARY, 0);
if (fd < 0)
error (1, errno, "reading %s", finfo->fullname);
- fd = filter_through_gzip (fd, 1, file_gzip_level, &gzip_pid);
- f = fdopen (fd, "rb");
- status = buf_read_file_to_eof (f, &list, &last);
- size = buf_chain_length (list);
- if (status == -2)
- (*protocol->memory_error) (protocol);
- else if (status != 0)
- error (1, ferror (f) ? errno : 0, "reading %s",
- finfo->fullname);
- if (fclose (f) == EOF)
+ if (read_and_gzip (fd, finfo->fullname, &file,
+ &file_allocated, &file_used,
+ file_gzip_level))
+ error (1, 0, "aborting due to compression error");
+ size = file_used;
+ if (close (fd) < 0)
error (1, errno, "reading %s", finfo->fullname);
- if (waitpid (gzip_pid, &gzip_status, 0) == -1)
- error (1, errno, "waiting for gzip process %ld",
- (long) gzip_pid);
- else if (gzip_status != 0)
- error (1, 0, "gzip exited %d", gzip_status);
/* Prepending length with "z" is flag for using gzip here. */
buf_output0 (protocol, "z");
}
@@ -3618,7 +4022,13 @@ CVS server internal error: unhandled case in server_updated");
sprintf (size_text, "%lu\n", size);
buf_output0 (protocol, size_text);
- if (filebuf == NULL)
+ if (file != NULL)
+ {
+ buf_output (protocol, file, file_used);
+ free (file);
+ file = NULL;
+ }
+ else if (filebuf == NULL)
buf_append_data (protocol, list, last);
else
{
@@ -3641,7 +4051,11 @@ CVS server internal error: unhandled case in server_updated");
/* But if we are joining, we'll need the file when we call
join_file. */
&& !joining ())
- CVS_UNLINK (finfo->file);
+ {
+ if (CVS_UNLINK (finfo->file) < 0)
+ error (0, errno, "cannot remove temp file for %s",
+ finfo->fullname);
+ }
}
else if (scratched_file != NULL && entries_line == NULL)
{
@@ -4136,78 +4550,81 @@ struct request requests[] =
#define REQ_LINE(n, f, s) {n, s}
#endif
- REQ_LINE("Root", serve_root, rq_essential),
- REQ_LINE("Valid-responses", serve_valid_responses, rq_essential),
- REQ_LINE("valid-requests", serve_valid_requests, rq_essential),
- REQ_LINE("Repository", serve_repository, rq_optional),
- REQ_LINE("Directory", serve_directory, rq_essential),
- REQ_LINE("Max-dotdot", serve_max_dotdot, rq_optional),
- REQ_LINE("Static-directory", serve_static_directory, rq_optional),
- REQ_LINE("Sticky", serve_sticky, rq_optional),
- REQ_LINE("Checkin-prog", serve_checkin_prog, rq_optional),
- REQ_LINE("Update-prog", serve_update_prog, rq_optional),
- REQ_LINE("Entry", serve_entry, rq_essential),
- REQ_LINE("Kopt", serve_kopt, rq_optional),
- REQ_LINE("Modified", serve_modified, rq_essential),
- REQ_LINE("Is-modified", serve_is_modified, rq_optional),
+ REQ_LINE("Root", serve_root, RQ_ESSENTIAL | RQ_ROOTLESS),
+ REQ_LINE("Valid-responses", serve_valid_responses,
+ RQ_ESSENTIAL | RQ_ROOTLESS),
+ REQ_LINE("valid-requests", serve_valid_requests,
+ RQ_ESSENTIAL | RQ_ROOTLESS),
+ REQ_LINE("Repository", serve_repository, 0),
+ REQ_LINE("Directory", serve_directory, RQ_ESSENTIAL),
+ REQ_LINE("Max-dotdot", serve_max_dotdot, 0),
+ REQ_LINE("Static-directory", serve_static_directory, 0),
+ REQ_LINE("Sticky", serve_sticky, 0),
+ REQ_LINE("Checkin-prog", serve_checkin_prog, 0),
+ REQ_LINE("Update-prog", serve_update_prog, 0),
+ REQ_LINE("Entry", serve_entry, RQ_ESSENTIAL),
+ REQ_LINE("Kopt", serve_kopt, 0),
+ REQ_LINE("Checkin-time", serve_checkin_time, 0),
+ REQ_LINE("Modified", serve_modified, RQ_ESSENTIAL),
+ REQ_LINE("Is-modified", serve_is_modified, 0),
/* The client must send this request to interoperate with CVS 1.5
through 1.9 servers. The server must support it (although it can
be and is a noop) to interoperate with CVS 1.5 to 1.9 clients. */
- REQ_LINE("UseUnchanged", serve_enable_unchanged, rq_enableme),
-
- REQ_LINE("Unchanged", serve_unchanged, rq_essential),
- REQ_LINE("Notify", serve_notify, rq_optional),
- REQ_LINE("Questionable", serve_questionable, rq_optional),
- REQ_LINE("Case", serve_case, rq_optional),
- REQ_LINE("Argument", serve_argument, rq_essential),
- REQ_LINE("Argumentx", serve_argumentx, rq_essential),
- REQ_LINE("Global_option", serve_global_option, rq_optional),
- REQ_LINE("Gzip-stream", serve_gzip_stream, rq_optional),
+ REQ_LINE("UseUnchanged", serve_enable_unchanged, RQ_ENABLEME | RQ_ROOTLESS),
+
+ REQ_LINE("Unchanged", serve_unchanged, RQ_ESSENTIAL),
+ REQ_LINE("Notify", serve_notify, 0),
+ REQ_LINE("Questionable", serve_questionable, 0),
+ REQ_LINE("Case", serve_case, 0),
+ REQ_LINE("Argument", serve_argument, RQ_ESSENTIAL),
+ REQ_LINE("Argumentx", serve_argumentx, RQ_ESSENTIAL),
+ REQ_LINE("Global_option", serve_global_option, 0),
+ REQ_LINE("Gzip-stream", serve_gzip_stream, 0),
REQ_LINE("wrapper-sendme-rcsOptions",
serve_wrapper_sendme_rcs_options,
- rq_optional),
- REQ_LINE("Set", serve_set, rq_optional),
+ 0),
+ REQ_LINE("Set", serve_set, RQ_ROOTLESS),
#ifdef ENCRYPTION
# ifdef HAVE_KERBEROS
- REQ_LINE("Kerberos-encrypt", serve_kerberos_encrypt, rq_optional),
+ REQ_LINE("Kerberos-encrypt", serve_kerberos_encrypt, 0),
# endif
# ifdef HAVE_GSSAPI
- REQ_LINE("Gssapi-encrypt", serve_gssapi_encrypt, rq_optional),
+ REQ_LINE("Gssapi-encrypt", serve_gssapi_encrypt, 0),
# endif
#endif
#ifdef HAVE_GSSAPI
- REQ_LINE("Gssapi-authenticate", serve_gssapi_authenticate, rq_optional),
+ REQ_LINE("Gssapi-authenticate", serve_gssapi_authenticate, 0),
#endif
- REQ_LINE("expand-modules", serve_expand_modules, rq_optional),
- REQ_LINE("ci", serve_ci, rq_essential),
- REQ_LINE("co", serve_co, rq_essential),
- REQ_LINE("update", serve_update, rq_essential),
- REQ_LINE("diff", serve_diff, rq_optional),
- REQ_LINE("log", serve_log, rq_optional),
- REQ_LINE("add", serve_add, rq_optional),
- REQ_LINE("remove", serve_remove, rq_optional),
- REQ_LINE("update-patches", serve_ignore, rq_optional),
- REQ_LINE("gzip-file-contents", serve_gzip_contents, rq_optional),
- REQ_LINE("status", serve_status, rq_optional),
- REQ_LINE("rdiff", serve_rdiff, rq_optional),
- REQ_LINE("tag", serve_tag, rq_optional),
- REQ_LINE("rtag", serve_rtag, rq_optional),
- REQ_LINE("import", serve_import, rq_optional),
- REQ_LINE("admin", serve_admin, rq_optional),
- REQ_LINE("export", serve_export, rq_optional),
- REQ_LINE("history", serve_history, rq_optional),
- REQ_LINE("release", serve_release, rq_optional),
- REQ_LINE("watch-on", serve_watch_on, rq_optional),
- REQ_LINE("watch-off", serve_watch_off, rq_optional),
- REQ_LINE("watch-add", serve_watch_add, rq_optional),
- REQ_LINE("watch-remove", serve_watch_remove, rq_optional),
- REQ_LINE("watchers", serve_watchers, rq_optional),
- REQ_LINE("editors", serve_editors, rq_optional),
- REQ_LINE("init", serve_init, rq_optional),
- REQ_LINE("annotate", serve_annotate, rq_optional),
- REQ_LINE("noop", serve_noop, rq_optional),
- REQ_LINE(NULL, NULL, rq_optional)
+ REQ_LINE("expand-modules", serve_expand_modules, 0),
+ REQ_LINE("ci", serve_ci, RQ_ESSENTIAL),
+ REQ_LINE("co", serve_co, RQ_ESSENTIAL),
+ REQ_LINE("update", serve_update, RQ_ESSENTIAL),
+ REQ_LINE("diff", serve_diff, 0),
+ REQ_LINE("log", serve_log, 0),
+ REQ_LINE("add", serve_add, 0),
+ REQ_LINE("remove", serve_remove, 0),
+ REQ_LINE("update-patches", serve_ignore, 0),
+ REQ_LINE("gzip-file-contents", serve_gzip_contents, 0),
+ REQ_LINE("status", serve_status, 0),
+ REQ_LINE("rdiff", serve_rdiff, 0),
+ REQ_LINE("tag", serve_tag, 0),
+ REQ_LINE("rtag", serve_rtag, 0),
+ REQ_LINE("import", serve_import, 0),
+ REQ_LINE("admin", serve_admin, 0),
+ REQ_LINE("export", serve_export, 0),
+ REQ_LINE("history", serve_history, 0),
+ REQ_LINE("release", serve_release, 0),
+ REQ_LINE("watch-on", serve_watch_on, 0),
+ REQ_LINE("watch-off", serve_watch_off, 0),
+ REQ_LINE("watch-add", serve_watch_add, 0),
+ REQ_LINE("watch-remove", serve_watch_remove, 0),
+ REQ_LINE("watchers", serve_watchers, 0),
+ REQ_LINE("editors", serve_editors, 0),
+ REQ_LINE("init", serve_init, RQ_ROOTLESS),
+ REQ_LINE("annotate", serve_annotate, 0),
+ REQ_LINE("noop", serve_noop, 0),
+ REQ_LINE(NULL, NULL, 0)
#undef REQ_LINE
};
@@ -4605,7 +5022,31 @@ error ENOMEM Virtual memory exhausted.\n");
* "co".
*/
continue;
- (*rq->func) (cmd);
+
+ if (!(rq->flags & RQ_ROOTLESS)
+ && CVSroot_directory == NULL)
+ {
+ /* For commands which change the way in which data
+ is sent and received, for example Gzip-stream,
+ this does the wrong thing. Since the client
+ assumes that everything is being compressed,
+ unconditionally, there is no way to give this
+ error to the client without turning on
+ compression. The obvious fix would be to make
+ Gzip-stream RQ_ROOTLESS (with the corresponding
+ change to the spec), and that might be a good
+ idea but then again I can see some settings in
+ CVSROOT about what compression level to allow.
+ I suppose a more baroque answer would be to
+ turn on compression (say, at level 1), just
+ enough to give the "Root request missing"
+ error. For now we just lose. */
+ if (alloc_pending (80))
+ sprintf (pending_error_text,
+ "E Protocol error: Root request missing");
+ }
+ else
+ (*rq->func) (cmd);
break;
}
if (rq->name == NULL)
@@ -4637,48 +5078,74 @@ switch_to_user (username)
pw = getpwnam (username);
if (pw == NULL)
{
+ /* Normally this won't be reached; check_password contains
+ a similar check. */
+
printf ("E Fatal error, aborting.\n\
error 0 %s: no such user\n", username);
- /* I'm doing this manually rather than via error_exit ()
- because I'm not sure whether we want to call server_cleanup.
- Needs more investigation.... */
-
-#ifdef SYSTEM_CLEANUP
- /* Hook for OS-specific behavior, for example socket subsystems on
- NT and OS2 or dealing with windows and arguments on Mac. */
- SYSTEM_CLEANUP ();
-#endif
-
- exit (EXIT_FAILURE);
+ /* Don't worry about server_cleanup; server_active isn't set yet. */
+ error_exit ();
}
- /* FIXME? We don't check for errors from initgroups, setuid, &c.
- I think this mainly would come up if someone is trying to run
- the server as a non-root user. I think we should be checking for
- errors and aborting (as with the error above from getpwnam) if
- there is an error (presumably EPERM). That means that pserver
- should continue to work right if all of the "system usernames"
- in CVSROOT/passwd match the user which the server is being run
- as (in inetd.conf), but fail otherwise. */
-
#if HAVE_INITGROUPS
- initgroups (pw->pw_name, pw->pw_gid);
+ if (initgroups (pw->pw_name, pw->pw_gid) < 0
+# ifdef EPERM
+ /* At least on the system I tried, initgroups() only works as root.
+ But we do still want to report ENOMEM and whatever other
+ errors initgroups() might dish up. */
+ && errno != EPERM
+# endif
+ )
+ {
+ /* This could be a warning, but I'm not sure I see the point
+ in doing that instead of an error given that it would happen
+ on every connection. We could log it somewhere and not tell
+ the user. But at least for now make it an error. */
+ printf ("error 0 initgroups failed: %s\n", strerror (errno));
+ /* Don't worry about server_cleanup; server_active isn't set yet. */
+ error_exit ();
+ }
#endif /* HAVE_INITGROUPS */
#ifdef SETXID_SUPPORT
/* honor the setgid bit iff set*/
if (getgid() != getegid())
{
- setgid (getegid ());
+ if (setgid (getegid ()) < 0)
+ {
+ /* See comments at setuid call below for more discussion. */
+ printf ("error 0 setuid failed: %s\n", strerror (errno));
+ /* Don't worry about server_cleanup;
+ server_active isn't set yet. */
+ error_exit ();
+ }
}
else
-#else
+#endif
{
- setgid (pw->pw_gid);
+ if (setgid (pw->pw_gid) < 0)
+ {
+ /* See comments at setuid call below for more discussion. */
+ printf ("error 0 setuid failed: %s\n", strerror (errno));
+ /* Don't worry about server_cleanup;
+ server_active isn't set yet. */
+ error_exit ();
+ }
}
-#endif
- setuid (pw->pw_uid);
+ if (setuid (pw->pw_uid) < 0)
+ {
+ /* Note that this means that if run as a non-root user,
+ CVSROOT/passwd must contain the user we are running as
+ (e.g. "joe:FsEfVcu:cvs" if run as "cvs" user). This seems
+ cleaner than ignoring the error like CVS 1.10 and older but
+ it does mean that some people might need to update their
+ CVSROOT/passwd file. */
+ printf ("error 0 setuid failed: %s\n", strerror (errno));
+ /* Don't worry about server_cleanup; server_active isn't set yet. */
+ error_exit ();
+ }
+
/* We don't want our umask to change file modes. The modes should
be set by the modes used in the repository, and by the umask of
the client. */
@@ -5089,32 +5556,23 @@ pserver_authenticate_connection ()
host_user = check_password (username, descrambled_password, repository);
memset (descrambled_password, 0, strlen (descrambled_password));
free (descrambled_password);
- if (host_user)
- {
- printf ("I LOVE YOU\n");
- fflush (stdout);
- }
- else
+ if (host_user == NULL)
{
i_hate_you:
printf ("I HATE YOU\n");
fflush (stdout);
- /* I'm doing this manually rather than via error_exit ()
- because I'm not sure whether we want to call server_cleanup.
- Needs more investigation.... */
-
-#ifdef SYSTEM_CLEANUP
- /* Hook for OS-specific behavior, for example socket subsystems on
- NT and OS2 or dealing with windows and arguments on Mac. */
- SYSTEM_CLEANUP ();
-#endif
- exit (EXIT_FAILURE);
+ /* Don't worry about server_cleanup, server_active isn't set
+ yet. */
+ error_exit ();
}
/* Don't go any farther if we're just responding to "cvs login". */
if (verify_and_exit)
{
+ printf ("I LOVE YOU\n");
+ fflush (stdout);
+
#ifdef SYSTEM_CLEANUP
/* Hook for OS-specific behavior, for example socket subsystems on
NT and OS2 or dealing with windows and arguments on Mac. */
@@ -5136,6 +5594,8 @@ pserver_authenticate_connection ()
free (username);
free (password);
+ printf ("I LOVE YOU\n");
+ fflush (stdout);
#endif /* AUTH_SERVER_SUPPORT */
}
diff --git a/contrib/cvs/src/update.c b/contrib/cvs/src/update.c
index dd64ab7..89e8323 100644
--- a/contrib/cvs/src/update.c
+++ b/contrib/cvs/src/update.c
@@ -1,36 +1,38 @@
/*
* Copyright (c) 1992, Brian Berliner and Jeff Polk
* Copyright (c) 1989-1992, Brian Berliner
- *
+ *
* You may distribute under the terms of the GNU General Public License as
* specified in the README file that comes with the CVS source distribution.
- *
+ *
* "update" updates the version in the present directory with respect to the RCS
* repository. The present version must have been created by "checkout". The
* user can keep up-to-date by calling "update" whenever he feels like it.
- *
+ *
* The present version can be committed by "commit", but this keeps the version
* in tact.
- *
+ *
* Arguments following the options are taken to be file names to be updated,
* rather than updating the entire directory.
- *
+ *
* Modified or non-existent RCS files are checked out and reported as U
* <user_file>
- *
+ *
* Modified user files are reported as M <user_file>. If both the RCS file and
* the user file have been modified, the user file is replaced by the result
* of rcsmerge, and a backup file is written for the user in .#file.version.
* If this throws up irreconcilable differences, the file is reported as C
* <user_file>, and as M <user_file> otherwise.
- *
+ *
* Files added but not yet committed are reported as A <user_file>. Files
* removed but not yet committed are reported as R <user_file>.
- *
+ *
* If the current directory contains subdirectories that hold concurrent
* versions, these are updated too. If the -d option was specified, new
* directories added to the repository are automatically created and updated
* as well.
+ *
+ * $FreeBSD$
*/
#include "cvs.h"
@@ -284,11 +286,11 @@ update (argc, argv)
if (failed_patches == NULL)
{
- send_file_names (argc, argv, SEND_EXPAND_WILD);
/* If noexec, probably could be setting SEND_NO_CONTENTS.
Same caveats as for "cvs status" apply. */
send_files (argc, argv, local, aflag,
update_build_dirs ? SEND_BUILD_DIRS : 0);
+ send_file_names (argc, argv, SEND_EXPAND_WILD);
}
else
{
@@ -304,10 +306,13 @@ update (argc, argv)
}
for (i = 0; i < failed_patches_count; i++)
- (void) unlink_file (failed_patches[i]);
- send_file_names (failed_patches_count, failed_patches, 0);
+ if (unlink_file (failed_patches[i]) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s",
+ failed_patches[i]);
send_files (failed_patches_count, failed_patches, local,
aflag, update_build_dirs ? SEND_BUILD_DIRS : 0);
+ send_file_names (failed_patches_count, failed_patches, 0);
}
failed_patches = NULL;
@@ -486,9 +491,12 @@ do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag,
{
time_t now;
- (void) time (&now);
- if (now == last_register_time)
+ for (;;)
+ {
+ (void) time (&now);
+ if (now != last_register_time) break;
sleep (1); /* to avoid time-stamp races */
+ }
}
return (err);
@@ -631,15 +639,7 @@ update_fileproc (callerdat, finfo)
write_letter (finfo, 'C');
break;
case T_NEEDS_MERGE: /* needs merging */
- if (noexec)
- {
- retval = 1;
- write_letter (finfo, 'C');
- }
- else
- {
- retval = merge_file (finfo, vers);
- }
+ retval = merge_file (finfo, vers);
break;
case T_MODIFIED: /* locally modified */
retval = 0;
@@ -874,6 +874,28 @@ update_dirent_proc (callerdat, dir, repository, update_dir, entries)
if (!update_build_dirs)
return (R_SKIP_ALL);
+ /* Various CVS administrators are in the habit of removing
+ the repository directory for things they don't want any
+ more. I've even been known to do it myself (on rare
+ occasions). Not the usual recommended practice, but we
+ want to try to come up with some kind of
+ reasonable/documented/sensible behavior. Generally
+ the behavior is to just skip over that directory (see
+ dirs test in sanity.sh; the case which reaches here
+ is when update -d is specified, and the working directory
+ is gone but the subdirectory is still mentioned in
+ CVS/Entries). */
+ if (1
+#ifdef SERVER_SUPPORT
+ /* In the remote case, the client should refrain from
+ sending us the directory in the first place. So we
+ want to continue to give an error, so clients make
+ sure to do this. */
+ && !server_active
+#endif
+ && !isdir (repository))
+ return R_SKIP_ALL;
+
if (noexec)
{
/* ignore the missing dir if -n is specified */
@@ -1285,11 +1307,14 @@ VERS: ", 0);
{
Vers_TS *xvers_ts;
- if (revbuf != NULL)
+ if (revbuf != NULL && !noexec)
{
struct stat sb;
- /* FIXME: We should have RCS_checkout return the mode. */
+ /* FIXME: We should have RCS_checkout return the mode.
+ That would also fix the kludge with noexec, above, which
+ is here only because noexec doesn't write srcfile->path
+ for us to stat. */
if (stat (vers_ts->srcfile->path, &sb) < 0)
error (1, errno, "cannot stat %s",
vers_ts->srcfile->path);
@@ -1489,7 +1514,7 @@ struct patch_file_data
/* Whether to compute the MD5 checksum. */
int compute_checksum;
/* Data structure for computing the MD5 checksum. */
- struct MD5Context context;
+ struct cvs_MD5Context context;
/* Set if the file has a final newline. */
int final_nl;
};
@@ -1568,7 +1593,11 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
if (isfile (finfo->file))
rename_file (finfo->file, backup);
else
- (void) unlink_file (backup);
+ {
+ if (unlink_file (backup) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", backup);
+ }
file1 = xmalloc (strlen (finfo->file)
+ sizeof (CVSADM)
@@ -1617,10 +1646,10 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
data.fp = e;
data.final_nl = 0;
data.compute_checksum = 1;
- MD5Init (&data.context);
+ cvs_MD5Init (&data.context);
retcode = RCS_checkout (vers_ts->srcfile, (char *) NULL,
- vers_ts->vn_rcs, (char *) NULL,
+ vers_ts->vn_rcs, vers_ts->vn_tag,
vers_ts->options, RUN_TTY,
patch_file_write, (void *) &data);
@@ -1630,7 +1659,7 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
if (retcode != 0 || ! data.final_nl)
fail = 1;
else
- MD5Final (checksum, &data.context);
+ cvs_MD5Final (checksum, &data.context);
}
retcode = 0;
@@ -1755,9 +1784,15 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
retval = retcode;
}
- (void) unlink_file (backup);
- (void) unlink_file (file1);
- (void) unlink_file (file2);
+ if (unlink_file (backup) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", backup);
+ if (unlink_file (file1) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", file1);
+ if (unlink_file (file2) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", file2);
free (backup);
free (file1);
@@ -1783,7 +1818,7 @@ patch_file_write (callerdat, buffer, len)
data->final_nl = (buffer[len - 1] == '\n');
if (data->compute_checksum)
- MD5Update (&data->context, (unsigned char *) buffer, len);
+ cvs_MD5Update (&data->context, (unsigned char *) buffer, len);
}
#endif /* SERVER_SUPPORT */
@@ -1859,7 +1894,8 @@ merge_file (finfo, vers)
+ 10);
(void) sprintf (backup, "%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user);
- (void) unlink_file (backup);
+ if (unlink_file (backup) && !existence_error (errno))
+ error (0, errno, "unable to remove %s", backup);
copy_file (finfo->file, backup);
xchmod (finfo->file, 1);
@@ -1962,10 +1998,17 @@ merge_file (finfo, vers)
}
#endif
+ /* FIXME: the noexec case is broken. RCS_merge could be doing the
+ xcmp on the temporary files without much hassle, I think. */
if (!noexec && !xcmp (backup, finfo->file))
{
- printf ("%s already contains the differences between %s and %s\n",
- finfo->fullname, vers->vn_user, vers->vn_rcs);
+ cvs_output (finfo->fullname, 0);
+ cvs_output (" already contains the differences between ", 0);
+ cvs_output (vers->vn_user, 0);
+ cvs_output (" and ", 0);
+ cvs_output (vers->vn_rcs, 0);
+ cvs_output ("\n", 1);
+
history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file,
finfo->repository);
retval = 0;
@@ -1974,8 +2017,7 @@ merge_file (finfo, vers)
if (status == 1)
{
- if (!noexec)
- error (0, 0, "conflicts found in %s", finfo->fullname);
+ error (0, 0, "conflicts found in %s", finfo->fullname);
write_letter (finfo, 'C');
@@ -2326,7 +2368,9 @@ join_file (finfo, vers)
+ 10);
(void) sprintf (backup, "%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user);
- (void) unlink_file (backup);
+ if (unlink_file (backup) < 0
+ && !existence_error (errno))
+ error (0, errno, "cannot remove %s", backup);
copy_file (finfo->file, backup);
xchmod (finfo->file, 1);
OpenPOWER on IntegriCloud