1 /* Copyright (c) 2003, 2021, Oracle and/or its affiliates.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    Without limiting anything contained in the foregoing, this file,
15    which is part of C Driver for MySQL (Connector/C), is also subject to the
16    Universal FOSS Exception, version 1.0, a copy of which can be found at
17    http://oss.oracle.com/licenses/universal-foss-exception.
18 
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22    GNU General Public License, version 2.0, for more details.
23 
24    You should have received a copy of the GNU General Public License
25    along with this program; if not, write to the Free Software
26    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
27 
28 #include "mysys_priv.h"
29 #include "my_sys.h"
30 #include "mysys_err.h"
31 #include <errno.h>
32 #include "my_thread_local.h"
33 
34 static void (*before_sync_wait)(void)= 0;
35 static void (*after_sync_wait)(void)= 0;
36 
thr_set_sync_wait_callback(void (* before_wait)(void),void (* after_wait)(void))37 void thr_set_sync_wait_callback(void (*before_wait)(void),
38                                 void (*after_wait)(void))
39 {
40   before_sync_wait= before_wait;
41   after_sync_wait= after_wait;
42 }
43 
44 /*
45   Sync data in file to disk
46 
47   SYNOPSIS
48     my_sync()
49     fd			File descritor to sync
50     my_flags		Flags (now only MY_WME is supported)
51 
52   NOTE
53     If file system supports its, only file data is synced, not inode data.
54 
55     MY_IGNORE_BADFD is useful when fd is "volatile" - not protected by a
56     mutex. In this case by the time of fsync(), fd may be already closed by
57     another thread, or even reassigned to a different file. With this flag -
58     MY_IGNORE_BADFD - such a situation will not be considered an error.
59     (which is correct behaviour, if we know that the other thread synced the
60     file before closing)
61 
62   RETURN
63     0 ok
64     -1 error
65 */
66 
my_sync(File fd,myf my_flags)67 int my_sync(File fd, myf my_flags)
68 {
69   int res;
70   DBUG_ENTER("my_sync");
71   DBUG_PRINT("my",("Fd: %d  my_flags: %d", fd, my_flags));
72 
73   if (before_sync_wait)
74     (*before_sync_wait)();
75   do
76   {
77 #if defined(F_FULLFSYNC)
78     /*
79       In Mac OS X >= 10.3 this call is safer than fsync() (it forces the
80       disk's cache and guarantees ordered writes).
81     */
82     if (!(res= fcntl(fd, F_FULLFSYNC, 0)))
83       break; /* ok */
84     /* Some file systems don't support F_FULLFSYNC and fail above: */
85     DBUG_PRINT("info",("fcntl(F_FULLFSYNC) failed, falling back"));
86 #endif
87 #if defined(HAVE_FDATASYNC) && HAVE_DECL_FDATASYNC
88     res= fdatasync(fd);
89 #elif defined(HAVE_FSYNC)
90     res= fsync(fd);
91 #elif defined(_WIN32)
92     res= my_win_fsync(fd);
93 #else
94 #error Cannot find a way to sync a file, durability in danger
95     res= 0;					/* No sync (strange OS) */
96 #endif
97   } while (res == -1 && errno == EINTR);
98 
99   if (res)
100   {
101     int er= errno;
102     set_my_errno(er);
103     if (!er)
104       set_my_errno(-1);                             /* Unknown error */
105     if (after_sync_wait)
106       (*after_sync_wait)();
107     if ((my_flags & MY_IGNORE_BADFD) &&
108         (er == EBADF || er == EINVAL || er == EROFS
109 #ifdef __APPLE__
110         || er == ENOTSUP
111 #endif
112         ))
113     {
114       DBUG_PRINT("info", ("ignoring errno %d", er));
115       res= 0;
116     }
117     else if (my_flags & MY_WME)
118     {
119       char errbuf[MYSYS_STRERROR_SIZE];
120       my_error(EE_SYNC, MYF(0), my_filename(fd),
121                my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
122     }
123   }
124   else
125   {
126     if (after_sync_wait)
127       (*after_sync_wait)();
128   }
129   DBUG_RETURN(res);
130 } /* my_sync */
131 
132 
133 /*
134   Force directory information to disk.
135 
136   SYNOPSIS
137     my_sync_dir()
138     dir_name             the name of the directory
139     my_flags             flags (MY_WME etc)
140 
141   RETURN
142     0 if ok, !=0 if error
143 */
144 
145 #ifdef __linux__
146 static const char cur_dir_name[]= {FN_CURLIB, 0};
147 #endif
148 
my_sync_dir(const char * dir_name MY_ATTRIBUTE ((unused)),myf my_flags MY_ATTRIBUTE ((unused)))149 int my_sync_dir(const char *dir_name MY_ATTRIBUTE((unused)),
150                 myf my_flags MY_ATTRIBUTE((unused)))
151 {
152 /*
153   Only Linux is known to need an explicit sync of the directory to make sure a
154   file creation/deletion/renaming in(from,to) this directory durable.
155 */
156 #ifdef __linux__
157   File dir_fd;
158   int res= 0;
159   const char *correct_dir_name;
160   DBUG_ENTER("my_sync_dir");
161   DBUG_PRINT("my",("Dir: '%s'  my_flags: %d", dir_name, my_flags));
162   /* Sometimes the path does not contain an explicit directory */
163   correct_dir_name= (dir_name[0] == 0) ? cur_dir_name : dir_name;
164   /*
165     Syncing a dir may give EINVAL on tmpfs on Linux, which is ok.
166     EIO on the other hand is very important. Hence MY_IGNORE_BADFD.
167   */
168   if ((dir_fd= my_open(correct_dir_name, O_RDONLY, MYF(my_flags))) >= 0)
169   {
170     if (my_sync(dir_fd, MYF(my_flags | MY_IGNORE_BADFD)))
171       res= 2;
172     if (my_close(dir_fd, MYF(my_flags)))
173       res= 3;
174   }
175   else
176     res= 1;
177   DBUG_RETURN(res);
178 #else
179   return 0;
180 #endif
181 }
182 
183 
184 /*
185   Force directory information to disk.
186 
187   SYNOPSIS
188     my_sync_dir_by_file()
189     file_name            the name of a file in the directory
190     my_flags             flags (MY_WME etc)
191 
192   RETURN
193     0 if ok, !=0 if error
194 */
195 
my_sync_dir_by_file(const char * file_name MY_ATTRIBUTE ((unused)),myf my_flags MY_ATTRIBUTE ((unused)))196 int my_sync_dir_by_file(const char *file_name MY_ATTRIBUTE((unused)),
197                         myf my_flags MY_ATTRIBUTE((unused)))
198 {
199 #ifdef __linux__
200   char dir_name[FN_REFLEN];
201   size_t dir_name_length;
202   dirname_part(dir_name, file_name, &dir_name_length);
203   return my_sync_dir(dir_name, my_flags);
204 #else
205   return 0;
206 #endif
207 }
208