symlinkat: new module
[gnulib.git] / lib / symlinkat.c
1 /* Create a symlink relative to an open directory.
2    Copyright (C) 2009 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 <unistd.h>
22
23 #include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
24 #include "openat.h"
25 #include "openat-priv.h"
26 #include "save-cwd.h"
27
28 #if !HAVE_SYMLINK
29 /* Mingw lacks symlink, so this wrapper is trivial.  */
30
31 # include <errno.h>
32
33 int
34 symlinkat (char const *path1 _UNUSED_PARAMETER_, int fd _UNUSED_PARAMETER_,
35            char const *path2 _UNUSED_PARAMETER_)
36 {
37   errno = ENOSYS;
38   return -1;
39 }
40
41 #else /* HAVE_SYMLINK */
42
43 /* Our openat helper functions expect the directory parameter first,
44    not second.  These shims make life easier.  */
45
46 /* Like symlink, but with arguments reversed.  */
47 static int
48 symlink_reversed (char const *file, char const *contents)
49 {
50   return symlink (contents, file);
51 }
52
53 /* Like symlinkat, but with arguments reversed.  */
54
55 static int
56 symlinkat_reversed (int fd, char const *file, char const *contents);
57
58 # define AT_FUNC_NAME symlinkat_reversed
59 # define AT_FUNC_F1 symlink_reversed
60 # define AT_FUNC_POST_FILE_PARAM_DECLS , char const *contents
61 # define AT_FUNC_POST_FILE_ARGS        , contents
62 # include "at-func.c"
63 # undef AT_FUNC_NAME
64 # undef AT_FUNC_F1
65 # undef AT_FUNC_POST_FILE_PARAM_DECLS
66 # undef AT_FUNC_POST_FILE_ARGS
67
68 /* Create a symlink FILE, in the directory open on descriptor FD,
69    holding CONTENTS.  If possible, do it without changing the
70    working directory.  Otherwise, resort to using save_cwd/fchdir,
71    then mkdir/restore_cwd.  If either the save_cwd or the restore_cwd
72    fails, then give a diagnostic and exit nonzero.  */
73
74 int
75 symlinkat (char const *contents, int fd, char const *file)
76 {
77   return symlinkat_reversed (fd, file, contents);
78 }
79
80 #endif /* HAVE_SYMLINK */
81
82 /* Gnulib provides a readlink stub for mingw; use it for distinction
83    between EINVAL and ENOENT, rather than always failing with ENOSYS.  */
84
85 /* POSIX 2008 says that unlike readlink, readlinkat returns 0 for
86    success instead of the buffer length.  But this would render
87    readlinkat worthless since readlink does not guarantee a
88    NUL-terminated buffer.  Assume this was a bug in POSIX.  */
89
90 /* Read the contents of symlink FILE into buffer BUF of size LEN, in the
91    directory open on descriptor FD.  If possible, do it without changing
92    the working directory.  Otherwise, resort to using save_cwd/fchdir,
93    then mkdir/restore_cwd.  If either the save_cwd or the restore_cwd
94    fails, then give a diagnostic and exit nonzero.  */
95
96 #define AT_FUNC_NAME readlinkat
97 #define AT_FUNC_F1 readlink
98 #define AT_FUNC_POST_FILE_PARAM_DECLS , char *buf, size_t len
99 #define AT_FUNC_POST_FILE_ARGS        , buf, len
100 #define AT_FUNC_RESULT ssize_t
101 #include "at-func.c"
102 #undef AT_FUNC_NAME
103 #undef AT_FUNC_F1
104 #undef AT_FUNC_POST_FILE_PARAM_DECLS
105 #undef AT_FUNC_POST_FILE_ARGS
106 #undef AT_FUNC_RESULT