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 | |