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