From d1fdcab7425f36a34ddeaf304e2c6e3c471ba8db Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Thu, 31 Mar 2011 23:28:48 -0700 Subject: [PATCH] Replace two copies of readlink code with single gnulib version. --- ChangeLog | 7 ++ Makefile.in | 2 +- lib/allocator.h | 45 ++++++++++++ lib/careadlinkat.c | 179 +++++++++++++++++++++++++++++++++++++++++++++ lib/careadlinkat.h | 67 +++++++++++++++++ lib/gnulib.mk | 10 ++- m4/gl-comp.m4 | 10 +++ m4/ssize_t.m4 | 23 ++++++ src/ChangeLog | 12 +++ src/fileio.c | 36 ++------- src/filelock.c | 38 +++------- src/lisp.h | 2 + src/sysdep.c | 18 +++++ 13 files changed, 390 insertions(+), 59 deletions(-) create mode 100644 lib/allocator.h create mode 100644 lib/careadlinkat.c create mode 100644 lib/careadlinkat.h create mode 100644 m4/ssize_t.m4 diff --git a/ChangeLog b/ChangeLog index bf7a6af63b6..979b3efd89e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2011-04-01 Paul Eggert + + Replace two copies of readlink code with single gnulib version. + * Makefile.in (GNULIB_MODULES): Add careadlinkat. + * lib/allocator.h, lib/careadlinkat.c, lib/careadlinkat.h: + * m4/ssize_t.m4: New files, automatically generated from gnulib. + 2011-03-28 Glenn Morris * autogen/update_autogen: Pass -f to autoreconf. diff --git a/Makefile.in b/Makefile.in index 699589c6920..1ac77ed66ac 100644 --- a/Makefile.in +++ b/Makefile.in @@ -331,7 +331,7 @@ DOS_gnulib_comp.m4 = gl-comp.m4 # $(gnulib_srcdir) (relative to $(srcdir) and should have build tools # as per $(gnulib_srcdir)/DEPENDENCIES. GNULIB_MODULES = \ - crypto/md5 dtoastr filemode getloadavg getopt-gnu \ + careadlinkat crypto/md5 dtoastr filemode getloadavg getopt-gnu \ ignore-value intprops lstat mktime readlink \ socklen stdio strftime symlink sys_stat GNULIB_TOOL_FLAGS = \ diff --git a/lib/allocator.h b/lib/allocator.h new file mode 100644 index 00000000000..54cc5ff66f0 --- /dev/null +++ b/lib/allocator.h @@ -0,0 +1,45 @@ +/* Memory allocators such as malloc+free. + + Copyright (C) 2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Paul Eggert. */ + +#ifndef _GL_ALLOCATOR_H + +#include + +struct allocator +{ + /* Call MALLOC to allocate memory, like 'malloc'. On failure MALLOC + should return NULL, though not necessarily set errno. When given + a zero size it may return NULL even if successful. */ + void *(*malloc) (size_t); + + /* If nonnull, call REALLOC to reallocate memory, like 'realloc'. + On failure REALLOC should return NULL, though not necessarily set + errno. When given a zero size it may return NULL even if + successful. */ + void *(*realloc) (void *, size_t); + + /* Call FREE to free memory, like 'free'. */ + void (*free) (void *); + + /* If nonnull, call DIE if MALLOC or REALLOC fails. DIE should + not return. */ + void (*die) (void); +}; + +#endif diff --git a/lib/careadlinkat.c b/lib/careadlinkat.c new file mode 100644 index 00000000000..828c0508db7 --- /dev/null +++ b/lib/careadlinkat.c @@ -0,0 +1,179 @@ +/* Read symbolic links into a buffer without size limitation, relative to fd. + + Copyright (C) 2001, 2003-2004, 2007, 2009-2011 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Paul Eggert, Bruno Haible, and Jim Meyering. */ + +#include + +#include "careadlinkat.h" + +#include "allocator.h" + +#include +#include +#include +#include +#include + +/* Use the system functions, not the gnulib overrides, because this + module does not depend on GNU or POSIX semantics. */ +#undef malloc +#undef realloc + +/* Define this independently so that stdint.h is not a prerequisite. */ +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif + +#ifndef SSIZE_MAX +# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2)) +#endif + +#if ! HAVE_READLINKAT +/* Ignore FD. Get the symbolic link value of FILENAME and put it into + BUFFER, with size BUFFER_SIZE. This function acts like readlink + but has readlinkat's signature. */ +ssize_t +careadlinkatcwd (int fd, char const *filename, char *buffer, + size_t buffer_size) +{ + (void) fd; + return readlink (filename, buffer, buffer_size); +} +#endif + +/* Assuming the current directory is FD, get the symbolic link value + of FILENAME as a null-terminated string and put it into a buffer. + If FD is AT_FDCWD, FILENAME is interpreted relative to the current + working directory, as in openat. + + If the link is small enough to fit into BUFFER put it there. + BUFFER's size is BUFFER_SIZE, and BUFFER can be null + if BUFFER_SIZE is zero. + + If the link is not small, put it into a dynamically allocated + buffer managed by ALLOC. It is the caller's responsibility to free + the returned value if it is nonnull and is not BUFFER. A null + ALLOC stands for the standard allocator. + + The PREADLINKAT function specifies how to read links. + + If successful, return the buffer address; otherwise return NULL and + set errno. */ + +char * +careadlinkat (int fd, char const *filename, + char *buffer, size_t buffer_size, + struct allocator const *alloc, + ssize_t (*preadlinkat) (int, char const *, char *, size_t)) +{ + char *buf; + size_t buf_size; + size_t buf_size_max = + SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX; + char stack_buf[1024]; + + void *(*pmalloc) (size_t) = malloc; + void *(*prealloc) (void *, size_t) = realloc; + void (*pfree) (void *) = free; + void (*pdie) (void) = NULL; + if (alloc) + { + pmalloc = alloc->malloc; + prealloc = alloc->realloc; + pfree = alloc->free; + pdie = alloc->die; + } + + if (! buffer_size) + { + /* Allocate the initial buffer on the stack. This way, in the + common case of a symlink of small size, we get away with a + single small malloc() instead of a big malloc() followed by a + shrinking realloc(). */ + buffer = stack_buf; + buffer_size = sizeof stack_buf; + } + + buf = buffer; + buf_size = buffer_size; + + do + { + /* Attempt to read the link into the current buffer. */ + ssize_t link_length = preadlinkat (fd, filename, buf, buf_size); + size_t link_size; + if (link_length < 0) + { + /* On AIX 5L v5.3 and HP-UX 11i v2 04/09, readlink returns -1 + with errno == ERANGE if the buffer is too small. */ + int readlinkat_errno = errno; + if (readlinkat_errno != ERANGE) + { + if (buf != buffer) + { + pfree (buf); + errno = readlinkat_errno; + } + return NULL; + } + } + + link_size = link_length; + + if (link_size < buf_size) + { + buf[link_size++] = '\0'; + + if (buf == stack_buf) + { + char *b = (char *) pmalloc (link_size); + if (! b) + break; + memcpy (b, buf, link_size); + buf = b; + } + else if (link_size < buf_size && buf != buffer && prealloc) + { + /* Shrink BUF before returning it. */ + char *b = (char *) prealloc (buf, link_size); + if (b) + buf = b; + } + + return buf; + } + + if (buf != buffer) + pfree (buf); + + if (buf_size <= buf_size_max / 2) + buf_size *= 2; + else if (buf_size < buf_size_max) + buf_size = buf_size_max; + else + break; + buf = (char *) pmalloc (buf_size); + } + while (buf); + + if (pdie) + pdie (); + errno = ENOMEM; + return NULL; +} diff --git a/lib/careadlinkat.h b/lib/careadlinkat.h new file mode 100644 index 00000000000..c5e4bcfc15f --- /dev/null +++ b/lib/careadlinkat.h @@ -0,0 +1,67 @@ +/* Read symbolic links into a buffer without size limitation, relative to fd. + + Copyright (C) 2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Paul Eggert, Bruno Haible, and Jim Meyering. */ + +#ifndef _GL_CAREADLINKAT_H + +#include +#include + +struct allocator; + +/* Assuming the current directory is FD, get the symbolic link value + of FILENAME as a null-terminated string and put it into a buffer. + If FD is AT_FDCWD, FILENAME is interpreted relative to the current + working directory, as in openat. + + If the link is small enough to fit into BUFFER put it there. + BUFFER's size is BUFFER_SIZE, and BUFFER can be null + if BUFFER_SIZE is zero. + + If the link is not small, put it into a dynamically allocated + buffer managed by ALLOC. It is the caller's responsibility to free + the returned value if it is nonnull and is not BUFFER. + + The PREADLINKAT function specifies how to read links. + + If successful, return the buffer address; otherwise return NULL and + set errno. */ + +char *careadlinkat (int fd, char const *filename, + char *buffer, size_t buffer_size, + struct allocator const *alloc, + ssize_t (*preadlinkat) (int, char const *, + char *, size_t)); + +/* Suitable values for careadlinkat's FD and PREADLINKAT arguments, + when doing a plain readlink. */ +#if HAVE_READLINKAT +# define careadlinkatcwd readlinkat +#else +/* Define AT_FDCWD independently, so that the careadlinkat module does + not depend on the fcntl-h module. The value does not matter, since + careadlinkatcwd ignores it, but we might as well use the same value + as fcntl-h. */ +# ifndef AT_FDCWD +# define AT_FDCWD (-3041965) +# endif +ssize_t careadlinkatcwd (int fd, char const *filename, + char *buffer, size_t buffer_size); +#endif + +#endif /* _GL_CAREADLINKAT_H */ diff --git a/lib/gnulib.mk b/lib/gnulib.mk index 030f95b7a68..bb5bdcf852e 100644 --- a/lib/gnulib.mk +++ b/lib/gnulib.mk @@ -9,7 +9,7 @@ # the same distribution terms as the rest of that program. # # Generated by gnulib-tool. -# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=. --makefile-name=gnulib.mk --no-libtool --macro-prefix=gl --no-vc-files crypto/md5 dtoastr filemode getloadavg getopt-gnu ignore-value intprops lstat mktime readlink socklen stdio strftime symlink sys_stat +# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=. --makefile-name=gnulib.mk --no-libtool --macro-prefix=gl --no-vc-files careadlinkat crypto/md5 dtoastr filemode getloadavg getopt-gnu ignore-value intprops lstat mktime readlink socklen stdio strftime symlink sys_stat MOSTLYCLEANFILES += core *.stackdump @@ -69,6 +69,14 @@ EXTRA_DIST += $(top_srcdir)/./c++defs.h ## end gnulib module c++defs +## begin gnulib module careadlinkat + +libgnu_a_SOURCES += careadlinkat.c + +EXTRA_DIST += allocator.h careadlinkat.h + +## end gnulib module careadlinkat + ## begin gnulib module crypto/md5 diff --git a/m4/gl-comp.m4 b/m4/gl-comp.m4 index af3cae75abb..43cce9b3676 100644 --- a/m4/gl-comp.m4 +++ b/m4/gl-comp.m4 @@ -28,6 +28,7 @@ AC_DEFUN([gl_EARLY], AC_REQUIRE([AC_PROG_RANLIB]) # Code from module arg-nonnull: # Code from module c++defs: + # Code from module careadlinkat: # Code from module crypto/md5: # Code from module dosname: # Code from module dtoastr: @@ -46,6 +47,7 @@ AC_DEFUN([gl_EARLY], # Code from module multiarch: # Code from module readlink: # Code from module socklen: + # Code from module ssize_t: # Code from module stat: # Code from module stdbool: # Code from module stddef: @@ -79,6 +81,8 @@ AC_DEFUN([gl_INIT], gl_source_base='lib' # Code from module arg-nonnull: # Code from module c++defs: + # Code from module careadlinkat: + AC_CHECK_FUNCS_ONCE([readlinkat]) # Code from module crypto/md5: gl_MD5 # Code from module dosname: @@ -115,6 +119,8 @@ AC_DEFUN([gl_INIT], gl_UNISTD_MODULE_INDICATOR([readlink]) # Code from module socklen: gl_TYPE_SOCKLEN_T + # Code from module ssize_t: + gt_TYPE_SSIZE_T # Code from module stat: gl_FUNC_STAT gl_SYS_STAT_MODULE_INDICATOR([stat]) @@ -287,6 +293,9 @@ AC_DEFUN([gl_FILE_LIST], [ build-aux/arg-nonnull.h build-aux/c++defs.h build-aux/warn-on-use.h + lib/allocator.h + lib/careadlinkat.c + lib/careadlinkat.h lib/dosname.h lib/dtoastr.c lib/filemode.c @@ -335,6 +344,7 @@ AC_DEFUN([gl_FILE_LIST], [ m4/multiarch.m4 m4/readlink.m4 m4/socklen.m4 + m4/ssize_t.m4 m4/st_dm_mode.m4 m4/stat.m4 m4/stdbool.m4 diff --git a/m4/ssize_t.m4 b/m4/ssize_t.m4 new file mode 100644 index 00000000000..d7127521ebe --- /dev/null +++ b/m4/ssize_t.m4 @@ -0,0 +1,23 @@ +# ssize_t.m4 serial 5 (gettext-0.18.2) +dnl Copyright (C) 2001-2003, 2006, 2010-2011 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. +dnl Test whether ssize_t is defined. + +AC_DEFUN([gt_TYPE_SSIZE_T], +[ + AC_CACHE_CHECK([for ssize_t], [gt_cv_ssize_t], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[int x = sizeof (ssize_t *) + sizeof (ssize_t); + return !x;]])], + [gt_cv_ssize_t=yes], [gt_cv_ssize_t=no])]) + if test $gt_cv_ssize_t = no; then + AC_DEFINE([ssize_t], [int], + [Define as a signed type of the same size as size_t.]) + fi +]) diff --git a/src/ChangeLog b/src/ChangeLog index c2e28251cb0..5649c8819d3 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,15 @@ +2011-04-01 Paul Eggert + + Replace two copies of readlink code with single gnulib version. + The gnulib version avoids calling malloc in the usual case, + and on 64-bit hosts doesn't have some arbitrary 32-bit limits. + * fileio.c (Ffile_symlink_p): Use emacs_readlink. + * filelock.c (current_lock_owner): Likewise. + * lisp.h (READLINK_BUFSIZE, emacs_readlink): New function. + * sysdep.c: Include allocator.h, careadlinkat.h. + (emacs_no_realloc_allocator): New static constant. + (emacs_readlink): New function. + 2011-03-31 Juanma Barranquero * xdisp.c (redisplay_internal): Fix prototype. diff --git a/src/fileio.c b/src/fileio.c index 85431dfd5b1..552044f7272 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -2579,9 +2579,8 @@ points to a nonexistent file. */) { Lisp_Object handler; char *buf; - int bufsize; - int valsize; Lisp_Object val; + char readlink_buf[READLINK_BUFSIZE]; CHECK_STRING (filename); filename = Fexpand_file_name (filename, Qnil); @@ -2594,36 +2593,15 @@ points to a nonexistent file. */) filename = ENCODE_FILE (filename); - bufsize = 50; - buf = NULL; - do - { - bufsize *= 2; - buf = (char *) xrealloc (buf, bufsize); - memset (buf, 0, bufsize); - - errno = 0; - valsize = readlink (SSDATA (filename), buf, bufsize); - if (valsize == -1) - { -#ifdef ERANGE - /* HP-UX reports ERANGE if buffer is too small. */ - if (errno == ERANGE) - valsize = bufsize; - else -#endif - { - xfree (buf); - return Qnil; - } - } - } - while (valsize >= bufsize); + buf = emacs_readlink (SSDATA (filename), readlink_buf); + if (! buf) + return Qnil; - val = make_string (buf, valsize); + val = build_string (buf); if (buf[0] == '/' && strchr (buf, ':')) val = concat2 (build_string ("/:"), val); - xfree (buf); + if (buf != readlink_buf) + xfree (buf); val = DECODE_FILE (val); return val; } diff --git a/src/filelock.c b/src/filelock.c index 2138eaa502b..13b27c72f19 100644 --- a/src/filelock.c +++ b/src/filelock.c @@ -396,36 +396,16 @@ within_one_second (time_t a, time_t b) static int current_lock_owner (lock_info_type *owner, char *lfname) { - int len, ret; + int ret; + size_t len; int local_owner = 0; char *at, *dot, *colon; - char *lfinfo = 0; - int bufsize = 50; - /* Read arbitrarily-long contents of symlink. Similar code in - file-symlink-p in fileio.c. */ - do - { - bufsize *= 2; - lfinfo = (char *) xrealloc (lfinfo, bufsize); - errno = 0; - len = readlink (lfname, lfinfo, bufsize); -#ifdef ERANGE - /* HP-UX reports ERANGE if the buffer is too small. */ - if (len == -1 && errno == ERANGE) - len = bufsize; -#endif - } - while (len >= bufsize); + char readlink_buf[READLINK_BUFSIZE]; + char *lfinfo = emacs_readlink (lfname, readlink_buf); /* If nonexistent lock file, all is well; otherwise, got strange error. */ - if (len == -1) - { - xfree (lfinfo); - return errno == ENOENT ? 0 : -1; - } - - /* Link info exists, so `len' is its length. Null terminate. */ - lfinfo[len] = 0; + if (!lfinfo) + return errno == ENOENT ? 0 : -1; /* Even if the caller doesn't want the owner info, we still have to read it to determine return value, so allocate it. */ @@ -441,7 +421,8 @@ current_lock_owner (lock_info_type *owner, char *lfname) dot = strrchr (lfinfo, '.'); if (!at || !dot) { - xfree (lfinfo); + if (lfinfo != readlink_buf) + xfree (lfinfo); return -1; } len = at - lfinfo; @@ -467,7 +448,8 @@ current_lock_owner (lock_info_type *owner, char *lfname) owner->host[len] = 0; /* We're done looking at the link info. */ - xfree (lfinfo); + if (lfinfo != readlink_buf) + xfree (lfinfo); /* On current host? */ if (STRINGP (Fsystem_name ()) diff --git a/src/lisp.h b/src/lisp.h index 85838d111db..63f346f6a25 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3340,6 +3340,8 @@ extern int emacs_open (const char *, int, int); extern int emacs_close (int); extern int emacs_read (int, char *, unsigned int); extern int emacs_write (int, const char *, unsigned int); +enum { READLINK_BUFSIZE = 1024 }; +extern char *emacs_readlink (const char *, char [READLINK_BUFSIZE]); #ifndef HAVE_MEMSET extern void *memset (void *, int, size_t); #endif diff --git a/src/sysdep.c b/src/sysdep.c index 1bb400421f0..a165a9ca52f 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -31,6 +31,8 @@ along with GNU Emacs. If not, see . */ #endif /* HAVE_LIMITS_H */ #include +#include +#include #include #include "lisp.h" @@ -1866,6 +1868,22 @@ emacs_write (int fildes, const char *buf, unsigned int nbyte) } return (bytes_written); } + +static struct allocator const emacs_norealloc_allocator = + { xmalloc, NULL, xfree, memory_full }; + +/* Get the symbolic link value of FILENAME. Return a pointer to a + NUL-terminated string. If readlink fails, return NULL and set + errno. If the value fits in INITIAL_BUF, return INITIAL_BUF. + Otherwise, allocate memory and return a pointer to that memory. If + memory allocation fails, diagnose and fail without returning. If + successful, store the length of the symbolic link into *LINKLEN. */ +char * +emacs_readlink (char const *filename, char initial_buf[READLINK_BUFSIZE]) +{ + return careadlinkat (AT_FDCWD, filename, initial_buf, READLINK_BUFSIZE, + &emacs_norealloc_allocator, careadlinkatcwd); +} #ifdef USG /* -- 2.39.5