/**************************************************************************** * NCSA HDF * * Software Development Group * * National Center for Supercomputing Applications * * University of Illinois at Urbana-Champaign * * 605 E. Springfield, Champaign IL 61820 * * * * For conditions of distribution and use, see the accompanying * * hdf/COPYING file. * * * MODIFICATIONS * Robb Matzke, 30 Aug 1997 * Added `ERRORS' fields to function prologues. * ****************************************************************************/ #ifdef RCSID static char RcsId[] = "@(#)$Revision$"; #endif /* $Id$ */ #define H5F_PACKAGE /*suppress error about including H5Fpkg */ /* Predefined file drivers */ #include /*temporary in-memory files */ #include /*family of files */ #include /*MPI-2 I/O */ #include /*GASS I/O */ #include /*Grid Storage I/O */ #include /*in-memory files streamed via sockets */ #include /*SRB I/O */ #include /*multiple files partitioned by mem usage */ #include /*Posix unbuffered I/O */ #include /* Standard C buffered I/O */ #include /* sec2 driver with logging, for debugging */ /* Packages needed by this file... */ #include /*library functions */ #include /*attributes */ #include /*datasets */ #include /*Free Lists */ #include /*object IDs */ #include /*cache */ #include /*error handling */ #include /*file access */ #include /*file driver */ #include /*symbol tables */ #include /*core memory management */ #include /*property lists */ #include /*data types */ #define PABLO_MASK H5F_mask /* * Define the default file creation property list. */ const H5F_create_t H5F_create_dflt = { 0, /* Default user-block size */ 4, /* Default 1/2 rank for symtab leaf nodes */ { /* Default 1/2 rank for btree intern nodes */ 16, /* Symbol table internal nodes */ 32, /* Indexed storage internal nodes */ 0, /* unused */ 0, /* unused */ 0, /* unused */ 0, /* unused */ 0, /* unused */ 0, /* unused */ }, sizeof(haddr_t), /* Default offset size */ sizeof(hsize_t), /* Default length size */ HDF5_BOOTBLOCK_VERSION, /* Current Boot-Block version # */ HDF5_FREESPACE_VERSION, /* Current Free-Space info version # */ HDF5_OBJECTDIR_VERSION, /* Current Object Directory info version # */ HDF5_SHAREDHEADER_VERSION, /* Current Shared-Header format version # */ }; /* * Define the default file access property list. The template is initialized * by H5F_init_interface(). */ H5F_access_t H5F_access_dflt; /* * Define the default mount property list. */ const H5F_mprop_t H5F_mount_dflt = { FALSE, /* Absolute symlinks are wrt mount root */ }; /* Interface initialization */ static intn interface_initialize_g = 0; #define INTERFACE_INIT H5F_init_interface static herr_t H5F_init_interface(void); /* PRIVATE PROTOTYPES */ static H5F_t *H5F_new(H5F_file_t *shared, hid_t fcpl_id, hid_t fapl_id); static herr_t H5F_dest(H5F_t *f); static herr_t H5F_flush(H5F_t *f, H5F_scope_t scope, hbool_t invalidate, hbool_t alloc_only); static haddr_t H5F_locate_signature(H5FD_t *file); static intn H5F_flush_all_cb(H5F_t *f, const void *_invalidate); /* Declare a free list to manage the H5F_t struct */ H5FL_DEFINE_STATIC(H5F_t); /* Declare a free list to manage the H5F_file_t struct */ H5FL_DEFINE_STATIC(H5F_file_t); /* Declare the external free list for the H5G_t struct */ H5FL_EXTERN(H5G_t); /*------------------------------------------------------------------------- * Function: H5F_init * * Purpose: Initialize the interface from some other layer. * * Return: Success: non-negative * * Failure: negative * * Programmer: Robb Matzke * Wednesday, December 16, 1998 * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5F_init(void) { FUNC_ENTER(H5F_init, FAIL); /* FUNC_ENTER() does all the work */ FUNC_LEAVE(SUCCEED); } /*------------------------------------------------------------------------- * Function: H5F_init_interface * * Purpose: Initialize interface-specific information. * * Return: Success: non-negative * * Failure: negative * * Programmer: Robb Matzke * Friday, November 20, 1998 * * Modifications: * Robb Matzke, 4 Aug 1997 * Changed pablo mask from H5_mask to H5F_mask for the FUNC_LEAVE call. * It was already H5F_mask for the PABLO_TRACE_ON call. * * Kim Yates, 1998-08-16 * Added .disp, .btype, .ftype to H5F_access_t. * * Robb Matzke, 1999-02-19 * Added initialization for the H5I_FILE_CLOSING ID group. * * Raymond Lu, April 10, 2000 * Put SRB into the 'Register predefined file drivers' list. * * Thomas Radke, 2000-09-12 * Put Stream VFD into the 'Register predefined file drivers' list. *------------------------------------------------------------------------- */ static herr_t H5F_init_interface(void) { herr_t ret_value = SUCCEED; herr_t status; FUNC_ENTER(H5F_init_interface, FAIL); #ifdef H5_HAVE_PARALLEL { /* Allow MPI buf-and-file-type optimizations? */ const char *s = HDgetenv ("HDF5_MPI_1_METAWRITE"); if (s && HDisdigit(*s)) { H5_mpi_1_metawrite_g = (int)HDstrtol (s, NULL, 0); } } #endif /* * Initialize the atom group for the file IDs. There are two groups: * the H5I_FILE group contains all the ID's for files which are currently * open at the public API level while the H5I_FILE_CLOSING group contains * ID's for files for which the application has called H5Fclose() but * which are pending completion because there are object headers still * open within the file. */ if (H5I_init_group(H5I_FILE, H5I_FILEID_HASHSIZE, 0, (H5I_free_t)H5F_close)<0 || H5I_init_group(H5I_FILE_CLOSING, H5I_FILEID_HASHSIZE, 0, (H5I_free_t)H5F_close)<0) { HRETURN_ERROR (H5E_FILE, H5E_CANTINIT, FAIL, "unable to initialize interface"); } /* Register the default file creation & access properties */ /* Register predefined file drivers */ H5E_BEGIN_TRY { if ((status=H5FD_SEC2)<0) goto end_registration; if ((status=H5FD_STDIO)<0) goto end_registration; if ((status=H5FD_FAMILY)<0) goto end_registration; #ifdef H5_HAVE_GASS if ((status=H5FD_GASS)<0) goto end_registration; #endif #ifdef H5_HAVE_GRIDSTORAGE if ((status=H5FD_DPSS)<0) goto end_registration; #endif #ifdef H5_HAVE_SRB if ((status=H5FD_SRB)<0) goto end_registration; #endif if ((status=H5FD_CORE)<0) goto end_registration; if ((status=H5FD_MULTI)<0) goto end_registration; #ifdef H5_HAVE_PARALLEL if ((status=H5FD_MPIO)<0) goto end_registration; #endif #ifdef H5_HAVE_STREAM if ((status=H5FD_STREAM)<0) goto end_registration; #endif end_registration: ; } H5E_END_TRY; if (status<0) { HRETURN_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "file driver registration failed"); } /* Initialize the default file access property list */ H5F_access_dflt.mdc_nelmts = H5AC_NSLOTS; H5F_access_dflt.rdcc_nelmts = 521; H5F_access_dflt.rdcc_nbytes = 1024*1024; /*1MB*/ H5F_access_dflt.rdcc_w0 = 0.75; /*preempt fully read chunks*/ H5F_access_dflt.threshold = 1; /*alignment applies to everything*/ H5F_access_dflt.alignment = 1; /*no alignment*/ H5F_access_dflt.gc_ref = 0; /*don't garbage-collect references*/ H5F_access_dflt.meta_block_size = 2048; /* set metadata block allocations to 2KB */ H5F_access_dflt.sieve_buf_size = 64*1024; /* set sieve buffer allocation to 64KB */ H5F_access_dflt.driver_id = H5FD_SEC2; /*default driver*/ H5F_access_dflt.driver_info = NULL; /*driver file access properties*/ FUNC_LEAVE(ret_value); } /*------------------------------------------------------------------------- * Function: H5F_term_interface * * Purpose: Terminate this interface: free all memory and reset global * variables to their initial values. Release all ID groups * associated with this interface. * * Return: Success: Positive if anything was done that might * have affected other interfaces; zero * otherwise. * * Failure: Never fails. * * Programmer: Robb Matzke * Friday, February 19, 1999 * * Modifications: * *------------------------------------------------------------------------- */ intn H5F_term_interface(void) { intn n = 0; if (interface_initialize_g) { if ((n=H5I_nmembers(H5I_FILE))) { H5F_close_all(); } else if (0==(n=H5I_nmembers(H5I_FILE_CLOSING))) { H5I_destroy_group(H5I_FILE); H5I_destroy_group(H5I_FILE_CLOSING); interface_initialize_g = 0; n = 1; /*H5I*/ } } return n; } /*------------------------------------------------------------------------- * Function: H5F_flush_all_cb * * Purpose: Callback function for H5F_flush_all(). * * Return: Always returns zero. * * Programmer: Robb Matzke * Friday, February 19, 1999 * * Modifications: * *------------------------------------------------------------------------- */ static intn H5F_flush_all_cb(H5F_t *f, const void *_invalidate) { hbool_t invalidate = *((const hbool_t*)_invalidate); H5F_flush(f, H5F_SCOPE_LOCAL, invalidate, FALSE); return 0; } /*------------------------------------------------------------------------- * Function: H5F_flush_all * * Purpose: Flush all open files. If INVALIDATE is true then also remove * everything from the cache. * * Return: Success: Non-negative * * Failure: Negative * * Programmer: Robb Matzke * Thursday, February 18, 1999 * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5F_flush_all(hbool_t invalidate) { FUNC_ENTER(H5F_flush_all, FAIL); H5I_search(H5I_FILE, (H5I_search_func_t)H5F_flush_all_cb, (void*)&invalidate); FUNC_LEAVE(SUCCEED); } /*------------------------------------------------------------------------- * Function: H5F_close_all * * Purpose: Close all open files. Any file which has open object headers * will be moved from the H5I_FILE group to the H5I_FILE_CLOSING * group. * * Return: Success: Non-negative * * Failure: Negative * * Programmer: Robb Matzke * Friday, February 19, 1999 * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5F_close_all(void) { FUNC_ENTER(H5F_close_all, FAIL); if (H5I_clear_group(H5I_FILE, FALSE)<0) { HRETURN_ERROR(H5E_FILE, H5E_CLOSEERROR, FAIL, "unable to close one or more files"); } FUNC_LEAVE(SUCCEED); } /*-------------------------------------------------------------------------- NAME H5F_encode_length_unusual -- encode an unusual length size USAGE void H5F_encode_length_unusual(f, p, l) const H5F_t *f; IN: pointer to the file record uint8_t **p; IN: pointer to buffer pointer to encode length in uint8_t *l; IN: pointer to length to encode ERRORS RETURNS none DESCRIPTION Encode non-standard (i.e. not 2, 4 or 8-byte) lengths in file meta-data. --------------------------------------------------------------------------*/ void H5F_encode_length_unusual(const H5F_t *f, uint8_t **p, uint8_t *l) { intn i = (intn)H5F_SIZEOF_SIZE(f)-1; #ifdef WORDS_BIGENDIAN /* * For non-little-endian platforms, encode each byte in memory backwards. */ for (/*void*/; i>=0; i--, (*p)++)*(*p) = *(l+i); #else /* platform has little-endian integers */ HDmemcpy(*p,l,(size_t)(i+1)); *p+=(i+1); #endif } /*------------------------------------------------------------------------- * Function: H5Fget_create_plist * * Purpose: Get an atom for a copy of the file-creation property list for * this file. This function returns an atom with a copy of the * properties used to create a file. * * Return: Success: template ID * * Failure: FAIL * * Programmer: Unknown * * Modifications: * * Robb Matzke, 18 Feb 1998 * Calls H5P_copy() to copy the property list and H5P_close() to free * that property list if an error occurs. * *------------------------------------------------------------------------- */ hid_t H5Fget_create_plist(hid_t file_id) { H5F_t *file = NULL; hid_t ret_value = FAIL; H5P_t *plist = NULL; FUNC_ENTER(H5Fget_create_plist, FAIL); H5TRACE1("i","i",file_id); /* check args */ if (H5I_FILE!=H5I_get_type(file_id) || NULL==(file=H5I_object(file_id))) { HRETURN_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file"); } /* Create the property list object to return */ if (NULL==(plist=H5P_copy(H5P_FILE_CREATE, file->shared->fcpl))) { HRETURN_ERROR(H5E_INTERNAL, H5E_CANTINIT, FAIL, "unable to copy file creation properties"); } /* Create an atom */ if ((ret_value = H5P_create(H5P_FILE_CREATE, plist)) < 0) { H5P_close(plist); HRETURN_ERROR(H5E_ATOM, H5E_CANTREGISTER, FAIL, "unable to register property list"); } FUNC_LEAVE(ret_value); } /*------------------------------------------------------------------------- * Function: H5Fget_access_plist * * Purpose: Returns a copy of the file access property list of the * specified file. * * Return: Success: Object ID for a copy of the file access * property list. * * Failure: FAIL * * Programmer: Robb Matzke * Wednesday, February 18, 1998 * * Modifications: * *------------------------------------------------------------------------- */ hid_t H5Fget_access_plist(hid_t file_id) { H5F_t *f = NULL; H5F_access_t _fapl; H5P_t *plist=NULL; hid_t ret_value = FAIL; FUNC_ENTER(H5Fget_access_plist, FAIL); H5TRACE1("i","i",file_id); /* Check args */ if (H5I_FILE!=H5I_get_type(file_id) || NULL==(f=H5I_object(file_id))) { HRETURN_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file"); } /* Initialize the property list */ HDmemset(&_fapl, 0, sizeof _fapl); _fapl.mdc_nelmts = f->shared->mdc_nelmts; _fapl.rdcc_nelmts = f->shared->rdcc_nelmts; _fapl.rdcc_nbytes = f->shared->rdcc_nbytes; _fapl.rdcc_w0 = f->shared->rdcc_w0; _fapl.threshold = f->shared->threshold; _fapl.alignment = f->shared->alignment; _fapl.gc_ref = f->shared->gc_ref; _fapl.meta_block_size = f->shared->lf->def_meta_block_size; _fapl.sieve_buf_size = f->shared->sieve_buf_size; _fapl.driver_id = f->shared->lf->driver_id; _fapl.driver_info = NULL; /*just for now */ /* Copy properties */ if (NULL==(plist=H5P_copy(H5P_FILE_ACCESS, &_fapl))) { HRETURN_ERROR(H5E_INTERNAL, H5E_CANTINIT, FAIL, "unable to copy file access properties"); } /* Get the properties for the file driver */ plist->u.faccess.driver_info = H5FD_fapl_get(f->shared->lf); /* Create an atom */ if ((ret_value = H5P_create(H5P_FILE_ACCESS, plist))<0) { H5P_close(plist); HRETURN_ERROR(H5E_ATOM, H5E_CANTREGISTER, FAIL, "unable to register property list"); } FUNC_LEAVE(ret_value); } /*------------------------------------------------------------------------- * Function: H5F_equal * * Purpose: Compares NEEDLE to a file from the HAYSTACK. * * Return: Success: Returns positive if two files are equal, * zero otherwise. * * Failure: Negative * * Programmer: Robb Matzke * Monday, August 2, 1999 * * Modifications: * *------------------------------------------------------------------------- */ static intn H5F_equal(void *_haystack, const void *_needle) { H5F_t *haystack = (H5F_t*)_haystack; const H5FD_t *needle = (const H5FD_t*)_needle; intn retval; FUNC_ENTER(H5F_equal, FAIL); retval = (0==H5FD_cmp(haystack->shared->lf, needle)); FUNC_LEAVE(retval); } /*------------------------------------------------------------------------- * Function: H5F_locate_signature * * Purpose: Finds the HDF5 boot block signature in a file. The signature * can appear at address 0, or any power of two beginning with * 512. * * Return: Success: The absolute format address of the signature. * * Failure: HADDR_UNDEF * * Programmer: Robb Matzke * Friday, November 7, 1997 * * Modifications: * Robb Matzke, 1999-08-02 * Rewritten to use the virtual file layer. *------------------------------------------------------------------------- */ static haddr_t H5F_locate_signature(H5FD_t *file) { haddr_t addr, eoa; uint8_t buf[H5F_SIGNATURE_LEN]; uintn n, maxpow; FUNC_ENTER(H5F_locate_signature, HADDR_UNDEF); /* Find the least N such that 2^N is larger than the file size */ if (HADDR_UNDEF==(addr=H5FD_get_eof(file)) || HADDR_UNDEF==(eoa=H5FD_get_eoa(file))) { HRETURN_ERROR(H5E_IO, H5E_CANTINIT, HADDR_UNDEF, "unable to obtain EOF/EOA value"); } for (maxpow=0; addr; maxpow++) addr>>=1; maxpow = MAX(maxpow, 9); /* * Search for the file signature at format address zero followed by * powers of two larger than 9. */ for (n=8; n=maxpow) { H5FD_set_eoa(file, eoa); HRETURN_ERROR(H5E_IO, H5E_CANTINIT, HADDR_UNDEF, "unable to find a valid file signature"); } /* Success */ FUNC_LEAVE(addr); } /*------------------------------------------------------------------------- * Function: H5Fis_hdf5 * * Purpose: Check the file signature to detect an HDF5 file. * * Bugs: This function is not robust: it only uses the default file * driver when attempting to open the file when in fact it * should use all known file drivers. * * Return: Success: TRUE/FALSE * * Failure: Negative * * Programmer: Unknown * * Modifications: * Robb Matzke, 1999-08-02 * Rewritten to use the virtual file layer. *------------------------------------------------------------------------- */ htri_t H5Fis_hdf5(const char *name) { H5FD_t *file = NULL; htri_t ret_value = FAIL; FUNC_ENTER(H5Fis_hdf5, FAIL); H5TRACE1("b","s",name); /* Check args and all the boring stuff. */ if (!name || !*name) { HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "no file name specified"); } /* Open the file at the virtual file layer */ if (NULL==(file=H5FD_open(name, H5F_ACC_RDONLY, H5P_DEFAULT, HADDR_UNDEF))) { HGOTO_ERROR(H5E_IO, H5E_CANTINIT, FAIL, "unable to open file"); } /* The file is an hdf5 file if the hdf5 file signature can be found */ ret_value = (HADDR_UNDEF!=H5F_locate_signature(file)); done: /* Close the file */ if (file) H5FD_close(file); FUNC_LEAVE(ret_value); } /*------------------------------------------------------------------------- * Function: H5F_new * * Purpose: Creates a new file object and initializes it. The * H5Fopen and H5Fcreate functions then fill in various * fields. If SHARED is a non-null pointer then the shared info * to which it points has the reference count incremented. * Otherwise a new, empty shared info struct is created and * initialized with the specified file access property list. * * Errors: * * Return: Success: Ptr to a new file struct. * * Failure: NULL * * Programmer: Robb Matzke * matzke@llnl.gov * Jul 18 1997 * * Modifications: * *------------------------------------------------------------------------- */ static H5F_t * H5F_new(H5F_file_t *shared, hid_t fcpl_id, hid_t fapl_id) { H5F_t *f=NULL, *ret_value=NULL; intn n; const H5F_create_t *fcpl=NULL; const H5F_access_t *fapl=NULL; FUNC_ENTER(H5F_new, NULL); if (NULL==(f=H5FL_ALLOC(H5F_t,1))) { HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed"); } if (shared) { f->shared = shared; } else { f->shared = H5FL_ALLOC(H5F_file_t,1); f->shared->boot_addr = HADDR_UNDEF; f->shared->base_addr = HADDR_UNDEF; f->shared->freespace_addr = HADDR_UNDEF; f->shared->driver_addr = HADDR_UNDEF; /* * Copy the file creation and file access property lists into the * new file handle. We do this early because some values might need * to change as the file is being opened. */ fcpl = (H5P_DEFAULT==fcpl_id)? &H5F_create_dflt : (const H5F_create_t *)H5I_object(fcpl_id); if (NULL==(f->shared->fcpl=H5P_copy(H5P_FILE_CREATE, fcpl))) { HRETURN_ERROR(H5E_FILE, H5E_CANTINIT, NULL, "unable to copy file creation property list"); } fapl = (H5P_DEFAULT==fapl_id)? &H5F_access_dflt : (const H5F_access_t *)H5I_object(fapl_id); f->shared->mdc_nelmts = fapl->mdc_nelmts; f->shared->rdcc_nelmts = fapl->rdcc_nelmts; f->shared->rdcc_nbytes = fapl->rdcc_nbytes; f->shared->rdcc_w0 = fapl->rdcc_w0; f->shared->threshold = fapl->threshold; f->shared->alignment = fapl->alignment; f->shared->gc_ref = fapl->gc_ref; f->shared->sieve_buf_size = fapl->sieve_buf_size; #ifdef H5_HAVE_PARALLEL /* * Disable cache if file is open using MPIO driver. Parallel * does not permit caching. (maybe able to relax it for * read only open.) */ if (H5FD_MPIO==fapl->driver_id){ f->shared->rdcc_nbytes = 0; f->shared->mdc_nelmts = 0; } #endif /* * Create a meta data cache with the specified number of elements. * The cache might be created with a different number of elements and * the access property list should be updated to reflect that. */ if ((n=H5AC_create(f, f->shared->mdc_nelmts))<0) { HRETURN_ERROR(H5E_FILE, H5E_CANTINIT, NULL, "unable to create meta data cache"); } f->shared->mdc_nelmts = n; /* Create the chunk cache */ H5F_istore_init(f); } f->shared->nrefs++; f->nrefs = 1; ret_value = f; done: if (!ret_value && f) { if (!shared) H5FL_FREE(H5F_file_t,f->shared); H5FL_FREE(H5F_t,f); } FUNC_LEAVE(ret_value); } /*------------------------------------------------------------------------- * Function: H5F_dest * * Purpose: Destroys a file structure. This function flushes the cache * but doesn't do any other cleanup other than freeing memory * for the file struct. The shared info for the file is freed * only when its reference count reaches zero. * * Return: Non-negative on success/Negative on failure * * Programmer: Robb Matzke * matzke@llnl.gov * Jul 18 1997 * * Modifications: * * Robb Matzke, 1998-10-14 * Nothing happens unless the reference count for the H5F_t goes to * zero. The reference counts are decremented here. * * Robb Matzke, 1999-02-19 * More careful about decrementing reference counts so they don't go * negative or wrap around to some huge value. Nothing happens if a * reference count is already zero. *------------------------------------------------------------------------- */ static herr_t H5F_dest(H5F_t *f) { herr_t ret_value = SUCCEED; FUNC_ENTER(H5F_dest, FAIL); if (f && 1==f->nrefs) { if (1==f->shared->nrefs) { /* * Do not close the root group since we didn't count it, but free * the memory associated with it. */ H5FL_FREE(H5G_t,f->shared->root_grp); f->shared->root_grp=NULL; if (H5AC_dest(f)) { HERROR(H5E_FILE, H5E_CANTINIT, "problems closing file"); ret_value = FAIL; /*but keep going*/ } if (H5F_istore_dest (f)<0) { HERROR(H5E_FILE, H5E_CANTINIT, "problems closing file"); ret_value = FAIL; /*but keep going*/ } f->shared->cwfs = H5MM_xfree (f->shared->cwfs); /* Free the data sieve buffer, if it's been allocated */ if(f->shared->sieve_buf) { assert(f->shared->sieve_dirty==0); /* The buffer had better be flushed... */ f->shared->sieve_buf = H5MM_xfree (f->shared->sieve_buf); } /* end if */ /* Destroy file creation properties */ H5P_close(f->shared->fcpl); /* Destroy shared file struct */ if (H5FD_close(f->shared->lf)<0) { HERROR(H5E_FILE, H5E_CANTINIT, "problems closing file"); ret_value = FAIL; /*but keep going*/ } f->shared = H5FL_FREE(H5F_file_t,f->shared); } else if (f->shared->nrefs>0) { /* * There are other references to the shared part of the file. * Only decrement the reference count. */ --f->shared->nrefs; } /* Free the non-shared part of the file */ f->name = H5MM_xfree(f->name); f->mtab.child = H5MM_xfree(f->mtab.child); f->mtab.nalloc = 0; H5FL_FREE(H5F_t,f); } else if (f && f->nrefs>0) { /* * There are other references to this file. Only decrement the * reference count. */ --f->nrefs; } FUNC_LEAVE(ret_value); } /*------------------------------------------------------------------------- * Function: H5F_open * * Purpose: Opens (or creates) a file. This function understands the * following flags which are similar in nature to the Posix * open(2) flags. * * H5F_ACC_RDWR: Open with read/write access. If the file is * currently open for read-only access then it * will be reopened. Absence of this flag * implies read-only access. * * H5F_ACC_CREAT: Create a new file if it doesn't exist yet. * The permissions are 0666 bit-wise AND with * the current umask. H5F_ACC_WRITE must also * be specified. * * H5F_ACC_EXCL: This flag causes H5F_open() to fail if the * file already exists. * * H5F_ACC_TRUNC: The file is truncated and a new HDF5 superblock * is written. This operation will fail if the * file is already open. * * Unlinking the file name from the group directed graph while * the file is opened causes the file to continue to exist but * one will not be able to upgrade the file from read-only * access to read-write access by reopening it. Disk resources * for the file are released when all handles to the file are * closed. NOTE: This paragraph probably only applies to Unix; * deleting the file name in other OS's has undefined results. * * The CREATE_PARMS argument is optional. A null pointer will * cause the default file creation parameters to be used. * * The ACCESS_PARMS argument is optional. A null pointer will * cause the default file access parameters to be used. * * Return: Success: A new file pointer. * * Failure: NULL * * Programmer: Robb Matzke * Tuesday, September 23, 1997 * * Modifications: * Albert Cheng, 1998-02-05 * Added the access_parms argument to pass down access template * information. * * Robb Matzke, 1998-02-18 * The H5F_access_t changed to allow more generality. The low * level driver is part of the file access template so the TYPE * argument has been removed. * * Robb Matzke, 1999-08-02 * Rewritten to use the virtual file layer. * * Robb Matzke, 1999-08-16 * Added decoding of file driver information block, which uses a * formerly reserved address slot in the boot block in order to * be compatible with previous versions of the file format. * * Robb Matzke, 1999-08-20 * Optimizations for opening a file. If the driver can't * determine when two file handles refer to the same file then * we open the file in one step. Otherwise if the first attempt * to open the file fails then we skip the second attempt if the * arguments would be the same. *------------------------------------------------------------------------- */ H5F_t * H5F_open(const char *name, uintn flags, hid_t fcpl_id, hid_t fapl_id) { H5F_t *file=NULL; /*the success return value */ H5F_t *ret_value=NULL;/*actual return value */ H5F_file_t *shared=NULL; /*shared part of `file' */ H5FD_t *lf=NULL; /*file driver part of `shared' */ uint8_t buf[256]; /*temporary I/O buffer */ const uint8_t *p; /*ptr into temp I/O buffer */ size_t fixed_size=24; /*fixed sizeof superblock */ size_t variable_size; /*variable sizeof superblock */ size_t driver_size; /*size of driver info block */ H5G_entry_t root_ent; /*root symbol table entry */ haddr_t eof; /*end of file address */ haddr_t stored_eoa; /*relative end-of-addr in file */ uintn tent_flags; /*tentative flags */ char driver_name[9]; /*file driver name/version */ hbool_t driver_has_cmp; /*`cmp' callback defined? */ FUNC_ENTER(H5F_open, NULL); /* * If the driver has a `cmp' method then the driver is capable of * determining when two file handles refer to the same file and the * library can insure that when the application opens a file twice that * the two handles coordinate their operations appropriately. Otherwise * it is the application's responsibility to never open the same file * more than once at a time. */ driver_has_cmp = H5FD_has_cmp(fapl_id); /* * Opening a file is a two step process. First we try to open the file in * a way which doesn't affect its state (like not truncating or creating * it) so we can compare it with files that are already open. If that * fails then we try again with the full set of flags (only if they're * different than the original failed attempt). However, if the file * driver can't distinquish between files then there's no reason to open * the file tentatively because it's the application's responsibility to * prevent this situation (there's no way for us to detect it here * anyway). */ if (driver_has_cmp) { tent_flags = flags & ~(H5F_ACC_CREAT|H5F_ACC_TRUNC|H5F_ACC_EXCL); } else { tent_flags = flags; } if (NULL==(lf=H5FD_open(name, tent_flags, fapl_id, HADDR_UNDEF))) { if (tent_flags == flags) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to open file"); } H5E_clear(); tent_flags = flags; if (NULL==(lf=H5FD_open(name, tent_flags, fapl_id, HADDR_UNDEF))) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to open file"); } } /* Is the file already open? */ if ((file=H5I_search(H5I_FILE, H5F_equal, lf)) || (file=H5I_search(H5I_FILE_CLOSING, H5F_equal, lf))) { /* * The file is already open, so use that one instead of the one we * just opened. We only one one H5FD_t* per file so one doesn't * confuse the other. But fail if this request was to truncate the * file (since we can't do that while the file is open), or if the * request was to create a non-existent file (since the file already * exists), or if the new request adds write access (since the * readers don't expect the file to change under them). */ H5FD_close(lf); if (flags & H5F_ACC_TRUNC) { file = NULL; /*to prevent destruction of wrong file*/ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to truncate a file which is already open"); } if (flags & H5F_ACC_EXCL) { file = NULL; /*to prevent destruction of wrong file*/ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "file exists"); } if ((flags & H5F_ACC_RDWR) && 0==(file->intent & H5F_ACC_RDWR)) { file = NULL; /*to prevent destruction of wrong file*/ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "file is already open for read-only"); } file = H5F_new(file->shared, fcpl_id, fapl_id); lf = file->shared->lf; } else if (flags!=tent_flags) { /* * This file is not yet open by the library and the flags we used to * open it are different than the desired flags. Close the tentative * file and open it for real. */ H5FD_close(lf); if (NULL==(lf=H5FD_open(name, flags, fapl_id, HADDR_UNDEF))) { file = NULL; /*to prevent destruction of wrong file*/ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to open file"); } if (NULL==(file = H5F_new(NULL, fcpl_id, fapl_id))) HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to create new file object"); file->shared->flags = flags; file->shared->lf = lf; } else { /* * This file is not yet open by the library and our tentative opening * above is good enough. */ if (NULL==(file = H5F_new(NULL, fcpl_id, fapl_id))) HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to create new file object"); file->shared->flags = flags; file->shared->lf = lf; } /* Short cuts */ shared = file->shared; lf = shared->lf; /* * The intent at the top level file struct are not necessarily the same as * the flags at the bottom. The top level describes how the file can be * accessed through the HDF5 library. The bottom level describes how the * file can be accessed through the C library. */ file->intent = flags; file->name = H5MM_xstrdup(name); /* * Read or write the file superblock, depending on whether the file is * empty or not. */ if (0==H5FD_get_eof(lf) && (flags & H5F_ACC_RDWR)) { /* * The superblock starts immediately after the user-defined header, * which we have already insured is a proper size. The base address * is set to the same thing as the superblock for now. */ shared->boot_addr = shared->fcpl->userblock_size; shared->base_addr = shared->boot_addr; shared->consist_flags = 0x03; if (H5F_flush(file, H5F_SCOPE_LOCAL, FALSE, TRUE)<0) { HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, NULL, "unable to write file superblock"); } /* Create and open the root group */ if (H5G_mkroot(file, NULL)<0) { HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, NULL, "unable to create/open root group"); } } else if (1==shared->nrefs) { /* Read the superblock if it hasn't been read before. */ if (HADDR_UNDEF==(shared->boot_addr=H5F_locate_signature(lf))) { HGOTO_ERROR(H5E_FILE, H5E_NOTHDF5, NULL, "unable to find file signature"); } if (H5FD_set_eoa(lf, shared->boot_addr+fixed_size)<0 || H5FD_read(lf, H5FD_MEM_SUPER, H5P_DEFAULT, shared->boot_addr, fixed_size, buf)<0) { HGOTO_ERROR(H5E_FILE, H5E_READERROR, NULL, "unable to read superblock"); } /* Signature, already checked */ p = buf + H5F_SIGNATURE_LEN; /* Superblock version */ shared->fcpl->bootblock_ver = *p++; if (HDF5_BOOTBLOCK_VERSION!=shared->fcpl->bootblock_ver) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "bad superblock version number"); } /* Freespace version */ shared->fcpl->freespace_ver = *p++; if (HDF5_FREESPACE_VERSION!=shared->fcpl->freespace_ver) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "bad free space version number"); } /* Root group version number */ shared->fcpl->objectdir_ver = *p++; if (HDF5_OBJECTDIR_VERSION!=shared->fcpl->objectdir_ver) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "bad root group version number"); } /* reserved */ p++; /* Shared header version number */ shared->fcpl->sharedheader_ver = *p++; if (HDF5_SHAREDHEADER_VERSION!=shared->fcpl->sharedheader_ver) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "bad shared header version number"); } /* Size of file addresses */ shared->fcpl->sizeof_addr = *p++; if (shared->fcpl->sizeof_addr != 2 && shared->fcpl->sizeof_addr != 4 && shared->fcpl->sizeof_addr != 8 && shared->fcpl->sizeof_addr != 16 && shared->fcpl->sizeof_addr != 32) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "bad file sizeof(address)"); } /* Size of file sizes */ shared->fcpl->sizeof_size = *p++; if (shared->fcpl->sizeof_size != 2 && shared->fcpl->sizeof_size != 4 && shared->fcpl->sizeof_size != 8 && shared->fcpl->sizeof_size != 16 && shared->fcpl->sizeof_size != 32) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "bad file sizeof(size)"); } /* Reserved byte */ p++; /* Various B-tree sizes */ UINT16DECODE(p, shared->fcpl->sym_leaf_k); if (shared->fcpl->sym_leaf_k < 1) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "bad symbol table leaf node 1/2 rank"); } UINT16DECODE(p, shared->fcpl->btree_k[H5B_SNODE_ID]); if (shared->fcpl->btree_k[H5B_SNODE_ID] < 1) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "bad symbol table internal node 1/2 rank"); } /* File consistency flags. Not really used yet */ UINT32DECODE(p, shared->consist_flags); assert((size_t)(p-buf) == fixed_size); /* Decode the variable-length part of the superblock... */ variable_size = H5F_SIZEOF_ADDR(file) + /*base addr*/ H5F_SIZEOF_ADDR(file) + /*global free list*/ H5F_SIZEOF_ADDR(file) + /*end-of-address*/ H5F_SIZEOF_ADDR(file) + /*reserved address*/ H5G_SIZEOF_ENTRY(file); /*root group ptr*/ assert(variable_size<=sizeof buf); if (H5FD_set_eoa(lf, shared->boot_addr+fixed_size+variable_size)<0 || H5FD_read(lf, H5FD_MEM_SUPER, H5P_DEFAULT, shared->boot_addr+fixed_size, variable_size, buf)<0) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to read superblock"); } p = buf; H5F_addr_decode(file, &p, &(shared->base_addr)/*out*/); H5F_addr_decode(file, &p, &(shared->freespace_addr)/*out*/); H5F_addr_decode(file, &p, &stored_eoa/*out*/); H5F_addr_decode(file, &p, &(shared->driver_addr)/*out*/); if (H5G_ent_decode(file, &p, &root_ent/*out*/)<0) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to read root symbol entry"); } /* Decode the optional driver information block */ if (H5F_addr_defined(shared->driver_addr)) { haddr_t drv_addr = shared->base_addr + shared->driver_addr; if (H5FD_set_eoa(lf, drv_addr+16)<0 || H5FD_read(lf, H5FD_MEM_SUPER, H5P_DEFAULT, drv_addr, 16, buf)<0) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to read driver information block"); } p = buf; /* Version number */ if (HDF5_DRIVERINFO_VERSION!=*p++) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "bad driver information block version number"); } /* Reserved */ p += 3; /* Driver info size */ UINT32DECODE(p, driver_size); /* Driver name and/or version */ strncpy(driver_name, (const char *)p, 8); driver_name[8] = '\0'; /* Read driver information and decode */ if (H5FD_set_eoa(lf, drv_addr+16+driver_size)<0 || H5FD_read(lf, H5FD_MEM_SUPER, H5P_DEFAULT, drv_addr+16, driver_size, buf)<0) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to read file driver information"); } if (H5FD_sb_decode(lf, driver_name, buf)<0) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to decode driver information"); } } /* Make sure we can open the root group */ if (H5G_mkroot(file, &root_ent)<0) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to read root group"); } /* * The user-defined data is the area of the file before the base * address. */ shared->fcpl->userblock_size = shared->base_addr; /* * Make sure that the data is not truncated. One case where this is * possible is if the first file of a family of files was opened * individually. */ if (HADDR_UNDEF==(eof=H5FD_get_eof(lf))) { HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to determine file size"); } if (eoffile; } if (!f) { HRETURN_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "object is not associated with a file"); } /* Flush the file */ if (H5F_flush(f, scope, FALSE, FALSE)<0) { HRETURN_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "flush failed"); } FUNC_LEAVE(SUCCEED); } /*------------------------------------------------------------------------- * Function: H5F_flush * * Purpose: Flushes (and optionally invalidates) cached data plus the * file boot block. If the logical file size field is zero * then it is updated to be the length of the boot block. * * Return: Non-negative on success/Negative on failure * * Programmer: Robb Matzke * matzke@llnl.gov * Aug 29 1997 * * Modifications: * rky 1998-08-28 * Only p0 writes metadata to disk. * * Robb Matzke, 1998-10-16 * Added the `scope' argument to indicate what should be * flushed. If the value is H5F_SCOPE_GLOBAL then the entire * virtual file is flushed; a value of H5F_SCOPE_LOCAL means * that only the specified file is flushed. A value of * H5F_SCOPE_DOWN means flush the specified file and all * children. * * Robb Matzke, 1999-08-02 * If ALLOC_ONLY is non-zero then all this function does is * allocate space for the userblock and superblock. Also * rewritten to use the virtual file layer. * * Robb Matzke, 1999-08-16 * The driver information block is encoded and either allocated * or written to disk. *------------------------------------------------------------------------- */ static herr_t H5F_flush(H5F_t *f, H5F_scope_t scope, hbool_t invalidate, hbool_t alloc_only) { uint8_t sbuf[2048], dbuf[2048], *p=NULL; uintn nerrors=0, i; hsize_t superblock_size, driver_size; char driver_name[9]; FUNC_ENTER(H5F_flush, FAIL); /* * Nothing to do if the file is read only. This determination is made at * the shared open(2) flags level, implying that opening a file twice, * once for read-only and once for read-write, and then calling * H5F_flush() with the read-only handle, still causes data to be flushed. */ if (0 == (H5F_ACC_RDWR & f->shared->flags)) { HRETURN(SUCCEED); } /* Flush other stuff depending on scope */ if (H5F_SCOPE_GLOBAL==scope) { while (f->mtab.parent) f = f->mtab.parent; scope = H5F_SCOPE_DOWN; } if (H5F_SCOPE_DOWN==scope) { for (i=0; imtab.nmounts; i++) { if (H5F_flush(f->mtab.child[i].file, scope, invalidate, FALSE)<0) { nerrors++; } } } /* flush the data sieve buffer, if we have a dirty one */ if(!alloc_only && f->shared->sieve_buf && f->shared->sieve_dirty) { /* Write dirty data sieve buffer to file */ if (H5F_block_write(f, H5FD_MEM_DRAW, f->shared->sieve_loc, f->shared->sieve_size, H5P_DEFAULT, f->shared->sieve_buf)<0) { HRETURN_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "block write failed"); } /* Reset sieve buffer dirty flag */ f->shared->sieve_dirty=0; } /* end if */ /* flush the entire raw data cache */ if (!alloc_only && H5F_istore_flush (f, invalidate)<0) { HRETURN_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush raw data cache"); } /* flush (and invalidate) the entire meta data cache */ if (!alloc_only && H5AC_flush(f, NULL, HADDR_UNDEF, invalidate)<0) { HRETURN_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush meta data cache"); } /* encode the file boot block */ p = sbuf; HDmemcpy(p, H5F_SIGNATURE, H5F_SIGNATURE_LEN); p += H5F_SIGNATURE_LEN; *p++ = f->shared->fcpl->bootblock_ver; *p++ = f->shared->fcpl->freespace_ver; *p++ = f->shared->fcpl->objectdir_ver; *p++ = 0; /*reserved*/ *p++ = f->shared->fcpl->sharedheader_ver; assert (H5F_SIZEOF_ADDR(f)<=255); *p++ = (uint8_t)H5F_SIZEOF_ADDR(f); assert (H5F_SIZEOF_SIZE(f)<=255); *p++ = (uint8_t)H5F_SIZEOF_SIZE(f); *p++ = 0; /*reserved */ UINT16ENCODE(p, f->shared->fcpl->sym_leaf_k); UINT16ENCODE(p, f->shared->fcpl->btree_k[H5B_SNODE_ID]); UINT32ENCODE(p, f->shared->consist_flags); H5F_addr_encode(f, &p, f->shared->base_addr); H5F_addr_encode(f, &p, f->shared->freespace_addr); H5F_addr_encode(f, &p, H5FD_get_eoa(f->shared->lf)); H5F_addr_encode(f, &p, f->shared->driver_addr); H5G_ent_encode(f, &p, H5G_entof(f->shared->root_grp)); superblock_size = p-sbuf; /* * Encode the driver information block. */ if ((driver_size=H5FD_sb_size(f->shared->lf))) { driver_size += 16; /*driver block header */ assert(driver_size<=sizeof dbuf); p = dbuf; /* Version */ *p++ = HDF5_DRIVERINFO_VERSION; /* Reserved*/ p += 3; /* Driver info size, excluding header */ UINT32ENCODE(p, driver_size-16); /* Encode driver-specific data */ if (H5FD_sb_encode(f->shared->lf, driver_name, dbuf+16)<0) { HRETURN_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "unable to encode driver information"); } /* Driver name */ HDmemcpy(dbuf+8, driver_name, 8); } if (alloc_only) { /* * Allocate space for the userblock, superblock, and driver info * block. We do it with one allocation request because the userblock * and superblock need to be at the beginning of the file and only * the first allocation request is required to return memory at * format address zero. */ haddr_t addr = H5FD_alloc(f->shared->lf, H5FD_MEM_SUPER, (f->shared->base_addr + superblock_size + driver_size)); if (HADDR_UNDEF==addr) { HRETURN_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "unable to allocate file space for userblock " "and/or superblock"); } if (0!=addr) { HRETURN_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "file driver failed to allocate userblock " "and/or superblock at address zero"); } /* * The file driver information block begins immediately after the * superblock. */ if (driver_size>0) { f->shared->driver_addr = superblock_size; } } else { /* Write superblock */ #ifdef H5_HAVE_PARALLEL if (IS_H5FD_MPIO(f)) H5FD_mpio_tas_allsame(f->shared->lf, TRUE); /*only p0 will write*/ #endif if (H5FD_write(f->shared->lf, H5FD_MEM_SUPER, H5P_DEFAULT, f->shared->boot_addr, superblock_size, sbuf)<0) { HRETURN_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "unable to write superblock"); } /* Write driver information block */ if (HADDR_UNDEF!=f->shared->driver_addr) { #ifdef H5_HAVE_PARALLEL if (IS_H5FD_MPIO(f)) H5FD_mpio_tas_allsame(f->shared->lf, TRUE); /*only p0 will write*/ #endif if (H5FD_write(f->shared->lf, H5FD_MEM_SUPER, H5P_DEFAULT, f->shared->base_addr+superblock_size, driver_size, dbuf)<0) { HRETURN_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "unable to write driver information block"); } } } /* Flush file buffers to disk */ if (!alloc_only && H5FD_flush(f->shared->lf)<0) { HRETURN_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "low level flush failed"); } /* Check flush errors for children - errors are already on the stack */ if (nerrors) HRETURN(FAIL); FUNC_LEAVE(SUCCEED); } /*------------------------------------------------------------------------- * Function: H5F_close * * Purpose: Closes a file or causes the close operation to be pended. * This function is called two ways: from the API it gets called * by H5Fclose->H5I_dec_ref->H5F_close when H5I_dec_ref() * decrements the file ID reference count to zero. The file ID * is removed from the H5I_FILE group by H5I_dec_ref() just * before H5F_close() is called. If there are open object * headers then the close is pended by moving the file to the * H5I_FILE_CLOSING ID group (the f->closing contains the ID * assigned to file). * * This function is also called directly from H5O_close() when * the last object header is closed for the file and the file * has a pending close. * * Return: Non-negative on success/Negative on failure * * Programmer: Robb Matzke * Tuesday, September 23, 1997 * * Modifications: * Robb Matzke, 1998-10-14 * Nothing happens unless the H5F_t reference count is one (the * file is flushed anyway). The reference count is decremented * by H5F_dest(). * * Robb Matzke, 1999-08-02 * Modified to use the virtual file layer. *------------------------------------------------------------------------- */ herr_t H5F_close(H5F_t *f) { uintn i; FUNC_ENTER(H5F_close, FAIL); assert(f->nrefs>0); /* * If this file is referenced more than once then just decrement the * count, flush the file, and return. */ if (f->nrefs>1) { if (H5F_flush(f, H5F_SCOPE_LOCAL, FALSE, FALSE)<0) { HRETURN_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache"); } H5F_dest(f); /*decrement reference counts*/ HRETURN(SUCCEED); } /* * Unmount and close each child before closing the current file. */ assert(NULL==f->mtab.parent); for (i=0; imtab.nmounts; i++) { f->mtab.child[i].file->mtab.parent = NULL; H5G_close(f->mtab.child[i].group); H5F_close(f->mtab.child[i].file); } f->mtab.nmounts = 0; /* * If object headers are still open then delay deletion of resources until * they have all been closed. Flush all caches and update the object * header anyway so that failing to close all objects isn't a major * problem. If the file is on the H5I_FILE list then move it to the * H5I_FILE_CLOSING list instead. */ if (f->nopen_objs>0) { if (H5F_flush(f, H5F_SCOPE_LOCAL, FALSE, FALSE)<0) { HRETURN_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache"); } #ifdef H5F_DEBUG if (H5DEBUG(F)) { fprintf(H5DEBUG(F), "H5F: H5F_close(%s): %u object header%s still " "open (file close will complete when %s closed)\n", f->name, f->nopen_objs, 1 == f->nopen_objs?" is":"s are", 1 == f->nopen_objs?"that header is":"those headers are"); } #endif if (!f->closing) { f->closing = H5I_register(H5I_FILE_CLOSING, f); } HRETURN(SUCCEED); } else if (f->closing) { #ifdef H5F_DEBUG if (H5DEBUG(F)) { fprintf(H5DEBUG(F), "H5F: H5F_close: operation completing\n"); } #endif } /* * If this is the last reference to the shared part of the file then * close it also. */ assert(1==f->nrefs); if (1==f->shared->nrefs) { /* Flush and destroy all caches */ if (H5F_flush(f, H5F_SCOPE_LOCAL, TRUE, FALSE)<0) { HRETURN_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache"); } /* Dump debugging info */ H5AC_debug(f); H5F_istore_stats(f, FALSE); } else { /* * Flush all caches but do not destroy. As long as all handles for * this file are closed the flush isn't really necessary, but lets * just be safe. */ if (H5F_flush(f, H5F_SCOPE_LOCAL, TRUE, FALSE)<0) { HRETURN_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache"); } } /* * Destroy the H5F_t struct and decrement the reference count for the * shared H5F_file_t struct. If the reference count for the H5F_file_t * struct reaches zero then destroy it also. */ if (H5F_dest(f)<0) { HRETURN_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "problems closing file"); } FUNC_LEAVE(SUCCEED); } /*------------------------------------------------------------------------- * Function: H5Fclose * * Purpose: This function closes the file specified by FILE_ID by * flushing all data to storage, and terminating access to the * file through FILE_ID. If objects (e.g., datasets, groups, * etc.) are open in the file then the underlying storage is not * closed until those objects are closed; however, all data for * the file and the open objects is flushed. * * Return: Success: Non-negative * * Failure: Negative * * Programmer: Robb Matzke * Saturday, February 20, 1999 * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5Fclose(hid_t file_id) { herr_t ret_value = SUCCEED; FUNC_ENTER(H5Fclose, FAIL); H5TRACE1("e","i",file_id); /* Check/fix arguments. */ if (H5I_FILE != H5I_get_type(file_id)) { HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file atom"); } if (NULL == H5I_object(file_id)) { HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "unable to unatomize file"); } /* * Decrement reference count on atom. When it reaches zero the file will * be closed. */ if (H5I_dec_ref (file_id)<0) { HGOTO_ERROR (H5E_ATOM, H5E_CANTINIT, FAIL, "problems closing file"); } done: FUNC_LEAVE(ret_value < 0 ? FAIL : SUCCEED); } /*------------------------------------------------------------------------- * Function: H5F_mount * * Purpose: Mount file CHILD onto the group specified by LOC and NAME, * using mount properties in PLIST. CHILD must not already be * mouted and must not be a mount ancestor of the mount-point. * * Return: Non-negative on success/Negative on failure * * Programmer: Robb Matzke * Tuesday, October 6, 1998 * * Modifications: * * Robb Matzke, 1998-10-14 * The reference count for the mounted H5F_t is incremented. * *------------------------------------------------------------------------- */ static herr_t H5F_mount(H5G_entry_t *loc, const char *name, H5F_t *child, const H5F_mprop_t UNUSED *plist) { H5G_t *mount_point = NULL; /*mount point group */ H5G_entry_t *mp_ent = NULL; /*mount point symbol table entry*/ H5F_t *ancestor = NULL; /*ancestor files */ H5F_t *parent = NULL; /*file containing mount point */ intn lt, rt, md, cmp; /*binary search indices */ H5G_entry_t *ent = NULL; /*temporary symbol table entry */ herr_t ret_value = FAIL; /*return value */ FUNC_ENTER(H5F_mount, FAIL); assert(loc); assert(name && *name); assert(child); assert(plist); /* * Check that the child isn't mounted, that the mount point exists, and * that the mount wouldn't introduce a cycle in the mount tree. */ if (child->mtab.parent) { HGOTO_ERROR(H5E_FILE, H5E_MOUNT, FAIL, "file is already mounted"); } if (NULL==(mount_point=H5G_open(loc, name))) { HGOTO_ERROR(H5E_FILE, H5E_MOUNT, FAIL, "mount point not found"); } parent = H5G_fileof(mount_point); mp_ent = H5G_entof(mount_point); for (ancestor=parent; ancestor; ancestor=ancestor->mtab.parent) { if (ancestor==child) { HGOTO_ERROR(H5E_FILE, H5E_MOUNT, FAIL, "mount would introduce a cycle"); } } /* * Use a binary search to locate the position that the child should be * inserted into the parent mount table. At the end of this paragraph * `md' will be the index where the child should be inserted. */ lt = md = 0; rt = parent->mtab.nmounts; cmp = -1; while (ltmtab.child[md].group); cmp = H5F_addr_cmp(mp_ent->header, ent->header); if (cmp<0) { rt = md; } else if (cmp>0) { lt = md+1; } } if (cmp>0) md++; if (!cmp) { HGOTO_ERROR(H5E_FILE, H5E_MOUNT, FAIL, "mount point is already in use"); } /* Make room in the table */ if (parent->mtab.nmounts>=parent->mtab.nalloc) { uintn n = MAX(16, 2*parent->mtab.nalloc); H5F_mount_t *x = H5MM_realloc(parent->mtab.child, n*sizeof(parent->mtab.child[0])); if (!x) { HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for mount table"); } parent->mtab.child = x; parent->mtab.nalloc = n; } /* Insert into table */ HDmemmove(parent->mtab.child+md+1, parent->mtab.child+md, (parent->mtab.nmounts-md)*sizeof(parent->mtab.child[0])); parent->mtab.nmounts++; parent->mtab.child[md].group = mount_point; parent->mtab.child[md].file = child; child->mtab.parent = parent; child->nrefs++; ret_value = SUCCEED; done: if (ret_value<0 && mount_point) { H5G_close(mount_point); } FUNC_LEAVE(ret_value); } /*------------------------------------------------------------------------- * Function: H5F_unmount * * Purpose: Unmount the child which is mounted at the group specified by * LOC and NAME or fail if nothing is mounted there. Neither * file is closed. * * Because the mount point is specified by name and opened as a * group, the H5G_namei() will resolve it to the root of the * mounted file, not the group where the file is mounted. * * Return: Non-negative on success/Negative on failure * * Programmer: Robb Matzke * Tuesday, October 6, 1998 * * Modifications: * * Robb Matzke, 1998-10-14 * The ref count for the child is decremented by calling H5F_close(). * *------------------------------------------------------------------------- */ static herr_t H5F_unmount(H5G_entry_t *loc, const char *name) { H5G_t *mounted = NULL; /*mount point group */ H5G_entry_t *mnt_ent = NULL; /*mounted symbol table entry */ H5F_t *child = NULL; /*mounted file */ H5F_t *parent = NULL; /*file where mounted */ H5G_entry_t *ent = NULL; /*temporary symbol table entry */ herr_t ret_value = FAIL; /*return value */ uintn i; /*coutners */ intn lt, rt, md=(-1), cmp; /*binary search indices */ FUNC_ENTER(H5F_unmount, FAIL); assert(loc); assert(name && *name); /* * Get the mount point, or more precisely the root of the mounted file. * If we get the root group and the file has a parent in the mount tree, * then we must have found the mount point. */ if (NULL==(mounted=H5G_open(loc, name))) { HGOTO_ERROR(H5E_FILE, H5E_MOUNT, FAIL, "mount point not found"); } child = H5G_fileof(mounted); mnt_ent = H5G_entof(mounted); ent = H5G_entof(child->shared->root_grp); if (child->mtab.parent && H5F_addr_eq(mnt_ent->header, ent->header)) { /* * We've been given the root group of the child. We do a reverse * lookup in the parent's mount table to find the correct entry. */ parent = child->mtab.parent; for (i=0; imtab.nmounts; i++) { if (parent->mtab.child[i].file==child) { /* Unmount the child */ parent->mtab.nmounts -= 1; H5G_close(parent->mtab.child[i].group); child->mtab.parent = NULL; H5F_close(child); HDmemmove(parent->mtab.child+i, parent->mtab.child+i+1, ((parent->mtab.nmounts-i)* sizeof(parent->mtab.child[0]))); ret_value = SUCCEED; } } assert(ret_value>=0); } else { /* * We've been given the mount point in the parent. We use a binary * search in the parent to locate the mounted file, if any. */ parent = child; /*we guessed wrong*/ lt = 0; rt = parent->mtab.nmounts; cmp = -1; while (ltmtab.child[md].group); cmp = H5F_addr_cmp(mnt_ent->header, ent->header); if (cmp<0) { rt = md; } else { lt = md+1; } } if (cmp) { HGOTO_ERROR(H5E_FILE, H5E_MOUNT, FAIL, "not a mount point"); } /* Unmount the child */ parent->mtab.nmounts -= 1; H5G_close(parent->mtab.child[md].group); parent->mtab.child[md].file->mtab.parent = NULL; H5F_close(parent->mtab.child[md].file); HDmemmove(parent->mtab.child+md, parent->mtab.child+md+1, (parent->mtab.nmounts-md)*sizeof(parent->mtab.child[0])); ret_value = SUCCEED; } done: if (mounted) H5G_close(mounted); FUNC_LEAVE(ret_value); } /*------------------------------------------------------------------------- * Function: H5F_mountpoint * * Purpose: If ENT is a mount point then copy the entry for the root * group of the mounted file into ENT. * * Return: Non-negative on success/Negative on failure * * Programmer: Robb Matzke * Tuesday, October 6, 1998 * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5F_mountpoint(H5G_entry_t *find/*in,out*/) { H5F_t *parent = find->file; intn lt, rt, md=(-1), cmp; H5G_entry_t *ent = NULL; FUNC_ENTER(H5F_mountpoint, FAIL); assert(find); /* * The loop is necessary because we might have file1 mounted at the root * of file2, which is mounted somewhere in file3. */ do { /* * Use a binary search to find the potential mount point in the mount * table for the parent */ lt = 0; rt = parent->mtab.nmounts; cmp = -1; while (ltmtab.child[md].group); cmp = H5F_addr_cmp(find->header, ent->header); if (cmp<0) { rt = md; } else { lt = md+1; } } /* Copy root info over to ENT */ if (0==cmp) { ent = H5G_entof(parent->mtab.child[md].file->shared->root_grp); *find = *ent; parent = ent->file; } } while (!cmp); FUNC_LEAVE(SUCCEED); } /*------------------------------------------------------------------------- * Function: H5Fmount * * Purpose: Mount file CHILD_ID onto the group specified by LOC_ID and * NAME using mount properties PLIST_ID. * * Return: Non-negative on success/Negative on failure * * Programmer: Robb Matzke * Tuesday, October 6, 1998 * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5Fmount(hid_t loc_id, const char *name, hid_t child_id, hid_t plist_id) { H5G_entry_t *loc = NULL; const H5F_mprop_t *plist = NULL; H5F_t *child = NULL; FUNC_ENTER(H5Fmount, FAIL); H5TRACE4("e","isii",loc_id,name,child_id,plist_id); /* Check arguments */ if (NULL==(loc=H5G_loc(loc_id))) { HRETURN_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location"); } if (!name || !*name) { HRETURN_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no name"); } if (H5I_FILE!=H5I_get_type(child_id) || NULL==(child=H5I_object(child_id))) { HRETURN_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file"); } if (H5P_DEFAULT==plist_id) { plist = &H5F_mount_dflt; } else if (H5P_MOUNT!=H5P_get_class(plist_id) || NULL==(plist=H5I_object(plist_id))) { HRETURN_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a mount property list"); } /* Do the mount */ if (H5F_mount(loc, name, child, plist)<0) { HRETURN_ERROR(H5E_FILE, H5E_MOUNT, FAIL, "unable to mount file"); } FUNC_LEAVE(SUCCEED); } /*------------------------------------------------------------------------- * Function: H5Funmount * * Purpose: Given a mount point, dissassociate the mount point's file * from the file mounted there. Do not close either file. * * The mount point can either be the group in the parent or the * root group of the mounted file (both groups have the same * name). If the mount point was opened before the mount then * it's the group in the parent, but if it was opened after the * mount then it's the root group of the child. * * Return: Non-negative on success/Negative on failure * * Programmer: Robb Matzke * Tuesday, October 6, 1998 * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5Funmount(hid_t loc_id, const char *name) { H5G_entry_t *loc = NULL; FUNC_ENTER(H5Funmount, FAIL); H5TRACE2("e","is",loc_id,name); /* Check args */ if (NULL==(loc=H5G_loc(loc_id))) { HRETURN_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location"); } if (!name || !*name) { HRETURN_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no name"); } /* Unmount */ if (H5F_unmount(loc, name)<0) { HRETURN_ERROR(H5E_FILE, H5E_MOUNT, FAIL, "unable to unmount file"); } FUNC_LEAVE(SUCCEED); } /*------------------------------------------------------------------------- * Function: H5Freopen * * Purpose: Reopen a file. The new file handle which is returned points * to the same file as the specified file handle. Both handles * share caches and other information. The only difference * between the handles is that the new handle is not mounted * anywhere and no files are mounted on it. * * Return: Success: New file ID * * Failure: FAIL * * Programmer: Robb Matzke * Friday, October 16, 1998 * * Modifications: * *------------------------------------------------------------------------- */ hid_t H5Freopen(hid_t file_id) { H5F_t *old_file=NULL; H5F_t *new_file=NULL; hid_t ret_value = -1; FUNC_ENTER(H5Freopen, FAIL); H5TRACE1("i","i",file_id); if (H5I_FILE!=H5I_get_type(file_id) || NULL==(old_file=H5I_object(file_id))) { HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file"); } if (NULL==(new_file=H5F_new(old_file->shared, H5P_DEFAULT, H5P_DEFAULT))) { HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "unable to reopen file"); } if ((ret_value=H5I_register(H5I_FILE, new_file))<0) { HGOTO_ERROR(H5E_ATOM, H5E_CANTREGISTER, FAIL, "unable to atomize file handle"); } done: if (ret_value<0 && new_file) { H5F_close(new_file); } FUNC_LEAVE(ret_value); } /*------------------------------------------------------------------------- * Function: H5F_get_intent * * Purpose: Quick and dirty routine to retrieve the file's 'intent' flags * (Mainly added to stop non-file routines from poking about in the * H5F_t data structure) * * Return: 'intent' on success/abort on failure (shouldn't fail) * * Programmer: Quincey Koziol * September 29, 2000 * * Modifications: * *------------------------------------------------------------------------- */ uintn H5F_get_intent(H5F_t *f) { FUNC_ENTER(H5F_get_intent, 0); assert(f); FUNC_LEAVE(f->intent); } /*------------------------------------------------------------------------- * Function: H5F_sizeof_addr * * Purpose: Quick and dirty routine to retrieve the size of the file's size_t * (Mainly added to stop non-file routines from poking about in the * H5F_t data structure) * * Return: 'sizeof_addr' on success/abort on failure (shouldn't fail) * * Programmer: Quincey Koziol * September 29, 2000 * * Modifications: * *------------------------------------------------------------------------- */ size_t H5F_sizeof_addr(H5F_t *f) { FUNC_ENTER(H5F_sizeof_addr, 0); assert(f); FUNC_LEAVE((f)->shared->fcpl->sizeof_addr) } /*------------------------------------------------------------------------- * Function: H5F_sizeof_size * * Purpose: Quick and dirty routine to retrieve the size of the file's off_t * (Mainly added to stop non-file routines from poking about in the * H5F_t data structure) * * Return: 'sizeof_size' on success/abort on failure (shouldn't fail) * * Programmer: Quincey Koziol * September 29, 2000 * * Modifications: * *------------------------------------------------------------------------- */ size_t H5F_sizeof_size(H5F_t *f) { FUNC_ENTER(H5F_sizeof_size, 0); assert(f); FUNC_LEAVE((f)->shared->fcpl->sizeof_addr) } /*------------------------------------------------------------------------- * Function: H5F_get_intent * * Purpose: Quick and dirty routine to retrieve the file's 'driver_id' value * (Mainly added to stop non-file routines from poking about in the * H5F_t data structure) * * Return: 'driver_id' on success/abort on failure (shouldn't fail) * * Programmer: Quincey Koziol * October 10, 2000 * * Modifications: * *------------------------------------------------------------------------- */ hid_t H5F_get_driver_id(H5F_t *f) { FUNC_ENTER(H5F_get_driver_id, 0); assert(f); FUNC_LEAVE(f->shared->lf->driver_id); } /*------------------------------------------------------------------------- * Function: H5F_block_read * * Purpose: Reads some data from a file/server/etc into a buffer. * The data is contiguous. The address is relative to the base * address for the file. * * Errors: * IO READERROR Low-level read failed. * * Return: Non-negative on success/Negative on failure * * Programmer: Robb Matzke * matzke@llnl.gov * Jul 10 1997 * * Modifications: * Albert Cheng, 1998-06-02 * Added XFER_MODE argument * * Robb Matzke, 1999-07-28 * The ADDR argument is passed by value. * * Robb Matzke, 1999-08-02 * Modified to use the virtual file layer. The data transfer * property list is passed in by object ID since that's how the * virtual file layer needs it. *------------------------------------------------------------------------- */ herr_t H5F_block_read(H5F_t *f, H5FD_mem_t type, haddr_t addr, hsize_t size, hid_t dxpl_id, void *buf/*out*/) { haddr_t abs_addr; FUNC_ENTER(H5F_block_read, FAIL); assert(sizeshared->base_addr + addr; /* Read the data */ if (H5FD_read(f->shared->lf, type, dxpl_id, abs_addr, size, buf)<0) { HRETURN_ERROR(H5E_IO, H5E_READERROR, FAIL, "file read failed"); } FUNC_LEAVE(SUCCEED); } /*------------------------------------------------------------------------- * Function: H5F_block_write * * Purpose: Writes some data from memory to a file/server/etc. The * data is contiguous. The address is relative to the base * address. * * Errors: * IO WRITEERROR Low-level write failed. * IO WRITEERROR No write intent. * * Return: Non-negative on success/Negative on failure * * Programmer: Robb Matzke * matzke@llnl.gov * Jul 10 1997 * * Modifications: * Albert Cheng, 1998-06-02 * Added XFER_MODE argument * * Robb Matzke, 1999-07-28 * The ADDR argument is passed by value. * * Robb Matzke, 1999-08-02 * Modified to use the virtual file layer. The data transfer * property list is passed in by object ID since that's how the * virtual file layer needs it. *------------------------------------------------------------------------- */ herr_t H5F_block_write(H5F_t *f, H5FD_mem_t type, haddr_t addr, hsize_t size, hid_t dxpl_id, const void *buf) { haddr_t abs_addr; FUNC_ENTER(H5F_block_write, FAIL); assert (sizeintent & H5F_ACC_RDWR)) { HRETURN_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "no write intent"); } /* Convert the relative address to an absolute address */ abs_addr = f->shared->base_addr + addr; /* Write the data */ if (H5FD_write(f->shared->lf, type, dxpl_id, abs_addr, size, buf)) { HRETURN_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "file write failed"); } FUNC_LEAVE(SUCCEED); } /*------------------------------------------------------------------------- * Function: H5F_addr_encode * * Purpose: Encodes an address into the buffer pointed to by *PP and * then increments the pointer to the first byte after the * address. An undefined value is stored as all 1's. * * Return: void * * Programmer: Robb Matzke * Friday, November 7, 1997 * * Modifications: * Robb Matzke, 1999-07-28 * The ADDR argument is passed by value. *------------------------------------------------------------------------- */ void H5F_addr_encode(H5F_t *f, uint8_t **pp/*in,out*/, haddr_t addr) { uintn i; haddr_t tmp; assert(f); assert(pp && *pp); if (H5F_addr_defined(addr)) { tmp = addr; for (i=0; i>= 8; } assert("overflow" && 0 == tmp); } else { for (i=0; ioffset and how it is being called. *------------------------------------------------------------------------- */ herr_t H5F_addr_pack(H5F_t UNUSED *f, haddr_t *addr_p/*out*/, const unsigned long objno[2]) { assert(f); assert(objno); assert(addr_p); *addr_p = objno[0]; #if SIZEOF_LONG= 0); assert(fwidth >= 0); /* debug */ HDfprintf(stream, "%*sFile Boot Block...\n", indent, ""); HDfprintf(stream, "%*s%-*s %s\n", indent, "", fwidth, "File name:", f->name); HDfprintf(stream, "%*s%-*s 0x%08x\n", indent, "", fwidth, "Flags", (unsigned) (f->shared->flags)); HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "Reference count:", (unsigned) (f->shared->nrefs)); HDfprintf(stream, "%*s%-*s 0x%08lx\n", indent, "", fwidth, "Consistency flags:", (unsigned long) (f->shared->consist_flags)); HDfprintf(stream, "%*s%-*s %a (abs)\n", indent, "", fwidth, "Address of boot block:", f->shared->boot_addr); HDfprintf(stream, "%*s%-*s %a (abs)\n", indent, "", fwidth, "Base address:", f->shared->base_addr); HDfprintf(stream, "%*s%-*s %a (rel)\n", indent, "", fwidth, "Free list address:", f->shared->freespace_addr); HDfprintf(stream, "%*s%-*s %a (rel)\n", indent, "", fwidth, "Driver information block:", f->shared->driver_addr); HDfprintf(stream, "%*s%-*s %lu bytes\n", indent, "", fwidth, "Size of user block:", (unsigned long) (f->shared->fcpl->userblock_size)); HDfprintf(stream, "%*s%-*s %u bytes\n", indent, "", fwidth, "Size of file size_t type:", (unsigned) (f->shared->fcpl->sizeof_size)); HDfprintf(stream, "%*s%-*s %u bytes\n", indent, "", fwidth, "Size of file haddr_t type:", (unsigned) (f->shared->fcpl->sizeof_addr)); HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "Symbol table leaf node 1/2 rank:", (unsigned) (f->shared->fcpl->sym_leaf_k)); HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "Symbol table internal node 1/2 rank:", (unsigned) (f->shared->fcpl->btree_k[H5B_SNODE_ID])); HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "Boot block version number:", (unsigned) (f->shared->fcpl->bootblock_ver)); HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "Free list version number:", (unsigned) (f->shared->fcpl->freespace_ver)); HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "Object directory version number:", (unsigned) (f->shared->fcpl->objectdir_ver)); HDfprintf(stream, "%*s%-*s %u\n", indent, "", fwidth, "Shared header version number:", (unsigned) (f->shared->fcpl->sharedheader_ver)); HDfprintf(stream, "%*s%-*s %s\n", indent, "", fwidth, "Root group symbol table entry:", f->shared->root_grp ? "" : "(none)"); if (f->shared->root_grp) { H5G_ent_debug(f, H5G_entof(f->shared->root_grp), stream, indent+3, MAX(0, fwidth-3), HADDR_UNDEF); } FUNC_LEAVE(SUCCEED); }