/** Default Zip compression level */ extern uint page_zip_level;
#ifdef UNIV_PFS_IO /* Keys to register InnoDB I/O with performance schema */
mysql_pfs_key_t innodb_data_file_key;
mysql_pfs_key_t innodb_temp_file_key; #endif
/** Handle errors for file operations. @param[in]namenameofafileorNULL @param[in]operationoperation @param[in]should_abortwhethertoabortonanunknownerror @param[in]on_error_silentwhethertosuppressreportsofnon-fatalerrors
@return true if we should retry the operation */ static bool
os_file_handle_error_cond_exit( constchar* name, constchar* operation, bool should_abort, bool on_error_silent) noexcept;
/** Does error handling when a file operation fails.
@param operation name of operation that failed */ staticvoid os_file_handle_error(constchar *operation)
{
os_file_handle_error_cond_exit(nullptr, operation, true, false);
}
/** Does error handling when a file operation fails. @param[in]namenameofafileorNULL @param[in]operationoperationnamethatfailed @param[in]on_error_silentiftruethendon'tprintanymessagetothelog.
@return true if we should retry the operation */ static bool
os_file_handle_error_no_exit( constchar* name, constchar* operation, bool on_error_silent)
{ /* Don't exit in case of unknown error */ return(os_file_handle_error_cond_exit(
name, operation, false, on_error_silent));
}
/** Handle RENAME error. @paramnameoldnameofthefile
@param new_name new name of the file */ staticvoid os_file_handle_rename_error(constchar* name, constchar* new_name)
{ if (os_file_get_last_error(true) != OS_FILE_DISK_FULL) {
ib::error() << "Cannot rename file '" << name << "' to '"
<< new_name << "'";
} elseif (!os_has_said_disk_full) {
os_has_said_disk_full = true; /* Disk full error is reported irrespective of the
on_error_silent setting. */
ib::error() << "Full disk prevents renaming file '"
<< name << "' to '" << new_name << "'";
}
}
if (result || (GetLastError() == ERROR_IO_PENDING)) { /* Wait for async io to complete */
result = GetOverlappedResult(handle, &overlapped, bytes_returned, TRUE);
}
return result;
}
#endif
/** Helper class for doing synchronous file IO. Currently, the objective istohidetheOSspecificcode,sothatthehigherlevelfunctionsaren't
peppered with #ifdef. Makes the code flow difficult to follow. */ class SyncFileIO
{ public: /** Constructor @param[in]fhFilehandle @param[in,out]bufBuffertoread/write @param[in]nNumberofbytestoread/write
@param[in] offset Offset where to read or write */
SyncFileIO(os_file_t fh, void *buf, ulint n, os_offset_t offset) :
m_fh(fh), m_buf(buf), m_n(static_cast<ssize_t>(n)), m_offset(offset)
{ ut_ad(m_n > 0); }
/** Do the read/write @param[in]requestTheIOcontextandtype
@return the number of bytes read/written or negative value on error */
ssize_t execute(const IORequest &request);
/** Move the read/write offset up to where the partial IO succeeded.
@param[in] n_bytes The number of bytes to advance */ void advance(ssize_t n_bytes)
{
m_offset+= n_bytes;
ut_ad(m_n >= n_bytes);
m_n-= n_bytes;
m_buf= reinterpret_cast<uchar*>(m_buf) + n_bytes;
}
private: /** Open file handle */ const os_file_t m_fh; /** Buffer to read/write */ void *m_buf; /** Number of bytes to read/write */
ssize_t m_n; /** Offset from where to read/write */
os_offset_t m_offset;
/** Do the read/write @paramrequestTheIOcontextandtype @paramnNumberofbytestoread/write
@return the number of bytes read/written or negative value on error */
ssize_t execute_low(const IORequest& request, ssize_t n);
};
#ifndef _WIN32 /* On Microsoft Windows, mandatory locking is used */ /** Obtain an exclusive lock on a file. @paramfdfiledescriptor @paramnamefilename
@return 0 on success */ int os_file_lock(int fd, constchar *name) noexcept
{ struct flock lk;
/** Rewind file to its start, read at most size - 1 bytes from it to str, and NUL-terminatestr.Allerrorsaresilentlyignored.Thisfunctionis mostlymeanttobeusedwithtemporaryfiles. @param[in,out]fileFiletoreadfrom @param[in,out]strBufferwheretoread
@param[in] size Size of buffer */ void
os_file_read_string(
FILE* file, char* str,
ulint size) noexcept
{ if (size != 0) {
rewind(file);
size_t flen = fread(str, 1, size - 1, file);
str[flen] = '\0';
}
}
void os_file_make_data_dir_path(char *data_dir_path) noexcept
{ /* Replace the period before the extension with a null byte. */ char* ptr = strrchr(data_dir_path, '.');
if (ptr == NULL) { return;
}
ptr[0] = '\0';
/* The tablename starts after the last slash. */
ptr = strrchr(data_dir_path, '/');
if (ptr == NULL) { return;
}
ptr[0] = '\0';
char* tablename = ptr + 1;
/* The databasename starts after the next to last slash. */
ptr = strrchr(data_dir_path, '/'); #ifdef _WIN32 if (char *aptr = strrchr(data_dir_path, '\\')) { if (aptr > ptr) {
ptr = aptr;
}
} #endif
if (ptr == NULL) { return;
}
ulint tablename_len = strlen(tablename);
memmove(++ptr, tablename, tablename_len);
ptr[tablename_len] = '\0';
}
/** Check if the path refers to the root of a drive using a pointer tothelastdirectoryseparatorthatthecallerhasfixed. @param[in]pathpathname @param[in]pathlastdirectoryseparatorinthepath
@return true if this path is a drive root, false if not */ staticbool os_file_is_root(constchar *path, constchar *last_slash) noexcept
{ return( #ifdef _WIN32
(last_slash == path + 2 && path[1] == ':') || #endif/* _WIN32 */
last_slash == path);
}
/** Return the parent directory component of a null-terminated path. Returnanewbuffercontainingthestringupto,butnotincluding, thefinalcomponentofthepath. Thepathreturnedwillnotcontainatrailingseparator. Donotreturnarootpath,returnNULLinstead. Thefinalcomponenttrimmedoffmaybeafilenameoradirectoryname. Ifthefinalcomponentistheonlycomponentofthepath,returnNULL. Itisthecaller'sresponsibilitytofreethereturnedstringafterit isnolongerneeded. @param[in]pathPathname
@return own: parent directory of the path */ static char*
os_file_get_parent_dir( constchar* path)
{ /* Find the offset of the last slash */ constchar* last_slash = strrchr(path, '/');
#ifdef _WIN32 if (constchar *last = strrchr(path, '\\')) { if (last > last_slash) {
last_slash = last;
}
} #endif
if (!last_slash) { /* No slash in the path, return NULL */ return(NULL);
}
/* Ok, there is a slash. Is there anything after it? */ constbool has_trailing_slash = last_slash[1] == '\0';
/* Check for the root of a drive. */ if (os_file_is_root(path, last_slash)) { return(NULL);
}
/* If a trailing slash prevented the first strrchr() from trimming
the last component of the path, trim that component now. */ if (has_trailing_slash) { /* Back up to the previous slash. */
last_slash--; while (last_slash > path
&& (IF_WIN(last_slash[0] != '\\' &&,)
last_slash[0] != '/')) {
last_slash--;
}
/* Check for the root of a drive. */ if (os_file_is_root(path, last_slash)) { return(NULL);
}
if (last_slash - path < 0) { /* Sanity check, it prevents gcc from trying to handle this case which
* results in warnings for some optimized builds */ return (NULL);
}
/** Do the read/write @param[in]requestTheIOcontextandtype @param[in]nNumberofbytestoread/write
@return the number of bytes read/written or negative value on error */
ssize_t
SyncFileIO::execute_low(const IORequest& request, ssize_t n)
{
ut_ad(n > 0);
ut_ad(size_t(n) <= os_file_request_size_max);
if (request.is_read()) return IF_WIN(tpool::pread(m_fh, m_buf, n, m_offset), pread(m_fh, m_buf, n, m_offset)); return IF_WIN(tpool::pwrite(m_fh, m_buf, n, m_offset), pwrite(m_fh, m_buf, n, m_offset));
}
/** Do the read/write @param[in]requestTheIOcontextandtype
@return the number of bytes read/written or negative value on error */
ssize_t
SyncFileIO::execute(const IORequest& request)
{
ssize_t n_bytes= 0;
ut_ad(m_n > 0);
while (size_t(m_n) > os_file_request_size_max)
{
ssize_t n_partial_bytes= execute_low(request, os_file_request_size_max); if (n_partial_bytes < 0) return n_partial_bytes;
n_bytes+= n_partial_bytes; if (n_partial_bytes != os_file_request_size_max) return n_bytes;
advance(os_file_request_size_max);
}
if (ssize_t n= execute_low(request, m_n))
{ if (n < 0) return n;
n_bytes += n;
} return n_bytes;
}
#ifndef _WIN32 /** Free storage space associated with a section of the file. @param[in]fhOpenfilehandle @param[in]offStartingoffset(SEEK_SET) @param[in]lenSizeofthehole
@return DB_SUCCESS or error code */ static
dberr_t
os_file_punch_hole_posix(
os_file_t fh,
os_offset_t off,
os_offset_t len)
{
if (errno == ENOTSUP) { return(DB_IO_NO_PUNCH_HOLE);
}
ib::warn()
<< "fallocate("
<<", FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, "
<< off << ", " << len << ") returned errno: "
<< errno;
return(DB_IO_ERROR);
#elifdefined __sun__
// Use F_FREESP
#endif/* HAVE_FALLOC_PUNCH_HOLE_AND_KEEP_SIZE */
return(DB_IO_NO_PUNCH_HOLE);
}
/** Retrieves the last error number if an error occurs in a file io function. ThenumbershouldberetrievedbeforeanyotherOScalls(becausetheymay overwritetheerrornumber).Ifthenumberisnotknowntothisprogram, theOSerrornumber+100isreturned. @param[in]report_all_errorstrueifwewantanerrormessage printedofallerrors @param[in]on_error_silenttruethendon'tprintanydiagnostic tothelog
@return error number, or OS error number + 100 */
ulint os_file_get_last_error(bool report_all_errors, bool on_error_silent)
noexcept
{ int err = errno;
ib::error()
<< "Operating system error number "
<< err
<< " in a file operation.";
if (err == EACCES) {
ib::error()
<< "The error means mariadbd does not have" " the access rights to the directory.";
} else { if (strerror(err) != NULL) {
ib::error()
<< "Error number " << err << " means '"
<< strerror(err) << "'";
}
ib::info() << OPERATING_SYSTEM_ERROR_MSG;
}
}
switch (err) { case ENOSPC: return(OS_FILE_DISK_FULL); case ENOENT: return(OS_FILE_NOT_FOUND); case EEXIST: return(OS_FILE_ALREADY_EXISTS); case EXDEV: case ENOTDIR: case EISDIR: case EPERM: return(OS_FILE_PATH_ERROR); case EAGAIN: if (srv_use_native_aio) { return(OS_FILE_AIO_RESOURCES_RESERVED);
} break; case EINTR: if (srv_use_native_aio) { return(OS_FILE_AIO_INTERRUPTED);
} break; case EACCES: return(OS_FILE_ACCESS_VIOLATION);
} return(OS_FILE_ERROR_MAX + err);
}
/** Wrapper to fsync() or fdatasync() that retries the call on some errors. Returnsthevalue0ifsuccessful;otherwisethevalue-1isreturnedand theglobalvariableerrnoissettoindicatetheerror. @param[in]fileopenfilehandle
@return 0 if success, -1 otherwise */ staticint os_file_sync_posix(os_file_t file) noexcept
{ #if !defined(HAVE_FDATASYNC) || HAVE_DECL_FDATASYNC == 0 auto func= fsync; auto func_name= "fsync()"; #else auto func= fdatasync; auto func_name= "fdatasync()"; #endif
ulint failures= 0;
for (;;)
{
++os_n_fsyncs;
int ret= func(file);
if (ret == 0) return ret;
switch (errno)
{ case ENOLCK:
++failures;
ut_a(failures < 1000);
if (!(failures % 100))
ib::warn() << func_name << ": No locks available; retrying";
/** Check the existence and type of the given file. @param[in]pathpathnameoffile @param[out]existstrueifthefileexists @param[out]typeTypeofthefile,ifitexists
@return true if call succeeded */ static bool
os_file_status_posix( constchar* path, bool* exists,
os_file_type_t* type) noexcept
{ struct stat statinfo;
int ret = stat(path, &statinfo);
*exists = !ret;
if (!ret) { /* file exists, everything OK */
} elseif (errno == ENOENT || errno == ENOTDIR || errno == ENAMETOOLONG) { /* file does not exist */ return(true);
} else { /* file exists, but stat call failed */
os_file_handle_error_no_exit(path, "stat", false); return(false);
}
if (S_ISDIR(statinfo.st_mode)) {
*type = OS_FILE_TYPE_DIR;
/** Deletes a file if it exists. The file has to be closed before calling this. @param[in]namefilepathasanull-terminatedstring @param[out]existindicateiffilepre-exist
@return true if success */ bool
os_file_delete_if_exists_func( constchar* name, bool* exist)
{ if (exist != NULL) {
*exist = true;
}
/** Deletes a file. The file has to be closed before calling this. @param[in]namefilepathasanull-terminatedstring
@return true if success */ bool
os_file_delete_func( constchar* name)
{ int ret;
ret = unlink(name);
if (ret != 0) {
os_file_handle_error_no_exit(name, "delete", FALSE);
return(false);
}
return(true);
}
/** NOTE! Use the corresponding macro os_file_rename(), not directly this function! Renamesafile(canalsomoveittoanotherdirectory).Itissafestthatthe fileisclosedbeforecallingthisfunction. @param[in]oldpatholdfilepathasanull-terminatedstring @param[in]newpathnewfilepath
@return true if success */ bool
os_file_rename_func( constchar* oldpath, constchar* newpath)
{ #ifdef UNIV_DEBUG
os_file_type_t type; bool exists;
/* New path must not exist. */
ut_ad(os_file_status(newpath, &exists, &type));
ut_ad(!exists);
/* Old path must exist. */
ut_ad(os_file_status(oldpath, &exists, &type));
ut_ad(exists); #endif/* UNIV_DEBUG */
int ret;
ret = rename(oldpath, newpath);
if (ret != 0) {
os_file_handle_rename_error(oldpath, newpath);
return(false);
}
return(true);
}
/** NOTE! Use the corresponding macro os_file_close(), not directly this function! Closesafilehandle.Incaseoferror,errornumbercanberetrievedwith os_file_get_last_error. @param[in]fileHandletoclose
@return true if success */ bool os_file_close_func(os_file_t file)
{ int ret= close(file);
/** This function returns information about the specified file @param[in]pathpathnameofthefile @param[out]stat_infoinformationofafileinadirectory @param[in,out]statinfoinformationofafileinadirectory @param[in]check_rw_permfortestingwhetherthefilecanbeopened inRWmode @param[in]read_onlyiftruereadonlymodechecksareenforced
@return DB_SUCCESS if all OK */ static
dberr_t
os_file_get_status_posix( constchar* path,
os_file_stat_t* stat_info, struct stat* statinfo, bool check_rw_perm, bool read_only)
{ int ret = stat(path, statinfo);
if (ret && (errno == ENOENT || errno == ENOTDIR
|| errno == ENAMETOOLONG)) { /* file does not exist */
return(DB_NOT_FOUND);
} elseif (ret) { /* file exists, but stat call failed */
/** Truncates a file to a specified size in bytes. Donothingifthesizetopreserveisgreaterorequaltothecurrent sizeofthefile. @param[in]pathnamefilepath @param[in]filefiletobetruncated @param[in]sizesizetopreserveinbytes
@return true if success */ static bool
os_file_truncate_posix( constchar* pathname,
os_file_t file,
os_offset_t size)
{ int res = ftruncate(file, size);
switch (err) { case0: returntrue; default:
sql_print_error("InnoDB: preallocating %" PRIu64 " bytes for file %s failed with error %d",
size, name, err); /* fall through */ case EINTR:
errno = err; returnfalse; case EINVAL: case EOPNOTSUPP: /* fall back to the code below */ break;
} # else/* HAVE_POSIX_ALLOCATE */
os_offset_t current_size = os_file_get_size(file); # endif /* HAVE_POSIX_ALLOCATE */
current_size &= ~4095ULL;
if (current_size >= size) { returntrue;
}
/* Write up to 1 megabyte at a time. */
ulint buf_size = std::min<ulint>(64,
ulint(size >> srv_page_size_shift))
<< srv_page_size_shift;
/* Align the buffer for possible raw i/o */
byte* buf = static_cast<byte*>(aligned_malloc(buf_size,
srv_page_size)); /* Write buffer full of zeros */
memset(buf, 0, buf_size);
/** Free storage space associated with a section of the file. @param[in]fhOpenfilehandle @param[in]offStartingoffset(SEEK_SET) @param[in]lenSizeofthehole
@return 0 on success or errno */ static
dberr_t
os_file_punch_hole_win32(
os_file_t fh,
os_offset_t off,
os_offset_t len)
{
FILE_ZERO_DATA_INFORMATION punch;
punch.FileOffset.QuadPart = off;
punch.BeyondFinalZero.QuadPart = off + len;
/* If lpOverlapped is NULL, lpBytesReturned cannot be NULL,
therefore we pass a dummy parameter. */
DWORD temp; BOOL success = os_win32_device_io_control(
fh, FSCTL_SET_ZERO_DATA, &punch, sizeof(punch),
NULL, 0, &temp);
/** Check the existence and type of the given file. @param[in]pathpathnameoffile @param[out]existstrueifthefileexists @param[out]typeTypeofthefile,ifitexists
@return true if call succeeded */ static bool
os_file_status_win32( constchar* path, bool* exists,
os_file_type_t* type)
{ int ret; struct _stat64 statinfo;
/** NOTE! Use the corresponding macro os_file_flush(), not directly this function! Flushesthewritebuffersofagivenfiletothedisk. @param[in]filehandletoafile
@return true if success */ bool os_file_flush_func(os_file_t file) noexcept
{ if (UNIV_UNLIKELY(my_disable_sync)) returntrue;
/* Since Windows returns ERROR_INVALID_FUNCTION if the 'file' is actuallyarawdevice,wechoosetoignorethaterrorifweareusing
raw disks */ if (srv_start_raw_disk_in_use && GetLastError() == ERROR_INVALID_FUNCTION) returntrue;
os_file_handle_error("flush");
/* It is a fatal error if a file flush does not succeed, because then
the database can get corrupt on disk */
ut_error;
returnfalse;
}
/** Retrieves the last error number if an error occurs in a file io function. ThenumbershouldberetrievedbeforeanyotherOScalls(becausetheymay overwritetheerrornumber).Ifthenumberisnotknowntothisprogram, thenOSerrornumber+OS_FILE_ERROR_MAXisreturned. @param[in]report_all_errorstrueifwewantanerrormessage printedofallerrors @param[in]on_error_silenttruethendon'tprintanydiagnostic tothelog
@return error number, or OS error number + OS_FILE_ERROR_MAX */
ulint os_file_get_last_error(bool report_all_errors, bool on_error_silent)
noexcept
{
ulint err = (ulint) GetLastError();
ib::error()
<< "Operating system error number " << err
<< " in a file operation.";
switch (err) { case ERROR_PATH_NOT_FOUND: break; case ERROR_ACCESS_DENIED:
ib::error()
<< "The error means mariadbd does not have" " the access rights to" " the directory. It may also be" " you have created a subdirectory" " of the same name as a data file."; break; case ERROR_SHARING_VIOLATION: case ERROR_LOCK_VIOLATION:
ib::error()
<< "The error means that another program" " is using InnoDB's files." " This might be a backup or antivirus" " software or another instance" " of MariaDB." " Please close it to get rid of this error."; break; case ERROR_WORKING_SET_QUOTA: case ERROR_NO_SYSTEM_RESOURCES:
ib::error()
<< "The error means that there are no" " sufficient system resources or quota to" " complete the operation."; break; case ERROR_OPERATION_ABORTED:
ib::error()
<< "The error means that the I/O" " operation has been aborted" " because of either a thread exit" " or an application request." " Retry attempt is made."; break; default:
ib::info() << OPERATING_SYSTEM_ERROR_MSG;
}
}
switch (create_mode) { case OS_FILE_OPEN_RAW:
ut_a(!read_only); /* On Windows Physical devices require admin privileges and havetohavethewrite-sharemodeset.Seetheremarks
section for the CreateFile() function documentation in MSDN. */
/** NOTE! Use the corresponding macro os_file_create_simple_no_error_handling(), notdirectlythisfunction! Asimplefunctiontoopenorcreateafile. @param[in]namenameofthefileorpathasanull-terminated string @param[in]create_modecreatemode @param[in]access_typeOS_FILE_READ_ONLY,OS_FILE_READ_WRITE,or OS_FILE_READ_ALLOW_DELETE;thelastoptionis usedbyabackupprogramreadingthefile @param[out]successtrueifsucceeded @returnown:handletothefile,notdefinediferror,errornumber
can be retrieved with os_file_get_last_error */
switch (access_type) { case OS_FILE_READ_ONLY: break; case OS_FILE_READ_WRITE:
access = GENERIC_READ | GENERIC_WRITE; break; default:
ut_ad(access_type == OS_FILE_READ_ALLOW_DELETE); /* A backup program has to give mariadbd the maximum
freedom to do what it likes with the file */
share_mode |= FILE_SHARE_DELETE | FILE_SHARE_WRITE
| FILE_SHARE_READ;
}
}
/** Deletes a file if it exists. The file has to be closed before calling this. @param[in]namefilepathasanull-terminatedstring @param[out]existindicateiffilepre-exist
@return true if success */ bool
os_file_delete_if_exists_func( constchar* name, bool* exist)
{
ulint count = 0;
if (exist != NULL) {
*exist = true;
}
for (;;) { /* In Windows, deleting an .ibd file may fail if thefileisbeingaccessedbyanexternalprogram,
such as a backup tool. */
bool ret = DeleteFile((LPCTSTR) name);
if (ret) { return(true);
}
switch (GetLastError()) { case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: /* the file does not exist, this not an error */ if (exist != NULL) {
*exist = false;
} /* fall through */ case ERROR_ACCESS_DENIED: return(true);
}
++count;
if (count > 100 && 0 == (count % 10)) {
/* Print error information */
os_file_get_last_error(true);
ib::warn() << "Delete of file '" << name << "' failed.";
}
/** Deletes a file. The file has to be closed before calling this. @param[in]nameFilepathasNULterminatedstring
@return true if success */ bool
os_file_delete_func( constchar* name)
{
ulint count = 0;
for (;;) { /* In Windows, deleting an .ibd file may fail if thefileisbeingaccessedbyanexternalprogram,
such as a backup tool. */
BOOL ret = DeleteFile((LPCTSTR) name);
if (ret) { return(true);
}
if (GetLastError() == ERROR_FILE_NOT_FOUND) { /* If the file does not exist, we classify this as
a 'mild' error and return */
return(false);
}
++count;
if (count > 100 && 0 == (count % 10)) {
/* print error information */
os_file_get_last_error(true);
ib::warn()
<< "Cannot delete file '" << name << "'. Is "
<< "another program accessing it?";
}
/** This function returns information about the specified file @param[in]pathpathnameofthefile @param[out]stat_infoinformationofafileinadirectory @param[in,out]statinfoinformationofafileinadirectory @param[in]check_rw_permfortestingwhetherthefilecanbeopened inRWmode @param[in]read_onlytrueifthefileisopenedinread-onlymode
@return DB_SUCCESS if all OK */ static
dberr_t
os_file_get_status_win32( constchar* path,
os_file_stat_t* stat_info, struct _stat64* statinfo, bool check_rw_perm, bool read_only)
{ int ret = _stat64(path, statinfo);
if (ret && (errno == ENOENT || errno == ENOTDIR
|| errno == ENAMETOOLONG)) { /* file does not exist */
return(DB_NOT_FOUND);
} elseif (ret) { /* file exists, but stat call failed */
/** NOTE! Use the corresponding macro os_file_write(), not directly Requestsasynchronouswriteoperation. @param[in]typeIOflags @param[in]filehandletoanopenfile @param[out]bufbufferfromwhichtowrite @param[in]offsetfileoffsetfromthestartwheretoread @param[in]nnumberofbytestoread,startingfromoffset @returnerrorcode
@retval DB_SUCCESS if the operation succeeded */
dberr_t
os_file_write_func( const IORequest& type, constchar* name,
os_file_t file, constvoid* buf,
os_offset_t offset,
ulint n)
{
dberr_t err;
ut_ad(n > 0);
ssize_t n_bytes = os_file_pwrite(type, file, (byte*)buf, n, offset, &err);
if ((ulint) n_bytes != n && !os_has_said_disk_full) {
ib::error()
<< "Write to file " << name << " failed at offset "
<< offset << ", " << n
<< " bytes should have been written," " only " << n_bytes << " were written." " Operating system error number " << IF_WIN(GetLastError(),errno) << "." " Check that your OS and file system" " support files of this size." " Check also that the disk is not full" " or a disk quota exceeded."; #ifndef _WIN32 if (strerror(errno) != NULL) {
ib::error()
<< "Error number " << errno
<< " means '" << strerror(errno) << "'";
}
dberr_t err;
ssize_t n_bytes= os_file_pread(type, file, buf, n, offset, &err);
if (o)
*o= ulint(n_bytes);
if (ulint(n_bytes) == n || err != DB_SUCCESS) return err;
os_file_handle_error_no_exit(type.node ? type.node->name : nullptr, "read", false);
sql_print_error("InnoDB: Tried to read %zu bytes at offset %" PRIu64 " of file %s, but was only able to read %zd",
n, offset, type.node ? type.node->name : "(unknown)",
n_bytes);
return err ? err : DB_IO_ERROR;
}
/** Handle errors for file operations. @param[in]namenameofafileorNULL @param[in]operationoperation @param[in]should_abortwhethertoabortonanunknownerror @param[in]on_error_silentwhethertosuppressreportsofnon-fatalerrors
@return true if we should retry the operation */ static MY_ATTRIBUTE((warn_unused_result)) bool
os_file_handle_error_cond_exit( constchar* name, constchar* operation, bool should_abort, bool on_error_silent) noexcept
{
ulint err;
/** Check if the file system supports sparse files. @paramfhfilehandle
@return true if the file system supports sparse files */ staticbool os_is_sparse_file_supported(os_file_t fh) noexcept
{ #ifdef _WIN32
FILE_ATTRIBUTE_TAG_INFO info; if (GetFileInformationByHandleEx(fh, FileAttributeTagInfo,
&info, (DWORD)sizeof(info))) { if (info.FileAttributes != INVALID_FILE_ATTRIBUTES) { return (info.FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0;
}
} returnfalse; #else /* We don't know the FS block size, use the sector size. The FS
will do the magic. */ return DB_SUCCESS == os_file_punch_hole_posix(fh, 0, srv_page_size); #endif/* _WIN32 */
}
/** Truncate a file to a specified size in bytes. @param[in]pathnamefilepath @param[in]filefiletobetruncated @param[in]sizesizepreservedinbytes @param[in]allow_shrinkwhethertoallowthefiletobecomesmaller
@return true if success */ bool
os_file_truncate( constchar* pathname,
os_file_t file,
os_offset_t size, bool allow_shrink) noexcept
{ if (!allow_shrink) { /* Do nothing if the size preserved is larger than or
equal to the current size of file */
os_offset_t size_bytes = os_file_get_size(file);
/** This function returns information about the specified file @param[in]pathpathnameofthefile @param[out]stat_infoinformationofafileinadirectory @param[in]check_rw_permfortestingwhetherthefilecanbeopened inRWmode @param[in]read_onlytrueiffileisopenedinread-onlymode
@return DB_SUCCESS if all OK */
dberr_t
os_file_get_status( constchar* path,
os_file_stat_t* stat_info, bool check_rw_perm, bool read_only) noexcept
{
dberr_t ret;
#ifdef _WIN32 struct _stat64 info;
ret = os_file_get_status_win32(
path, stat_info, &info, check_rw_perm, read_only);
#else struct stat info;
ret = os_file_get_status_posix(
path, stat_info, &info, check_rw_perm, read_only);
int os_aio_init() noexcept
{ int max_write_events= int(srv_n_write_io_threads *
OS_AIO_N_PENDING_IOS_PER_THREAD); int max_read_events= int(srv_n_read_io_threads *
OS_AIO_N_PENDING_IOS_PER_THREAD); int max_events= max_read_events + max_write_events; int ret= 1;
@param[in]n_reader_threads-maxnumberofconcurrently executingreadcallbacks @param[in]n_writer_thread-maxnumberofconcurrently executingwritecallbacks @return0forsuccess,!=0forerror.
*/ int os_aio_resize(ulint n_reader_threads, ulint n_writer_threads) noexcept
{ /* Lock the slots, and wait until all current IOs finish.*/
std::unique_lock<std::mutex> lk_read(read_slots->mutex()),
lk_write(write_slots->mutex());
/* Now, all IOs have finished and no new ones can start, due to locks. */ int max_read_events= int(n_reader_threads * OS_AIO_N_PENDING_IOS_PER_THREAD); int max_write_events= int(n_writer_threads * OS_AIO_N_PENDING_IOS_PER_THREAD); int events= max_read_events + max_write_events;
/* Do the Linux AIO dance (this will try to create a new
io context with changed max_events, etc.) */
int ret= srv_thread_pool->reconfigure_aio(srv_use_native_aio, events);
if (ret)
{ /** Do the best effort. We can't change the parallel io number,
but we still can adjust the number of concurrent completion handlers.*/
read_slots->task_group().set_max_tasks(static_cast<int>(n_reader_threads));
write_slots->task_group().set_max_tasks(static_cast<int>(n_writer_threads));
} else
{ /* Allocation succeeded, resize the slots*/
read_slots->resize(max_read_events, static_cast<int>(n_reader_threads));
write_slots->resize(max_write_events, static_cast<int>(n_writer_threads));
} return ret;
}
/** Wait until there are no pending asynchronous writes. */ staticvoid os_aio_wait_until_no_pending_writes_low(bool declare) noexcept
{ constbool notify_wait= declare && write_slots->pending_io_count();
if (notify_wait)
tpool::tpool_wait_begin();
write_slots->wait();
if (notify_wait)
tpool::tpool_wait_end();
}
/** Wait until there are no pending asynchronous writes.
@param declare whether the wait will be declared in tpool */ void os_aio_wait_until_no_pending_writes(bool declare) noexcept
{
os_aio_wait_until_no_pending_writes_low(declare);
buf_dblwr.wait_flush_buffered_writes();
}
/** @return number of pending reads */
size_t os_aio_pending_reads() noexcept
{
std::lock_guard<std::mutex> lock(read_slots->mutex()); return read_slots->pending_io_count();
}
/** @return approximate number of pending reads */
size_t os_aio_pending_reads_approx() noexcept
{ return read_slots->pending_io_count();
}
/** @return number of pending writes */
size_t os_aio_pending_writes() noexcept
{
std::lock_guard<std::mutex> lock(write_slots->mutex()); return write_slots->pending_io_count();
}
/** @return approximate number of pending writes */
size_t os_aio_pending_writes_approx() noexcept
{ return write_slots->pending_io_count();
}
/** Wait until all pending asynchronous reads have completed.
@param declare whether the wait will be declared in tpool */ void os_aio_wait_until_no_pending_reads(bool declare) noexcept
{ constbool notify_wait= declare && read_slots->pending_io_count();
if (!GetVolumeNameForVolumeMountPoint(volume_mount_point, volume_name,
array_elements(volume_name)))
{ /* This can fail, e.g if file is on network share */ returnfalse;
}
/* Chomp last backslash, this is needed to open volume.*/
size_t length= strlen(volume_name); if (length && volume_name[length - 1] == '\\')
volume_name[length - 1]= 0;
if (!space->is_compressed())
punch_hole= 0; elseif (my_test_if_thinly_provisioned(file))
punch_hole= 2; else
punch_hole= IF_WIN(, !create ||) os_is_sparse_file_supported(file); /* For temporary tablespace or during IMPORT TABLESPACE, we
disable neighbour flushing and do not care about atomicity. */ if (space->is_temporary())
{
on_ssd= true;
atomic_write= true; return;
} if (space->is_being_imported())
{
on_ssd= true;
atomic_write= true; if (!space->is_compressed()) return;
} #ifdef _WIN32
on_ssd= is_file_on_ssd(file, name);
FILE_STORAGE_INFO info; if (GetFileInformationByHandleEx(file, FileStorageInfo, &info, sizeof info))
block_size= info.PhysicalBytesPerSectorForAtomicity; else
block_size= 512; #else struct stat statbuf; if (!fstat(file, &statbuf))
{
block_size= statbuf.st_blksize; # ifdef __linux__
on_ssd= fil_system.is_ssd(statbuf.st_dev); # endif
} #endif
/* On Windows, all single sector writes are atomic, as per
WriteFile() documentation on MSDN. */
atomic_write= srv_use_atomic_writes &&
IF_WIN(srv_page_size == block_size,
my_test_if_atomic_write(file, space->physical_size()));
}
/** Read the first page of a data file.
@return whether the page was found valid */ bool fil_node_t::read_page0(const byte *dpage, bool no_lsn) noexcept
{
mysql_mutex_assert_owner(&fil_system.mutex);
ut_ad(!dpage || no_lsn); constunsigned psize= space->physical_size();
os_offset_t size_bytes= os_file_get_size(handle); if (size_bytes == os_offset_t(-1)) returnfalse; const uint32_t min_size= FIL_IBD_FILE_INITIAL_SIZE * psize;
if (size_bytes < min_size)
{
ib::error() << "The size of the file " << name
<< " is only " << size_bytes
<< " bytes, should be at least " << min_size; returnfalse;
}
if (!deferred)
{
page_t *apage= static_cast<byte*>(aligned_malloc(psize, psize)); if (os_file_read(IORequestRead, handle, apage, 0, psize, nullptr) !=
DB_SUCCESS)
{
sql_print_error("InnoDB: Unable to read first page of file %s", name);
err_exit:
aligned_free(apage); returnfalse;
}
if (!fil_space_t::is_flags_equal((flags & ~FSP_FLAGS_MEM_MASK),
(space->flags & ~FSP_FLAGS_MEM_MASK)) &&
!fil_space_t::is_flags_equal((space->flags & ~FSP_FLAGS_MEM_MASK),
(flags & ~FSP_FLAGS_MEM_MASK)))
{
invalid: if (dpage) goto retry_dpage;
sql_print_error("InnoDB: Expected tablespace flags 0x%" PRIx32 " but found 0x%" PRIx32 " in the file %s", space->flags, flags, name); goto err_exit;
}
flags_ok:
ut_ad(!(flags & FSP_FLAGS_MEM_MASK));
if (buf_page_is_corrupted(!no_lsn, page, flags) != NOT_CORRUPTED)
{ if (dpage) goto retry_dpage;
sql_print_error("InnoDB: The first page of file %s is corrupted", name); goto err_exit;
}
if (UNIV_UNLIKELY(space_id != space->id))
{ if (dpage) goto retry_dpage;
sql_print_error("InnoDB: Expected tablespace id %zu but found %zu" " in the file %s", ulint{space->id}, ulint{space_id},
name); goto err_exit;
}
/* Try to read crypt_data from page 0 if it is not yet read. */ if (!space->crypt_data)
space->crypt_data= fil_space_read_crypt_data(
fil_space_t::zip_size(flags), page);
aligned_free(apage);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.