| 1 | /* Copyright (c) 2000, 2012, Oracle and/or its affiliates |
| 2 | Copyright (c) 1985, 2011, Monty Program Ab |
| 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; version 2 of the License. |
| 7 | |
| 8 | This program is distributed in the hope that it will be useful, |
| 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | GNU General Public License for more details. |
| 12 | |
| 13 | You should have received a copy of the GNU General Public License |
| 14 | along with this program; if not, write to the Free Software |
| 15 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ |
| 16 | |
| 17 | #include "mysys_priv.h" |
| 18 | #include "my_static.h" |
| 19 | #include <errno.h> |
| 20 | #include "mysys_err.h" |
| 21 | |
| 22 | static void make_ftype(char * to,int flag); |
| 23 | |
| 24 | /* |
| 25 | Open a file as stream |
| 26 | |
| 27 | SYNOPSIS |
| 28 | my_fopen() |
| 29 | FileName Path-name of file |
| 30 | Flags Read | write | append | trunc (like for open()) |
| 31 | MyFlags Flags for handling errors |
| 32 | |
| 33 | RETURN |
| 34 | 0 Error |
| 35 | # File handler |
| 36 | */ |
| 37 | |
| 38 | FILE *my_fopen(const char *filename, int flags, myf MyFlags) |
| 39 | { |
| 40 | FILE *fd; |
| 41 | char type[10]; |
| 42 | DBUG_ENTER("my_fopen" ); |
| 43 | DBUG_PRINT("my" ,("Name: '%s' flags: %d MyFlags: %lu" , |
| 44 | filename, flags, MyFlags)); |
| 45 | |
| 46 | make_ftype(type,flags); |
| 47 | |
| 48 | #ifdef _WIN32 |
| 49 | fd= my_win_fopen(filename, type); |
| 50 | #else |
| 51 | fd= fopen(filename, type); |
| 52 | #endif |
| 53 | if (fd != 0) |
| 54 | { |
| 55 | /* |
| 56 | The test works if MY_NFILE < 128. The problem is that fileno() is char |
| 57 | on some OS (SUNOS). Actually the filename save isn't that important |
| 58 | so we can ignore if this doesn't work. |
| 59 | */ |
| 60 | |
| 61 | int filedesc= my_fileno(fd); |
| 62 | if ((uint)filedesc >= my_file_limit) |
| 63 | { |
| 64 | statistic_increment(my_stream_opened,&THR_LOCK_open); |
| 65 | DBUG_RETURN(fd); /* safeguard */ |
| 66 | } |
| 67 | my_file_info[filedesc].name= (char*) my_strdup(filename,MyFlags); |
| 68 | statistic_increment(my_stream_opened, &THR_LOCK_open); |
| 69 | statistic_increment(my_file_total_opened, &THR_LOCK_open); |
| 70 | my_file_info[filedesc].type= STREAM_BY_FOPEN; |
| 71 | DBUG_PRINT("exit" ,("stream: %p" , fd)); |
| 72 | DBUG_RETURN(fd); |
| 73 | } |
| 74 | else |
| 75 | my_errno=errno; |
| 76 | DBUG_PRINT("error" ,("Got error %d on open" ,my_errno)); |
| 77 | if (MyFlags & (MY_FFNF | MY_FAE | MY_WME)) |
| 78 | my_error((flags & O_RDONLY) ? EE_FILENOTFOUND : EE_CANTCREATEFILE, |
| 79 | MYF(ME_BELL+ME_WAITTANG), filename, my_errno); |
| 80 | DBUG_RETURN((FILE*) 0); |
| 81 | } /* my_fopen */ |
| 82 | |
| 83 | |
| 84 | #if defined(_WIN32) |
| 85 | |
| 86 | static FILE *my_win_freopen(const char *path, const char *mode, FILE *stream) |
| 87 | { |
| 88 | int handle_fd, fd= _fileno(stream); |
| 89 | HANDLE osfh; |
| 90 | |
| 91 | DBUG_ASSERT(path && stream); |
| 92 | DBUG_ASSERT(strchr(mode, 'a')); /* We use FILE_APPEND_DATA below */ |
| 93 | |
| 94 | /* Services don't have stdout/stderr on Windows, so _fileno returns -1. */ |
| 95 | if (fd < 0) |
| 96 | { |
| 97 | if (!freopen(path, mode, stream)) |
| 98 | return NULL; |
| 99 | |
| 100 | fd= _fileno(stream); |
| 101 | } |
| 102 | |
| 103 | if ((osfh= CreateFile(path, GENERIC_READ | FILE_APPEND_DATA, |
| 104 | FILE_SHARE_READ | FILE_SHARE_WRITE | |
| 105 | FILE_SHARE_DELETE, NULL, |
| 106 | OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, |
| 107 | NULL)) == INVALID_HANDLE_VALUE) |
| 108 | return NULL; |
| 109 | |
| 110 | if ((handle_fd= _open_osfhandle((intptr_t)osfh, _O_TEXT)) == -1) |
| 111 | { |
| 112 | CloseHandle(osfh); |
| 113 | return NULL; |
| 114 | } |
| 115 | |
| 116 | if (_dup2(handle_fd, fd) < 0) |
| 117 | { |
| 118 | CloseHandle(osfh); |
| 119 | return NULL; |
| 120 | } |
| 121 | |
| 122 | _close(handle_fd); |
| 123 | |
| 124 | return stream; |
| 125 | } |
| 126 | |
| 127 | #endif |
| 128 | |
| 129 | |
| 130 | /** |
| 131 | Change the file associated with a file stream. |
| 132 | |
| 133 | @param path Path to file. |
| 134 | @param mode Mode of the stream. |
| 135 | @param stream File stream. |
| 136 | |
| 137 | @note |
| 138 | This function is used to redirect stdout and stderr to a file and |
| 139 | subsequently to close and reopen that file for log rotation. |
| 140 | |
| 141 | @retval A FILE pointer on success. Otherwise, NULL. |
| 142 | */ |
| 143 | |
| 144 | FILE *my_freopen(const char *path, const char *mode, FILE *stream) |
| 145 | { |
| 146 | FILE *result; |
| 147 | |
| 148 | #if defined(_WIN32) |
| 149 | result= my_win_freopen(path, mode, stream); |
| 150 | #else |
| 151 | result= freopen(path, mode, stream); |
| 152 | #endif |
| 153 | |
| 154 | return result; |
| 155 | } |
| 156 | |
| 157 | |
| 158 | /* Close a stream */ |
| 159 | int my_fclose(FILE *fd, myf MyFlags) |
| 160 | { |
| 161 | int err,file; |
| 162 | char *name= NULL; |
| 163 | DBUG_ENTER("my_fclose" ); |
| 164 | DBUG_PRINT("my" ,("stream: %p MyFlags: %lu" , fd, MyFlags)); |
| 165 | |
| 166 | file= my_fileno(fd); |
| 167 | if ((uint) file < my_file_limit && my_file_info[file].type != UNOPEN) |
| 168 | { |
| 169 | name= my_file_info[file].name; |
| 170 | my_file_info[file].name= NULL; |
| 171 | my_file_info[file].type= UNOPEN; |
| 172 | } |
| 173 | #ifndef _WIN32 |
| 174 | do |
| 175 | { |
| 176 | err= fclose(fd); |
| 177 | } while (err == -1 && errno == EINTR); |
| 178 | #else |
| 179 | err= my_win_fclose(fd); |
| 180 | #endif |
| 181 | if(err < 0) |
| 182 | { |
| 183 | my_errno=errno; |
| 184 | if (MyFlags & (MY_FAE | MY_WME)) |
| 185 | my_error(EE_BADCLOSE, MYF(ME_BELL+ME_WAITTANG), |
| 186 | name,errno); |
| 187 | } |
| 188 | else |
| 189 | statistic_decrement(my_stream_opened, &THR_LOCK_open); |
| 190 | |
| 191 | if (name) |
| 192 | { |
| 193 | my_free(name); |
| 194 | } |
| 195 | DBUG_RETURN(err); |
| 196 | } /* my_fclose */ |
| 197 | |
| 198 | |
| 199 | /* Make a stream out of a file handle */ |
| 200 | /* Name may be 0 */ |
| 201 | |
| 202 | FILE *my_fdopen(File Filedes, const char *name, int Flags, myf MyFlags) |
| 203 | { |
| 204 | FILE *fd; |
| 205 | char type[5]; |
| 206 | DBUG_ENTER("my_fdopen" ); |
| 207 | DBUG_PRINT("my" ,("fd: %d Flags: %d MyFlags: %lu" , |
| 208 | Filedes, Flags, MyFlags)); |
| 209 | |
| 210 | make_ftype(type,Flags); |
| 211 | #ifdef _WIN32 |
| 212 | fd= my_win_fdopen(Filedes, type); |
| 213 | #else |
| 214 | fd= fdopen(Filedes, type); |
| 215 | #endif |
| 216 | if (!fd) |
| 217 | { |
| 218 | my_errno=errno; |
| 219 | if (MyFlags & (MY_FAE | MY_WME)) |
| 220 | my_error(EE_CANT_OPEN_STREAM, MYF(ME_BELL+ME_WAITTANG),errno); |
| 221 | } |
| 222 | else |
| 223 | { |
| 224 | statistic_increment(my_stream_opened, &THR_LOCK_open); |
| 225 | if ((uint) Filedes < (uint) my_file_limit) |
| 226 | { |
| 227 | if (my_file_info[Filedes].type != UNOPEN) |
| 228 | { |
| 229 | statistic_decrement(my_file_opened, &THR_LOCK_open); /* File is opened with my_open ! */ |
| 230 | } |
| 231 | else |
| 232 | { |
| 233 | my_file_info[Filedes].name= my_strdup(name,MyFlags); |
| 234 | } |
| 235 | my_file_info[Filedes].type = STREAM_BY_FDOPEN; |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | DBUG_PRINT("exit" ,("stream: %p" , fd)); |
| 240 | DBUG_RETURN(fd); |
| 241 | } /* my_fdopen */ |
| 242 | |
| 243 | |
| 244 | /* |
| 245 | Make a fopen() typestring from a open() type bitmap |
| 246 | |
| 247 | SYNOPSIS |
| 248 | make_ftype() |
| 249 | to String for fopen() is stored here |
| 250 | flag Flag used by open() |
| 251 | |
| 252 | IMPLEMENTATION |
| 253 | This routine attempts to find the best possible match |
| 254 | between a numeric option and a string option that could be |
| 255 | fed to fopen. There is not a 1 to 1 mapping between the two. |
| 256 | |
| 257 | NOTE |
| 258 | On Unix, O_RDONLY is usually 0 |
| 259 | |
| 260 | MAPPING |
| 261 | r == O_RDONLY |
| 262 | w == O_WRONLY|O_TRUNC|O_CREAT |
| 263 | a == O_WRONLY|O_APPEND|O_CREAT |
| 264 | r+ == O_RDWR |
| 265 | w+ == O_RDWR|O_TRUNC|O_CREAT |
| 266 | a+ == O_RDWR|O_APPEND|O_CREAT |
| 267 | b == FILE_BINARY |
| 268 | e == O_CLOEXEC |
| 269 | */ |
| 270 | |
| 271 | static void make_ftype(register char * to, register int flag) |
| 272 | { |
| 273 | /* check some possible invalid combinations */ |
| 274 | DBUG_ASSERT((flag & (O_TRUNC | O_APPEND)) != (O_TRUNC | O_APPEND)); |
| 275 | DBUG_ASSERT((flag & (O_WRONLY | O_RDWR)) != (O_WRONLY | O_RDWR)); |
| 276 | |
| 277 | if ((flag & (O_RDONLY|O_WRONLY)) == O_WRONLY) |
| 278 | *to++= (flag & O_APPEND) ? 'a' : 'w'; |
| 279 | else if (flag & O_RDWR) |
| 280 | { |
| 281 | /* Add '+' after theese */ |
| 282 | if (flag & (O_TRUNC | O_CREAT)) |
| 283 | *to++= 'w'; |
| 284 | else if (flag & O_APPEND) |
| 285 | *to++= 'a'; |
| 286 | else |
| 287 | *to++= 'r'; |
| 288 | *to++= '+'; |
| 289 | } |
| 290 | else |
| 291 | *to++= 'r'; |
| 292 | |
| 293 | if (flag & FILE_BINARY) |
| 294 | *to++='b'; |
| 295 | |
| 296 | if (O_CLOEXEC) |
| 297 | *to++= 'e'; |
| 298 | |
| 299 | *to='\0'; |
| 300 | } /* make_ftype */ |
| 301 | |