diff options
| author | Paul Eggert | 2017-08-12 14:00:17 -0700 |
|---|---|---|
| committer | Paul Eggert | 2017-08-12 15:14:45 -0700 |
| commit | a6ad98ad66e1d0c0dac5f25ba91e11d0cf9da725 (patch) | |
| tree | 183748df6a5b71f1b48deecc824eef27939ff2f8 /src | |
| parent | 9eb30cb03613ae158c870d603a05a6a6393dc485 (diff) | |
| download | emacs-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.c | 1 | ||||
| -rw-r--r-- | src/fileio.c | 170 | ||||
| -rw-r--r-- | src/filelock.c | 13 | ||||
| -rw-r--r-- | src/lisp.h | 1 |
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 | ||
| 658 | static const char make_temp_name_tbl[64] = | 659 | DEFUN ("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', | 662 | Return the name of the generated file. If DIR-FLAG is zero, do not |
| 662 | 'Q','R','S','T','U','V','W','X', | 663 | create the file, just its name. Otherwise, if DIR-FLAG is non-nil, |
| 663 | 'Y','Z','a','b','c','d','e','f', | 664 | create 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 | |||
| 670 | static 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 | 666 | Signal 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 | 668 | This 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 | |||
| 688 | Lisp_Object | ||
| 689 | make_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 | ||
| 785 | DEFUN ("make-temp-name", Fmake_temp_name, Smake_temp_name, 1, 1, 0, | 706 | DEFUN ("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). |
| 787 | The Emacs process number forms part of the result, so there is no | ||
| 788 | danger 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 | ||
| 791 | This function tries to choose a name that has no existing file. | 709 | This function tries to choose a name that has no existing file. |
| 792 | For this to work, PREFIX should be an absolute file name, and PREFIX | 710 | For this to work, PREFIX should be an absolute file name, and PREFIX |
| 793 | and the returned string should both be non-magic. | 711 | and the returned string should both be non-magic. |
| 794 | 712 | ||
| 795 | There is a race condition between calling `make-temp-name' and creating the | 713 | There is a race condition between calling `make-temp-name' and |
| 796 | file, which opens all kinds of security holes. For that reason, you should | 714 | later creating the file, which opens all kinds of security holes. |
| 797 | normally use `make-temp-file' instead. */) | 715 | For 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 | ||
| 803 | DEFUN ("expand-file-name", Fexpand_file_name, Sexpand_file_name, 1, 2, 0, | 722 | DEFUN ("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 *); | |||
| 4014 | extern bool file_accessible_directory_p (Lisp_Object); | 4014 | extern bool file_accessible_directory_p (Lisp_Object); |
| 4015 | extern void init_fileio (void); | 4015 | extern void init_fileio (void); |
| 4016 | extern void syms_of_fileio (void); | 4016 | extern void syms_of_fileio (void); |
| 4017 | extern Lisp_Object make_temp_name (Lisp_Object, bool); | ||
| 4018 | 4017 | ||
| 4019 | /* Defined in search.c. */ | 4018 | /* Defined in search.c. */ |
| 4020 | extern void shrink_regexp_cache (void); | 4019 | extern void shrink_regexp_cache (void); |