Subject: * unix/tclUnixFCmd.c (TclUnixOpenTemporaryFile): [Bug 2933003]: Factor out all the code to do temporary file creation so that it is possible to make it correct in one place. Allow overriding of the back-stop default temporary file location at compile time by setting the TCL_TEMPORARY_FILE_DIRECTORY #def to a string containing the directory name (defaults to "/tmp" as that is the most common default).

From: "[email-masked]" <dkf>
Date: Wed, 14 Nov 2012 13:01:46 +0000

---
 ChangeLog                 |  9 ++++++++
 generic/tclInt.decls      |  6 ++++++
 generic/tclIntPlatDecls.h | 20 ++++++++++++------
 generic/tclStubInit.c     |  4 ++--
 unix/tclUnixFCmd.c        | 54 +++++++++++++++++++++++++++++++++++++++--------
 unix/tclUnixPipe.c        | 43 ++++++++++---------------------------
 6 files changed, 87 insertions(+), 49 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index ff9713f..652022e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2012-11-14  Donal K. Fellows  <[email-masked]>
+
+	* unix/tclUnixFCmd.c (TclUnixOpenTemporaryFile): [Bug 2933003]: Factor
+	out all the code to do temporary file creation so that it is possible
+	to make it correct in one place. Allow overriding of the back-stop
+	default temporary file location at compile time by setting the
+	TCL_TEMPORARY_FILE_DIRECTORY #def to a string containing the directory
+	name (defaults to "/tmp" as that is the most common default).
+
 2012-11-13  Joe Mistachkin  <[email-masked]>
 
 	* win/tclWinInit.c: also search for the library directory (init.tcl,
diff --git a/generic/tclInt.decls b/generic/tclInt.decls
index 9f73a31..8f8b992 100644
--- a/generic/tclInt.decls
+++ b/generic/tclInt.decls
@@ -1219,6 +1219,12 @@ declare 14 unix {
 	    const Tcl_StatBuf *statBufPtr, int dontCopyAtts)
 }
 
+# Added in 8.6; core of TclpOpenTemporaryFile
+declare 20 unix {
+    int TclUnixOpenTemporaryFile(Tcl_Obj *dirObj, Tcl_Obj *basenameObj,
+	    Tcl_Obj *extensionObj, Tcl_Obj *resultingNameObj)
+}
+
 ################################
 # Mac OS X specific functions
 
diff --git a/generic/tclIntPlatDecls.h b/generic/tclIntPlatDecls.h
index 36cb918..16d8896 100644
--- a/generic/tclIntPlatDecls.h
+++ b/generic/tclIntPlatDecls.h
@@ -84,7 +84,10 @@ EXTERN int		TclUnixCopyFile(const char *src, const char *dst,
 /* Slot 17 is reserved */
 /* Slot 18 is reserved */
 /* Slot 19 is reserved */
-/* Slot 20 is reserved */
+/* 20 */
+EXTERN int		TclUnixOpenTemporaryFile(Tcl_Obj *dirObj,
+				Tcl_Obj *basenameObj, Tcl_Obj *extensionObj,
+				Tcl_Obj *resultingNameObj);
 /* Slot 21 is reserved */
 /* Slot 22 is reserved */
 /* Slot 23 is reserved */
@@ -225,7 +228,10 @@ EXTERN int		TclMacOSXMatchType(Tcl_Interp *interp,
 /* 19 */
 EXTERN void		TclMacOSXNotifierAddRunLoopMode(
 				const void *runLoopMode);
-/* Slot 20 is reserved */
+/* 20 */
+EXTERN int		TclUnixOpenTemporaryFile(Tcl_Obj *dirObj,
+				Tcl_Obj *basenameObj, Tcl_Obj *extensionObj,
+				Tcl_Obj *resultingNameObj);
 /* Slot 21 is reserved */
 /* Slot 22 is reserved */
 /* Slot 23 is reserved */
@@ -263,7 +269,7 @@ typedef struct TclIntPlatStubs {
     void (*reserved17)(void);
     void (*reserved18)(void);
     void (*reserved19)(void);
-    void (*reserved20)(void);
+    int (*tclUnixOpenTemporaryFile) (Tcl_Obj *dirObj, Tcl_Obj *basenameObj, Tcl_Obj *extensionObj, Tcl_Obj *resultingNameObj); /* 20 */
     void (*reserved21)(void);
     void (*reserved22)(void);
     void (*reserved23)(void);
@@ -327,7 +333,7 @@ typedef struct TclIntPlatStubs {
     int (*tclMacOSXCopyFileAttributes) (const char *src, const char *dst, const Tcl_StatBuf *statBufPtr); /* 17 */
     int (*tclMacOSXMatchType) (Tcl_Interp *interp, const char *pathName, const char *fileName, Tcl_StatBuf *statBufPtr, Tcl_GlobTypeData *types); /* 18 */
     void (*tclMacOSXNotifierAddRunLoopMode) (const void *runLoopMode); /* 19 */
-    void (*reserved20)(void);
+    int (*tclUnixOpenTemporaryFile) (Tcl_Obj *dirObj, Tcl_Obj *basenameObj, Tcl_Obj *extensionObj, Tcl_Obj *resultingNameObj); /* 20 */
     void (*reserved21)(void);
     void (*reserved22)(void);
     void (*reserved23)(void);
@@ -389,7 +395,8 @@ extern const TclIntPlatStubs *tclIntPlatStubsPtr;
 /* Slot 17 is reserved */
 /* Slot 18 is reserved */
 /* Slot 19 is reserved */
-/* Slot 20 is reserved */
+#define TclUnixOpenTemporaryFile \
+	(tclIntPlatStubsPtr->tclUnixOpenTemporaryFile) /* 20 */
 /* Slot 21 is reserved */
 /* Slot 22 is reserved */
 /* Slot 23 is reserved */
@@ -501,7 +508,8 @@ extern const TclIntPlatStubs *tclIntPlatStubsPtr;
 	(tclIntPlatStubsPtr->tclMacOSXMatchType) /* 18 */
 #define TclMacOSXNotifierAddRunLoopMode \
 	(tclIntPlatStubsPtr->tclMacOSXNotifierAddRunLoopMode) /* 19 */
-/* Slot 20 is reserved */
+#define TclUnixOpenTemporaryFile \
+	(tclIntPlatStubsPtr->tclUnixOpenTemporaryFile) /* 20 */
 /* Slot 21 is reserved */
 /* Slot 22 is reserved */
 /* Slot 23 is reserved */
diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c
index 75af3b7..0bede56 100644
--- a/generic/tclStubInit.c
+++ b/generic/tclStubInit.c
@@ -465,7 +465,7 @@ static const TclIntPlatStubs tclIntPlatStubs = {
     0, /* 17 */
     0, /* 18 */
     0, /* 19 */
-    0, /* 20 */
+    TclUnixOpenTemporaryFile, /* 20 */
     0, /* 21 */
     0, /* 22 */
     0, /* 23 */
@@ -529,7 +529,7 @@ static const TclIntPlatStubs tclIntPlatStubs = {
     TclMacOSXCopyFileAttributes, /* 17 */
     TclMacOSXMatchType, /* 18 */
     TclMacOSXNotifierAddRunLoopMode, /* 19 */
-    0, /* 20 */
+    TclUnixOpenTemporaryFile, /* 20 */
     0, /* 21 */
     0, /* 22 */
     0, /* 23 */
diff --git a/unix/tclUnixFCmd.c b/unix/tclUnixFCmd.c
index d3cc6bf..559992f 100644
--- a/unix/tclUnixFCmd.c
+++ b/unix/tclUnixFCmd.c
@@ -62,6 +62,16 @@
 #define DOTREE_F	3	/* regular file */
 
 /*
+ * Fallback temporary file location the temporary file generation code. Can be
+ * overridden at compile time for when it is known that temp files can't be
+ * written to /tmp (hello, iOS!).
+ */
+
+#ifndef TCL_TEMPORARY_FILE_DIRECTORY
+#define TCL_TEMPORARY_FILE_DIRECTORY	"/tmp"
+#endif
+
+/*
  * Callbacks for file attributes code.
  */
 
@@ -2093,7 +2103,7 @@ TclpObjNormalizePath(
 /*
  *----------------------------------------------------------------------
  *
- * TclpOpenTemporaryFile --
+ * TclpOpenTemporaryFile, TclUnixOpenTemporaryFile --
  *
  *	Creates a temporary file, possibly based on the supplied bits and
  *	pieces of template supplied in the first three arguments. If the
@@ -2103,7 +2113,12 @@ TclpObjNormalizePath(
  *	file to go away once it is no longer needed.
  *
  * Results:
- *	A read-write Tcl Channel open on the file.
+ *	A read-write Tcl Channel open on the file for TclpOpenTemporaryFile,
+ *	or a file descriptor (or -1 on failure) for TclUnixOpenTemporaryFile.
+ *
+ * Side effects:
+ *	Accesses the filesystem. Will set the contents of the Tcl_Obj fourth
+ *	argument (if that is non-NULL).
  *
  *----------------------------------------------------------------------
  */
@@ -2115,11 +2130,30 @@ TclpOpenTemporaryFile(
     Tcl_Obj *extensionObj,
     Tcl_Obj *resultingNameObj)
 {
-    Tcl_Channel chan;
+    int fd = TclUnixOpenTemporaryFile(dirObj, basenameObj, extensionObj,
+	    resultingNameObj);
+
+    if (fd == -1) {
+	return NULL;
+    }
+    return Tcl_MakeFileChannel(INT2PTR(fd), TCL_READABLE|TCL_WRITABLE);
+}
+
+int
+TclUnixOpenTemporaryFile(
+    Tcl_Obj *dirObj,
+    Tcl_Obj *basenameObj,
+    Tcl_Obj *extensionObj,
+    Tcl_Obj *resultingNameObj)
+{
     Tcl_DString template, tmp;
     const char *string;
     int len, fd;
 
+    /*
+     * We should also check against making more then TMP_MAX of these.
+     */
+
     if (dirObj) {
 	string = Tcl_GetStringFromObj(dirObj, &len);
 	Tcl_UtfToExternalDString(NULL, string, len, &template);
@@ -2155,9 +2189,10 @@ TclpOpenTemporaryFile(
     }
 
     if (fd == -1) {
-	return NULL;
+	Tcl_DStringFree(&template);
+	return -1;
     }
-    chan = Tcl_MakeFileChannel(INT2PTR(fd), TCL_READABLE|TCL_WRITABLE);
+
     if (resultingNameObj) {
 	Tcl_ExternalToUtfDString(NULL, Tcl_DStringValue(&template),
 		Tcl_DStringLength(&template), &tmp);
@@ -2176,7 +2211,7 @@ TclpOpenTemporaryFile(
     }
     Tcl_DStringFree(&template);
 
-    return chan;
+    return fd;
 }
 
 /*
@@ -2203,11 +2238,12 @@ DefaultTempDir(void)
 #endif
 
     /*
-     * Assume that "/tmp" is always an existing writable directory; we've no
-     * recovery mechanism if it isn't.
+     * Assume that the default location ("/tmp" if not overridden) is always
+     * an existing writable directory; we've no recovery mechanism if it
+     * isn't.
      */
 
-    return "/tmp";
+    return TCL_TEMPORARY_FILE_DIRECTORY;
 }
 
 #if defined(HAVE_CHFLAGS) && defined(UF_IMMUTABLE)
diff --git a/unix/tclUnixPipe.c b/unix/tclUnixPipe.c
index 654c9d8..9c21b28 100644
--- a/unix/tclUnixPipe.c
+++ b/unix/tclUnixPipe.c
@@ -188,28 +188,16 @@ TclFile
 TclpCreateTempFile(
     const char *contents)	/* String to write into temp file, or NULL. */
 {
-    char fileName[L_tmpnam + 9];
-    const char *native;
-    Tcl_DString dstring;
-    int fd;
+    int fd = TclUnixOpenTemporaryFile(NULL, NULL, NULL, NULL);
 
-    /*
-     * We should also check against making more then TMP_MAX of these.
-     */
-
-    strcpy(fileName, P_tmpdir);				/* INTL: Native. */
-    if (fileName[strlen(fileName) - 1] != '/') {
-	strcat(fileName, "/");				/* INTL: Native. */
-    }
-    strcat(fileName, "tclXXXXXX");
-    fd = mkstemp(fileName);				/* INTL: Native. */
     if (fd == -1) {
 	return NULL;
     }
     fcntl(fd, F_SETFD, FD_CLOEXEC);
-    unlink(fileName);					/* INTL: Native. */
-
     if (contents != NULL) {
+	Tcl_DString dstring;
+	char *native;
+
 	native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring);
 	if (write(fd, native, Tcl_DStringLength(&dstring)) == -1) {
 	    close(fd);
@@ -241,29 +229,20 @@ TclpCreateTempFile(
 Tcl_Obj *
 TclpTempFileName(void)
 {
-    char fileName[L_tmpnam + 9];
-    Tcl_Obj *result = NULL;
+    Tcl_Obj *nameObj = Tcl_NewObj();
     int fd;
 
-    /*
-     * We should also check against making more then TMP_MAX of these.
-     */
-
-    strcpy(fileName, P_tmpdir);		/* INTL: Native. */
-    if (fileName[strlen(fileName) - 1] != '/') {
-	strcat(fileName, "/");		/* INTL: Native. */
-    }
-    strcat(fileName, "tclXXXXXX");
-    fd = mkstemp(fileName);		/* INTL: Native. */
+    Tcl_IncrRefCount(nameObj);
+    fd = TclUnixOpenTemporaryFile(NULL, NULL, NULL, nameObj);
     if (fd == -1) {
+	Tcl_DecrRefCount(nameObj);
 	return NULL;
     }
-    fcntl(fd, F_SETFD, FD_CLOEXEC);
-    unlink(fileName);			/* INTL: Native. */
 
-    result = TclpNativeToNormalized(fileName);
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
+    TclpObjDeleteFile(nameObj);
     close(fd);
-    return result;
+    return nameObj;
 }
 
 /*
-- 
cgit v0.12