X-Authentication-Warning: delorie.com: mail set sender to djgpp-workers-bounces using -f X-Recipient: djgpp-workers AT delorie DOT com Message-ID: <52D08912.4080904@gmx.de> Date: Sat, 11 Jan 2014 00:58:10 +0100 From: Juan Manuel Guerrero User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Thunderbird/24.2.0 MIME-Version: 1.0 To: djgpp-workers AT delorie DOT com Subject: Re: Implementation of mkdtemp. References: <52BDF3B8 DOT 5020205 AT gmx DOT de> In-Reply-To: <52BDF3B8.5020205@gmx.de> Content-Type: text/plain; charset=ISO-8859-15; format=flowed Content-Transfer-Encoding: 7bit X-Provags-ID: V03:K0:F6JwZa7e3GNdcem9otAINTvnigxUB1ew/RSaDHu2adSezCkUt+A FKhpmkRKZ7n4r8vT1iU+1ifbhe/M00zkB5YEcjxPyJoF5fiQbU8ey/stwsuXq/FCndpASr9 g0OfttIsVWstTIMHFO47+IHdI290zUV0WEsetXKhDfKT74hGK5gwzEerECDe42YA8GC6FVz iC230QJ33ONmLTYbx1LEQ== Reply-To: djgpp-workers AT delorie DOT com Am 27.12.2013 22:40, schrieb Juan Manuel Guerrero: > The patch below provides an implementation of mkdtemp together with a small > test program. AFAIK the function is POSIX.1-2008. > The implementation uses mktemp to create a directory from the template. > This makes that the implementation deviates from the posix standard: > - due to the 8.3 file name limitation the string before XXXXXX is truncated > to 2 characters by mktemp. > - mktemp appends a .tmp extension to the created file name. > Make mkdtemp fully POSIX posix conform would impose either to change mktemp > or to write a new mktemp version for mkdtemp. Changing mktemp may break old > so I have decided to accept these deviations from standard. > As usual, suggestions, objections, comments are welcome. [snip] OFYI, I committed the patch below that makes mktemp a little bit more POSIX compliant. Both mkstemp and mkdtemp use it to create an unique file name. Now mktemp first checks if LFN support is available or not. If LFN is available mktemp will check if the passed template has at least six upper case trailing 'X's as POSIX mandates. If this is not the case it will abort without changing the name template but setting EINVAL. If the template is valid then a file name will be created without truncating the leading part of the template. This means that the file name will not fit into the 8.3 limits of DOS. If LFN support is available there is no reason to accept the 8.3 file name limitation. E.g.: the prefix "foobar" in the template foobarXXXXXX will not be truncated to "fo" as it used to be. Also mktemp will no longer append a trailing ".tmp" extension to the file name as this may be the case under certain circumstances. If LFN support is not available, then mktemp behavior will be identical as it has been before. IOW, if only SFN support is available mktemp will work in exactly the same way as before. In this case there will be no user visible changes. I have added some checks too. Regards, Juan M. Guerrero # cvs ci -m"Info about POSIX compliant behavior of mktemp added." djgpp/src/docs/kb/wc204.txi # cvs ci -m"Copyright updated." djgpp/src/libc/compat/stdio/mkstemp.c # cvs ci -m"Added POSIX compliant behavior to mktemp if LFN support is available." djgpp/src/libc/compat/stdio/mktemp.c # cvs ci -m"Added documentation about POSIX compliant behavior of mktemp if LFN support is available." djgpp/src/libc/compat/stdio/mktemp.txh 2013-12-31 Juan Manuel Guerrero * djgpp/src/docs/kb/wc204.txi: Info about POSIX compliant behavior of mktemp and mkstemp added. * djgpp/src/libc/compat/stdio/mkstemp.c: Copyright updated. * djgpp/src/libc/compat/stdio/mktemp.c: Added POSIX compliant behavior to mktemp if LFN support is available. * djgpp/src/libc/compat/stdio/mktemp.txh: Added documentation about POSIX compliant behavior of mktemp if LFN support is available. * djgpp/src/libc/compat/stdio/mkstemp.txh: Added documentation about POSIX compliant behavior of mkstemp if LFN support is available. 2013-12-23 Juan Manuel Guerrero * djgpp/include/stdlib.h: Prototype for mkdtemp added. * djgpp/src/libc/posix/stdlib/mkdtemp.c: Implementation of mkdtemp. * djgpp/src/libc/posix/stdlib/mkdtemp.txh: Documentation of mkdtemp. * djgpp/src/libc/posix/stdlib/makefile: mkdtemp added to target list. * djgpp/src/docs/kb/wc204.txi: Info about mkdtemp added. * djgpp/tests/libc/posix/stdlib/makefile: Check for mkdtemp added to target list. * djgpp/tests/libc/posix/stdlib/mkdtemp.c: Checks for mkdtemp. diff -aprNU5 djgpp.orig/src/docs/kb/wc204.txi djgpp/src/docs/kb/wc204.txi --- djgpp.orig/src/docs/kb/wc204.txi 2013-12-31 17:31:22 +0100 +++ djgpp/src/docs/kb/wc204.txi 2014-01-09 22:21:30 +0100 @@ -1366,5 +1366,14 @@ A bug was fixed in @code{memalign} and @ with linux and other @acronym{POSIX} compliant systems. This has been changed so that the argument order now is the same that on @acronym{POSIX} compliant systems. @code{valloc}, that calls @code{memalign}, has been adjusted accordingly. The @code{memalign} declaration has been moved from @file{stdlib.h} to @file{malloc.h} to comply with the @acronym{C99} standard. + +@findex mktemp AT r{, and @acronym{SUS} compliance} +@findex mkstemp AT r{, and @acronym{SUS} compliance} +The behavior of @code{mktemp} and @code{mkstemp} now depends on if @acronym{LFN} support +is available or not. If @acronym{LFN} support is available the generated file names +will comply with the @acronym{POSIX} standard. This means all characters of the template +before the trailing six upper case @code{X}s will not be truncated to create a valid +short file name. If @acronym{LFN} support is not available @code{mktemp} will behave +as before. This means only two characters before the trailing @code{X}s will be possible. diff -aprNU5 djgpp.orig/src/libc/compat/stdio/mkstemp.c djgpp/src/libc/compat/stdio/mkstemp.c --- djgpp.orig/src/libc/compat/stdio/mkstemp.c 2013-12-31 18:56:00 +0100 +++ djgpp/src/libc/compat/stdio/mkstemp.c 2014-01-09 22:24:04 +0100 @@ -1,5 +1,6 @@ +/* Copyright (C) 2014 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2013 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2009 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */ diff -aprNU5 djgpp.orig/src/libc/compat/stdio/mkstemp.txh djgpp/src/libc/compat/stdio/mkstemp.txh --- djgpp.orig/src/libc/compat/stdio/mkstemp.txh 2009-04-13 12:35:04 +0100 +++ djgpp/src/libc/compat/stdio/mkstemp.txh 2014-01-09 22:21:30 +0100 @@ -14,13 +14,16 @@ int mkstemp(char *template); @code{X} characters. This function replaces the @code{XXXXXX} with a set of characters such that the resulting file name names a nonexisting file. It then creates and opens the file in a way which guarantees that no other process can access this file. -Note that since MS-DOS is limited to eight characters for the file name, -and since none of the @code{X}'s get replaced by a dot, you can only -have two additional characters before the @code{X}'s. +Note that since MS-DOS and file systems that lack of @acronym{LFN} support +are limited to eight characters for the file name, and since none of the @code{X}'s +get replaced by a dot, you can only have two additional characters before +the @code{X}'s. But if the file systems offers @acronym{LFN} support no limitation +concerning the number of characters before the @code{X}'s is given and the +created file names will be @acronym{POSIX} compliant. Note also that the path you give will be modified in place. @subheading Return Value @@ -36,9 +39,9 @@ prototype is also kept in @code{ #include #include +#include #include #include #include #include +#include static int mktemp_count = -1; char * mktemp(char *_template) { - static int count = 0; - char *cp, *dp; - int i, len, xcount, loopcnt; - char real_path[FILENAME_MAX]; + register int i, len, xcount; + unsigned int use_lfn = _USE_LFN; - /* Reinitialize counter if we were restarted (emacs). */ - if (__bss_count != mktemp_count) + for (i = 0; _template[i]; i++) + ; + for (xcount = 0, len = i; xcount < 6 && _template[--i] == 'X'; xcount++) + ; + if (use_lfn && xcount < 6) { - mktemp_count = __bss_count; - count = 0; + errno = EINVAL; + return NULL; } - - len = strlen(_template); - cp = _template + len; - - xcount = 0; - while (xcount < 6 && cp > _template && cp[-1] == 'X') - xcount++, cp--; - - if (xcount) + else { - dp = cp; - while (dp > _template && dp[-1] != '/' && dp[-1] != '\\' && dp[-1] != ':') - dp--; - - /* Keep the first characters of the template, but turn the rest into - Xs. */ - while (cp > dp + 8 - xcount) + static int count = 0; + + /* Reinitialize counter if we were restarted (emacs). */ + if (__bss_count != mktemp_count) { - *--cp = 'X'; - xcount = (xcount >= 6) ? 6 : 1 + xcount; + mktemp_count = __bss_count; + count = 0; } - /* If dots occur too early -- squash them. */ - while (dp < cp) + if (xcount) { - if (*dp == '.') *dp = 'a'; - dp++; - } + char *cp, *dp; + char real_path[FILENAME_MAX]; + int loopcnt; - /* Try to add ".tmp" to the filename. Truncate unused Xs. */ - if (cp + xcount + 3 < _template + len) - strcpy (cp + xcount, ".tmp"); - else - cp[xcount] = 0; + dp = cp = _template + i; - /* This loop can run up to 2<<(5*6) times, or about 10^9 times. */ - for (loopcnt = 0; loopcnt < (1 << (5 * xcount)); loopcnt++) - { - int c = count++; - for (i = 0; i < xcount; i++, c >>= 5) - cp[i] = "abcdefghijklmnopqrstuvwxyz012345"[c & 0x1f]; - if (!__solve_symlinks(_template, real_path)) + while (dp > _template && dp[-1] != '/' && dp[-1] != '\\' && dp[-1] != ':') + dp--; + + if (!use_lfn) + { + /* Keep the first characters of the template, + but turn the rest into Xs. */ + while (cp > dp + 8 - xcount) + { + *--cp = 'X'; + xcount = (xcount >= 6) ? 6 : 1 + xcount; + } + + /* If dots occur too early -- squash them. */ + while (dp < cp) + { + if (*dp == '.') *dp = 'a'; + dp++; + } + + /* Try to add ".tmp" to the filename. Truncate unused Xs. */ + if (cp + xcount + 3 < _template + len) + strcpy (cp + xcount, ".tmp"); + else + cp[xcount] = 0; + + } + + /* This loop can run up to 2<<(5*6) times, or about 10^9 times. */ + for (loopcnt = 0; loopcnt < (1 << (5 * xcount)); loopcnt++) { - *_template = 0; - return NULL; + int c = count++; + for (i = 0; i < xcount; i++, c >>= 5) + cp[i] = "abcdefghijklmnopqrstuvwxyz012345"[c & 0x1f]; + if (!__solve_symlinks(_template, real_path)) + { + *_template = 0; + return NULL; + } + if (!__file_exists(real_path)) + return _template; } - if (!__file_exists(real_path)) - return _template; } } /* Failure: truncate the template and return NULL. */ *_template = 0; @@ -90,44 +107,52 @@ int main(void) { char *s, *s0; s = strdup (s0 = "/usr/foo/dddddddXXXXXX"); + errno = 0; mktemp (s); - printf ("%s -> %s\n", s0, s); + printf ("%s -> %s errno = %d\n", s0, s, errno); s = strdup (s0 = "/usr/foo/ddddddXXXXXX"); + errno = 0; mktemp (s); - printf ("%s -> %s\n", s0, s); + printf ("%s -> %s errno = %d\n", s0, s, errno); s = strdup (s0 = "/usr/foo/dddddXXXXXX"); + errno = 0; mktemp (s); - printf ("%s -> %s\n", s0, s); + printf ("%s -> %s errno = %d\n", s0, s, errno); s = strdup (s0 = "/usr/foo/ddddXXXXXX"); + errno = 0; mktemp (s); - printf ("%s -> %s\n", s0, s); + printf ("%s -> %s errno = %d\n", s0, s, errno); s = strdup (s0 = "/usr/foo/dddXXXXXX"); + errno = 0; mktemp (s); - printf ("%s -> %s\n", s0, s); + printf ("%s -> %s errno = %d\n", s0, s, errno); s = strdup (s0 = "/usr/foo/.dddXXXXXX"); + errno = 0; mktemp (s); - printf ("%s -> %s\n", s0, s); + printf ("%s -> %s errno = %d\n", s0, s, errno); s = strdup (s0 = "/usr/foo/dddXXYXXX"); + errno = 0; mktemp (s); - printf ("%s -> %s\n", s0, s); + printf ("%s -> %s errno = %d\n", s0, s, errno); s = strdup (s0 = "/usr/foo/XXXXXX"); + errno = 0; mktemp (s); - printf ("%s -> %s\n", s0, s); + printf ("%s -> %s errno = %d\n", s0, s, errno); s = strdup (s0 = "/usr/foo/XXXXXXXX"); + errno = 0; mktemp (s); - printf ("%s -> %s\n", s0, s); + printf ("%s -> %s errno = %d\n", s0, s, errno); return 0; } #endif - diff -aprNU5 djgpp.orig/src/libc/compat/stdio/mktemp.txh djgpp/src/libc/compat/stdio/mktemp.txh --- djgpp.orig/src/libc/compat/stdio/mktemp.txh 2009-04-13 12:35:10 +0100 +++ djgpp/src/libc/compat/stdio/mktemp.txh 2014-01-09 22:21:30 +0100 @@ -13,13 +13,16 @@ char *mktemp(char *template); @var{template} is a file specification that ends with six trailing @code{X} characters. This function replaces the @code{XXXXXX} with a set of characters such that the resulting file name names a nonexisting file. -Note that since MS-DOS is limited to eight characters for the file name, -and since none of the @code{X}'s get replaced by a dot, you can only -have two additional characters before the @code{X}'s. +Note that since MS-DOS and file systems that lack of @acronym{LFN} support +are limited to eight characters for the file name, and since none of the @code{X}'s +get replaced by a dot, you can only have two additional characters before +the @code{X}'s. But if the file systems offers @acronym{LFN} support no limitation +concerning the number of characters before the @code{X}'s is given and the +created file names will be @acronym{POSIX} compliant. @subheading Return Value If a unique name cannot be chosen, @code{NULL} is returned. Otherwise the resulting filename is returned. @@ -33,11 +36,11 @@ prototype is also kept in @code{ +#include +#include +#include +#include +#include + +char * +mkdtemp(char *_template) +{ + char tmp_name[FILENAME_MAX]; + char real_path[FILENAME_MAX]; + int rv; + + + do { + strcpy(tmp_name, _template); + errno = 0; + } while (mktemp(tmp_name) != NULL + && __solve_symlinks(tmp_name, real_path) + && (rv = mkdir(real_path, S_IWUSR)) + && errno == EEXIST); + + if (rv == 0) + { + strcpy(_template, tmp_name); + return _template; + } + else + return NULL; +} diff -aprNU5 djgpp.orig/src/libc/posix/stdlib/mkdtemp.txh djgpp/src/libc/posix/stdlib/mkdtemp.txh --- djgpp.orig/src/libc/posix/stdlib/mkdtemp.txh 1970-01-01 01:00:00 +0100 +++ djgpp/src/libc/posix/stdlib/mkdtemp.txh 2014-01-09 22:42:40 +0100 @@ -0,0 +1,45 @@ +@node mkdtemp, file system +@findex mkdtemp +@subheading Syntax + +@example +#include + +char *mkdtemp(char *template); +@end example + +@subheading Description + +The @code{mkdtemp} function generates an uniquely named temporary directory +from template. @var{template} is a file specification that ends with six +trailing @code{X} characters. This function replaces the @code{XXXXXX} with +a set of characters such that the resulting directory name names a nonexisting +directory. It then creates the directory. + +Note that since MS-DOS and file systems that lack of @acronym{LFN} support +are limited to eight characters for the file name, and since none of the @code{X}'s +get replaced by a dot, you can only have two additional characters before +the @code{X}'s. But if the file systems offers @acronym{LFN} support no limitation +concerning the number of characters before the @code{X}'s is given and the +created file names will be @acronym{POSIX} compliant. + +Note also that the path you give will be modified in place. + +@subheading Return Value + +The pointer to the modified template string on success, +and @code{NULL} on failure, in which case @code{errno} is set +appropriately. + + +@subheading Portability + +@portability !ansi, posix + +@subheading Example + +@example +char temp_dir, path[100]; +strcpy(path, _USE_LFN ? "/tmp/temp_directoryXXXXXX" : "/tmp/tdXXXXXX"); +temp_dir = mkdtemp(path); +@end example diff -aprNU5 djgpp.orig/tests/libc/posix/stdlib/makefile djgpp/tests/libc/posix/stdlib/makefile --- djgpp.orig/tests/libc/posix/stdlib/makefile 1970-01-01 01:00:00 +0100 +++ djgpp/tests/libc/posix/stdlib/makefile 2014-01-09 22:42:40 +0100 @@ -0,0 +1,5 @@ +TOP=../.. + +SRC += mkdtemp.c + +include $(TOP)/../makefile.inc diff -aprNU5 djgpp.orig/tests/libc/posix/stdlib/mkdtemp.c djgpp/tests/libc/posix/stdlib/mkdtemp.c --- djgpp.orig/tests/libc/posix/stdlib/mkdtemp.c 1970-01-01 01:00:00 +0100 +++ djgpp/tests/libc/posix/stdlib/mkdtemp.c 2014-01-09 22:42:40 +0100 @@ -0,0 +1,32 @@ +#include +#include +#include +#include + + +int +main(void) +{ + char *s; + const char *s0[] = { + "/dev/env/TMPDIR/foobarXXXXXX", + "/dev/env/TMPDIR/foobarXXXXXX", + "/dev/env/TMPDIR/foobarXXXXX", + "/dev/env/TMPDIR/foobarXXXXXY", + "/dev/env/TMPDIR/foo/barXXXXXX", + "/dev/env/TMPDIR/XXXXXX", + "/dev/env/TMPDIR/XXXXXXXX" + }; + unsigned int i; + + + for (i = 0; i < sizeof s0 / sizeof s0[0]; i++) + { + s = strdup(s0[i]); + errno = 0; + mkdtemp(s); + printf("%s -> %s errno = %d\n", s0[i], s, errno); + } + + return 0; +}