1/* provide a replacement openat function
2   Copyright (C) 2004, 2005 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 2, or (at your option)
7   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, write to the Free Software Foundation,
16   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17#include <sys/cdefs.h>
18__RCSID("$NetBSD: openat.c,v 1.2 2016/05/17 14:00:09 christos Exp $");
19
20
21/* written by Jim Meyering */
22
23#ifdef HAVE_CONFIG_H
24# include <config.h>
25#endif
26
27#include "openat.h"
28
29#include <stdlib.h>
30#include <stdarg.h>
31#include <unistd.h>
32#include <errno.h>
33#include <fcntl.h>
34
35#include "error.h"
36#include "exitfail.h"
37#include "save-cwd.h"
38
39#include "gettext.h"
40#define _(msgid) gettext (msgid)
41
42/* Replacement for Solaris' openat function.
43   <http://www.google.com/search?q=openat+site:docs.sun.com>
44   Simulate it by doing save_cwd/fchdir/open/restore_cwd.
45   If either the save_cwd or the restore_cwd fails (relatively unlikely,
46   and usually indicative of a problem that deserves close attention),
47   then give a diagnostic and exit nonzero.
48   Otherwise, upon failure, set errno and return -1, as openat does.
49   Upon successful completion, return a file descriptor.  */
50int
51rpl_openat (int fd, char const *file, int flags, ...)
52{
53  struct saved_cwd saved_cwd;
54  int saved_errno;
55  int new_fd;
56  mode_t mode = 0;
57
58  if (flags & O_CREAT)
59    {
60      va_list arg;
61      va_start (arg, flags);
62
63      /* Assume that mode_t is passed compatibly with mode_t's type
64	 after argument promotion.  */
65      mode = va_arg (arg, mode_t);
66
67      va_end (arg);
68    }
69
70  if (fd == AT_FDCWD || *file == '/')
71    return open (file, flags, mode);
72
73  if (save_cwd (&saved_cwd) != 0)
74    error (exit_failure, errno,
75	   _("openat: unable to record current working directory"));
76
77  if (fchdir (fd) != 0)
78    {
79      saved_errno = errno;
80      free_cwd (&saved_cwd);
81      errno = saved_errno;
82      return -1;
83    }
84
85  new_fd = open (file, flags, mode);
86  saved_errno = errno;
87
88  if (restore_cwd (&saved_cwd) != 0)
89    error (exit_failure, errno,
90	   _("openat: unable to restore working directory"));
91
92  free_cwd (&saved_cwd);
93
94  errno = saved_errno;
95  return new_fd;
96}
97
98/* Replacement for Solaris' function by the same name.
99   <http://www.google.com/search?q=fdopendir+site:docs.sun.com>
100   Simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd.
101   If either the save_cwd or the restore_cwd fails (relatively unlikely,
102   and usually indicative of a problem that deserves close attention),
103   then give a diagnostic and exit nonzero.
104   Otherwise, this function works just like Solaris' fdopendir.  */
105DIR *
106fdopendir (int fd)
107{
108  struct saved_cwd saved_cwd;
109  int saved_errno;
110  DIR *dir;
111
112  if (fd == AT_FDCWD)
113    return opendir (".");
114
115  if (save_cwd (&saved_cwd) != 0)
116    error (exit_failure, errno,
117	   _("fdopendir: unable to record current working directory"));
118
119  if (fchdir (fd) != 0)
120    {
121      saved_errno = errno;
122      free_cwd (&saved_cwd);
123      errno = saved_errno;
124      return NULL;
125    }
126
127  dir = opendir (".");
128  saved_errno = errno;
129
130  if (restore_cwd (&saved_cwd) != 0)
131    error (exit_failure, errno,
132	   _("fdopendir: unable to restore working directory"));
133
134  free_cwd (&saved_cwd);
135
136  errno = saved_errno;
137  return dir;
138}
139
140/* Replacement for Solaris' function by the same name.
141   <http://www.google.com/search?q=fstatat+site:docs.sun.com>
142   Simulate it by doing save_cwd/fchdir/(stat|lstat)/restore_cwd.
143   If either the save_cwd or the restore_cwd fails (relatively unlikely,
144   and usually indicative of a problem that deserves close attention),
145   then give a diagnostic and exit nonzero.
146   Otherwise, this function works just like Solaris' fstatat.  */
147int
148fstatat (int fd, char const *file, struct stat *st, int flag)
149{
150  struct saved_cwd saved_cwd;
151  int saved_errno;
152  int err;
153
154  if (fd == AT_FDCWD)
155    return (flag == AT_SYMLINK_NOFOLLOW
156	    ? lstat (file, st)
157	    : stat (file, st));
158
159  if (save_cwd (&saved_cwd) != 0)
160    error (exit_failure, errno,
161	   _("fstatat: unable to record current working directory"));
162
163  if (fchdir (fd) != 0)
164    {
165      saved_errno = errno;
166      free_cwd (&saved_cwd);
167      errno = saved_errno;
168      return -1;
169    }
170
171  err = (flag == AT_SYMLINK_NOFOLLOW
172	 ? lstat (file, st)
173	 : stat (file, st));
174  saved_errno = errno;
175
176  if (restore_cwd (&saved_cwd) != 0)
177    error (exit_failure, errno,
178	   _("fstatat: unable to restore working directory"));
179
180  free_cwd (&saved_cwd);
181
182  errno = saved_errno;
183  return err;
184}
185