Пример #1
0
/*
 * Unlink a file or dir, if possible.  If it is a directory do a deep
 * removal of all of the files in the directory.  Return -1 on error
 * (in which case errno is set).
 */
int
unlink_file_dir (const char *f)
{
    struct stat sb;

    /* This is called by the server parent process in contexts where
       it is not OK to send output (e.g. after we sent "ok" to the
       client).  */
    if (!server_active)
	TRACE (TRACE_FUNCTION, "unlink_file_dir(%s)", f);

    if (noexec)
	return 0;

    /* For at least some unices, if root tries to unlink() a directory,
       instead of doing something rational like returning EISDIR,
       the system will gleefully go ahead and corrupt the filesystem.
       So we first call stat() to see if it is OK to call unlink().  This
       doesn't quite work--if someone creates a directory between the
       call to stat() and the call to unlink(), we'll still corrupt
       the filesystem.  Where is the Unix Haters Handbook when you need
       it?  */
    if (stat (f, &sb) < 0)
    {
	if (existence_error (errno))
	{
	    /* The file or directory doesn't exist anyhow.  */
	    return -1;
	}
    }
    else if (S_ISDIR (sb.st_mode))
	return deep_remove_dir (f);

    return CVS_UNLINK (f);
}
Пример #2
0
/*
 * Unlink a file or dir, if possible.  If it is a directory do a deep
 * removal of all of the files in the directory.  Return -1 on error
 * (in which case errno is set).
 */
int unlink_file_dir (const char *f)
{
    struct stat sb;

    TRACE(1,"unlink_file_dir(%s)",PATCH_NULL(f));

    if (noexec)
        return (0);

    /* For at least some unices, if root tries to unlink() a directory,
       instead of doing something rational like returning EISDIR,
       the system will gleefully go ahead and corrupt the filesystem.
       So we first call stat() to see if it is OK to call unlink().  This
       doesn't quite work--if someone creates a directory between the
       call to stat() and the call to unlink(), we'll still corrupt
       the filesystem.  Where is the Unix Haters Handbook when you need
       it?  */
    if (stat (f, &sb) < 0)
    {
        if (existence_error (errno))
        {
            /* The file or directory doesn't exist anyhow.  */
            return -1;
        }
    }
    else if (S_ISDIR (sb.st_mode))
        return deep_remove_dir (f);

    return CVS_UNLINK (f);
}
Пример #3
0
/*
 * unlink a file, if possible.
 */
int unlink_file (const char *f)
{
    TRACE(1,"unlink_file(%s)",PATCH_NULL(f));
    if (noexec)
        return (0);

    return (CVS_UNLINK (f));
}
Пример #4
0
/*
 * unlink a file, if possible.
 */
int
unlink_file (const char *f)
{
    TRACE (TRACE_FUNCTION, "unlink_file(%s)", f);

    if (noexec)
	return (0);

    return (CVS_UNLINK (f));
}
Пример #5
0
/* Generate a unique temporary filename and return an open file stream
 * to the truncated file by that name
 *
 *  INPUTS
 *	filename	where to place the pointer to the newly allocated file
 *   			name string
 *
 *  OUTPUTS
 *	filename	dereferenced, will point to the newly allocated file
 *			name string.  This value is undefined if the function
 *			returns an error.
 *
 *  RETURNS
 *	An open file pointer to a read/write mode empty temporary file with the
 *	unique file name or NULL on failure.
 *
 *  ERRORS
 *	On error, errno will be set to some value either by CVS_FOPEN or
 *	whatever system function is called to generate the temporary file name.
 *	The value of filename is undefined on error.
 */
FILE *
cvs_temp_file (char **filename)
{
    char *fn;
    FILE *fp;
    int fd;

    /* FIXME - I'd like to be returning NULL here in noexec mode, but I think
     * some of the rcs & diff functions which rely on a temp file run in
     * noexec mode too.
     */

    assert (filename != NULL);

    fn = Xasprintf ("%s/%s", get_cvs_tmp_dir (), "cvsXXXXXX");
    fd = mkstemp (fn);

    /* a NULL return will be interpreted by callers as an error and
     * errno should still be set
     */
    if (fd == -1)
	fp = NULL;
    else if ((fp = CVS_FDOPEN (fd, "w+")) == NULL)
    {
	/* Attempt to close and unlink the file since mkstemp returned
	 * sucessfully and we believe it's been created and opened.
	 */
 	int save_errno = errno;
	if (close (fd))
	    error (0, errno, "Failed to close temporary file %s", fn);
	if (CVS_UNLINK (fn))
	    error (0, errno, "Failed to unlink temporary file %s", fn);
	errno = save_errno;
    }

    if (fp == NULL)
	free (fn);

    /* mkstemp is defined to open mode 0600 using glibc 2.0.7+.  There used
     * to be a complicated #ifdef checking the library versions here and then
     * a chmod 0600 on the temp file for versions of glibc less than 2.1.  This
     * is rather a special case, leaves a race condition open regardless, and
     * one could hope that sysadmins have read the relevant security
     * announcements and upgraded by now to a version with a fix committed in
     * January of 1999.
     *
     * If it is decided at some point that old, buggy versions of glibc should
     * still be catered to, a umask of 0600 should be set before file creation
     * instead then reset after file creation since this would avoid the race
     * condition that the chmod left open to exploitation.
     */

    *filename = fn;
    return fp;
}
Пример #6
0
static
#endif /* !PROXY_SUPPORT */
void
log_buffer_closelog (struct buffer *buf)
{
    struct log_buffer *lb = buf->closure;
    void *tmp;

#ifdef PROXY_SUPPORT
    lb->disabled = true;
#endif /* PROXY_SUPPORT */

    /* Close the log.  */
    if (lb->log)
    {
	tmp = lb->log;
	lb->log = NULL;
	if (fclose (tmp) < 0)
	    error (0, errno, "closing log file");
    }

#ifdef PROXY_SUPPORT
    /* Delete the log if we know its name.  */
    if (lb->back_fn)
    {
	tmp = lb->back_fn;
	lb->back_fn = NULL;
	if (CVS_UNLINK (tmp))
	    error (0, errno, "Failed to delete log file.");
	free (tmp);
    }

    if (lb->back_buf)
    {
	tmp = lb->back_buf;
	lb->back_buf = NULL;
	buf_free (tmp);
    }
#endif /* PROXY_SUPPORT */
}
Пример #7
0
static int
deep_remove_dir (const char *path)
{
    DIR		  *dirp;
    struct dirent *dp;

    if (rmdir (path) != 0)
    {
	if (errno == ENOTEMPTY
	    || errno == EEXIST
	    /* Ugly workaround for ugly AIX 4.1 (and 3.2) header bug
	       (it defines ENOTEMPTY and EEXIST to 17 but actually
	       returns 87).  */
	    || (ENOTEMPTY == 17 && EEXIST == 17 && errno == 87))
	{
	    if ((dirp = CVS_OPENDIR (path)) == NULL)
		/* If unable to open the directory return
		 * an error
		 */
		return -1;

	    errno = 0;
	    while ((dp = CVS_READDIR (dirp)) != NULL)
	    {
		char *buf;

		if (strcmp (dp->d_name, ".") == 0 ||
			    strcmp (dp->d_name, "..") == 0)
		    continue;

		buf = Xasprintf ("%s/%s", path, dp->d_name);

		/* See comment in unlink_file_dir explanation of why we use
		   isdir instead of just calling unlink and checking the
		   status.  */
		if (isdir (buf)) 
		{
		    if (deep_remove_dir (buf))
		    {
			CVS_CLOSEDIR (dirp);
			free (buf);
			return -1;
		    }
		}
		else
		{
		    if (CVS_UNLINK (buf) != 0)
		    {
			CVS_CLOSEDIR (dirp);
			free (buf);
			return -1;
		    }
		}
		free (buf);

		errno = 0;
	    }
	    if (errno != 0)
	    {
		int save_errno = errno;
		CVS_CLOSEDIR (dirp);
		errno = save_errno;
		return -1;
	    }
	    CVS_CLOSEDIR (dirp);
	    return rmdir (path);
	}
	else
	    return -1;
    }

    /* Was able to remove the directory return 0 */
    return 0;
}
Пример #8
0
int
No_Difference (struct file_info *finfo, Vers_TS *vers)
{
    Node *p;
    int ret;
    char *ts, *options;
    int retcode = 0;
    char *tocvsPath;

    /* If ts_user is "Is-modified", we can only conclude the files are
       different (since we don't have the file's contents).  */
    if (vers->ts_user != NULL
	&& strcmp (vers->ts_user, "Is-modified") == 0)
	return -1;

    if (!vers->srcfile || !vers->srcfile->path)
	return (-1);			/* different since we couldn't tell */

#ifdef PRESERVE_PERMISSIONS_SUPPORT
    /* If special files are in use, then any mismatch of file metadata
       information also means that the files should be considered different. */
    if (preserve_perms && special_file_mismatch (finfo, vers->vn_user, NULL))
	return 1;
#endif

    if (vers->entdata && vers->entdata->options)
	options = xstrdup (vers->entdata->options);
    else
	options = xstrdup ("");

    tocvsPath = wrap_tocvs_process_file (finfo->file);
    retcode = RCS_cmp_file (vers->srcfile, vers->vn_user, NULL, NULL, options,
			    tocvsPath == NULL ? finfo->file : tocvsPath);
    if (retcode == 0)
    {
	/* no difference was found, so fix the entries file */
	ts = time_stamp (finfo->file);
	Register (finfo->entries, finfo->file,
		  vers->vn_user ? vers->vn_user : vers->vn_rcs, ts,
		  options, vers->tag, vers->date, NULL);
#ifdef SERVER_SUPPORT
	if (server_active)
	{
	    /* We need to update the entries line on the client side.  */
	    server_update_entries (finfo->file, finfo->update_dir,
				   finfo->repository, SERVER_UPDATED);
	}
#endif
	free (ts);

	/* update the entdata pointer in the vers_ts structure */
	p = findnode (finfo->entries, finfo->file);
	assert (p);
	vers->entdata = p->data;

	ret = 0;
    }
    else
	ret = 1;			/* files were really different */

    if (tocvsPath)
    {
	/* Need to call unlink myself because the noexec variable
	 * has been set to 1.  */
	TRACE (TRACE_FUNCTION, "unlink (%s)", tocvsPath);
	if ( CVS_UNLINK (tocvsPath) < 0)
	    error (0, errno, "could not remove %s", tocvsPath);
    }

    free (options);
    return ret;
}
Пример #9
0
/* Diff revisions and/or files.  OPTS controls the format of the diff
   (it contains options such as "-w -c", &c), or "" for the default.
   OPTIONS controls keyword expansion, as a string starting with "-k",
   or "" to use the default.  REV1 is the first revision to compare
   against; it must be non-NULL.  If REV2 is non-NULL, compare REV1
   and REV2; if REV2 is NULL compare REV1 with the file in the working
   directory, whose name is WORKFILE.  LABEL1 and LABEL2 are default
   file labels, and (if non-NULL) should be added as -L options
   to diff.  Output goes to stdout.

   Return value is 0 for success, -1 for a failure which set errno,
   or positive for a failure which printed a message on stderr.

   This used to exec rcsdiff, but now calls RCS_checkout and diff_exec.

   An issue is what timezone is used for the dates which appear in the
   diff output.  rcsdiff uses the -z flag, which is not presently
   processed by CVS diff, but I'm not sure exactly how hard to worry
   about this--any such features are undocumented in the context of
   CVS, and I'm not sure how important to users.  */
int
RCS_exec_rcsdiff (RCSNode *rcsfile, int diff_argc,
		  char * const *diff_argv, const char *options,
                  const char *rev1, const char *rev1_cache, const char *rev2,
                  const char *label1, const char *label2, const char *workfile)
{
    char *tmpfile1 = NULL;
    char *tmpfile2 = NULL;
    const char *use_file1, *use_file2;
    int status, retval;


    cvs_output ("\
===================================================================\n\
RCS file: ", 0);
    cvs_output (rcsfile->print_path, 0);
    cvs_output ("\n", 1);

    /* Historically, `cvs diff' has expanded the $Name keyword to the
       empty string when checking out revisions.  This is an accident,
       but no one has considered the issue thoroughly enough to determine
       what the best behavior is.  Passing NULL for the `nametag' argument
       preserves the existing behavior. */

    cvs_output ("retrieving revision ", 0);
    cvs_output (rev1, 0);
    cvs_output ("\n", 1);

    if (rev1_cache != NULL)
	use_file1 = rev1_cache;
    else
    {
	tmpfile1 = cvs_temp_name();
	status = RCS_checkout (rcsfile, NULL, rev1, NULL, options, tmpfile1,
	                       NULL, NULL);
	if (status > 0)
	{
	    retval = status;
	    goto error_return;
	}
	else if (status < 0)
	{
	    error( 0, errno,
	           "cannot check out revision %s of %s", rev1, rcsfile->path );
	    retval = 1;
	    goto error_return;
	}
	use_file1 = tmpfile1;
    }

    if (rev2 == NULL)
    {
	assert (workfile != NULL);
	use_file2 = workfile;
    }
    else
    {
	tmpfile2 = cvs_temp_name ();
	cvs_output ("retrieving revision ", 0);
	cvs_output (rev2, 0);
	cvs_output ("\n", 1);
	status = RCS_checkout (rcsfile, NULL, rev2, NULL, options,
			       tmpfile2, NULL, NULL);
	if (status > 0)
	{
	    retval = status;
	    goto error_return;
	}
	else if (status < 0)
	{
	    error (0, errno,
		   "cannot check out revision %s of %s", rev2, rcsfile->path);
	    return 1;
	}
	use_file2 = tmpfile2;
    }

    RCS_output_diff_options (diff_argc, diff_argv, rev1, rev2, workfile);
    status = diff_exec (use_file1, use_file2, label1, label2,
			diff_argc, diff_argv, RUN_TTY);
    if (status >= 0)
    {
	retval = status;
	goto error_return;
    }
    else if (status < 0)
    {
	error (0, errno,
	       "cannot diff %s and %s", use_file1, use_file2);
	retval = 1;
	goto error_return;
    }

 error_return:
    {
	/* Call CVS_UNLINK() below rather than unlink_file to avoid the check
	 * for noexec.
	 */
	if( tmpfile1 != NULL )
	{
	    if( CVS_UNLINK( tmpfile1 ) < 0 )
	    {
		if( !existence_error( errno ) )
		    error( 0, errno, "cannot remove temp file %s", tmpfile1 );
	    }
	    free( tmpfile1 );
	}
	if( tmpfile2 != NULL )
	{
	    if( CVS_UNLINK( tmpfile2 ) < 0 )
	    {
		if( !existence_error( errno ) )
		    error( 0, errno, "cannot remove temp file %s", tmpfile2 );
	    }
	    free (tmpfile2);
	}
    }

    return retval;
}
Пример #10
0
/* ARGSUSED */
static int remove_fileproc (void *callerdat, struct file_info *finfo)
{
    Vers_TS *vers;

    if (force)
    {
	if (!noexec)
	{
	    if ( CVS_UNLINK (finfo->file) < 0 && ! existence_error (errno))
	    {
		error (0, errno, "unable to remove %s", fn_root(finfo->fullname));
	    }
	}
	/* else FIXME should probably act as if the file doesn't exist
	   in doing the following checks.  */
    }

    vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0, 0);

    if (vers->ts_user && (!vers->vn_user || strcmp(vers->vn_user,"0")))
    {
	existing_files++;
	if (!quiet)
	    error (0, 0, "file `%s' still in working directory",
		   fn_root(finfo->fullname));
    }
    else if (vers->vn_user == NULL)
    {
	if (!quiet)
	    error (0, 0, "nothing known about `%s'", fn_root(finfo->fullname));
    }
    else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
    {
	char *fname;

	/*
	 * It's a file that has been added, but not commited yet. So,
	 * remove the ,t file for it and scratch it from the
	 * entries file.  */
	Scratch_Entry (finfo->entries, finfo->file);
	fname = (char*)xmalloc (strlen (finfo->file)
			 + sizeof (CVSADM)
			 + sizeof (CVSEXT_LOG)
			 + 10);
	(void) sprintf (fname, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG);
	if (unlink_file (fname) < 0
	    && !existence_error (errno))
	    error (0, errno, "cannot remove %s", CVSEXT_LOG);
	if (!quiet)
	    error (0, 0, "removed `%s'", fn_root(finfo->fullname));

#ifdef SERVER_SUPPORT
	if (server_active)
	    server_checked_in (finfo->file, finfo->update_dir, finfo->repository);
#endif
	xfree (fname);
    }
    else if (vers->vn_user[0] == '-')
    {
	if (!quiet)
	    error (0, 0, "file `%s' already scheduled for removal",
		   fn_root(finfo->fullname));
    }
    else if (vers->tag != NULL && ((isdigit ((unsigned char) *vers->tag)) || !RCS_isbranch(finfo->rcs, vers->tag)))
    {
	/* Commit will just give an error, and so there seems to be
	   little reason to allow the remove.  I mean, conflicts that
	   arise out of parallel development are one thing, but conflicts
	   that arise from sticky tags are quite another.  */
	error (0, 0, "\
cannot remove file `%s' which has a sticky tag of `%s'",
	       fn_root(finfo->fullname), vers->tag);
	bad_files++;
    }
    else
    {
	char *fname;

	/* Re-register it with a negative version number.  */
	fname = (char*)xmalloc (strlen (vers->vn_user) + 5);
	(void) strcpy (fname, "-");
	(void) strcat (fname, vers->vn_user);
	Register (finfo->entries, finfo->file, fname, vers->ts_rcs, vers->options,
		  vers->tag, vers->date, vers->ts_conflict, NULL, NULL, vers->tt_rcs, vers->edit_revision, vers->edit_tag, vers->edit_bugid, NULL);
	if (!quiet)
	    error (0, 0, "scheduling `%s' for removal", fn_root(finfo->fullname));
	removed_files++;

#ifdef SERVER_SUPPORT
	if (server_active)
	    server_checked_in (finfo->file, finfo->update_dir, finfo->repository);
#endif
	xfree (fname);
    }

    freevers_ts (&vers);
    return (0);
}
Пример #11
0
/*ARGSUSED*/
static int remove_force_fileproc (void *callerdat, struct file_info *finfo)
{
    if (CVS_UNLINK (finfo->file) < 0 && ! existence_error (errno))
	error (0, errno, "unable to remove %s", fn_root(finfo->fullname));
    return 0;
}
Пример #12
0
/* There are at least four functions for generating temporary
 * filenames.  We use mkstemp (BSD 4.3) if possible, else tempnam (SVID 3),
 * else mktemp (BSD 4.3), and as last resort tmpnam (POSIX).  Reason is that
 * mkstemp, tempnam, and mktemp both allow to specify the directory in which
 * the temporary file will be created.
 *
 * And the _correct_ way to use the deprecated functions probably involves
 * opening file descriptors using O_EXCL & O_CREAT and even doing the annoying
 * NFS locking thing, but until I hear of more problems, I'm not going to
 * bother.
 */
FILE *cvs_temp_file (char **filename)
{
    char *fn;
    FILE *fp;

    /* FIXME - I'd like to be returning NULL here in noexec mode, but I think
     * some of the rcs & diff functions which rely on a temp file run in
     * noexec mode too.
     */

    assert (filename != NULL);

#ifdef HAVE_MKSTEMP

    {
        int fd;

        fn = (char*)xmalloc (strlen (Tmpdir) + 11);
        sprintf (fn, "%s/%s", Tmpdir, "cvsXXXXXX" );
        fd = mkstemp (fn);

        /* a NULL return will be interpreted by callers as an error and
         * errno should still be set
         */
        if (fd == -1) fp = NULL;
        else if ((fp = CVS_FDOPEN (fd, "w+")) == NULL)
        {
            /* attempt to close and unlink the file since mkstemp returned sucessfully and
             * we believe it's been created and opened
             */
            int save_errno = errno;
            if (close (fd))
                error (0, errno, "Failed to close temporary file %s", fn_root(fn));
            if (CVS_UNLINK (fn))
                error (0, errno, "Failed to unlink temporary file %s", fn_root(fn));
            errno = save_errno;
        }

        if (fp == NULL) xfree (fn);
        /* mkstemp is defined to open mode 0600 using glibc 2.0.7+ */
        /* FIXME - configure can probably tell us which version of glibc we are
         * linking to and not chmod for 2.0.7+
         */
        else chmod (fn, 0600);

    }

#elif HAVE_TEMPNAM

    /* tempnam has been deprecated due to under-specification */

    fn = tempnam (Tmpdir, "cvs");
    if (fn == NULL) fp = NULL;
    else if ((fp = CVS_FOPEN (fn, "w+")) == NULL) xfree (fn);
    else chmod (fn, 0600);

    /* tempnam returns a pointer to a newly malloc'd string, so there's
     * no need for a xstrdup
     */

#elif HAVE_MKTEMP

    /* mktemp has been deprecated due to the BSD 4.3 specification specifying
     * that XXXXXX will be replaced by a PID and a letter, creating only 26
     * possibilities, a security risk, and a race condition.
     */

    {
        char *ifn;

        ifn = xmalloc (strlen (Tmpdir) + 11);
        sprintf (ifn, "%s/%s", Tmpdir, "cvsXXXXXX" );
        fn = mktemp (ifn);

        if (fn == NULL) fp = NULL;
        else fp = CVS_FOPEN (fn, "w+");

        if (fp == NULL) xfree (ifn);
        else chmod (fn, 0600);

    }

#else	/* use tmpnam if all else fails */

    /* tmpnam is deprecated */

    {
        char ifn[L_tmpnam + 1];

        fn = tmpnam (ifn);

        if (fn == NULL) fp = NULL;
        else if ((fp = CVS_FOPEN (ifn, "w+")) != NULL)
        {
            fn = xstrdup (ifn);
            chmod (fn, 0600);
        }

    }

#endif

    *filename = fn;
    return fp;
}