re-mark 1.29b-2 as not yet uploaded (merge madness!)
[debian/tar] / gnu / readlinkat.c
1 /* Read a symlink relative to an open directory.
2    Copyright (C) 2009-2015 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 /* written by Eric Blake */
18
19 #include <config.h>
20
21 #include <errno.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <sys/stat.h>
25
26 #if HAVE_READLINKAT
27
28 # undef readlinkat
29
30 ssize_t
31 rpl_readlinkat (int fd, char const *file, char *buf, size_t len)
32 {
33 # if READLINK_TRAILING_SLASH_BUG
34   size_t file_len = strlen (file);
35   if (file_len && file[file_len - 1] == '/')
36     {
37       /* Even if FILE without the slash is a symlink to a directory,
38          both lstat() and stat() must resolve the trailing slash to
39          the directory rather than the symlink.  We can therefore
40          safely use stat() to distinguish between EINVAL and
41          ENOTDIR/ENOENT, avoiding extra overhead of rpl_lstat().  */
42       struct stat st;
43       if (stat (file, &st) == 0)
44         errno = EINVAL;
45       return -1;
46     }
47 # endif /* READLINK_TRAILING_SLASH_BUG */
48   return readlinkat (fd, file, buf, len);
49 }
50
51 #else
52
53 /* Gnulib provides a readlink stub for mingw; use it for distinction
54    between EINVAL and ENOENT, rather than always failing with ENOSYS.  */
55
56 /* POSIX 2008 says that unlike readlink, readlinkat returns 0 for
57    success instead of the buffer length.  But this would render
58    readlinkat worthless since readlink does not guarantee a
59    NUL-terminated buffer.  Assume this was a bug in POSIX.  */
60
61 /* Read the contents of symlink FILE into buffer BUF of size LEN, in the
62    directory open on descriptor FD.  If possible, do it without changing
63    the working directory.  Otherwise, resort to using save_cwd/fchdir,
64    then readlink/restore_cwd.  If either the save_cwd or the restore_cwd
65    fails, then give a diagnostic and exit nonzero.  */
66
67 # define AT_FUNC_NAME readlinkat
68 # define AT_FUNC_F1 readlink
69 # define AT_FUNC_POST_FILE_PARAM_DECLS , char *buf, size_t len
70 # define AT_FUNC_POST_FILE_ARGS        , buf, len
71 # define AT_FUNC_RESULT ssize_t
72 # include "at-func.c"
73 # undef AT_FUNC_NAME
74 # undef AT_FUNC_F1
75 # undef AT_FUNC_POST_FILE_PARAM_DECLS
76 # undef AT_FUNC_POST_FILE_ARGS
77 # undef AT_FUNC_RESULT
78
79 #endif