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
22ulong my_sync_count; /* Count number of sync calls */
23
24static void (*before_sync_wait)(void)= 0;
25static void (*after_sync_wait)(void)= 0;
26
27void 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
64int 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
148int 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
191int 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