| 1 | /* |
| 2 | Copyright (c) 2003, 2011, Oracle and/or its affiliates |
| 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 St, Fifth Floor, Boston, MA 02110-1301 USA */ |
| 16 | |
| 17 | #include "mysys_priv.h" |
| 18 | #include "mysys_err.h" |
| 19 | #include <errno.h> |
| 20 | |
| 21 | |
| 22 | ulong my_sync_count; /* Count number of sync calls */ |
| 23 | |
| 24 | static void (*before_sync_wait)(void)= 0; |
| 25 | static void (*after_sync_wait)(void)= 0; |
| 26 | |
| 27 | void thr_set_sync_wait_callback(void (*before_wait)(void), |
| 28 | void (*after_wait)(void)) |
| 29 | { |
| 30 | before_sync_wait= before_wait; |
| 31 | after_sync_wait= after_wait; |
| 32 | } |
| 33 | |
| 34 | /* |
| 35 | Sync data in file to disk |
| 36 | |
| 37 | SYNOPSIS |
| 38 | my_sync() |
| 39 | fd File descritor to sync |
| 40 | my_flags Flags (now only MY_WME is supported) |
| 41 | |
| 42 | NOTE |
| 43 | If file system supports its, only file data is synced, not inode data. |
| 44 | |
| 45 | MY_IGNORE_BADFD is useful when fd is "volatile" - not protected by a |
| 46 | mutex. In this case by the time of fsync(), fd may be already closed by |
| 47 | another thread, or even reassigned to a different file. With this flag - |
| 48 | MY_IGNORE_BADFD - such a situation will not be considered an error. |
| 49 | (which is correct behaviour, if we know that the other thread synced the |
| 50 | file before closing) |
| 51 | |
| 52 | MY_SYNC_FILESIZE is useful when syncing a file after it has been extended. |
| 53 | On Linux, fdatasync() on ext3/ext4 file systems does not properly flush |
| 54 | to disk the inode data required to preserve the added data across a crash |
| 55 | (this looks to be a bug). But when a file is extended, inode data will most |
| 56 | likely need flushing in any case, so passing MY_SYNC_FILESIZE as flags |
| 57 | is not likely to be any slower, and will be crash safe on Linux ext3/ext4. |
| 58 | |
| 59 | RETURN |
| 60 | 0 ok |
| 61 | -1 error |
| 62 | */ |
| 63 | |
| 64 | int my_sync(File fd, myf my_flags) |
| 65 | { |
| 66 | int res; |
| 67 | DBUG_ENTER("my_sync" ); |
| 68 | DBUG_PRINT("my" ,("fd: %d my_flags: %lu" , fd, my_flags)); |
| 69 | |
| 70 | if (my_disable_sync) |
| 71 | DBUG_RETURN(0); |
| 72 | |
| 73 | statistic_increment(my_sync_count,&THR_LOCK_open); |
| 74 | |
| 75 | if (before_sync_wait) |
| 76 | (*before_sync_wait)(); |
| 77 | |
| 78 | do |
| 79 | { |
| 80 | #if defined(F_FULLFSYNC) |
| 81 | /* |
| 82 | In Mac OS X >= 10.3 this call is safer than fsync() (it forces the |
| 83 | disk's cache and guarantees ordered writes). |
| 84 | */ |
| 85 | if (!(res= fcntl(fd, F_FULLFSYNC, 0))) |
| 86 | break; /* ok */ |
| 87 | /* Some file systems don't support F_FULLFSYNC and fail above: */ |
| 88 | DBUG_PRINT("info" ,("fcntl(F_FULLFSYNC) failed, falling back" )); |
| 89 | #endif |
| 90 | #if defined(HAVE_FDATASYNC) && HAVE_DECL_FDATASYNC |
| 91 | if (!(my_flags & MY_SYNC_FILESIZE)) |
| 92 | res= fdatasync(fd); |
| 93 | else |
| 94 | { |
| 95 | #endif |
| 96 | #if defined(HAVE_FSYNC) |
| 97 | res= fsync(fd); |
| 98 | if (res == -1 && errno == ENOLCK) |
| 99 | res= 0; /* Result Bug in Old FreeBSD */ |
| 100 | #elif defined(_WIN32) |
| 101 | res= my_win_fsync(fd); |
| 102 | #else |
| 103 | #error Cannot find a way to sync a file, durability in danger |
| 104 | res= 0; /* No sync (strange OS) */ |
| 105 | #endif |
| 106 | #if defined(HAVE_FDATASYNC) && HAVE_DECL_FDATASYNC |
| 107 | } |
| 108 | #endif |
| 109 | } while (res == -1 && errno == EINTR); |
| 110 | |
| 111 | if (res) |
| 112 | { |
| 113 | int er= errno; |
| 114 | if (!(my_errno= er)) |
| 115 | my_errno= -1; /* Unknown error */ |
| 116 | if (after_sync_wait) |
| 117 | (*after_sync_wait)(); |
| 118 | if ((my_flags & MY_IGNORE_BADFD) && |
| 119 | (er == EBADF || er == EINVAL || er == EROFS)) |
| 120 | { |
| 121 | DBUG_PRINT("info" , ("ignoring errno %d" , er)); |
| 122 | res= 0; |
| 123 | } |
| 124 | else if (my_flags & MY_WME) |
| 125 | my_error(EE_SYNC, MYF(ME_BELL+ME_WAITTANG), my_filename(fd), my_errno); |
| 126 | } |
| 127 | else |
| 128 | { |
| 129 | if (after_sync_wait) |
| 130 | (*after_sync_wait)(); |
| 131 | } |
| 132 | DBUG_RETURN(res); |
| 133 | } /* my_sync */ |
| 134 | |
| 135 | |
| 136 | /* |
| 137 | Force directory information to disk. |
| 138 | |
| 139 | SYNOPSIS |
| 140 | my_sync_dir() |
| 141 | dir_name the name of the directory |
| 142 | my_flags flags (MY_WME etc) |
| 143 | |
| 144 | RETURN |
| 145 | 0 if ok, !=0 if error |
| 146 | */ |
| 147 | |
| 148 | int my_sync_dir(const char *dir_name __attribute__((unused)), |
| 149 | myf my_flags __attribute__((unused))) |
| 150 | { |
| 151 | #ifdef NEED_EXPLICIT_SYNC_DIR |
| 152 | static const char cur_dir_name[]= {FN_CURLIB, 0}; |
| 153 | File dir_fd; |
| 154 | int res= 0; |
| 155 | const char *correct_dir_name; |
| 156 | DBUG_ENTER("my_sync_dir" ); |
| 157 | DBUG_PRINT("my" ,("Dir: '%s' my_flags: %lu" , dir_name, my_flags)); |
| 158 | /* Sometimes the path does not contain an explicit directory */ |
| 159 | correct_dir_name= (dir_name[0] == 0) ? cur_dir_name : dir_name; |
| 160 | /* |
| 161 | Syncing a dir may give EINVAL on tmpfs on Linux, which is ok. |
| 162 | EIO on the other hand is very important. Hence MY_IGNORE_BADFD. |
| 163 | */ |
| 164 | if ((dir_fd= my_open(correct_dir_name, O_RDONLY, MYF(my_flags))) >= 0) |
| 165 | { |
| 166 | if (my_sync(dir_fd, MYF(my_flags | MY_IGNORE_BADFD))) |
| 167 | res= 2; |
| 168 | if (my_close(dir_fd, MYF(my_flags))) |
| 169 | res= 3; |
| 170 | } |
| 171 | else |
| 172 | res= 1; |
| 173 | DBUG_RETURN(res); |
| 174 | #else |
| 175 | return 0; |
| 176 | #endif |
| 177 | } |
| 178 | |
| 179 | /* |
| 180 | Force directory information to disk. |
| 181 | |
| 182 | SYNOPSIS |
| 183 | my_sync_dir_by_file() |
| 184 | file_name the name of a file in the directory |
| 185 | my_flags flags (MY_WME etc) |
| 186 | |
| 187 | RETURN |
| 188 | 0 if ok, !=0 if error |
| 189 | */ |
| 190 | |
| 191 | int my_sync_dir_by_file(const char *file_name __attribute__((unused)), |
| 192 | myf my_flags __attribute__((unused))) |
| 193 | { |
| 194 | #ifdef NEED_EXPLICIT_SYNC_DIR |
| 195 | char dir_name[FN_REFLEN]; |
| 196 | size_t dir_name_length; |
| 197 | dirname_part(dir_name, file_name, &dir_name_length); |
| 198 | return my_sync_dir(dir_name, my_flags & ~MY_NOSYMLINKS); |
| 199 | #else |
| 200 | return 0; |
| 201 | #endif |
| 202 | } |
| 203 | |