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