1/*****************************************************************************
2
3Copyright (c) 1995, 2019, Oracle and/or its affiliates. All Rights Reserved.
4
5This program is free software; you can redistribute it and/or modify it under
6the terms of the GNU General Public License, version 2.0, as published by the
7Free Software Foundation.
8
9This program is also distributed with certain software (including but not
10limited to OpenSSL) that is licensed under separate terms, as designated in a
11particular file or component or in included license documentation. The authors
12of MySQL hereby grant you an additional permission to link the program and
13your derivative works with the separately licensed software that they have
14included with MySQL.
15
16This program is distributed in the hope that it will be useful, but WITHOUT
17ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19for more details.
20
21You should have received a copy of the GNU General Public License along with
22this program; if not, write to the Free Software Foundation, Inc.,
2351 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24
25*****************************************************************************/
26
27/** @file include/fsp0fsp.ic
28 File space management
29
30 Created 12/18/1995 Heikki Tuuri
31 *******************************************************/
32
33/** Checks if a page address is an extent descriptor page address.
34@param[in]	page_id		page id
35@param[in]	page_size	page size
36@return true if a descriptor page */
37UNIV_INLINE
38ibool fsp_descr_page(const page_id_t &page_id, const page_size_t &page_size) {
39  return ((page_id.page_no() & (page_size.physical() - 1)) == FSP_XDES_OFFSET);
40}
41
42/** Determine if the tablespace is compressed from tablespace flags.
43@param[in]	flags	Tablespace flags
44@return true if compressed, false if not compressed */
45UNIV_INLINE
46bool fsp_flags_is_compressed(uint32_t flags) {
47  return (FSP_FLAGS_GET_ZIP_SSIZE(flags) != 0);
48}
49
50#define ACTUAL_SSIZE(ssize) (0 == ssize ? UNIV_PAGE_SSIZE_ORIG : ssize)
51
52/** Determine if two tablespaces are equivalent or compatible.
53@param[in]	flags1	First tablespace flags
54@param[in]	flags2	Second tablespace flags
55@return true the flags are compatible, false if not */
56UNIV_INLINE
57bool fsp_flags_are_equal(uint32_t flags1, uint32_t flags2) {
58  /* If either one of these flags is UINT32_UNDEFINED,
59  then they are not equal */
60  if (flags1 == UINT32_UNDEFINED || flags2 == UINT32_UNDEFINED) {
61    return (false);
62  }
63
64  if (!fsp_is_shared_tablespace(flags1) || !fsp_is_shared_tablespace(flags2)) {
65    /* At least one of these is a single-table tablespaces so all
66    flags must match. */
67    return (flags1 == flags2);
68  }
69
70  /* Both are shared tablespaces which can contain all formats.
71  But they must have the same logical and physical page size.
72  Once InnoDB can support multiple page sizes together,
73  the logical page size will not matter. */
74  ulint zip_ssize1 = ACTUAL_SSIZE(FSP_FLAGS_GET_ZIP_SSIZE(flags1));
75  ulint zip_ssize2 = ACTUAL_SSIZE(FSP_FLAGS_GET_ZIP_SSIZE(flags2));
76  ulint page_ssize1 = ACTUAL_SSIZE(FSP_FLAGS_GET_PAGE_SSIZE(flags1));
77  ulint page_ssize2 = ACTUAL_SSIZE(FSP_FLAGS_GET_PAGE_SSIZE(flags2));
78
79  return (zip_ssize1 == zip_ssize2 && page_ssize1 == page_ssize2);
80}
81
82/** Convert a page size, which is a power of 2, to an ssize, which is
83the number of bit shifts from 512 to make that page size.
84@param[in]	page_size	compressed page size in bytes
85@return an ssize created from the page size provided. */
86UNIV_INLINE
87uint32_t page_size_to_ssize(ulint page_size) {
88  uint32_t ssize;
89
90  for (ssize = UNIV_ZIP_SIZE_SHIFT_MIN;
91       static_cast<uint32_t>(1 << ssize) < page_size; ssize++) {
92  }
93
94  return (ssize - UNIV_ZIP_SIZE_SHIFT_MIN + 1);
95}
96
97/** Add the compressed page size to the tablespace flags.
98@param[in]	flags		Tablespace flags
99@param[in]	page_size	page sizes in bytes and compression flag.
100@return tablespace flags after zip size is added */
101UNIV_INLINE
102uint32_t fsp_flags_set_zip_size(uint32_t flags, const page_size_t &page_size) {
103  if (!page_size.is_compressed()) {
104    return (flags);
105  }
106
107  /* Zip size should be a power of 2 between UNIV_ZIP_SIZE_MIN
108  and UNIV_ZIP_SIZE_MAX */
109  ut_ad(page_size.physical() >= UNIV_ZIP_SIZE_MIN);
110  ut_ad(page_size.physical() <= UNIV_ZIP_SIZE_MAX);
111  ut_ad(ut_is_2pow(page_size.physical()));
112
113  uint32_t ssize = page_size_to_ssize(page_size.physical());
114
115  ut_ad(ssize > 0);
116  ut_ad(ssize <= UNIV_PAGE_SSIZE_MAX);
117
118  flags |= (ssize << FSP_FLAGS_POS_ZIP_SSIZE);
119
120  ut_ad(fsp_flags_is_valid(flags));
121
122  return (flags);
123}
124
125/** Add the page size to the tablespace flags.
126@param[in]	flags		Tablespace flags
127@param[in]	page_size	page sizes in bytes and compression flag.
128@return tablespace flags after page size is added */
129UNIV_INLINE
130uint32_t fsp_flags_set_page_size(uint32_t flags, const page_size_t &page_size) {
131  /* Page size should be a power of two between UNIV_PAGE_SIZE_MIN
132  and UNIV_PAGE_SIZE */
133  ut_ad(page_size.logical() >= UNIV_PAGE_SIZE_MIN);
134  ut_ad(page_size.logical() <= UNIV_PAGE_SIZE_MAX);
135  ut_ad(ut_is_2pow(page_size.logical()));
136
137  /* Remove this assert once we add support for different
138  page size per tablespace. Currently all tablespaces must
139  have a page size that is equal to innodb-page-size */
140  ut_ad(page_size.logical() == UNIV_PAGE_SIZE);
141
142  if (page_size.logical() == UNIV_PAGE_SIZE_ORIG) {
143    ut_ad(0 == FSP_FLAGS_GET_PAGE_SSIZE(flags));
144
145  } else {
146    uint32_t ssize = page_size_to_ssize(page_size.logical());
147
148    ut_ad(ssize);
149    ut_ad(ssize <= UNIV_PAGE_SSIZE_MAX);
150
151    flags |= (ssize << FSP_FLAGS_POS_PAGE_SSIZE);
152  }
153
154  ut_ad(fsp_flags_is_valid(flags));
155
156  return (flags);
157}
158
159/** Initialize an FSP flags integer.
160@param[in]	page_size	page sizes in bytes and compression flag.
161@param[in]	atomic_blobs	Used by Dynammic and Compressed.
162@param[in]	has_data_dir	This tablespace is in a remote location.
163@param[in]	is_shared	This tablespace can be shared by many tables.
164@param[in]	is_temporary	This tablespace is temporary.
165@param[in]	is_encrypted	This tablespace is encrypted.
166@return tablespace flags after initialization */
167UNIV_INLINE
168uint32_t fsp_flags_init(const page_size_t &page_size, bool atomic_blobs,
169                        bool has_data_dir, bool is_shared, bool is_temporary,
170                        bool is_encrypted) {
171  ut_ad(page_size.physical() <= page_size.logical());
172  ut_ad(!page_size.is_compressed() || atomic_blobs);
173
174  /* Page size should be a power of two between UNIV_PAGE_SIZE_MIN
175  and UNIV_PAGE_SIZE, but zip_size may be 0 if not compressed. */
176  uint32_t flags = fsp_flags_set_page_size(0, page_size);
177
178  if (atomic_blobs) {
179    flags |= FSP_FLAGS_MASK_POST_ANTELOPE | FSP_FLAGS_MASK_ATOMIC_BLOBS;
180  }
181
182  /* If the zip_size is explicit and different from the default,
183  compressed row format is implied. */
184  flags = fsp_flags_set_zip_size(flags, page_size);
185
186  if (has_data_dir) {
187    flags |= FSP_FLAGS_MASK_DATA_DIR;
188  }
189
190  /* Shared tablespaces can hold all row formats, so we only mark the
191  POST_ANTELOPE and ATOMIC_BLOB bits if it is compressed. */
192  if (is_shared) {
193    ut_ad(!has_data_dir);
194    flags |= FSP_FLAGS_MASK_SHARED;
195  }
196
197  if (is_temporary) {
198    ut_ad(!has_data_dir);
199    flags |= FSP_FLAGS_MASK_TEMPORARY;
200  }
201
202  if (is_encrypted) {
203    flags |= FSP_FLAGS_MASK_ENCRYPTION;
204  }
205
206  return (flags);
207}
208
209/** Calculates the descriptor index within a descriptor page.
210@param[in]	page_size	page size
211@param[in]	offset		page offset
212@return descriptor index */
213UNIV_INLINE
214ulint xdes_calc_descriptor_index(const page_size_t &page_size, ulint offset) {
215  return (ut_2pow_remainder(offset, page_size.physical()) / FSP_EXTENT_SIZE);
216}
217
218/** Gets a descriptor bit of a page.
219 @return true if free */
220UNIV_INLINE
221ibool xdes_get_bit(const xdes_t *descr, /*!< in: descriptor */
222                   ulint bit,        /*!< in: XDES_FREE_BIT or XDES_CLEAN_BIT */
223                   page_no_t offset) /*!< in: page offset within extent:
224                                     0 ... FSP_EXTENT_SIZE - 1 */
225{
226  ut_ad(offset < FSP_EXTENT_SIZE);
227  ut_ad(bit == XDES_FREE_BIT || bit == XDES_CLEAN_BIT);
228
229  ulint index = bit + XDES_BITS_PER_PAGE * offset;
230
231  ulint bit_index = index % 8;
232  ulint byte_index = index / 8;
233
234  return (ut_bit_get_nth(
235      mach_read_ulint(descr + XDES_BITMAP + byte_index, MLOG_1BYTE),
236      bit_index));
237}
238
239/** Calculates the page where the descriptor of a page resides.
240@param[in]	page_size	page size
241@param[in]	offset		page offset
242@return descriptor page offset */
243UNIV_INLINE
244page_no_t xdes_calc_descriptor_page(const page_size_t &page_size,
245                                    page_no_t offset) {
246  static_assert(UNIV_PAGE_SIZE_MAX > XDES_ARR_OFFSET + (UNIV_PAGE_SIZE_MAX /
247                                                        FSP_EXTENT_SIZE_MAX) *
248                                                           XDES_SIZE_MAX,
249                "Extent descriptor won't fit on a page");
250
251  static_assert(UNIV_ZIP_SIZE_MIN > XDES_ARR_OFFSET + (UNIV_ZIP_SIZE_MIN /
252                                                       FSP_EXTENT_SIZE_MIN) *
253                                                          XDES_SIZE_MIN,
254                "Extent descriptor won't fit on a page");
255
256  ut_ad(UNIV_PAGE_SIZE >
257        XDES_ARR_OFFSET + (UNIV_PAGE_SIZE / FSP_EXTENT_SIZE) * XDES_SIZE);
258  ut_ad(UNIV_ZIP_SIZE_MIN >
259        XDES_ARR_OFFSET + (UNIV_ZIP_SIZE_MIN / FSP_EXTENT_SIZE) * XDES_SIZE);
260
261#ifdef UNIV_DEBUG
262  if (page_size.is_compressed()) {
263    ut_a(page_size.physical() >
264         XDES_ARR_OFFSET +
265             (page_size.physical() / FSP_EXTENT_SIZE) * XDES_SIZE);
266  }
267#endif /* UNIV_DEBUG */
268
269  return (ut_2pow_round(offset, page_size.physical()));
270}
271
272/** Calculates the descriptor array size.
273@param[in]	page_size	page size
274@return size of descriptor array */
275UNIV_INLINE
276ulint xdes_arr_size(const page_size_t &page_size) {
277  return (page_size.physical() / FSP_EXTENT_SIZE);
278}
279
280/** Check if a specified page is inode page or not. This is used for
281index root pages of hard-coded DD tables, we can safely assume that the passed
282in page number is in the range of pages which are only either index root page
283or inode page
284@param[in]	page	Page number to check
285@return true if it's inode page, otherwise false */
286inline bool fsp_is_inode_page(page_no_t page) {
287  static const uint inode_per_page = FSP_SEG_INODES_PER_PAGE(univ_page_size);
288
289  /* Every two inode would be allocated for one index, and all inodes in
290  two inode pages are needed exactly for FSP_SEG_INODES_PER_PAGE indexes.
291  One inode page is at the beginning of one cycle, the other is in the
292  middle. If FSP_SEG_INODES_PER_PAGE is even, the second inode page
293  is at cycle / 2, if odd, it should be (cycle + 1) / 2 in the cycle. */
294  static const uint cycle = inode_per_page + 2;
295
296  /* Number of all hard-coded DD table indexes. Please sync it with
297  innodb_dd_table array. */
298  static const uint indexes = 98;
299
300  /* Max page number for index root pages of hard-coded DD tables. */
301  static const uint max_page_no =
302      FSP_FIRST_INODE_PAGE_NO + 1 /* SDI Index page */
303      + (indexes / inode_per_page) * cycle + (indexes % inode_per_page) +
304      ((indexes % inode_per_page) / ((inode_per_page + 1) / 2));
305
306  /* The page range should be determinate for different page sizes. */
307  ut_a(page >= FSP_FIRST_INODE_PAGE_NO);
308  ut_a(page <= max_page_no);
309
310  uint step = (page - FSP_FIRST_INODE_PAGE_NO) % cycle;
311
312  return (step == 0 || step == (cycle + 1) / 2);
313}
314
315/** Validate the tablespace flags.
316These flags are stored in the tablespace header at offset FSP_SPACE_FLAGS.
317They should be 0 for ROW_FORMAT=COMPACT and ROW_FORMAT=REDUNDANT.
318The newer row formats, COMPRESSED and DYNAMIC, will have at least
319the DICT_TF_COMPACT bit set.
320@param[in]	flags	Tablespace flags
321@return true if valid, false if not */
322inline bool fsp_flags_is_valid(uint32_t flags) {
323  bool post_antelope = FSP_FLAGS_GET_POST_ANTELOPE(flags);
324  ulint zip_ssize = FSP_FLAGS_GET_ZIP_SSIZE(flags);
325  bool atomic_blobs = FSP_FLAGS_HAS_ATOMIC_BLOBS(flags);
326  ulint page_ssize = FSP_FLAGS_GET_PAGE_SSIZE(flags);
327  bool has_data_dir = FSP_FLAGS_HAS_DATA_DIR(flags);
328  bool is_shared = FSP_FLAGS_GET_SHARED(flags);
329  bool is_temp = FSP_FLAGS_GET_TEMPORARY(flags);
330
331  ulint unused = FSP_FLAGS_GET_UNUSED(flags);
332
333  DBUG_EXECUTE_IF("fsp_flags_is_valid_failure", return (false););
334
335  /* The Antelope row formats REDUNDANT and COMPACT did
336  not use tablespace flags, so the entire 4-byte field
337  is zero for Antelope row formats. */
338  if (flags == 0) {
339    return (true);
340  }
341
342  /* Row_FORMAT=COMPRESSED and ROW_FORMAT=DYNAMIC use a feature called
343  ATOMIC_BLOBS which builds on the page structure introduced for the
344  COMPACT row format by allowing long fields to be broken into prefix
345  and externally stored parts. So if it is Post_antelope, it uses
346  Atomic BLOBs. */
347  if (post_antelope != atomic_blobs) {
348    return (false);
349  }
350
351  /* Make sure there are no bits that we do not know about. */
352  if (unused != 0) {
353    return (false);
354  }
355
356  /* The zip ssize can be zero if it is other than compressed row format,
357  or it could be from 1 to the max. */
358  if (zip_ssize > PAGE_ZIP_SSIZE_MAX) {
359    return (false);
360  }
361
362  /* The actual page size must be within 4k and 64K (3 =< ssize =< 7). */
363  if (page_ssize != 0 &&
364      (page_ssize < UNIV_PAGE_SSIZE_MIN || page_ssize > UNIV_PAGE_SSIZE_MAX)) {
365    return (false);
366  }
367
368  /* Only single-table tablespaces use the DATA DIRECTORY clause.
369  It is not compatible with the TABLESPACE clause.  Nor is it
370  compatible with the TEMPORARY clause. */
371  if (has_data_dir && (is_shared || is_temp)) {
372    return (false);
373  }
374
375#if FSP_FLAGS_POS_UNUSED != 15
376#error You have added a new FSP_FLAG without adding a validation check.
377#endif
378
379  return (true);
380}
381
382/** Get the offset of SDI root page number in page 0.
383@param[in]	page_size	page size.
384@return	offset on success, otherwise 0. */
385inline ulint fsp_header_get_sdi_offset(const page_size_t &page_size) {
386  ulint offset;
387#ifdef UNIV_DEBUG
388  ulint left_size;
389#endif
390
391  offset = XDES_ARR_OFFSET + XDES_SIZE * xdes_arr_size(page_size) +
392           Encryption::INFO_MAX_SIZE;
393#ifdef UNIV_DEBUG
394  left_size =
395      page_size.physical() - FSP_HEADER_OFFSET - offset - FIL_PAGE_DATA_END;
396
397  ut_ad(left_size >= FSP_SDI_HEADER_LEN);
398#endif
399  return (offset);
400}
401
402/** Get the offset of encrytion progress information in page 0.
403@param[in]      page_size       page size.
404@return offset on success, otherwise 0. */
405inline ulint fsp_header_get_encryption_progress_offset(
406    const page_size_t &page_size) {
407  ulint offset;
408#ifdef UNIV_DEBUG
409  ulint left_size;
410#endif
411
412  offset = fsp_header_get_sdi_offset(page_size) + FSP_SDI_HEADER_LEN;
413#ifdef UNIV_DEBUG
414  left_size =
415      page_size.physical() - FSP_HEADER_OFFSET - offset - FIL_PAGE_DATA_END;
416
417  ut_ad(left_size >=
418        Encryption::OPERATION_INFO_SIZE + Encryption::PROGRESS_INFO_SIZE);
419#endif
420
421  return (offset);
422}
423
424/** Reads the server space version from the first page of a tablespace.
425@param[in]      page            first page of a tablespace
426@return space server version */
427UNIV_INLINE
428uint32 fsp_header_get_server_version(const page_t *page) {
429  uint32 version;
430
431  version = mach_read_from_4(page + FIL_PAGE_SRV_VERSION);
432
433  return (version);
434}
435
436/** Reads the server space version from the first page of a tablespace.
437@param[in]      page            first page of a tablespace
438@return space server version */
439UNIV_INLINE
440uint32 fsp_header_get_space_version(const page_t *page) {
441  uint32 version;
442
443  version = mach_read_from_4(page + FIL_PAGE_SPACE_VERSION);
444
445  return (version);
446}
447