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