Merge changes from vendor branch into mainline

This commit is contained in:
Peter Wemm 1998-03-10 13:58:02 +00:00
parent beb1c32dd3
commit bde4819bd2
13 changed files with 2849 additions and 865 deletions

View File

@ -19,6 +19,7 @@
#include "getline.h"
#include "edit.h"
#include "fileattr.h"
#include "hardlink.h"
static Dtype check_direntproc PROTO ((void *callerdat, char *dir,
char *repos, char *update_dir,
@ -81,7 +82,6 @@ static List *mulist;
static char *message;
static time_t last_register_time;
static const char *const commit_usage[] =
{
"Usage: %s %s [-nRlf] [-m msg | -F logfile] [-r rev] files...\n",
@ -622,10 +622,23 @@ commit (argc, argv)
lock_tree_for_write (argc, argv, local, aflag);
/*
* Set up the master update list
* Set up the master update list and hard link list
*/
mulist = getlist ();
#ifdef PRESERVE_PERMISSIONS_SUPPORT
if (preserve_perms)
{
hardlist = getlist ();
/*
* We need to save the working directory so that
* check_fileproc can construct a full pathname for each file.
*/
working_dir = xgetwd();
}
#endif
/*
* Run the recursion processor to verify the files are all up-to-date
*/
@ -638,6 +651,17 @@ commit (argc, argv)
error (1, 0, "correct above errors first!");
}
#ifdef PRESERVE_PERMISSIONS_SUPPORT
if (preserve_perms)
{
/* hardlist now includes a complete index of the files
to be committed, indexed by inode. For each inode,
compile a list of the files that are linked to it,
and save this list in each file's hardlink_info node. */
(void) walklist (hardlist, cache_hardlinks_proc, NULL);
}
#endif
/*
* Run the recursion processor to commit the files
*/
@ -992,6 +1016,43 @@ warning: file `%s' seems to still contain conflict indicators",
ci->options = xstrdup(vers->options);
p->data = (char *) ci;
(void) addnode (cilist, p);
#ifdef PRESERVE_PERMISSIONS_SUPPORT
if (preserve_perms)
{
/* Add this file to hardlist, indexed on its inode. When
we are done, we can find out what files are hardlinked
to a given file by looking up its inode in hardlist. */
char *fullpath;
Node *linkp;
struct hardlink_info *hlinfo;
/* Get the full pathname of the current file. */
fullpath = xmalloc (strlen(working_dir) +
strlen(finfo->fullname) + 2);
sprintf (fullpath, "%s/%s", working_dir, finfo->fullname);
/* To permit following links in subdirectories, files
are keyed on finfo->fullname, not on finfo->name. */
linkp = lookup_file_by_inode (fullpath);
/* If linkp is NULL, the file doesn't exist... maybe
we're doing a remove operation? */
if (linkp != NULL)
{
/* Create a new hardlink_info node, which will record
the current file's status and the links listed in its
`hardlinks' delta field. We will append this
hardlink_info node to the appropriate hardlist entry. */
hlinfo = (struct hardlink_info *)
xmalloc (sizeof (struct hardlink_info));
hlinfo->status = status;
hlinfo->links = NULL;
linkp->data = (char *) hlinfo;
}
}
#endif
break;
case T_UNKNOWN:
error (0, 0, "nothing known about `%s'", finfo->fullname);
@ -1280,8 +1341,9 @@ commit_fileproc (callerdat, finfo)
/* Doesn't matter, it won't get checked. */
SERVER_UPDATED,
(struct stat *) NULL,
(unsigned char *) NULL);
(mode_t) -1,
(unsigned char *) NULL,
(struct buffer *) NULL);
}
#endif
}
@ -1645,9 +1707,8 @@ remove_file (finfo, tag, message)
(RCSCHECKOUTPROC) NULL, (void *) NULL);
if (retcode != 0)
{
if (!quiet)
error (0, retcode == -1 ? errno : 0,
"failed to check out `%s'", finfo->fullname);
error (0, 0,
"failed to check out `%s'", finfo->fullname);
return (1);
}
@ -1981,13 +2042,21 @@ internal error: `%s' didn't move out of the attic",
if (tag && newfile)
{
char *tmp;
FILE *fp;
/* move the new file out of the way. */
fname = xmalloc (strlen (file) + sizeof (CVSADM)
+ sizeof (CVSPREFIX) + 10);
(void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file);
rename_file (file, fname);
copy_file (DEVNULL, file);
/* Create empty FILE. Can't use copy_file with a DEVNULL
argument -- copy_file now ignores device files. */
fp = fopen (file, "w");
if (fp == NULL)
error (1, errno, "cannot open %s for writing", file);
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", file);
tmp = xmalloc (strlen (file) + strlen (tag) + 80);
/* commit a dead revision. */
@ -2158,18 +2227,31 @@ lock_RCS (user, rcs, rev, repository)
{
(void) RCS_lock(rcs, rev, 1);
}
RCS_rewrite (rcs, NULL, NULL);
/* We used to call RCS_rewrite here, and that might seem
appropriate in order to write out the locked revision
information. However, such a call would actually serve no
purpose. CVS locks will prevent any interference from other
CVS processes. The comment above rcs_internal_lockfile
explains that it is already unsafe to use RCS and CVS
simultaneously. It follows that writing out the locked
revision information here would add no additional security.
If we ever do care about it, the proper fix is to create the
RCS lock file before calling this function, and maintain it
until the checkin is complete.
The call to RCS_lock is still required at present, since in
some cases RCS_checkin will determine which revision to check
in by looking for a lock. FIXME: This is rather roundabout,
and a more straightforward approach would probably be easier to
understand. */
if (err == 0)
{
if (sbranch != NULL)
free (sbranch);
if (branch)
{
sbranch = branch;
}
else
sbranch = NULL;
sbranch = branch;
return (0);
}
@ -2184,7 +2266,8 @@ lock_RCS (user, rcs, rev, repository)
/* 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. */
the fact. TODO: now that RCS has been librarified, we have the power
to change this. */
static void
fix_rcs_modes (rcs, user)
@ -2194,6 +2277,12 @@ fix_rcs_modes (rcs, 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. */
@ -2203,6 +2292,9 @@ fix_rcs_modes (rcs, user)
/* 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

View File

@ -407,6 +407,7 @@ int RCS_merge PROTO((RCSNode *, char *, char *, char *, char *, char *));
#define RCS_FLAGS_DEAD 2
#define RCS_FLAGS_QUIET 4
#define RCS_FLAGS_MODTIME 8
#define RCS_FLAGS_KEEPFILE 16
extern int RCS_exec_rcsdiff PROTO ((RCSNode *rcsfile,
char *opts, char *options,
@ -427,7 +428,7 @@ DBM *open_module PROTO((void));
FILE *open_file PROTO((const char *, const char *));
List *Find_Directories PROTO((char *repository, int which, List *entries));
void Entries_Close PROTO((List *entries));
List *Entries_Open PROTO((int aflag));
List *Entries_Open PROTO ((int aflag, char *update_dir));
void Subdirs_Known PROTO((List *entries));
void Subdir_Register PROTO((List *, const char *, const char *));
void Subdir_Deregister PROTO((List *, const char *, const char *));
@ -466,10 +467,12 @@ int SIG_register PROTO((int sig, SIGCLEANUPPROC sigcleanup));
int isdir PROTO((const char *file));
int isfile PROTO((const char *file));
int islink PROTO((const char *file));
int isdevice PROTO ((const char *));
int isreadable PROTO((const char *file));
int iswritable PROTO((const char *file));
int isaccessible PROTO((const char *file, const int mode));
int isabsolute PROTO((const char *filename));
char *xreadlink PROTO((const char *link));
char *last_component PROTO((char *path));
char *get_homedir PROTO ((void));
char *cvs_temp_name PROTO ((void));
@ -733,6 +736,9 @@ void freevers_ts PROTO ((Vers_TS ** versp));
int Checkin PROTO ((int type, struct file_info *finfo, char *rcs, char *rev,
char *tag, char *options, char *message));
int No_Difference PROTO ((struct file_info *finfo, Vers_TS *vers));
/* TODO: can the finfo argument to special_file_mismatch be changed? -twp */
int special_file_mismatch PROTO ((struct file_info *finfo,
char *rev1, char *rev2));
/* CVSADM_BASEREV stuff, from entries.c. */
extern char *base_get PROTO ((struct file_info *));

View File

@ -638,13 +638,11 @@ RCS file: ", 0);
: vers->options),
tmp, (RCSCHECKOUTPROC) NULL,
(void *) NULL);
if (retcode == -1)
if (retcode != 0)
{
(void) CVS_UNLINK (tmp);
error (1, errno, "fork failed during checkout of %s",
vers->srcfile->path);
diff_mark_errors (err);
return err;
}
/* FIXME: what if retcode > 0? */
status = diff_exec (DEVNULL, tmp, opts, RUN_TTY);
}
@ -659,13 +657,11 @@ RCS file: ", 0);
*options ? options : vers->options,
tmp, (RCSCHECKOUTPROC) NULL,
(void *) NULL);
if (retcode == -1)
if (retcode != 0)
{
(void) CVS_UNLINK (tmp);
error (1, errno, "fork failed during checkout of %s",
vers->srcfile->path);
diff_mark_errors (err);
return err;
}
/* FIXME: what if retcode > 0? */
status = diff_exec (tmp, DEVNULL, opts, RUN_TTY);
}
@ -721,7 +717,8 @@ RCS file: ", 0);
if (empty_file == DIFF_REMOVED
|| (empty_file == DIFF_ADDED && use_rev2 != NULL))
{
(void) CVS_UNLINK (tmp);
if (CVS_UNLINK (tmp) < 0)
error (0, errno, "cannot remove %s", tmp);
free (tmp);
}

View File

@ -260,7 +260,11 @@ import (argc, argv)
tmpfile = cvs_temp_name ();
if ((logfp = CVS_FOPEN (tmpfile, "w+")) == NULL)
error (1, errno, "cannot create temporary file `%s'", tmpfile);
(void) CVS_UNLINK (tmpfile); /* to be sure it goes away */
/* On systems where we can unlink an open file, do so, so it will go
away no matter how we exit. FIXME-maybe: Should be checking for
errors but I'm not sure which error(s) we get if we are on a system
where one can't unlink open files. */
(void) CVS_UNLINK (tmpfile);
(void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]);
(void) fprintf (logfp, "Release Tags:\t");
for (i = 2; i < argc; i++)
@ -320,11 +324,13 @@ import (argc, argv)
(void) addnode (ulist, p);
Update_Logfile (repository, message, logfp, ulist);
dellist (&ulist);
(void) fclose (logfp);
if (fclose (logfp) < 0)
error (0, errno, "error closing %s", tmpfile);
/* Make sure the temporary file goes away, even on systems that don't let
you delete a file that's in use. */
CVS_UNLINK (tmpfile);
if (CVS_UNLINK (tmpfile) < 0 && !existence_error (errno))
error (0, errno, "cannot remove %s", tmpfile);
free (tmpfile);
if (message)
@ -491,7 +497,7 @@ process_import_file (message, vfile, vtag, targc, targv)
/* Reading all the entries for each file is fairly silly, and
probably slow. But I am too lazy at the moment to do
anything else. */
entries = Entries_Open (0);
entries = Entries_Open (0, NULL);
node = findnode_fn (entries, vfile);
if (node != NULL)
{
@ -977,6 +983,7 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
char *userfile;
char *local_opt = key_opt;
char *free_opt = NULL;
mode_t file_type;
if (noexec)
return (0);
@ -1004,18 +1011,39 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
which does not depend on what the client or server OS is, as
documented in cvsclient.texi), but as long as the server just
runs on unix it is a moot point. */
fpuser = CVS_FOPEN (userfile,
((local_opt != NULL && strcmp (local_opt, "b") == 0)
? "rb"
: "r")
);
if (fpuser == NULL)
/* If PreservePermissions is set, then make sure that the file
is a plain file before trying to open it. Longstanding (although
often unpopular) CVS behavior has been to follow symlinks, so we
maintain that behavior if PreservePermissions is not on.
NOTE: this error message used to be `cannot fstat', but is now
`cannot lstat'. I don't see a way around this, since we must
stat the file before opening it. -twp */
if (CVS_LSTAT (userfile, &sb) < 0)
error (1, errno, "cannot lstat %s", user);
file_type = sb.st_mode & S_IFMT;
fpuser = NULL;
if (!preserve_perms || file_type == S_IFREG)
{
/* not fatal, continue import */
fperror (add_logfp, 0, errno, "ERROR: cannot read file %s", userfile);
error (0, errno, "ERROR: cannot read file %s", userfile);
goto read_error;
fpuser = CVS_FOPEN (userfile,
((local_opt != NULL && strcmp (local_opt, "b") == 0)
? "rb"
: "r")
);
if (fpuser == NULL)
{
/* not fatal, continue import */
if (add_logfp != NULL)
fperror (add_logfp, 0, errno,
"ERROR: cannot read file %s", userfile);
error (0, errno, "ERROR: cannot read file %s", userfile);
goto read_error;
}
}
fprcs = CVS_FOPEN (rcs, "w+b");
if (fprcs == NULL)
{
@ -1082,10 +1110,6 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
if (fprintf (fprcs, "\012") < 0)
goto write_error;
/* Get information on modtime and mode. */
if (fstat (fileno (fpuser), &sb) < 0)
error (1, errno, "cannot fstat %s", user);
/* Write the revision(s), with the date and author and so on
(that is "delta" rather than "deltatext" from rcsfile(5)). */
if (add_vhead != NULL)
@ -1118,13 +1142,102 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
if (fprintf (fprcs, "next ;\012") < 0)
goto write_error;
#ifdef PRESERVE_PERMISSIONS_SUPPORT
/* Store initial permissions if necessary. */
if (preserve_perms)
{
if (file_type == S_IFLNK)
{
char *link = xreadlink (userfile);
if (fprintf (fprcs, "symlink\t@") < 0 ||
expand_at_signs (link, strlen (link), fprcs) < 0 ||
fprintf (fprcs, "@;\012") < 0)
goto write_error;
free (link);
}
else
{
if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0)
goto write_error;
if (fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0)
goto write_error;
if (fprintf (fprcs, "permissions\t%o;\012",
sb.st_mode & 07777) < 0)
goto write_error;
switch (file_type)
{
case S_IFREG: break;
case S_IFCHR:
case S_IFBLK:
if (fprintf (fprcs, "special\t%s %lu;\012",
(file_type == S_IFCHR
? "character"
: "block"),
(unsigned long) sb.st_rdev) < 0)
goto write_error;
break;
default:
error (0, 0,
"can't import %s: unknown kind of special file",
userfile);
}
}
}
#endif
if (add_vbranch != NULL)
{
if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 ||
fprintf (fprcs, "date %s; author %s; state Exp;\012",
altdate1, author) < 0 ||
fprintf (fprcs, "branches ;\012") < 0 ||
fprintf (fprcs, "next ;\012\012") < 0)
fprintf (fprcs, "next ;\012") < 0)
goto write_error;
#ifdef PRESERVE_PERMISSIONS_SUPPORT
/* Store initial permissions if necessary. */
if (preserve_perms)
{
if (file_type == S_IFLNK)
{
char *link = xreadlink (userfile);
if (fprintf (fprcs, "symlink\t@") < 0 ||
expand_at_signs (link, strlen (link), fprcs) < 0 ||
fprintf (fprcs, "@;\012") < 0)
goto write_error;
free (link);
}
else
{
if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0 ||
fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0 ||
fprintf (fprcs, "permissions\t%o;\012",
sb.st_mode & 07777) < 0)
goto write_error;
switch (file_type)
{
case S_IFREG: break;
case S_IFCHR:
case S_IFBLK:
if (fprintf (fprcs, "special\t%s %lu;\012",
(file_type == S_IFCHR
? "character"
: "block"),
(unsigned long) sb.st_rdev) < 0)
goto write_error;
break;
default:
error (0, 0,
"cannot import %s: special file of unknown type",
userfile);
}
}
}
#endif
if (fprintf (fprcs, "\012") < 0)
goto write_error;
}
}
@ -1170,7 +1283,9 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
goto write_error;
}
/* Now copy over the contents of the file, expanding at signs. */
/* Now copy over the contents of the file, expanding at signs.
If preserve_perms is set, do this only for regular files. */
if (!preserve_perms || file_type == S_IFREG)
{
char buf[8192];
unsigned int len;
@ -1208,7 +1323,12 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
ierrno = errno;
goto write_error_noclose;
}
(void) fclose (fpuser);
/* Close fpuser only if we opened it to begin with. */
if (fpuser != NULL)
{
if (fclose (fpuser) < 0)
error (0, errno, "cannot close %s", user);
}
/*
* Fix the modes on the RCS files. The user modes of the original
@ -1224,8 +1344,9 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
if (chmod (rcs, mode) < 0)
{
ierrno = errno;
fperror (add_logfp, 0, ierrno,
"WARNING: cannot change mode of file %s", rcs);
if (add_logfp != NULL)
fperror (add_logfp, 0, ierrno,
"WARNING: cannot change mode of file %s", rcs);
error (0, ierrno, "WARNING: cannot change mode of file %s", rcs);
err++;
}
@ -1238,15 +1359,20 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
write_error:
ierrno = errno;
(void) fclose (fprcs);
if (fclose (fprcs) < 0)
error (0, errno, "cannot close %s", rcs);
write_error_noclose:
(void) fclose (fpuser);
fperror (add_logfp, 0, ierrno, "ERROR: cannot write file %s", rcs);
if (fclose (fpuser) < 0)
error (0, errno, "cannot close %s", user);
if (add_logfp != NULL)
fperror (add_logfp, 0, ierrno, "ERROR: cannot write file %s", rcs);
error (0, ierrno, "ERROR: cannot write file %s", rcs);
if (ierrno == ENOSPC)
{
(void) CVS_UNLINK (rcs);
fperror (add_logfp, 0, 0, "ERROR: out of space - aborting");
if (CVS_UNLINK (rcs) < 0)
error (0, errno, "cannot remove %s", rcs);
if (add_logfp != NULL)
fperror (add_logfp, 0, 0, "ERROR: out of space - aborting");
error (1, 0, "ERROR: out of space - aborting");
}
read_error:
@ -1271,20 +1397,27 @@ expand_at_signs (buf, size, fp)
off_t size;
FILE *fp;
{
char *cp, *end;
register char *cp, *next;
errno = 0;
for (cp = buf, end = buf + size; cp < end; cp++)
cp = buf;
while ((next = memchr (cp, '@', size)) != NULL)
{
if (*cp == '@')
{
if (putc ('@', fp) == EOF && errno != 0)
return EOF;
}
if (putc (*cp, fp) == EOF && errno != 0)
return (EOF);
int len;
++next;
len = next - cp;
if (fwrite (cp, 1, len, fp) != len)
return EOF;
if (putc ('@', fp) == EOF)
return EOF;
cp = next;
size -= len;
}
return (1);
if (fwrite (cp, 1, size, fp) != size)
return EOF;
return 1;
}
/*

View File

@ -709,7 +709,6 @@ lock_obtained (repos)
static int lock_filesdoneproc PROTO ((void *callerdat, int err,
char *repository, char *update_dir,
List *entries));
static int fsortcmp PROTO((const Node * p, const Node * q));
/*
* Create a list of repositories to lock
@ -738,17 +737,6 @@ lock_filesdoneproc (callerdat, err, repository, update_dir, entries)
return (err);
}
/*
* compare two lock list nodes (for sort)
*/
static int
fsortcmp (p, q)
const Node *p;
const Node *q;
{
return (strcmp (p->key, q->key));
}
void
lock_tree_for_write (argc, argv, local, aflag)
int argc;

View File

@ -532,7 +532,7 @@ main (argc, argv)
(void) fputs (config_string, stdout);
(void) fputs ("\n", stdout);
(void) fputs ("\
Copyright (c) 1989-1997 Brian Berliner, david d `zoo' zuhn, \n\
Copyright (c) 1989-1998 Brian Berliner, david d `zoo' zuhn, \n\
Jeff Polk, and other authors\n", stdout);
(void) fputs ("\n", stdout);
(void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout);

View File

@ -279,6 +279,10 @@ static const char *const modules_contents[] = {
static const char *const config_contents[] = {
"# Set this to \"no\" if pserver shouldn't check system users/passwords\n",
"#SystemAuth=no\n",
"\n",
"# Set `PreservePermissions' to `yes' to save file status information\n",
"# in the repository.\n",
"#PreservePermissions=no\n",
NULL
};
@ -538,7 +542,7 @@ checkout_file (file, temp)
(RCSCHECKOUTPROC) NULL, (void *) NULL);
if (retcode != 0)
{
error (0, retcode == -1 ? errno : 0, "failed to check out %s file",
error (0, 0, "failed to check out %s file",
file);
}
freercsnode (&rcsnode);

File diff suppressed because it is too large Load Diff

View File

@ -169,13 +169,17 @@ typedef struct rcsversnode RCSVers;
/* The type of a function passed to RCS_checkout. */
typedef void (*RCSCHECKOUTPROC) PROTO ((void *, const char *, size_t));
#ifdef __STDC__
struct rcsbuffer;
#endif
/*
* exported interfaces
*/
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 **));
void RCS_reparsercsfile PROTO((RCSNode *, FILE **, struct rcsbuffer *));
char *RCS_check_kflag PROTO((const char *arg));
char *RCS_getdate PROTO((RCSNode * rcs, char *date, int force_tag_match));
@ -220,6 +224,8 @@ void RCS_setincexc PROTO ((const char *arg));
void RCS_setlocalid PROTO ((const char *arg));
char *make_file_label PROTO ((char *, char *, RCSNode *));
extern int preserve_perms;
/* From import.c. */
extern int add_rcs_file PROTO ((char *, char *, char *, char *, char *,
char *, char *, int, char **,

View File

@ -599,7 +599,42 @@ diff_exec (file1, file2, options, out)
char *options;
char *out;
{
char *args = xmalloc (strlen (options) + 10);
char *args;
#ifdef PRESERVE_PERMISSIONS_SUPPORT
/* If either file1 or file2 are special files, pretend they are
/dev/null. Reason: suppose a file that represents a block
special device in one revision becomes a regular file. CVS
must find the `difference' between these files, but a special
file contains no data useful for calculating this metric. The
safe thing to do is to treat the special file as an empty file,
thus recording the regular file's full contents. Doing so will
create extremely large deltas at the point of transition
between device files and regular files, but this is probably
very rare anyway.
There may be ways around this, but I think they are fraught
with danger. -twp */
if (preserve_perms &&
strcmp (file1, DEVNULL) != 0 &&
strcmp (file2, DEVNULL) != 0)
{
struct stat sb1, sb2;
if (CVS_LSTAT (file1, &sb1) < 0)
error (1, errno, "cannot get file information for %s", file1);
if (CVS_LSTAT (file2, &sb2) < 0)
error (1, errno, "cannot get file information for %s", file2);
if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode))
file1 = DEVNULL;
if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode))
file2 = DEVNULL;
}
#endif
args = xmalloc (strlen (options) + 10);
/* The first word in this string is used only for error reporting. */
sprintf (args, "diff %s", options);
call_diff_setup (args);
@ -619,7 +654,31 @@ diff_execv (file1, file2, label1, label2, options, out)
char *options;
char *out;
{
char *args = xmalloc (strlen (options) + 10);
char *args;
#ifdef PRESERVE_PERMISSIONS_SUPPORT
/* Pretend that special files are /dev/null for purposes of making
diffs. See comments in diff_exec. */
if (preserve_perms &&
strcmp (file1, DEVNULL) != 0 &&
strcmp (file2, DEVNULL) != 0)
{
struct stat sb1, sb2;
if (CVS_LSTAT (file1, &sb1) < 0)
error (1, errno, "cannot get file information for %s", file1);
if (CVS_LSTAT (file2, &sb2) < 0)
error (1, errno, "cannot get file information for %s", file2);
if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode))
file1 = DEVNULL;
if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode))
file2 = DEVNULL;
}
#endif
args = xmalloc (strlen (options) + 10);
/* The first word in this string is used only for error reporting. */
/* I guess we are pretty confident that options starts with a space. */
sprintf (args, "diff%s", options);

View File

@ -448,7 +448,7 @@ do_recursion (frame)
{
/* we will process files, so pre-parse entries */
if (frame->which & W_LOCAL)
entries = Entries_Open (frame->aflag);
entries = Entries_Open (frame->aflag, NULL);
}
}

View File

@ -1197,7 +1197,7 @@ serve_modified (arg)
}
{
int status = change_mode (arg, mode_text);
int status = change_mode (arg, mode_text, 0);
free (mode_text);
if (status)
{
@ -3358,12 +3358,13 @@ server_modtime (finfo, vers_ts)
/* See server.h for description. */
void
server_updated (finfo, vers, updated, file_info, checksum)
server_updated (finfo, vers, updated, mode, checksum, filebuf)
struct file_info *finfo;
Vers_TS *vers;
enum server_updated_arg4 updated;
struct stat *file_info;
mode_t mode;
unsigned char *checksum;
struct buffer *filebuf;
{
if (noexec)
{
@ -3382,25 +3383,43 @@ server_updated (finfo, vers, updated, file_info, checksum)
if (entries_line != NULL && scratched_file == NULL)
{
FILE *f;
struct stat sb;
struct buffer_data *list, *last;
unsigned long size;
char size_text[80];
if ( CVS_STAT (finfo->file, &sb) < 0)
if (filebuf != NULL)
{
if (existence_error (errno))
size = buf_length (filebuf);
if (mode == (mode_t) -1)
error (1, 0, "\
CVS server internal error: no mode in server_updated");
}
else
{
struct stat sb;
if ( CVS_STAT (finfo->file, &sb) < 0)
{
/*
* If we have a sticky tag for a branch on which the
* file is dead, and cvs update the directory, it gets
* a T_CHECKOUT but no file. So in this case just
* forget the whole thing. */
free (entries_line);
entries_line = NULL;
goto done;
if (existence_error (errno))
{
/* If we have a sticky tag for a branch on which
the file is dead, and cvs update the directory,
it gets a T_CHECKOUT but no file. So in this
case just forget the whole thing. */
free (entries_line);
entries_line = NULL;
goto done;
}
error (1, errno, "reading %s", finfo->fullname);
}
size = sb.st_size;
if (mode == (mode_t) -1)
{
/* FIXME: When we check out files the umask of the
server (set in .bashrc if rsh is in use) affects
what mode we send, and it shouldn't. */
mode = sb.st_mode;
}
error (1, errno, "reading %s", finfo->fullname);
}
if (checksum != NULL)
@ -3469,21 +3488,14 @@ server_updated (finfo, vers, updated, file_info, checksum)
{
char *mode_string;
/* FIXME: When we check out files the umask of the server
(set in .bashrc if rsh is in use) affects what mode we
send, and it shouldn't. */
if (file_info != NULL)
mode_string = mode_to_string (file_info->st_mode);
else
mode_string = mode_to_string (sb.st_mode);
mode_string = mode_to_string (mode);
buf_output0 (protocol, mode_string);
buf_output0 (protocol, "\n");
free (mode_string);
}
list = last = NULL;
size = 0;
if (sb.st_size > 0)
if (size > 0)
{
/* Throughout this section we use binary mode to read the
file we are sending. The client handles any line ending
@ -3496,11 +3508,19 @@ server_updated (finfo, vers, updated, file_info, checksum)
* might be computable somehow; using 100 here is just
* a first approximation.
*/
&& sb.st_size > 100)
&& size > 100)
{
int status, fd, gzip_status;
pid_t gzip_pid;
/* Callers must avoid passing us a buffer if
file_gzip_level is set. We could handle this case,
but it's not worth it since this case never arises
with a current client and server. */
if (filebuf != NULL)
error (1, 0, "\
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);
@ -3523,15 +3543,14 @@ server_updated (finfo, vers, updated, file_info, checksum)
/* Prepending length with "z" is flag for using gzip here. */
buf_output0 (protocol, "z");
}
else
else if (filebuf == NULL)
{
long status;
size = sb.st_size;
f = CVS_FOPEN (finfo->file, "rb");
if (f == NULL)
error (1, errno, "reading %s", finfo->fullname);
status = buf_read_file (f, sb.st_size, &list, &last);
status = buf_read_file (f, size, &list, &last);
if (status == -2)
(*protocol->memory_error) (protocol);
else if (status != 0)
@ -3545,7 +3564,13 @@ server_updated (finfo, vers, updated, file_info, checksum)
sprintf (size_text, "%lu\n", size);
buf_output0 (protocol, size_text);
buf_append_data (protocol, list, last);
if (filebuf == NULL)
buf_append_data (protocol, list, last);
else
{
buf_append_buffer (protocol, filebuf);
buf_free (filebuf);
}
/* Note we only send a newline here if the file ended with one. */
/*
@ -3558,6 +3583,7 @@ server_updated (finfo, vers, updated, file_info, checksum)
if ((updated == SERVER_UPDATED
|| updated == SERVER_PATCHED
|| updated == SERVER_RCS_DIFF)
&& filebuf != NULL
/* But if we are joining, we'll need the file when we call
join_file. */
&& !joining ())
@ -5611,7 +5637,7 @@ this client does not support writing binary files to stdout");
I assume that what they are talking about can also be helped
by flushing the stream before changing the mode. */
fflush (stdout);
oldmode = _setmode (_fileno (stdout), _O_BINARY);
oldmode = _setmode (_fileno (stdout), OPEN_BINARY);
if (oldmode < 0)
error (0, errno, "failed to setmode on stdout");
#endif
@ -5626,7 +5652,7 @@ this client does not support writing binary files to stdout");
}
#ifdef USE_SETMODE_STDOUT
fflush (stdout);
if (_setmode (_fileno (stdout), oldmode) != _O_BINARY)
if (_setmode (_fileno (stdout), oldmode) != OPEN_BINARY)
error (0, errno, "failed to setmode on stdout");
#endif
}

View File

@ -42,9 +42,14 @@
#include "fileattr.h"
#include "edit.h"
#include "getline.h"
#include "buffer.h"
#include "hardlink.h"
static int checkout_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts,
int adding));
int adding, int merging, int update_server));
#ifdef SERVER_SUPPORT
static void checkout_to_buffer PROTO ((void *, const char *, size_t));
#endif
#ifdef SERVER_SUPPORT
static int patch_file PROTO ((struct file_info *finfo,
Vers_TS *vers_ts,
@ -64,6 +69,9 @@ static int update_fileproc PROTO ((void *callerdat, struct file_info *));
static int update_filesdone_proc PROTO ((void *callerdat, int err,
char *repository, char *update_dir,
List *entries));
#ifdef PRESERVE_PERMISSIONS_SUPPORT
static int get_linkinfo_proc PROTO ((void *callerdat, struct file_info *));
#endif
static void write_letter PROTO ((struct file_info *finfo, int letter));
static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts));
@ -79,6 +87,10 @@ static char *date = NULL;
static int rewrite_tag;
static int nonbranch;
/* If we set the tag or date for a subdirectory, we use this to undo
the setting. See update_dirent_proc. */
static char *tag_update_dir;
static char *join_rev1, *date_rev1;
static char *join_rev2, *date_rev2;
static int aflag = 0;
@ -437,6 +449,32 @@ do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag,
else
date_rev2 = (char *) NULL;
#ifdef PRESERVE_PERMISSIONS_SUPPORT
if (preserve_perms)
{
/* We need to do an extra recursion, bleah. It's to make sure
that we know as much as possible about file linkage. */
hardlist = getlist();
working_dir = xgetwd(); /* save top-level working dir */
/* FIXME-twp: the arguments to start_recursion make me dizzy. This
function call was copied from the update_fileproc call that
follows it; someone should make sure that I did it right. */
err = start_recursion (get_linkinfo_proc, (FILESDONEPROC) NULL,
(DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
argc, argv, local, which, aflag, 1,
preload_update_dir, 1);
if (err)
return (err);
/* FIXME-twp: at this point we should walk the hardlist
and update the `links' field of each hardlink_info struct
to list the files that are linked on dist. That would make
it easier & more efficient to compare the disk linkage with
the repository linkage (a simple strcmp). */
}
#endif
/* call the recursion processor */
err = start_recursion (update_fileproc, update_filesdone_proc,
update_dirent_proc, update_dirleave_proc, NULL,
@ -456,6 +494,50 @@ do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag,
return (err);
}
#ifdef PRESERVE_PERMISSIONS_SUPPORT
/*
* The get_linkinfo_proc callback adds each file to the hardlist
* (see hardlink.c).
*/
static int
get_linkinfo_proc (callerdat, finfo)
void *callerdat;
struct file_info *finfo;
{
char *fullpath;
Node *linkp;
struct hardlink_info *hlinfo;
/* Get the full pathname of the current file. */
fullpath = xmalloc (strlen(working_dir) +
strlen(finfo->fullname) + 2);
sprintf (fullpath, "%s/%s", working_dir, finfo->fullname);
/* To permit recursing into subdirectories, files
are keyed on the full pathname and not on the basename. */
linkp = lookup_file_by_inode (fullpath);
if (linkp == NULL)
{
/* The file isn't on disk; we are probably restoring
a file that was removed. */
return 0;
}
/* Create a new, empty hardlink_info node. */
hlinfo = (struct hardlink_info *)
xmalloc (sizeof (struct hardlink_info));
hlinfo->status = (Ctype) 0; /* is this dumb? */
hlinfo->checked_out = 0;
hlinfo->links = NULL;
linkp->data = (char *) hlinfo;
return 0;
}
#endif
/*
* This is the callback proc for update. It is called for each file in each
* directory by the recursion code. The current directory is the local
@ -526,7 +608,7 @@ update_fileproc (callerdat, finfo)
#ifdef SERVER_SUPPORT
case T_PATCH: /* needs patch */
#endif
retval = checkout_file (finfo, vers, 0);
retval = checkout_file (finfo, vers, 0, 0, 0);
break;
default: /* can't ever happen :-) */
@ -629,7 +711,8 @@ update_fileproc (callerdat, finfo)
(rcs_diff_patches
? SERVER_RCS_DIFF
: SERVER_PATCHED),
&file_info, checksum);
file_info.st_mode, checksum,
(struct buffer *) NULL);
break;
}
}
@ -639,13 +722,7 @@ update_fileproc (callerdat, finfo)
/* Fall through. */
#endif
case T_CHECKOUT: /* needs checkout */
retval = checkout_file (finfo, vers, 0);
#ifdef SERVER_SUPPORT
if (server_active && retval == 0)
server_updated (finfo, vers,
SERVER_UPDATED, (struct stat *) NULL,
(unsigned char *) NULL);
#endif
retval = checkout_file (finfo, vers, 0, 0, 1);
break;
case T_ADDED: /* added but not committed */
write_letter (finfo, 'A');
@ -663,8 +740,9 @@ update_fileproc (callerdat, finfo)
if (vers->ts_user == NULL)
server_scratch_entry_only ();
server_updated (finfo, vers,
SERVER_UPDATED, (struct stat *) NULL,
(unsigned char *) NULL);
SERVER_UPDATED, (mode_t) -1,
(unsigned char *) NULL,
(struct buffer *) NULL);
}
#endif
break;
@ -806,6 +884,26 @@ update_dirent_proc (callerdat, dir, repository, update_dir, entries)
else
{
/* otherwise, create the dir and appropriate adm files */
/* If no tag or date were specified on the command line,
and we're not using -A, we want the subdirectory to use
the tag and date, if any, of the current directory.
That way, update -d will work correctly when working on
a branch.
We use TAG_UPDATE_DIR to undo the tag setting in
update_dirleave_proc. If we did not do this, we would
not correctly handle a working directory with multiple
tags (and maybe we should prohibit such working
directories, but they work now and we shouldn't make
them stop working without more thought). */
if ((tag == NULL && date == NULL) && ! aflag)
{
ParseTag (&tag, &date, &nonbranch);
if (tag != NULL || date != NULL)
tag_update_dir = xstrdup (update_dir);
}
make_directory (dir);
Create_Admin (dir, update_dir, repository, tag, date,
/* This is a guess. We will rewrite it later
@ -897,6 +995,27 @@ update_dirleave_proc (callerdat, dir, err, update_dir, entries)
{
FILE *fp;
/* If we set the tag or date for a new subdirectory in
update_dirent_proc, and we're now done with that subdirectory,
undo the tag/date setting. Note that we know that the tag and
date were both originally NULL in this case. */
if (tag_update_dir != NULL && strcmp (update_dir, tag_update_dir) == 0)
{
if (tag != NULL)
{
free (tag);
tag = NULL;
}
if (date != NULL)
{
free (date);
date = NULL;
}
nonbranch = 0;
free (tag_update_dir);
tag_update_dir = NULL;
}
/* run the update_prog if there is one */
/* FIXME: should be checking for errors from CVS_FOPEN and printing
them if not existence_error. */
@ -1016,7 +1135,7 @@ isemptydir (dir, might_not_exist)
if (CVS_CHDIR (dir) < 0)
error (1, errno, "cannot change directory to %s", dir);
l = Entries_Open (0);
l = Entries_Open (0, NULL);
files_removed = walklist (l, isremoved, 0);
Entries_Close (l);
@ -1063,22 +1182,29 @@ scratch_file (finfo)
* Check out a file.
*/
static int
checkout_file (finfo, vers_ts, adding)
checkout_file (finfo, vers_ts, adding, merging, update_server)
struct file_info *finfo;
Vers_TS *vers_ts;
int adding;
int merging;
int update_server;
{
char *backup;
int set_time, retval = 0;
int retcode = 0;
int status;
int file_is_dead;
struct buffer *revbuf;
/* Solely to suppress a warning from gcc -Wall. */
backup = NULL;
revbuf = NULL;
/* don't screw with backup files if we're going to stdout */
if (!pipeout)
/* Don't screw with backup files if we're going to stdout, or if
we are the server. */
if (!pipeout
#ifdef SERVER_SUPPORT
&& ! server_active
#endif
)
{
backup = xmalloc (strlen (finfo->file)
+ sizeof (CVSADM)
@ -1088,6 +1214,7 @@ checkout_file (finfo, vers_ts, adding)
if (isfile (finfo->file))
rename_file (finfo->file, backup);
else
{
/* If -f/-t wrappers are being used to wrap up a directory,
then backup might be a directory instead of just a file. */
if (unlink_file_dir (backup) < 0)
@ -1097,6 +1224,9 @@ checkout_file (finfo, vers_ts, adding)
/* FIXME: should include update_dir in message. */
error (0, errno, "error removing %s", backup);
}
free (backup);
backup = NULL;
}
}
file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs);
@ -1124,22 +1254,64 @@ VERS: ", 0);
}
}
status = RCS_checkout (vers_ts->srcfile,
pipeout ? NULL : finfo->file,
vers_ts->vn_rcs, vers_ts->vn_tag,
vers_ts->options, RUN_TTY,
(RCSCHECKOUTPROC) NULL, (void *) NULL);
#ifdef SERVER_SUPPORT
if (update_server
&& server_active
&& ! pipeout
&& ! file_gzip_level
&& ! joining ()
&& ! wrap_name_has (finfo->file, WRAP_FROMCVS))
{
revbuf = buf_nonio_initialize ((BUFMEMERRPROC) NULL);
status = RCS_checkout (vers_ts->srcfile, (char *) NULL,
vers_ts->vn_rcs, vers_ts->vn_tag,
vers_ts->options, RUN_TTY,
checkout_to_buffer, revbuf);
}
else
#endif
status = RCS_checkout (vers_ts->srcfile,
pipeout ? NULL : finfo->file,
vers_ts->vn_rcs, vers_ts->vn_tag,
vers_ts->options, RUN_TTY,
(RCSCHECKOUTPROC) NULL, (void *) NULL);
}
if (file_is_dead || status == 0)
{
mode_t mode;
mode = (mode_t) -1;
if (!pipeout)
{
Vers_TS *xvers_ts;
if (revbuf != NULL)
{
struct stat sb;
/* FIXME: We should have RCS_checkout return the mode. */
if (stat (vers_ts->srcfile->path, &sb) < 0)
error (1, errno, "cannot stat %s",
vers_ts->srcfile->path);
mode = sb.st_mode &~ (S_IWRITE | S_IWGRP | S_IWOTH);
}
if (cvswrite
&& !file_is_dead
&& !fileattr_get (finfo->file, "_watched"))
xchmod (finfo->file, 1);
{
if (revbuf == NULL)
xchmod (finfo->file, 1);
else
{
/* We know that we are the server here, so
although xchmod checks umask, we don't bother. */
mode |= (((mode & S_IRUSR) ? S_IWUSR : 0)
| ((mode & S_IRGRP) ? S_IWGRP : 0)
| ((mode & S_IROTH) ? S_IWOTH : 0));
}
}
{
/* A newly checked out file is never under the spell
@ -1171,6 +1343,27 @@ VERS: ", 0);
if (strcmp (xvers_ts->options, "-V4") == 0)
xvers_ts->options[0] = '\0';
if (revbuf != NULL)
{
/* If we stored the file data into a buffer, then we
didn't create a file at all, so xvers_ts->ts_user
is wrong. The correct value is to have it be the
same as xvers_ts->ts_rcs, meaning that the working
file is unchanged from the RCS file.
FIXME: We should tell Version_TS not to waste time
statting the nonexistent file.
FIXME: Actually, I don't think the ts_user value
matters at all here. The only use I know of is
that it is printed in a trace message by
Server_Register. */
if (xvers_ts->ts_user != NULL)
free (xvers_ts->ts_user);
xvers_ts->ts_user = xstrdup (xvers_ts->ts_rcs);
}
(void) time (&last_register_time);
if (file_is_dead)
@ -1179,7 +1372,7 @@ VERS: ", 0);
{
error (0, 0,
"warning: %s is not (any longer) pertinent",
finfo->fullname);
finfo->fullname);
}
Scratch_Entry (finfo->entries, finfo->file);
#ifdef SERVER_SUPPORT
@ -1226,21 +1419,29 @@ VERS: ", 0);
write_letter (finfo, 'U');
}
}
#ifdef SERVER_SUPPORT
if (update_server && server_active)
server_updated (finfo, vers_ts,
merging ? SERVER_MERGED : SERVER_UPDATED,
mode, (unsigned char *) NULL, revbuf);
#endif
}
else
{
int old_errno = errno; /* save errno value over the rename */
if (!pipeout && isfile (backup))
if (backup != NULL)
{
rename_file (backup, finfo->file);
free (backup);
backup = NULL;
}
error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
"could not check out %s", finfo->fullname);
error (0, 0, "could not check out %s", finfo->fullname);
retval = retcode;
retval = status;
}
if (!pipeout)
if (backup != NULL)
{
/* If -f/-t wrappers are being used to wrap up a directory,
then backup might be a directory instead of just a file. */
@ -1259,6 +1460,24 @@ VERS: ", 0);
#ifdef SERVER_SUPPORT
/* This function is used to write data from a file being checked out
into a buffer. */
static void
checkout_to_buffer (callerdat, data, len)
void *callerdat;
const char *data;
size_t len;
{
struct buffer *buf = (struct buffer *) callerdat;
buf_output (buf, data, len);
}
#endif /* SERVER_SUPPORT */
#ifdef SERVER_SUPPORT
/* This structure is used to pass information between patch_file and
patch_file_write. */
@ -1334,6 +1553,14 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
free (rev);
}
/* If the revision is dead, let checkout_file handle it rather
than duplicating the processing here. */
if (RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs))
{
*docheckout = 1;
return 0;
}
backup = xmalloc (strlen (finfo->file)
+ sizeof (CVSADM)
+ sizeof (CVSPREFIX)
@ -1638,25 +1865,32 @@ merge_file (finfo, vers)
xchmod (finfo->file, 1);
if (strcmp (vers->options, "-kb") == 0
|| wrap_merge_is_copy (finfo->file))
|| wrap_merge_is_copy (finfo->file)
|| special_file_mismatch (finfo, NULL, vers->vn_rcs))
{
/* For binary files, a merge is always a conflict. We give the
/* For binary files, a merge is always a conflict. Same for
files whose permissions or linkage do not match. We give the
user the two files, and let them resolve it. It is possible
that we should require a "touch foo" or similar step before
we allow a checkin. */
status = checkout_file (finfo, vers, 0);
/* TODO: it may not always be necessary to regard a permission
mismatch as a conflict. The working file and the RCS file
have a common ancestor `A'; if the working file's permissions
match A's, then it's probably safe to overwrite them with the
RCS permissions. Only if the working file, the RCS file, and
A all disagree should this be considered a conflict. But more
thought needs to go into this, and in the meantime it is safe
to treat any such mismatch as an automatic conflict. -twp */
#ifdef SERVER_SUPPORT
/* Send the new contents of the file before the message. If we
wanted to be totally correct, we would have the client write
the message only after the file has safely been written. */
if (server_active)
{
server_copy_file (finfo->file, finfo->update_dir,
finfo->repository, backup);
server_updated (finfo, vers, SERVER_MERGED,
(struct stat *) NULL, (unsigned char *) NULL);
}
#endif
status = checkout_file (finfo, vers, 0, 1, 1);
/* Is there a better term than "nonmergeable file"? What we
really mean is, not something that CVS cannot or does not
want to merge (there might be an external manual or
@ -1717,7 +1951,8 @@ merge_file (finfo, vers)
server_copy_file (finfo->file, finfo->update_dir, finfo->repository,
backup);
server_updated (finfo, vers, SERVER_MERGED,
(struct stat *) NULL, (unsigned char *) NULL);
(mode_t) -1, (unsigned char *) NULL,
(struct buffer *) NULL);
}
#endif
@ -1951,8 +2186,8 @@ join_file (finfo, vers)
if (server_active)
{
server_scratch (finfo->file);
server_updated (finfo, vers, SERVER_UPDATED, (struct stat *) NULL,
(unsigned char *) NULL);
server_updated (finfo, vers, SERVER_UPDATED, (mode_t) -1,
(unsigned char *) NULL, (struct buffer *) NULL);
}
#endif
mrev = xmalloc (strlen (vers->vn_user) + 2);
@ -2005,14 +2240,7 @@ join_file (finfo, vers)
/* FIXME: If checkout_file fails, we should arrange to
return a non-zero exit status. */
status = checkout_file (finfo, xvers, 1);
#ifdef SERVER_SUPPORT
if (server_active && status == 0)
server_updated (finfo, xvers,
SERVER_UPDATED, (struct stat *) NULL,
(unsigned char *) NULL);
#endif
status = checkout_file (finfo, xvers, 1, 0, 1);
freevers_ts (&xvers);
@ -2074,7 +2302,7 @@ join_file (finfo, vers)
(char *) NULL, RUN_TTY,
(RCSCHECKOUTPROC) NULL, (void *) NULL);
if (retcode != 0)
error (1, retcode == -1 ? errno : 0,
error (1, 0,
"failed to check out %s file", finfo->fullname);
}
#endif
@ -2147,9 +2375,11 @@ join_file (finfo, vers)
write_letter (finfo, 'U');
}
else if (strcmp (options, "-kb") == 0
|| wrap_merge_is_copy (finfo->file))
|| wrap_merge_is_copy (finfo->file)
|| special_file_mismatch (finfo, rev1, rev2))
{
/* We are dealing with binary files, but real merging would
/* We are dealing with binary files, or files with a
permission/linkage mismatch, and real merging would
need to take place. This is a conflict. We give the user
the two files, and let them resolve it. It is possible
that we should require a "touch foo" or similar step before
@ -2227,12 +2457,318 @@ join_file (finfo, vers)
server_copy_file (finfo->file, finfo->update_dir, finfo->repository,
backup);
server_updated (finfo, vers, SERVER_MERGED,
(struct stat *) NULL, (unsigned char *) NULL);
(mode_t) -1, (unsigned char *) NULL,
(struct buffer *) NULL);
}
#endif
free (backup);
}
/*
* Report whether revisions REV1 and REV2 of FINFO agree on:
* . file ownership
* . permissions
* . major and minor device numbers
* . symbolic links
* . hard links
*
* If either REV1 or REV2 is NULL, the working copy is used instead.
*
* Return 1 if the files differ on these data.
*/
int
special_file_mismatch (finfo, rev1, rev2)
struct file_info *finfo;
char *rev1;
char *rev2;
{
#ifdef PRESERVE_PERMISSIONS_SUPPORT
struct stat sb;
RCSVers *vp;
Node *n;
uid_t rev1_uid, rev2_uid;
gid_t rev1_gid, rev2_gid;
mode_t rev1_mode, rev2_mode;
unsigned long dev_long;
dev_t rev1_dev, rev2_dev;
char *rev1_symlink = NULL;
char *rev2_symlink = NULL;
char *rev1_hardlinks = NULL;
char *rev2_hardlinks = NULL;
int check_uids, check_gids, check_modes;
int result;
/* If we don't care about special file info, then
don't report a mismatch in any case. */
if (!preserve_perms)
return 0;
/* When special_file_mismatch is called from No_Difference, the
RCS file has been only partially parsed. We must read the
delta tree in order to compare special file info recorded in
the delta nodes. (I think this is safe. -twp) */
if (finfo->rcs->flags & PARTIAL)
RCS_reparsercsfile (finfo->rcs, NULL, NULL);
check_uids = check_gids = check_modes = 1;
/* Obtain file information for REV1. If this is null, then stat
finfo->file and use that info. */
/* If a revision does not know anything about its status,
then presumably it doesn't matter, and indicates no conflict. */
if (rev1 == NULL)
{
if (islink (finfo->file))
rev1_symlink = xreadlink (finfo->file);
else
{
if (CVS_LSTAT (finfo->file, &sb) < 0)
error (1, errno, "could not get file information for %s",
finfo->file);
rev1_uid = sb.st_uid;
rev1_gid = sb.st_gid;
rev1_mode = sb.st_mode;
if (S_ISBLK (rev1_mode) || S_ISCHR (rev1_mode))
rev1_dev = sb.st_rdev;
}
rev1_hardlinks = list_files_linked_to (finfo->file);
}
else
{
n = findnode (finfo->rcs->versions, rev1);
vp = (RCSVers *) n->data;
n = findnode (vp->other_delta, "symlink");
if (n != NULL)
rev1_symlink = xstrdup (n->data);
else
{
n = findnode (vp->other_delta, "owner");
if (n == NULL)
check_uids = 0; /* don't care */
else
rev1_uid = strtoul (n->data, NULL, 10);
n = findnode (vp->other_delta, "group");
if (n == NULL)
check_gids = 0; /* don't care */
else
rev1_gid = strtoul (n->data, NULL, 10);
n = findnode (vp->other_delta, "permissions");
if (n == NULL)
check_modes = 0; /* don't care */
else
rev1_mode = strtoul (n->data, NULL, 8);
n = findnode (vp->other_delta, "special");
if (n == NULL)
rev1_mode |= S_IFREG;
else
{
/* If the size of `ftype' changes, fix the sscanf call also */
char ftype[16];
if (sscanf (n->data, "%16s %lu", ftype,
&dev_long) < 2)
error (1, 0, "%s:%s has bad `special' newphrase %s",
finfo->file, rev1, n->data);
rev1_dev = dev_long;
if (strcmp (ftype, "character") == 0)
rev1_mode |= S_IFCHR;
else if (strcmp (ftype, "block") == 0)
rev1_mode |= S_IFBLK;
else
error (0, 0, "%s:%s unknown file type `%s'",
finfo->file, rev1, ftype);
}
n = findnode (vp->other_delta, "hardlinks");
if (n == NULL)
rev1_hardlinks = xstrdup ("");
else
rev1_hardlinks = xstrdup (n->data);
}
}
/* Obtain file information for REV2. */
if (rev2 == NULL)
{
if (islink (finfo->file))
rev2_symlink = xreadlink (finfo->file);
else
{
if (CVS_LSTAT (finfo->file, &sb) < 0)
error (1, errno, "could not get file information for %s",
finfo->file);
rev2_uid = sb.st_uid;
rev2_gid = sb.st_gid;
rev2_mode = sb.st_mode;
if (S_ISBLK (rev2_mode) || S_ISCHR (rev2_mode))
rev2_dev = sb.st_rdev;
}
rev2_hardlinks = list_files_linked_to (finfo->file);
}
else
{
n = findnode (finfo->rcs->versions, rev2);
vp = (RCSVers *) n->data;
n = findnode (vp->other_delta, "symlink");
if (n != NULL)
rev2_symlink = xstrdup (n->data);
else
{
n = findnode (vp->other_delta, "owner");
if (n == NULL)
check_uids = 0; /* don't care */
else
rev2_uid = strtoul (n->data, NULL, 10);
n = findnode (vp->other_delta, "group");
if (n == NULL)
check_gids = 0; /* don't care */
else
rev2_gid = strtoul (n->data, NULL, 10);
n = findnode (vp->other_delta, "permissions");
if (n == NULL)
check_modes = 0; /* don't care */
else
rev2_mode = strtoul (n->data, NULL, 8);
n = findnode (vp->other_delta, "special");
if (n == NULL)
rev2_mode |= S_IFREG;
else
{
/* If the size of `ftype' changes, fix the sscanf call also */
char ftype[16];
if (sscanf (n->data, "%16s %lu", ftype,
&dev_long) < 2)
error (1, 0, "%s:%s has bad `special' newphrase %s",
finfo->file, rev2, n->data);
rev2_dev = dev_long;
if (strcmp (ftype, "character") == 0)
rev2_mode |= S_IFCHR;
else if (strcmp (ftype, "block") == 0)
rev2_mode |= S_IFBLK;
else
error (0, 0, "%s:%s unknown file type `%s'",
finfo->file, rev2, ftype);
}
n = findnode (vp->other_delta, "hardlinks");
if (n == NULL)
rev2_hardlinks = xstrdup ("");
else
rev2_hardlinks = xstrdup (n->data);
}
}
/* Check the user/group ownerships and file permissions, printing
an error for each mismatch found. Return 0 if all characteristics
matched, and 1 otherwise. */
result = 0;
/* Compare symlinks first, since symlinks are simpler (don't have
any other characteristics). */
if (rev1_symlink != NULL && rev2_symlink == NULL)
{
error (0, 0, "%s is a symbolic link",
(rev1 == NULL ? "working file" : rev1));
result = 1;
}
else if (rev1_symlink == NULL && rev2_symlink != NULL)
{
error (0, 0, "%s is a symbolic link",
(rev2 == NULL ? "working file" : rev2));
result = 1;
}
else if (rev1_symlink != NULL)
result = (strcmp (rev1_symlink, rev2_symlink) == 0);
else
{
/* Compare user ownership. */
if (check_uids && rev1_uid != rev2_uid)
{
error (0, 0, "%s: owner mismatch between %s and %s",
finfo->file,
(rev1 == NULL ? "working file" : rev1),
(rev2 == NULL ? "working file" : rev2));
result = 1;
}
/* Compare group ownership. */
if (check_gids && rev1_gid != rev2_gid)
{
error (0, 0, "%s: group mismatch between %s and %s",
finfo->file,
(rev1 == NULL ? "working file" : rev1),
(rev2 == NULL ? "working file" : rev2));
result = 1;
}
/* Compare permissions. */
if (check_modes &&
(rev1_mode & 07777) != (rev2_mode & 07777))
{
error (0, 0, "%s: permission mismatch between %s and %s",
finfo->file,
(rev1 == NULL ? "working file" : rev1),
(rev2 == NULL ? "working file" : rev2));
result = 1;
}
/* Compare device file characteristics. */
if ((rev1_mode & S_IFMT) != (rev2_mode & S_IFMT))
{
error (0, 0, "%s: %s and %s are different file types",
finfo->file,
(rev1 == NULL ? "working file" : rev1),
(rev2 == NULL ? "working file" : rev2));
result = 1;
}
else if (S_ISBLK (rev1_mode))
{
if (rev1_dev != rev2_dev)
{
error (0, 0, "%s: device numbers of %s and %s do not match",
finfo->file,
(rev1 == NULL ? "working file" : rev1),
(rev2 == NULL ? "working file" : rev2));
result = 1;
}
}
/* Compare hard links. */
if (strcmp (rev1_hardlinks, rev2_hardlinks) != 0)
{
error (0, 0, "%s: hard linkage of %s and %s do not match",
finfo->file,
(rev1 == NULL ? "working file" : rev1),
(rev2 == NULL ? "working file" : rev2));
result = 1;
}
}
if (rev1_symlink != NULL)
free (rev1_symlink);
if (rev2_symlink != NULL)
free (rev2_symlink);
if (rev1_hardlinks != NULL)
free (rev1_hardlinks);
if (rev2_hardlinks != NULL)
free (rev2_hardlinks);
return result;
#else
return 0;
#endif
}
int
joining ()
{