CUBRID Engine  latest
disk_manager.c
Go to the documentation of this file.
1 /*
2  * Copyright 2008 Search Solution Corporation
3  * Copyright 2016 CUBRID Corporation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 /*
20  * disk_manager.c - Disk managment module (at server)
21  */
22 
23 #ident "$Id$"
24 
25 #include "config.h"
26 
27 #include <stdlib.h>
28 #include <stddef.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <errno.h>
32 
33 #if !defined (WINDOWS)
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #endif /* !WINDOWS */
38 
39 #include "disk_manager.h"
40 
41 #include "porting.h"
42 #include "porting_inline.hpp"
43 #include "system_parameter.h"
44 #include "error_manager.h"
45 #include "language_support.h"
46 #include "intl_support.h"
47 #include "xserver_interface.h"
48 #include "file_io.h"
49 #include "page_buffer.h"
50 #include "log_append.hpp"
51 #include "log_manager.h"
52 #include "log_lsa.hpp"
53 #include "log_volids.hpp"
54 #include "critical_section.h"
55 #include "boot_sr.h"
56 #include "tz_support.h"
57 #include "db_date.h"
58 #include "bit.h"
59 #include "fault_injection.h"
60 #include "vacuum.h"
61 #include "dbtype.h"
62 #include "thread_daemon.hpp"
63 #include "thread_entry_task.hpp"
64 #include "thread_manager.hpp"
65 #include "double_write_buffer.h"
66 
67 /************************************************************************/
68 /* Define structures, globals, and macro's */
69 /************************************************************************/
70 
71 /* DON'T USE sizeof on this structure.. size if variable */
74 { /* Volume header */
75  /* DON'T MOVE THE MAGIC FIELD. IT IS USED BY FILEC */
76  char magic[CUBRID_MAGIC_MAX_LENGTH]; /* Magic value for file/magic Unix utility */
77  INT16 iopagesize; /* This was only added for checking purposes. The actual value is stored on the log */
78  INT16 volid; /* Volume identifier */
79  INT8 db_charset; /* charset of database */
80  INT8 dummy1; /* Dummy fields for alignment */
81  DB_VOLPURPOSE purpose; /* Permanent or temporary volume purpose */
82  DB_VOLTYPE type; /* Permanent or temporary volume type */
83  DKNPAGES sect_npgs; /* Size of sector in pages */
84  DKNSECTS nsect_total; /* Total number of sectors */
85  DKNSECTS nsect_max; /* Maximum number of sectors */
86  SECTID hint_allocsect; /* Hint for next sector to be allocated */
87  DKNPAGES stab_npages; /* Size of sector allocation table in pages */
88  PAGEID stab_first_page; /* First page of sector allocation table */
89  PAGEID sys_lastpage; /* Last system page */
90  INT32 dummy2; /* Dummy fields for alignment */
91  INT64 db_creation; /* Database creation time. For safety reasons, this value is set on all volumes and the
92  * log. The value is generated by the log manager */
93  LOG_LSA chkpt_lsa; /* Lowest log sequence address to start the recovery process of this volume */
94  HFID boot_hfid; /* System Heap file for booting purposes and multi volumes */
95  INT32 reserved0; /* reserved area */
96  INT32 reserved1; /* reserved area */
97  INT32 reserved2; /* reserved area */
98  INT32 reserved3; /* reserved area */
99  INT16 next_volid; /* next volume identifier * */
100  INT16 offset_to_vol_fullname; /* Offset to vol_fullname */
101  INT16 offset_to_next_vol_fullname; /* Offset to next vol_fullname */
102  INT16 offset_to_vol_remarks; /* Offset to vol_remarks */
103 
104  char var_fields[1]; /* Variable length fields addresses by the offset Current ordering is: 1) vol_fullname,
105  * 2) next_vol_fullname 3) volume remarks The length is DB_PAGESIZE - offset of
106  * var_fields */
107 
108 };
109 
112 { /* Recovery for links */
113  INT16 next_volid;
114  char next_vol_fullname[1]; /* Actually more than one */
115 };
116 
119 { /* Recovery for changes */
120  INT64 db_creation;
122  char vol_fullname[1]; /* Actually more than one */
123 };
124 
125 /* recovery data for volume expand */
128 {
130  INT16 volid;
131 };
132 
133 /* show volume header context structure */
136 {
137  int volume_id; /* volume id */
138 };
139 
142 {
143  VOLID volid; /* volume id be found */
144  bool exists; /* whether volid does exist */
145 };
146 
147 /************************************************************************/
148 /* Disk cache section */
149 /************************************************************************/
150 
153 {
155  DKNSECTS nsect_free; /* Hint of free sectors on volume */
156 };
157 
160 {
163  volatile DKNSECTS nsect_max;
165 
166  pthread_mutex_t mutex_reserve;
167 #if !defined (NDEBUG)
168  volatile int owner_reserve;
169 #endif /* !NDEBUG */
170 
174 };
175 
178 {
180 };
181 
184 {
188 };
189 
190 typedef struct disk_cache DISK_CACHE;
192 {
193  int nvols_perm; /* number of permanent type volumes */
194  int nvols_temp; /* number of temporary type volumes */
195  DISK_CACHE_VOLINFO vols[LOG_MAX_DBVOLID + 1]; /* volume info array */
196 
197  DISK_PERM_PURPOSE_INFO perm_purpose_info; /* info for permanent purpose */
198  DISK_TEMP_PURPOSE_INFO temp_purpose_info; /* info for temporary purpose */
199 
200  pthread_mutex_t mutex_extend; /* note: never get expand mutex while keeping reserve mutexes */
201 #if !defined (NDEBUG)
202  volatile int owner_extend;
203 #endif /* !NDEBUG */
204 };
205 
207 
209 
210 /************************************************************************/
211 /* Disk allocation table section */
212 /************************************************************************/
213 
214 /* Disk allocation table is a bitmap that keeps track of reserved sectors. When files are created or extended, they
215  * reserve a number of sectors from disk (each sector containing a predefined number of pages). */
216 
217 /* The default unit used to divide a page of allocation table. Currently it is a char (8 bit).
218  * If we ever want to change the type of unit, this can be modified and should be handled automatically. However, some
219  * other structures may need updating (e.g. DISK_ALLOCTBL_CURSOR).
220  */
221 typedef UINT64 DISK_STAB_UNIT;
222 #define DISK_STAB_UNIT_SIZE_OF sizeof (DISK_STAB_UNIT)
223 
224 /* Disk allocation table cursor. Used to iterate through table bits. */
227 {
228  const DISK_VOLUME_HEADER *volheader; /* Volume header */
229 
230  PAGEID pageid; /* Current page ID */
231  int offset_to_unit; /* Offset to current unit in page. */
232  int offset_to_bit; /* Offset to current bit in unit. */
233 
234  SECTID sectid; /* Sector ID */
235 
236  PAGE_PTR page; /* Fixed table page. */
237  DISK_STAB_UNIT *unit; /* Unit pointer in current page. */
238 };
239 #define DISK_STAB_CURSOR_INITIALIZER { NULL, 0, 0, 0, 0, NULL, NULL }
240 
241 /* Allocation table macro's */
242 /* Bit count in a unit */
243 #define DISK_STAB_UNIT_BIT_COUNT ((int) (DISK_STAB_UNIT_SIZE_OF * CHAR_BIT))
244 /* Unit count in a table page */
245 #define DISK_STAB_PAGE_UNITS_COUNT ((int) (DB_PAGESIZE / DISK_STAB_UNIT_SIZE_OF))
246 /* Bit count in a table page */
247 #define DISK_STAB_PAGE_BIT_COUNT ((int) (DISK_STAB_UNIT_BIT_COUNT * DISK_STAB_PAGE_UNITS_COUNT))
248 
249 /* Get page offset for sector ID. Note this is not the real page ID (since table does not start from page 0). */
250 #define DISK_ALLOCTBL_SECTOR_PAGE_OFFSET(sect) ((sect) / DISK_STAB_PAGE_BIT_COUNT)
251 /* Get unit offset in page for sector ID. */
252 #define DISK_ALLOCTBL_SECTOR_UNIT_OFFSET(sect) (((sect) % DISK_STAB_PAGE_BIT_COUNT) / DISK_STAB_UNIT_BIT_COUNT)
253 /* Get bit offset in unit for sector ID */
254 #define DISK_ALLOCTBL_SECTOR_BIT_OFFSET(sect) (((sect) % DISK_STAB_PAGE_BIT_COUNT) % DISK_STAB_UNIT_BIT_COUNT)
255 
256 #define DISK_SECTS_ROUND_UP(nsects) (CEIL_PTVDIV (nsects, DISK_STAB_UNIT_BIT_COUNT) * DISK_STAB_UNIT_BIT_COUNT)
257 #define DISK_SECTS_ROUND_DOWN(nsects) ((nsects / DISK_STAB_UNIT_BIT_COUNT) * DISK_STAB_UNIT_BIT_COUNT)
258 #define DISK_SECTS_ASSERT_ROUNDED(nsects) assert (nsects == DISK_SECTS_ROUND_DOWN (nsects))
259 
260 #define DISK_STAB_NPAGES(nsect_max) (CEIL_PTVDIV (nsect_max, DISK_STAB_PAGE_BIT_COUNT))
261 
262 /* function used by disk_stab_iterate_units */
263 typedef int (*DISK_STAB_UNIT_FUNC) (THREAD_ENTRY * thread_p, DISK_STAB_CURSOR * cursor, bool * stop, void *args);
264 
265 /************************************************************************/
266 /* Sector reserve section */
267 /************************************************************************/
268 
271 {
274 };
275 #define DISK_PRERESERVE_BUF_DEFAULT 16
276 
279 {
282 
283  DISK_CACHE_VOL_RESERVE cache_vol_reserve[VOLID_MAX];
286 
288 
290 };
291 
292 /************************************************************************/
293 /* Disk create, extend, destroy section */
294 /************************************************************************/
295 
296 /* minimum volume sectors for create/expand... at least one allocation table unit */
297 #define DISK_MIN_VOLUME_SECTS DISK_STAB_UNIT_BIT_COUNT
298 
299 /* when allocating remaining disk space, leave out 64MB for safety */
300 #define DISK_SAFE_OSDISK_FREE_SPACE (64 * 1024 * 1024)
301 #define DISK_SAFE_OSDISK_FREE_SECTS (DISK_SAFE_OSDISK_FREE_SPACE / IO_SECTORSIZE)
302 
303 /************************************************************************/
304 /* Utility section */
305 /************************************************************************/
306 
307 /* logging */
308 static bool disk_Logging = false;
309 #define disk_log(func, msg, ...) \
310  if (disk_Logging) \
311  _er_log_debug (ARG_FILE_LINE, "DISK " func " " LOG_THREAD_TRAN_MSG ": \n\t" msg "\n", \
312  LOG_THREAD_TRAN_ARGS (thread_get_thread_entry_info ()), __VA_ARGS__)
313 
314 #define DISK_VOLHEADER_MSG \
315  "\t\tvolume header: \n" \
316  "\t\t\tvolid = %d\n" \
317  "\t\t\ttype = %s\n" \
318  "\t\t\tpurpose = %s\n" \
319  "\t\t\tsectors: total = %d, max = %d\n" \
320  "\t\t\thint_allocset = %d\n" \
321  "\t\t\tsector allocation table: start = %d, num pages = %d\n" \
322  "\t\t\tchkpt_lsa = %lld|%d\n" \
323  "\t\t\tnext_volid = %d\n"
324 #define DISK_VOLHEADER_AS_ARGS(arg_volh) \
325  (arg_volh)->volid, \
326  disk_type_to_string ((arg_volh)->type), \
327  disk_purpose_to_string ((arg_volh)->purpose), \
328  (arg_volh)->nsect_total, (arg_volh)->nsect_max, \
329  (arg_volh)->hint_allocsect, \
330  (arg_volh)->stab_first_page, (arg_volh)->stab_npages, \
331  LSA_AS_ARGS (&(arg_volh)->chkpt_lsa), \
332  (arg_volh)->next_volid
333 
334 #define DISK_RESERVE_CONTEXT_MSG \
335  "\t\tcontext: \n" \
336  "\t\t\tnsect_total = %d\n" \
337  "\t\t\tn_cache_vol_reserve = %d, n_cache_reserve_remaining = %d\n" \
338  "\t\t\tpurpose = %s"
339 #define DISK_RESERVE_CONTEXT_AS_ARGS(context) \
340  (context)->nsect_total, (context)->n_cache_vol_reserve, (context)->n_cache_reserve_remaining, \
341  disk_purpose_to_string ((context)->purpose)
342 
343 #define DISK_SYS_NPAGE_SIZE(nsect_max) (1 + DISK_STAB_NPAGES (nsect_max))
344 #define DISK_SYS_NSECT_SIZE(nsect_max) (CEIL_PTVDIV (DISK_SYS_NPAGE_SIZE (nsect_max), DISK_SECTOR_NPAGES))
345 
346 /************************************************************************/
347 /* Declare static functions. */
348 /************************************************************************/
349 
354 static int disk_vhdr_set_vol_fullname (DISK_VOLUME_HEADER * vhdr, const char *vol_fullname);
355 static int disk_vhdr_set_next_vol_fullname (DISK_VOLUME_HEADER * vhdr, const char *next_vol_fullname);
356 static int disk_vhdr_set_vol_remarks (DISK_VOLUME_HEADER * vhdr, const char *vol_remarks);
357 
358 static bool disk_cache_load_all_volumes (THREAD_ENTRY * thread_p);
359 static bool disk_cache_load_volume (THREAD_ENTRY * thread_p, INT16 volid, void *ignore);
360 
361 static const char *disk_purpose_to_string (DISK_VOLPURPOSE purpose);
362 static const char *disk_type_to_string (DB_VOLTYPE voltype);
363 
364 static int disk_stab_dump (THREAD_ENTRY * thread_p, FILE * fp, const DISK_VOLUME_HEADER * volheader);
365 
366 static int disk_dump_volume_system_info (THREAD_ENTRY * thread_p, FILE * fp, INT16 volid);
367 static bool disk_dump_goodvol_all (THREAD_ENTRY * thread_p, INT16 volid, void *ignore);
368 static void disk_vhdr_dump (FILE * fp, const DISK_VOLUME_HEADER * vhdr);
369 
371 static bool disk_check_volume_exist (THREAD_ENTRY * thread_p, VOLID volid, void *arg);
372 static int disk_can_overwrite_data_volume (THREAD_ENTRY * thread_p, const char *vol_label_p, bool * can_overwrite);
373 
374 /************************************************************************/
375 /* Disk sector table section */
376 /************************************************************************/
377 
385  const DISK_STAB_CURSOR * second_cursor) __attribute__ ((ALWAYS_INLINE));
397 static int disk_stab_unit_reserve (THREAD_ENTRY * thread_p, DISK_STAB_CURSOR * cursor, bool * stop, void *args);
398 
399 static int disk_stab_iterate_units (THREAD_ENTRY * thread_p, const DISK_VOLUME_HEADER * volheader,
401  DISK_STAB_UNIT_FUNC f_unit, void *f_unit_args);
402 static int disk_stab_iterate_units_all (THREAD_ENTRY * thread_p, const DISK_VOLUME_HEADER * volheader,
403  PGBUF_LATCH_MODE mode, DISK_STAB_UNIT_FUNC f_unit, void *f_unit_args);
404 static int disk_stab_dump_unit (THREAD_ENTRY * thread_p, DISK_STAB_CURSOR * cursor, bool * stop, void *args);
405 static int disk_stab_count_free (THREAD_ENTRY * thread_p, DISK_STAB_CURSOR * cursor, bool * stop, void *args);
406 static int disk_stab_set_bits_contiguous (THREAD_ENTRY * thread_p, DISK_STAB_CURSOR * cursor, bool * stop, void *args);
407 
408 /************************************************************************/
409 /* Disk cache section */
410 /************************************************************************/
411 
412 static int disk_volume_boot (THREAD_ENTRY * thread_p, VOLID volid, DB_VOLPURPOSE * purpose_out,
413  DB_VOLTYPE * voltype_out, DISK_VOLUME_SPACE_INFO * space_out);
419 
420 /************************************************************************/
421 /* Sector reserve section */
422 /************************************************************************/
423 
424 static int disk_reserve_sectors_in_volume (THREAD_ENTRY * thread_p, int vol_index, DISK_RESERVE_CONTEXT * context);
425 static DISK_ISVALID disk_is_sector_reserved (THREAD_ENTRY * thread_p, const DISK_VOLUME_HEADER * volheader,
426  SECTID sectid, bool debug_crash);
427 static int disk_reserve_from_cache (THREAD_ENTRY * thread_p, DISK_RESERVE_CONTEXT * context, bool * did_extend);
430 static int disk_extend (THREAD_ENTRY * thread_p, DISK_EXTEND_INFO * expand_info,
431  DISK_RESERVE_CONTEXT * reserve_context);
432 static int disk_volume_expand (THREAD_ENTRY * thread_p, VOLID volid, DB_VOLTYPE voltype, DKNSECTS nsect_extend,
433  DKNSECTS * nsect_extended_out);
434 static int disk_add_volume (THREAD_ENTRY * thread_p, DBDEF_VOL_EXT_INFO * extinfo, VOLID * volid_out,
435  DKNSECTS * nsects_free_out);
440  VSID * vsids);
442 static int disk_stab_unit_unreserve (THREAD_ENTRY * thread_p, DISK_STAB_CURSOR * cursor, bool * stop, void *args);
444  DISK_RESERVE_CONTEXT * context);
445 static int disk_stab_unit_check_reserved (THREAD_ENTRY * thread_p, DISK_STAB_CURSOR * cursor, bool * stop, void *args);
446 
447 /************************************************************************/
448 /* Other */
449 /************************************************************************/
450 
451 static int disk_stab_init (THREAD_ENTRY * thread_p, DISK_VOLUME_HEADER * volheader);
454 
455 static int disk_format (THREAD_ENTRY * thread_p, const char *dbname, INT16 volid, DBDEF_VOL_EXT_INFO * ext_info,
456  DKNSECTS * nsect_free_out);
457 
459  PAGE_PTR * page_volheader_out, DISK_VOLUME_HEADER ** volheader_out
460 #if !defined (NDEBUG)
461  , const char *file, int line
462 #endif /* !NDEBUG */
464 #if defined (NDEBUG)
465 #define disk_get_volheader disk_get_volheader_internal
466 #else /* !NDEBUG */
467 #define disk_get_volheader(...) disk_get_volheader_internal (__VA_ARGS__, ARG_FILE_LINE)
468 #endif /* !NDEBUG */
469 
470 static int disk_cache_init (void);
471 static void disk_cache_final (void);
478 static DISK_ISVALID disk_check_volume (THREAD_ENTRY * thread_p, INT16 volid, bool repair);
479 
480 // *INDENT-OFF*
482 
485 // *INDENT-ON*
486 
487 /************************************************************************/
488 /* End of static functions */
489 /************************************************************************/
490 
491 /************************************************************************/
492 /* Define functions. */
493 /************************************************************************/
494 
495 /************************************************************************/
496 /* Disk volume manipulation */
497 /************************************************************************/
498 
499 /*
500  * disk_format () - format new volume
501  *
502  * return : error code
503  * thread_p (in) : thread entry
504  * dbname (in) : database name
505  * volid (in) : new volume identifier
506  * ext_info (in) : all extension info
507  * nsect_free_out (out) : output number of free sectors in new volume
508  */
509 static int
510 disk_format (THREAD_ENTRY * thread_p, const char *dbname, VOLID volid, DBDEF_VOL_EXT_INFO * ext_info,
511  DKNSECTS * nsect_free_out)
512 {
513 #if !defined (NDEBUG)
514  /* inject faults to test recovery */
515 #define fault_inject_random_crash() \
516  if (vol_purpose == DB_PERMANENT_DATA_PURPOSE) FI_TEST (thread_p, FI_TEST_DISK_MANAGER_VOLUME_ADD, 0)
517 #else /* RELEASE */
518 #define fault_inject_random_crash()
519 #endif /* RELEASE */
520 
521  int vdes; /* Volume descriptor */
522  DISK_VOLUME_HEADER *vhdr; /* Pointer to volume header */
523  VPID vpid; /* Volume and page identifiers */
524  LOG_DATA_ADDR addr; /* Address of logging data */
525  const char *vol_fullname = ext_info->name;
526  DKNSECTS max_npages = DISK_SECTS_NPAGES (ext_info->nsect_max);
527  int kbytes_to_be_written_per_sec = ext_info->max_writesize_in_sec;
528  DISK_VOLPURPOSE vol_purpose = ext_info->purpose;
529  DKNPAGES extend_npages = DISK_SECTS_NPAGES (ext_info->nsect_total);
530  INT16 prev_volid;
531  int error_code = NO_ERROR;
532 
533  assert ((int) sizeof (DISK_VOLUME_HEADER) <= DB_PAGESIZE);
534 
535  addr.vfid = NULL;
536 
537  if ((strlen (vol_fullname) + 1 > DB_MAX_PATH_LENGTH)
538  || (DB_PAGESIZE < (PGLENGTH) (SSIZEOF (DISK_VOLUME_HEADER) + strlen (vol_fullname) + 1)))
539  {
541  strlen (vol_fullname) + 1, DB_MAX_PATH_LENGTH);
543  }
544 
545  /* make sure that this is a valid purpose */
546  if (vol_purpose != DB_PERMANENT_DATA_PURPOSE && vol_purpose != DB_TEMPORARY_DATA_PURPOSE)
547  {
548  assert (false);
552  }
553 
554  /* safe guard: permanent volumes with temporary data purpose must be maxed */
555  assert (vol_purpose != DB_TEMPORARY_DATA_PURPOSE || ext_info->voltype != DB_PERMANENT_VOLTYPE
556  || ext_info->nsect_total == ext_info->nsect_max);
557 
558  /* undo must be logical since we are going to remove the volume in the case of rollback (really a crash since we are
559  * in a top operation) */
560  addr.offset = (PGLENGTH) volid;
561  addr.pgptr = NULL;
562  if (ext_info->voltype == DB_PERMANENT_VOLTYPE)
563  {
564  log_append_undo_data (thread_p, RVDK_FORMAT, &addr, (int) strlen (vol_fullname) + 1, vol_fullname);
565  }
567 
568  /* this log must be flushed. */
569  logpb_force_flush_pages (thread_p);
571 
572  /* create and initialize the volume. recovery information is initialized in every page. */
573  vdes = fileio_format (thread_p, dbname, vol_fullname, volid, extend_npages, vol_purpose == DB_PERMANENT_DATA_PURPOSE,
574  false, false, IO_PAGESIZE, kbytes_to_be_written_per_sec, false);
575  if (vdes == NULL_VOLDES)
576  {
577  ASSERT_ERROR_AND_SET (error_code);
578  return error_code;
579  }
580  /* from now on, if error occurs, we need to go to exit */
582 
583  /* initialize the volume header and the sector and page allocation tables */
584  vpid.volid = volid;
586 
587  /* lock the volume header in exclusive mode and then fetch the page. */
589  if (addr.pgptr == NULL)
590  {
591  ASSERT_ERROR_AND_SET (error_code);
592  goto exit;
593  }
594  (void) pgbuf_set_page_ptype (thread_p, addr.pgptr, PAGE_VOLHEADER);
595 
596  /* initialize the header */
597  vhdr = (DISK_VOLUME_HEADER *) addr.pgptr;
598 
600  vhdr->iopagesize = IO_PAGESIZE;
601  vhdr->volid = volid;
602  vhdr->purpose = vol_purpose;
603  vhdr->type = ext_info->voltype;
605  vhdr->nsect_total = ext_info->nsect_total;
606  vhdr->nsect_max = ext_info->nsect_max;
607  vhdr->db_charset = lang_charset ();
608  vhdr->hint_allocsect = NULL_SECTID;
609  vhdr->dummy1 = vhdr->dummy2 = 0; /* alignment. You may use it. */
610  vhdr->reserved0 = vhdr->reserved1 = vhdr->reserved2 = vhdr->reserved3 = 0; /* for future extension */
611 
612  /* set sector table info in volume header */
613  disk_volume_header_set_stab (vol_purpose, vhdr);
614  if (vhdr->sys_lastpage >= extend_npages)
615  {
616  assert (false);
617  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_FORMAT_BAD_NPAGES, 2, vol_fullname, extend_npages);
618  error_code = ER_IO_FORMAT_BAD_NPAGES;
619  goto exit;
620  }
621 
622  /* Find the time of the creation of the database and the current LSA checkpoint. */
623 
624  error_code = log_get_db_start_parameters (&vhdr->db_creation, &vhdr->chkpt_lsa);
625  if (error_code != NO_ERROR)
626  {
627  ASSERT_ERROR ();
628  goto exit;
629  }
630 
631  /* Initialize the system heap file for booting purposes. This field is reseted after the heap file is created by the
632  * boot manager */
633 
634  vhdr->boot_hfid.vfid.volid = NULL_VOLID;
636  vhdr->boot_hfid.hpgid = NULL_PAGEID;
637 
638  /* Initialize variable length fields */
639 
640  vhdr->next_volid = NULL_VOLID;
642  vhdr->var_fields[vhdr->offset_to_vol_fullname] = '\0';
643  error_code = disk_vhdr_set_vol_fullname (vhdr, vol_fullname);
644  if (error_code != NO_ERROR)
645  {
646  ASSERT_ERROR ();
647  goto exit;
648  }
649 
650  error_code = disk_vhdr_set_next_vol_fullname (vhdr, NULL);
651  if (error_code != NO_ERROR)
652  {
653  ASSERT_ERROR ();
654  goto exit;
655  }
656 
657  error_code = disk_vhdr_set_vol_remarks (vhdr, ext_info->comments);
658  if (error_code != NO_ERROR)
659  {
660  ASSERT_ERROR ();
661  goto exit;
662  }
663 
664  /* Make sure that in the case of a crash, the volume is created. Otherwise, the recovery will not work */
665 
666  if (ext_info->voltype == DB_PERMANENT_VOLTYPE)
667  {
668  log_append_dboutside_redo (thread_p, RVDK_NEWVOL, sizeof (*vhdr) + disk_vhdr_length_of_varfields (vhdr), vhdr);
669 
671 
672  /* Even though the volume header page is not completed at this moment, to write REDO log for the header page is
673  * crucial for redo recovery since disk_map_init and disk_set_link will write their redo logs. These functions
674  * will access the header page during restart recovery. Another REDO log for RVDK_FORMAT will be written to
675  * completely log the header page including the volume link.
676  *
677  * TODO: I think this log entry and comment are obsolete. In fact disk_stab_init and disk_set_link will not access
678  * this volume header page during recovery. However, I don't like to change this without a thorough
679  * examination, although it does bring a lot of pain. I will just use the addr.offset field to make each
680  * RVDK_FORMAT calls recovery detectable.
681  */
682  addr.offset = -1; /* First call is marked with offset -1. */
683  log_append_redo_data (thread_p, RVDK_FORMAT, &addr, sizeof (*vhdr) + disk_vhdr_length_of_varfields (vhdr), vhdr);
684 
686  }
687 
688  /* Now initialize the sector and page allocator tables and link the volume to previous allocated volume */
689  prev_volid = fileio_find_previous_perm_volume (thread_p, volid);
690  error_code = disk_stab_init (thread_p, vhdr);
691  if (error_code != NO_ERROR)
692  {
693  /* Problems setting the map allocation tables, release the header page, dismount and destroy the volume, and
694  * return */
695  ASSERT_ERROR ();
696  goto exit;
697  }
699 
700  if (ext_info->voltype == DB_PERMANENT_VOLTYPE && volid != LOG_DBFIRST_VOLID)
701  {
702  error_code = disk_set_link (thread_p, prev_volid, volid, vol_fullname, true, DISK_FLUSH);
703  if (error_code != NO_ERROR)
704  {
705  ASSERT_ERROR ();
706  goto exit;
707  }
709  }
710 
711  if (ext_info->voltype == DB_PERMANENT_VOLTYPE)
712  {
713  addr.offset = 0; /* Header is located at position zero */
714  log_append_redo_data (thread_p, RVDK_FORMAT, &addr, sizeof (*vhdr) + disk_vhdr_length_of_varfields (vhdr), vhdr);
715 
717  }
718 
719  /* if this is a volume with temporary purposes, we do not log any disk driver related changes any longer.
720  * indicate that by setting the disk pages to temporary lsa */
721  if (vol_purpose == DB_TEMPORARY_DATA_PURPOSE)
722  {
723  /* todo: understand what this code is supposed to do */
724  PAGE_PTR pgptr = NULL; /* Page pointer */
725  bool flushed;
726 
727  /* Flush the pages so that the log is forced */
728  (void) pgbuf_flush_all (thread_p, volid);
729 
730  /* Flush dwb also */
731  error_code = dwb_flush_force (thread_p, &flushed);
732  if (error_code != NO_ERROR)
733  {
734  ASSERT_ERROR ();
735  goto exit;
736  }
737 
738  /* TODO: Does it hold??
739  * assert (flushed == true);
740  */
741 
742  for (vpid.volid = volid, vpid.pageid = DISK_VOLHEADER_PAGE; vpid.pageid <= vhdr->sys_lastpage; vpid.pageid++)
743  {
744  pgptr = pgbuf_fix (thread_p, &vpid, OLD_PAGE, PGBUF_LATCH_WRITE, PGBUF_UNCONDITIONAL_LATCH);
745  if (pgptr != NULL)
746  {
747  pgbuf_set_lsa_as_temporary (thread_p, pgptr);
748  pgbuf_unfix_and_init (thread_p, pgptr);
749  }
750  }
751  if (ext_info->voltype == DB_PERMANENT_VOLTYPE)
752  {
753  /* Flush all dirty pages and then invalidate them from page buffer pool. So that we can reset the recovery
754  * information directly using the io module */
755 
756  (void) pgbuf_invalidate_all (thread_p, volid); /* Flush and invalidate */
757  error_code = fileio_reset_volume (thread_p, vdes, vol_fullname, max_npages, &PGBUF_TEMP_LSA);
758  if (error_code != NO_ERROR)
759  {
760  ASSERT_ERROR ();
761  /* Problems reseting the pages of the permanent volume for temporary storage purposes... That is, with a
762  * tempvol LSA. dismount and destroy the volume, and return */
763  goto exit;
764  }
765  }
766  }
767 
768  (void) disk_verify_volume_header (thread_p, addr.pgptr);
769 
770  *nsect_free_out = vhdr->nsect_total - SECTOR_FROM_PAGEID (vhdr->sys_lastpage) - 1;
771  pgbuf_set_dirty_and_free (thread_p, addr.pgptr);
772 
774 
775  /* Flush all pages that were formatted. This is not needed, but it is done for security reasons to identify the volume
776  * in case of a system crash. Note that the identification may not be possible during media crashes */
777  (void) pgbuf_flush_all (thread_p, volid);
778  (void) fileio_synchronize (thread_p, vdes, vol_fullname, FILEIO_SYNC_ALSO_FLUSH_DWB);
779 
781 
782  /* todo: temporary is not logged because code should avoid it. this complicated system that uses page buffer should
783  * not be necessary. with the exception of file manager and disk manager, who already manage to skip logging on
784  * temporary files, all other changes on temporary pages are really not logged. so why bother? */
785 
786 exit:
787 
788  if (addr.pgptr != NULL)
789  {
790  pgbuf_unfix (thread_p, addr.pgptr);
791  }
792 
793  if (error_code != NO_ERROR)
794  {
795  /* volume is going to be removed, so we should invalidate all its pages in page buffer */
796  (void) pgbuf_invalidate_all (thread_p, volid);
797 
798  if (ext_info->voltype == DB_TEMPORARY_VOLTYPE)
799  {
800  /* since temporary volumes are not logged, we cannot rely on rollback. we need to remove volume immediately.
801  */
802  if (disk_unformat (thread_p, vol_fullname) != NO_ERROR)
803  {
804  assert (false);
805  }
806  }
807  }
808 
809  return error_code;
810 
811 #undef fault_inject_random_crash
812 }
813 
814 /*
815  * disk_unformat () - Destroy/unformat a volume with the given name
816  * return: NO_ERROR
817  * vol_fullname(in): Full name of volume to unformat
818  */
819 int
820 disk_unformat (THREAD_ENTRY * thread_p, const char *vol_fullname)
821 {
822  INT16 volid;
823  int ret = NO_ERROR;
824 
825  volid = fileio_find_volume_id_with_label (thread_p, vol_fullname);
826  if (volid != NULL_VOLID)
827  {
828  (void) pgbuf_flush_all (thread_p, volid);
829  (void) pgbuf_invalidate_all (thread_p, volid);
830  }
831 
832  fileio_unformat (thread_p, vol_fullname);
833 
834  return ret;
835 }
836 
837 /*
838  * disk_set_creation () - Change database creation information of the given volume
839  * return: NO_ERROR
840  * volid(in): Volume identifier
841  * new_vol_fullname(in): New volume label/name
842  * new_dbcreation(in): New database creation time
843  * new_chkptlsa(in): New checkpoint
844  * logchange(in): Whether or not to log the change
845  * flush_page(in): true for flush dirty page. otherwise, false
846  *
847  * Note: This function is targeted for the log and recovery manager. It is used when a database is copied or renamed.
848  */
849 int
850 disk_set_creation (THREAD_ENTRY * thread_p, INT16 volid, const char *new_vol_fullname, const INT64 * new_dbcreation,
851  const LOG_LSA * new_chkptlsa, bool logchange, DISK_FLUSH_TYPE flush)
852 {
853  DISK_VOLUME_HEADER *vhdr = NULL;
855  DISK_RECV_CHANGE_CREATION *undo_recv;
856  DISK_RECV_CHANGE_CREATION *redo_recv;
857  int error_code = NO_ERROR;
858 
859  if ((int) strlen (new_vol_fullname) + 1 > DB_MAX_PATH_LENGTH)
860  {
862  (int) strlen (new_vol_fullname) + 1, DB_MAX_PATH_LENGTH);
864  }
865 
866  error_code = disk_get_volheader (thread_p, volid, PGBUF_LATCH_WRITE, &addr.pgptr, &vhdr);
867  if (error_code != NO_ERROR)
868  {
869  ASSERT_ERROR ();
870  return error_code;
871  }
872 
873  /* Do I need to log anything ? */
874  if (logchange != false)
875  {
876  int undo_size, redo_size;
877 
878  undo_size = (sizeof (*undo_recv) + (int) strlen (disk_vhdr_get_vol_fullname (vhdr)));
879  undo_recv = (DISK_RECV_CHANGE_CREATION *) malloc (undo_size);
880  if (undo_recv == NULL)
881  {
883  goto error;
884  }
885 
886  redo_size = sizeof (*redo_recv) + (int) strlen (new_vol_fullname);
887  redo_recv = (DISK_RECV_CHANGE_CREATION *) malloc (redo_size);
888  if (redo_recv == NULL)
889  {
891  free_and_init (undo_recv);
892  goto error;
893  }
894 
895  /* Undo stuff */
896  memcpy (&undo_recv->db_creation, &vhdr->db_creation, sizeof (vhdr->db_creation));
897  memcpy (&undo_recv->chkpt_lsa, &vhdr->chkpt_lsa, sizeof (vhdr->chkpt_lsa));
898  (void) strcpy (undo_recv->vol_fullname, disk_vhdr_get_vol_fullname (vhdr));
899 
900  /* Redo stuff */
901  memcpy (&redo_recv->db_creation, new_dbcreation, sizeof (*new_dbcreation));
902  memcpy (&redo_recv->chkpt_lsa, new_chkptlsa, sizeof (*new_chkptlsa));
903  (void) strcpy (redo_recv->vol_fullname, new_vol_fullname);
904 
905  log_append_undoredo_data (thread_p, RVDK_CHANGE_CREATION, &addr, undo_size, redo_size, undo_recv, redo_recv);
906  free_and_init (undo_recv);
907  free_and_init (redo_recv);
908  }
909  else
910  {
911  log_skip_logging (thread_p, &addr);
912  }
913 
914  /* Modify volume creation information */
915  memcpy (&vhdr->db_creation, new_dbcreation, sizeof (*new_dbcreation));
916  memcpy (&vhdr->chkpt_lsa, new_chkptlsa, sizeof (*new_chkptlsa));
917  if (disk_vhdr_set_vol_fullname (vhdr, new_vol_fullname) != NO_ERROR)
918  {
919  goto error;
920  }
921 
922  (void) disk_verify_volume_header (thread_p, addr.pgptr);
923 
924  if (flush == DISK_FLUSH)
925  {
926  assert (false);
927  pgbuf_set_dirty (thread_p, addr.pgptr, DONT_FREE);
928  pgbuf_flush (thread_p, addr.pgptr, FREE);
929  }
930  else if (flush == DISK_FLUSH_AND_INVALIDATE)
931  {
932  pgbuf_set_dirty (thread_p, addr.pgptr, DONT_FREE);
933  if (pgbuf_invalidate (thread_p, addr.pgptr) != NO_ERROR)
934  {
935  return ER_FAILED;
936  }
937  }
938  else
939  { /* DISK_DONT_FLUSH */
940  pgbuf_set_dirty (thread_p, addr.pgptr, FREE);
941  }
942  addr.pgptr = NULL;
943 
944  return NO_ERROR;
945 
946 error:
947  assert (addr.pgptr != NULL);
948 
949  (void) disk_verify_volume_header (thread_p, addr.pgptr);
950 
951  pgbuf_unfix_and_init (thread_p, addr.pgptr);
952 
953  return ER_FAILED;
954 }
955 
956 /*
957  * disk_set_link () - Link the given permanent volume with the previous permanent volume
958  * return: NO_ERROR
959  * volid(in): Volume identifier
960  * next_volid (in): next volume identifier
961  * next_volext_fullname(in): next volume label/name
962  * logchange(in): Whether or not to log the change
963  * flush(in):
964  *
965  * Note: No logging is intended for exclusive use by the log and recovery manager. It is used when a database
966  * is copied or renamed.
967  */
968 int
969 disk_set_link (THREAD_ENTRY * thread_p, INT16 volid, INT16 next_volid, const char *next_volext_fullname, bool logchange,
970  DISK_FLUSH_TYPE flush)
971 {
972  DISK_VOLUME_HEADER *vhdr;
974  VPID vpid;
975  DISK_RECV_LINK_PERM_VOLUME *undo_recv;
976  DISK_RECV_LINK_PERM_VOLUME *redo_recv;
977  int error_code = NO_ERROR;
978 
979  addr.vfid = NULL;
980  addr.offset = 0;
981 
982  /* Get the header of the previous permanent volume */
983 
984  error_code = disk_get_volheader (thread_p, volid, PGBUF_LATCH_WRITE, &addr.pgptr, &vhdr);
985  if (error_code != NO_ERROR)
986  {
987  ASSERT_ERROR ();
988  return error_code;
989  }
990 
991  vpid.volid = volid;
993 
994  /* Do I need to log anything ? */
995  if (logchange == true)
996  {
997  int undo_size, redo_size;
998 
999  if (next_volid != NULL_VOLID)
1000  {
1001  /* Before updating and logging disk link to a new volume, we need to log a page flush on undo.
1002  * Recovery must flush disk link changes before removing volume from disk.
1003  */
1004  log_append_undo_data2 (thread_p, RVPGBUF_FLUSH_PAGE, NULL, NULL, 0, sizeof (vpid), &vpid);
1005  }
1006 
1007  undo_size = (sizeof (*undo_recv) + (int) strlen (disk_vhdr_get_next_vol_fullname (vhdr)));
1008  undo_recv = (DISK_RECV_LINK_PERM_VOLUME *) malloc (undo_size);
1009  if (undo_recv == NULL)
1010  {
1012  goto error;
1013  }
1014 
1015  redo_size = sizeof (*redo_recv) + (int) strlen (next_volext_fullname);
1016  redo_recv = (DISK_RECV_LINK_PERM_VOLUME *) malloc (redo_size);
1017  if (redo_recv == NULL)
1018  {
1020  free_and_init (undo_recv);
1021  goto error;
1022  }
1023 
1024  undo_recv->next_volid = vhdr->next_volid;
1025  (void) strcpy (undo_recv->next_vol_fullname, disk_vhdr_get_next_vol_fullname (vhdr));
1026 
1027  redo_recv->next_volid = next_volid;
1028  (void) strcpy (redo_recv->next_vol_fullname, next_volext_fullname);
1029 
1030  log_append_undoredo_data (thread_p, RVDK_LINK_PERM_VOLEXT, &addr, undo_size, redo_size, undo_recv, redo_recv);
1031 
1032  free_and_init (undo_recv);
1033  free_and_init (redo_recv);
1034  }
1035  else
1036  {
1037  log_skip_logging (thread_p, &addr);
1038  }
1039 
1040  /* Modify the header */
1041  vhdr->next_volid = next_volid;
1042  if (disk_vhdr_set_next_vol_fullname (vhdr, next_volext_fullname) != NO_ERROR)
1043  {
1044  goto error;
1045  }
1046 
1047  /* Forcing the log here to be safer, especially in the case of permanent temp volumes. */
1048  logpb_force_flush_pages (thread_p);
1049 
1050  (void) disk_verify_volume_header (thread_p, addr.pgptr);
1051 
1052  pgbuf_set_dirty (thread_p, addr.pgptr, DONT_FREE);
1053  if (flush == DISK_FLUSH_AND_INVALIDATE)
1054  {
1055  /* this will invoke pgbuf_flush */
1056  if (pgbuf_invalidate (thread_p, addr.pgptr) != NO_ERROR)
1057  {
1058  return ER_FAILED;
1059  }
1060  }
1061  else
1062  {
1063  pgbuf_flush (thread_p, addr.pgptr, FREE);
1064  }
1065  addr.pgptr = NULL;
1066 
1067  return NO_ERROR;
1068 
1069 error:
1070  assert (addr.pgptr != NULL);
1071 
1072  (void) disk_verify_volume_header (thread_p, addr.pgptr);
1073 
1074  pgbuf_unfix_and_init (thread_p, addr.pgptr);
1075 
1076  return ER_FAILED;
1077 }
1078 
1079 /*
1080  * disk_set_boot_hfid () - Reset system boot heap
1081  * return: NO_ERROR
1082  * volid(in): Permanent volume identifier
1083  * hfid(in): System boot heap file
1084  *
1085  * Note: The system boot file filed of in the volume header is redefined to point to the given value. This function
1086  * is called only during the initialization process.
1087  */
1088 int
1089 disk_set_boot_hfid (THREAD_ENTRY * thread_p, INT16 volid, const HFID * hfid)
1090 {
1091  DISK_VOLUME_HEADER *vhdr;
1092  VPID vpid;
1093  LOG_DATA_ADDR addr;
1094 
1095  addr.vfid = NULL;
1096  addr.offset = 0;
1097 
1098  vpid.volid = volid;
1099  vpid.pageid = DISK_VOLHEADER_PAGE;
1100 
1102  if (addr.pgptr == NULL)
1103  {
1104  return ER_FAILED;
1105  }
1106 
1107  (void) disk_verify_volume_header (thread_p, addr.pgptr);
1108 
1109  vhdr = (DISK_VOLUME_HEADER *) addr.pgptr;
1110 
1111  log_append_undoredo_data (thread_p, RVDK_RESET_BOOT_HFID, &addr, sizeof (vhdr->boot_hfid), sizeof (*hfid),
1112  &vhdr->boot_hfid, &hfid);
1113  HFID_COPY (&(vhdr->boot_hfid), hfid);
1114 
1115  (void) disk_verify_volume_header (thread_p, addr.pgptr);
1116 
1117  pgbuf_set_dirty (thread_p, addr.pgptr, FREE);
1118  addr.pgptr = NULL;
1119 
1120  return NO_ERROR;
1121 }
1122 
1123 /*
1124  * disk_get_boot_hfid () - Find the system boot heap file
1125  * return: hfid on success or NULL on failure
1126  * volid(in): Permanent volume identifier
1127  * hfid(out): System boot heap file
1128  */
1129 HFID *
1130 disk_get_boot_hfid (THREAD_ENTRY * thread_p, INT16 volid, HFID * hfid)
1131 {
1132  DISK_VOLUME_HEADER *vhdr;
1133  PAGE_PTR pgptr = NULL;
1134 
1135  if (disk_get_volheader (thread_p, volid, PGBUF_LATCH_READ, &pgptr, &vhdr) != NO_ERROR)
1136  {
1137  ASSERT_ERROR ();
1138  return NULL;
1139  }
1140 
1141  HFID_COPY (hfid, &(vhdr->boot_hfid));
1142 
1143  (void) disk_verify_volume_header (thread_p, pgptr);
1144 
1145  pgbuf_unfix_and_init (thread_p, pgptr);
1146 
1147  if (HFID_IS_NULL (hfid))
1148  {
1149  return NULL;
1150  }
1151 
1152  return hfid;
1153 }
1154 
1155 /*
1156  * disk_get_link () - Find the name of the next permananet volume extension
1157  * return: next_volext_fullname or NULL in case of error
1158  * volid(in): Volume identifier
1159  * next_volid(out): Next volume identifier
1160  * next_volext_fullname(out): Next volume extension
1161  *
1162  * Note: If there is none, next_volext_fullname is set to null string
1163  */
1164 char *
1165 disk_get_link (THREAD_ENTRY * thread_p, INT16 volid, INT16 * next_volid, char *next_volext_fullname)
1166 {
1167  DISK_VOLUME_HEADER *vhdr;
1168  PAGE_PTR pgptr = NULL;
1169 
1170  assert (next_volid != NULL);
1171  assert (next_volext_fullname != NULL);
1172 
1173  if (disk_get_volheader (thread_p, volid, PGBUF_LATCH_READ, &pgptr, &vhdr) != NO_ERROR)
1174  {
1175  ASSERT_ERROR ();
1176  return NULL;
1177  }
1178 
1179  *next_volid = vhdr->next_volid;
1180  strncpy (next_volext_fullname, disk_vhdr_get_next_vol_fullname (vhdr), DB_MAX_PATH_LENGTH);
1181 
1182  (void) disk_verify_volume_header (thread_p, pgptr);
1183 
1184  pgbuf_unfix_and_init (thread_p, pgptr);
1185 
1186  return next_volext_fullname;
1187 }
1188 
1189 
1190 /*
1191  * disk_rv_redo_dboutside_newvol () - Redo the initialization of a disk from the point of view of operating system
1192  * return: NO_ERROR
1193  * rcv(in): Recovery structure
1194  */
1195 int
1197 {
1198  DISK_VOLUME_HEADER *vhdr;
1199  char *vol_label;
1200 
1201  vhdr = (DISK_VOLUME_HEADER *) rcv->data;
1202  vol_label = disk_vhdr_get_vol_fullname (vhdr);
1203 
1205  {
1206  (void) fileio_format (thread_p, NULL, vol_label, vhdr->volid, DISK_SECTS_NPAGES (vhdr->nsect_total),
1207  vhdr->purpose != DB_TEMPORARY_DATA_PURPOSE, false, false, IO_PAGESIZE, 0, false);
1208  (void) pgbuf_invalidate_all (thread_p, vhdr->volid);
1209  }
1210 
1211  return NO_ERROR;
1212 }
1213 
1214 /*
1215  * disk_rv_undo_format () - Undo the initialization of a disk. The disk is uninitialized or removed
1216  * return: NO_ERROR
1217  * rcv(in): Recovery structure
1218  */
1219 int
1221 {
1222  VOLID volid = (VOLID) rcv->offset;
1223  int ret = NO_ERROR;
1224 
1225  /* we need to undo volume create. this is logged at the start of disk_format, so we may be in one of next situations:
1226  * 1. not recovery, an error must have occurred during disk_add_volume execution. it was not yet added to cache,
1227  * so all this has to do is unformat (the number of volumes will be decremented by disk_add_volume).
1228  * condition: LOG_ISRESTARTED () == true
1229  * 2. recovery, but crash occurred before volume is registered in system. volume is not loaded to cache, so all
1230  * this has to do it unformat.
1231  * condition: LOG_ISRESTARTED () == false AND volid >= disk_Cache->nvols_perm
1232  * 3. recovery, and crash occurred after volume was registered in system, but before completing the action. the
1233  * volume was loaded in cache during restart. we need to remove it completely from cache. volid is last in cache
1234  * in this case.
1235  * condition: LOG_ISRESTARTED () == false AND volid == disk_Cache->nvols_perm - 1
1236  */
1237 
1238  if (!LOG_ISRESTARTED () && volid == disk_Cache->nvols_perm - 1)
1239  {
1240  VPID vpid_volheader;
1241  PAGE_PTR page_volheader = NULL;
1242  DKNSECTS total = 0, max = 0;
1243  DKNSECTS free = 0;
1244 
1245  /* case 3. remove volume from disk cache completely. */
1246 
1247  er_stack_push ();
1248 
1249  /* we need page to remove total/max from disk cache. however, it must be allocated. */
1250  vpid_volheader.volid = volid;
1251  vpid_volheader.pageid = DISK_VOLHEADER_PAGE;
1252  ret = pgbuf_fix_if_not_deallocated (thread_p, &vpid_volheader, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH,
1253  &page_volheader);
1254  if (ret != NO_ERROR)
1255  {
1256  /* what is this error?? */
1257  assert_release (false);
1258  }
1259  else if (page_volheader == NULL)
1260  {
1261  /* volume was not created. fall through. */
1262  assert (disk_Cache->vols[volid].nsect_free == 0);
1263  }
1264  else
1265  {
1266  /* get total/max from header */
1267  DISK_VOLUME_HEADER *volheader = (DISK_VOLUME_HEADER *) page_volheader;
1268  total = volheader->nsect_total;
1269  max = volheader->nsect_max;
1270 
1271  pgbuf_unfix (thread_p, page_volheader);
1272  }
1273 
1274  er_stack_pop ();
1275 
1276  /* volume was added to cache. now remove it */
1277  free = disk_Cache->vols[volid].nsect_free;
1278  disk_cache_lock_reserve_for_purpose (disk_Cache->vols[volid].purpose);
1279  disk_cache_update_vol_free (volid, -disk_Cache->vols[volid].nsect_free);
1280  disk_cache_unlock_reserve_for_purpose (disk_Cache->vols[volid].purpose);
1281 
1282  assert (disk_Cache->vols[volid].nsect_free == 0);
1283  disk_Cache->nvols_perm--;
1284  disk_Cache->vols[volid].purpose = DISK_UNKNOWN_PURPOSE;
1285 
1286  disk_Cache->perm_purpose_info.extend_info.nsect_total -= total;
1287  disk_Cache->perm_purpose_info.extend_info.nsect_max -= max;
1288 
1289  if (disk_Cache->perm_purpose_info.extend_info.volid_extend == volid)
1290  {
1291  /* no longer true */
1293  }
1294 
1295  disk_log ("disk_rv_undo_format", "remove volume %d from cache (free = %d, total = %d, max = %d).",
1296  volid, free, total, max);
1297  }
1298  else
1299  {
1300  /* case 1 or 2. disk cache is not updated here. */
1301  assert (disk_Cache->nvols_perm <= volid || (LOG_ISRESTARTED () && (disk_Cache->nvols_perm - 1 == volid)));
1302  assert (disk_Cache->vols[volid].nsect_free == 0);
1303  assert (disk_Cache->perm_purpose_info.extend_info.volid_extend != volid);
1304 
1305  /* make sure purpose is reset */
1306  disk_Cache->vols[volid].purpose = DISK_UNKNOWN_PURPOSE;
1307 
1308  disk_log ("disk_rv_undo_format", "remove volume %d", volid);
1309  }
1310 
1311  ret = disk_unformat (thread_p, (char *) rcv->data);
1312  /* we need to remove from volume info file too... the only way is to recreate it. */
1313  (void) logpb_recreate_volume_info (thread_p);
1315 
1316  return ret;
1317 }
1318 
1319 /*
1320  * disk_rv_redo_format () - Redo the initialization of a disk.
1321  * return: NO_ERROR
1322  * rcv(in): Recovery structure
1323  */
1324 int
1326 {
1327  int error_code = NO_ERROR;
1328  DISK_VOLUME_HEADER *volheader;
1329  bool is_first_call = rcv->offset == -1;
1330  DKNSECTS nsect_free = 0;
1331  DKNSECTS nsect_diff;
1332 
1333  rcv->offset = 0;
1334  (void) pgbuf_set_page_ptype (thread_p, rcv->pgptr, PAGE_VOLHEADER);
1335  error_code = log_rv_copy_char (thread_p, rcv);
1336  assert (error_code == NO_ERROR);
1337 
1338  disk_verify_volume_header (thread_p, rcv->pgptr);
1339  volheader = (DISK_VOLUME_HEADER *) rcv->pgptr;
1340 
1341  if (is_first_call)
1342  {
1343  /* don't update disk cache. if the volume is successfully created, another disk_rv_redo_format call will follow,
1344  * and we can update disk cache then. */
1345 
1346  disk_log ("disk_rv_redo_format", "first call for volume %d at lsa %lld|%d", volheader->volid,
1347  PGBUF_PAGE_LSA_AS_ARGS (rcv->pgptr));
1348  return NO_ERROR;
1349  }
1350 
1351  /* this is the second call of disk_rv_redo_format. we need to update disk cache too.
1352  * you may be tempted to think that this is a new volume and that all of it must be empty. oh well, even though you
1353  * are right most of the time, we can have this beautiful case:
1354  * 1. volume is successfully created.
1355  * 2. some sectors are reserved.
1356  * 3. some (or all) sector table pages are flushed to disk, but not the volume header page.
1357  * 4. other sectors are reserved.
1358  * 5. server crashes.
1359  *
1360  * while we are here, the sectors reserved in step #2 are already found in sector table pages, so the assumption that
1361  * whole volume is empty is wrong. we need to count the sectors to make sure we are not wrong. */
1362 
1363  if (disk_Cache->nvols_perm == volheader->volid)
1364  {
1365  /* add to disk cache */
1366  disk_Cache->nvols_perm++;
1367  disk_Cache->vols[volheader->volid].purpose = volheader->purpose;
1368  disk_Cache->vols[volheader->volid].nsect_free = 0;
1369 
1370  disk_Cache->perm_purpose_info.extend_info.nsect_total += volheader->nsect_total;
1371  disk_Cache->perm_purpose_info.extend_info.nsect_max += volheader->nsect_max;
1372  }
1373 
1374  /* fix cache... */
1375  error_code = disk_stab_iterate_units_all (thread_p, volheader, PGBUF_LATCH_READ, disk_stab_count_free, &nsect_free);
1376  if (error_code != NO_ERROR)
1377  {
1378  assert_release (false);
1379  return error_code;
1380  }
1381  nsect_diff = nsect_free - disk_Cache->vols[volheader->volid].nsect_free;
1382  if (nsect_diff != 0)
1383  {
1385  disk_cache_update_vol_free (volheader->volid, nsect_diff);
1387 
1388  /* there, I fixed it */
1389  }
1390 
1391  disk_log ("disk_rv_redo_format", "second call for volume %d at lsa %lld|%d, free = %d, total = %d, max = %d",
1392  volheader->volid, PGBUF_PAGE_LSA_AS_ARGS (rcv->pgptr), nsect_free, volheader->nsect_total,
1393  volheader->nsect_max);
1394 
1395  return error_code;
1396 }
1397 
1398 /*
1399  * disk_rv_dump_hdr () - Dump recovery header information.
1400  * return: void
1401  * length_ignore(in): Length of Recovery Data
1402  * data(in): The data being logged
1403  */
1404 void
1405 disk_rv_dump_hdr (FILE * fp, int length_ignore, void *data)
1406 {
1407  DISK_VOLUME_HEADER *vhdr;
1408 
1409  vhdr = (DISK_VOLUME_HEADER *) data;
1410  disk_vhdr_dump (fp, vhdr);
1411 }
1412 
1413 /*
1414  * disk_rv_redo_init_map () - REDO the initialization of map table page.
1415  * return: NO_ERROR
1416  * rcv(in): Recovery structure
1417  */
1418 int
1420 {
1421  DKNSECTS nsects;
1422  DISK_STAB_UNIT *stab_unit;
1423 
1424  (void) pgbuf_set_page_ptype (thread_p, rcv->pgptr, PAGE_VOLBITMAP);
1425 
1426  nsects = *(DKNSECTS *) rcv->data;
1427 
1428  /* Initialize the page to zeros, and allocate the needed bits for the pages or sectors */
1429  memset (rcv->pgptr, 0, DB_PAGESIZE);
1430 
1431  /* One byte at a time */
1432  for (stab_unit = (DISK_STAB_UNIT *) rcv->pgptr; nsects >= DISK_STAB_UNIT_BIT_COUNT;
1433  nsects -= DISK_STAB_UNIT_BIT_COUNT, stab_unit++)
1434  {
1435  *stab_unit = BIT64_FULL;
1436  }
1437  if (nsects > 0)
1438  {
1439  *stab_unit = bit64_set_trailing_bits (0, nsects);
1440  }
1441 
1442  pgbuf_set_dirty (thread_p, rcv->pgptr, DONT_FREE);
1443  return NO_ERROR;
1444 }
1445 
1446 /*
1447  * disk_rv_dump_init_map () - Dump redo information to initialize a map table page
1448  * return: void
1449  * length_ignore(in): Length of Recovery Data
1450  * data(in): The data being logged
1451  */
1452 void
1453 disk_rv_dump_init_map (FILE * fp, int length_ignore, void *data)
1454 {
1455  fprintf (fp, "Nalloc_bits = %d\n", *(INT32 *) data);
1456 }
1457 
1458 /*
1459  * disk_rv_undoredo_set_creation_time () - Recover the modification of change creation stuff
1460  * return: NO_ERROR
1461  * rcv(in): Recovery structure
1462  */
1463 int
1465 {
1466  DISK_VOLUME_HEADER *vhdr;
1467  DISK_RECV_CHANGE_CREATION *change;
1468  int ret = NO_ERROR;
1469 
1470  vhdr = (DISK_VOLUME_HEADER *) rcv->pgptr;
1471  change = (DISK_RECV_CHANGE_CREATION *) rcv->data;
1472 
1473  memcpy (&vhdr->db_creation, &change->db_creation, sizeof (change->db_creation));
1474  memcpy (&vhdr->chkpt_lsa, &change->chkpt_lsa, sizeof (change->chkpt_lsa));
1475  ret = disk_vhdr_set_vol_fullname (vhdr, change->vol_fullname);
1476 
1477  pgbuf_set_dirty (thread_p, rcv->pgptr, DONT_FREE);
1478 
1479  return NO_ERROR;
1480 }
1481 
1482 /*
1483  * disk_rv_dump_set_creation_time () - Dump either redo or undo change creation information
1484  * return: void
1485  * length_ignore(in): Length of Recovery Data
1486  * data(in): The data being logged
1487  */
1488 void
1489 disk_rv_dump_set_creation_time (FILE * fp, int length_ignore, void *data)
1490 {
1491  DISK_RECV_CHANGE_CREATION *change;
1492 
1493  change = (DISK_RECV_CHANGE_CREATION *) data;
1494 
1495  fprintf (fp, "Label = %s, Db_creation = %lld, chkpt = %lld|%lld\n", change->vol_fullname,
1496  (long long) change->db_creation, (long long) change->chkpt_lsa.pageid, (long long) change->chkpt_lsa.offset);
1497 }
1498 
1499 /*
1500  * disk_rv_undoredo_link () - Recover the link of a volume extension
1501  * return: NO_ERROR
1502  * rcv(in): Recovery structure
1503  */
1504 int
1506 {
1507  DISK_VOLUME_HEADER *vhdr;
1509  int ret = NO_ERROR;
1510 
1511  vhdr = (DISK_VOLUME_HEADER *) rcv->pgptr;
1512  link = (DISK_RECV_LINK_PERM_VOLUME *) rcv->data;
1513 
1514  vhdr->next_volid = link->next_volid;
1516  pgbuf_set_dirty (thread_p, rcv->pgptr, DONT_FREE);
1517 
1518  return NO_ERROR;
1519 }
1520 
1521 /*
1522  * disk_rv_dump_link () - Dump either redo or undo link of a volume extension
1523  * return: void
1524  * length_ignore(in): Length of Recovery Data
1525  * data(in): The data being logged
1526  */
1527 void
1528 disk_rv_dump_link (FILE * fp, int length_ignore, void *data)
1529 {
1531 
1532  link = (DISK_RECV_LINK_PERM_VOLUME *) data;
1533 
1534  fprintf (fp, "Next_Volid = %d, Next_Volextension = %s\n", link->next_volid, link->next_vol_fullname);
1535 }
1536 
1537 /*
1538  * disk_rv_undoredo_set_boot_hfid () - Recover the reset of boot system heap
1539  * return: NO_ERROR
1540  * rcv(in): Recovery structure
1541  */
1542 int
1544 {
1545  DISK_VOLUME_HEADER *vhdr;
1546  HFID *hfid;
1547 
1548  vhdr = (DISK_VOLUME_HEADER *) rcv->pgptr;
1549  hfid = (HFID *) rcv->data;
1550 
1551  vhdr->boot_hfid.vfid.volid = hfid->vfid.volid;
1552  vhdr->boot_hfid.vfid.fileid = hfid->vfid.fileid;
1553  vhdr->boot_hfid.hpgid = hfid->hpgid;
1554 
1555  pgbuf_set_dirty (thread_p, rcv->pgptr, DONT_FREE);
1556 
1557  return NO_ERROR;
1558 }
1559 
1560 /*
1561  * disk_rv_dump_set_boot_hfid () - Dump either redo/undo reset of boot system heap
1562  * return: void
1563  * length_ignore(in): Length of Recovery Data
1564  * data(in): The data being logged
1565  */
1566 void
1567 disk_rv_dump_set_boot_hfid (FILE * fp, int length_ignore, void *data)
1568 {
1569  HFID *hfid;
1570 
1571  hfid = (HFID *) data;
1572  fprintf (fp, "Heap: Volid = %d, Fileid = %d, Header_pageid = %d\n", hfid->vfid.volid, hfid->vfid.fileid, hfid->hpgid);
1573 }
1574 
1575 /*
1576  * disk_rv_redo_volume_expand ()
1577  *
1578  * return: NO_ERROR
1579  * rcv(in): Recovery structure
1580  */
1581 int
1583 {
1585 
1586  assert (rcv->length == sizeof (info));
1587  info = *((DISK_RECV_DATA_VOLUME_EXPAND *) rcv->data);
1588 
1589  return fileio_expand_to (thread_p, info.volid, info.npages, DB_PERMANENT_VOLTYPE);
1590 }
1591 
1592 /*
1593  * disk_rv_dump_volume_expand () -
1594  *
1595  * return: void
1596  * length_ignore(in): Length of Recovery Data
1597  * data(in):
1598  */
1599 void
1600 disk_rv_dump_volume_expand (FILE * fp, int length_ignore, void *data)
1601 {
1603 
1604  info = (DISK_RECV_DATA_VOLUME_EXPAND *) data;
1605 
1606  fprintf (fp, "Volid = %d, volume new size in pages = %d\n", info->volid, info->npages);
1607 }
1608 
1609 /*
1610  * disk_extend () - expand disk storage by extending existing volumes or by adding new volumes.
1611  *
1612  * return : error code
1613  * thread_p (in) : thread entry
1614  * extend_info (in) : disk extend info
1615  * reserve_context (in) : reserve context (can be NULL)
1616  */
1617 static int
1618 disk_extend (THREAD_ENTRY * thread_p, DISK_EXTEND_INFO * extend_info, DISK_RESERVE_CONTEXT * reserve_context)
1619 {
1620 #if defined (SERVER_MODE)
1621 #define DISK_EXTEND_TEMP_REGISTER() \
1622  do \
1623  { \
1624  if (voltype == DB_TEMPORARY_VOLTYPE) \
1625  { \
1626  tsc_getticks (&end_tick); \
1627  tsc_elapsed_time_usec (&tv_diff, end_tick, start_tick); \
1628  TSC_ADD_TIMEVAL (thread_p->event_stats.temp_expand_time, tv_diff); \
1629  thread_p->event_stats.temp_expand_pages += DISK_SECTS_NPAGES (nsect_temp_extended); \
1630  } \
1631  } \
1632  while (0)
1633 #define DISK_EXTEND_TEMP_COLLECT(nsects) \
1634  do \
1635  { \
1636  if (voltype == DB_TEMPORARY_VOLTYPE) \
1637  { \
1638  nsect_temp_extended += (nsects); \
1639  } \
1640  } \
1641  while (0)
1642 #endif /* SERVER_MODE */
1643 
1644  DKNSECTS free = extend_info->nsect_free;
1645  DKNSECTS intention = extend_info->nsect_intention;
1646  DKNSECTS total = extend_info->nsect_total;
1647  DKNSECTS max = extend_info->nsect_max;
1648  DB_VOLTYPE voltype = extend_info->voltype;
1649 
1650  DKNSECTS nsect_extend;
1651  DKNSECTS target_free;
1652 
1653  DBDEF_VOL_EXT_INFO volext;
1654  VOLID volid_new = NULL_VOLID;
1655 
1656  DKNSECTS nsect_free_new = 0;
1657 
1658 #if defined (SERVER_MODE)
1659  TSC_TICKS start_tick, end_tick;
1660  TSCTIMEVAL tv_diff;
1661  DKNSECTS nsect_temp_extended = 0;
1662 #endif /* SERVER_MODE */
1663 
1664  bool check_interrupt = logtb_get_check_interrupt (thread_p);
1665  bool continue_check = false;
1666  int error_code = NO_ERROR;
1667 
1668  /* How this works:
1669  * There can be only one expand running for permanent volumes and one for temporary volumes. Any expander must first
1670  * lock expand mutex.
1671  *
1672  * We want to avoid concurrent expansions as much as possible, therefore on each expansion we try to allocate more
1673  * disk space than currently necessary. Moreover, there is an auto-expansion thread that tries to keep a stable level
1674  * of free space. As long as the worker requirements do not spike, they would never have to do the disk expansion.
1675  *
1676  * However, we cannot rule out spikes in disk space requirements. We cannot even rule out concurrent spikes. Intention
1677  * field saves all sector requests that could not be satisfied with existing free space. Therefore, one expand
1678  * iteration can serve all expansion needs.
1679  *
1680  * This being said, now let's get to how expand works.
1681  *
1682  * First we decide how much to expand. we set the target_free to MAX (1% current size, min threshold). Then we subtract
1683  * the current free space. If the difference is negative, we set it to 0.
1684  * Then we add the reserve intentions that could not be satisfied by existing disk space.
1685  *
1686  * Once we decide how much we want to expand, we first extend last volume are already extended to their maximum
1687  * capacities. If last volume is also extended to its maximum capacity, we start adding new volumes.
1688  *
1689  * NOTE: The same algorithm is applied to both permanent and temporary files. More exactly, the expansion is allowed
1690  * for permanent volumes used for permanent data purpose or temporary files used for temporary data purpose.
1691  * Permanent volumes for temporary data purpose can only be added by user and are never extended.
1692  */
1693 
1694  assert (disk_Cache->owner_extend == thread_get_entry_index (thread_p));
1695 
1696  /* expand */
1697  /* what is the desired remaining free after expand? */
1698  target_free = MAX ((DKNSECTS) (total * 0.01), DISK_MIN_VOLUME_SECTS);
1699  /* what is the desired expansion? do not expand less than intention. */
1700  nsect_extend = MAX (target_free - free, 0) + intention;
1701  if (nsect_extend <= 0)
1702  {
1703  /* no expand needed */
1704  return NO_ERROR;
1705  }
1706 
1707  disk_log ("disk_extend", "extend %s disk by %d sectors.", disk_type_to_string (extend_info->voltype), nsect_extend);
1708 
1709 #if defined (SERVER_MODE)
1710  if (voltype == DB_TEMPORARY_VOLTYPE)
1711  {
1712  tsc_getticks (&start_tick);
1713  }
1714 #endif /* SERVER_MODE */
1715 
1716  if (total < max)
1717  {
1718  /* first expand last volume to its capacity */
1719  DKNSECTS to_expand;
1720 
1721  assert (extend_info->volid_extend != NULL_VOLID);
1722 
1723  to_expand = MIN (nsect_extend, max - total);
1724 
1725  log_sysop_start (thread_p);
1726 
1727  (void) logtb_set_check_interrupt (thread_p, false);
1728  error_code = disk_volume_expand (thread_p, extend_info->volid_extend, voltype, to_expand, &nsect_free_new);
1729  (void) logtb_set_check_interrupt (thread_p, check_interrupt);
1730  if (error_code != NO_ERROR)
1731  {
1732  ASSERT_ERROR ();
1733  log_sysop_abort (thread_p);
1734  return error_code;
1735  }
1736 
1737  log_sysop_commit (thread_p);
1738  assert (nsect_free_new >= to_expand);
1739 
1740  if (extend_info->nsect_total == extend_info->nsect_max)
1741  {
1742  /* this cannot be extended any longer. make sure it's not going to be used for that. */
1743  extend_info->volid_extend = NULL_VOLID;
1744  }
1745 
1746  disk_log ("disk_extend", "expanded volume %d by %d sectors for %s.", extend_info->volid_extend, nsect_free_new,
1747  disk_type_to_string (extend_info->voltype));
1748 
1749  /* subtract from what we need to expand */
1750  nsect_extend -= nsect_free_new;
1751 
1752  /* no one else modifies total, it is protected by expand mutex */
1753  extend_info->nsect_total += nsect_free_new;
1754 
1755  disk_cache_lock_reserve (extend_info);
1756  disk_cache_update_vol_free (extend_info->volid_extend, nsect_free_new);
1757 
1758  if (reserve_context != NULL && reserve_context->n_cache_reserve_remaining > 0)
1759  {
1760  disk_reserve_from_cache_volume (extend_info->volid_extend, reserve_context);
1761  }
1762  disk_cache_unlock_reserve (extend_info);
1763 
1764 #if defined (SERVER_MODE)
1765  DISK_EXTEND_TEMP_COLLECT (nsect_free_new);
1766 #endif /* SERVER_MODE */
1767 
1768  if (nsect_extend <= 0)
1769  {
1770  /* it is enough */
1771 #if defined (SERVER_MODE)
1772  DISK_EXTEND_TEMP_REGISTER ();
1773 #endif /* SERVER_MODE */
1774  return NO_ERROR;
1775  }
1776  assert (extend_info->nsect_total == extend_info->nsect_max);
1777  }
1778  assert (nsect_extend > 0);
1779 
1780  /* add new volume(s) */
1781  volext.nsect_max = extend_info->nsect_vol_max;
1782  volext.comments = "Automatic Volume Extension";
1783  volext.voltype = voltype;
1785  volext.overwrite = false;
1786  volext.max_writesize_in_sec = 0;
1787  while (nsect_extend > 0)
1788  {
1789  volext.path = NULL;
1790  volext.name = NULL;
1791 
1792  if (check_interrupt)
1793  {
1794  if (logtb_is_interrupted (thread_p, true, &continue_check))
1795  {
1797  return ER_INTERRUPTED;
1798  }
1799  }
1800 
1801  /* set size to remaining sectors */
1802  volext.nsect_total = nsect_extend + DISK_SYS_NSECT_SIZE (volext.nsect_max);
1803  /* but size cannot exceed max */
1804  volext.nsect_total = MIN (volext.nsect_max, volext.nsect_total);
1805  /* and it cannot be lower than a minimum size */
1806  volext.nsect_total = MAX (volext.nsect_total, DISK_MIN_VOLUME_SECTS);
1807  /* we always keep rounded number of sectors */
1808  volext.nsect_total = DISK_SECTS_ROUND_UP (volext.nsect_total);
1809 
1810  /* add new volume */
1811  (void) logtb_set_check_interrupt (thread_p, false);
1812  error_code = disk_add_volume (thread_p, &volext, &volid_new, &nsect_free_new);
1813  (void) logtb_set_check_interrupt (thread_p, check_interrupt);
1814  if (error_code != NO_ERROR)
1815  {
1816  ASSERT_ERROR ();
1817  return error_code;
1818  }
1819  assert (disk_Cache->nvols_perm + disk_Cache->nvols_temp <= LOG_MAX_DBVOLID);
1820 
1821  disk_log ("disk_extend", "added new volume %d with %d free sectors for %s.", volid_new, nsect_free_new,
1822  disk_type_to_string (extend_info->voltype));
1823 
1824  nsect_extend -= nsect_free_new;
1825 
1826  /* update total and max */
1827  extend_info->nsect_total += volext.nsect_total;
1828  extend_info->nsect_max += volext.nsect_max;
1829 
1830  disk_cache_lock_reserve (extend_info);
1831  /* add new volume */
1832  disk_Cache->vols[volid_new].nsect_free = nsect_free_new;
1833  assert (disk_Cache->vols[volid_new].purpose == volext.purpose);
1834  extend_info->nsect_free += nsect_free_new;
1835 
1836  if (reserve_context && reserve_context->n_cache_reserve_remaining > 0)
1837  {
1838  /* reserve ahead */
1839  disk_reserve_from_cache_volume (volid_new, reserve_context);
1840  }
1841 
1842  disk_cache_unlock_reserve (extend_info);
1843 
1844  if (extend_info->nsect_total < extend_info->nsect_max)
1845  {
1846  /* update volume used for auto extend */
1847  extend_info->volid_extend = volid_new;
1848  }
1849 
1850  assert (disk_is_valid_volid (volid_new));
1851 
1852 #if defined (SERVER_MODE)
1853  DISK_EXTEND_TEMP_COLLECT (volext.nsect_total);
1854 #endif /* SERVER_MODE */
1855  }
1856 
1857  /* finished expand */
1858 
1859  /* safe guard: if this was called during sector reservation, the expansion should cover all required sectors. */
1860  assert (reserve_context == NULL || reserve_context->n_cache_reserve_remaining == 0);
1861 #if defined (SERVER_MODE)
1862  DISK_EXTEND_TEMP_REGISTER ();
1863 #endif /* SERVER_MODE */
1864  return NO_ERROR;
1865 
1866 #if defined (SERVER_MODE)
1867 #undef DISK_EXTEND_TEMP_COLLECT
1868 #undef DISK_EXTEND_TEMP_REGISTER
1869 #endif /* SERVER_MODE */
1870 }
1871 
1872 /*
1873  * disk_volume_expand () - expand disk space for volume
1874  *
1875  * return : error code
1876  * thread_p (in) : thread entry
1877  * volid (in) : volume identifier
1878  * voltype (in) : volume type (hint, caller should know)
1879  * nsect_extend (in) : desired extension
1880  * nsect_extended_out (out) : extended size (rounded up desired extension)
1881  */
1882 static int
1883 disk_volume_expand (THREAD_ENTRY * thread_p, VOLID volid, DB_VOLTYPE voltype, DKNSECTS nsect_extend,
1884  DKNSECTS * nsect_extended_out)
1885 {
1886  PAGE_PTR page_volheader = NULL;
1887  DISK_VOLUME_HEADER *volheader = NULL;
1889  int volume_new_npages;
1890  bool do_logging;
1891  int error_code = NO_ERROR;
1892  LOG_LSA prev_lsa = LSA_INITIALIZER;
1893 
1894  assert (nsect_extend > 0);
1895 
1896  /* how it works:
1897  *
1898  * to make sure extension is correctly recovered, we need to:
1899  * 1. use a system operation (we need to undo change in volume header if expand is not completed).
1900  * 2. undoredo update on volume header.
1901  * 3. log expansion (unattached redo - is always executed).
1902  * 4. commit system operation (to cancel the volume header change undo).
1903  * 5. flush log! without this last step, it is still possible to expand the volume without recovery
1904  * 6. now it is safe to expand volume.
1905  *
1906  */
1907 
1908  /* round up */
1909  nsect_extend = DISK_SECTS_ROUND_UP (nsect_extend);
1910 
1911  /* fix volume header */
1912  error_code = disk_get_volheader (thread_p, volid, PGBUF_LATCH_WRITE, &page_volheader, &volheader);
1913  if (error_code != NO_ERROR)
1914  {
1915  assert_release (false);
1917  return ER_FAILED;
1918  }
1919 
1920  assert (volheader->type == voltype);
1921  do_logging = (volheader->type == DB_PERMANENT_VOLTYPE);
1922 
1923  /* we need system op here */
1924  log_sysop_start (thread_p);
1925 
1926  /* update sector total number */
1927  volheader->nsect_total += nsect_extend;
1928  disk_verify_volume_header (thread_p, page_volheader);
1929  if (do_logging)
1930  {
1931  prev_lsa = *pgbuf_get_lsa (page_volheader);
1932  log_append_undoredo_data2 (thread_p, RVDK_VOLHEAD_EXPAND, NULL, page_volheader, NULL_OFFSET,
1933  sizeof (nsect_extend), sizeof (nsect_extend), &nsect_extend, &nsect_extend);
1934  }
1935  disk_log ("disk_volume_expand", "add %d more sectors to: \n" DISK_VOLHEADER_MSG
1936  "\t\t\t" PGBUF_PAGE_MODIFY_MSG ("page"), nsect_extend,
1937  DISK_VOLHEADER_AS_ARGS (volheader), PGBUF_PAGE_MODIFY_ARGS (page_volheader, &prev_lsa));
1938 
1940 
1941  /* volume new size in pages */
1942  volume_new_npages = DISK_SECTS_NPAGES (volheader->nsect_total);
1943 
1944  if (do_logging)
1945  {
1946  /* volume expand must be logged before freeing volume header page. otherwise, this can happen:
1947  *
1948  * 1. page is freed.
1949  * 2. log is flushed including RVDK_VOLHEAD_EXPAND record.
1950  * 3. system crashes before logging RVDK_EXPAND_VOLUME.
1951  * 4. after recovery, volume header page says volume is expanded, but it is not.
1952  */
1953  log_data.volid = volid;
1954  log_data.npages = volume_new_npages;
1955 
1957  }
1958 
1959  /* now it is safe to free volume header */
1960  pgbuf_set_dirty_and_free (thread_p, page_volheader);
1961 
1963 
1964  /* we can now end system op. */
1965  log_sysop_commit (thread_p);
1966 
1968 
1969  /* to be sure expansion really happens on recovery too, we must flush log! */
1970  logpb_force_flush_pages (thread_p);
1971 
1973 
1974  /* expand volume */
1975  error_code = fileio_expand_to (thread_p, volid, volume_new_npages, voltype);
1976  if (error_code != NO_ERROR)
1977  {
1978  // important note - we just committed volume expansion; we cannot afford any failures here
1979  // caller won't update cache!!
1980  assert (false);
1981  return error_code;
1982  }
1983 
1985 
1986  *nsect_extended_out = nsect_extend;
1987 
1988  /* success */
1989  /* caller will update cache */
1990  return NO_ERROR;
1991 }
1992 
1993 /*
1994  * disk_rv_volhead_extend_redo () - volume header redo recovery after volume extension.
1995  *
1996  * return : Error code
1997  * thread_p (in) : Thread entry
1998  * rcv (in) : Recovery data
1999  */
2000 int
2002 {
2003  DISK_VOLUME_HEADER *volheader = (DISK_VOLUME_HEADER *) rcv->pgptr;
2004  DKNSECTS nsect_extend = *(DKNSECTS *) rcv->data;
2005  DISK_STAB_CURSOR start_cursor;
2006  DISK_STAB_CURSOR end_cursor;
2007  int nfree = 0;
2008  int error_code = NO_ERROR;
2009 
2010  assert (rcv->length == sizeof (nsect_extend));
2011  assert (volheader->type == DB_PERMANENT_VOLTYPE);
2012  assert (volheader->purpose == DB_PERMANENT_DATA_PURPOSE);
2013 
2014  disk_verify_volume_header (thread_p, rcv->pgptr);
2015  volheader->nsect_total += nsect_extend;
2016  disk_verify_volume_header (thread_p, rcv->pgptr);
2017 
2018  /* we may think that all these are new sectors, free to reserve, but that may not always be true:
2019  * 1. volume is extended.
2020  * 2. some of the sectors in the extended sectors are reserved.
2021  * 3. sector table pages are flushed to disk.
2022  * 4. other sectors are reserved.
2023  * 5. server crashes.
2024  *
2025  * at this point, sectors reserved in step #2 are already set and they cannot be considered free. */
2026 
2027  disk_stab_cursor_set_at_sectid (volheader, volheader->nsect_total - nsect_extend, &start_cursor);
2028  disk_stab_cursor_set_at_end (volheader, &end_cursor);
2029 
2030  error_code =
2031  disk_stab_iterate_units (thread_p, volheader, PGBUF_LATCH_READ, &start_cursor, &end_cursor, disk_stab_count_free,
2032  &nfree);
2033  if (error_code != NO_ERROR)
2034  {
2035  assert_release (false);
2036  return error_code;
2037  }
2038  assert (nfree <= nsect_extend);
2039 
2040  disk_Cache->perm_purpose_info.extend_info.nsect_total += nsect_extend;
2042  disk_cache_update_vol_free (volheader->volid, nfree);
2044 
2045  disk_log ("disk_rv_volhead_extend_redo", "extended volume %d total sectors %d (out of which %d were free). "
2046  "volume header lsa %lld|%d", volheader->volid, nsect_extend, nfree, PGBUF_PAGE_LSA_AS_ARGS (rcv->pgptr));
2047 
2048  pgbuf_set_dirty (thread_p, rcv->pgptr, DONT_FREE);
2049  return NO_ERROR;
2050 }
2051 
2052 /*
2053  * disk_rv_volhead_extend_undo () - undo the volume header update on aborted expansion
2054  *
2055  * return : NO_ERROR
2056  * thread_p (in) : thread entry
2057  * rcv (in) : recovery data
2058  */
2059 int
2061 {
2062  DISK_VOLUME_HEADER *volheader = (DISK_VOLUME_HEADER *) rcv->pgptr;
2063  DKNSECTS nsect_extend = *(DKNSECTS *) rcv->data;
2064 
2065  assert (rcv->length == sizeof (nsect_extend));
2066  assert (volheader->type == DB_PERMANENT_VOLTYPE);
2067  assert (volheader->purpose == DB_PERMANENT_DATA_PURPOSE);
2068 
2069  disk_verify_volume_header (thread_p, rcv->pgptr);
2070  volheader->nsect_total -= nsect_extend;
2071  disk_verify_volume_header (thread_p, rcv->pgptr);
2072 
2073  /* disk cache is updated, but no sector could be reserved.
2074  * NOTE: I assume here that extension is part of a committed sysop */
2075  disk_Cache->perm_purpose_info.extend_info.nsect_total -= nsect_extend;
2077  disk_cache_update_vol_free (volheader->volid, -nsect_extend);
2079 
2080  disk_log ("disk_rv_volhead_extend_undo", "undo volume %d extension - remove %d sectors. volume header lsa %lld|%d\n",
2081  volheader->volid, nsect_extend, PGBUF_PAGE_LSA_AS_ARGS (rcv->pgptr));
2082  pgbuf_set_dirty (thread_p, rcv->pgptr, DONT_FREE);
2083  return NO_ERROR;
2084 }
2085 
2086 /*
2087  * disk_add_volume () - add new volume (permanent or temporary)
2088  *
2089  * return : error code
2090  * thread_p (in) : thread entry
2091  * extinfo (in) : extend info
2092  * volid_out (out) : new volume identifier
2093  * nsects_free_out (out) : output the number of free sectors in new volume
2094  */
2095 static int
2096 disk_add_volume (THREAD_ENTRY * thread_p, DBDEF_VOL_EXT_INFO * extinfo, VOLID * volid_out, DKNSECTS * nsects_free_out)
2097 {
2098  char fullname[PATH_MAX];
2099  VOLID volid;
2100  DKNSECTS nsect_part_max;
2101  int error_code = NO_ERROR;
2102  bool can_overwrite;
2103 
2104  /* how it works:
2105  *
2106  * we need to do several steps to add a new volume:
2107  * 1. get from boot the full name (path + file name) and volume id.
2108  * 2. make sure there is enough space on disk to handle a new volume.
2109  * 3. notify page buffer (it needs to track permanent/temporary volumes).
2110  * 4. format new volume.
2111  * 5. update volume info file (if permanent volume).
2112  * 6. update boot_Db_parm.
2113  */
2114 
2115  if (disk_Cache->nvols_perm + disk_Cache->nvols_temp >= LOG_MAX_DBVOLID)
2116  {
2117  /* oops, too many volumes */
2120  }
2121 
2122  /* get from boot the volume name and identifier */
2123  error_code =
2124  boot_get_new_volume_name_and_id (thread_p, extinfo->voltype, extinfo->path, extinfo->name, fullname, &volid);
2125  if (error_code != NO_ERROR)
2126  {
2127  ASSERT_ERROR ();
2128  return error_code;
2129  }
2130 
2131  /* make sure the total and max size are rounded */
2132  extinfo->nsect_max = DISK_SECTS_ROUND_UP (extinfo->nsect_max);
2133  extinfo->nsect_total = DISK_SECTS_ROUND_UP (extinfo->nsect_total);
2134 
2135  disk_log ("disk_add_volume", "add new %s volume with purpose %s:\n" "\tname=%s\n" "\tcomments=%s\n" "\tpath=%s\n"
2136  "\tfullname = %s\n" "\ttotal sectors = %d\n" "\tmax sectors = %d", disk_type_to_string (extinfo->voltype),
2137  disk_purpose_to_string (extinfo->purpose), extinfo->name ? extinfo->name : "(UNKNOWN)",
2138  extinfo->comments ? extinfo->comments : "(UNKNOWN)", extinfo->path ? extinfo->path : "(UNKNOWN)",
2139  fullname, extinfo->nsect_total, extinfo->nsect_max);
2140 
2141 #if !defined (WINDOWS)
2142  {
2143  DBDEF_VOL_EXT_INFO temp_extinfo = *extinfo;
2144  char vol_realpath[PATH_MAX];
2145  char link_path[PATH_MAX];
2146  char link_fullname[PATH_MAX];
2147  struct stat stat_buf;
2148 
2149  if (stat (fullname, &stat_buf) == 0 /* file exists */
2150  && S_ISCHR (stat_buf.st_mode)) /* is the raw device? */
2151  {
2152  temp_extinfo.path = fileio_get_directory_path (link_path, boot_db_full_name ());
2153  if (temp_extinfo.path == NULL)
2154  {
2155  link_path[0] = '\0';
2156  temp_extinfo.path = link_path;
2157  }
2158  temp_extinfo.name = fileio_get_base_file_name (boot_db_full_name ());
2159  fileio_make_volume_ext_name (link_fullname, temp_extinfo.path, temp_extinfo.name, volid);
2160 
2161  if (realpath (fullname, vol_realpath) != NULL)
2162  {
2163  strcpy (fullname, vol_realpath);
2164  }
2165  (void) unlink (link_fullname);
2166  if (symlink (fullname, link_fullname) != 0)
2167  {
2168  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BO_CANNOT_CREATE_LINK, 2, fullname, link_fullname);
2169  return ER_BO_CANNOT_CREATE_LINK;
2170  }
2171  strcpy (fullname, link_fullname);
2172 
2173  /* we don't know character special files size */
2174  nsect_part_max = VOL_MAX_NSECTS (IO_PAGESIZE);
2175  }
2176  else
2177  {
2178  nsect_part_max = fileio_get_number_of_partition_free_sectors (fullname);
2179  }
2180  }
2181 #else /* WINDOWS */
2182  nsect_part_max = fileio_get_number_of_partition_free_sectors (fullname);
2183 #endif /* WINDOWS */
2184 
2185  if (nsect_part_max >= 0 && nsect_part_max < extinfo->nsect_max)
2186  {
2187  /* not enough space on disk */
2189  DISK_SECTS_NPAGES (extinfo->nsect_max), DISK_SECTS_SIZE (extinfo->nsect_max) / 1204 /* KB */ ,
2190  DISK_SECTS_NPAGES (nsect_part_max), DISK_SECTS_SIZE (nsect_part_max) / 1204 /* KB */ );
2192  }
2193 
2194  if (extinfo->comments == NULL)
2195  {
2196  extinfo->comments = "Volume Extension";
2197  }
2198  extinfo->name = fullname;
2199 
2200  if (!extinfo->overwrite && fileio_is_volume_exist (extinfo->name))
2201  {
2202  if (disk_can_overwrite_data_volume (thread_p, extinfo->name, &can_overwrite) != NO_ERROR
2203  || can_overwrite == false)
2204  {
2206  return ER_BO_VOLUME_EXISTS;
2207  }
2208  }
2209 
2210  log_sysop_start (thread_p);
2211 
2212  /* with disk_format, we start fixing pages. page fixing may depend on */
2213  if (extinfo->voltype == DB_PERMANENT_VOLTYPE)
2214  {
2215  disk_Cache->nvols_perm++;
2216  }
2217  else
2218  {
2219  disk_Cache->nvols_temp++;
2220  }
2221  disk_Cache->vols[volid].purpose = extinfo->purpose;
2222 
2223  error_code = disk_format (thread_p, boot_db_full_name (), volid, extinfo, nsects_free_out);
2224  if (error_code != NO_ERROR)
2225  {
2226  ASSERT_ERROR ();
2227  goto exit;
2228  }
2229 
2230  if (extinfo->voltype == DB_PERMANENT_VOLTYPE)
2231  {
2232  if (logpb_add_volume (NULL, volid, extinfo->name, DB_PERMANENT_DATA_PURPOSE) == NULL_VOLID)
2233  {
2234  ASSERT_ERROR_AND_SET (error_code);
2235  goto exit;
2236  }
2237  }
2238 
2239  /* this must be last step */
2240  error_code = boot_dbparm_save_volume (thread_p, extinfo->voltype, volid);
2241  if (error_code != NO_ERROR)
2242  {
2243  ASSERT_ERROR ();
2244  if (extinfo->voltype == DB_TEMPORARY_VOLTYPE)
2245  {
2246  /* rollback will not remove volume, we have to do it manually */
2247  if (disk_unformat (thread_p, extinfo->name) != NO_ERROR)
2248  {
2249  assert (false);
2250  }
2251  }
2252  goto exit;
2253  }
2254 
2255  assert (error_code == NO_ERROR);
2256  *volid_out = volid;
2257 
2258 exit:
2259  if (error_code == NO_ERROR)
2260  {
2261  log_sysop_commit (thread_p);
2262  }
2263  else
2264  {
2265  log_sysop_abort (thread_p);
2266 
2267  /* undo incrementing volume count. rollback won't do it. */
2268  if (extinfo->voltype == DB_TEMPORARY_VOLTYPE)
2269  {
2270  disk_Cache->nvols_temp--;
2271  }
2272  else
2273  {
2274  disk_Cache->nvols_perm--;
2275  }
2276  }
2277 
2278  return error_code;
2279 }
2280 
2281 /*
2282  * disk_add_volume_extension () - add new volume extension. this can be called by addvol utility or on create database.
2283  *
2284  * return : error code
2285  * thread_p (in) : thread entry
2286  * purpose (in) : permanent or temporary purpose
2287  * npages (in) : desired number of pages
2288  * path (in) : path to volume file
2289  * name (in) : name of volume file
2290  * comments (in) : comments in volume header
2291  * max_write_size_in_sec (in) : write speed (to limit IO when database is on line)
2292  * overwrite (in) : true to overwrite existing file, false otherwise
2293  * volid_out (out) : Output new volume identifier
2294  */
2295 int
2297  const char *name, const char *comments, int max_write_size_in_sec, bool overwrite,
2298  VOLID * volid_out)
2299 {
2300  DBDEF_VOL_EXT_INFO ext_info;
2301  VOLID volid_new;
2302  DKNSECTS nsect_free;
2303  char buf_realpath[PATH_MAX];
2304  int error_code = NO_ERROR;
2305 
2306  *volid_out = NULL_VOLID;
2307 
2308  error_code = csect_enter_as_reader (thread_p, CSECT_DISK_CHECK, INF_WAIT);
2309 
2310  /* first we need to block other expansions */
2311  disk_lock_extend ();
2312 
2313  /* build volume extension info */
2314  ext_info.purpose = purpose;
2315  if (path != NULL && realpath (path, buf_realpath) != NULL)
2316  {
2317  ext_info.path = buf_realpath;
2318  }
2319  else
2320  {
2321  ext_info.path = path;
2322  }
2323  ext_info.name = name;
2324  ext_info.comments = comments;
2325  ext_info.max_writesize_in_sec = max_write_size_in_sec;
2326  ext_info.overwrite = overwrite;
2327 
2328  /* compute total/max sectors. we always keep a rounded number of sectors. */
2329  ext_info.nsect_total = disk_sectors_to_extend_npages (npages);
2330  ext_info.nsect_max = ext_info.nsect_total;
2331  ext_info.max_npages = npages; /* this is obsolete. I set it just to see it if a crash occurs. */
2332 
2333  /* extensions are permanent */
2334  ext_info.voltype = DB_PERMANENT_VOLTYPE;
2335 
2336  /* add volume */
2337  error_code = disk_add_volume (thread_p, &ext_info, &volid_new, &nsect_free);
2338  if (error_code != NO_ERROR)
2339  {
2340  ASSERT_ERROR ();
2341  disk_unlock_extend ();
2342  csect_exit (thread_p, CSECT_DISK_CHECK);
2343  return error_code;
2344  }
2345  assert (volid_new == disk_Cache->nvols_perm - 1);
2346 
2347  if (ext_info.purpose == DB_PERMANENT_DATA_PURPOSE)
2348  {
2349  disk_Cache->perm_purpose_info.extend_info.nsect_total += ext_info.nsect_total;
2350  disk_Cache->perm_purpose_info.extend_info.nsect_max += ext_info.nsect_max;
2351  }
2352  else
2353  {
2354  disk_Cache->temp_purpose_info.nsect_perm_total += ext_info.nsect_total;
2355  }
2356 
2358  assert (disk_Cache->vols[volid_new].purpose == ext_info.purpose);
2359  assert (disk_Cache->vols[volid_new].nsect_free == 0);
2360 
2361  disk_cache_update_vol_free (volid_new, nsect_free);
2363 
2364  /* unblock expand */
2365  disk_unlock_extend ();
2366  csect_exit (thread_p, CSECT_DISK_CHECK);
2367 
2368  assert (disk_is_valid_volid (volid_new));
2369 
2370  /* output volume identifier */
2371  *volid_out = volid_new;
2372 
2373  /* success */
2374  return NO_ERROR;
2375 }
2376 
2377 /*
2378  * disk_volume_boot () - Boot disk volume.
2379  *
2380  * return : error code
2381  * thread_p (in) : thread entry
2382  * volid (in) : volume identifier
2383  * purpose_out (out) : output volume purpose
2384  * voltype_out (out) : output volume type
2385  * space_out (out) : output space information
2386  */
2387 static int
2388 disk_volume_boot (THREAD_ENTRY * thread_p, VOLID volid, DB_VOLPURPOSE * purpose_out, DB_VOLTYPE * voltype_out,
2389  DISK_VOLUME_SPACE_INFO * space_out)
2390 {
2391  PAGE_PTR page_volheader = NULL;
2392  DISK_VOLUME_HEADER *volheader;
2393  int error_code = NO_ERROR;
2394 
2395  assert (volid != NULL_VOLID);
2396 
2397  error_code = disk_get_volheader (thread_p, volid, PGBUF_LATCH_WRITE, &page_volheader, &volheader);
2398  if (error_code != NO_ERROR)
2399  {
2400  ASSERT_ERROR ();
2401  return error_code;
2402  }
2403 
2404  *purpose_out = volheader->purpose;
2405  *voltype_out = volheader->type;
2406 
2407  if (*voltype_out == DB_TEMPORARY_VOLTYPE)
2408  {
2409  /* don't load temporary volumes */
2410  pgbuf_unfix_and_init (thread_p, page_volheader);
2411  return NO_ERROR;
2412  }
2413 
2414  /* get space info */
2415  space_out->n_max_sects = volheader->nsect_max;
2416  space_out->n_total_sects = volheader->nsect_total;
2417 
2418  if (volheader->purpose == DB_TEMPORARY_DATA_PURPOSE)
2419  {
2420  /* reset volume */
2421  assert (volheader->nsect_max == volheader->nsect_total);
2422  /* set back sectors used by system */
2423  error_code = disk_stab_init (thread_p, volheader);
2424  if (error_code != NO_ERROR)
2425  {
2426  ASSERT_ERROR ();
2427  goto exit;
2428  }
2429  space_out->n_free_sects = space_out->n_total_sects - SECTOR_FROM_PAGEID (volheader->sys_lastpage) - 1;
2430  }
2431  else
2432  {
2433  space_out->n_free_sects = 0;
2434  error_code =
2436  &space_out->n_free_sects);
2437  if (error_code != NO_ERROR)
2438  {
2439  ASSERT_ERROR ();
2440  goto exit;
2441  }
2442  }
2443 
2444  assert (error_code == NO_ERROR);
2445 
2446 exit:
2447  if (page_volheader != NULL)
2448  {
2449  pgbuf_unfix (thread_p, page_volheader);
2450  }
2451 
2452  return error_code;
2453 }
2454 
2455 #if defined (SERVER_MODE)
2456 int
2457 disk_auto_expand (THREAD_ENTRY * thread_p)
2458 {
2459  int error_code = NO_ERROR;
2460 
2461  /* todo: we cannot expand the volumes unless we have a transaction descriptor. we might allocate a special tdes for
2462  * auto-volume expansion thread, similar to how vacuum works. otherwise, it can be limited to extend last
2463  * volume only.
2464  * for now, do nothing. we'll think about it later.
2465  */
2466 
2467  return error_code;
2468 }
2469 #endif /* SERVER_MODE */
2470 
2471 // *INDENT-OFF*
2472 #if defined (SERVER_MODE)
2473 static void
2474 disk_auto_expansion_execute (cubthread::entry & thread_ref)
2475 {
2476  if (!BO_IS_SERVER_RESTARTED ())
2477  {
2478  // wait for boot to finish
2479  return;
2480  }
2481 
2482  disk_auto_expand (&thread_ref);
2483 }
2484 #endif /* SERVER_MODE */
2485 
2486 #if defined (SERVER_MODE)
2487 /*
2488  * disk_auto_volume_expansion_daemon_init () - initialize disk auto volume expansion daemon
2489  */
2490 static void
2492 {
2493  // disk auto volume expansion is not yet implemented, uncomment below code when functionality will be available
2494  // see disk_auto_expand (THREAD_ENTRY *) function for more details
2495  /*
2496  assert (disk_Auto_volume_expansion_daemon == NULL);
2497 
2498  std::chrono::seconds interval_time = std::chrono::seconds (60);
2499  disk_Auto_volume_expansion_daemon = cubthread::get_manager ()->create_daemon (cubthread::looper (interval_time),
2500  new cubthread::entry_callable_task (disk_auto_expansion_execute));
2501  */
2502 }
2503 #endif /* SERVER_MODE */
2504 
2505 #if defined (SERVER_MODE)
2506 /*
2507  * disk_auto_volume_expansion_daemon_destroy () - destroy disk auto volume expansion daemon
2508  */
2509 static void
2511 {
2512  // disk auto volume expansion is not yet implemented, uncomment below code when functionality will be available
2513  // see disk_auto_expand (THREAD_ENTRY *) function for more details
2514  /*
2515  cubthread::get_manager ()->destroy_daemon (disk_Auto_volume_expansion_daemon);
2516  */
2517 }
2518 #endif /* SERVER_MODE */
2519 // *INDENT-ON*
2520 
2521 /************************************************************************/
2522 /* Disk cache section */
2523 /************************************************************************/
2524 
2525 /*
2526  * disk_cache_load_volume () - load and cache volume information
2527  *
2528  * return : true if successful, false otherwise
2529  * thread_p (in) : thread entry
2530  * volid (in) : volume identifier
2531  * ignore (in) : not used
2532  */
2533 static bool
2534 disk_cache_load_volume (THREAD_ENTRY * thread_p, INT16 volid, void *ignore)
2535 {
2536  DB_VOLPURPOSE vol_purpose;
2537  DB_VOLTYPE vol_type;
2539 
2540  if (disk_volume_boot (thread_p, volid, &vol_purpose, &vol_type, &space_info) != NO_ERROR)
2541  {
2542  ASSERT_ERROR ();
2543  return false;
2544  }
2545 
2546  if (vol_type != DB_PERMANENT_VOLTYPE)
2547  {
2548  /* don't save temporary volumes... they will be dropped anyway */
2549  return true;
2550  }
2551 
2552  /* called during boot, no sync required */
2553  if (vol_purpose == DB_PERMANENT_DATA_PURPOSE)
2554  {
2555  disk_Cache->perm_purpose_info.extend_info.nsect_free += space_info.n_free_sects;
2556  disk_Cache->perm_purpose_info.extend_info.nsect_total += space_info.n_total_sects;
2557  disk_Cache->perm_purpose_info.extend_info.nsect_max += space_info.n_max_sects;
2558 
2560  <= disk_Cache->perm_purpose_info.extend_info.nsect_total);
2562  <= disk_Cache->perm_purpose_info.extend_info.nsect_total);
2563 
2564  if (space_info.n_total_sects < space_info.n_max_sects)
2565  {
2568  }
2569  }
2570  else
2571  {
2572  assert (space_info.n_total_sects == space_info.n_max_sects);
2573 
2574  disk_Cache->temp_purpose_info.nsect_perm_free += space_info.n_free_sects;
2575  disk_Cache->temp_purpose_info.nsect_perm_total += space_info.n_total_sects;
2576 
2578  }
2579 
2580  disk_Cache->vols[volid].nsect_free = space_info.n_free_sects;
2581  disk_Cache->vols[volid].purpose = vol_purpose;
2582 
2583  disk_Cache->nvols_perm++;
2584  return true;
2585 }
2586 
2587 /*
2588  * disk_cache_init () - initialize disk cache
2589  *
2590  * return : error code
2591  * void (in) : void
2592  */
2593 static int
2595 {
2596  int i;
2597 
2598  assert (disk_Cache == NULL);
2599 
2600  disk_Cache = (DISK_CACHE *) malloc (sizeof (DISK_CACHE));
2601  if (disk_Cache == NULL)
2602  {
2604  return ER_OUT_OF_VIRTUAL_MEMORY;
2605  }
2606 
2607  disk_Cache->nvols_perm = 0;
2608  disk_Cache->nvols_temp = 0;
2609 
2612  disk_Cache->perm_purpose_info.extend_info.nsect_free = 0;
2613  disk_Cache->perm_purpose_info.extend_info.nsect_total = 0;
2614  disk_Cache->perm_purpose_info.extend_info.nsect_max = 0;
2619 #if !defined (NDEBUG)
2620  disk_Cache->perm_purpose_info.extend_info.owner_reserve = -1;
2621 #endif /* !NDEBUG */
2622 
2625  disk_Cache->temp_purpose_info.nsect_perm_free = 0;
2626  disk_Cache->temp_purpose_info.nsect_perm_total = 0;
2627  disk_Cache->temp_purpose_info.extend_info.nsect_free = 0;
2628  disk_Cache->temp_purpose_info.extend_info.nsect_total = 0;
2629  disk_Cache->temp_purpose_info.extend_info.nsect_max = 0;
2634 #if !defined (NDEBUG)
2635  disk_Cache->temp_purpose_info.extend_info.owner_reserve = -1;
2636 #endif /* !NDEBUG */
2637 
2638  pthread_mutex_init (&disk_Cache->mutex_extend, NULL);
2639 #if !defined (NDEBUG)
2640  disk_Cache->owner_extend = -1;
2641 #endif /* !NDEBUG */
2642 
2643  for (i = 0; i <= LOG_MAX_DBVOLID; i++)
2644  {
2645  disk_Cache->vols[i].purpose = DISK_UNKNOWN_PURPOSE;
2646  disk_Cache->vols[i].nsect_free = 0;
2647  }
2648  return NO_ERROR;
2649 }
2650 
2651 /*
2652  * disk_cache_final () - finalize disk cache
2653  */
2654 static void
2656 {
2657  if (disk_Cache == NULL)
2658  {
2659  /* not initialized */
2660  return;
2661  }
2662 
2663  assert (disk_Cache->perm_purpose_info.extend_info.owner_reserve == -1);
2664  assert (disk_Cache->temp_purpose_info.extend_info.owner_reserve == -1);
2665  assert (disk_Cache->owner_extend == -1);
2666 
2669  pthread_mutex_destroy (&disk_Cache->mutex_extend);
2670 
2671  free_and_init (disk_Cache);
2672 }
2673 
2674 /*
2675  * disk_cache_load_all_volumes () - load all disk volumes and save info to disk cache
2676  *
2677  * return : true if successful, false otherwise
2678  * thread_p (in) : thread entry
2679  */
2680 static bool
2682 {
2683  /* Cache every single volume */
2684  assert (disk_Cache != NULL);
2685  return fileio_map_mounted (thread_p, disk_cache_load_volume, NULL);
2686 }
2687 
2688 /*
2689  * disk_cache_free_reserved () - add reserved sectors to cache free sectors
2690  *
2691  * return : void
2692  * context (in) : reserve context
2693  */
2694 STATIC_INLINE void
2696 {
2697  int iter;
2698 
2700  for (iter = 0; iter < context->n_cache_vol_reserve; iter++)
2701  {
2703  }
2705 }
2706 
2707 /*
2708  * disk_cache_update_vol_free () - update number of volume free sectors in cache
2709  *
2710  * return : void
2711  * volid (in) : volume identifier
2712  * delta_free (in) : delta free sectors
2713  */
2714 STATIC_INLINE void
2716 {
2717  /* must be locked */
2718  disk_Cache->vols[volid].nsect_free += delta_free;
2719  assert (disk_Cache->vols[volid].nsect_free >= 0);
2720  disk_check_own_reserve_for_purpose (disk_Cache->vols[volid].purpose);
2721  if (disk_Cache->vols[volid].purpose == DB_PERMANENT_DATA_PURPOSE)
2722  {
2724  disk_Cache->perm_purpose_info.extend_info.nsect_free += delta_free;
2725  assert (disk_Cache->perm_purpose_info.extend_info.nsect_free >= 0);
2726 
2727  disk_log ("disk_cache_update_vol_free", "updated cached free for volid %d to %d and perm_perm to %d; "
2728  "delta free = %d", volid, disk_Cache->vols[volid].nsect_free,
2729  disk_Cache->perm_purpose_info.extend_info.nsect_free, delta_free);
2730  }
2731  else
2732  {
2733  if (disk_get_voltype (volid) == DB_PERMANENT_VOLTYPE)
2734  {
2735  disk_Cache->temp_purpose_info.nsect_perm_free += delta_free;
2736  assert (disk_Cache->temp_purpose_info.nsect_perm_free >= 0);
2737 
2738  disk_log ("disk_cache_update_vol_free", "updated cached free for volid %d to %d and perm_temp to %d; "
2739  "delta free = %d", volid, disk_Cache->vols[volid].nsect_free,
2740  disk_Cache->temp_purpose_info.nsect_perm_free, delta_free);
2741  }
2742  else
2743  {
2744  disk_Cache->temp_purpose_info.extend_info.nsect_free += delta_free;
2745  assert (disk_Cache->temp_purpose_info.extend_info.nsect_free >= 0);
2746 
2747  disk_log ("disk_cache_update_vol_free", "updated cached free for volid %d to %d and temp_temp to %d; "
2748  "delta free = %d", volid, disk_Cache->vols[volid].nsect_free,
2749  disk_Cache->temp_purpose_info.extend_info.nsect_free, delta_free);
2750  }
2751  }
2752 }
2753 
2754 /*
2755  * disk_lock_extend () - lock volume extension mutex
2756  */
2757 void
2759 {
2760 #if defined (NDEBUG)
2761  pthread_mutex_lock (&disk_Cache->mutex_extend);
2762 #else /* !NDEBUG */
2763  int me = thread_get_current_entry_index ();
2764 
2765  assert (me != disk_Cache->perm_purpose_info.extend_info.owner_reserve);
2766  assert (me != disk_Cache->temp_purpose_info.extend_info.owner_reserve);
2767  if (me == disk_Cache->owner_extend)
2768  {
2769  /* already owner */
2770  assert (false);
2771  return;
2772  }
2773 
2774  pthread_mutex_lock (&disk_Cache->mutex_extend);
2775  assert (disk_Cache->owner_extend == -1);
2776  disk_Cache->owner_extend = me;
2777 #endif /* !NDEBUG */
2778 }
2779 
2780 /*
2781  * disk_unlock_extend () - unlock volume extension mutex
2782  */
2783 void
2785 {
2786 #if defined (NDEBUG)
2787  pthread_mutex_unlock (&disk_Cache->mutex_extend);
2788 #else /* !NDEBUG */
2789  int me = thread_get_current_entry_index ();
2790 
2791  assert (disk_Cache->owner_extend == me);
2792  disk_Cache->owner_extend = -1;
2793  pthread_mutex_unlock (&disk_Cache->mutex_extend);
2794 #endif /* !NDEBUG */
2795 }
2796 
2797 /*
2798  * disk_cache_lock_reserve_for_purpose () - lock reservations for given purpose
2799  *
2800  * return : void
2801  * purpose (in) : permanent/temporary purpose
2802  */
2803 STATIC_INLINE void
2805 {
2806  if (purpose == DB_PERMANENT_DATA_PURPOSE)
2807  {
2809  }
2810  else
2811  {
2813  }
2814 }
2815 
2816 /*
2817  * disk_cache_unlock_reserve_for_purpose () - unlock reservations for given purpose
2818  *
2819  * return : void
2820  * purpose (in) : permanent/temporary purpose
2821  */
2822 STATIC_INLINE void
2824 {
2825  if (purpose == DB_PERMANENT_DATA_PURPOSE)
2826  {
2828  }
2829  else
2830  {
2832  }
2833 }
2834 
2835 /*
2836  * disk_cache_lock_reserve () - lock reservations (permanent or temporary)
2837  *
2838  * return : void
2839  * extend_info (in) : extend info (permanent or temporary)
2840  */
2841 STATIC_INLINE void
2843 {
2844 #if defined (NDEBUG)
2845  pthread_mutex_lock (&extend_info->mutex_reserve);
2846 #else /* !NDEBUG */
2847  int me = thread_get_current_entry_index ();
2848 
2849  if (me == extend_info->owner_reserve)
2850  {
2851  /* already owner */
2852  assert (false);
2853  return;
2854  }
2855  pthread_mutex_lock (&extend_info->mutex_reserve);
2856  assert (extend_info->owner_reserve == -1);
2857  extend_info->owner_reserve = me;
2858 #endif /* !NDEBUG */
2859 }
2860 
2861 /*
2862  * disk_cache_unlock_reserve () - lock reservations (permanent or temporary)
2863  *
2864  * return : void
2865  * extend_info (in) : extend info (permanent or temporary)
2866  */
2867 STATIC_INLINE void
2869 {
2870 #if defined (NDEBUG)
2871  pthread_mutex_unlock (&extend_info->mutex_reserve);
2872 #else /* !NDEBUG */
2873  int me = thread_get_current_entry_index ();
2874 
2875  assert (me == extend_info->owner_reserve);
2876  extend_info->owner_reserve = -1;
2877  pthread_mutex_unlock (&extend_info->mutex_reserve);
2878 #endif /* !NDEBUG */
2879 }
2880 
2881 /************************************************************************/
2882 /* Disk volume header section */
2883 /************************************************************************/
2884 
2885 /*
2886  * disk_volume_header_start_scan () - start scan function for show volume header
2887  * return: NO_ERROR, or ER_code
2888  *
2889  * thread_p(in):
2890  * type (in):
2891  * arg_values(in):
2892  * arg_cnt(in):
2893  * ptr(in/out): volume header context
2894  */
2895 extern int
2896 disk_volume_header_start_scan (THREAD_ENTRY * thread_p, int type, DB_VALUE ** arg_values, int arg_cnt, void **ptr)
2897 {
2898  int error = NO_ERROR;
2900 
2901  ctx = (DISK_VOL_HEADER_CONTEXT *) db_private_alloc (thread_p, sizeof (DISK_VOL_HEADER_CONTEXT));
2902  if (ctx == NULL)
2903  {
2904  ASSERT_ERROR_AND_SET (error);
2905  goto exit_on_error;
2906  }
2907  memset (ctx, 0, sizeof (DISK_VOL_HEADER_CONTEXT));
2908 
2909  assert (arg_values != NULL && arg_cnt && arg_values[0] != NULL);
2910  assert (DB_VALUE_TYPE (arg_values[0]) == DB_TYPE_INTEGER);
2911  ctx->volume_id = db_get_int (arg_values[0]);
2912 
2913  /* if volume id is out of range */
2914  if (ctx->volume_id < 0 || ctx->volume_id > DB_INT16_MAX)
2915  {
2917  error = ER_DIAG_VOLID_NOT_EXIST;
2918  goto exit_on_error;
2919  }
2920 
2921  /* check volume id exist or not */
2922  if (xdisk_is_volume_exist (thread_p, (INT16) ctx->volume_id) == false)
2923  {
2925  error = ER_DIAG_VOLID_NOT_EXIST;
2926  goto exit_on_error;
2927  }
2928 
2929  *ptr = ctx;
2930  return NO_ERROR;
2931 
2932 exit_on_error:
2933  if (ctx != NULL)
2934  {
2935  db_private_free (thread_p, ctx);
2936  }
2937  return error;
2938 }
2939 
2940 /*
2941  * disk_volume_header_next_scan () - next scan function for show volume header
2942  * return: NO_ERROR, or ER_code
2943  *
2944  * thread_p(in):
2945  * ptr(in/out): volume header context
2946  */
2947 SCAN_CODE
2948 disk_volume_header_next_scan (THREAD_ENTRY * thread_p, int cursor, DB_VALUE ** out_values, int out_cnt, void *ptr)
2949 {
2950  DISK_VOLUME_HEADER *vhdr;
2951  int error = NO_ERROR, idx = 0;
2952  PAGE_PTR pgptr = NULL;
2953  DB_DATETIME create_time;
2954  char buf[256];
2956 
2957  if (cursor >= 1)
2958  {
2959  return S_END;
2960  }
2961 
2962  error = disk_get_volheader (thread_p, ctx->volume_id, PGBUF_LATCH_READ, &pgptr, &vhdr);
2963  if (error != NO_ERROR)
2964  {
2965  ASSERT_ERROR ();
2966  goto exit;
2967  }
2968 
2969  /* fill each column */
2970  db_make_int (out_values[idx], ctx->volume_id);
2971  idx++;
2972 
2973  snprintf (buf, sizeof (buf), "MAGIC SYMBOL = %s at disk location = %lld", vhdr->magic,
2974  offsetof (FILEIO_PAGE, page) + (long long) offsetof (DISK_VOLUME_HEADER, magic));
2975  error = db_make_string_copy (out_values[idx], buf);
2976  idx++;
2977  if (error != NO_ERROR)
2978  {
2979  goto exit;
2980  }
2981 
2982  db_make_int (out_values[idx], vhdr->iopagesize);
2983  idx++;
2984 
2985  db_make_string (out_values[idx], disk_purpose_to_string (vhdr->purpose));
2986  idx++;
2987 
2988  db_make_string (out_values[idx], disk_type_to_string (vhdr->type));
2989  idx++;
2990 
2991  db_make_int (out_values[idx], vhdr->sect_npgs);
2992  idx++;
2993 
2994  db_make_int (out_values[idx], vhdr->nsect_total);
2995  idx++;
2996 
2997  /* free sects are not kept in header */
2998  db_make_int (out_values[idx], disk_Cache->vols[ctx->volume_id].nsect_free);
2999  idx++;
3000 
3001  db_make_int (out_values[idx], vhdr->nsect_max);
3002  idx++;
3003 
3004  db_make_int (out_values[idx], vhdr->hint_allocsect);
3005  idx++;
3006 
3007  db_make_int (out_values[idx], vhdr->stab_npages);
3008  idx++;
3009 
3010  db_make_int (out_values[idx], vhdr->stab_first_page);
3011  idx++;
3012 
3013  db_make_int (out_values[idx], vhdr->sys_lastpage);
3014  idx++;
3015 
3016  db_localdatetime ((time_t *) (&vhdr->db_creation), &create_time);
3017  db_make_datetime (out_values[idx], &create_time);
3018  idx++;
3019 
3020  db_make_int (out_values[idx], vhdr->db_charset);
3021  idx++;
3022 
3023  lsa_to_string (buf, sizeof (buf), &vhdr->chkpt_lsa);
3024  error = db_make_string_copy (out_values[idx], buf);
3025  idx++;
3026  if (error != NO_ERROR)
3027  {
3028  goto exit;
3029  }
3030 
3031  error = db_make_string_copy (out_values[idx], hfid_to_string (buf, sizeof (buf), &vhdr->boot_hfid));
3032  idx++;
3033  if (error != NO_ERROR)
3034  {
3035  goto exit;
3036  }
3037 
3038  error = db_make_string_copy (out_values[idx], (char *) (vhdr->var_fields + vhdr->offset_to_vol_fullname));
3039  idx++;
3040  if (error != NO_ERROR)
3041  {
3042  goto exit;
3043  }
3044 
3045  db_make_int (out_values[idx], vhdr->next_volid);
3046  idx++;
3047 
3048  error = db_make_string_copy (out_values[idx], (char *) (vhdr->var_fields + vhdr->offset_to_next_vol_fullname));
3049  idx++;
3050  if (error != NO_ERROR)
3051  {
3052  goto exit;
3053  }
3054 
3055  error = db_make_string_copy (out_values[idx], (char *) (vhdr->var_fields + vhdr->offset_to_vol_remarks));
3056  idx++;
3057  if (error != NO_ERROR)
3058  {
3059  goto exit;
3060  }
3061 
3062  assert (idx == out_cnt);
3063 
3064 exit:
3065  if (pgptr)
3066  {
3067  pgbuf_unfix (thread_p, pgptr);
3068  }
3069  return error == NO_ERROR ? S_SUCCESS : S_ERROR;
3070 }
3071 
3072 /*
3073  * disk_volume_header_end_scan() -- end scan function of show volume header
3074  * return: NO_ERROR, or ER_code
3075  *
3076  * thread_p(in):
3077  * ptr(in): volume header context
3078  */
3079 int
3081 {
3082  db_private_free_and_init (thread_p, *ptr);
3083  return NO_ERROR;
3084 }
3085 
3086 /*
3087  * disk_vhdr_dump () - Dump the volume header structure.
3088  * return: void
3089  * vhdr(in): Pointer to volume header
3090  *
3091  * Note: This function is used for debugging purposes.
3092  */
3093 static void
3094 disk_vhdr_dump (FILE * fp, const DISK_VOLUME_HEADER * vhdr)
3095 {
3096  char time_val[CTIME_MAX];
3097  time_t tmp_time;
3098 
3099  (void) fprintf (fp, " MAGIC SYMBOL = %s at disk location = %lld\n", vhdr->magic,
3100  offsetof (FILEIO_PAGE, page) + (long long) offsetof (DISK_VOLUME_HEADER, magic));
3101  (void) fprintf (fp, " io_pagesize = %d,\n", vhdr->iopagesize);
3102  (void) fprintf (fp, " VID = %d, VOL_FULLNAME = %s\n VOL PURPOSE = %s\n VOL TYPE = %s VOL_REMARKS = %s\n",
3105  (void) fprintf (fp, " NEXT_VID = %d, NEXT_VOL_FULLNAME = %s\n", vhdr->next_volid,
3107  (void) fprintf (fp, " LAST SYSTEM PAGE = %d\n", vhdr->sys_lastpage);
3108  (void) fprintf (fp, " SECTOR: SIZE IN PAGES = %10d, TOTAL = %10d,", vhdr->sect_npgs, vhdr->nsect_total);
3109  (void) fprintf (fp, " FREE = %10d, MAX=%10d\n", disk_Cache->vols[vhdr->volid].nsect_free, vhdr->nsect_max);
3110  (void) fprintf (fp, " %10s HINT_ALLOC = %10d\n", " ", vhdr->hint_allocsect);
3111  (void) fprintf (fp, " SECTOR TABLE: SIZE IN PAGES = %10d, FIRST_PAGE = %5d\n", vhdr->stab_npages,
3112  vhdr->stab_first_page);
3113 
3114  tmp_time = (time_t) vhdr->db_creation;
3115  (void) ctime_r (&tmp_time, time_val);
3116  (void) fprintf (fp, " Database creation time = %s\n Lowest Checkpoint for recovery = %lld|%lld\n", time_val,
3117  (long long) vhdr->chkpt_lsa.pageid, (long long) vhdr->chkpt_lsa.offset);
3118  (void) fprintf (fp, "Boot_hfid: volid %d, fileid %d header_pageid %d\n", vhdr->boot_hfid.vfid.volid,
3119  vhdr->boot_hfid.vfid.fileid, vhdr->boot_hfid.hpgid);
3120  (void) fprintf (fp, " db_charset = %d\n", vhdr->db_charset);
3121 }
3122 
3123 /*
3124  * disk_volume_header_set_stab () - knowing the maximum number of sectors, set header fields for sector tables
3125  *
3126  * return : void
3127  * volheader (in) : volume header
3128  */
3129 STATIC_INLINE void
3131 {
3132  volheader->stab_first_page = DISK_VOLHEADER_PAGE + 1;
3133  volheader->stab_npages = CEIL_PTVDIV (volheader->nsect_max, DISK_STAB_PAGE_BIT_COUNT);
3134  volheader->sys_lastpage = volheader->stab_first_page + volheader->stab_npages - 1;
3135 }
3136 
3137 /*
3138  * disk_verify_volume_header () -
3139  * return: void
3140  * pgptr(in): Pointer to volume header page
3141  */
3142 STATIC_INLINE void
3144 {
3145 #if !defined (NDEBUG)
3146  DISK_VOLUME_HEADER *vhdr;
3147 
3148  assert (pgptr != NULL);
3149  (void) pgbuf_check_page_ptype (thread_p, pgptr, PAGE_VOLHEADER);
3150  vhdr = (DISK_VOLUME_HEADER *) pgptr;
3151 
3152  assert (vhdr->sect_npgs == DISK_SECTOR_NPAGES);
3153  assert (vhdr->nsect_total > 0);
3154  assert (vhdr->nsect_total <= vhdr->nsect_max);
3157  assert (disk_Cache == NULL || disk_Cache->vols[vhdr->volid].nsect_free <= vhdr->nsect_total);
3158 
3162  assert (vhdr->sys_lastpage == (vhdr->stab_first_page + vhdr->stab_npages - 1));
3163 
3165  assert (vhdr->type == DB_PERMANENT_VOLTYPE || vhdr->type == DB_TEMPORARY_VOLTYPE);
3167 #endif /* !NDEBUG */
3168 }
3169 
3170 /*
3171  * disk_get_volheader_internal () - get volume header page and header
3172  *
3173  * return : error code
3174  * thread_p (in) : thread entry
3175  * volid (in) : volume id
3176  * latch_mode (in) : latch mode for volume header page
3177  * page_volheader_out (out) : output volume header page
3178  * volheader_out (out) : output volume header
3179  * file (in) : (debug only) caller file
3180  * line (in) : (debug only) caller line
3181  */
3182 STATIC_INLINE int
3184  PAGE_PTR * page_volheader_out, DISK_VOLUME_HEADER ** volheader_out
3185 #if !defined (NDEBUG)
3186  , const char *file, int line
3187 #endif /* !NDEBUG */
3188  )
3189 {
3190  VPID vpid_volheader;
3191  int error_code = NO_ERROR;
3192 
3193  vpid_volheader.volid = volid;
3194  vpid_volheader.pageid = DISK_VOLHEADER_PAGE;
3195 
3196  *page_volheader_out = pgbuf_fix (thread_p, &vpid_volheader, OLD_PAGE, latch_mode, PGBUF_UNCONDITIONAL_LATCH);
3197  if (*page_volheader_out == NULL)
3198  {
3199  ASSERT_ERROR_AND_SET (error_code);
3200  return error_code;
3201  }
3202 
3203  disk_verify_volume_header (thread_p, *page_volheader_out);
3204  *volheader_out = (DISK_VOLUME_HEADER *) (*page_volheader_out);
3205 
3206  return NO_ERROR;
3207 }
3208 
3209 /************************************************************************/
3210 /* Disk allocation table section. */
3211 /************************************************************************/
3212 
3213 /*
3214  * disk_stab_cursor_set_at_sectid () - Position cursor for allocation table at sector ID.
3215  *
3216  * return : Void.
3217  * volheader (in) : Volume header.
3218  * sectid (in) : Sector ID.
3219  * cursor (out) : Allocation table cursor.
3220  */
3221 STATIC_INLINE void
3223 {
3224  assert (volheader != NULL);
3225  assert (cursor != NULL);
3226  assert (sectid >= 0 && sectid <= volheader->nsect_total);
3227 
3228  cursor->volheader = volheader;
3229  cursor->sectid = sectid;
3230 
3231  cursor->pageid = volheader->stab_first_page + DISK_ALLOCTBL_SECTOR_PAGE_OFFSET (sectid);
3232  assert (cursor->pageid < volheader->stab_first_page + volheader->stab_npages);
3235 
3236  cursor->page = NULL;
3237  cursor->unit = NULL;
3238 }
3239 
3240 /*
3241  * disk_stab_cursor_set_at_end () - Position cursor at the end of allocation table.
3242  *
3243  * return : Void.
3244  * volheader (in) : Volume header.
3245  * cursor (out) : Allocation table cursor.
3246  */
3247 STATIC_INLINE void
3249 {
3250  assert (volheader != NULL);
3251  assert (cursor != NULL);
3252 
3253  cursor->volheader = volheader;
3254 
3256  disk_stab_cursor_set_at_sectid (volheader, volheader->nsect_total, cursor);
3257 }
3258 
3259 /*
3260  * disk_stab_cursor_set_at_start () - Position cursor at the start of allocation table.
3261  *
3262  * return : Void.
3263  * volheader (in) : Volume header.
3264  * cursor (out) : Allocation table cursor.
3265  */
3266 STATIC_INLINE void
3268 {
3269  assert (volheader != NULL);
3270  assert (cursor != NULL);
3271 
3272  cursor->volheader = volheader;
3273  cursor->sectid = 0;
3274 
3275  cursor->pageid = volheader->stab_first_page;
3276  cursor->offset_to_unit = 0;
3277  cursor->offset_to_bit = 0;
3278  cursor->page = NULL;
3279  cursor->unit = NULL;
3280 }
3281 
3282 /*
3283  * disk_stab_cursor_compare () - Compare two allocation table cursors.
3284  *
3285  * return : -1 if first cursor is positioned before second cursor.
3286  * 0 if both cursors have the same position.
3287  * 1 if first cursor is positioned after second cursor.
3288  * first_cursor (in) : First cursor.
3289  * second_cursor (in) : Second cursor.
3290  */
3291 STATIC_INLINE int
3292 disk_stab_cursor_compare (const DISK_STAB_CURSOR * first_cursor, const DISK_STAB_CURSOR * second_cursor)
3293 {
3294  assert (first_cursor != NULL);
3295  assert (second_cursor != NULL);
3296 
3297  if (first_cursor->pageid < second_cursor->pageid)
3298  {
3299  return -1;
3300  }
3301  else if (first_cursor->pageid > second_cursor->pageid)
3302  {
3303  return 1;
3304  }
3305 
3306  if (first_cursor->offset_to_unit < second_cursor->offset_to_unit)
3307  {
3308  return -1;
3309  }
3310  else if (first_cursor->offset_to_unit > second_cursor->offset_to_unit)
3311  {
3312  return 1;
3313  }
3314 
3315  if (first_cursor->offset_to_bit < second_cursor->offset_to_bit)
3316  {
3317  return -1;
3318  }
3319  else if (first_cursor->offset_to_bit > second_cursor->offset_to_bit)
3320  {
3321  return 1;
3322  }
3323 
3324  return 0;
3325 }
3326 
3327 /*
3328  * disk_stab_cursor_check_valid () - Check allocation table cursor validity.
3329  *
3330  * return : void.
3331  * cursor (in) : Allocation table cursor.
3332  *
3333  * NOTE: This is a debug function.
3334  */
3335 STATIC_INLINE void
3337 {
3338  assert (cursor != NULL);
3339 
3340  if (cursor->pageid == NULL_PAGEID)
3341  {
3342  /* Must be recovery. */
3343  assert (!LOG_ISRESTARTED ());
3344  }
3345  else
3346  {
3347  /* Cursor must have valid volheader pointer. */
3348  assert (cursor->volheader != NULL);
3349 
3350  /* Cursor must have a valid position. */
3351  assert (cursor->pageid >= cursor->volheader->stab_first_page);
3352  assert (cursor->pageid < cursor->volheader->stab_first_page + cursor->volheader->stab_npages);
3354  + cursor->offset_to_unit * DISK_STAB_UNIT_BIT_COUNT + cursor->offset_to_bit == cursor->sectid);
3355  }
3356 
3357  assert (cursor->offset_to_unit >= 0);
3359  assert (cursor->offset_to_bit >= 0);
3361 
3362  if (cursor->unit != NULL)
3363  {
3364  /* Must have a page fixed */
3365  assert (cursor->page != NULL);
3366  /* Unit pointer must match offset_to_unit */
3367  assert ((int) ((char *) cursor->unit - cursor->page) == (int) (cursor->offset_to_unit * DISK_STAB_UNIT_SIZE_OF));
3368  }
3369 }
3370 
3371 /*
3372  * disk_stab_cursor_is_bit_set () - Is bit set on current allocation table cursor.
3373  *
3374  * return : True if bit is set, false otherwise.
3375  * cursor (in) : Allocation table cursor.
3376  */
3377 STATIC_INLINE bool
3379 {
3381 
3382  /* update if unit size is changed */
3383  return bit64_is_set (*cursor->unit, cursor->offset_to_bit);
3384 }
3385 
3386 /*
3387  * disk_stab_cursor_set_bit () - Set bit on current allocation table cursor.
3388  *
3389  * return : Void.
3390  * cursor (in/out) : Allocation table cursor.
3391  */
3392 STATIC_INLINE void
3394 {
3396 
3397  assert (!disk_stab_cursor_is_bit_set (cursor));
3398 
3399  /* update if unit size is changed */
3400  *cursor->unit = bit64_set (*cursor->unit, cursor->offset_to_bit);
3401 }
3402 
3403 /*
3404  * disk_stab_cursor_clear_bit () - Clear bit on current allocation table cursor.
3405  *
3406  * return : Void.
3407  * cursor (in/out) : Allocation table cursor.
3408  */
3409 STATIC_INLINE void
3411 {
3413 
3414  assert (!disk_stab_cursor_is_bit_set (cursor));
3415 
3416  /* update if unit size is changed */
3417  *cursor->unit = bit64_clear (*cursor->unit, cursor->offset_to_bit);
3418 }
3419 
3420 /*
3421  * disk_stab_cursor_get_bit_index_in_page () - Get the index of cursor bit in allocation table page.
3422  *
3423  * return : Bit index in page.
3424  * cursor (in) : Allocation table cursor.
3425  */
3426 STATIC_INLINE int
3428 {
3430 
3431  return cursor->offset_to_unit * DISK_STAB_UNIT_BIT_COUNT + cursor->offset_to_bit;
3432 }
3433 
3434 /*
3435  * disk_alloctbl_cursor_get_sectid () - Get the sector ID of cursor.
3436  *
3437  * return : Sector ID.
3438  * cursor (in) : Allocation table cursor.
3439  */
3442 {
3444 
3445  return cursor->sectid;
3446 }
3447 
3448 /*
3449  * disk_stab_cursor_fix () - Fix current table page.
3450  *
3451  * return : Error code.
3452  * thread_p (in) : Thread entry.
3453  * cursor (in/out) : Allocation table cursor.
3454  * latch_mode (in) : Page latch mode.
3455  */
3456 STATIC_INLINE int
3458 {
3460  int error_code = NO_ERROR;
3461 
3462  assert (cursor->page == NULL);
3463 
3464  cursor->unit = NULL;
3465 
3466  /* Fix page. */
3467  vpid.volid = cursor->volheader->volid;
3468  vpid.pageid = cursor->pageid;
3469  cursor->page = pgbuf_fix (thread_p, &vpid, OLD_PAGE, latch_mode, PGBUF_UNCONDITIONAL_LATCH);
3470  if (cursor->page == NULL)
3471  {
3472  ASSERT_ERROR_AND_SET (error_code);
3473  return error_code;
3474  }
3475 
3476  cursor->unit = ((DISK_STAB_UNIT *) cursor->page) + cursor->offset_to_unit;
3477 
3478  return NO_ERROR;
3479 }
3480 
3481 /*
3482  * disk_stab_cursor_unfix () - Unfix page from allocation table cursor.
3483  *
3484  * return : Void.
3485  * thread_p (in) : Thread entry.
3486  * cursor (in/out) : Allocation table cursor.
3487  */
3488 STATIC_INLINE void
3490 {
3491  if (cursor->page != NULL)
3492  {
3493  pgbuf_unfix_and_init (thread_p, cursor->page);
3494  }
3495  cursor->unit = NULL;
3496 }
3497 
3498 /*
3499  * disk_stab_unit_reserve () - DISK_STAB_UNIT_FUNC function used to lookup and reserve free sectors
3500  *
3501  * return : NO_ERROR
3502  * thread_p (in) : thread entry
3503  * cursor (in) : disk sector table cursor
3504  * stop (out) : output true when all required sectors are reserved
3505  * args (in/out) : reserve context
3506  */
3507 static int
3508 disk_stab_unit_reserve (THREAD_ENTRY * thread_p, DISK_STAB_CURSOR * cursor, bool * stop, void *args)
3509 {
3510  DISK_RESERVE_CONTEXT *context;
3511  DISK_STAB_UNIT log_unit;
3512  SECTID sectid;
3513 
3514  /* how it works
3515  * look for unset bits and reserve the required number of sectors.
3516  * we have two special cases, which can be very usual:
3517  * 1. full unit - nothing can be reserved, so we early out
3518  * 2. empty unit - we can consume it all (if we need it all) or just trailing bits.
3519  * otherwise, we iterate bit by bit and reserve free sectors.
3520  */
3521 
3522  if (*cursor->unit == BIT64_FULL)
3523  {
3524  /* nothing free */
3525  return NO_ERROR;
3526  }
3527 
3528  context = (DISK_RESERVE_CONTEXT *) args;
3529  assert (context->nsects_lastvol_remaining > 0);
3530 
3531  if (*cursor->unit == 0)
3532  {
3533  /* empty unit. set all required bits */
3534  int bits_to_set = MIN (context->nsects_lastvol_remaining, DISK_STAB_UNIT_BIT_COUNT);
3535  int i;
3536 
3537  if (bits_to_set == DISK_STAB_UNIT_BIT_COUNT)
3538  {
3539  /* Consume all unit */
3540  *cursor->unit = BIT64_FULL;
3541  }
3542  else
3543  {
3544  /* consume only part of unit */
3545  *cursor->unit = bit64_set_trailing_bits (*cursor->unit, bits_to_set);
3546  }
3547  /* what we log */
3548  log_unit = *cursor->unit;
3549 
3550  /* update reserve context */
3551  context->nsects_lastvol_remaining -= bits_to_set;
3552 
3553  /* save sector ids */
3554  for (i = 0, sectid = disk_stab_cursor_get_sectid (cursor); i < bits_to_set; i++, sectid++)
3555  {
3556  context->vsidp->volid = cursor->volheader->volid;
3557  context->vsidp->sectid = sectid;
3558  context->vsidp++;
3559 
3560  disk_log ("disk_stab_unit_reserve", "reserved sectid %d in volume %d.", sectid, cursor->volheader->volid);
3561  }
3562  }
3563  else
3564  {
3565  /* iterate through unit bits */
3566  log_unit = 0;
3567  for (cursor->offset_to_bit = bit64_count_trailing_ones (*cursor->unit), cursor->sectid += cursor->offset_to_bit;
3569  cursor->offset_to_bit++, cursor->sectid++)
3570  {
3572 
3573  if (!disk_stab_cursor_is_bit_set (cursor))
3574  {
3575  /* reserve this sector */
3576  disk_stab_cursor_set_bit (cursor);
3577 
3578  /* update what we log */
3579  log_unit = bit64_set (log_unit, cursor->offset_to_bit);
3580 
3581  /* update context */
3582  context->nsects_lastvol_remaining--;
3583 
3584  /* save vsid */
3585  context->vsidp->volid = cursor->volheader->volid;
3586  context->vsidp->sectid = cursor->sectid;
3587  context->vsidp++;
3588 
3589  disk_log ("disk_stab_unit_reserve", "reserved sectid %d in volume %d.", cursor->sectid,
3590  cursor->volheader->volid);
3591  }
3592  }
3593  }
3594 
3595  /* safe guard: we must have done something, so log_unit cannot be 0 */
3596  assert (log_unit != 0);
3597  /* safe guard: all bits in log_unit must be set */
3598  assert ((log_unit & *cursor->unit) == log_unit);
3599  if (context->purpose == DB_PERMANENT_DATA_PURPOSE)
3600  {
3601  /* log changes */
3603  sizeof (log_unit), sizeof (log_unit), &log_unit, &log_unit);
3604  }
3605  /* page was modified */
3606  pgbuf_set_dirty (thread_p, cursor->page, DONT_FREE);
3607 
3608  if (context->nsects_lastvol_remaining <= 0)
3609  {
3610  /* all required sectors are reserved, we can stop now */
3611  assert (context->nsects_lastvol_remaining == 0);
3612  *stop = true;
3613  }
3614  return NO_ERROR;
3615 }
3616 
3617 /*
3618  * disk_stab_iterate_units () - iterate through units between start and end and call argument function. start and end
3619  * cursor should be aligned.
3620  *
3621  * return : error code
3622  * thread_p (in) : thread entry
3623  * volheader (in) : volume header
3624  * mode (in) : page latch mode
3625  * start (in) : start cursor
3626  * end (in) : end cursor
3627  * f_unit (in) : function called on each unit
3628  * f_unit_args (in) : argument for unit function
3629  */
3630 static int
3632  DISK_STAB_CURSOR * start, DISK_STAB_CURSOR * end, DISK_STAB_UNIT_FUNC f_unit,
3633  void *f_unit_args)
3634 {
3636  DISK_STAB_UNIT *end_unit;
3637  bool stop = false;
3638  int error_code = NO_ERROR;
3639 
3640  assert (volheader != NULL);
3641  assert (start->offset_to_bit == 0);
3642  assert (end->offset_to_bit == 0);
3643  assert (disk_stab_cursor_compare (start, end) < 0);
3644 
3645  /* iterate through pages */
3646  for (cursor = *start; cursor.pageid <= end->pageid; cursor.pageid++, cursor.offset_to_unit = 0)
3647  {
3648  assert (cursor.page == NULL);
3649  disk_stab_cursor_check_valid (&cursor);
3650 
3651  error_code = disk_stab_cursor_fix (thread_p, &cursor, mode);
3652  if (error_code != NO_ERROR)
3653  {
3654  ASSERT_ERROR ();
3655  return error_code;
3656  }
3657 
3658  /* iterate through units */
3659 
3660  /* set end_unit */
3661  end_unit = ((DISK_STAB_UNIT *) cursor.page)
3662  + (cursor.pageid == end->pageid ? end->offset_to_unit : DISK_STAB_PAGE_UNITS_COUNT);
3663 
3664  /* iterate */
3665  for (; cursor.unit < end_unit;
3666  cursor.unit++, cursor.offset_to_unit++, cursor.sectid += (DISK_STAB_UNIT_BIT_COUNT - cursor.offset_to_bit),
3667  cursor.offset_to_bit = 0)
3668  {
3669  disk_stab_cursor_check_valid (&cursor);
3670 
3671  /* call unit function */
3672  error_code = f_unit (thread_p, &cursor, &stop, f_unit_args);
3673  if (error_code != NO_ERROR)
3674  {
3675  ASSERT_ERROR ();
3676  disk_stab_cursor_unfix (thread_p, &cursor);
3677  return error_code;
3678  }
3679  if (stop)
3680  {
3681  /* stop */
3682  disk_stab_cursor_unfix (thread_p, &cursor);
3683  return NO_ERROR;
3684  }
3685  }
3686 
3687  disk_stab_cursor_unfix (thread_p, &cursor);
3688  }
3689 
3690  return NO_ERROR;
3691 }
3692 
3693 /*
3694  * disk_stab_iterate_units_all () - iterate trough all sector table units and apply function
3695  *
3696  * return : error code
3697  * thread_p (in) : thread entry
3698  * volheader (in) : volume header
3699  * mode (in) : page latch mode
3700  * f_unit (in) : function to apply for each unit
3701  * f_unit_args (in) : argument for unit function
3702  */
3703 static int
3705  DISK_STAB_UNIT_FUNC f_unit, void *f_unit_args)
3706 {
3707  DISK_STAB_CURSOR start, end;
3708 
3709  disk_stab_cursor_set_at_start (volheader, &start);
3710  disk_stab_cursor_set_at_end (volheader, &end);
3711 
3712  return disk_stab_iterate_units (thread_p, volheader, mode, &start, &end, f_unit, f_unit_args);
3713 }
3714 
3715 /*
3716  * disk_stab_count_free () - DISK_STAB_UNIT_FUNC to count free sectors
3717  *
3718  * return : NO_ERROR
3719  * thread_p (in) : thread entry
3720  * cursor (in) : disk sector table cursor
3721  * stop (in) : not used
3722  * args (in/out) : free sectors total count
3723  */
3724 static int
3725 disk_stab_count_free (THREAD_ENTRY * thread_p, DISK_STAB_CURSOR * cursor, bool * stop, void *args)
3726 {
3727  DKNSECTS *nfreep = (DKNSECTS *) args;
3728 
3729  /* add zero bit count to free sectors total count */
3730  *nfreep += bit64_count_zeros (*cursor->unit);
3731  return NO_ERROR;
3732 }
3733 
3734 /*
3735  * disk_stab_set_bits_contiguous () - set first bits
3736  *
3737  * return : NO_ERROR
3738  * thread_p (in) : thread entry
3739  * cursor (in) : sector table cursor
3740  * stop (out) : output true when all required bits are set
3741  * args (in/out) : remaining number of bits to set
3742  */
3743 static int
3744 disk_stab_set_bits_contiguous (THREAD_ENTRY * thread_p, DISK_STAB_CURSOR * cursor, bool * stop, void *args)
3745 {
3746  DKNSECTS *nsectsp = (DKNSECTS *) args;
3747 
3748  if (*nsectsp > DISK_STAB_UNIT_BIT_COUNT)
3749  {
3750  *cursor->unit = BIT64_FULL;
3751  (*nsectsp) -= DISK_STAB_UNIT_BIT_COUNT;
3752  }
3753  else
3754  {
3755  *cursor->unit = bit64_set_trailing_bits (0, *nsectsp);
3756  *nsectsp = 0;
3757  *stop = true;
3758  }
3759 
3760  return NO_ERROR;
3761 }
3762 
3763 /*
3764  * disk_stab_dump_unit () - dump a unit in sector table
3765  *
3766  * return : NO_ERROR
3767  * thread_p (in) : thread entry
3768  * cursor (in) : sector table cursor
3769  * stop (in) : not used
3770  * args (in/out) : output file
3771  */
3772 static int
3773 disk_stab_dump_unit (THREAD_ENTRY * thread_p, DISK_STAB_CURSOR * cursor, bool * stop, void *args)
3774 {
3775  FILE *fp = (FILE *) args;
3776  int bit;
3777 
3778  fprintf (fp, "\n%10d", cursor->sectid);
3779 
3780  for (bit = 0; bit < DISK_STAB_UNIT_BIT_COUNT; bit++)
3781  {
3782  if (bit % CHAR_BIT == 0)
3783  {
3784  fprintf (fp, " ");
3785  }
3786  fprintf (fp, "%d", disk_stab_cursor_is_bit_set (cursor) ? 1 : 0);
3787  }
3788 
3789  return NO_ERROR;
3790 }
3791 
3792 /*
3793  * disk_stab_dump () - Dump the content of the allocation map table
3794  * return: NO_ERROR
3795  * vpid(in): Complete Page identifier
3796  * at_name(in): Name of allocator table
3797  * at_fpageid(in): First page of map allocation table
3798  * at_lpageid(in): Last page of map allocation table
3799  * all_fid(in): First allocation(page/sector) id
3800  * all_lid(in): Last allocation(page/sector) id
3801  *
3802  * Note: This function is used for debugging purposes.
3803  */
3804 static int
3805 disk_stab_dump (THREAD_ENTRY * thread_p, FILE * fp, const DISK_VOLUME_HEADER * volheader)
3806 {
3807  int error_code = NO_ERROR;
3808 
3809  fprintf (fp, "SECTOR TABLE BITMAP:\n\n");
3810  fprintf (fp, " 01234567 01234567 01234567 01234567 01234567 01234567 01234567 01234567");
3811 
3812  error_code = disk_stab_iterate_units_all (thread_p, volheader, PGBUF_LATCH_READ, disk_stab_dump_unit, fp);
3813  if (error_code != NO_ERROR)
3814  {
3815  ASSERT_ERROR ();
3816  return error_code;
3817  }
3818  fprintf (fp, "\n");
3819  return NO_ERROR;
3820 }
3821 
3822 /************************************************************************/
3823 /* Sector reserve section */
3824 /************************************************************************/
3825 
3826 /*
3827  * disk_rv_reserve_sectors () - Apply recovery for reserving sectors.
3828  *
3829  * return : Error code.
3830  * thread_p (in) : Thread entry.
3831  * rcv (in) : Recovery data.
3832  */
3833 int
3835 {
3836  DISK_STAB_UNIT rv_unit = *(DISK_STAB_UNIT *) rcv->data;
3837  DISK_STAB_UNIT *stab_unit;
3838  VOLID volid;
3840  DKNSECTS nsect;
3841  int error_code = NO_ERROR;
3842 
3843  assert (rcv->pgptr != NULL);
3844  assert (rcv->length == sizeof (rv_unit));
3845  assert (rcv->offset >= 0 && rcv->offset < DISK_STAB_PAGE_UNITS_COUNT);
3846 
3847  /* we need to enter CSECT_DISK_CHECK as reader, to make sure we do not conflict with disk check */
3848  error_code = csect_enter_as_reader (thread_p, CSECT_DISK_CHECK, 0);
3849  if (error_code == NO_ERROR)
3850  {
3851  /* we locked. */
3852  }
3853  else if (error_code == ETIMEDOUT)
3854  {
3855  /* disk check is in progress. unfix page, get critical section again and then fix page and make the changes */
3856  VPID vpid;
3857  pgbuf_get_vpid (rcv->pgptr, &vpid);
3858  pgbuf_unfix_and_init (thread_p, rcv->pgptr);
3859  error_code = csect_enter_as_reader (thread_p, CSECT_DISK_CHECK, INF_WAIT);
3860  if (error_code != NO_ERROR)
3861  {
3862  assert_release (false);
3863  return ER_FAILED;
3864  }
3865  /* fix page again */
3867  if (rcv->pgptr == NULL)
3868  {
3869  /* should not fail */
3870  assert_release (false);
3871  csect_exit (thread_p, CSECT_DISK_CHECK);
3872  return ER_FAILED;
3873  }
3874  }
3875  else
3876  {
3877  assert_release (false);
3878  return ER_FAILED;
3879  }
3880 
3881  pgbuf_check_page_ptype (thread_p, rcv->pgptr, PAGE_VOLBITMAP);
3882 
3883  stab_unit = ((DISK_STAB_UNIT *) rcv->pgptr) + rcv->offset;
3884  assert (((*stab_unit) & rv_unit) == 0);
3885  *stab_unit |= rv_unit;
3886 
3887  pgbuf_set_dirty (thread_p, rcv->pgptr, DONT_FREE);
3888 
3889  /* update cache */
3890  volid = pgbuf_get_volume_id (rcv->pgptr);
3891  purpose = disk_get_volpurpose (volid);
3892 
3893  nsect = bit64_count_ones (rv_unit);
3894 
3896  disk_cache_update_vol_free (volid, -nsect);
3898 
3899  disk_log ("disk_rv_reserve_sectors", "reserved %d sectors in " PGBUF_PAGE_STATE_MSG ("sector table page")
3900  "\n\tstab_unit = " BIT64_HEXA_PRINT_FORMAT "\n\trv_unit = " BIT64_HEXA_PRINT_FORMAT,
3901  nsect, PGBUF_PAGE_STATE_ARGS (rcv->pgptr), *stab_unit, rv_unit);
3902 
3903  csect_exit (thread_p, CSECT_DISK_CHECK);
3904  return NO_ERROR;
3905 }
3906 
3907 /*
3908  * disk_rv_unreserve_sectors () - Apply recovery for unreserving sectors.
3909  *
3910  * return : Error code.
3911  * thread_p (in) : Thread entry.
3912  * rcv (in) : Recovery data.
3913  */
3914 int
3916 {
3917  DISK_STAB_UNIT rv_unit = *(DISK_STAB_UNIT *) rcv->data;
3918  DISK_STAB_UNIT *stab_unit;
3919  VOLID volid;
3921  DKNSECTS nsect;
3922  int error_code = NO_ERROR;
3923 
3924  assert (rcv->pgptr != NULL);
3925  assert (rcv->length == sizeof (rv_unit));
3926  assert (rcv->offset >= 0 && rcv->offset < DISK_STAB_PAGE_UNITS_COUNT);
3927 
3928  /* we need to enter CSECT_DISK_CHECK as reader, to make sure we do not conflict with disk check */
3929  error_code = csect_enter_as_reader (thread_p, CSECT_DISK_CHECK, 0);
3930  if (error_code == NO_ERROR)
3931  {
3932  /* we locked. */
3933  }
3934  else if (error_code == ETIMEDOUT)
3935  {
3936  /* disk check is in progress. unfix page, get critical section again and then fix page and make the changes */
3937  VPID vpid;
3938  pgbuf_get_vpid (rcv->pgptr, &vpid);
3939  pgbuf_unfix_and_init (thread_p, rcv->pgptr);
3940  error_code = csect_enter_as_reader (thread_p, CSECT_DISK_CHECK, INF_WAIT);
3941  if (error_code != NO_ERROR)
3942  {
3943  assert_release (false);
3944  return ER_FAILED;
3945  }
3946  /* fix page again */
3948  if (rcv->pgptr == NULL)
3949  {
3950  /* should not fail */
3951  assert_release (false);
3952  csect_exit (thread_p, CSECT_DISK_CHECK);
3953  return ER_FAILED;
3954  }
3955  }
3956  else
3957  {
3958  assert_release (false);
3959  return ER_FAILED;
3960  }
3961 
3962  pgbuf_check_page_ptype (thread_p, rcv->pgptr, PAGE_VOLBITMAP);
3963 
3964  stab_unit = ((DISK_STAB_UNIT *) rcv->pgptr) + rcv->offset;
3965  assert (((*stab_unit) & rv_unit) == rv_unit);
3966  *stab_unit &= ~rv_unit;
3967 
3968  pgbuf_set_dirty (thread_p, rcv->pgptr, DONT_FREE);
3969 
3970  /* update cache */
3971  volid = pgbuf_get_volume_id (rcv->pgptr);
3972  purpose = disk_get_volpurpose (volid);
3973 
3974  nsect = bit64_count_ones (rv_unit);
3975 
3977  disk_cache_update_vol_free (volid, nsect);
3979 
3980  disk_log ("disk_rv_unreserve_sectors", "unreserved %d sectors in " PGBUF_PAGE_STATE_MSG ("sector table page")
3981  "\n\tstab_unit = " BIT64_HEXA_PRINT_FORMAT "\n\trv_unit = " BIT64_HEXA_PRINT_FORMAT,
3982  nsect, PGBUF_PAGE_STATE_ARGS (rcv->pgptr), *stab_unit, rv_unit);
3983 
3984  csect_exit (thread_p, CSECT_DISK_CHECK);
3985  return NO_ERROR;
3986 }
3987 
3988 /*
3989  * disk_reserve_sectors_in_volume () - Reserve a number of sectors in the given volume.
3990  *
3991  * return : Error code.
3992  * thread_p (in) : Thread entry.
3993  * vol_index (in) : The index of volume in reserve context
3994  * context (in/out) : Reserve context
3995  */
3996 static int
3998 {
3999  VOLID volid;
4000  PAGE_PTR page_volheader = NULL;
4001  DISK_VOLUME_HEADER *volheader = NULL;
4004  int error_code = NO_ERROR;
4005 
4006  volid = context->cache_vol_reserve[vol_index].volid;
4007  if (volid == NULL_VOLID)
4008  {
4009  assert_release (false);
4010  return ER_FAILED;
4011  }
4012  context->nsects_lastvol_remaining = context->cache_vol_reserve[vol_index].nsect;
4013  assert (context->nsects_lastvol_remaining > 0);
4014 
4015  /* fix volume header */
4016  error_code = disk_get_volheader (thread_p, volid, PGBUF_LATCH_WRITE, &page_volheader, &volheader);
4017  if (error_code != NO_ERROR)
4018  {
4019  ASSERT_ERROR ();
4020  return error_code;
4021  }
4022 
4023  disk_log ("disk_reserve_sectors_in_volume", "reserve %d sectors in volume %d.", context->nsects_lastvol_remaining,
4024  volid);
4025 
4026  /* reserve all possible sectors. */
4027  if (volheader->hint_allocsect > 0 && volheader->hint_allocsect < volheader->nsect_total)
4028  {
4029  /* start with hinted sector */
4031  disk_stab_cursor_set_at_sectid (volheader, volheader->hint_allocsect, &start_cursor);
4032  disk_stab_cursor_set_at_end (volheader, &end_cursor);
4033 
4034  /* reserve sectors after hint */
4035  error_code = disk_stab_iterate_units (thread_p, volheader, PGBUF_LATCH_WRITE, &start_cursor, &end_cursor,
4036  disk_stab_unit_reserve, context);
4037  if (error_code != NO_ERROR)
4038  {
4039  ASSERT_ERROR ();
4040  goto exit;
4041  }
4042  if (context->nsects_lastvol_remaining > 0)
4043  {
4044  /* we need to reserve more. reserve sectors before hint */
4045  end_cursor = start_cursor;
4046  disk_stab_cursor_set_at_start (volheader, &start_cursor);
4047  error_code = disk_stab_iterate_units (thread_p, volheader, PGBUF_LATCH_WRITE, &start_cursor, &end_cursor,
4048  disk_stab_unit_reserve, context);
4049  if (error_code != NO_ERROR)
4050  {
4051  ASSERT_ERROR ();
4052  goto exit;
4053  }
4054  }
4055  }
4056  else
4057  {
4058  /* search the entire sector table */
4059  disk_stab_cursor_set_at_start (volheader, &start_cursor);
4060  disk_stab_cursor_set_at_end (volheader, &end_cursor);
4061  error_code = disk_stab_iterate_units (thread_p, volheader, PGBUF_LATCH_WRITE, &start_cursor, &end_cursor,
4062  disk_stab_unit_reserve, context);
4063  if (error_code != NO_ERROR)
4064  {
4065  ASSERT_ERROR ();
4066  goto exit;
4067  }
4068  }
4069 
4070  if (context->nsects_lastvol_remaining != 0)
4071  {
4072  /* our logic must be flawed... the sectors are reserved ahead so we should have found enough free sectors */
4073  assert_release (false);
4074  error_code = ER_FAILED;
4075  goto exit;
4076  }
4077 
4078  /* update hint */
4079  volheader->hint_allocsect = (context->vsidp - 1)->sectid + 1;
4080  volheader->hint_allocsect = DISK_SECTS_ROUND_DOWN (volheader->hint_allocsect);
4081  /* we don't really need to set the page dirty or log the hint change. */
4082 
4083 exit:
4084  if (page_volheader != NULL)
4085  {
4086  pgbuf_unfix (thread_p, page_volheader);
4087  }
4088  return error_code;
4089 }
4090 
4091 /*
4092  * disk_is_page_sector_reserved () - Is sector of page reserved?
4093  *
4094  * return : Valid if sector of page is reserved, invalid (or error) otherwise.
4095  * thread_p (in) : Thread entry
4096  * volid (in) : Page volid
4097  * pageid (in) : Page pageid
4098  */
4101 {
4102  return disk_is_page_sector_reserved_with_debug_crash (thread_p, volid, pageid, false);
4103 }
4104 
4105 /*
4106  * disk_is_page_sector_reserved_with_error () - Is sector of page reserved?
4107  *
4108  * return : Valid if sector of page is reserved, invalid (or error) otherwise.
4109  * thread_p (in) : Thread entry
4110  * volid (in) : Page volid
4111  * pageid (in) : Page pageid
4112  * debug_crash (in) : crash when invalid in debug mode
4113  */
4115 disk_is_page_sector_reserved_with_debug_crash (THREAD_ENTRY * thread_p, VOLID volid, PAGEID pageid, bool debug_crash)
4116 {
4117  PAGE_PTR page_volheader = NULL;
4118  DISK_VOLUME_HEADER *volheader;
4119  DISK_ISVALID isvalid = DISK_VALID;
4120  SECTID sectid;
4121  bool old_check_interrupt;
4122  int old_wait_msecs;
4123 
4124  old_check_interrupt = logtb_set_check_interrupt (thread_p, false);
4125  old_wait_msecs = xlogtb_reset_wait_msecs (thread_p, LK_INFINITE_WAIT);
4126 
4127  if (fileio_get_volume_descriptor (volid) == NULL_VOLDES || pageid < 0)
4128  {
4129  /* invalid */
4130  assert (!debug_crash);
4131  isvalid = DISK_INVALID;
4132  goto exit;
4133  }
4134 
4135  if (pageid == DISK_VOLHEADER_PAGE)
4136  {
4137  /* valid */
4138  isvalid = DISK_VALID;
4139  goto exit;
4140  }
4141 
4142  if (disk_get_volheader (thread_p, volid, PGBUF_LATCH_READ, &page_volheader, &volheader) != NO_ERROR)
4143  {
4144  ASSERT_ERROR ();
4145  isvalid = DISK_ERROR;
4146  goto exit;
4147  }
4148 
4149  if (pageid <= volheader->sys_lastpage)
4150  {
4151  isvalid = DISK_VALID;
4152  goto exit;
4153  }
4154  if (pageid > DISK_SECTS_NPAGES (volheader->nsect_total))
4155  {
4156  assert (!debug_crash);
4157  isvalid = DISK_INVALID;
4158  goto exit;
4159  }
4160 
4161  sectid = SECTOR_FROM_PAGEID (pageid);
4162  isvalid = disk_is_sector_reserved (thread_p, volheader, sectid, debug_crash);
4163 
4164 exit:
4165  xlogtb_reset_wait_msecs (thread_p, old_wait_msecs);
4166  (void) logtb_set_check_interrupt (thread_p, old_check_interrupt);
4167 
4168  if (page_volheader)
4169  {
4170  pgbuf_unfix (thread_p, page_volheader);
4171  }
4172 
4173  return isvalid;
4174 }
4175 
4176 /*
4177  * disk_is_sector_reserved () - Return valid if sector is reserved, invalid otherwise.
4178  *
4179  * return : Valid if sector is reserved, invalid if it is not reserved or error if table page cannot be fixed.
4180  * thread_p (in) : Thread entry
4181  * volheader (in) : Volume header
4182  * sectid (in) : Sector ID
4183  * debug_crash (in) : crash when invalid in debug mode
4184  */
4185 static DISK_ISVALID
4186 disk_is_sector_reserved (THREAD_ENTRY * thread_p, const DISK_VOLUME_HEADER * volheader, SECTID sectid, bool debug_crash)
4187 {
4188  DISK_STAB_CURSOR cursor_sectid;
4189 
4190  disk_stab_cursor_set_at_sectid (volheader, sectid, &cursor_sectid);
4191  if (disk_stab_cursor_fix (thread_p, &cursor_sectid, PGBUF_LATCH_READ) != NO_ERROR)
4192  {
4193  ASSERT_ERROR ();
4194  return DISK_ERROR;
4195  }
4196 
4197  if (!disk_stab_cursor_is_bit_set (&cursor_sectid))
4198  {
4199  assert (!debug_crash);
4200  disk_stab_cursor_unfix (thread_p, &cursor_sectid);
4201  return DISK_INVALID;
4202  }
4203  else
4204  {
4205  disk_stab_cursor_unfix (thread_p, &cursor_sectid);
4206  return DISK_VALID;
4207  }
4208 }
4209 
4210 /*
4211  * disk_reserve_sectors () - Reserve the required number of sectors in all database volumes.
4212  *
4213  * return : Error code.
4214  * thread_p (in) : Thread entry.
4215  * purpose (in) : Reservations purpose (data, index, generic or temp).
4216  * volid_hint (in) : Hint a volume to be checked first.
4217  * n_sectors (in) : Number of sectors to reserve.
4218  * reserved_sectors (out) : Array of reserved sectors.
4219  */
4220 int
4221 disk_reserve_sectors (THREAD_ENTRY * thread_p, DB_VOLPURPOSE purpose, VOLID volid_hint, int n_sectors,
4222  VSID * reserved_sectors)
4223 {
4224  int iter;
4225  DISK_RESERVE_CONTEXT context;
4226  int nreserved;
4227  bool retried = false;
4228  bool did_extend = false;
4229  int error_code = NO_ERROR;
4230 
4231  assert (purpose == DB_PERMANENT_DATA_PURPOSE || purpose == DB_TEMPORARY_DATA_PURPOSE);
4232 
4233  if (n_sectors <= 0 || reserved_sectors == NULL)
4234  {
4235  assert_release (false);
4236  return ER_FAILED;
4237  }
4238 
4239  if (purpose != DB_TEMPORARY_DATA_PURPOSE && !log_check_system_op_is_started (thread_p))
4240  {
4241  /* We really need a system operation. */
4242  assert (false);
4244  return ER_FAILED;
4245  }
4246 
4247 retry:
4248  disk_log ("disk_reserve_sectors", "reserve %d sectors for %s.", n_sectors, disk_purpose_to_string (purpose));
4249 
4250  log_sysop_start (thread_p);
4251 
4252  /* we don't want to conflict with disk check */
4253  error_code = csect_enter_as_reader (thread_p, CSECT_DISK_CHECK, INF_WAIT);
4254  if (error_code != NO_ERROR)
4255  {
4256  ASSERT_ERROR ();
4257  log_sysop_abort (thread_p);
4258  return error_code;
4259  }
4260 
4261  /* init context */
4262  context.nsect_total = n_sectors;
4263  context.n_cache_reserve_remaining = n_sectors;
4264  context.vsidp = reserved_sectors;
4265  context.n_cache_vol_reserve = 0;
4266  context.purpose = purpose;
4267 
4268  error_code = disk_reserve_from_cache (thread_p, &context, &did_extend);
4269  if (error_code != NO_ERROR)
4270  {
4271  ASSERT_ERROR ();
4272  goto error;
4273  }
4274 
4275  for (iter = 0; iter < context.n_cache_vol_reserve; iter++)
4276  {
4277  error_code = disk_reserve_sectors_in_volume (thread_p, iter, &context);
4278  if (error_code != NO_ERROR)
4279  {
4280  ASSERT_ERROR ();
4281  goto error;
4282  }
4283  }
4284 
4285  /* Should have enough sectors. */
4286  assert ((context.vsidp - reserved_sectors) == n_sectors);
4287 
4288  csect_exit (thread_p, CSECT_DISK_CHECK);
4289  /* attach sys op to outer */
4290  log_sysop_attach_to_outer (thread_p);
4291 
4292 #if !defined (NDEBUG)
4293  if (did_extend)
4294  {
4295  /* safe-guard: catch inconsistencies early */
4296  assert (disk_check (thread_p, false) != DISK_INVALID);
4297  }
4298 #endif /* !NDEBUG */
4299 
4300  return NO_ERROR;
4301 
4302 error:
4303  nreserved = (int) (context.vsidp - reserved_sectors);
4304  if (nreserved > 0)
4305  {
4306  int iter_vsid;
4307 
4308  if (purpose == DB_TEMPORARY_DATA_PURPOSE)
4309  {
4310  /* nothing was logged. we need to revert any partial allocations we may have made. */
4311  bool save_check_interrupt = logtb_set_check_interrupt (thread_p, false);
4312 
4313  qsort (reserved_sectors, nreserved, sizeof (VSID), disk_compare_vsids);
4314  if (disk_unreserve_ordered_sectors_without_csect (thread_p, purpose, nreserved, reserved_sectors) != NO_ERROR)
4315  {
4316  assert_release (false);
4317  }
4318  (void) logtb_set_check_interrupt (thread_p, save_check_interrupt);
4319  }
4320 
4321  /* we'll need to remove reservations for the rest of sectors (that were not allocated from disk). but first,
4322  * let's avoid removing the reservations for the ones we allocated from disk and rollbacked (they have been
4323  * removed from cache too) */
4324  for (iter_vsid = 0; iter_vsid < nreserved; iter_vsid++)
4325  {
4326  /* search vsid in volumes */
4327  for (iter = 0; iter < context.n_cache_vol_reserve; iter++)
4328  {
4329  if (reserved_sectors[iter_vsid].volid == context.cache_vol_reserve[iter].volid)
4330  {
4331  context.cache_vol_reserve[iter].nsect--;
4332  break;
4333  }
4334  }
4335  assert (iter < context.n_cache_vol_reserve);
4336  }
4337  }
4338 
4339  /* undo cache reserve */
4340  disk_cache_free_reserved (&context);
4341 
4342  csect_exit (thread_p, CSECT_DISK_CHECK);
4343 
4344  /* abort any changes */
4345  log_sysop_abort (thread_p);
4346 
4347  if (error_code == ER_INTERRUPTED /* interrupted error */
4348  || error_code == ER_IO_MOUNT_FAIL || error_code == ER_IO_FORMAT_OUT_OF_SPACE || error_code == ER_IO_WRITE
4349  || error_code == ER_BO_CANNOT_CREATE_VOL /* IO errors */ )
4350  {
4351  /* this is expected. */
4352  return error_code;
4353  }
4354  /* this is not. */
4355  assert_release (false);
4356 
4357  /* let's try to do something about this. */
4358 
4359  if (retried)
4360  {
4361  /* already tried... this is not a good sign */
4362  _er_log_debug (ARG_FILE_LINE, "disk_reserve_sectors retried and failed! error_code = %d \n", error_code);
4363  return error_code;
4364  }
4365 
4366  /* disk_check should fix any cache inconsistencies */
4367  if (disk_check (thread_p, true) == DISK_INVALID)
4368  {
4369  /* oh, it was bad */
4370  error_code = NO_ERROR;
4371  er_clear ();
4372  retried = true;
4373  goto retry;
4374  }
4375 
4376  /* it was not disk cache... */
4377  _er_log_debug (ARG_FILE_LINE, "disk_reserve_sectors failed unexpectedly! error_code = %d \n", error_code);
4378  return error_code;
4379 }
4380 
4381 /*
4382  * disk_reserve_from_cache () - First step of sector reservation on disk. This searches the cache for free space in
4383  * existing volumes. If not enough available sectors were found, volumes will be
4384  * expanded/added until all sectors could be reserved.
4385  * NOTE: this will modify the disk cache. It will "move" free sectors from disk cache
4386  * to reserve context. If any error occurs, the sectors must be returned to disk cache.
4387  *
4388  * return : Error code
4389  * thread_p (in) : Thread entry
4390  * context (in/out) : Reserve context
4391  * did_extend (out) :
4392  */
4393 static int
4394 disk_reserve_from_cache (THREAD_ENTRY * thread_p, DISK_RESERVE_CONTEXT * context, bool * did_extend)
4395 {
4396  DISK_EXTEND_INFO *extend_info;
4397  DKNSECTS save_remaining;
4398  int error_code = NO_ERROR;
4399 
4400  if (disk_Cache == NULL)
4401  {
4402  /* not initialized? */
4403  assert_release (false);
4404  return ER_FAILED;
4405  }
4406 
4408  if (context->purpose == DB_TEMPORARY_DATA_PURPOSE)
4409  {
4410  /* if we want to allocate temporary files, we have two options: preallocated permanent volumes (but with the
4411  * purpose of temporary files) or temporary volumes. try first the permanent volumes */
4412 
4413  extend_info = &disk_Cache->temp_purpose_info.extend_info;
4414 
4415  if (disk_Cache->temp_purpose_info.nsect_perm_free > 0)
4416  {
4418  }
4419  if (context->n_cache_reserve_remaining <= 0)
4420  {
4421  /* found enough sectors */
4422  assert (context->n_cache_reserve_remaining == 0);
4424  return NO_ERROR;
4425  }
4426 
4427  /* reserve sectors from temporary volumes */
4428  extend_info = &disk_Cache->temp_purpose_info.extend_info;
4429  if (extend_info->nsect_total - extend_info->nsect_free + context->n_cache_reserve_remaining
4431  {
4432  /* too much temporary space */
4433  assert (false);
4437  }
4438 
4439  /* fall through */
4440  }
4441  else
4442  {
4443  extend_info = &disk_Cache->perm_purpose_info.extend_info;
4444  /* fall through */
4445  }
4446 
4447  assert (context->n_cache_reserve_remaining > 0);
4448  assert (extend_info->owner_reserve == thread_get_entry_index (thread_p));
4449 
4450  if (extend_info->nsect_free > context->n_cache_reserve_remaining)
4451  {
4452  disk_reserve_from_cache_vols (extend_info->voltype, context);
4453  if (context->n_cache_reserve_remaining <= 0)
4454  {
4455  /* found enough sectors */
4456  assert (context->n_cache_reserve_remaining == 0);
4457  disk_cache_unlock_reserve (extend_info);
4458  return NO_ERROR;
4459  }
4460  }
4461 
4462  /* we might have to expand */
4463  /* first, save our intention in case somebody else will do the expand */
4464  extend_info->nsect_intention += context->n_cache_reserve_remaining;
4465 
4466  disk_log ("disk_reserve_from_cache", "existing free space could not satisfy reserve. "
4467  "increment intention by %d to %d for %s.", context->n_cache_reserve_remaining,
4468  extend_info->nsect_intention, disk_type_to_string (extend_info->voltype));
4469 
4470  disk_cache_unlock_reserve (extend_info);
4471 
4472  /* now lock expand */
4473  disk_lock_extend ();
4474 
4475  /* check again free sectors */
4476  disk_cache_lock_reserve (extend_info);
4477  if (extend_info->nsect_free > context->n_cache_reserve_remaining)
4478  {
4479  /* somebody else expanded? try again to reserve */
4480  /* also update intention */
4481  extend_info->nsect_intention -= context->n_cache_reserve_remaining;
4482 
4483  disk_log ("disk_reserve_from_cache", "somebody else extended disk. try reserve from cache again. "
4484  "also decrement intention by %d to %d for %s.", context->n_cache_reserve_remaining,
4485  extend_info->nsect_intention, disk_type_to_string (extend_info->voltype));
4486 
4487  disk_reserve_from_cache_vols (extend_info->voltype, context);
4488  if (context->n_cache_reserve_remaining <= 0)
4489  {
4490  assert (context->n_cache_reserve_remaining == 0);
4491  disk_cache_unlock_reserve (extend_info);
4492  disk_unlock_extend ();
4493  return NO_ERROR;
4494  }
4495 
4496  extend_info->nsect_intention += context->n_cache_reserve_remaining;
4497 
4498  disk_log ("disk_reserve_from_cache", "could not reserve enough from cache. need to do extend. "
4499  "increment intention by %d to %d for %s.", context->n_cache_reserve_remaining,
4500  extend_info->nsect_intention, disk_type_to_string (extend_info->voltype));
4501  }
4502 
4503  /* ok, we really have to extend the disk space ourselves */
4504  save_remaining = context->n_cache_reserve_remaining;
4505 
4506  disk_cache_unlock_reserve (extend_info);
4507 
4508  error_code = disk_extend (thread_p, extend_info, context);
4509 
4510  /* remove intention */
4511  disk_cache_lock_reserve (extend_info);
4512  extend_info->nsect_intention -= save_remaining;
4513  disk_log ("disk_reserve_from_cache", "extend done. decrement intention by %d to %d for %s. \n",
4514  save_remaining, extend_info->nsect_intention, disk_type_to_string (extend_info->voltype));
4515  disk_cache_unlock_reserve (extend_info);
4516 
4517  disk_unlock_extend ();
4518  if (error_code != NO_ERROR)
4519  {
4520  ASSERT_ERROR ();
4521  return error_code;
4522  }
4523  if (context->n_cache_reserve_remaining > 0)
4524  {
4525  assert_release (false);
4526  return ER_FAILED;
4527  }
4528 
4529  *did_extend = true;
4530 
4531  /* all cache reservations were made */
4532  return NO_ERROR;
4533 }
4534 
4535 /*
4536  * disk_reserve_from_cache_vols () - reserve sectors in disk cache volumes
4537  *
4538  * return : Void
4539  * purpose (in) : Permanent/temporary purpose
4540  * context (in) : Reserve context
4541  */
4542 STATIC_INLINE void
4544 {
4545  VOLID volid_iter;
4546  VOLID start_iter, end_iter, incr;
4547  DKNSECTS min_free;
4548 
4549  assert (disk_compatible_type_and_purpose (type, context->purpose));
4550 
4551  if (type == DB_PERMANENT_VOLTYPE)
4552  {
4553  start_iter = 0;
4554  end_iter = disk_Cache->nvols_perm;
4555  incr = 1;
4556 
4557  min_free = MIN (context->nsect_total, disk_Cache->perm_purpose_info.extend_info.nsect_vol_max) / 2;
4558  }
4559  else
4560  {
4561  start_iter = LOG_MAX_DBVOLID;
4562  end_iter = LOG_MAX_DBVOLID - disk_Cache->nvols_temp;
4563  incr = -1;
4564 
4565  min_free = MIN (context->nsect_total, disk_Cache->temp_purpose_info.extend_info.nsect_vol_max) / 2;
4566  }
4567 
4568  /* make sure we search for at least one sector */
4569  min_free = MAX (min_free, 1);
4570 
4571  for (volid_iter = start_iter; volid_iter != end_iter && context->n_cache_reserve_remaining > 0; volid_iter += incr)
4572  {
4573  if (disk_Cache->vols[volid_iter].purpose != context->purpose)
4574  {
4575  /* not the right purpose. */
4576  continue;
4577  }
4578  if (disk_Cache->vols[volid_iter].nsect_free < min_free)
4579  {
4580  /* avoid unnecessary fragmentation */
4581  continue;
4582  }
4583  /* reserve from this volume */
4584  disk_reserve_from_cache_volume (volid_iter, context);
4585  }
4586 }
4587 
4588 /*
4589  * disk_reserve_from_cache_volume () - reserve sectors from cache
4590  *
4591  * return : void
4592  * volid (in) : volume identifier
4593  * nsects (in) : number of reserved sectors
4594  * context (in/out) : reserve context
4595  */
4596 STATIC_INLINE void
4598 {
4599  DKNSECTS nsects;
4600 
4601  if (context->n_cache_vol_reserve >= LOG_MAX_DBVOLID)
4602  {
4603  assert_release (false);
4604  return;
4605  }
4607  assert (context->n_cache_reserve_remaining > 0);
4608  assert (disk_Cache->vols[volid].nsect_free > 0);
4609 
4610  nsects = MIN (disk_Cache->vols[volid].nsect_free, context->n_cache_reserve_remaining);
4611  disk_cache_update_vol_free (volid, -nsects);
4612 
4613  context->cache_vol_reserve[context->n_cache_vol_reserve].volid = volid;
4614  context->cache_vol_reserve[context->n_cache_vol_reserve].nsect = nsects;
4615  context->n_cache_reserve_remaining -= nsects;
4616 
4617  disk_log ("disk_reserve_from_cache_volume", "reserved %d sectors from volid = %d, \n" DISK_RESERVE_CONTEXT_MSG,
4618  nsects, volid, DISK_RESERVE_CONTEXT_AS_ARGS (context));
4619 
4620  context->n_cache_vol_reserve++;
4621  assert (context->n_cache_reserve_remaining >= 0);
4622 }
4623 
4624 /*
4625  * disk_unreserve_ordered_sectors () - un-reserve given list of sectors from disk volumes. the list must be ordered.
4626  *
4627  * return : error code
4628  * thread_p (in) : thread entry
4629  * purpose (in) : the purpose of reserved sectors
4630  * nsects (in) : number of sectors
4631  * vsids (in) : array of sectors
4632  */
4633 int
4635 {
4636  int error_code = NO_ERROR;
4637 
4638  error_code = csect_enter_as_reader (thread_p, CSECT_DISK_CHECK, INF_WAIT);
4639  if (error_code != NO_ERROR)
4640  {
4641  ASSERT_ERROR ();
4642  return error_code;
4643  }
4644 
4645  error_code = disk_unreserve_ordered_sectors_without_csect (thread_p, purpose, nsects, vsids);
4646  if (error_code != NO_ERROR)
4647  {
4648  ASSERT_ERROR ();
4649  }
4650 
4651  csect_exit (thread_p, CSECT_DISK_CHECK);
4652  return error_code;
4653 }
4654 
4655 /*
4656  * disk_unreserve_ordered_sectors_without_csect () - un-reserve given list of sectors from disk volumes. the list must
4657  * be ordered. caller must make sure to lock CSECT_DISK_CHECK.
4658  *
4659  * return : error code
4660  * thread_p (in) : thread entry
4661  * purpose (in) : the purpose of reserved sectors
4662  * nsects (in) : number of sectors
4663  * vsids (in) : array of sectors
4664  */
4665 static int
4667 {
4668  int start_index = 0;
4669  int end_index = 0;
4670  int index;
4671  VOLID volid = NULL_VOLID;
4672  DISK_RESERVE_CONTEXT context;
4673  int error_code = NO_ERROR;
4674 
4675  context.nsect_total = nsects;
4676  context.n_cache_reserve_remaining = nsects;
4677 
4678  context.n_cache_vol_reserve = 0;
4679  context.vsidp = vsids;
4680  context.purpose = purpose;
4681 
4682  /* note: vsids are ordered */
4683  for (start_index = 0; start_index < nsects; start_index = end_index)
4684  {
4685  assert (volid < vsids[start_index].volid);
4686  volid = vsids[start_index].volid;
4687  for (end_index = start_index + 1; end_index < nsects && vsids[end_index].volid == volid; end_index++)
4688  {
4689  assert (vsids[end_index].sectid > vsids[end_index - 1].sectid);
4690  }
4691  context.cache_vol_reserve[context.n_cache_vol_reserve].nsect = end_index - start_index;
4692  context.cache_vol_reserve[context.n_cache_vol_reserve].volid = volid;
4693  context.n_cache_vol_reserve++;
4694  }
4695  assert (end_index == nsects);
4696 
4697  disk_log ("disk_unreserve_ordered_sectors", "unreserve sectors.\n" DISK_RESERVE_CONTEXT_MSG,
4698  DISK_RESERVE_CONTEXT_AS_ARGS (&context));
4699 
4700  for (index = 0; index < context.n_cache_vol_reserve; index++)
4701  {
4702  /* unreserve volume sectors */
4704 
4705  error_code = disk_unreserve_sectors_from_volume (thread_p, context.cache_vol_reserve[index].volid, &context);
4706  if (error_code != NO_ERROR)
4707  {
4708  ASSERT_ERROR ();
4709  return error_code;
4710  }
4711  }
4712 
4713  return NO_ERROR;
4714 }
4715 
4716 /*
4717  * disk_unreserve_sectors_from_volume () - un-reserve sectors indicated in reserve context from volume's sector table
4718  *
4719  * return : error code
4720  * thread_p (in) : thread entry
4721  * volid (in) : volume identifier
4722  * context (in) : reserve context
4723  */
4724 static int
4726 {
4727  PAGE_PTR page_volheader;
4728  DISK_VOLUME_HEADER *volheader = NULL;
4729  SECTID sectid_start_cursor;
4730  DISK_STAB_CURSOR start_cursor, end_cursor;
4731  int error_code = NO_ERROR;
4732 
4733  assert (context != NULL && context->nsects_lastvol_remaining > 0);
4734 
4735  disk_log ("disk_unreserve_sectors_from_volume", "unreserve %d sectors in volume %d.",
4736  context->nsects_lastvol_remaining, volid);
4737 
4738  error_code = disk_get_volheader (thread_p, volid, PGBUF_LATCH_WRITE, &page_volheader, &volheader);
4739  if (error_code != NO_ERROR)
4740  {
4741  ASSERT_ERROR ();
4742  return error_code;
4743  }
4744 
4745  /* un-reserve all given sectors. */
4746 
4747  /* use disk_stab_iterate_units. set starting cursor to first sector rounded down */
4748  sectid_start_cursor = DISK_SECTS_ROUND_DOWN (context->vsidp->sectid);
4749  disk_stab_cursor_set_at_sectid (volheader, sectid_start_cursor, &start_cursor);
4750  disk_stab_cursor_set_at_end (volheader, &end_cursor);
4751  error_code =
4752  disk_stab_iterate_units (thread_p, volheader, PGBUF_LATCH_WRITE, &start_cursor, &end_cursor,
4753  disk_stab_unit_unreserve, context);
4754  if (error_code != NO_ERROR)
4755  {
4756  ASSERT_ERROR ();
4757  goto exit;
4758  }
4759 
4760  /* that's it */
4761  assert (error_code == NO_ERROR);
4762 
4763 exit:
4764  pgbuf_unfix (thread_p, page_volheader);
4765 
4766  return error_code;
4767 }
4768 
4769 /*
4770  * disk_stab_unit_unreserve () - DISK_STAB_UNIT_FUNC used to un-reserve sectors from sector table
4771  *
4772  * return : NO_ERROR
4773  * thread_p (in) : thread entry
4774  * cursor (in) : sector table cursor
4775  * stop (in) : output true when all sectors have been un-reserved
4776  * args (in) : reserve context
4777  */
4778 static int
4779 disk_stab_unit_unreserve (THREAD_ENTRY * thread_p, DISK_STAB_CURSOR * cursor, bool * stop, void *args)
4780 {
4781  DISK_RESERVE_CONTEXT *context = (DISK_RESERVE_CONTEXT *) args;
4782  DISK_STAB_UNIT unreserve_bits = 0;
4784  int nsect = 0;
4785 
4786  while (context->nsects_lastvol_remaining > 0 && context->vsidp->sectid < cursor->sectid + DISK_STAB_UNIT_BIT_COUNT)
4787  {
4788  unreserve_bits = bit64_set (unreserve_bits, context->vsidp->sectid - cursor->sectid);
4789  context->nsects_lastvol_remaining--;
4790 
4791  disk_log ("disk_stab_unit_unreserve", "unreserve sectid %d from volume %d.", context->vsidp->sectid,
4792  context->vsidp->volid);
4793 
4794  context->vsidp++;
4795  nsect++;
4796  }
4797 
4798  /* all bits must be set */
4799  assert ((unreserve_bits & (*cursor->unit)) == unreserve_bits);
4800  if (unreserve_bits != 0)
4801  {
4802  if (context->purpose == DB_PERMANENT_DATA_PURPOSE)
4803  {
4804  /* postpone */
4805  addr.pgptr = cursor->page;
4806  addr.offset = cursor->offset_to_unit;
4807  log_append_postpone (thread_p, RVDK_UNRESERVE_SECTORS, &addr, sizeof (unreserve_bits), &unreserve_bits);
4808  }
4809  else
4810  {
4811  /* remove immediately */
4812  (*cursor->unit) &= ~unreserve_bits;
4813  pgbuf_set_dirty (thread_p, cursor->page, DONT_FREE);
4814 
4816  assert (nsect > 0);
4817 
4819  disk_cache_update_vol_free (cursor->volheader->volid, nsect);
4821  }
4822  }
4823 
4824  if (context->nsects_lastvol_remaining <= 0)
4825  {
4826  assert (context->nsects_lastvol_remaining == 0);
4827  *stop = true;
4828  }
4829  return NO_ERROR;
4830 }
4831 
4832 /*
4833  * disk_stab_init () - initialize disk sector table
4834  *
4835  * return : error code
4836  * thread_p (in) : thread entry
4837  * volheader (in) : volume header
4838  */
4839 static int
4841 {
4842  DKNSECTS nsects_sys = SECTOR_FROM_PAGEID (volheader->sys_lastpage) + 1;
4843  DKNSECTS nsect_copy = 0;
4844  VPID vpid_stab;
4845  PAGE_PTR page_stab = NULL;
4846  DISK_STAB_CURSOR start_cursor;
4847  DISK_STAB_CURSOR end_cursor;
4848  int error_code = NO_ERROR;
4849 
4850  assert (nsects_sys < DISK_STAB_PAGE_BIT_COUNT);
4851 
4852  vpid_stab.volid = volheader->volid;
4853  for (vpid_stab.pageid = volheader->stab_first_page;
4854  vpid_stab.pageid < volheader->stab_first_page + volheader->stab_npages; vpid_stab.pageid++)
4855  {
4856  page_stab = pgbuf_fix (thread_p, &vpid_stab, NEW_PAGE, PGBUF_LATCH_WRITE, PGBUF_UNCONDITIONAL_LATCH);
4857  if (page_stab == NULL)
4858  {
4859  ASSERT_ERROR_AND_SET (error_code);
4860  return error_code;
4861  }
4862 
4863  pgbuf_set_page_ptype (thread_p, page_stab, PAGE_VOLBITMAP);
4864 
4865  if (volheader->purpose == DB_TEMPORARY_DATA_PURPOSE)
4866  {
4867  /* why is this necessary? */
4868  pgbuf_set_lsa_as_temporary (thread_p, page_stab);
4869  }
4870 
4871  memset (page_stab, 0, DB_PAGESIZE);
4872 
4873  if (nsects_sys > 0)
4874  {
4875  nsect_copy = nsects_sys;
4876 
4877  disk_stab_cursor_set_at_sectid (volheader,
4878  (vpid_stab.pageid - volheader->stab_first_page) * DISK_STAB_PAGE_BIT_COUNT,
4879  &start_cursor);
4880  if (vpid_stab.pageid == volheader->stab_first_page + volheader->stab_npages - 1)
4881  {
4882  disk_stab_cursor_set_at_end (volheader, &end_cursor);
4883  }
4884  else
4885  {
4886  disk_stab_cursor_set_at_sectid (volheader,
4887  (vpid_stab.pageid + 1 - volheader->stab_first_page)
4888  * DISK_STAB_PAGE_BIT_COUNT, &end_cursor);
4889  }
4890  error_code = disk_stab_iterate_units (thread_p, volheader, PGBUF_LATCH_WRITE, &start_cursor, &end_cursor,
4891  disk_stab_set_bits_contiguous, &nsect_copy);
4892  if (error_code != NO_ERROR)
4893  {
4894  ASSERT_ERROR ();
4895  pgbuf_unfix (thread_p, page_stab);
4896  return error_code;
4897  }
4898  }
4899 
4900  if (volheader->purpose != DB_TEMPORARY_DATA_PURPOSE)
4901  {
4902  DKNSECTS nsects_set = nsects_sys - nsect_copy;
4903  log_append_redo_data2 (thread_p, RVDK_INITMAP, NULL, page_stab, NULL_OFFSET, sizeof (nsects_set),
4904  &nsects_set);
4905  }
4906  if (!LOG_ISRESTARTED ())
4907  {
4908  /* page buffer will invalidated and pages will not be flushed. */
4909  pgbuf_set_dirty (thread_p, page_stab, DONT_FREE);
4910  pgbuf_flush (thread_p, page_stab, FREE);
4911  page_stab = NULL;
4912  }
4913  else
4914  {
4915  pgbuf_set_dirty_and_free (thread_p, page_stab);
4916  }
4917 
4918  nsects_sys = nsect_copy;
4919  nsect_copy = 0;
4920  }
4921 
4922  return NO_ERROR;
4923 }
4924 
4925 /*
4926  * disk_manager_init () - load disk manager and allocate all required resources
4927  *
4928  * return : error code
4929  * thread_p (in) : thread entry
4930  * load_from_disk (in) : true to also populate disk cache with volume info
4931  */
4932 int
4933 disk_manager_init (THREAD_ENTRY * thread_p, bool load_from_disk)
4934 {
4935  int error_code = NO_ERROR;
4936 
4938  if (disk_Temp_max_sects < 0)
4939  {
4940  disk_Temp_max_sects = SECTID_MAX; /* infinite */
4941  }
4942  else
4943  {
4945  }
4946 
4948 
4949  if (disk_Cache != NULL)
4950  {
4951  disk_log ("disk_manager_init", "%s", "reload disk cache");
4952  disk_cache_final ();
4953  }
4954  error_code = disk_cache_init ();
4955  if (error_code != NO_ERROR)
4956  {
4957  ASSERT_ERROR ();
4958  return error_code;
4959  }
4960  assert (disk_Cache != NULL);
4961 
4962  if (load_from_disk && !disk_cache_load_all_volumes (thread_p))
4963  {
4964  ASSERT_ERROR_AND_SET (error_code);
4965  disk_manager_final ();
4966  return error_code;
4967  }
4968 
4969 #if defined (SERVER_MODE)
4971 #endif /* SERVER_MODE */
4972 
4973  return NO_ERROR;
4974 }
4975 
4976 /*
4977  * disk_manager_final () - free disk manager resources
4978  */
4979 void
4981 {
4982 #if defined (SERVER_MODE)
4984 #endif /* SERVER_MODE */
4985 
4986  disk_cache_final ();
4987 }
4988 
4989 /*
4990  * disk_format_first_volume () - format first database volume
4991  *
4992  * return : error code
4993  * thread_p (in) : thread entry
4994  * full_dbname (in) : database full name
4995  * dbcomments (in) : database comments
4996  * npages (in) : desired number of pages
4997  * todo: replace with number of sectors or disk size
4998  *
4999  * NOTE: disk manager is also initialized
5000  */
5001 int
5002 disk_format_first_volume (THREAD_ENTRY * thread_p, const char *full_dbname, const char *dbcomments, DKNPAGES npages)
5003 {
5004  int error_code = NO_ERROR;
5005  DBDEF_VOL_EXT_INFO ext_info;
5006  DKNSECTS nsect_free = 0;
5007 
5008  error_code = disk_manager_init (thread_p, false);
5009  if (error_code != NO_ERROR)
5010  {
5011  ASSERT_ERROR ();
5012  return error_code;
5013  }
5014 
5015  ext_info.name = full_dbname;
5016  ext_info.comments = dbcomments;
5017  ext_info.nsect_total = disk_sectors_to_extend_npages (npages);
5018  ext_info.nsect_max = ext_info.nsect_total;
5019  ext_info.max_writesize_in_sec = 0;
5020  ext_info.overwrite = false;
5022  ext_info.voltype = DB_PERMANENT_VOLTYPE;
5023 
5024  disk_Cache->nvols_perm = 1;
5026 
5027  error_code = disk_format (thread_p, full_dbname, LOG_DBFIRST_VOLID, &ext_info, &nsect_free);
5028  if (error_code != NO_ERROR)
5029  {
5030  ASSERT_ERROR ();
5031  disk_Cache->nvols_perm = 0;
5032  return error_code;
5033  }
5034 
5035  disk_Cache->vols[LOG_DBFIRST_VOLID].nsect_free = nsect_free;
5036  disk_Cache->perm_purpose_info.extend_info.nsect_free = nsect_free;
5037  disk_Cache->perm_purpose_info.extend_info.nsect_total = ext_info.nsect_total;
5038  disk_Cache->perm_purpose_info.extend_info.nsect_max = ext_info.nsect_max;
5039 
5040  return NO_ERROR;
5041 }
5042 
5043 /*
5044  * disk_check_own_reserve_for_purpose () - check current thread owns reserve mutex (based on purpose)
5045  *
5046  * return : true if thread owns mutex, false otherwise
5047  * purpose (in) : volume purpose
5048  */
5049 STATIC_INLINE void
5051 {
5053  == ((purpose == DB_PERMANENT_DATA_PURPOSE)
5056 }
5057 
5058 /*
5059  * disk_check_sectors_are_reserved () - check all given sectors are reserved
5060  *
5061  * return : DISK_INVALID for unexpected flaws, DISK_ERROR for expected errors, DISK_VALID for successful check
5062  * thread_p (in) : thread entry
5063  * vsids (in) : VSID array
5064  * nsects (in) : VSID number
5065  */
5067 disk_check_sectors_are_reserved (THREAD_ENTRY * thread_p, VSID * vsids, int nsects)
5068 {
5069  int start_index = 0;
5070  int end_index = 0;
5071  int index;
5072  VOLID volid = NULL_VOLID;
5073  DISK_RESERVE_CONTEXT context;
5074 
5075  DISK_ISVALID valid = DISK_VALID;
5076  DISK_ISVALID allvalid = DISK_VALID;
5077 
5078  context.nsect_total = nsects;
5079  context.n_cache_vol_reserve = 0;
5080  context.vsidp = vsids;
5081  /* purpose is not relevant */
5082  context.purpose = DISK_UNKNOWN_PURPOSE;
5083 
5084  /* note: vsids are ordered */
5085  for (start_index = 0; start_index < nsects; start_index = end_index)
5086  {
5087  assert (volid < vsids[start_index].volid);
5088  volid = vsids[start_index].volid;
5089  for (end_index = start_index + 1; end_index < nsects && vsids[end_index].volid == volid; end_index++)
5090  {
5091  assert (vsids[end_index].sectid > vsids[end_index - 1].sectid);
5092  }
5093  context.cache_vol_reserve[context.n_cache_vol_reserve].nsect = end_index - start_index;
5094  context.cache_vol_reserve[context.n_cache_vol_reserve].volid = volid;
5095  context.n_cache_vol_reserve++;
5096  }
5097  assert (end_index == nsects);
5098 
5099  for (index = 0; index < context.n_cache_vol_reserve; index++)
5100  {
5101  /* unreserve volume sectors */
5103 
5104  valid = disk_check_sectors_are_reserved_in_volume (thread_p, context.cache_vol_reserve[index].volid, &context);
5105  if (valid == DISK_INVALID)
5106  {
5107  allvalid = DISK_INVALID;
5108  /* continue checking */
5109  }
5110  else if (valid == DISK_ERROR)
5111  {
5112  ASSERT_ERROR ();
5113  return valid;
5114  }
5115  }
5116 
5117  return allvalid;
5118 }
5119 
5120 /*
5121  * disk_check_sectors_are_reserved_in_volume () - check that sectors in reserve context for given volume are reserved
5122  *
5123  * return : error code
5124  * thread_p (in) : thread entry
5125  * volid (in) : volume identifier
5126  * context (in) : reserve context
5127  */
5128 static DISK_ISVALID
5130 {
5131  PAGE_PTR page_volheader = NULL;
5132  DISK_VOLUME_HEADER *volheader = NULL;
5133  SECTID sectid_start_cursor;
5134  DISK_STAB_CURSOR start_cursor, end_cursor;
5135  int error_code = NO_ERROR;
5136 
5137  assert (context != NULL && context->nsects_lastvol_remaining > 0);
5138 
5139  if (disk_get_volheader (thread_p, volid, PGBUF_LATCH_READ, &page_volheader, &volheader) != NO_ERROR)
5140  {
5141  ASSERT_ERROR ();
5142  return DISK_ERROR;
5143  }
5144 
5145  sectid_start_cursor = DISK_SECTS_ROUND_DOWN (context->vsidp->sectid);
5146  disk_stab_cursor_set_at_sectid (volheader, sectid_start_cursor, &start_cursor);
5147  disk_stab_cursor_set_at_end (volheader, &end_cursor);
5148  error_code = disk_stab_iterate_units (thread_p, volheader, PGBUF_LATCH_READ, &start_cursor, &end_cursor,
5150  pgbuf_unfix (thread_p, page_volheader);
5151  if (error_code == ER_FAILED)
5152  {
5153  return DISK_INVALID;
5154  }
5155  else if (error_code != NO_ERROR)
5156  {
5157  return DISK_ERROR;
5158  }
5159  return DISK_VALID;
5160 }
5161 
5162 /*
5163  * disk_stab_unit_check_reserved () - check unit for reserved sectors
5164  *
5165  * return : error code
5166  * thread_p (in) : thread entry
5167  * cursor (in) : sector table cursor
5168  * stop (out) : output true when no sectors are left to check
5169  * args (in/out) : reserve context
5170  */
5171 static int
5172 disk_stab_unit_check_reserved (THREAD_ENTRY * thread_p, DISK_STAB_CURSOR * cursor, bool * stop, void *args)
5173 {
5174  DISK_RESERVE_CONTEXT *context = (DISK_RESERVE_CONTEXT *) args;
5175  DISK_STAB_UNIT bits_check_reserved = 0;
5176 
5177  while (context->nsects_lastvol_remaining > 0 && context->vsidp->sectid < cursor->sectid + DISK_STAB_UNIT_BIT_COUNT)
5178  {
5179  bits_check_reserved = bit64_set (bits_check_reserved, context->vsidp->sectid - cursor->sectid);
5180  context->nsects_lastvol_remaining--;
5181 
5182  disk_log ("disk_stab_unit_unreserve", "unreserve sectid %d from volume %d.", context->vsidp->sectid,
5183  context->vsidp->volid);
5184 
5185  context->vsidp++;
5186  }
5187 
5188  /* all bits must be set */
5189  if ((bits_check_reserved & (*cursor->unit)) != bits_check_reserved)
5190  {
5191  /* not all bits are set */
5192  assert_release (false);
5193  return ER_FAILED;
5194  }
5195  if (context->nsects_lastvol_remaining <= 0)
5196  {
5197  assert (context->nsects_lastvol_remaining == 0);
5198  *stop = true;
5199  }
5200  return NO_ERROR;
5201 }
5202 
5203 /************************************************************************/
5204 /* Utility section */
5205 /************************************************************************/
5206 
5207 /*
5208  * disk_purpose_to_string () - Return the volume purpose in string format
5209  * return:
5210  * vol_purpose(in): Purpose of volume
5211  */
5212 static const char *
5214 {
5215  switch (vol_purpose)
5216  {
5218  return "Permanent data purpose";
5220  return "Temporary data purpose";
5221  default:
5222  assert (false);
5223  break;
5224  }
5225  return "Unknown purpose";
5226 }
5227 
5228 /*
5229  * disk_type_to_string () - volume type to string
5230  *
5231  * return : volume type to string
5232  * voltype (in) : volume type
5233  */
5234 static const char *
5236 {
5237  return voltype == DB_PERMANENT_VOLTYPE ? "Permanent Volume" : "Temporary Volume";
5238 }
5239 
5240 /*
5241  * disk_vhdr_set_vol_fullname () -
5242  * return: NO_ERROR
5243  * vhdr(in):
5244  * vol_fullname(in):
5245  */
5246 static int
5247 disk_vhdr_set_vol_fullname (DISK_VOLUME_HEADER * vhdr, const char *vol_fullname)
5248 {
5249  int length_diff;
5250  int length_to_move;
5251  int ret = NO_ERROR;
5252 
5253  length_diff = vhdr->offset_to_vol_remarks;
5254 
5255  length_to_move = (length_diff + (int) strlen (vhdr->var_fields + length_diff) + 1
5256  - vhdr->offset_to_next_vol_fullname);
5257 
5258  /* Difference in length between new name and old name */
5259  length_diff = (((int) strlen (vol_fullname) + 1)
5261 
5262  if (length_diff != 0)
5263  {
5264  /* We need to either move to right(expand) or left(shrink) the rest of the variable length fields */
5265  memmove (disk_vhdr_get_next_vol_fullname (vhdr) + length_diff, disk_vhdr_get_next_vol_fullname (vhdr),
5266  length_to_move);
5267  vhdr->offset_to_next_vol_fullname += length_diff;
5268  vhdr->offset_to_vol_remarks += length_diff;
5269  }
5270 
5271  (void) memcpy (disk_vhdr_get_vol_fullname (vhdr), vol_fullname,
5272  MIN ((ssize_t) strlen (vol_fullname) + 1, DB_MAX_PATH_LENGTH));
5273  return ret;
5274 }
5275 
5276 /*
5277  * disk_vhdr_set_next_vol_fullname () -
5278  * return: NO_ERROR
5279  * vhdr(in):
5280  * next_vol_fullname(in):
5281  */
5282 static int
5283 disk_vhdr_set_next_vol_fullname (DISK_VOLUME_HEADER * vhdr, const char *next_vol_fullname)
5284 {
5285  int length_diff;
5286  int length_to_move;
5287  int ret = NO_ERROR;
5288  int next_vol_fullname_size;
5289 
5290  if (next_vol_fullname == NULL)
5291  {
5292  next_vol_fullname = "";
5293  next_vol_fullname_size = 1;
5294  }
5295  else
5296  {
5297  next_vol_fullname_size = (int) strlen (next_vol_fullname) + 1;
5298  if (next_vol_fullname_size > PATH_MAX)
5299  {
5300  next_vol_fullname_size = PATH_MAX;
5301  }
5302  }
5303 
5304  length_diff = vhdr->offset_to_vol_remarks;
5305 
5306  length_to_move = (int) strlen (vhdr->var_fields + length_diff) + 1;
5307 
5308  /* Difference in length between new name and old name */
5309  length_diff = (next_vol_fullname_size - (vhdr->offset_to_vol_remarks - vhdr->offset_to_next_vol_fullname));
5310 
5311  if (length_diff != 0)
5312  {
5313  /* We need to either move to right(expand) or left(shrink) the rest of the variable length fields */
5314  memmove (disk_vhdr_get_vol_remarks (vhdr) + length_diff, disk_vhdr_get_vol_remarks (vhdr), length_to_move);
5315  vhdr->offset_to_vol_remarks += length_diff;
5316  }
5317 
5318  (void) memcpy (disk_vhdr_get_next_vol_fullname (vhdr), next_vol_fullname, next_vol_fullname_size);
5319 
5320  return ret;
5321 }
5322 
5323 /*
5324  * disk_vhdr_set_vol_remarks () -
5325  * return: NO_ERROR
5326  * vhdr(in):
5327  * vol_remarks(in):
5328  */
5329 static int
5330 disk_vhdr_set_vol_remarks (DISK_VOLUME_HEADER * vhdr, const char *vol_remarks)
5331 {
5332  int maxsize;
5333  int ret = NO_ERROR;
5334 
5335  if (vol_remarks != NULL)
5336  {
5337  maxsize = (DB_PAGESIZE - offsetof (DISK_VOLUME_HEADER, var_fields) - vhdr->offset_to_vol_remarks);
5338 
5339  if ((int) strlen (vol_remarks) > maxsize)
5340  {
5341  /* Does not fit.. Truncate the comment */
5342  (void) strncpy (disk_vhdr_get_vol_remarks (vhdr), vol_remarks, maxsize - 1);
5343  vhdr->var_fields[maxsize] = '\0';
5344  }
5345  else
5346  {
5347  (void) strcpy (disk_vhdr_get_vol_remarks (vhdr), vol_remarks);
5348  }
5349  }
5350  else
5351  {
5352  vhdr->var_fields[vhdr->offset_to_vol_remarks] = '\0';
5353  }
5354 
5355  return ret;
5356 }
5357 
5358 /*
5359  * disk_vhdr_get_vol_fullname () - get full name from volume header
5360  *
5361  * return : full name
5362  * vhdr (in) : volume header
5363  */
5364 STATIC_INLINE char *
5366 {
5367  return ((char *) (vhdr->var_fields + vhdr->offset_to_vol_fullname));
5368 }
5369 
5370 /*
5371  * disk_vhdr_get_next_vol_fullname () - get next volume full name from volume header
5372  *
5373  * return : next volume full name
5374  * vhdr (in) : volume header
5375  */
5376 STATIC_INLINE char *
5378 {
5379  return ((char *) (vhdr->var_fields + vhdr->offset_to_next_vol_fullname));
5380 }
5381 
5382 /*
5383  * disk_vhdr_get_vol_remarks () - get remarks from volume header
5384  *
5385  * return : remarks
5386  * vhdr (in) : volume header
5387  */
5388 STATIC_INLINE char *
5390 {
5391  return ((char *) (vhdr->var_fields + vhdr->offset_to_vol_remarks));
5392 }
5393 
5394 /*
5395  * disk_vhdr_length_of_varfields () - get length of volume header including variable fields
5396  *
5397  * return : total length
5398  * vhdr (in) : volume header
5399  */
5400 STATIC_INLINE int
5402 {
5403  return (vhdr->offset_to_vol_remarks + (int) strlen (disk_vhdr_get_vol_remarks (vhdr)));
5404 }
5405 
5406 /*
5407  * disk_set_checkpoint () - Reset the recovery checkpoint address for this volume
5408  * return: NO_ERROR;
5409  * volid(in): Permanent volume identifier
5410  * log_chkpt_lsa(in): Recovery checkpoint for volume
5411  *
5412  * Note: The dirty pages of this volume (except the header page which maintains the checkpoint value) are not
5413  * written out. The function assumes that all volume pages with lsa smaller that the given one has already
5414  * been forced to disk (e.g., by the log and recovery manager).
5415  *
5416  * When a backup of the database is taken, it is important that the volume header page is forced out.
5417  * The checkpoint on the volume is used as an indicator to start a media recovery process, so it may be good idea
5418  * to force all dirty unfixed pages.
5419  */
5420 int
5421 disk_set_checkpoint (THREAD_ENTRY * thread_p, INT16 volid, const LOG_LSA * log_chkpt_lsa)
5422 {
5423  DISK_VOLUME_HEADER *vhdr;
5424  LOG_DATA_ADDR addr;
5425  int error_code = NO_ERROR;
5426 
5427  addr.pgptr = NULL;
5428  addr.vfid = NULL;
5429  addr.offset = 0;
5430 
5431  error_code = disk_get_volheader (thread_p, volid, PGBUF_LATCH_WRITE, &addr.pgptr, &vhdr);
5432  if (error_code != NO_ERROR)
5433  {
5434  ASSERT_ERROR ();
5435  return error_code;
5436  }
5437 
5438  vhdr->chkpt_lsa.pageid = log_chkpt_lsa->pageid;
5439  vhdr->chkpt_lsa.offset = log_chkpt_lsa->offset;
5440 
5441  (void) disk_verify_volume_header (thread_p, addr.pgptr);
5442 
5443  log_skip_logging (thread_p, &addr);
5444  pgbuf_set_dirty (thread_p, addr.pgptr, DONT_FREE);
5445  pgbuf_flush (thread_p, addr.pgptr, FREE);
5446  addr.pgptr = NULL;
5447 
5448  return NO_ERROR;
5449 }
5450 
5451 /*
5452  * disk_get_checkpoint () - Get the recovery checkpoint address of this volume
5453  * return: NO_ERROR
5454  * volid(in): Permanent volume identifier
5455  * vol_lsa(out): Volume recovery checkpoint
5456  */
5457 int
5458 disk_get_checkpoint (THREAD_ENTRY * thread_p, INT16 volid, LOG_LSA * vol_lsa)
5459 {
5460  DISK_VOLUME_HEADER *vhdr;
5461  PAGE_PTR hdr_pgptr = NULL;
5462  int error_code = NO_ERROR;
5463 
5464  error_code = disk_get_volheader (thread_p, volid, PGBUF_LATCH_READ, &hdr_pgptr, &vhdr);
5465  if (error_code != NO_ERROR)
5466  {
5467  ASSERT_ERROR ();
5468  return error_code;
5469  }
5470 
5471  vol_lsa->pageid = vhdr->chkpt_lsa.pageid;
5472  vol_lsa->offset = vhdr->chkpt_lsa.offset;
5473 
5474  (void) disk_verify_volume_header (thread_p, hdr_pgptr);
5475 
5476  pgbuf_unfix_and_init (thread_p, hdr_pgptr);
5477 
5478  return NO_ERROR;
5479 }
5480 
5481 /*
5482  * disk_get_creation_time () - Get the database creation time according to the volume header
5483  * return: void
5484  * volid(in): Permanent volume identifier
5485  * db_creation(out): Database creation time according to the volume
5486  */
5487 int
5488 disk_get_creation_time (THREAD_ENTRY * thread_p, INT16 volid, INT64 * db_creation)
5489 {
5490  DISK_VOLUME_HEADER *vhdr;
5491  PAGE_PTR hdr_pgptr = NULL;
5492  int error_code = NO_ERROR;
5493 
5494  error_code = disk_get_volheader (thread_p, volid, PGBUF_LATCH_READ, &hdr_pgptr, &vhdr);
5495  if (error_code != NO_ERROR)
5496  {
5497  ASSERT_ERROR ();
5498  return error_code;
5499  }
5500 
5501  memcpy (db_creation, &vhdr->db_creation, sizeof (*db_creation));
5502 
5503  (void) disk_verify_volume_header (thread_p, hdr_pgptr);
5504 
5505  pgbuf_unfix_and_init (thread_p, hdr_pgptr);
5506 
5507  return NO_ERROR;
5508 }
5509 
5510 /*
5511  * xdisk_get_purpose () - Find the main purpose of the given volume
5512  * return: volume_purpose or DISK_UNKNOWN_PURPOSE
5513  * volid(in): Permanent volume identifier
5514  */
5516 xdisk_get_purpose (THREAD_ENTRY * thread_p, INT16 volid)
5517 {
5518  if (disk_Cache == NULL)
5519  {
5520  /* manager not initialized. only allow fetches from first volume. */
5521  assert (volid == LOG_DBFIRST_VOLID);
5523  }
5524 
5525  if (volid < LOG_DBFIRST_VOLID)
5526  {
5527  /* system volumes */
5528  return DISK_UNKNOWN_PURPOSE;
5529  }
5530 
5531  if (!disk_is_valid_volid (volid))
5532  {
5533  assert (false);
5534  return DISK_UNKNOWN_PURPOSE;
5535  }
5536 
5537  return disk_get_volpurpose (volid);
5538 }
5539 
5540 /*
5541  * xdisk_get_purpose_and_space_info () - Find the main purpose and space info of the volume
5542  *
5543  * return: volid or NULL_VOLID in case of error
5544  * volid(in): Permanent volume identifier. If NULL_VOLID is given, total information of all volumes is requested.
5545  * vol_purpose(out): Purpose for the given volume
5546  * space_info (out): space info of the volume.
5547  *
5548  * Note: The free number of pages should be taken as an approximation by the caller since we do not leave the page
5549  * locked after the inquire. That is, someone else can allocate pages
5550  */
5551 int
5553  DISK_VOLUME_SPACE_INFO * space_info)
5554 {
5555  int error_code = NO_ERROR;
5556 
5557  assert (volid != NULL_VOLID);
5558  assert (disk_Cache != NULL);
5559  assert (volid < disk_Cache->nvols_perm || volid > LOG_MAX_DBVOLID - disk_Cache->nvols_temp);
5560 
5561  if (space_info != NULL)
5562  {
5563  /* we don't cache total/max sectors */
5564  PAGE_PTR page_volheader;
5565  DISK_VOLUME_HEADER *volheader;
5566 
5567  memset (space_info, 0, sizeof (*space_info));
5568 
5569  error_code = disk_get_volheader (thread_p, volid, PGBUF_LATCH_READ, &page_volheader, &volheader);
5570  if (error_code != NO_ERROR)
5571  {
5572  ASSERT_ERROR ();
5573  return error_code;
5574  }
5575 
5576  space_info->n_max_sects = volheader->nsect_max;
5577  space_info->n_total_sects = volheader->nsect_total;
5578 
5579  pgbuf_unfix_and_init (thread_p, page_volheader);
5580 
5581  space_info->n_free_sects = disk_Cache->vols[volid].nsect_free;
5582  }
5583  if (vol_purpose != NULL)
5584  {
5585  *vol_purpose = disk_Cache->vols[volid].purpose;
5586  }
5587 
5588  return NO_ERROR;
5589 }
5590 
5591 /*
5592  * xdisk_get_purpose_and_sys_lastpage () - Find the main purpose of the given volume and the pagied of the last system
5593  * page used by the volume
5594  * return: volid or NULL_VOLID in case of error
5595  * volid(in): Permanent volume identifier
5596  * vol_purpose(out): Purpose for the given volume
5597  * sys_lastpage(out): Pageid of last system page
5598  */
5599 INT16
5600 xdisk_get_purpose_and_sys_lastpage (THREAD_ENTRY * thread_p, INT16 volid, DISK_VOLPURPOSE * vol_purpose,
5601  INT32 * sys_lastpage)
5602 {
5603  DISK_VOLUME_HEADER *vhdr;
5604  PAGE_PTR hdr_pgptr = NULL;
5605 
5606  /* The purpose of a volume does not change, so we do not lock the page */
5607  if (disk_get_volheader (thread_p, volid, PGBUF_LATCH_READ, &hdr_pgptr, &vhdr) != NO_ERROR)
5608  {
5609  ASSERT_ERROR ();
5610  return NULL_VOLID;
5611  }
5612 
5613  *vol_purpose = vhdr->purpose;
5614  *sys_lastpage = vhdr->sys_lastpage;
5615 
5616  (void) disk_verify_volume_header (thread_p, hdr_pgptr);
5617 
5618  pgbuf_unfix_and_init (thread_p, hdr_pgptr);
5619 
5620  return volid;
5621 }
5622 
5623 /*
5624  * xdisk_get_total_numpages () - Return the number of total pages for the given volume
5625  * return: Total Number of pages
5626  * volid(in): Permanent volume identifier
5627  */
5628 INT32
5629 xdisk_get_total_numpages (THREAD_ENTRY * thread_p, INT16 volid)
5630 {
5631  /* we don't have this info in cache */
5632  /* todo: investigate further its usage */
5633  PAGE_PTR page_volheader;
5634  DISK_VOLUME_HEADER *volheader;
5635 
5636  DKNPAGES npages;
5637 
5638  if (disk_get_volheader (thread_p, volid, PGBUF_LATCH_READ, &page_volheader, &volheader) != NO_ERROR)
5639  {
5640  ASSERT_ERROR ();
5641  return -1;
5642  }
5643  npages = DISK_SECTS_NPAGES (volheader->nsect_total);
5644 
5645  pgbuf_unfix (thread_p, page_volheader);
5646 
5647  return npages;
5648 }
5649 
5650 /*
5651  * xdisk_get_free_numpages () - Return the number of free pages for the given volume
5652  * return: Number of free pages
5653  * volid(in): Permanent volume identifier
5654  *
5655  * Note: The free number of pages should be taken as an approximation by the caller since we do not leave the page
5656  * locked after the inquire. That is, someone else can allocate pages.
5657  */
5658 INT32
5659 xdisk_get_free_numpages (THREAD_ENTRY * thread_p, INT16 volid)
5660 {
5661  /* get from cache. */
5662  /* todo: investigate usage */
5663  assert (disk_Cache != NULL);
5664  assert (volid <= disk_Cache->nvols_perm || volid > LOG_MAX_DBVOLID - disk_Cache->nvols_temp);
5665 
5666  return DISK_SECTS_NPAGES (disk_Cache->vols[volid].nsect_free);
5667 }
5668 
5669 /*
5670  * xdisk_is_volume_exist () -
5671  * return:
5672  * volid(in): volume identifier
5673  */
5674 bool
5676 {
5678 
5679  vol_info.volid = volid;
5680  vol_info.exists = false;
5681  (void) fileio_map_mounted (thread_p, disk_check_volume_exist, &vol_info);
5682 
5683  return vol_info.exists;
5684 }
5685 
5686 /*
5687  * disk_get_total_numsectors () - Return the number of total sectors for the given volume
5688  * return: Total Number of sectors
5689  * volid(in): Permanent volume identifier
5690  */
5691 INT32
5692 disk_get_total_numsectors (THREAD_ENTRY * thread_p, INT16 volid)
5693 {
5694  DISK_VOLUME_HEADER *vhdr;
5695  PAGE_PTR hdr_pgptr = NULL;
5696  INT32 total_sects;
5697 
5698  /* todo: will we need this? */
5699 
5700  if (disk_get_volheader (thread_p, volid, PGBUF_LATCH_READ, &hdr_pgptr, &vhdr) != NO_ERROR)
5701  {
5702  ASSERT_ERROR ();
5703  return -1;
5704  }
5705 
5706  total_sects = vhdr->nsect_total;
5707 
5708  pgbuf_unfix_and_init (thread_p, hdr_pgptr);
5709 
5710  return total_sects;
5711 }
5712 
5713 /*
5714  * xdisk_get_fullname () - Find the name of the volume and copy it into vol_fullname
5715  * return: vol_fullname on success or NULL on failure
5716  * volid(in): Permanent volume identifier
5717  * vol_fullname(out): Address where the name of the volume is placed.
5718  * The size must be at least DB_MAX_PATH_LENGTH
5719  *
5720  * Note: Alternative function fileio_get_volume_label which is much faster and does not copy the name
5721  */
5722 char *
5723 xdisk_get_fullname (THREAD_ENTRY * thread_p, INT16 volid, char *vol_fullname)
5724 {
5725  DISK_VOLUME_HEADER *vhdr;
5726  PAGE_PTR hdr_pgptr = NULL;
5727 
5728  if (vol_fullname == NULL)
5729  {
5731  return NULL;
5732  }
5733 
5734  if (disk_get_volheader (thread_p, volid, PGBUF_LATCH_READ, &hdr_pgptr, &vhdr) != NO_ERROR)
5735  {
5736  ASSERT_ERROR ();
5737  *vol_fullname = '\0';
5738  return NULL;
5739  }
5740 
5741  strncpy (vol_fullname, disk_vhdr_get_vol_fullname (vhdr), DB_MAX_PATH_LENGTH);
5742 
5743  (void) disk_verify_volume_header (thread_p, hdr_pgptr);
5744 
5745  pgbuf_unfix_and_init (thread_p, hdr_pgptr);
5746 
5747  return vol_fullname;
5748 }
5749 
5750 /*
5751  * xdisk_get_remarks () - Find the remarks attached to the volume creation
5752  * return: remarks string
5753  * volid(in): Permanent volume identifier
5754  *
5755  * Note: The string returned, must be freed by using free_and_init.
5756  */
5757 char *
5758 xdisk_get_remarks (THREAD_ENTRY * thread_p, INT16 volid)
5759 {
5760  DISK_VOLUME_HEADER *vhdr;
5761  PAGE_PTR hdr_pgptr = NULL;
5762  char *remarks;
5763 
5764  if (disk_get_volheader (thread_p, volid, PGBUF_LATCH_READ, &hdr_pgptr, &vhdr) != NO_ERROR)
5765  {
5766  ASSERT_ERROR ();
5767  return NULL;
5768  }
5769 
5770  remarks = (char *) malloc ((int) strlen (disk_vhdr_get_vol_remarks (vhdr)) + 1);
5771  if (remarks != NULL)
5772  {
5773  strcpy (remarks, disk_vhdr_get_vol_remarks (vhdr));
5774  }
5775 
5776  pgbuf_unfix_and_init (thread_p, hdr_pgptr);
5777 
5778  return remarks;
5779 }
5780 
5781 /*
5782  * disk_get_boot_db_charset () - Find the system boot charset
5783  * return: error code
5784  * volid(in): Permanent volume identifier
5785  * db_charset(out): System boot charset
5786  */
5787 int
5789 {
5790  DISK_VOLUME_HEADER *vhdr;
5791  PAGE_PTR pgptr = NULL;
5792 
5793  int error_code = NO_ERROR;
5794 
5795  assert (db_charset != NULL);
5796 
5797  error_code = disk_get_volheader (thread_p, volid, PGBUF_LATCH_READ, &pgptr, &vhdr);
5798  if (error_code != NO_ERROR)
5799  {
5800  ASSERT_ERROR ();
5801  return error_code;
5802  }
5803 
5804  *db_charset = (INTL_CODESET) vhdr->db_charset;
5805 
5806  pgbuf_unfix_and_init (thread_p, pgptr);
5807 
5808  return NO_ERROR;
5809 }
5810 
5811 /*
5812  * disk_dump_goodvol_system () - dump volume system information
5813  *
5814  * return : error code
5815  * thread_p (in) : thread entry
5816  * fp (in) : output file
5817  * volid (in) : volume identifier
5818  */
5819 static int
5820 disk_dump_volume_system_info (THREAD_ENTRY * thread_p, FILE * fp, INT16 volid)
5821 {
5822  DISK_VOLUME_HEADER *vhdr;
5823  PAGE_PTR hdr_pgptr = NULL;
5824  int error_code = NO_ERROR;
5825 
5826  error_code = disk_get_volheader (thread_p, volid, PGBUF_LATCH_READ, &hdr_pgptr, &vhdr);
5827  if (error_code != NO_ERROR)
5828  {
5829  ASSERT_ERROR ();
5830  return error_code;
5831  }
5832  disk_vhdr_dump (fp, vhdr);
5833  error_code = disk_stab_dump (thread_p, fp, vhdr);
5834  if (error_code != NO_ERROR)
5835  {
5836  ASSERT_ERROR ();
5837  }
5838  else
5839  {
5840  (void) fprintf (fp, "\n\n");
5841  }
5842  pgbuf_unfix_and_init (thread_p, hdr_pgptr);
5843 
5844  return error_code;
5845 }
5846 
5847 /*
5848  * disk_dump_all () - Dump the system area information of every single volume,
5849  * but log and backup volumes.
5850  * return: NO_ERROR;
5851  */
5852 int
5853 disk_dump_all (THREAD_ENTRY * thread_p, FILE * fp)
5854 {
5855  int ret = NO_ERROR;
5856 
5857  ret = (fileio_map_mounted (thread_p, disk_dump_goodvol_all, NULL) == true ? NO_ERROR : ER_FAILED);
5858 
5859  return ret;
5860 }
5861 
5862 /*
5863  * disk_dump_goodvol_all () - Dump all information of given volume
5864  * return: true
5865  * volid(in): Permanent volume identifier
5866  * ignore(in):
5867  */
5868 static bool
5869 disk_dump_goodvol_all (THREAD_ENTRY * thread_p, INT16 volid, void *ignore)
5870 {
5871  (void) disk_dump_volume_system_info (thread_p, stdout, volid);
5872 
5873  return true;
5874 }
5875 
5876 /*
5877  * disk_is_valid_volid () - is volume identifier valid? (permanent or temporary)
5878  *
5879  * return : true if volume id is valid, false otherwise
5880  * volid (in) : volume identifier
5881  */
5882 STATIC_INLINE bool
5884 {
5885  return volid < disk_Cache->nvols_perm || volid > LOG_MAX_DBVOLID - disk_Cache->nvols_temp;
5886 }
5887 
5888 /*
5889  * disk_get_volpurpose () - get volume purpose
5890  *
5891  * return : volume purpose
5892  * volid (in) : volume identifier
5893  */
5896 {
5897  assert (disk_Cache != NULL);
5898  assert (LOG_DBFIRST_VOLID <= volid && disk_is_valid_volid (volid));
5899 
5900  return disk_Cache->vols[volid].purpose;
5901 }
5902 
5903 /*
5904  * disk_get_voltype () - get volume type
5905  *
5906  * return : permanent/temporary volume type
5907  * volid (in) : volume identifier
5908  */
5911 {
5912  assert (disk_Cache != NULL);
5913  assert (disk_is_valid_volid (volid));
5914 
5915  return volid < disk_Cache->nvols_perm ? DB_PERMANENT_VOLTYPE : DB_TEMPORARY_VOLTYPE;
5916 }
5917 
5918 /*
5919  * disk_spacedb () - get space info from disk manager
5920  *
5921  * return : error code
5922  * thread_p (in) : thread entry
5923  * spaceall (out) : output info on all volumes
5924  * spacevols (out) : output info for each volume if not null
5925  */
5926 int
5927 disk_spacedb (THREAD_ENTRY * thread_p, SPACEDB_ALL * spaceall, SPACEDB_ONEVOL ** spacevols)
5928 {
5929  int i;
5930  int iter_vol;
5931  int iter_spacevols;
5932  int nvols_total = 0;
5933  bool is_extend_locked = false;
5934 
5935  int error_code = NO_ERROR;
5936 
5937  assert (spaceall != NULL);
5938 
5939  spaceall[SPACEDB_PERM_TEMP_ALL].nvols = 0;
5940 
5941  /* block extensions for the short period we'll be reading volume info. */
5942  disk_lock_extend ();
5943  is_extend_locked = true;
5944 
5945  /* get number of volumes. we know the total number of permanent type volumes, we'll have to count how many of them
5946  * have temporary purpose */
5947  for (iter_vol = 0; iter_vol < disk_Cache->nvols_perm; iter_vol++)
5948  {
5949  if (disk_Cache->vols[iter_vol].purpose == DB_TEMPORARY_DATA_PURPOSE)
5950  {
5951  spaceall[SPACEDB_PERM_TEMP_ALL].nvols++;
5952  }
5953  }
5954 
5955  spaceall[SPACEDB_PERM_PERM_ALL].nvols = disk_Cache->nvols_perm - spaceall[SPACEDB_PERM_TEMP_ALL].nvols;
5956  spaceall[SPACEDB_TEMP_TEMP_ALL].nvols = disk_Cache->nvols_temp;
5957  nvols_total = disk_Cache->nvols_perm + disk_Cache->nvols_temp;
5958 
5959  spaceall[SPACEDB_PERM_PERM_ALL].npage_used =
5961  - disk_Cache->perm_purpose_info.extend_info.nsect_free);
5963 
5964  spaceall[SPACEDB_PERM_TEMP_ALL].npage_used =
5967 
5968  spaceall[SPACEDB_TEMP_TEMP_ALL].npage_used =
5970  - disk_Cache->temp_purpose_info.extend_info.nsect_free);
5972 
5973  if (spacevols != NULL)
5974  {
5975  /* get info on each volume */
5976  iter_spacevols = 0;
5977 
5978  *spacevols = (SPACEDB_ONEVOL *) malloc (nvols_total * sizeof (SPACEDB_ONEVOL));
5979  if (*spacevols == NULL)
5980  {
5982  error_code = ER_OUT_OF_VIRTUAL_MEMORY;
5983  goto exit;
5984  }
5985 
5986  for (iter_vol = 0; iter_vol < disk_Cache->nvols_perm; iter_vol++)
5987  {
5988  (*spacevols)[iter_spacevols].volid = iter_vol;
5989  (*spacevols)[iter_spacevols].type = DB_PERMANENT_VOLTYPE;
5990  (*spacevols)[iter_spacevols].purpose = disk_Cache->vols[iter_vol].purpose;
5991  (*spacevols)[iter_spacevols].npage_free = DISK_SECTS_NPAGES (disk_Cache->vols[iter_vol].nsect_free);
5992  iter_spacevols++;
5993  }
5994  for (iter_vol = LOG_MAX_DBVOLID - disk_Cache->nvols_temp + 1; iter_vol <= LOG_MAX_DBVOLID; iter_vol++)
5995  {
5996  (*spacevols)[iter_spacevols].volid = iter_vol;
5997  (*spacevols)[iter_spacevols].type = DB_TEMPORARY_VOLTYPE;
5998  (*spacevols)[iter_spacevols].purpose = disk_Cache->vols[iter_vol].purpose;
5999  (*spacevols)[iter_spacevols].npage_free = DISK_SECTS_NPAGES (disk_Cache->vols[iter_vol].nsect_free);
6000  iter_spacevols++;
6001  }
6002  assert (iter_spacevols == nvols_total);
6003  }
6004 
6005  /* now unlock the extensions */
6006  disk_unlock_extend ();
6007  is_extend_locked = false;
6008 
6009  /* complete total values */
6010  memset (&spaceall[SPACEDB_TOTAL_ALL], 0, sizeof (spaceall[SPACEDB_TOTAL_ALL]));
6011  for (i = 0; i < SPACEDB_TOTAL_ALL; i++)
6012  {
6013  spaceall[SPACEDB_TOTAL_ALL].nvols += spaceall[i].nvols;
6014  spaceall[SPACEDB_TOTAL_ALL].npage_used += spaceall[i].npage_used;
6015  spaceall[SPACEDB_TOTAL_ALL].npage_free += spaceall[i].npage_free;
6016  }
6017 
6018  if (spacevols != NULL)
6019  {
6020  PAGE_PTR page_volheader = NULL;
6021  DISK_VOLUME_HEADER *volheader = NULL;
6022 
6023  /* we still have to read the total number of sectors and names for each volume, which are found in volumes header
6024  * pages, which need latches, which are slow, therefore we do it here, after unlocking extend. */
6025  assert (*spacevols != NULL);
6026 
6027  for (iter_spacevols = 0; iter_spacevols < nvols_total; iter_spacevols++)
6028  {
6029  error_code =
6030  disk_get_volheader (thread_p, (*spacevols)[iter_spacevols].volid, PGBUF_LATCH_READ, &page_volheader,
6031  &volheader);
6032  if (error_code != NO_ERROR)
6033  {
6034  ASSERT_ERROR ();
6035  goto exit;
6036  }
6037  (*spacevols)[iter_spacevols].npage_used =
6038  DISK_SECTS_NPAGES (volheader->nsect_total) - (*spacevols)[iter_spacevols].npage_free;
6039  strncpy ((*spacevols)[iter_spacevols].name, disk_vhdr_get_vol_fullname (volheader), DB_MAX_PATH_LENGTH);
6040  pgbuf_unfix_and_init (thread_p, page_volheader);
6041  }
6042  }
6043 
6044  /* success */
6045  assert (error_code == NO_ERROR);
6046 
6047 exit:
6048 
6049  if (is_extend_locked)
6050  {
6051  disk_unlock_extend ();
6052  }
6053 
6054  if (error_code != NO_ERROR && *spacevols != NULL)
6055  {
6056  /* free spacevols */
6057  free_and_init (*spacevols);
6058  }
6059 
6060  return error_code;
6061 }
6062 
6063 /*
6064  * disk_compare_vsids () - Compare two sector identifiers.
6065  *
6066  * return : 1 if first sector is bigger, -1 if first sector is smaller and 0 if sector ids are equal
6067  * first (in) : first sector id
6068  * second (in) : second sector id
6069  */
6070 int
6071 disk_compare_vsids (const void *first, const void *second)
6072 {
6073  VSID *first_vsid = (VSID *) first;
6074  VSID *second_vsid = (VSID *) second;
6075 
6076  if (first_vsid->volid > second_vsid->volid)
6077  {
6078  return 1;
6079  }
6080  else if (first_vsid->volid < second_vsid->volid)
6081  {
6082  return -1;
6083  }
6084  return (int) (first_vsid->sectid - second_vsid->sectid);
6085 }
6086 
6087 /************************************************************************/
6088 /* Disk check section */
6089 /************************************************************************/
6090 
6091 /*
6092  * disk_check_volume_exist () - check whether volume existed
6093  * return: NO_ERROR, or ER_code
6094  *
6095  * thread_p(in):
6096  * volid(in):
6097  * arg(in/out):CHECK_VOL_INFO structure
6098  */
6099 static bool
6100 disk_check_volume_exist (THREAD_ENTRY * thread_p, VOLID volid, void *arg)
6101 {
6102  DISK_CHECK_VOL_INFO *vol_infop = (DISK_CHECK_VOL_INFO *) arg;
6103 
6104  if (volid == vol_infop->volid)
6105  {
6106  vol_infop->exists = true;
6107  }
6108  return true;
6109 }
6110 
6111 /*
6112  * disk_can_overwrite_data_volume() - check whether data volume can be overwritten
6113  *
6114  * return : error code
6115  * thread_p(in) : thread entry
6116  * vol_label_p(in) : volume label
6117  * can_overwrite(out) : true, if the volume can be overwritten
6118  */
6119 static int
6120 disk_can_overwrite_data_volume (THREAD_ENTRY * thread_p, const char *vol_label_p, bool * can_overwrite)
6121 {
6122  char data_buf[IO_MAX_PAGE_SIZE + MAX_ALIGNMENT], *data;
6123  DISK_VOLUME_HEADER *vhdr;
6124  int vol_fd = NULL_VOLDES;
6125  off_t read_size;
6126  size_t page_reserver_area;
6127  int error_code = NO_ERROR;
6128  struct stat stat_buffer;
6129 
6130  assert (vol_label_p != NULL && can_overwrite != NULL);
6131 
6132  *can_overwrite = false;
6133 
6134 #if !defined(CS_MODE)
6135  /* Is volume already mounted ? */
6136  vol_fd = fileio_find_volume_descriptor_with_label (vol_label_p);
6137  if (vol_fd != NULL_VOLDES)
6138  {
6139  /* Can't overwrite the mounted volume. */
6140  return NO_ERROR;
6141  }
6142 #endif /* !CS_MODE */
6143 
6144  data = PTR_ALIGN (data_buf, MAX_ALIGNMENT);
6145  page_reserver_area = offsetof (FILEIO_PAGE, page);
6146  vhdr = (DISK_VOLUME_HEADER *) (data + page_reserver_area);
6147 
6148  /* Check the existence of the file by opening the file */
6149  vol_fd = fileio_open (vol_label_p, O_RDONLY, 0);
6150  if (vol_fd == NULL_VOLDES)
6151  {
6152  /* Someone deleted the volume. It can be written. */
6153  *can_overwrite = true;
6154  return NO_ERROR;
6155  }
6156 
6157  /* Computes the number of bytes to read. */
6158  read_size = offsetof (DISK_VOLUME_HEADER, db_creation) + sizeof (((DISK_VOLUME_HEADER *) 0)->db_creation);
6159  assert (read_size > (off_t) (offsetof (DISK_VOLUME_HEADER, magic) + CUBRID_MAGIC_MAX_LENGTH));
6160  read_size += page_reserver_area;
6161 
6162  if (fstat (vol_fd, &stat_buffer) != 0)
6163  {
6164  error_code = ER_FAILED;
6165  goto end;
6166  }
6167 
6168  // We will overwrite the file:
6169  // 0 size or the creation timestamp of the file is older than that of db.
6170  if (stat_buffer.st_size == 0)
6171  {
6172  // regard a failure happened during creation.
6173  *can_overwrite = true;
6174  goto end;
6175  }
6176  else if (stat_buffer.st_size < read_size)
6177  {
6178  /* Is not a CUBRID file. It can't be overwritten. */
6179  goto end;
6180  }
6181 
6182  /* Read block flush size bytes at offset 0. */
6183  if (fileio_read (thread_p, vol_fd, data, 0, read_size) == NULL)
6184  {
6185  error_code = ER_FAILED;
6186  goto end;
6187  }
6188 
6189  /* Check whether the existing volume is leaked from previous database and can be overwritten. */
6191  && log_Gl.hdr.db_creation > vhdr->db_creation)
6192  {
6193  /* Old CUBRID file. It can be overwritten. */
6194  *can_overwrite = true;
6195  }
6196 
6197 end:
6198  if (vol_fd != NULL_VOLDES)
6199  {
6200  fileio_close (vol_fd);
6201  }
6202 
6203  return error_code;
6204 }
6205 
6206 /*
6207  * disk_compatible_type_and_purpose () - is volume purpose compatible to volume type?
6208  *
6209  * return : true if compatible, false otherwise
6210  * type (in) : volume type
6211  * purpose (in) : volume purpose
6212  */
6213 STATIC_INLINE bool
6215 {
6216  /* temporary type with permanent purpose is not compatible */
6217  return type == DB_PERMANENT_VOLTYPE || purpose == DB_TEMPORARY_DATA_PURPOSE;
6218 }
6219 
6220 /*
6221  * disk_check_volume () - compare cache and volume and check for inconsistencies
6222  *
6223  * return : DISK_VALID if no inconsistency or if all inconsistencies have been fixed
6224  * DISK_ERROR if expected errors occurred
6225  * DISK_INVALID if cache and/or volume header are inconsistent
6226  * thread_p (in) : thread entry
6227  * volid (in) : volume identifier
6228  * repair (in) : true to try fix the inconsistencies
6229  */
6230 static DISK_ISVALID
6231 disk_check_volume (THREAD_ENTRY * thread_p, INT16 volid, bool repair)
6232 {
6233  DISK_ISVALID valid = DISK_VALID;
6234  DISK_VOLUME_HEADER *volheader;
6235  PAGE_PTR page_volheader = NULL;
6236  DKNSECTS nfree = 0;
6237  int error_code = NO_ERROR;
6238 
6239  /* get check critical section. it will prevent reservations/extensions to interfere with the process */
6240  error_code = csect_enter (thread_p, CSECT_DISK_CHECK, INF_WAIT);
6241  if (error_code != NO_ERROR)
6242  {
6243  ASSERT_ERROR ();
6244  return DISK_ERROR;
6245  }
6246 
6247  if (disk_get_volheader (thread_p, volid, PGBUF_LATCH_READ, &page_volheader, &volheader) != NO_ERROR)
6248  {
6249  ASSERT_ERROR ();
6250  valid = DISK_ERROR;
6251  goto exit;
6252  }
6253 
6254  /* check that purpose matches */
6255  if (volheader->purpose != disk_Cache->vols[volid].purpose)
6256  {
6257  assert (false);
6258  if (repair)
6259  {
6260  disk_Cache->vols[volid].purpose = volheader->purpose;
6261  }
6262  else
6263  {
6265  valid = DISK_INVALID;
6266  goto exit;
6267  }
6268  }
6269 
6270  /* count free sectors */
6271  error_code = disk_stab_iterate_units_all (thread_p, volheader, PGBUF_LATCH_READ, disk_stab_count_free, &nfree);
6272  if (error_code != NO_ERROR)
6273  {
6274  ASSERT_ERROR ();
6275  valid = DISK_ERROR;
6276  goto exit;
6277  }
6278 
6279  /* check (and maybe fix) cache inconsistencies */
6281  if (nfree != disk_Cache->vols[volid].nsect_free)
6282  {
6283  /* inconsistent! */
6284  assert (false);
6285 
6286  if (repair)
6287  {
6288  DKNSECTS diff = nfree - disk_Cache->vols[volid].nsect_free;
6289  disk_cache_update_vol_free (volid, diff);
6290  }
6291  else
6292  {
6294  fileio_get_volume_label (volid, PEEK), disk_Cache->vols[volid].nsect_free, nfree);
6295  valid = DISK_INVALID;
6296  }
6297  }
6299 
6300  /* the following check also added to the disk_verify_volume_header() macro */
6301  if (volheader->sect_npgs != DISK_SECTOR_NPAGES
6302  || volheader->stab_first_page != DISK_VOLHEADER_PAGE + 1
6303  || volheader->sys_lastpage != (volheader->stab_first_page + volheader->stab_npages - 1)
6304  || volheader->nsect_total > volheader->nsect_max
6305  || volheader->stab_npages < CEIL_PTVDIV (volheader->nsect_max, DISK_STAB_PAGE_BIT_COUNT)
6306  || volheader->nsect_total < disk_Cache->vols[volid].nsect_free)
6307  {
6308  assert (false);
6310  fileio_get_volume_label (volid, PEEK));
6311  valid = DISK_INVALID;
6312  }
6313 
6314  /* permanent volume is un-maxed if and only if it is the auto-extend volume */
6315  if (volheader->purpose == DB_PERMANENT_DATA_PURPOSE && volheader->nsect_max > volheader->nsect_total
6316  && disk_Cache->perm_purpose_info.extend_info.volid_extend != volid)
6317  {
6318  /* inconsistent! */
6319  assert_release (false);
6320 
6321  /* how to repair?? */
6322  valid = DISK_INVALID;
6323  }
6324 
6325 exit:
6326  if (page_volheader != NULL)
6327  {
6328  pgbuf_unfix_and_init (thread_p, page_volheader);
6329  }
6330 
6331  disk_log ("disk_check_volume", "check volume %d is %s", volid, valid == DISK_VALID ? "valid" : "not valid");
6332 
6333  csect_exit (thread_p, CSECT_DISK_CHECK);
6334 
6335  return valid;
6336 }
6337 
6338 /*
6339  * disk_check () - check disk cache is not out of sync
6340  *
6341  * return : DISK_VALID if all was ok or fixed
6342  DISK_ERROR if expected error occurred
6343  DISK_INVALID if disk cache is in an invalid state
6344  * thread_p (in) : thread entry
6345  * repair (in) : true to repair invalid states (if possible)
6346  */
6348 disk_check (THREAD_ENTRY * thread_p, bool repair)
6349 {
6350  DISK_ISVALID valid = DISK_VALID;
6351  int nvols_perm;
6352  int nvols_temp;
6353  VOLID volid_perm_last;
6354  VOLID volid_temp_last;
6355  VOLID volid_iter;
6356  DKNSECTS perm_free;
6357  DKNSECTS temp_free;
6358  int error_code = NO_ERROR;
6359 
6360  error_code = csect_enter (thread_p, CSECT_DISK_CHECK, INF_WAIT);
6361  if (error_code != NO_ERROR)
6362  {
6363  ASSERT_ERROR ();
6364  return DISK_ERROR;
6365  }
6366 
6367  /* first step: check cache matches boot_db_parm */
6368  nvols_perm = xboot_find_number_permanent_volumes (thread_p);
6369  nvols_temp = xboot_find_number_temp_volumes (thread_p);
6370  volid_perm_last = xboot_find_last_permanent (thread_p);
6371  volid_temp_last = xboot_find_last_temp (thread_p);
6372 
6373  if (nvols_perm != volid_perm_last + 1)
6374  {
6375  /* cannot repair */
6376  assert_release (false);
6377  csect_exit (thread_p, CSECT_DISK_CHECK);
6378  return DISK_INVALID;
6379  }
6380 
6381  if (nvols_temp > 0 && (nvols_temp != (LOG_MAX_DBVOLID - volid_temp_last + 1)))
6382  {
6383  /* cannot repair */
6384  assert_release (false);
6385  csect_exit (thread_p, CSECT_DISK_CHECK);
6386  return DISK_INVALID;
6387  }
6388 
6389  if (nvols_perm != disk_Cache->nvols_perm)
6390  {
6391  assert (false);
6392  if (repair)
6393  {
6394  disk_Cache->nvols_perm = nvols_perm;
6395  }
6396  else
6397  {
6399  csect_exit (thread_p, CSECT_DISK_CHECK);
6400  return DISK_INVALID;
6401  }
6402  }
6403 
6404  if (nvols_temp > 0 && nvols_temp != disk_Cache->nvols_temp)
6405  {
6406  assert (false);
6407  if (repair)
6408  {
6409  disk_Cache->nvols_temp = nvols_temp;
6410  }
6411  else
6412  {
6414  csect_exit (thread_p, CSECT_DISK_CHECK);
6415  return DISK_INVALID;
6416  }
6417  }
6418 
6419  disk_log ("disk_check", "first check step is %s", valid == DISK_VALID ? "valid" : "not valid");
6420 
6421  /* release critical section. we will get it for each volume we check, to avoid blocking all reservations and
6422  * extensions for a long time. */
6423  csect_exit (thread_p, CSECT_DISK_CHECK);
6424 
6425  /* second step: check volume cached info is consistent */
6426  for (volid_iter = 0; volid_iter < disk_Cache->nvols_perm; volid_iter++)
6427  {
6428  valid = disk_check_volume (thread_p, volid_iter, repair);
6429  if (valid != DISK_VALID)
6430  {
6431  assert (valid == DISK_ERROR);
6432  ASSERT_ERROR ();
6433  return valid;
6434  }
6435  }
6436 
6437  for (volid_iter = LOG_MAX_DBVOLID; volid_iter > LOG_MAX_DBVOLID - disk_Cache->nvols_temp; volid_iter--)
6438  {
6439  valid = disk_check_volume (thread_p, volid_iter, repair);
6440  if (valid != DISK_VALID)
6441  {
6442  assert (valid == DISK_ERROR);
6443  ASSERT_ERROR ();
6444  return valid;
6445  }
6446  }
6447 
6448  /* third step: check information aggregated for each purpose matches the sum of all volumes */
6449  error_code = csect_enter (thread_p, CSECT_DISK_CHECK, INF_WAIT);
6450  if (error_code != NO_ERROR)
6451  {
6452  ASSERT_ERROR ();
6453  return DISK_ERROR;
6454  }
6455 
6456  /* check permanently stored volumes */
6457  for (perm_free = 0, temp_free = 0, volid_iter = 0; volid_iter < disk_Cache->nvols_perm; volid_iter++)
6458  {
6459  if (disk_Cache->vols[volid_iter].purpose == DB_PERMANENT_DATA_PURPOSE)
6460  {
6461  perm_free += disk_Cache->vols[volid_iter].nsect_free;
6462  }
6463  else
6464  {
6465  temp_free += disk_Cache->vols[volid_iter].nsect_free;
6466  }
6467  }
6468 
6469  if (perm_free != disk_Cache->perm_purpose_info.extend_info.nsect_free)
6470  {
6471  assert (false);
6472  if (repair)
6473  {
6474  disk_Cache->perm_purpose_info.extend_info.nsect_free = perm_free;
6475  }
6476  else
6477  {
6479  csect_exit (thread_p, CSECT_DISK_CHECK);
6480  return DISK_INVALID;
6481  }
6482  }
6483 
6484  if (temp_free != disk_Cache->temp_purpose_info.nsect_perm_free)
6485  {
6486  assert (false);
6487  if (repair)
6488  {
6489  disk_Cache->temp_purpose_info.nsect_perm_free = temp_free;
6490  }
6491  else
6492  {
6494  csect_exit (thread_p, CSECT_DISK_CHECK);
6495  return DISK_INVALID;
6496  }
6497  }
6498 
6499  /* check temporarily stored volumes */
6500  for (temp_free = 0, volid_iter = LOG_MAX_DBVOLID; volid_iter > LOG_MAX_DBVOLID - disk_Cache->nvols_temp; volid_iter--)
6501  {
6502  temp_free += disk_Cache->vols[volid_iter].nsect_free;
6503  }
6504 
6505  if (temp_free != disk_Cache->temp_purpose_info.extend_info.nsect_free)
6506  {
6507  assert (false);
6508  if (repair)
6509  {
6510  disk_Cache->temp_purpose_info.extend_info.nsect_free = temp_free;
6511  }
6512  else
6513  {
6515  csect_exit (thread_p, CSECT_DISK_CHECK);
6516  return DISK_INVALID;
6517  }
6518  }
6519 
6520  disk_log ("disk_check", "full check is %s", "valid");
6521 
6522  /* all valid or all repaired */
6523  csect_exit (thread_p, CSECT_DISK_CHECK);
6524 
6525  return DISK_VALID;
6526 }
6527 
6528 #if defined (SA_MODE)
6529 /*
6530  * disk_map_clone_create () - create a clone of all permanent volumes sector table maps
6531  *
6532  * return : error code
6533  * thread_p (in) : thread entry
6534  * disk_map_clone (out) : disk sector table maps clone
6535  */
6536 int
6537 disk_map_clone_create (THREAD_ENTRY * thread_p, DISK_VOLMAP_CLONE ** disk_map_clone)
6538 {
6539  VOLID iter;
6540 
6541  PAGE_PTR page_volheader = NULL;
6542  DISK_VOLUME_HEADER *volheader = NULL;
6543 
6544  VPID vpid_stab;
6545  PAGE_PTR page_stab = NULL;
6546  DKNSECTS nsects;
6547  int memsize;
6548  char *ptr_map = NULL;
6549 
6550  int error_code = NO_ERROR;
6551 
6552  *disk_map_clone = (DISK_VOLMAP_CLONE *) calloc (disk_Cache->nvols_perm, sizeof (DISK_VOLMAP_CLONE));
6553  if (*disk_map_clone == NULL)
6554  {
6556  disk_Cache->nvols_perm * sizeof (DISK_VOLMAP_CLONE));
6557  return ER_OUT_OF_VIRTUAL_MEMORY;
6558  }
6559 
6560  for (iter = 0; iter < disk_Cache->nvols_perm; iter++)
6561  {
6562  if (disk_Cache->vols[iter].purpose == DB_TEMPORARY_DATA_PURPOSE)
6563  {
6564  /* we don't consider volumes with temporary purpose here */
6565  continue;
6566  }
6567 
6568  error_code = disk_get_volheader (thread_p, iter, PGBUF_LATCH_READ, &page_volheader, &volheader);
6569  if (error_code != NO_ERROR)
6570  {
6571  ASSERT_ERROR ();
6572  goto exit;
6573  }
6574 
6575  (*disk_map_clone)[iter].size_map = volheader->nsect_total / CHAR_BIT;
6576  (*disk_map_clone)[iter].map = (char *) malloc ((*disk_map_clone)[iter].size_map);
6577  if ((*disk_map_clone)[iter].map == NULL)
6578  {
6579  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (*disk_map_clone)[iter].size_map);
6580  error_code = ER_OUT_OF_VIRTUAL_MEMORY;
6581  goto exit;
6582  }
6583 
6584  /* copy map */
6585  vpid_stab.volid = iter;
6586  vpid_stab.pageid = volheader->stab_first_page;
6587  ptr_map = (*disk_map_clone)[iter].map;
6588  for (nsects = volheader->nsect_total; nsects >= 0; nsects -= DISK_STAB_PAGE_BIT_COUNT)
6589  {
6590  memsize = MIN (DB_PAGESIZE, nsects / CHAR_BIT);
6591 
6592  page_stab = pgbuf_fix (thread_p, &vpid_stab, OLD_PAGE, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH);
6593  if (page_stab == NULL)
6594  {
6595  ASSERT_ERROR_AND_SET (error_code);
6596  goto exit;
6597  }
6598 
6599  memcpy (ptr_map, page_stab, memsize);
6600 
6601  pgbuf_unfix_and_init (thread_p, page_stab);
6602 
6603  ptr_map += memsize;
6604  vpid_stab.pageid++;
6605  }
6606  assert (ptr_map == (*disk_map_clone)[iter].map + (*disk_map_clone)[iter].size_map);
6607  assert (vpid_stab.pageid <= volheader->stab_first_page + volheader->stab_npages);
6608 
6609  /* clear sectors used by system */
6610  ptr_map = (*disk_map_clone)[iter].map;
6611  for (nsects = SECTOR_FROM_PAGEID (volheader->sys_lastpage) + 1; nsects >= DISK_STAB_UNIT_BIT_COUNT;
6612  nsects -= DISK_STAB_UNIT_BIT_COUNT)
6613  {
6614  *(DISK_STAB_UNIT *) ptr_map = 0;
6615  ptr_map += DISK_STAB_UNIT_SIZE_OF;
6616  }
6617 
6618  /* clear trailing bits */
6619  *(DISK_STAB_UNIT *) ptr_map &= ~bit64_set_trailing_bits (0, nsects);
6620 
6621  pgbuf_unfix_and_init (thread_p, page_volheader);
6622  }
6623 
6624  /* disk map clone successfully created */
6625  assert (error_code == NO_ERROR);
6626 
6627 exit:
6628  if (error_code != NO_ERROR)
6629  {
6630  disk_map_clone_free (disk_map_clone);
6631  }
6632  if (page_stab != NULL)
6633  {
6634  pgbuf_unfix (thread_p, page_stab);
6635  }
6636  if (page_volheader != NULL)
6637  {
6638  pgbuf_unfix (thread_p, page_volheader);
6639  }
6640  return error_code;
6641 }
6642 
6643 /*
6644  * disk_map_clone_free () - free sector table maps clone
6645  *
6646  * return : void
6647  * disk_map_clone (in/out) : sector table maps clone. it is set to NULL.
6648  */
6649 void
6650 disk_map_clone_free (DISK_VOLMAP_CLONE ** disk_map_clone)
6651 {
6652  VOLID iter;
6653 
6654  for (iter = 0; iter < disk_Cache->nvols_perm; iter++)
6655  {
6656  free ((*disk_map_clone)[iter].map);
6657  }
6658  free_and_init (*disk_map_clone);
6659 }
6660 
6661 /*
6662  * disk_map_clone_clear () - clear the bit for given VSID in sector table map clone
6663  *
6664  * return : DISK_VALID if all checks pass, DISK_INVALID otherwise
6665  * disk_map_clone (in/out) : sector table maps clone
6666  * vsid (in) : VSID
6667  */
6669 disk_map_clone_clear (VSID * vsid, DISK_VOLMAP_CLONE * disk_map_clone)
6670 {
6671  int offset_unit = vsid->sectid / DISK_STAB_UNIT_BIT_COUNT;
6672  int offset_bit = vsid->sectid % DISK_STAB_UNIT_BIT_COUNT;
6673  DISK_STAB_UNIT *unit = ((DISK_STAB_UNIT *) disk_map_clone[vsid->volid].map) + offset_unit;
6674 
6675  if (vsid->sectid > disk_map_clone[vsid->volid].size_map * CHAR_BIT)
6676  {
6677  /* overflow */
6678  assert_release (false);
6679  return DISK_INVALID;
6680  }
6681 
6682  if (!bit64_is_set (*unit, offset_bit))
6683  {
6684  /* not reserved */
6685  assert_release (false);
6686  return DISK_INVALID;
6687  }
6688 
6689  *unit = bit64_clear (*unit, offset_bit);
6690 
6691  return DISK_VALID;
6692 }
6693 
6694 /*
6695  * disk_map_clone_check_leaks () - check disk map clone for sector leaks
6696  *
6697  * return : DISK_VALID if no leak, DISK_INVALID otherwise
6698  * disk_map_clone (in) : disk map clone
6699  */
6701 disk_map_clone_check_leaks (DISK_VOLMAP_CLONE * disk_map_clone)
6702 {
6703  DISK_STAB_UNIT *unit;
6704  VOLID volid;
6705 
6706  for (volid = 0; volid < disk_Cache->nvols_perm; volid++)
6707  {
6708  for (unit = (DISK_STAB_UNIT *) disk_map_clone[volid].map;
6709  (char *) unit < disk_map_clone[volid].map + disk_map_clone[volid].size_map; unit++)
6710  {
6711  if (*unit != 0)
6712  {
6713  /* leaked sectors */
6714  assert_release (false);
6715  return DISK_INVALID;
6716  }
6717  }
6718  }
6719 
6720  return DISK_VALID;
6721 }
6722 #endif /* SA_MODE */
6723 
6724 #if !defined (NDEBUG)
6725 void
6726 disk_volheader_check_magic (THREAD_ENTRY * thread_p, const PAGE_PTR page_volheader)
6727 {
6728  DISK_VOLUME_HEADER *volheader;
6729 
6730  (void) pgbuf_check_page_ptype (thread_p, page_volheader, PAGE_VOLHEADER);
6731 
6732  volheader = (DISK_VOLUME_HEADER *) page_volheader;
6733  assert (strncmp (volheader->magic, CUBRID_MAGIC_DATABASE_VOLUME, CUBRID_MAGIC_MAX_LENGTH) == 0);
6734 }
6735 #endif /* !NDEBUG */
6736 
6737 /*
6738  * disk_sectors_to_extend_npages () - compute the rounded number of sectors necessary to extend a number of pages
6739  *
6740  * return : The number of sectors
6741  * num_pages (in) : required number of pages
6742  **/
6743 int
6744 disk_sectors_to_extend_npages (const int num_pages)
6745 {
6746  return DISK_SECTS_ROUND_UP (DISK_PAGES_TO_SECTS (num_pages));
6747 }
6748 
6749 /************************************************************************/
6750 /* End of file */
6751 /************************************************************************/
DB_VOLTYPE voltype
Definition: disk_manager.c:173
PGLENGTH offset
Definition: recovery.h:201
int disk_reserve_sectors(THREAD_ENTRY *thread_p, DB_VOLPURPOSE purpose, VOLID volid_hint, int n_sectors, VSID *reserved_sectors)
char * PAGE_PTR
DKNSECTS nsect_total
Definition: disk_manager.c:84
void fileio_make_volume_ext_name(char *vol_ext_full_name_p, const char *ext_path_p, const char *ext_name_p, VOLID vol_id)
Definition: file_io.c:5654
#define DISK_ALLOCTBL_SECTOR_BIT_OFFSET(sect)
Definition: disk_manager.c:254
void disk_volheader_check_magic(THREAD_ENTRY *thread_p, const PAGE_PTR page_volheader)
DKNPAGES stab_npages
Definition: disk_manager.c:87
static int disk_can_overwrite_data_volume(THREAD_ENTRY *thread_p, const char *vol_label_p, bool *can_overwrite)
STATIC_INLINE void disk_stab_cursor_set_at_sectid(const DISK_VOLUME_HEADER *volheader, SECTID sectid, DISK_STAB_CURSOR *cursor) __attribute__((ALWAYS_INLINE))
int db_make_datetime(DB_VALUE *value, const DB_DATETIME *datetime)
#define NO_ERROR
Definition: error_code.h:46
STATIC_INLINE void disk_stab_cursor_unfix(THREAD_ENTRY *thread_p, DISK_STAB_CURSOR *cursor) __attribute__((ALWAYS_INLINE))
void log_append_redo_data(THREAD_ENTRY *thread_p, LOG_RCVINDEX rcvindex, LOG_DATA_ADDR *addr, int length, const void *data)
Definition: log_manager.c:1979
#define __attribute__(X)
Definition: porting.h:36
void er_stack_push(void)
int disk_get_boot_db_charset(THREAD_ENTRY *thread_p, INT16 volid, INTL_CODESET *db_charset)
DB_VOLPURPOSE purpose
Definition: disk_manager.c:81
void log_append_undoredo_data2(THREAD_ENTRY *thread_p, LOG_RCVINDEX rcvindex, const VFID *vfid, PAGE_PTR pgptr, PGLENGTH offset, int undo_length, int redo_length, const void *undo_data, const void *redo_data)
Definition: log_manager.c:1861
static int disk_unreserve_sectors_from_volume(THREAD_ENTRY *thread_p, VOLID volid, DISK_RESERVE_CONTEXT *context)
#define IO_PAGESIZE
static void disk_cache_final(void)
static bool disk_check_volume_exist(THREAD_ENTRY *thread_p, VOLID volid, void *arg)
#define BO_IS_SERVER_RESTARTED()
Definition: boot_sr.h:84
#define ASSERT_ERROR()
SCAN_CODE
#define DISK_VOLHEADER_MSG
Definition: disk_manager.c:314
DISK_PERM_PURPOSE_INFO perm_purpose_info
Definition: disk_manager.c:197
DISK_TEMP_PURPOSE_INFO temp_purpose_info
Definition: disk_manager.c:198
#define DISK_STAB_CURSOR_INITIALIZER
Definition: disk_manager.c:239
#define pthread_mutex_init(a, b)
Definition: area_alloc.c:48
int bit64_count_trailing_ones(UINT64 i)
Definition: bit.c:513
#define LOG_DATA_ADDR_INITIALIZER
Definition: log_append.hpp:63
STATIC_INLINE bool disk_stab_cursor_is_bit_set(const DISK_STAB_CURSOR *cursor) __attribute__((ALWAYS_INLINE))
DB_VOLPURPOSE purpose
Definition: disk_manager.c:289
int fileio_reset_volume(THREAD_ENTRY *thread_p, int vol_fd, const char *vlabel, DKNPAGES npages, const LOG_LSA *reset_lsa_p)
Definition: file_io.c:2903
void log_append_dboutside_redo(THREAD_ENTRY *thread_p, LOG_RCVINDEX rcvindex, int length, const void *data)
Definition: log_manager.c:2627
STATIC_INLINE SECTID disk_stab_cursor_get_sectid(const DISK_STAB_CURSOR *cursor) __attribute__((ALWAYS_INLINE))
STATIC_INLINE void disk_reserve_from_cache_vols(DB_VOLTYPE type, DISK_RESERVE_CONTEXT *context) __attribute__((ALWAYS_INLINE))
PAGEID DKNPAGES
void * fileio_read(THREAD_ENTRY *thread_p, int vol_fd, void *io_page_p, PAGEID page_id, size_t page_size)
Definition: file_io.c:3950
int db_get_int(const DB_VALUE *value)
INT32 SECTID
#define DISK_SECTS_SIZE(nsects)
Definition: disk_manager.h:69
static bool disk_cache_load_all_volumes(THREAD_ENTRY *thread_p)
#define disk_log(func, msg,...)
Definition: disk_manager.c:309
#define disk_get_volheader(...)
Definition: disk_manager.c:467
#define DISK_VOLPURPOSE
static int disk_stab_unit_check_reserved(THREAD_ENTRY *thread_p, DISK_STAB_CURSOR *cursor, bool *stop, void *args)
#define ER_FAILED
Definition: error_code.h:47
#define ER_IO_FORMAT_OUT_OF_SPACE
Definition: error_code.h:58
INT16 offset_to_vol_fullname
Definition: disk_manager.c:100
LOG_GLOBAL log_Gl
LOG_HEADER hdr
Definition: log_impl.h:653
#define csect_enter(a, b, c)
Definition: cnv.c:138
#define ALWAYS_INLINE
VOLID fileio_find_volume_id_with_label(THREAD_ENTRY *thread_p, const char *vol_label_p)
Definition: file_io.c:6319
#define pthread_mutex_unlock(a)
Definition: area_alloc.c:51
void fileio_unformat(THREAD_ENTRY *thread_p, const char *vol_label_p)
Definition: file_io.c:2721
#define pgbuf_unfix(thread_p, pgptr)
Definition: page_buffer.h:276
int disk_dump_all(THREAD_ENTRY *thread_p, FILE *fp)
int disk_set_link(THREAD_ENTRY *thread_p, INT16 volid, INT16 next_volid, const char *next_volext_fullname, bool logchange, DISK_FLUSH_TYPE flush)
Definition: disk_manager.c:969
#define pgbuf_invalidate(thread_p, pgptr)
Definition: page_buffer.h:290
void disk_rv_dump_hdr(FILE *fp, int length_ignore, void *data)
void logpb_force_flush_pages(THREAD_ENTRY *thread_p)
static int disk_volume_expand(THREAD_ENTRY *thread_p, VOLID volid, DB_VOLTYPE voltype, DKNSECTS nsect_extend, DKNSECTS *nsect_extended_out)
int disk_set_checkpoint(THREAD_ENTRY *thread_p, INT16 volid, const LOG_LSA *log_chkpt_lsa)
#define ASSERT_ERROR_AND_SET(error_code)
#define LSA_INITIALIZER
Definition: log_lsa.hpp:76
#define DISK_MIN_VOLUME_SECTS
Definition: disk_manager.c:297
#define assert_release(e)
Definition: error_manager.h:96
void pgbuf_set_dirty(THREAD_ENTRY *thread_p, PAGE_PTR pgptr, bool free_page)
Definition: page_buffer.c:4280
static int disk_format(THREAD_ENTRY *thread_p, const char *dbname, INT16 volid, DBDEF_VOL_EXT_INFO *ext_info, DKNSECTS *nsect_free_out)
Definition: disk_manager.c:510
int thread_get_current_entry_index(void)
VOLID logpb_add_volume(const char *db_fullname, VOLID new_volid, const char *new_volfullname, DISK_VOLPURPOSE new_volpurpose)
void log_append_undo_data2(THREAD_ENTRY *thread_p, LOG_RCVINDEX rcvindex, const VFID *vfid, PAGE_PTR pgptr, PGLENGTH offset, int length, const void *data)
Definition: log_manager.c:1933
INT16 VOLID
void log_sysop_start(THREAD_ENTRY *thread_p)
Definition: log_manager.c:3578
VOLID xboot_find_last_permanent(THREAD_ENTRY *thread_p)
Definition: boot_sr.c:377
int disk_rv_undoredo_link(THREAD_ENTRY *thread_p, LOG_RCV *rcv)
#define ER_BO_FULL_DATABASE_NAME_IS_TOO_LONG
Definition: error_code.h:178
struct timeval TSCTIMEVAL
Definition: tsc_timer.h:40
char * xdisk_get_remarks(THREAD_ENTRY *thread_p, INT16 volid)
int disk_compare_vsids(const void *first, const void *second)
static bool disk_Logging
Definition: disk_manager.c:308
static int disk_vhdr_set_vol_fullname(DISK_VOLUME_HEADER *vhdr, const char *vol_fullname)
bool fileio_is_volume_exist(const char *vol_label_p)
Definition: file_io.c:5094
volatile DKNSECTS nsect_free
Definition: disk_manager.c:161
const VOLID LOG_MAX_DBVOLID
Definition: log_volids.hpp:34
#define DISK_RESERVE_CONTEXT_MSG
Definition: disk_manager.c:334
int32_t pageid
Definition: dbtype_def.h:879
DKNSECTS nsect_perm_free
Definition: disk_manager.c:186
static int disk_stab_dump_unit(THREAD_ENTRY *thread_p, DISK_STAB_CURSOR *cursor, bool *stop, void *args)
void disk_rv_dump_set_boot_hfid(FILE *fp, int length_ignore, void *data)
INT32 hpgid
#define diff
Definition: mprec.h:352
static void disk_vhdr_dump(FILE *fp, const DISK_VOLUME_HEADER *vhdr)
#define ER_IO_FORMAT_BAD_NPAGES
Definition: error_code.h:56
#define VPID_INITIALIZER
Definition: dbtype_def.h:894
static int disk_stab_init(THREAD_ENTRY *thread_p, DISK_VOLUME_HEADER *volheader)
bool xdisk_is_volume_exist(THREAD_ENTRY *thread_p, VOLID volid)
UINT64 DISK_STAB_UNIT
Definition: disk_manager.c:221
DKNSECTS fileio_get_number_of_partition_free_sectors(const char *path_p)
Definition: file_io.c:5003
STATIC_INLINE void disk_stab_cursor_clear_bit(DISK_STAB_CURSOR *cursor) __attribute__((ALWAYS_INLINE))
#define PTR_ALIGN(addr, boundary)
Definition: memory_alloc.h:77
bool logtb_get_check_interrupt(THREAD_ENTRY *thread_p)
int disk_format_first_volume(THREAD_ENTRY *thread_p, const char *full_dbname, const char *dbcomments, DKNPAGES npages)
#define ER_BO_MAXTEMP_SPACE_HAS_BEEN_EXCEEDED
Definition: error_code.h:708
int disk_unreserve_ordered_sectors(THREAD_ENTRY *thread_p, DB_VOLPURPOSE purpose, int nsects, VSID *vsids)
#define VOL_MAX_NSECTS(page_size)
#define ER_BO_CANNOT_CREATE_LINK
Definition: error_code.h:971
char * fileio_get_directory_path(char *path_p, const char *full_name_p)
Definition: file_io.c:5567
#define pgbuf_invalidate_all(thread_p, volid)
Definition: page_buffer.h:286
void _er_log_debug(const char *file_name, const int line_no, const char *fmt,...)
#define NULL_VOLDES
Definition: file_io.h:44
#define VOLID_MAX
int disk_rv_volhead_extend_redo(THREAD_ENTRY *thread_p, LOG_RCV *rcv)
#define MAX_ALIGNMENT
Definition: memory_alloc.h:70
static void disk_auto_volume_expansion_daemon_init()
int disk_rv_reserve_sectors(THREAD_ENTRY *thread_p, LOG_RCV *rcv)
#define BIT64_FULL
Definition: bit.h:33
int xboot_find_number_temp_volumes(THREAD_ENTRY *thread_p)
Definition: boot_sr.c:359
int xlogtb_reset_wait_msecs(THREAD_ENTRY *thread_p, int wait_msecs)
void THREAD_ENTRY
#define ER_BO_CANNOT_CREATE_VOL
Definition: error_code.h:184
#define NULL_PAGEID
#define pgbuf_unfix_and_init(thread_p, pgptr)
Definition: page_buffer.h:63
int disk_add_volume_extension(THREAD_ENTRY *thread_p, DB_VOLPURPOSE purpose, DKNPAGES npages, const char *path, const char *name, const char *comments, int max_write_size_in_sec, bool overwrite, VOLID *volid_out)
static int disk_vhdr_set_next_vol_fullname(DISK_VOLUME_HEADER *vhdr, const char *next_vol_fullname)
STATIC_INLINE int disk_stab_cursor_get_bit_index_in_page(const DISK_STAB_CURSOR *cursor) __attribute__((ALWAYS_INLINE))
DISK_ISVALID disk_check_sectors_are_reserved(THREAD_ENTRY *thread_p, VSID *vsids, int nsects)
#define ER_BO_VOLUME_EXISTS
Definition: error_code.h:185
int disk_volume_header_end_scan(THREAD_ENTRY *thread_p, void **ptr)
int disk_rv_unreserve_sectors(THREAD_ENTRY *thread_p, LOG_RCV *rcv)
INT64 db_creation
int thread_get_entry_index(cubthread::entry *thread_p)
#define FREE(PTR)
Definition: cas_common.h:56
void log_append_redo_data2(THREAD_ENTRY *thread_p, LOG_RCVINDEX rcvindex, const VFID *vfid, PAGE_PTR pgptr, PGLENGTH offset, int length, const void *data)
Definition: log_manager.c:1995
int db_make_string(DB_VALUE *value, DB_CONST_C_CHAR str)
STATIC_INLINE void disk_check_own_reserve_for_purpose(DB_VOLPURPOSE purpose) __attribute__((ALWAYS_INLINE))
#define DISK_SECTS_ROUND_UP(nsects)
Definition: disk_manager.c:256
UINT64 prm_get_bigint_value(PARAM_ID prm_id)
INTL_CODESET lang_charset(void)
int disk_set_creation(THREAD_ENTRY *thread_p, INT16 volid, const char *new_vol_fullname, const INT64 *new_dbcreation, const LOG_LSA *new_chkptlsa, bool logchange, DISK_FLUSH_TYPE flush)
Definition: disk_manager.c:850
static int disk_stab_dump(THREAD_ENTRY *thread_p, FILE *fp, const DISK_VOLUME_HEADER *volheader)
const char * boot_db_full_name()
Definition: boot_sr.c:470
int disk_rv_undo_format(THREAD_ENTRY *thread_p, LOG_RCV *rcv)
#define SECTOR_FROM_PAGEID(pageid)
void er_set(int severity, const char *file_name, const int line_no, int err_id, int num_args,...)
int fileio_find_volume_descriptor_with_label(const char *vol_label_p)
Definition: file_io.c:6544
int xboot_find_number_permanent_volumes(THREAD_ENTRY *thread_p)
Definition: boot_sr.c:341
char * fileio_get_volume_label(VOLID vol_id, bool is_peek)
Definition: file_io.c:6182
SCAN_CODE disk_volume_header_next_scan(THREAD_ENTRY *thread_p, int cursor, DB_VALUE **out_values, int out_cnt, void *ptr)
int log_rv_copy_char(THREAD_ENTRY *thread_p, LOG_RCV *rcv)
Definition: log_manager.c:8696
PAGE_PTR pgptr
Definition: recovery.h:199
const char * fileio_get_base_file_name(const char *full_name_p)
Definition: file_io.c:5533
#define assert(x)
void log_skip_logging(THREAD_ENTRY *thread_p, LOG_DATA_ADDR *addr)
Definition: log_manager.c:3244
STATIC_INLINE void disk_stab_cursor_check_valid(const DISK_STAB_CURSOR *cursor) __attribute__((ALWAYS_INLINE))
const VOLID LOG_DBFIRST_VOLID
Definition: log_volids.hpp:38
STATIC_INLINE void disk_volume_header_set_stab(DB_VOLPURPOSE vol_purpose, DISK_VOLUME_HEADER *volheader) __attribute__((ALWAYS_INLINE))
char * disk_get_link(THREAD_ENTRY *thread_p, INT16 volid, INT16 *next_volid, char *next_volext_fullname)
DISK_FLUSH_TYPE
Definition: disk_manager.h:46
int32_t fileid
Definition: dbtype_def.h:886
bool pgbuf_check_page_ptype(THREAD_ENTRY *thread_p, PAGE_PTR pgptr, PAGE_TYPE ptype)
INT32 xdisk_get_free_numpages(THREAD_ENTRY *thread_p, INT16 volid)
DISK_ISVALID disk_is_page_sector_reserved_with_debug_crash(THREAD_ENTRY *thread_p, VOLID volid, PAGEID pageid, bool debug_crash)
volatile DKNSECTS nsect_max
Definition: disk_manager.c:163
int prm_get_integer_value(PARAM_ID prm_id)
#define pgbuf_set_dirty_and_free(thread_p, pgptr)
Definition: page_buffer.h:351
UINT64 bit64_set_trailing_bits(UINT64 i, int n)
Definition: bit.c:595
int disk_rv_redo_format(THREAD_ENTRY *thread_p, LOG_RCV *rcv)
#define ER_GENERIC_ERROR
Definition: error_code.h:49
#define STATIC_INLINE
LOG_LSA * pgbuf_get_lsa(PAGE_PTR pgptr)
Definition: page_buffer.c:4318
const log_lsa PGBUF_TEMP_LSA
Definition: page_buffer.h:244
PGBUF_LATCH_MODE
Definition: page_buffer.h:176
void fileio_close(int vol_fd)
Definition: file_io.c:2078
STATIC_INLINE void disk_cache_lock_reserve_for_purpose(DB_VOLPURPOSE purpose) __attribute__((ALWAYS_INLINE))
int(* DISK_STAB_UNIT_FUNC)(THREAD_ENTRY *thread_p, DISK_STAB_CURSOR *cursor, bool *stop, void *args)
Definition: disk_manager.c:263
#define CUBRID_MAGIC_MAX_LENGTH
#define ER_OUT_OF_VIRTUAL_MEMORY
Definition: error_code.h:50
void log_append_undo_data(THREAD_ENTRY *thread_p, LOG_RCVINDEX rcvindex, LOG_DATA_ADDR *addr, int length, const void *data)
Definition: log_manager.c:1917
STATIC_INLINE bool disk_compatible_type_and_purpose(DB_VOLTYPE type, DB_VOLPURPOSE purpose) __attribute__((ALWAYS_INLINE))
DISK_EXTEND_INFO extend_info
Definition: disk_manager.c:179
int logpb_recreate_volume_info(THREAD_ENTRY *thread_p)
int disk_volume_header_start_scan(THREAD_ENTRY *thread_p, int type, DB_VALUE **arg_values, int arg_cnt, void **ptr)
STATIC_INLINE void disk_verify_volume_header(THREAD_ENTRY *thread_p, PAGE_PTR pgptr) __attribute__((ALWAYS_INLINE))
void lsa_to_string(char *buf, int buf_size, const log_lsa *lsa)
Definition: log_lsa.cpp:28
const DISK_VOLUME_HEADER * volheader
Definition: disk_manager.c:228
HFID * disk_get_boot_hfid(THREAD_ENTRY *thread_p, INT16 volid, HFID *hfid)
int disk_rv_undoredo_set_boot_hfid(THREAD_ENTRY *thread_p, LOG_RCV *rcv)
#define IO_SECTORSIZE
#define DISK_SECTS_NPAGES(nsects)
Definition: disk_manager.h:70
UINT64 bit64_clear(UINT64 i, int off)
Definition: bit.c:587
#define DISK_SYS_NSECT_SIZE(nsect_max)
Definition: disk_manager.c:344
STATIC_INLINE DB_VOLPURPOSE disk_get_volpurpose(VOLID volid) __attribute__((ALWAYS_INLINE))
static bool disk_cache_load_volume(THREAD_ENTRY *thread_p, INT16 volid, void *ignore)
int disk_manager_init(THREAD_ENTRY *thread_p, bool load_from_disk)
void db_localdatetime(time_t *epoch_time, DB_DATETIME *datetime)
Definition: db_date.c:1030
static int disk_stab_iterate_units(THREAD_ENTRY *thread_p, const DISK_VOLUME_HEADER *volheader, PGBUF_LATCH_MODE mode, DISK_STAB_CURSOR *start, DISK_STAB_CURSOR *end, DISK_STAB_UNIT_FUNC f_unit, void *f_unit_args)
static enum scanner_mode mode
static int disk_unreserve_ordered_sectors_without_csect(THREAD_ENTRY *thread_p, DB_VOLPURPOSE purpose, int nsects, VSID *vsids)
int disk_set_boot_hfid(THREAD_ENTRY *thread_p, INT16 volid, const HFID *hfid)
int disk_unformat(THREAD_ENTRY *thread_p, const char *vol_fullname)
Definition: disk_manager.c:820
static int disk_dump_volume_system_info(THREAD_ENTRY *thread_p, FILE *fp, INT16 volid)
static int disk_reserve_from_cache(THREAD_ENTRY *thread_p, DISK_RESERVE_CONTEXT *context, bool *did_extend)
short volid
Definition: dbtype_def.h:880
volatile DKNSECTS nsect_total
Definition: disk_manager.c:162
#define DISK_SECTS_ASSERT_ROUNDED(nsects)
Definition: disk_manager.c:258
int boot_dbparm_save_volume(THREAD_ENTRY *thread_p, DB_VOLTYPE voltype, VOLID volid)
Definition: boot_sr.c:6069
DKNPAGES npage_used
#define DB_MAX_PATH_LENGTH
VFID vfid
std::int64_t pageid
Definition: log_lsa.hpp:36
int length
Definition: recovery.h:202
INT32 disk_get_total_numsectors(THREAD_ENTRY *thread_p, INT16 volid)
#define DISK_STAB_PAGE_UNITS_COUNT
Definition: disk_manager.c:245
#define DISK_VOLHEADER_AS_ARGS(arg_volh)
Definition: disk_manager.c:324
static const char * disk_purpose_to_string(DISK_VOLPURPOSE purpose)
STATIC_INLINE void disk_reserve_from_cache_volume(VOLID volid, DISK_RESERVE_CONTEXT *context) __attribute__((ALWAYS_INLINE))
STATIC_INLINE void disk_cache_free_reserved(DISK_RESERVE_CONTEXT *context) __attribute__((ALWAYS_INLINE))
#define NULL
Definition: freelistheap.h:34
#define PGBUF_PAGE_MODIFY_MSG(name)
Definition: page_buffer.h:59
#define ER_DISK_UNKNOWN_PURPOSE
Definition: error_code.h:685
#define CTIME_MAX
Definition: porting.h:72
char * xdisk_get_fullname(THREAD_ENTRY *thread_p, INT16 volid, char *vol_fullname)
DISK_CACHE_VOL_RESERVE cache_vol_reserve[VOLID_MAX]
Definition: disk_manager.c:283
void disk_manager_final(void)
UINT64 bit64_set(UINT64 i, int off)
Definition: bit.c:579
static char * dbname
void tsc_getticks(TSC_TICKS *tck)
Definition: tsc_timer.c:81
if(extra_options)
Definition: dynamic_load.c:958
INT32 xdisk_get_total_numpages(THREAD_ENTRY *thread_p, INT16 volid)
const VFID * vfid
Definition: log_append.hpp:56
#define PGBUF_PAGE_MODIFY_ARGS(pg, prev_lsa)
Definition: page_buffer.h:60
#define ER_IO_MOUNT_FAIL
Definition: error_code.h:59
char * hfid_to_string(char *buf, int buf_size, HFID *hfid)
PAGE_PTR pgptr
Definition: log_append.hpp:57
#define db_private_free_and_init(thrd, ptr)
Definition: memory_alloc.h:141
int disk_sectors_to_extend_npages(const int num_pages)
void log_append_undoredo_data(THREAD_ENTRY *thread_p, LOG_RCVINDEX rcvindex, LOG_DATA_ADDR *addr, int undo_length, int redo_length, const void *undo_data, const void *redo_data)
Definition: log_manager.c:1837
STATIC_INLINE DB_VOLTYPE disk_get_voltype(VOLID volid) __attribute__((ALWAYS_INLINE))
#define pgbuf_fix(thread_p, vpid, fetch_mode, requestmode, condition)
Definition: page_buffer.h:255
void disk_rv_dump_set_creation_time(FILE *fp, int length_ignore, void *data)
static int disk_stab_count_free(THREAD_ENTRY *thread_p, DISK_STAB_CURSOR *cursor, bool *stop, void *args)
void disk_rv_dump_volume_expand(FILE *fp, int length_ignore, void *data)
static int disk_extend(THREAD_ENTRY *thread_p, DISK_EXTEND_INFO *expand_info, DISK_RESERVE_CONTEXT *reserve_context)
DISK_ISVALID disk_is_page_sector_reserved(THREAD_ENTRY *thread_p, VOLID volid, PAGEID pageid)
#define csect_exit(a, b)
Definition: cnv.c:139
#define db_private_free(thrd, ptr)
Definition: memory_alloc.h:229
int xdisk_get_purpose_and_space_info(THREAD_ENTRY *thread_p, VOLID volid, DISK_VOLPURPOSE *vol_purpose, DISK_VOLUME_SPACE_INFO *space_info)
#define db_private_alloc(thrd, size)
Definition: memory_alloc.h:227
int disk_rv_redo_volume_expand(THREAD_ENTRY *thread_p, LOG_RCV *rcv)
static void disk_auto_volume_expansion_daemon_destroy()
int disk_get_checkpoint(THREAD_ENTRY *thread_p, INT16 volid, LOG_LSA *vol_lsa)
#define NULL_OFFSET
#define ER_DISK_INCONSISTENT_NFREE_SECTS
Definition: error_code.h:635
#define CEIL_PTVDIV(dividend, divisor)
Definition: memory_alloc.h:50
bool logtb_set_check_interrupt(THREAD_ENTRY *thread_p, bool flag)
STATIC_INLINE void disk_cache_lock_reserve(DISK_EXTEND_INFO *expand_info) __attribute__((ALWAYS_INLINE))
#define ER_BO_MAXNUM_VOLS_HAS_BEEN_EXCEEDED
Definition: error_code.h:181
#define DISK_VOLUME_SPACE_INFO_INITIALIZER
Definition: disk_manager.h:67
int pgbuf_flush_all(THREAD_ENTRY *thread_p, VOLID volid)
Definition: page_buffer.c:3083
static DISK_ISVALID disk_is_sector_reserved(THREAD_ENTRY *thread_p, const DISK_VOLUME_HEADER *volheader, SECTID sectid, bool debug_crash)
STATIC_INLINE int disk_get_volheader_internal(THREAD_ENTRY *thread_p, VOLID volid, PGBUF_LATCH_MODE latch_mode, PAGE_PTR *page_volheader_out, DISK_VOLUME_HEADER **volheader_out, const char *file, int line) __attribute__((ALWAYS_INLINE))
void pgbuf_get_vpid(PAGE_PTR pgptr, VPID *vpid)
Definition: page_buffer.c:4579
offset_type offset
Definition: log_append.hpp:58
void disk_rv_dump_link(FILE *fp, int length_ignore, void *data)
#define ER_IO_WRITE
Definition: error_code.h:63
void log_sysop_abort(THREAD_ENTRY *thread_p)
Definition: log_manager.c:4017
#define max(a, b)
DKNSECTS nsect_perm_total
Definition: disk_manager.c:187
void er_stack_pop(void)
STATIC_INLINE void disk_cache_update_vol_free(VOLID volid, DKNSECTS delta_free) __attribute__((ALWAYS_INLINE))
STATIC_INLINE int disk_stab_cursor_compare(const DISK_STAB_CURSOR *first_cursor, const DISK_STAB_CURSOR *second_cursor) __attribute__((ALWAYS_INLINE))
int disk_rv_redo_dboutside_newvol(THREAD_ENTRY *thread_p, LOG_RCV *rcv)
STATIC_INLINE char * disk_vhdr_get_next_vol_fullname(const DISK_VOLUME_HEADER *vhdr) __attribute__((ALWAYS_INLINE))
static int disk_volume_boot(THREAD_ENTRY *thread_p, VOLID volid, DB_VOLPURPOSE *purpose_out, DB_VOLTYPE *voltype_out, DISK_VOLUME_SPACE_INFO *space_out)
STATIC_INLINE int disk_stab_cursor_fix(THREAD_ENTRY *thread_p, DISK_STAB_CURSOR *cursor, PGBUF_LATCH_MODE latch_mode) __attribute__((ALWAYS_INLINE))
STATIC_INLINE void disk_stab_cursor_set_at_start(const DISK_VOLUME_HEADER *volheader, DISK_STAB_CURSOR *cursor) __attribute__((ALWAYS_INLINE))
volatile int owner_extend
Definition: disk_manager.c:202
DISK_VOLPURPOSE xdisk_get_purpose(THREAD_ENTRY *thread_p, INT16 volid)
bool logtb_is_interrupted(THREAD_ENTRY *thread_p, bool clear, bool *continue_checking)
DB_VOLPURPOSE
Definition: dbtype_def.h:185
static void error(const char *msg)
Definition: gencat.c:331
void log_append_postpone(THREAD_ENTRY *thread_p, LOG_RCVINDEX rcvindex, LOG_DATA_ADDR *addr, int length, const void *data)
Definition: log_manager.c:2698
const char * data
Definition: recovery.h:203
#define FI_TEST(th, code, state)
#define ER_INTERRUPTED
Definition: error_code.h:51
bool log_check_system_op_is_started(THREAD_ENTRY *thread_p)
Definition: log_manager.c:4166
#define pgbuf_fix_if_not_deallocated(thread_p, vpid, latch_mode, latch_condition, page)
Definition: page_buffer.h:441
#define HFID_IS_NULL(hfid)
#define ARG_FILE_LINE
Definition: error_manager.h:44
int fileio_expand_to(THREAD_ENTRY *thread_p, VOLID vol_id, DKNPAGES size_npages, DB_VOLTYPE voltype)
Definition: file_io.c:2512
#define ER_DIAG_VOLID_NOT_EXIST
Definition: error_code.h:1465
VOLID pgbuf_get_volume_id(PAGE_PTR pgptr)
Definition: page_buffer.c:4707
int disk_rv_undoredo_set_creation_time(THREAD_ENTRY *thread_p, LOG_RCV *rcv)
static int disk_cache_init(void)
static int disk_vhdr_set_vol_remarks(DISK_VOLUME_HEADER *vhdr, const char *vol_remarks)
STATIC_INLINE void disk_stab_cursor_set_at_end(const DISK_VOLUME_HEADER *volheader, DISK_STAB_CURSOR *cursor) __attribute__((ALWAYS_INLINE))
static bool disk_dump_goodvol_all(THREAD_ENTRY *thread_p, INT16 volid, void *ignore)
#define DB_INT16_MAX
Definition: dbtype_def.h:630
DB_VOLTYPE
Definition: dbtype_def.h:192
int fileio_synchronize(THREAD_ENTRY *thread_p, int vol_fd, const char *vlabel, FILEIO_SYNC_OPTION sync_dwb)
Definition: file_io.c:4441
INT16 PGLENGTH
INT16 offset_to_next_vol_fullname
Definition: disk_manager.c:101
#define DISK_VOLHEADER_PAGE
Definition: disk_manager.h:35
void disk_unlock_extend(void)
char magic[CUBRID_MAGIC_MAX_LENGTH]
Definition: disk_manager.c:76
STATIC_INLINE char * disk_vhdr_get_vol_fullname(const DISK_VOLUME_HEADER *vhdr) __attribute__((ALWAYS_INLINE))
#define csect_enter_as_reader(a, b, c)
int32_t sectid
Definition: dbtype_def.h:930
#define free_and_init(ptr)
Definition: memory_alloc.h:147
#define LOG_ISRESTARTED()
Definition: log_impl.h:232
void pgbuf_flush(THREAD_ENTRY *thread_p, PAGE_PTR pgptr, bool free_page)
Definition: page_buffer.c:2933
#define strlen(s1)
Definition: intl_support.c:43
VOLID fileio_find_previous_perm_volume(THREAD_ENTRY *thread_p, VOLID volid)
Definition: file_io.c:6434
#define DISK_PAGES_TO_SECTS(npages)
Definition: disk_manager.h:71
int bit64_count_zeros(UINT64 i)
Definition: bit.c:460
#define ER_OBJ_INVALID_ARGUMENTS
Definition: error_code.h:275
#define DISK_SECTS_ROUND_DOWN(nsects)
Definition: disk_manager.c:257
static int disk_stab_set_bits_contiguous(THREAD_ENTRY *thread_p, DISK_STAB_CURSOR *cursor, bool *stop, void *args)
#define fault_inject_random_crash()
int db_make_string_copy(DB_VALUE *value, DB_CONST_C_CHAR str)
#define DB_PAGESIZE
void disk_rv_dump_init_map(FILE *fp, int length_ignore, void *data)
int disk_spacedb(THREAD_ENTRY *thread_p, SPACEDB_ALL *spaceall, SPACEDB_ONEVOL **spacevols)
enum intl_codeset INTL_CODESET
Definition: intl_support.h:190
DISK_CACHE_VOLINFO vols[LOG_MAX_DBVOLID+1]
Definition: disk_manager.c:195
#define BIT64_HEXA_PRINT_FORMAT
Definition: bit.h:38
void pgbuf_set_page_ptype(THREAD_ENTRY *thread_p, PAGE_PTR pgptr, PAGE_TYPE ptype)
Definition: page_buffer.c:4847
bool prm_get_bool_value(PARAM_ID prm_id)
static int disk_reserve_sectors_in_volume(THREAD_ENTRY *thread_p, int vol_index, DISK_RESERVE_CONTEXT *context)
volatile DKNSECTS nsect_intention
Definition: disk_manager.c:164
void er_clear(void)
int boot_get_new_volume_name_and_id(THREAD_ENTRY *thread_p, DB_VOLTYPE voltype, const char *given_path, const char *given_name, char *fullname_newvol_out, VOLID *volid_newvol_out)
Definition: boot_sr.c:5995
void log_sysop_attach_to_outer(THREAD_ENTRY *thread_p)
Definition: log_manager.c:4076
void log_sysop_commit(THREAD_ENTRY *thread_p)
Definition: log_manager.c:3895
STATIC_INLINE void disk_cache_unlock_reserve(DISK_EXTEND_INFO *expand_info) __attribute__((ALWAYS_INLINE))
int fileio_format(THREAD_ENTRY *thread_p, const char *db_full_name_p, const char *vol_label_p, VOLID vol_id, DKNPAGES npages, bool is_sweep_clean, bool is_do_lock, bool is_do_sync, size_t page_size, int kbytes_to_be_written_per_sec, bool reuse_file)
Definition: file_io.c:2314
#define DISK_ALLOCTBL_SECTOR_PAGE_OFFSET(sect)
Definition: disk_manager.c:250
#define DB_VALUE_TYPE(value)
Definition: dbtype.h:72
int i
Definition: dynamic_load.c:954
int dwb_flush_force(THREAD_ENTRY *thread_p, bool *all_sync)
int disk_rv_volhead_extend_undo(THREAD_ENTRY *thread_p, LOG_RCV *rcv)
DKNSECTS nsect_vol_max
Definition: disk_manager.c:171
STATIC_INLINE int disk_vhdr_length_of_varfields(const DISK_VOLUME_HEADER *vhdr) __attribute__((ALWAYS_INLINE))
#define DISK_RESERVE_CONTEXT_AS_ARGS(context)
Definition: disk_manager.c:339
#define ER_DISK_INCONSISTENT_VOL_HEADER
Definition: error_code.h:636
pthread_mutex_t mutex_extend
Definition: disk_manager.c:200
#define NULL_VOLID
const char * comments
int bit64_count_ones(UINT64 i)
Definition: bit.c:451
DKNPAGES npage_free
#define pthread_mutex_lock(a)
Definition: area_alloc.c:50
#define DISK_STAB_UNIT_SIZE_OF
Definition: disk_manager.c:222
#define IO_MAX_PAGE_SIZE
INT16 xdisk_get_purpose_and_sys_lastpage(THREAD_ENTRY *thread_p, INT16 volid, DISK_VOLPURPOSE *vol_purpose, INT32 *sys_lastpage)
static const char * disk_type_to_string(DB_VOLTYPE voltype)
static cubthread::daemon * disk_Auto_volume_expansion_daemon
Definition: disk_manager.c:481
static int disk_stab_unit_unreserve(THREAD_ENTRY *thread_p, DISK_STAB_CURSOR *cursor, bool *stop, void *args)
static int disk_add_volume(THREAD_ENTRY *thread_p, DBDEF_VOL_EXT_INFO *extinfo, VOLID *volid_out, DKNSECTS *nsects_free_out)
short volid
Definition: dbtype_def.h:931
#define SECTID_MAX
int db_make_int(DB_VALUE *value, const int num)
DB_VOLPURPOSE purpose
Definition: disk_manager.c:154
pthread_mutex_t mutex_reserve
Definition: disk_manager.c:166
DISK_ISVALID disk_check(THREAD_ENTRY *thread_p, bool repair)
short volid
Definition: dbtype_def.h:887
STATIC_INLINE void disk_stab_cursor_set_bit(DISK_STAB_CURSOR *cursor) __attribute__((ALWAYS_INLINE))
#define PGBUF_PAGE_LSA_AS_ARGS(pg)
Definition: page_buffer.h:54
int disk_get_creation_time(THREAD_ENTRY *thread_p, INT16 volid, INT64 *db_creation)
STATIC_INLINE void disk_cache_unlock_reserve_for_purpose(DB_VOLPURPOSE purpose) __attribute__((ALWAYS_INLINE))
#define PGBUF_PAGE_STATE_MSG(name)
Definition: page_buffer.h:56
STATIC_INLINE char * disk_vhdr_get_vol_remarks(const DISK_VOLUME_HEADER *vhdr) __attribute__((ALWAYS_INLINE))
INT32 PAGEID
void disk_lock_extend(void)
#define DONT_FREE
Definition: page_buffer.h:41
int disk_rv_redo_init_map(THREAD_ENTRY *thread_p, LOG_RCV *rcv)
#define DISK_STAB_UNIT_BIT_COUNT
Definition: disk_manager.c:243
DB_VOLPURPOSE purpose
DKNSECTS nsects_lastvol_remaining
Definition: disk_manager.c:287
static int disk_stab_unit_reserve(THREAD_ENTRY *thread_p, DISK_STAB_CURSOR *cursor, bool *stop, void *args)
volatile int owner_reserve
Definition: disk_manager.c:168
DISK_STAB_UNIT * unit
Definition: disk_manager.c:237
#define PEEK
Definition: file_io.h:74
SECTID DKNSECTS
int log_get_db_start_parameters(INT64 *db_creation, LOG_LSA *chkpt_lsa)
Definition: log_manager.c:668
void pgbuf_set_lsa_as_temporary(THREAD_ENTRY *thread_p, PAGE_PTR pgptr)
Definition: page_buffer.c:4790
static DISK_CACHE * disk_Cache
Definition: disk_manager.c:206
int fileio_open(const char *vol_label_p, int flags, int mode)
Definition: file_io.c:1957
static DKNSECTS disk_Temp_max_sects
Definition: disk_manager.c:208
static DISK_ISVALID disk_check_volume(THREAD_ENTRY *thread_p, INT16 volid, bool repair)
#define CUBRID_MAGIC_DATABASE_VOLUME
#define PGBUF_PAGE_STATE_ARGS(pg)
Definition: page_buffer.h:57
#define HFID_COPY(hfid_ptr1, hfid_ptr2)
STATIC_INLINE bool disk_is_valid_volid(VOLID volid) __attribute__((ALWAYS_INLINE))
std::int64_t offset
Definition: log_lsa.hpp:37
#define DISK_ALLOCTBL_SECTOR_UNIT_OFFSET(sect)
Definition: disk_manager.c:252
static DISK_ISVALID disk_check_sectors_are_reserved_in_volume(THREAD_ENTRY *thread_p, VOLID volid, DISK_RESERVE_CONTEXT *context)
DISK_ISVALID
Definition: disk_manager.h:53
#define NULL_SECTID
#define DISK_SECTOR_NPAGES
bool fileio_map_mounted(THREAD_ENTRY *thread_p, bool(*fun)(THREAD_ENTRY *thread_p, VOLID vol_id, void *args), void *args)
Definition: file_io.c:3463
VOLID xboot_find_last_temp(THREAD_ENTRY *thread_p)
Definition: boot_sr.c:407
DISK_EXTEND_INFO extend_info
Definition: disk_manager.c:185
bool bit64_is_set(UINT64 i, int off)
Definition: bit.c:572
#define DISK_STAB_PAGE_BIT_COUNT
Definition: disk_manager.c:247
static int disk_stab_iterate_units_all(THREAD_ENTRY *thread_p, const DISK_VOLUME_HEADER *volheader, PGBUF_LATCH_MODE mode, DISK_STAB_UNIT_FUNC f_unit, void *f_unit_args)
#define pthread_mutex_destroy(a)
Definition: area_alloc.c:49
int fileio_get_volume_descriptor(VOLID vol_id)
Definition: file_io.c:6488