aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2017-08-12 14:00:17 -0700
committerPaul Eggert2017-08-12 15:14:45 -0700
commita6ad98ad66e1d0c0dac5f25ba91e11d0cf9da725 (patch)
tree183748df6a5b71f1b48deecc824eef27939ff2f8 /src
parent9eb30cb03613ae158c870d603a05a6a6393dc485 (diff)
downloademacs-a6ad98ad66e1d0c0dac5f25ba91e11d0cf9da725.tar.gz
emacs-a6ad98ad66e1d0c0dac5f25ba91e11d0cf9da725.zip
Improve make-temp-file performance on local files
For the motivation behind this patch, please see Bug#28023 and: http://emacshorrors.com/posts/make-temp-name.html Although, given the recent changes to Tramp, the related security problem in make-temp-file is already fixed, make-temp-file still has several unnecessary system calls. In the typical case on GNU/Linux, this patch replaces 8 syscalls (symlink, open, close, readlinkat, uname, getpid, unlink, umask) by 2 (open, close). * admin/merge-gnulib (GNULIB_MODULES): Add tempname, now that Emacs is using it directly. * configure.ac (AUTO_DEPEND): Remove AC_SYS_LONG_FILE_NAMES; no longer needed. * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. * lisp/files.el (files--make-magic-temp-file): Rename from make-temp-file. (make-temp-file): Use make-temp-file-internal for non-magic file names. * src/fileio.c: Include tempname.h. (make_temp_name_tbl, make_temp_name_count) (make_temp_name_count_initialized_p, make_temp_name): Remove. (Fmake_temp_file_internal): New function. (Fmake_temp_name): Use it. * src/filelock.c (get_boot_time): Use Fmake_temp_file_internal instead of make_temp_name.
Diffstat (limited to 'src')
-rw-r--r--src/buffer.c1
-rw-r--r--src/fileio.c170
-rw-r--r--src/filelock.c13
-rw-r--r--src/lisp.h1
4 files changed, 50 insertions, 135 deletions
diff --git a/src/buffer.c b/src/buffer.c
index 0d0f43e937b..2d508f35cf6 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1085,7 +1085,6 @@ is first appended to NAME, to speed up finding a non-existent buffer. */)
1085 genbase = name; 1085 genbase = name;
1086 else 1086 else
1087 { 1087 {
1088 /* Note fileio.c:make_temp_name does random differently. */
1089 char number[sizeof "-999999"]; 1088 char number[sizeof "-999999"];
1090 int i = XFASTINT (Frandom (make_number (999999))); 1089 int i = XFASTINT (Frandom (make_number (999999)));
1091 AUTO_STRING_WITH_LEN (lnumber, number, sprintf (number, "-%d", i)); 1090 AUTO_STRING_WITH_LEN (lnumber, number, sprintf (number, "-%d", i));
diff --git a/src/fileio.c b/src/fileio.c
index 31fd84512e1..b7e3b71a475 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -97,6 +97,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
97#include <allocator.h> 97#include <allocator.h>
98#include <careadlinkat.h> 98#include <careadlinkat.h>
99#include <stat-time.h> 99#include <stat-time.h>
100#include <tempname.h>
100 101
101#include <binary-io.h> 102#include <binary-io.h>
102 103
@@ -655,149 +656,67 @@ In Unix-syntax, this function just removes the final slash. */)
655 return val; 656 return val;
656} 657}
657 658
658static const char make_temp_name_tbl[64] = 659DEFUN ("make-temp-file-internal", Fmake_temp_file_internal,
659{ 660 Smake_temp_file_internal, 3, 3, 0,
660 'A','B','C','D','E','F','G','H', 661 doc: /* Generate a new file whose name starts with PREFIX, a string.
661 'I','J','K','L','M','N','O','P', 662Return the name of the generated file. If DIR-FLAG is zero, do not
662 'Q','R','S','T','U','V','W','X', 663create the file, just its name. Otherwise, if DIR-FLAG is non-nil,
663 'Y','Z','a','b','c','d','e','f', 664create an empty directory. The file name should end in SUFFIX.
664 'g','h','i','j','k','l','m','n',
665 'o','p','q','r','s','t','u','v',
666 'w','x','y','z','0','1','2','3',
667 '4','5','6','7','8','9','-','_'
668};
669
670static unsigned make_temp_name_count, make_temp_name_count_initialized_p;
671
672/* Value is a temporary file name starting with PREFIX, a string.
673 665
674 The Emacs process number forms part of the result, so there is 666Signal an error if the file could not be created.
675 no danger of generating a name being used by another process.
676 In addition, this function makes an attempt to choose a name
677 which has no existing file. To make this work, PREFIX should be
678 an absolute file name.
679 667
680 BASE64_P means add the pid as 3 characters in base64 668This function does not grok magic file names. */)
681 encoding. In this case, 6 characters will be added to PREFIX to 669 (Lisp_Object prefix, Lisp_Object dir_flag, Lisp_Object suffix)
682 form the file name. Otherwise, if Emacs is running on a system
683 with long file names, add the pid as a decimal number.
684
685 This function signals an error if no unique file name could be
686 generated. */
687
688Lisp_Object
689make_temp_name (Lisp_Object prefix, bool base64_p)
690{ 670{
691 Lisp_Object val, encoded_prefix; 671 bool make_temp_name = EQ (dir_flag, make_number (0));
692 ptrdiff_t len; 672 CHECK_STRING (suffix);
693 printmax_t pid; 673 if (!make_temp_name)
694 char *p, *data; 674 prefix = Fexpand_file_name (prefix, Vtemporary_file_directory);
695 char pidbuf[INT_BUFSIZE_BOUND (printmax_t)]; 675
696 int pidlen; 676 Lisp_Object encoded_prefix = ENCODE_FILE (prefix);
697 677 Lisp_Object encoded_suffix = ENCODE_FILE (suffix);
698 CHECK_STRING (prefix); 678 ptrdiff_t prefix_len = SBYTES (encoded_prefix);
699 679 ptrdiff_t suffix_len = SBYTES (encoded_suffix);
700 /* VAL is created by adding 6 characters to PREFIX. The first 680 if (INT_MAX < suffix_len)
701 three are the PID of this process, in base 64, and the second 681 args_out_of_range (prefix, suffix);
702 three are incremented if the file already exists. This ensures 682 int nX = 6;
703 262144 unique file names per PID per PREFIX. */ 683 Lisp_Object val = make_uninit_string (prefix_len + nX + suffix_len);
704 684 char *data = SSDATA (val);
705 pid = getpid (); 685 memcpy (data, SSDATA (encoded_prefix), prefix_len);
706 686 memset (data + prefix_len, 'X', nX);
707 if (base64_p) 687 memcpy (data + prefix_len + nX, SSDATA (encoded_suffix), suffix_len);
708 { 688 int kind = (NILP (dir_flag) ? GT_FILE
709 pidbuf[0] = make_temp_name_tbl[pid & 63], pid >>= 6; 689 : make_temp_name ? GT_NOCREATE
710 pidbuf[1] = make_temp_name_tbl[pid & 63], pid >>= 6; 690 : GT_DIR);
711 pidbuf[2] = make_temp_name_tbl[pid & 63], pid >>= 6; 691 int fd = gen_tempname (data, suffix_len, O_BINARY | O_CLOEXEC, kind);
712 pidlen = 3; 692 if (fd < 0 || (NILP (dir_flag) && emacs_close (fd) != 0))
713 }
714 else
715 { 693 {
716#ifdef HAVE_LONG_FILE_NAMES 694 static char const kind_message[][32] =
717 pidlen = sprintf (pidbuf, "%"pMd, pid);
718#else
719 pidbuf[0] = make_temp_name_tbl[pid & 63], pid >>= 6;
720 pidbuf[1] = make_temp_name_tbl[pid & 63], pid >>= 6;
721 pidbuf[2] = make_temp_name_tbl[pid & 63], pid >>= 6;
722 pidlen = 3;
723#endif
724 }
725
726 encoded_prefix = ENCODE_FILE (prefix);
727 len = SBYTES (encoded_prefix);
728 val = make_uninit_string (len + 3 + pidlen);
729 data = SSDATA (val);
730 memcpy (data, SSDATA (encoded_prefix), len);
731 p = data + len;
732
733 memcpy (p, pidbuf, pidlen);
734 p += pidlen;
735
736 /* Here we try to minimize useless stat'ing when this function is
737 invoked many times successively with the same PREFIX. We achieve
738 this by initializing count to a random value, and incrementing it
739 afterwards.
740
741 We don't want make-temp-name to be called while dumping,
742 because then make_temp_name_count_initialized_p would get set
743 and then make_temp_name_count would not be set when Emacs starts. */
744
745 if (!make_temp_name_count_initialized_p)
746 {
747 make_temp_name_count = time (NULL);
748 make_temp_name_count_initialized_p = 1;
749 }
750
751 while (1)
752 {
753 unsigned num = make_temp_name_count;
754
755 p[0] = make_temp_name_tbl[num & 63], num >>= 6;
756 p[1] = make_temp_name_tbl[num & 63], num >>= 6;
757 p[2] = make_temp_name_tbl[num & 63], num >>= 6;
758
759 /* Poor man's congruential RN generator. Replace with
760 ++make_temp_name_count for debugging. */
761 make_temp_name_count += 25229;
762 make_temp_name_count %= 225307;
763
764 if (!check_existing (data))
765 { 695 {
766 /* We want to return only if errno is ENOENT. */ 696 [GT_FILE] = "Creating file with prefix",
767 if (errno == ENOENT) 697 [GT_DIR] = "Creating directory with prefix",
768 return DECODE_FILE (val); 698 [GT_NOCREATE] = "Creating file name with prefix"
769 else 699 };
770 /* The error here is dubious, but there is little else we 700 report_file_error (kind_message[kind], prefix);
771 can do. The alternatives are to return nil, which is
772 as bad as (and in many cases worse than) throwing the
773 error, or to ignore the error, which will likely result
774 in looping through 225307 stat's, which is not only
775 dog-slow, but also useless since eventually nil would
776 have to be returned anyway. */
777 report_file_error ("Cannot create temporary name for prefix",
778 prefix);
779 /* not reached */
780 }
781 } 701 }
702 return DECODE_FILE (val);
782} 703}
783 704
784 705
785DEFUN ("make-temp-name", Fmake_temp_name, Smake_temp_name, 1, 1, 0, 706DEFUN ("make-temp-name", Fmake_temp_name, Smake_temp_name, 1, 1, 0,
786 doc: /* Generate temporary file name (string) starting with PREFIX (a string). 707 doc: /* Generate temporary file name (string) starting with PREFIX (a string).
787The Emacs process number forms part of the result, so there is no
788danger of generating a name being used by another Emacs process
789\(so long as only a single host can access the containing directory...).
790 708
791This function tries to choose a name that has no existing file. 709This function tries to choose a name that has no existing file.
792For this to work, PREFIX should be an absolute file name, and PREFIX 710For this to work, PREFIX should be an absolute file name, and PREFIX
793and the returned string should both be non-magic. 711and the returned string should both be non-magic.
794 712
795There is a race condition between calling `make-temp-name' and creating the 713There is a race condition between calling `make-temp-name' and
796file, which opens all kinds of security holes. For that reason, you should 714later creating the file, which opens all kinds of security holes.
797normally use `make-temp-file' instead. */) 715For that reason, you should normally use `make-temp-file' instead. */)
798 (Lisp_Object prefix) 716 (Lisp_Object prefix)
799{ 717{
800 return make_temp_name (prefix, 0); 718 return Fmake_temp_file_internal (prefix, make_number (0),
719 empty_unibyte_string);
801} 720}
802 721
803DEFUN ("expand-file-name", Fexpand_file_name, Sexpand_file_name, 1, 2, 0, 722DEFUN ("expand-file-name", Fexpand_file_name, Sexpand_file_name, 1, 2, 0,
@@ -6168,6 +6087,7 @@ This includes interactive calls to `delete-file' and
6168 defsubr (&Sfile_name_as_directory); 6087 defsubr (&Sfile_name_as_directory);
6169 defsubr (&Sdirectory_name_p); 6088 defsubr (&Sdirectory_name_p);
6170 defsubr (&Sdirectory_file_name); 6089 defsubr (&Sdirectory_file_name);
6090 defsubr (&Smake_temp_file_internal);
6171 defsubr (&Smake_temp_name); 6091 defsubr (&Smake_temp_name);
6172 defsubr (&Sexpand_file_name); 6092 defsubr (&Sexpand_file_name);
6173 defsubr (&Ssubstitute_in_file_name); 6093 defsubr (&Ssubstitute_in_file_name);
diff --git a/src/filelock.c b/src/filelock.c
index dd8cb28c425..3d6941695ae 100644
--- a/src/filelock.c
+++ b/src/filelock.c
@@ -206,14 +206,11 @@ get_boot_time (void)
206 WTMP_FILE, counter); 206 WTMP_FILE, counter);
207 if (! NILP (Ffile_exists_p (tempname))) 207 if (! NILP (Ffile_exists_p (tempname)))
208 { 208 {
209 /* The utmp functions on mescaline.gnu.org accept only 209 /* The utmp functions on older systems accept only file
210 file names up to 8 characters long. Choose a 2 210 names up to 8 bytes long. Choose a 2 byte prefix, so
211 character long prefix, and call make_temp_file with 211 the 6-byte suffix does not make the name too long. */
212 second arg non-zero, so that it will add not more 212 filename = Fmake_temp_file_internal (build_string ("wt"), Qnil,
213 than 6 characters to the prefix. */ 213 empty_unibyte_string);
214 filename = Fexpand_file_name (build_string ("wt"),
215 Vtemporary_file_directory);
216 filename = make_temp_name (filename, 1);
217 CALLN (Fcall_process, build_string ("gzip"), Qnil, 214 CALLN (Fcall_process, build_string ("gzip"), Qnil,
218 list2 (QCfile, filename), Qnil, 215 list2 (QCfile, filename), Qnil,
219 build_string ("-cd"), tempname); 216 build_string ("-cd"), tempname);
diff --git a/src/lisp.h b/src/lisp.h
index 25be5c0ceea..48cf3b30709 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4014,7 +4014,6 @@ extern bool file_directory_p (const char *);
4014extern bool file_accessible_directory_p (Lisp_Object); 4014extern bool file_accessible_directory_p (Lisp_Object);
4015extern void init_fileio (void); 4015extern void init_fileio (void);
4016extern void syms_of_fileio (void); 4016extern void syms_of_fileio (void);
4017extern Lisp_Object make_temp_name (Lisp_Object, bool);
4018 4017
4019/* Defined in search.c. */ 4018/* Defined in search.c. */
4020extern void shrink_regexp_cache (void); 4019extern void shrink_regexp_cache (void);