File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libiconv / srclib / careadlinkat.c
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Tue May 29 09:29:43 2012 UTC (12 years, 1 month ago) by misho
CVS tags: MAIN, HEAD
Initial revision

    1: /* Read symbolic links into a buffer without size limitation, relative to fd.
    2: 
    3:    Copyright (C) 2001, 2003-2004, 2007, 2009-2011 Free Software Foundation,
    4:    Inc.
    5: 
    6:    This program is free software: you can redistribute it and/or modify
    7:    it under the terms of the GNU General Public License as published by
    8:    the Free Software Foundation; either version 3 of the License, or
    9:    (at your option) any later version.
   10: 
   11:    This program is distributed in the hope that it will be useful,
   12:    but WITHOUT ANY WARRANTY; without even the implied warranty of
   13:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14:    GNU General Public License for more details.
   15: 
   16:    You should have received a copy of the GNU General Public License
   17:    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
   18: 
   19: /* Written by Paul Eggert, Bruno Haible, and Jim Meyering.  */
   20: 
   21: #include <config.h>
   22: 
   23: #include "careadlinkat.h"
   24: 
   25: #include <errno.h>
   26: #include <limits.h>
   27: #include <stdlib.h>
   28: #include <string.h>
   29: #include <unistd.h>
   30: 
   31: /* Define this independently so that stdint.h is not a prerequisite.  */
   32: #ifndef SIZE_MAX
   33: # define SIZE_MAX ((size_t) -1)
   34: #endif
   35: 
   36: #ifndef SSIZE_MAX
   37: # define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
   38: #endif
   39: 
   40: #include "allocator.h"
   41: 
   42: /* Get the symbolic link value of FILENAME and put it into BUFFER, with
   43:    size BUFFER_SIZE.  This function acts like readlink  but has
   44:    readlinkat's signature.  */
   45: ssize_t
   46: careadlinkatcwd (int fd, char const *filename, char *buffer,
   47:                  size_t buffer_size)
   48: {
   49:   /* FD must be AT_FDCWD here, otherwise the caller is using this
   50:      function in contexts for which it was not meant for.  */
   51:   if (fd != AT_FDCWD)
   52:     abort ();
   53:   return readlink (filename, buffer, buffer_size);
   54: }
   55: 
   56: /* Assuming the current directory is FD, get the symbolic link value
   57:    of FILENAME as a null-terminated string and put it into a buffer.
   58:    If FD is AT_FDCWD, FILENAME is interpreted relative to the current
   59:    working directory, as in openat.
   60: 
   61:    If the link is small enough to fit into BUFFER put it there.
   62:    BUFFER's size is BUFFER_SIZE, and BUFFER can be null
   63:    if BUFFER_SIZE is zero.
   64: 
   65:    If the link is not small, put it into a dynamically allocated
   66:    buffer managed by ALLOC.  It is the caller's responsibility to free
   67:    the returned value if it is nonnull and is not BUFFER.  A null
   68:    ALLOC stands for the standard allocator.
   69: 
   70:    The PREADLINKAT function specifies how to read links.  It operates
   71:    like POSIX readlinkat()
   72:    <http://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html>
   73:    but can assume that its first argument is the same as FD.
   74: 
   75:    If successful, return the buffer address; otherwise return NULL and
   76:    set errno.  */
   77: 
   78: char *
   79: careadlinkat (int fd, char const *filename,
   80:               char *buffer, size_t buffer_size,
   81:               struct allocator const *alloc,
   82:               ssize_t (*preadlinkat) (int, char const *, char *, size_t))
   83: {
   84:   char *buf;
   85:   size_t buf_size;
   86:   size_t buf_size_max =
   87:     SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX;
   88:   char stack_buf[1024];
   89: 
   90:   if (! alloc)
   91:     alloc = &stdlib_allocator;
   92: 
   93:   if (! buffer_size)
   94:     {
   95:       /* Allocate the initial buffer on the stack.  This way, in the
   96:          common case of a symlink of small size, we get away with a
   97:          single small malloc() instead of a big malloc() followed by a
   98:          shrinking realloc().  */
   99:       buffer = stack_buf;
  100:       buffer_size = sizeof stack_buf;
  101:     }
  102: 
  103:   buf = buffer;
  104:   buf_size = buffer_size;
  105: 
  106:   do
  107:     {
  108:       /* Attempt to read the link into the current buffer.  */
  109:       ssize_t link_length = preadlinkat (fd, filename, buf, buf_size);
  110:       size_t link_size;
  111:       if (link_length < 0)
  112:         {
  113:           /* On AIX 5L v5.3 and HP-UX 11i v2 04/09, readlink returns -1
  114:              with errno == ERANGE if the buffer is too small.  */
  115:           int readlinkat_errno = errno;
  116:           if (readlinkat_errno != ERANGE)
  117:             {
  118:               if (buf != buffer)
  119:                 {
  120:                   alloc->free (buf);
  121:                   errno = readlinkat_errno;
  122:                 }
  123:               return NULL;
  124:             }
  125:         }
  126: 
  127:       link_size = link_length;
  128: 
  129:       if (link_size < buf_size)
  130:         {
  131:           buf[link_size++] = '\0';
  132: 
  133:           if (buf == stack_buf)
  134:             {
  135:               char *b = (char *) alloc->allocate (link_size);
  136:               buf_size = link_size;
  137:               if (! b)
  138:                 break;
  139:               memcpy (b, buf, link_size);
  140:               buf = b;
  141:             }
  142:           else if (link_size < buf_size && buf != buffer && alloc->reallocate)
  143:             {
  144:               /* Shrink BUF before returning it.  */
  145:               char *b = (char *) alloc->reallocate (buf, link_size);
  146:               if (b)
  147:                 buf = b;
  148:             }
  149: 
  150:           return buf;
  151:         }
  152: 
  153:       if (buf != buffer)
  154:         alloc->free (buf);
  155: 
  156:       if (buf_size <= buf_size_max / 2)
  157:         buf_size *= 2;
  158:       else if (buf_size < buf_size_max)
  159:         buf_size = buf_size_max;
  160:       else if (buf_size_max < SIZE_MAX)
  161:         {
  162:           errno = ENAMETOOLONG;
  163:           return NULL;
  164:         }
  165:       else
  166:         break;
  167:       buf = (char *) alloc->allocate (buf_size);
  168:     }
  169:   while (buf);
  170: 
  171:   if (alloc->die)
  172:     alloc->die (buf_size);
  173:   errno = ENOMEM;
  174:   return NULL;
  175: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>