/* * 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); }
/* * 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); }
/* * 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)); }
/* * 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)); }
/* 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; }
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 */ }
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; }
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; }
/* 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; }
/* 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); }
/*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; }
/* 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; }