OSDN Git Service

6f2685e7cf9a1734768623f5be22738993204635
[mingw/mingw-org-wsl.git] / mingwrt / mingwex / mkstemp.c
1 /*
2  * mkstemp.c
3  *
4  * Implementation of an (approximately) POSIX conforming mkstemp(3)
5  * function; invocation is via an inline wrapper, defined in stdlib.h,
6  * which delegates to the library routine defined herein.
7  *
8  * $Id$
9  *
10  * Written by Keith Marshall  <keithmarshall@users.sourceforge.net>
11  * Copyright (C) 2013, 2014, MinGW.org Project.
12  *
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the "Software"),
16  * to deal in the Software without restriction, including without limitation
17  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18  * and/or sell copies of the Software, and to permit persons to whom the
19  * Software is furnished to do so, subject to the following conditions:
20  *
21  * The above copyright notice, this permission notice, and the following
22  * disclaimer shall be included in all copies or substantial portions of
23  * the Software.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
26  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
28  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER
31  * DEALINGS IN THE SOFTWARE.
32  *
33  */
34 #include <stdio.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <errno.h>
39
40 /* Candidate names for the temporary file are generated, based on a
41  * cryptographically secure random character sequence; this externally
42  * implemented character sequence generator is shared by mkstemp(3)
43  * and mkdtemp(3).
44  */
45 extern char *__mingw_crypto_tmpname( char * );
46
47 int __mingw_mkstemp( int setmode, char *template )
48 {
49   /* Implementation of the low-level functional support for mkstemp(3);
50    * this provides the formal function implementation, including support
51    * for adjustment of its behaviour w.r.t. temporary file persistence.
52    *
53    * By default, temporary files will persist until explicitly deleted;
54    * POSIX prescribes that temporary files are to be created with the
55    * following attributes, (with O_BINARY added, to ensure there is
56    * no undue influence from "helpful" text mode transformations):
57    */
58   static int omode = _O_CREAT | _O_EXCL | _O_RDWR | _O_BINARY;
59
60   if( setmode )
61     /* On POSIX platforms, programmers may adopt an idiom such as:
62      *
63      *   if( mkstemp( template ) >= 0 )
64      *   { unlink( template );
65      *     . . .
66      *   }
67      *
68      * to ensure that a temporary file does NOT persist after it is
69      * closed; MS-Windows does not allow such use of unlink(2), while
70      * the file remains open.  Thus, MS-Windows programmers must take
71      * extra care, to close and unlink temporary files AFTER use, if
72      * similar behaviour is desired.
73      *
74      * To mitigate this MS-Windows limitation, we provide support for
75      * an alternative, MinGW specific idiom:
76      *
77      *   #include <fcntl.h>
78      *
79      *   _MKSTEMP_SETMODE( _O_TEMPORARY );
80      *   if( mkstemp( template ) >= 0 )
81      *   {
82      *     . . .
83      *   }
84      *
85      * to achieve a similar effect to that of the above POSIX idiom.
86      */
87     return omode = (omode & ~_O_TEMPORARY) | (setmode & _O_TEMPORARY);
88
89   else
90   { /* Formal MinGW implementation of the mkstemp(3) function; to begin,
91      * we assume that it may fail, and record an invalid file descriptor
92      * for return in such eventuality.
93      */
94     int fd = -1;
95
96     /* Check that the caller gave us a viable template...
97      */
98     if( template == NULL )
99     {
100       /* ...bailing out, if nothing at all...
101        */
102       errno = EINVAL;
103     }
104     else
105     { /* ...but assume that anything at all is potentially viable;
106        * set up a retry limit, and estimate the storage requirement
107        * for a working scratch buffer.
108        */
109       int retry = TMP_MAX;
110       size_t bufsiz = 1 + strlen( template );
111
112       /* Until we either get a valid file descriptor, or we exhaust
113        * the retry limit while attempting to get one...
114        */
115       while( (fd < 0) && (retry-- > 0) )
116       {
117         /* ...set up the scratch buffer, copy the template into it,
118          * then transform to get a cryptographically secure candidate
119          * file name for the temporary file; (each retry cycle will
120          * generate a randomly differing candidate file name)...
121          */
122         char filename[bufsiz];
123         if( __mingw_crypto_tmpname( strcpy( filename, template ) ) == NULL )
124         {
125           /* ...bailing out, on any unsuccessful attempt to generate
126            * the candidate name; (this is most likely to occur during
127            * the first cycle, due to a malformed template; if we can
128            * successfully generate the first candidate, successive
129            * attempts are unlikely to fail).
130            */
131           errno = EINVAL;
132           retry = 0;
133         }
134         else
135         { /* We got a usable candidate file name; attempt to open it
136            * as a new file...
137            */
138           if( (fd = open( filename, omode, _S_IREAD | _S_IWRITE )) >= 0 )
139             /*
140              * ...and, on success, update the template to reflect the
141              * name of the file we've opened, and we are done...
142              */
143             strcpy( template, filename );
144
145           /* ...but, if we failed for any reason other than that a file
146            * with the candidate name already exists...
147            */
148           else if( errno != EEXIST )
149             /*
150              * ...then, any retry will most likely also fail, so we may
151              * as well just give up now.
152              */
153             retry = 0;
154         }
155       }
156     }
157     /* Finally, whether we succeeded in opening any temporary file, or we
158      * ultimately gave up in disgust, we return the prevailing state of the
159      * file descriptor we attempted to assign.
160      */
161     return fd;
162   }
163 }
164
165 /* $RCSfile$: end of file */