1/*****************************************************************************
2
3Copyright (c) 2013, 2016, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2017, 2018, MariaDB Corporation.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17
18*****************************************************************************/
19
20/**************************************************//**
21@file fsp/fsp0file.cc
22Tablespace data file implementation
23
24Created 2013-7-26 by Kevin Lewis
25*******************************************************/
26
27#include "ha_prototypes.h"
28
29#include "fil0fil.h"
30#include "fsp0types.h"
31#include "fsp0sysspace.h"
32#include "os0file.h"
33#include "page0page.h"
34#include "srv0start.h"
35#include "ut0new.h"
36#include "fil0crypt.h"
37
38/** Initialize the name, size and order of this datafile
39@param[in] name tablespace name, will be copied
40@param[in] flags tablespace flags */
41void
42Datafile::init(
43 const char* name,
44 ulint flags)
45{
46 ut_ad(m_name == NULL);
47 ut_ad(name != NULL);
48
49 m_name = mem_strdup(name);
50 m_flags = flags;
51}
52
53/** Release the resources. */
54void
55Datafile::shutdown()
56{
57 close();
58
59 ut_free(m_name);
60 m_name = NULL;
61 free_filepath();
62 free_first_page();
63}
64
65/** Create/open a data file.
66@param[in] read_only_mode if true, then readonly mode checks are enforced.
67@return DB_SUCCESS or error code */
68dberr_t
69Datafile::open_or_create(bool read_only_mode)
70{
71 bool success;
72 ut_a(m_filepath != NULL);
73 ut_ad(m_handle == OS_FILE_CLOSED);
74
75 m_handle = os_file_create(
76 innodb_data_file_key, m_filepath, m_open_flags,
77 OS_FILE_NORMAL, OS_DATA_FILE, read_only_mode, &success);
78
79 if (!success) {
80 m_last_os_error = os_file_get_last_error(true);
81 ib::error() << "Cannot open datafile '" << m_filepath << "'";
82 return(DB_CANNOT_OPEN_FILE);
83 }
84
85 return(DB_SUCCESS);
86}
87
88/** Open a data file in read-only mode to check if it exists so that it
89can be validated.
90@param[in] strict whether to issue error messages
91@return DB_SUCCESS or error code */
92dberr_t
93Datafile::open_read_only(bool strict)
94{
95 bool success = false;
96 ut_ad(m_handle == OS_FILE_CLOSED);
97
98 /* This function can be called for file objects that do not need
99 to be opened, which is the case when the m_filepath is NULL */
100 if (m_filepath == NULL) {
101 return(DB_ERROR);
102 }
103
104 set_open_flags(OS_FILE_OPEN);
105 m_handle = os_file_create_simple_no_error_handling(
106 innodb_data_file_key, m_filepath, m_open_flags,
107 OS_FILE_READ_ONLY, true, &success);
108
109 if (success) {
110 m_exists = true;
111 init_file_info();
112
113 return(DB_SUCCESS);
114 }
115
116 if (strict) {
117 m_last_os_error = os_file_get_last_error(true);
118 ib::error() << "Cannot open datafile for read-only: '"
119 << m_filepath << "' OS error: " << m_last_os_error;
120 }
121
122 return(DB_CANNOT_OPEN_FILE);
123}
124
125/** Open a data file in read-write mode during start-up so that
126doublewrite pages can be restored and then it can be validated.*
127@param[in] read_only_mode if true, then readonly mode checks are enforced.
128@return DB_SUCCESS or error code */
129dberr_t
130Datafile::open_read_write(bool read_only_mode)
131{
132 bool success = false;
133 ut_ad(m_handle == OS_FILE_CLOSED);
134
135 /* This function can be called for file objects that do not need
136 to be opened, which is the case when the m_filepath is NULL */
137 if (m_filepath == NULL) {
138 return(DB_ERROR);
139 }
140
141 set_open_flags(OS_FILE_OPEN);
142 m_handle = os_file_create_simple_no_error_handling(
143 innodb_data_file_key, m_filepath, m_open_flags,
144 OS_FILE_READ_WRITE, read_only_mode, &success);
145
146 if (!success) {
147 m_last_os_error = os_file_get_last_error(true);
148 ib::error() << "Cannot open datafile for read-write: '"
149 << m_filepath << "'";
150 return(DB_CANNOT_OPEN_FILE);
151 }
152
153 m_exists = true;
154
155 init_file_info();
156
157 return(DB_SUCCESS);
158}
159
160/** Initialize OS specific file info. */
161void
162Datafile::init_file_info()
163{
164#ifdef _WIN32
165 GetFileInformationByHandle(m_handle, &m_file_info);
166#else
167 fstat(m_handle, &m_file_info);
168#endif /* WIN32 */
169}
170
171/** Close a data file.
172@return DB_SUCCESS or error code */
173dberr_t
174Datafile::close()
175{
176 if (m_handle != OS_FILE_CLOSED) {
177 ibool success = os_file_close(m_handle);
178 ut_a(success);
179
180 m_handle = OS_FILE_CLOSED;
181 }
182
183 return(DB_SUCCESS);
184}
185
186/** Make a full filepath from a directory path and a filename.
187Prepend the dirpath to filename using the extension given.
188If dirpath is NULL, prepend the default datadir to filepath.
189Store the result in m_filepath.
190@param[in] dirpath directory path
191@param[in] filename filename or filepath
192@param[in] ext filename extension */
193void
194Datafile::make_filepath(
195 const char* dirpath,
196 const char* filename,
197 ib_extention ext)
198{
199 ut_ad(dirpath != NULL || filename != NULL);
200
201 free_filepath();
202
203 m_filepath = fil_make_filepath(dirpath, filename, ext, false);
204
205 ut_ad(m_filepath != NULL);
206
207 set_filename();
208}
209
210/** Set the filepath by duplicating the filepath sent in. This is the
211name of the file with its extension and absolute or relative path.
212@param[in] filepath filepath to set */
213void
214Datafile::set_filepath(const char* filepath)
215{
216 free_filepath();
217 m_filepath = static_cast<char*>(ut_malloc_nokey(strlen(filepath) + 1));
218 ::strcpy(m_filepath, filepath);
219 set_filename();
220}
221
222/** Free the filepath buffer. */
223void
224Datafile::free_filepath()
225{
226 if (m_filepath != NULL) {
227 ut_free(m_filepath);
228 m_filepath = NULL;
229 m_filename = NULL;
230 }
231}
232
233/** Do a quick test if the filepath provided looks the same as this filepath
234byte by byte. If they are two different looking paths to the same file,
235same_as() will be used to show that after the files are opened.
236@param[in] other filepath to compare with
237@retval true if it is the same filename by byte comparison
238@retval false if it looks different */
239bool
240Datafile::same_filepath_as(
241 const char* other) const
242{
243 return(0 == strcmp(m_filepath, other));
244}
245
246/** Test if another opened datafile is the same file as this object.
247@param[in] other Datafile to compare with
248@return true if it is the same file, else false */
249bool
250Datafile::same_as(
251 const Datafile& other) const
252{
253#ifdef _WIN32
254 return(m_file_info.dwVolumeSerialNumber
255 == other.m_file_info.dwVolumeSerialNumber
256 && m_file_info.nFileIndexHigh
257 == other.m_file_info.nFileIndexHigh
258 && m_file_info.nFileIndexLow
259 == other.m_file_info.nFileIndexLow);
260#else
261 return(m_file_info.st_ino == other.m_file_info.st_ino
262 && m_file_info.st_dev == other.m_file_info.st_dev);
263#endif /* WIN32 */
264}
265
266/** Allocate and set the datafile or tablespace name in m_name.
267If a name is provided, use it; else extract a file-per-table
268tablespace name from m_filepath. The value of m_name
269will be freed in the destructor.
270@param[in] name tablespace name if known, NULL if not */
271void
272Datafile::set_name(const char* name)
273{
274 ut_free(m_name);
275
276 if (name != NULL) {
277 m_name = mem_strdup(name);
278 } else {
279 m_name = fil_path_to_space_name(m_filepath);
280 }
281}
282
283/** Reads a few significant fields from the first page of the first
284datafile. The Datafile must already be open.
285@param[in] read_only_mode If true, then readonly mode checks are enforced.
286@return DB_SUCCESS or DB_IO_ERROR if page cannot be read */
287dberr_t
288Datafile::read_first_page(bool read_only_mode)
289{
290 if (m_handle == OS_FILE_CLOSED) {
291
292 dberr_t err = open_or_create(read_only_mode);
293
294 if (err != DB_SUCCESS) {
295 return(err);
296 }
297 }
298
299 m_first_page_buf = static_cast<byte*>(
300 ut_malloc_nokey(2 * UNIV_PAGE_SIZE_MAX));
301
302 /* Align the memory for a possible read from a raw device */
303
304 m_first_page = static_cast<byte*>(
305 ut_align(m_first_page_buf, srv_page_size));
306
307 IORequest request;
308 dberr_t err = DB_ERROR;
309 size_t page_size = UNIV_PAGE_SIZE_MAX;
310
311 /* Don't want unnecessary complaints about partial reads. */
312
313 request.disable_partial_io_warnings();
314
315 while (page_size >= UNIV_PAGE_SIZE_MIN) {
316
317 ulint n_read = 0;
318
319 err = os_file_read_no_error_handling(
320 request, m_handle, m_first_page, 0, page_size, &n_read);
321
322 if (err == DB_IO_ERROR && n_read >= UNIV_PAGE_SIZE_MIN) {
323
324 page_size >>= 1;
325
326 } else if (err == DB_SUCCESS) {
327
328 ut_a(n_read == page_size);
329
330 break;
331
332 } else if (srv_operation == SRV_OPERATION_BACKUP) {
333 break;
334 } else {
335
336 ib::error()
337 << "Cannot read first page of '"
338 << m_filepath << "' "
339 << ut_strerr(err);
340 break;
341 }
342 }
343
344 if (err != DB_SUCCESS) {
345 return(err);
346 }
347
348 if (m_order == 0) {
349 m_space_id = fsp_header_get_space_id(m_first_page);
350 m_flags = fsp_header_get_flags(m_first_page);
351 if (!fsp_flags_is_valid(m_flags, m_space_id)) {
352 ulint cflags = fsp_flags_convert_from_101(m_flags);
353 if (cflags == ULINT_UNDEFINED) {
354 ib::error()
355 << "Invalid flags " << ib::hex(m_flags)
356 << " in " << m_filepath;
357 return(DB_CORRUPTION);
358 } else {
359 m_flags = cflags;
360 }
361 }
362 }
363
364 const page_size_t ps(m_flags);
365 if (ps.physical() > page_size) {
366 ib::error() << "File " << m_filepath
367 << " should be longer than "
368 << page_size << " bytes";
369 return(DB_CORRUPTION);
370 }
371
372 return(err);
373}
374
375/** Free the first page from memory when it is no longer needed. */
376void
377Datafile::free_first_page()
378{
379 if (m_first_page_buf) {
380 ut_free(m_first_page_buf);
381 m_first_page_buf = NULL;
382 m_first_page = NULL;
383 }
384}
385
386/** Validates the datafile and checks that it conforms with the expected
387space ID and flags. The file should exist and be successfully opened
388in order for this function to validate it.
389@param[in] space_id The expected tablespace ID.
390@param[in] flags The expected tablespace flags.
391@retval DB_SUCCESS if tablespace is valid, DB_ERROR if not.
392m_is_valid is also set true on success, else false. */
393dberr_t
394Datafile::validate_to_dd(ulint space_id, ulint flags)
395{
396 dberr_t err;
397
398 if (!is_open()) {
399 return DB_ERROR;
400 }
401
402 /* Validate this single-table-tablespace with the data dictionary,
403 but do not compare the DATA_DIR flag, in case the tablespace was
404 remotely located. */
405 err = validate_first_page(0);
406 if (err != DB_SUCCESS) {
407 return(err);
408 }
409
410 flags &= ~FSP_FLAGS_MEM_MASK;
411
412 /* Make sure the datafile we found matched the space ID.
413 If the datafile is a file-per-table tablespace then also match
414 the row format and zip page size. */
415 if (m_space_id == space_id && m_flags == flags) {
416 /* Datafile matches the tablespace expected. */
417 return(DB_SUCCESS);
418 }
419
420 /* else do not use this tablespace. */
421 m_is_valid = false;
422
423 ib::error() << "Refusing to load '" << m_filepath << "' (id="
424 << m_space_id << ", flags=" << ib::hex(m_flags)
425 << "); dictionary contains id="
426 << space_id << ", flags=" << ib::hex(flags);
427
428 return(DB_ERROR);
429}
430
431/** Validates this datafile for the purpose of recovery. The file should
432exist and be successfully opened. We initially open it in read-only mode
433because we just want to read the SpaceID. However, if the first page is
434corrupt and needs to be restored from the doublewrite buffer, we will
435reopen it in write mode and ry to restore that page.
436@retval DB_SUCCESS if tablespace is valid, DB_ERROR if not.
437m_is_valid is also set true on success, else false. */
438dberr_t
439Datafile::validate_for_recovery()
440{
441 dberr_t err;
442
443 ut_ad(is_open());
444 ut_ad(!srv_read_only_mode);
445
446 err = validate_first_page(0);
447
448 switch (err) {
449 case DB_SUCCESS:
450 case DB_TABLESPACE_EXISTS:
451 break;
452
453 default:
454 /* Re-open the file in read-write mode Attempt to restore
455 page 0 from doublewrite and read the space ID from a survey
456 of the first few pages. */
457 close();
458 err = open_read_write(srv_read_only_mode);
459 if (err != DB_SUCCESS) {
460 return(err);
461 }
462
463 err = find_space_id();
464 if (err != DB_SUCCESS || m_space_id == 0) {
465 ib::error() << "Datafile '" << m_filepath << "' is"
466 " corrupted. Cannot determine the space ID from"
467 " the first 64 pages.";
468 return(err);
469 }
470
471 if (restore_from_doublewrite()) {
472 return(DB_CORRUPTION);
473 }
474
475 /* Free the previously read first page and then re-validate. */
476 free_first_page();
477 err = validate_first_page(0);
478 }
479
480 if (err == DB_SUCCESS) {
481 set_name(NULL);
482 }
483
484 return(err);
485}
486
487/** Check the consistency of the first page of a datafile when the
488tablespace is opened. This occurs before the fil_space_t is created
489so the Space ID found here must not already be open.
490m_is_valid is set true on success, else false.
491@param[out] flush_lsn contents of FIL_PAGE_FILE_FLUSH_LSN
492@retval DB_SUCCESS on if the datafile is valid
493@retval DB_CORRUPTION if the datafile is not readable
494@retval DB_TABLESPACE_EXISTS if there is a duplicate space_id */
495dberr_t
496Datafile::validate_first_page(lsn_t* flush_lsn)
497{
498 char* prev_name;
499 char* prev_filepath;
500 const char* error_txt = NULL;
501
502 m_is_valid = true;
503
504 if (m_first_page == NULL
505 && read_first_page(srv_read_only_mode) != DB_SUCCESS) {
506
507 error_txt = "Cannot read first page";
508 } else {
509 ut_ad(m_first_page_buf);
510 ut_ad(m_first_page);
511
512 if (flush_lsn != NULL) {
513
514 *flush_lsn = mach_read_from_8(
515 m_first_page + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION);
516 }
517 }
518
519 if (error_txt != NULL) {
520err_exit:
521 ib::error() << error_txt << " in datafile: " << m_filepath
522 << ", Space ID:" << m_space_id << ", Flags: "
523 << m_flags << ". " << TROUBLESHOOT_DATADICT_MSG;
524 m_is_valid = false;
525 free_first_page();
526 return(DB_CORRUPTION);
527 }
528
529 /* Check if the whole page is blank. */
530 if (!m_space_id && !m_flags) {
531 const byte* b = m_first_page;
532 ulint nonzero_bytes = srv_page_size;
533
534 while (*b == '\0' && --nonzero_bytes != 0) {
535
536 b++;
537 }
538
539 if (nonzero_bytes == 0) {
540 error_txt = "Header page consists of zero bytes";
541 goto err_exit;
542 }
543 }
544
545 if (!fsp_flags_is_valid(m_flags, m_space_id)) {
546 /* Tablespace flags must be valid. */
547 error_txt = "Tablespace flags are invalid";
548 goto err_exit;
549 }
550
551 const page_size_t page_size(m_flags);
552
553 if (srv_page_size != page_size.logical()) {
554 /* Logical size must be innodb_page_size. */
555 ib::error()
556 << "Data file '" << m_filepath << "' uses page size "
557 << page_size.logical() << ", but the innodb_page_size"
558 " start-up parameter is "
559 << srv_page_size;
560 free_first_page();
561 return(DB_ERROR);
562 }
563
564 if (page_get_page_no(m_first_page) != 0) {
565 /* First page must be number 0 */
566 error_txt = "Header page contains inconsistent data";
567 goto err_exit;
568 }
569
570 if (m_space_id == ULINT_UNDEFINED) {
571 /* The space_id can be most anything, except -1. */
572 error_txt = "A bad Space ID was found";
573 goto err_exit;
574 }
575
576 if (buf_page_is_corrupted(false, m_first_page, page_size)) {
577 /* Look for checksum and other corruptions. */
578 error_txt = "Checksum mismatch";
579 goto err_exit;
580 }
581
582 if (fil_space_read_name_and_filepath(
583 m_space_id, &prev_name, &prev_filepath)) {
584
585 if (0 == strcmp(m_filepath, prev_filepath)) {
586 ut_free(prev_name);
587 ut_free(prev_filepath);
588 return(DB_SUCCESS);
589 }
590
591 /* Make sure the space_id has not already been opened. */
592 ib::error() << "Attempted to open a previously opened"
593 " tablespace. Previous tablespace " << prev_name
594 << " at filepath: " << prev_filepath
595 << " uses space ID: " << m_space_id
596 << ". Cannot open filepath: " << m_filepath
597 << " which uses the same space ID.";
598
599 ut_free(prev_name);
600 ut_free(prev_filepath);
601
602 m_is_valid = false;
603
604 free_first_page();
605
606 return(is_predefined_tablespace(m_space_id)
607 ? DB_CORRUPTION
608 : DB_TABLESPACE_EXISTS);
609 }
610
611 return(DB_SUCCESS);
612}
613
614/** Determine the space id of the given file descriptor by reading a few
615pages from the beginning of the .ibd file.
616@return DB_SUCCESS if space id was successfully identified, else DB_ERROR. */
617dberr_t
618Datafile::find_space_id()
619{
620 os_offset_t file_size;
621
622 ut_ad(m_handle != OS_FILE_CLOSED);
623
624 file_size = os_file_get_size(m_handle);
625
626 if (file_size == (os_offset_t) -1) {
627 ib::error() << "Could not get file size of datafile '"
628 << m_filepath << "'";
629 return(DB_CORRUPTION);
630 }
631
632 /* Assuming a page size, read the space_id from each page and store it
633 in a map. Find out which space_id is agreed on by majority of the
634 pages. Choose that space_id. */
635 for (ulint page_size = UNIV_ZIP_SIZE_MIN;
636 page_size <= UNIV_PAGE_SIZE_MAX;
637 page_size <<= 1) {
638
639 /* map[space_id] = count of pages */
640 typedef std::map<
641 ulint,
642 ulint,
643 std::less<ulint>,
644 ut_allocator<std::pair<const ulint, ulint> > >
645 Pages;
646
647 Pages verify;
648 ulint page_count = 64;
649 ulint valid_pages = 0;
650
651 /* Adjust the number of pages to analyze based on file size */
652 while ((page_count * page_size) > file_size) {
653 --page_count;
654 }
655
656 ib::info()
657 << "Page size:" << page_size
658 << ". Pages to analyze:" << page_count;
659
660 byte* buf = static_cast<byte*>(
661 ut_malloc_nokey(2 * UNIV_PAGE_SIZE_MAX));
662
663 byte* page = static_cast<byte*>(
664 ut_align(buf, UNIV_SECTOR_SIZE));
665
666 for (ulint j = 0; j < page_count; ++j) {
667
668 dberr_t err;
669 ulint n_bytes = j * page_size;
670 IORequest request(IORequest::READ);
671
672 err = os_file_read(
673 request, m_handle, page, n_bytes, page_size);
674
675 if (err != DB_SUCCESS) {
676
677 ib::info()
678 << "READ FAIL: page_no:" << j;
679
680 continue;
681 }
682
683 bool noncompressed_ok = false;
684
685 /* For noncompressed pages, the page size must be
686 equal to srv_page_size. */
687 if (page_size == srv_page_size) {
688 noncompressed_ok = !buf_page_is_corrupted(
689 false, page, univ_page_size, NULL);
690 }
691
692 bool compressed_ok = false;
693
694 /* file-per-table tablespaces can be compressed with
695 the same physical and logical page size. General
696 tablespaces must have different physical and logical
697 page sizes in order to be compressed. For this check,
698 assume the page is compressed if univ_page_size.
699 logical() is equal to or less than 16k and the
700 page_size we are checking is equal to or less than
701 srv_page_size. */
702 if (srv_page_size <= UNIV_PAGE_SIZE_DEF
703 && page_size <= srv_page_size) {
704 const page_size_t compr_page_size(
705 page_size, srv_page_size,
706 true);
707
708 compressed_ok = !buf_page_is_corrupted(
709 false, page, compr_page_size, NULL);
710 }
711
712 if (noncompressed_ok || compressed_ok) {
713
714 ulint space_id = mach_read_from_4(page
715 + FIL_PAGE_SPACE_ID);
716
717 if (space_id > 0) {
718
719 ib::info()
720 << "VALID: space:"
721 << space_id << " page_no:" << j
722 << " page_size:" << page_size;
723
724 ++valid_pages;
725
726 ++verify[space_id];
727 }
728 }
729 }
730
731 ut_free(buf);
732
733 ib::info()
734 << "Page size: " << page_size
735 << ". Possible space_id count:" << verify.size();
736
737 const ulint pages_corrupted = 3;
738
739 for (ulint missed = 0; missed <= pages_corrupted; ++missed) {
740
741 for (Pages::const_iterator it = verify.begin();
742 it != verify.end();
743 ++it) {
744
745 ib::info() << "space_id:" << it->first
746 << ", Number of pages matched: "
747 << it->second << "/" << valid_pages
748 << " (" << page_size << ")";
749
750 if (it->second == (valid_pages - missed)) {
751 ib::info() << "Chosen space:"
752 << it->first;
753
754 m_space_id = it->first;
755 return(DB_SUCCESS);
756 }
757 }
758
759 }
760 }
761
762 return(DB_CORRUPTION);
763}
764
765
766/** Restore the first page of the tablespace from
767the double write buffer.
768@return whether the operation failed */
769bool
770Datafile::restore_from_doublewrite()
771{
772 if (srv_operation != SRV_OPERATION_NORMAL) {
773 return true;
774 }
775
776 /* Find if double write buffer contains page_no of given space id. */
777 const byte* page = recv_sys->dblwr.find_page(m_space_id, 0);
778 const page_id_t page_id(m_space_id, 0);
779
780 if (page == NULL) {
781 /* If the first page of the given user tablespace is not there
782 in the doublewrite buffer, then the recovery is going to fail
783 now. Hence this is treated as an error. */
784
785 ib::error()
786 << "Corrupted page " << page_id
787 << " of datafile '" << m_filepath
788 << "' could not be found in the doublewrite buffer.";
789
790 return(true);
791 }
792
793 ulint flags = mach_read_from_4(
794 FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page);
795
796 if (!fsp_flags_is_valid(flags, m_space_id)) {
797 ulint cflags = fsp_flags_convert_from_101(flags);
798 if (cflags == ULINT_UNDEFINED) {
799 ib::warn()
800 << "Ignoring a doublewrite copy of page "
801 << page_id
802 << " due to invalid flags " << ib::hex(flags);
803 return(true);
804 }
805 flags = cflags;
806 /* The flags on the page should be converted later. */
807 }
808
809 const page_size_t page_size(flags);
810
811 ut_a(page_get_page_no(page) == page_id.page_no());
812
813 ib::info() << "Restoring page " << page_id
814 << " of datafile '" << m_filepath
815 << "' from the doublewrite buffer. Writing "
816 << page_size.physical() << " bytes into file '"
817 << m_filepath << "'";
818
819 IORequest request(IORequest::WRITE);
820
821 return(os_file_write(
822 request,
823 m_filepath, m_handle, page, 0, page_size.physical())
824 != DB_SUCCESS);
825}
826
827/** Create a link filename based on the contents of m_name,
828open that file, and read the contents into m_filepath.
829@retval DB_SUCCESS if remote linked tablespace file is opened and read.
830@retval DB_CANNOT_OPEN_FILE if the link file does not exist. */
831dberr_t
832RemoteDatafile::open_link_file()
833{
834 if (m_link_filepath == NULL) {
835 m_link_filepath = fil_make_filepath(NULL, name(), ISL, false);
836 }
837
838 m_filepath = read_link_file(m_link_filepath);
839
840 return(m_filepath == NULL ? DB_CANNOT_OPEN_FILE : DB_SUCCESS);
841}
842
843/** Opens a handle to the file linked to in an InnoDB Symbolic Link file
844in read-only mode so that it can be validated.
845@param[in] strict whether to issue error messages
846@return DB_SUCCESS if remote linked tablespace file is found and opened. */
847dberr_t
848RemoteDatafile::open_read_only(bool strict)
849{
850 if (m_filepath == NULL && open_link_file() == DB_CANNOT_OPEN_FILE) {
851 return(DB_ERROR);
852 }
853
854 dberr_t err = Datafile::open_read_only(strict);
855
856 if (err != DB_SUCCESS && strict) {
857 /* The following call prints an error message */
858 os_file_get_last_error(true);
859 ib::error() << "A link file was found named '"
860 << m_link_filepath << "' but the linked tablespace '"
861 << m_filepath << "' could not be opened read-only.";
862 }
863
864 return(err);
865}
866
867/** Opens a handle to the file linked to in an InnoDB Symbolic Link file
868in read-write mode so that it can be restored from doublewrite and validated.
869@param[in] read_only_mode If true, then readonly mode checks are enforced.
870@return DB_SUCCESS if remote linked tablespace file is found and opened. */
871dberr_t
872RemoteDatafile::open_read_write(bool read_only_mode)
873{
874 if (m_filepath == NULL && open_link_file() == DB_CANNOT_OPEN_FILE) {
875 return(DB_ERROR);
876 }
877
878 dberr_t err = Datafile::open_read_write(read_only_mode);
879
880 if (err != DB_SUCCESS) {
881 /* The following call prints an error message */
882 m_last_os_error = os_file_get_last_error(true);
883 ib::error() << "A link file was found named '"
884 << m_link_filepath << "' but the linked data file '"
885 << m_filepath << "' could not be opened for writing.";
886 }
887
888 return(err);
889}
890
891/** Release the resources. */
892void
893RemoteDatafile::shutdown()
894{
895 Datafile::shutdown();
896
897 if (m_link_filepath != 0) {
898 ut_free(m_link_filepath);
899 m_link_filepath = 0;
900 }
901}
902
903/** Creates a new InnoDB Symbolic Link (ISL) file. It is always created
904under the 'datadir' of MySQL. The datadir is the directory of a
905running mysqld program. We can refer to it by simply using the path ".".
906@param[in] name tablespace name
907@param[in] filepath remote filepath of tablespace datafile
908@return DB_SUCCESS or error code */
909dberr_t
910RemoteDatafile::create_link_file(
911 const char* name,
912 const char* filepath)
913{
914 bool success;
915 dberr_t err = DB_SUCCESS;
916 char* link_filepath = NULL;
917 char* prev_filepath = NULL;
918
919 ut_ad(!srv_read_only_mode);
920 ut_ad(0 == strcmp(&filepath[strlen(filepath) - 4], DOT_IBD));
921
922 link_filepath = fil_make_filepath(NULL, name, ISL, false);
923
924 if (link_filepath == NULL) {
925 return(DB_ERROR);
926 }
927
928 prev_filepath = read_link_file(link_filepath);
929 if (prev_filepath) {
930 /* Truncate will call this with an existing
931 link file which contains the same filepath. */
932 bool same = !strcmp(prev_filepath, filepath);
933 ut_free(prev_filepath);
934 if (same) {
935 ut_free(link_filepath);
936 return(DB_SUCCESS);
937 }
938 }
939
940 /** Check if the file already exists. */
941 FILE* file = NULL;
942 bool exists;
943 os_file_type_t ftype;
944
945 success = os_file_status(link_filepath, &exists, &ftype);
946 ulint error = 0;
947
948 if (success && !exists) {
949
950 file = fopen(link_filepath, "w");
951 if (file == NULL) {
952 /* This call will print its own error message */
953 error = os_file_get_last_error(true);
954 }
955 } else {
956 error = OS_FILE_ALREADY_EXISTS;
957 }
958
959 if (error != 0) {
960
961 ib::error() << "Cannot create file " << link_filepath << ".";
962
963 if (error == OS_FILE_ALREADY_EXISTS) {
964 ib::error() << "The link file: " << link_filepath
965 << " already exists.";
966 err = DB_TABLESPACE_EXISTS;
967
968 } else if (error == OS_FILE_DISK_FULL) {
969 err = DB_OUT_OF_FILE_SPACE;
970
971 } else {
972 err = DB_ERROR;
973 }
974
975 /* file is not open, no need to close it. */
976 ut_free(link_filepath);
977 return(err);
978 }
979
980 ulint rbytes = fwrite(filepath, 1, strlen(filepath), file);
981
982 if (rbytes != strlen(filepath)) {
983 error = os_file_get_last_error(true);
984 ib::error() <<
985 "Cannot write link file: "
986 << link_filepath << " filepath: " << filepath;
987 err = DB_ERROR;
988 }
989
990 /* Close the file, we only need it at startup */
991 fclose(file);
992
993 ut_free(link_filepath);
994
995 return(err);
996}
997
998/** Delete an InnoDB Symbolic Link (ISL) file. */
999void
1000RemoteDatafile::delete_link_file(void)
1001{
1002 ut_ad(m_link_filepath != NULL);
1003
1004 if (m_link_filepath != NULL) {
1005 os_file_delete_if_exists(innodb_data_file_key,
1006 m_link_filepath, NULL);
1007 }
1008}
1009
1010/** Delete an InnoDB Symbolic Link (ISL) file by name.
1011@param[in] name tablespace name */
1012void
1013RemoteDatafile::delete_link_file(
1014 const char* name)
1015{
1016 char* link_filepath = fil_make_filepath(NULL, name, ISL, false);
1017
1018 if (link_filepath != NULL) {
1019 os_file_delete_if_exists(
1020 innodb_data_file_key, link_filepath, NULL);
1021
1022 ut_free(link_filepath);
1023 }
1024}
1025
1026/** Read an InnoDB Symbolic Link (ISL) file by name.
1027It is always created under the datadir of MySQL.
1028For file-per-table tablespaces, the isl file is expected to be
1029in a 'database' directory and called 'tablename.isl'.
1030The caller must free the memory returned if it is not null.
1031@param[in] link_filepath filepath of the ISL file
1032@return Filepath of the IBD file read from the ISL file */
1033char*
1034RemoteDatafile::read_link_file(
1035 const char* link_filepath)
1036{
1037 FILE* file = fopen(link_filepath, "r+b" STR_O_CLOEXEC);
1038 if (file == NULL) {
1039 return(NULL);
1040 }
1041
1042 char* filepath = static_cast<char*>(ut_malloc_nokey(OS_FILE_MAX_PATH));
1043
1044 os_file_read_string(file, filepath, OS_FILE_MAX_PATH);
1045 fclose(file);
1046
1047 if (filepath[0] != '\0') {
1048 /* Trim whitespace from end of filepath */
1049 ulint last_ch = strlen(filepath) - 1;
1050 while (last_ch > 4 && filepath[last_ch] <= 0x20) {
1051 filepath[last_ch--] = 0x00;
1052 }
1053 os_normalize_path(filepath);
1054 }
1055
1056 return(filepath);
1057}
1058