1 /* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
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 /**
29   @file mysys/my_pread.cc
30 */
31 
32 #include "my_config.h"
33 
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <stddef.h>
37 #include <stdio.h>
38 #include <sys/types.h>
39 #include <limits>
40 
41 #ifdef HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44 
45 #include "my_base.h"
46 #include "my_dbug.h"
47 #include "my_inttypes.h"
48 #include "my_io.h"
49 #include "my_sys.h"
50 #include "my_thread_local.h"
51 #include "mysys_err.h"
52 #if defined(_WIN32)
53 #include "mysys/mysys_priv.h"
54 #endif
55 
56 #ifndef _WIN32
57 // Mock away pwrite() for unit testing.
58 ssize_t (*mock_pwrite)(int fd, const void *buf, size_t count,
59                        off_t offset) = nullptr;
60 #endif
61 
62 /**
63   Read a chunk of bytes from a file from a given position.
64   @note This differs from the normal pread() call in that we don't care
65     to set the position in the file back to the original position
66     if the system doesn't support pread().
67 
68     @param Filedes	File decsriptor
69     @param Buffer	Buffer to read data into
70     @param Count	Number of bytes to read
71     @param offset	Position to read from
72     @param MyFlags	Flags
73 
74     @retval MY_FILE_ERROR in case of error
75     @retval Number of bytes read, otherwise
76 */
77 
my_pread(File Filedes,uchar * Buffer,size_t Count,my_off_t offset,myf MyFlags)78 size_t my_pread(File Filedes, uchar *Buffer, size_t Count, my_off_t offset,
79                 myf MyFlags) {
80   DBUG_TRACE;
81   for (;;) {
82     errno = 0; /* Linux, Windows don't reset this on EOF/success */
83 
84     const int64_t readbytes =
85 #if defined(_WIN32)
86         my_win_pread(Filedes, Buffer, Count, offset);
87 #else
88         pread(Filedes, Buffer, Count, offset);
89 #endif
90     const bool error = (readbytes != static_cast<int64_t>(Count));
91     if (error) {
92       set_my_errno(errno ? errno : -1);
93       if (errno == 0 || (readbytes != -1 && (MyFlags & (MY_NABP | MY_FNABP))))
94         set_my_errno(HA_ERR_FILE_TOO_SHORT);
95 
96       if ((readbytes == 0 || readbytes == -1) && errno == EINTR) {
97         continue; /* Interrupted */
98       }
99 
100       if (MyFlags & (MY_WME | MY_FAE | MY_FNABP)) {
101         if (readbytes == -1)
102           MyOsError(my_errno(), EE_READ, MYF(0), my_filename(Filedes));
103         else if (MyFlags & (MY_NABP | MY_FNABP))
104           MyOsError(my_errno(), EE_EOFERR, MYF(0), my_filename(Filedes));
105       }
106       if (readbytes == -1 || (MyFlags & (MY_FNABP | MY_NABP)))
107         return MY_FILE_ERROR;
108     }                                             // if (error)
109     if (MyFlags & (MY_NABP | MY_FNABP)) return 0; /* Read went ok; Return 0 */
110     DBUG_ASSERT(readbytes >= 0);
111     return readbytes; /* purecov: inspected */
112   }                   // for (;;)
113 }
114 
115 /**
116   Write a chunk of bytes to a file at a given position
117   @note
118     This differs from the normal pwrite() call in that we don't care
119     to set the position in the file back to the original position
120     if the system doesn't support pwrite()
121 
122     @param Filedes	File descriptor
123     @param Buffer	Buffer to write data from
124     @param Count	Number of bytes to write
125     @param offset	Position to write to
126     @param MyFlags	Flags
127 
128   @return if (MyFlags & (MY_NABP | MY_FNABP))
129    @retval 0  if Count == 0
130    @retval On success, 0
131    @retval On failure, (size_t)-1 == MY_FILE_ERROR
132 
133   otherwise
134     @retval 0  if Count == 0
135     @retval On success, the number of bytes written.
136     @retval On partial success (if less than Count bytes could be written),
137        the actual number of bytes written.
138     @retval On failure, (size_t)-1 == MY_FILE_ERROR
139 */
140 
my_pwrite(File Filedes,const uchar * Buffer,size_t Count,my_off_t offset,myf MyFlags)141 size_t my_pwrite(File Filedes, const uchar *Buffer, size_t Count,
142                  my_off_t offset, myf MyFlags) {
143   int64_t sum_written = 0;
144   uint errors = 0;
145   const size_t initial_count = Count;
146 
147   DBUG_TRACE;
148 
149   for (;;) {
150     errno = 0;
151     int64_t writtenbytes;
152 #if defined(_WIN32)
153     writtenbytes = my_win_pwrite(Filedes, Buffer, Count, offset);
154 #else
155     if (mock_pwrite)
156       writtenbytes = mock_pwrite(Filedes, Buffer, Count, offset);
157     else
158       writtenbytes = pwrite(Filedes, Buffer, Count, offset);
159 #endif
160     if (writtenbytes == static_cast<int64_t>(Count)) {
161       sum_written += writtenbytes;
162       break;
163     }
164     set_my_errno(errno);
165     if (writtenbytes != -1) {
166       sum_written += writtenbytes;
167       Buffer += writtenbytes;
168       Count -= writtenbytes;
169       offset += writtenbytes;
170     }
171 
172     if (is_killed_hook(nullptr))
173       MyFlags &= ~MY_WAIT_IF_FULL; /* End if aborted by user */
174 
175     if ((my_errno() == ENOSPC || my_errno() == EDQUOT) &&
176         (MyFlags & MY_WAIT_IF_FULL)) {
177       wait_for_free_space(my_filename(Filedes), errors);
178       errors++;
179       continue;
180     }
181     if (writtenbytes != 0 && writtenbytes != -1) continue;
182     if (my_errno() == EINTR) {
183       continue; /* Retry */
184     }
185     if (writtenbytes == 0 && !errors++) /* Retry once */
186     {
187       /* We may come here if the file quota is exeeded */
188       continue;
189     }
190     break; /* Return bytes written */
191   }
192   if (MyFlags & (MY_NABP | MY_FNABP)) {
193     if (sum_written == static_cast<int64_t>(initial_count))
194       return 0; /* Want only errors, not bytes written */
195     if (MyFlags & (MY_WME | MY_FAE | MY_FNABP)) {
196       MyOsError(my_errno(), EE_WRITE, MYF(0), my_filename(Filedes));
197     }
198     return MY_FILE_ERROR;
199   }
200   DBUG_EXECUTE_IF("check", my_seek(Filedes, -1, SEEK_SET, MYF(0)););
201 
202   if (sum_written == 0) return MY_FILE_ERROR;
203 
204   return sum_written;
205 }
206