Coverage Report

Created: 2025-12-31 08:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/netcdf-c-4.7.4/libhdf5/hdf5open.c
Line
Count
Source
1
/* Copyright 2003-2018, University Corporation for Atmospheric
2
 * Research. See COPYRIGHT file for copying and redistribution
3
 * conditions. */
4
/**
5
 * @file
6
 * @internal This file contains functions that are used in file
7
 * opens.
8
 *
9
 * @author Ed Hartnett
10
 */
11
12
#include "config.h"
13
#include "hdf5internal.h"
14
#include "ncrc.h"
15
#include "ncmodel.h"
16
17
#ifdef ENABLE_BYTERANGE
18
#include "H5FDhttp.h"
19
#endif
20
21
/*Nemonic */
22
0
#define FILTERACTIVE 1
23
24
77.5k
#define NUM_TYPES 12 /**< Number of netCDF atomic types. */
25
67.7k
#define CD_NELEMS_ZLIB 1 /**< Number of parameters needed for ZLIB filter. */
26
27
/** @internal Native HDF5 constants for atomic types. For performance,
28
 * fill this array only the first time, and keep it in global memory
29
 * for each further use. */
30
static hid_t h5_native_type_constant_g[NUM_TYPES];
31
32
/** @internal NetCDF atomic type names. */
33
static const char nc_type_name_g[NUM_TYPES][NC_MAX_NAME + 1] = {"char", "byte", "short",
34
                                                                "int", "float", "double", "ubyte",
35
                                                                "ushort", "uint", "int64",
36
                                                                "uint64", "string"};
37
38
/** @internal NetCDF atomic types. */
39
static const nc_type nc_type_constant_g[NUM_TYPES] = {NC_CHAR, NC_BYTE, NC_SHORT,
40
                                                      NC_INT, NC_FLOAT, NC_DOUBLE, NC_UBYTE,
41
                                                      NC_USHORT, NC_UINT, NC_INT64,
42
                                                      NC_UINT64, NC_STRING};
43
44
/** @internal NetCDF atomic type sizes. */
45
static const int nc_type_size_g[NUM_TYPES] = {sizeof(char), sizeof(char), sizeof(short),
46
                                              sizeof(int), sizeof(float), sizeof(double), sizeof(unsigned char),
47
                                              sizeof(unsigned short), sizeof(unsigned int), sizeof(long long),
48
                                              sizeof(unsigned long long), sizeof(char *)};
49
50
/** @internal These flags may not be set for open mode. */
51
static const int ILLEGAL_OPEN_FLAGS = (NC_MMAP);
52
53
/* From libsrc4, these are the netcdf-4 cache sizes. */
54
extern size_t nc4_chunk_cache_size;
55
extern size_t nc4_chunk_cache_nelems;
56
extern float nc4_chunk_cache_preemption;
57
58
/* From nc4mem.c */
59
extern int NC4_open_image_file(NC_FILE_INFO_T* h5);
60
61
/* Defined later in this file. */
62
static int rec_read_metadata(NC_GRP_INFO_T *grp);
63
64
/**
65
 * @internal Struct to track HDF5 object info, for
66
 * rec_read_metadata(). We get this info for every object in the
67
 * HDF5 file when we H5Literate() over the file. */
68
typedef struct hdf5_obj_info
69
{
70
    hid_t oid;                          /* HDF5 object ID */
71
    char oname[NC_MAX_NAME + 1];        /* Name of object */
72
    H5G_stat_t statbuf;                 /* Information about the object */
73
    struct hdf5_obj_info *next; /* Pointer to next node in list */
74
} hdf5_obj_info_t;
75
76
/**
77
 * @internal User data struct for call to H5Literate() in
78
 * rec_read_metadata(). When iterating through the objects in a
79
 * group, if we find child groups, we save their hdf5_obj_info_t
80
 * object in a list. Then we processes them after completely
81
 * processing the parent group. */
82
typedef struct user_data
83
{
84
    NClist *grps; /* NClist<hdf5_obj_info_t*> */
85
    NC_GRP_INFO_T *grp; /* Pointer to parent group */
86
} user_data_t;
87
88
/* Custom iteration callback data */
89
typedef struct {
90
    NC_GRP_INFO_T *grp;
91
    NC_VAR_INFO_T *var;
92
} att_iter_info;
93
94
/**
95
 * @internal Given an HDF5 type, set a pointer to netcdf type_info
96
 * struct, either an existing one (for user-defined types) or a newly
97
 * created one.
98
 *
99
 * @param h5 Pointer to HDF5 file info struct.
100
 * @param datasetid HDF5 dataset ID.
101
 * @param type_info Pointer to pointer that gets type info struct.
102
 *
103
 * @return ::NC_NOERR No error.
104
 * @return ::NC_EBADID Bad ncid.
105
 * @return ::NC_EHDFERR HDF5 returned error.
106
 * @return ::NC_EBADTYPID Type not found.
107
 * @author Ed Hartnett
108
 */
109
static int
110
get_type_info2(NC_FILE_INFO_T *h5, hid_t datasetid, NC_TYPE_INFO_T **type_info)
111
67.7k
{
112
67.7k
    NC_HDF5_TYPE_INFO_T *hdf5_type;
113
67.7k
    htri_t is_str, equal = 0;
114
67.7k
    H5T_class_t class;
115
67.7k
    hid_t native_typeid, hdf_typeid;
116
67.7k
    H5T_order_t order;
117
67.7k
    int t;
118
119
67.7k
    assert(h5 && type_info);
120
121
    /* Because these N5T_NATIVE_* constants are actually function calls
122
     * (!) in H5Tpublic.h, I can't initialize this array in the usual
123
     * way, because at least some C compilers (like Irix) complain
124
     * about calling functions when defining constants. So I have to do
125
     * it like this. Note that there's no native types for char or
126
     * string. Those are handled later. */
127
67.7k
    if (!h5_native_type_constant_g[1])
128
9
    {
129
9
        h5_native_type_constant_g[1] = H5T_NATIVE_SCHAR;
130
9
        h5_native_type_constant_g[2] = H5T_NATIVE_SHORT;
131
9
        h5_native_type_constant_g[3] = H5T_NATIVE_INT;
132
9
        h5_native_type_constant_g[4] = H5T_NATIVE_FLOAT;
133
9
        h5_native_type_constant_g[5] = H5T_NATIVE_DOUBLE;
134
9
        h5_native_type_constant_g[6] = H5T_NATIVE_UCHAR;
135
9
        h5_native_type_constant_g[7] = H5T_NATIVE_USHORT;
136
9
        h5_native_type_constant_g[8] = H5T_NATIVE_UINT;
137
9
        h5_native_type_constant_g[9] = H5T_NATIVE_LLONG;
138
9
        h5_native_type_constant_g[10] = H5T_NATIVE_ULLONG;
139
9
    }
140
141
    /* Get the HDF5 typeid - we'll need it later. */
142
67.7k
    if ((hdf_typeid = H5Dget_type(datasetid)) < 0)
143
0
        return NC_EHDFERR;
144
145
    /* Get the native typeid. Will be equivalent to hdf_typeid when
146
     * creating but not necessarily when reading, a variable. */
147
67.7k
    if ((native_typeid = H5Tget_native_type(hdf_typeid, H5T_DIR_DEFAULT)) < 0)
148
0
        return NC_EHDFERR;
149
150
    /* Is this type an integer, string, compound, or what? */
151
67.7k
    if ((class = H5Tget_class(native_typeid)) < 0)
152
0
        return NC_EHDFERR;
153
154
    /* Is this an atomic type? */
155
67.7k
    if (class == H5T_STRING || class == H5T_INTEGER || class == H5T_FLOAT)
156
67.7k
    {
157
        /* Allocate a phony NC_TYPE_INFO_T struct to hold type info. */
158
67.7k
        if (!(*type_info = calloc(1, sizeof(NC_TYPE_INFO_T))))
159
0
            return NC_ENOMEM;
160
161
        /* Allocate storage for HDF5-specific type info. */
162
67.7k
        if (!(hdf5_type = calloc(1, sizeof(NC_HDF5_TYPE_INFO_T))))
163
0
            return NC_ENOMEM;
164
67.7k
        (*type_info)->format_type_info = hdf5_type;
165
166
        /* H5Tequal doesn't work with H5T_C_S1 for some reason. But
167
         * H5Tget_class will return H5T_STRING if this is a string. */
168
67.7k
        if (class == H5T_STRING)
169
6.26k
        {
170
6.26k
            if ((is_str = H5Tis_variable_str(native_typeid)) < 0)
171
0
                return NC_EHDFERR;
172
            /* Make sure fixed-len strings will work like variable-len
173
             * strings */
174
6.26k
            if (is_str || H5Tget_size(hdf_typeid) > 1)
175
0
            {
176
                /* Set a class for the type */
177
0
                t = NUM_TYPES - 1;
178
0
                (*type_info)->nc_type_class = NC_STRING;
179
0
            }
180
6.26k
            else
181
6.26k
            {
182
                /* Set a class for the type */
183
6.26k
                t = 0;
184
6.26k
                (*type_info)->nc_type_class = NC_CHAR;
185
6.26k
            }
186
6.26k
        }
187
61.5k
        else
188
61.5k
        {
189
77.5k
            for (t = 1; t < NUM_TYPES - 1; t++)
190
77.5k
            {
191
77.5k
                if ((equal = H5Tequal(native_typeid,
192
77.5k
                                      h5_native_type_constant_g[t])) < 0)
193
0
                    return NC_EHDFERR;
194
77.5k
                if (equal)
195
61.5k
                    break;
196
77.5k
            }
197
198
            /* Find out about endianness. As of HDF 1.8.6, this works
199
             * with all data types Not just H5T_INTEGER. See
200
             * https://www.hdfgroup.org/HDF5/doc/RM/RM_H5T.html#Datatype-GetOrder */
201
61.5k
            if ((order = H5Tget_order(hdf_typeid)) < 0)
202
0
                return NC_EHDFERR;
203
204
61.5k
            if (order == H5T_ORDER_LE)
205
61.5k
                (*type_info)->endianness = NC_ENDIAN_LITTLE;
206
0
            else if (order == H5T_ORDER_BE)
207
0
                (*type_info)->endianness = NC_ENDIAN_BIG;
208
0
            else
209
0
                return NC_EBADTYPE;
210
211
61.5k
            if (class == H5T_INTEGER)
212
59.5k
                (*type_info)->nc_type_class = NC_INT;
213
1.98k
            else
214
1.98k
                (*type_info)->nc_type_class = NC_FLOAT;
215
61.5k
        }
216
67.7k
        (*type_info)->hdr.id = nc_type_constant_g[t];
217
67.7k
        (*type_info)->size = nc_type_size_g[t];
218
67.7k
        if (!((*type_info)->hdr.name = strdup(nc_type_name_g[t])))
219
0
            return NC_ENOMEM;
220
67.7k
        hdf5_type->hdf_typeid = hdf_typeid;
221
67.7k
        hdf5_type->native_hdf_typeid = native_typeid;
222
67.7k
        return NC_NOERR;
223
67.7k
    }
224
0
    else
225
0
    {
226
0
        NC_TYPE_INFO_T *type;
227
228
        /* This is a user-defined type. */
229
0
        if((type = nc4_rec_find_hdf_type(h5, native_typeid)))
230
0
            *type_info = type;
231
232
        /* The type entry in the array of user-defined types already has
233
         * an open data typeid (and native typeid), so close the ones we
234
         * opened above. */
235
0
        if (H5Tclose(native_typeid) < 0)
236
0
            return NC_EHDFERR;
237
0
        if (H5Tclose(hdf_typeid) < 0)
238
0
            return NC_EHDFERR;
239
240
0
        if (type)
241
0
            return NC_NOERR;
242
0
    }
243
244
0
    return NC_EBADTYPID;
245
67.7k
}
246
247
/**
248
 * @internal This function reads the coordinates attribute used for
249
 * multi-dimensional coordinates. It then sets var->dimids[], and
250
 * attempts to find a pointer to the dims and sets var->dim[] as well.
251
 *
252
 * @param grp Group info pointer.
253
 * @param var Var info pointer.
254
 *
255
 * @return ::NC_NOERR No error.
256
 * @return ::NC_ENOTATT Attribute does not exist.
257
 * @return ::NC_EATTMETA Attribute metadata error.
258
 * @return ::NC_EHDFERR HDF5 error.
259
 * @author Ed Hartnett
260
 */
261
static int
262
read_coord_dimids(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var)
263
68.1k
{
264
68.1k
    NC_HDF5_VAR_INFO_T *hdf5_var;
265
68.1k
    hid_t coord_att_typeid = -1, coord_attid = -1, spaceid = -1;
266
68.1k
    hssize_t npoints;
267
68.1k
    htri_t attr_exists;
268
68.1k
    int d;
269
68.1k
    int retval = NC_NOERR;
270
271
68.1k
    assert(grp && var && var->format_var_info);
272
68.1k
    LOG((3, "%s: var->hdr.name %s", __func__, var->hdr.name));
273
274
    /* Have we already read the coordinates hidden att for this var? */
275
68.1k
    if (var->coords_read)
276
369
        return NC_NOERR;
277
278
    /* Get HDF5-sepecific var info. */
279
67.7k
    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
280
281
    /* Does the COORDINATES att exist? */
282
67.7k
    if ((attr_exists = H5Aexists(hdf5_var->hdf_datasetid, COORDINATES)) < 0)
283
0
        return NC_EHDFERR;
284
67.7k
    if (!attr_exists)
285
57.2k
        return NC_ENOTATT;
286
287
    /* There is a hidden attribute telling us the ids of the
288
     * dimensions that apply to this multi-dimensional coordinate
289
     * variable. Read it. */
290
10.5k
    if ((coord_attid = H5Aopen_name(hdf5_var->hdf_datasetid, COORDINATES)) < 0)
291
0
        BAIL(NC_EATTMETA);
292
293
10.5k
    if ((coord_att_typeid = H5Aget_type(coord_attid)) < 0)
294
0
        BAIL(NC_EATTMETA);
295
296
    /* How many dimensions are there? */
297
10.5k
    if ((spaceid = H5Aget_space(coord_attid)) < 0)
298
0
        BAIL(NC_EATTMETA);
299
10.5k
    if ((npoints = H5Sget_simple_extent_npoints(spaceid)) < 0)
300
0
        BAIL(NC_EATTMETA);
301
302
    /* Check that the number of points is the same as the number of
303
     * dimensions for the variable. */
304
10.5k
    if (npoints != var->ndims)
305
0
        BAIL(NC_EATTMETA);
306
307
    /* Read the dimids for this var. */
308
10.5k
    if (H5Aread(coord_attid, coord_att_typeid, var->dimids) < 0)
309
0
        BAIL(NC_EATTMETA);
310
10.5k
    LOG((4, "read dimids for this var"));
311
312
    /* Update var->dim field based on the var->dimids. Ok if does not
313
     * find a dim at this time, but if found set it. */
314
36.5k
    for (d = 0; d < var->ndims; d++)
315
26.0k
        nc4_find_dim(grp, var->dimids[d], &var->dim[d], NULL);
316
317
    /* Remember that we have read the coordinates hidden attribute. */
318
10.5k
    var->coords_read = NC_TRUE;
319
320
10.5k
exit:
321
10.5k
    if (spaceid >= 0 && H5Sclose(spaceid) < 0)
322
0
        BAIL2(NC_EHDFERR);
323
10.5k
    if (coord_att_typeid >= 0 && H5Tclose(coord_att_typeid) < 0)
324
0
        BAIL2(NC_EHDFERR);
325
10.5k
    if (coord_attid >= 0 && H5Aclose(coord_attid) < 0)
326
0
        BAIL2(NC_EHDFERR);
327
10.5k
    return retval;
328
10.5k
}
329
330
/**
331
 * @internal This function is called when reading a file's metadata
332
 * for each dimension scale attached to a variable.
333
 *
334
 * @param did HDF5 ID for dimscale.
335
 * @param dim
336
 * @param dsid
337
 * @param dimscale_hdf5_objids
338
 *
339
 * @return 0 for success, -1 for error.
340
 * @author Ed Hartnett
341
 */
342
static herr_t
343
dimscale_visitor(hid_t did, unsigned dim, hid_t dsid,
344
                 void *dimscale_hdf5_objids)
345
40.2k
{
346
40.2k
    H5G_stat_t statbuf;
347
348
40.2k
    LOG((4, "%s", __func__));
349
350
    /* Get more info on the dimscale object.*/
351
40.2k
    if (H5Gget_objinfo(dsid, ".", 1, &statbuf) < 0)
352
0
        return -1;
353
354
    /* Pass this information back to caller. */
355
40.2k
    (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno[0] = statbuf.fileno[0];
356
40.2k
    (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno[1] = statbuf.fileno[1];
357
40.2k
    (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno[0] = statbuf.objno[0];
358
40.2k
    (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno[1] = statbuf.objno[1];
359
40.2k
    return 0;
360
40.2k
}
361
362
/**
363
 * @internal For files without any netCDF-4 dimensions defined, create
364
 * phony dimension to match the available datasets. Each new dimension
365
 * of a new size gets a phony dimension. However, if a var has more
366
 * than one dimension defined, and they are the same size, they each
367
 * get their own phony dimension (starting in netcdf-c-4.7.3).
368
 *
369
 * @param grp Pointer to the group info.
370
 * @param hdf_datasetid HDF5 datsetid for the var's dataset.
371
 * @param var Pointer to the var info.
372
 *
373
 * @returns NC_NOERR No error.
374
 * @returns NC_EHDFERR HDF5 returned an error.
375
 * @returns NC_ENOMEM Out of memory.
376
 * @author Ed Hartnett
377
 */
378
static int
379
create_phony_dims(NC_GRP_INFO_T *grp, hid_t hdf_datasetid, NC_VAR_INFO_T *var)
380
40.8k
{
381
40.8k
    NC_DIM_INFO_T *dim;
382
40.8k
    hid_t spaceid = 0;
383
40.8k
    hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL;
384
40.8k
    int dataset_ndims;
385
40.8k
    int d;
386
40.8k
    int retval = NC_NOERR;
387
388
    /* Find the space information for this dimension. */
389
40.8k
    if ((spaceid = H5Dget_space(hdf_datasetid)) < 0)
390
0
        BAIL(NC_EHDFERR);
391
392
    /* Get the len of each dim in the space. */
393
40.8k
    if (var->ndims)
394
0
    {
395
        /* Allocate storage for dim lens and max lens for this var. */
396
0
        if (!(h5dimlen = malloc(var->ndims * sizeof(hsize_t))))
397
0
            return NC_ENOMEM;
398
0
        if (!(h5dimlenmax = malloc(var->ndims * sizeof(hsize_t))))
399
0
            BAIL(NC_ENOMEM);
400
401
        /* Get ndims, also len and mac len of all dims. */
402
0
        if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid, h5dimlen,
403
0
                                                       h5dimlenmax)) < 0)
404
0
            BAIL(NC_EHDFERR);
405
0
        assert(dataset_ndims == var->ndims);
406
0
    }
407
40.8k
    else
408
40.8k
    {
409
        /* Make sure it's scalar. */
410
40.8k
        assert(H5Sget_simple_extent_type(spaceid) == H5S_SCALAR);
411
40.8k
    }
412
413
    /* Create a phony dimension for each dimension in the dataset,
414
     * unless there already is one the correct size. */
415
40.8k
    for (d = 0; d < var->ndims; d++)
416
0
    {
417
0
        int k;
418
0
        int match = 0;
419
420
        /* Is there already a phony dimension of the correct size? */
421
0
        for (k = 0; k < ncindexsize(grp->dim); k++)
422
0
        {
423
0
            dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, k);
424
0
            assert(dim);
425
0
            if ((dim->len == h5dimlen[d]) &&
426
0
                ((h5dimlenmax[d] == H5S_UNLIMITED && dim->unlimited) ||
427
0
                 (h5dimlenmax[d] != H5S_UNLIMITED && !dim->unlimited)))
428
0
            {
429
0
                int k1;
430
431
                /* We found a match! */
432
0
                match++;
433
434
                /* If this phony dimension has already in use for this
435
                 * var, we should not use it again. */
436
0
                for (k1 = 0; k1 < d; k1++)
437
0
                    if (var->dimids[k1] == dim->hdr.id)
438
0
                        match = 0;
439
440
0
                if (match)
441
0
                    break;
442
0
            }
443
0
        }
444
445
        /* Didn't find a phony dim? Then create one. */
446
0
        if (!match)
447
0
        {
448
0
            char phony_dim_name[NC_MAX_NAME + 1];
449
0
            sprintf(phony_dim_name, "phony_dim_%d", grp->nc4_info->next_dimid);
450
0
            LOG((3, "%s: creating phony dim for var %s", __func__, var->hdr.name));
451
452
            /* Add phony dim to metadata list. */
453
0
            if ((retval = nc4_dim_list_add(grp, phony_dim_name, h5dimlen[d], -1, &dim)))
454
0
                BAIL(retval);
455
456
            /* Create struct for HDF5-specific dim info. */
457
0
            if (!(dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T))))
458
0
                BAIL(NC_ENOMEM);
459
0
            if (h5dimlenmax[d] == H5S_UNLIMITED)
460
0
                dim->unlimited = NC_TRUE;
461
0
        }
462
463
        /* The variable must remember the dimid. */
464
0
        var->dimids[d] = dim->hdr.id;
465
0
        var->dim[d] = dim;
466
0
    } /* next dim */
467
468
40.8k
exit:
469
    /* Free resources. */
470
40.8k
    if (spaceid > 0 && H5Sclose(spaceid) < 0)
471
0
        BAIL2(NC_EHDFERR);
472
40.8k
    if (h5dimlenmax)
473
0
        free(h5dimlenmax);
474
40.8k
    if (h5dimlen)
475
0
        free(h5dimlen);
476
477
40.8k
    return retval;
478
40.8k
}
479
480
/**
481
 * @internal Iterate through the vars in this file and make sure we've
482
 * got a dimid and a pointer to a dim for each dimension. This may
483
 * already have been done using the COORDINATES hidden attribute, in
484
 * which case this function will not have to do anything. This is
485
 * desirable because recurdively matching the dimscales (when
486
 * necessary) is very much the slowest part of opening a file.
487
 *
488
 * @param grp Pointer to group info struct.
489
 *
490
 * @returns NC_NOERR No error.
491
 * @returns NC_EHDFERR HDF5 returned an error.
492
 * @returns NC_ENOMEM Out of memory.
493
 * @author Ed Hartnett
494
 */
495
static int
496
rec_match_dimscales(NC_GRP_INFO_T *grp)
497
5.94k
{
498
5.94k
    NC_VAR_INFO_T *var;
499
5.94k
    NC_DIM_INFO_T *dim;
500
5.94k
    int retval = NC_NOERR;
501
5.94k
    int i;
502
503
5.94k
    assert(grp && grp->hdr.name);
504
5.94k
    LOG((4, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
505
506
    /* Perform var dimscale match for child groups. */
507
5.94k
    for (i = 0; i < ncindexsize(grp->children); i++)
508
0
        if ((retval = rec_match_dimscales((NC_GRP_INFO_T *)ncindexith(grp->children, i))))
509
0
            return retval;
510
511
    /* Check all the vars in this group. If they have dimscale info,
512
     * try and find a dimension for them. */
513
73.7k
    for (i = 0; i < ncindexsize(grp->vars); i++)
514
67.7k
    {
515
67.7k
        NC_HDF5_VAR_INFO_T *hdf5_var;
516
67.7k
        int d;
517
518
        /* Get pointer to var and to the HDF5-specific var info. */
519
67.7k
        var = (NC_VAR_INFO_T *)ncindexith(grp->vars, i);
520
67.7k
        assert(var && var->format_var_info);
521
67.7k
        hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
522
523
        /* Check all vars and see if dim[i] != NULL if dimids[i]
524
         * valid. Recall that dimids were initialized to -1. */
525
110k
        for (d = 0; d < var->ndims; d++)
526
42.4k
        {
527
42.4k
            if (!var->dim[d])
528
18.0k
                nc4_find_dim(grp, var->dimids[d], &var->dim[d], NULL);
529
42.4k
        }
530
531
        /* Skip dimension scale variables */
532
67.7k
        if (var->dimscale)
533
1.47k
            continue;
534
535
        /* If we have already read hidden coordinates att, then we don't
536
         * have to match dimscales for this var. */
537
66.3k
        if (var->coords_read)
538
10.1k
            continue;
539
540
        /* Skip dimension scale variables */
541
56.1k
        if (!var->dimscale)
542
56.1k
        {
543
56.1k
            int d;
544
56.1k
            int j;
545
546
            /* Are there dimscales for this variable? */
547
56.1k
            if (hdf5_var->dimscale_hdf5_objids)
548
15.3k
            {
549
30.6k
                for (d = 0; d < var->ndims; d++)
550
15.3k
                {
551
15.3k
                    NC_GRP_INFO_T *g;
552
15.3k
                    nc_bool_t finished = NC_FALSE;
553
15.3k
                    LOG((5, "%s: var %s has dimscale info...", __func__, var->hdr.name));
554
555
                    /* If we already have the dimension, we don't need to
556
                     * match the dimscales. This is better because matching
557
                     * the dimscales is slow. */
558
15.3k
                    if (var->dim[d])
559
0
                        continue;
560
561
                    /* Now we have to try to match dimscales. Check this
562
                     * and parent groups. */
563
30.6k
                    for (g = grp; g && !finished; g = g->parent)
564
15.3k
                    {
565
                        /* Check all dims in this group. */
566
124k
                        for (j = 0; j < ncindexsize(g->dim); j++)
567
124k
                        {
568
                            /* Get the HDF5 specific dim info. */
569
124k
                            NC_HDF5_DIM_INFO_T *hdf5_dim;
570
124k
                            dim = (NC_DIM_INFO_T *)ncindexith(g->dim, j);
571
124k
                            assert(dim && dim->format_dim_info);
572
124k
                            hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
573
574
                            /* Check for exact match of fileno/objid arrays
575
                             * to find identical objects in HDF5 file. */
576
124k
                            if (hdf5_var->dimscale_hdf5_objids[d].fileno[0] == hdf5_dim->hdf5_objid.fileno[0] &&
577
124k
                                hdf5_var->dimscale_hdf5_objids[d].objno[0] == hdf5_dim->hdf5_objid.objno[0] &&
578
15.3k
                                hdf5_var->dimscale_hdf5_objids[d].fileno[1] == hdf5_dim->hdf5_objid.fileno[1] &&
579
15.3k
                                hdf5_var->dimscale_hdf5_objids[d].objno[1] == hdf5_dim->hdf5_objid.objno[1])
580
15.3k
                            {
581
15.3k
                                LOG((4, "%s: for dimension %d, found dim %s", __func__,
582
15.3k
                                     d, dim->hdr.name));
583
15.3k
                                var->dimids[d] = dim->hdr.id;
584
15.3k
                                var->dim[d] = dim;
585
15.3k
                                finished = NC_TRUE;
586
15.3k
                                break;
587
15.3k
                            }
588
124k
                        } /* next dim */
589
15.3k
                    } /* next grp */
590
15.3k
                } /* next var->dim */
591
15.3k
            }
592
40.8k
            else
593
40.8k
            {
594
                /* No dimscales for this var! Invent phony dimensions. */
595
40.8k
                if ((retval = create_phony_dims(grp, hdf5_var->hdf_datasetid, var)))
596
0
                    return retval;
597
40.8k
            }
598
56.1k
        }
599
56.1k
    }
600
601
5.94k
    return retval;
602
5.94k
}
603
604
/**
605
 * @internal Check for the attribute that indicates that netcdf
606
 * classic model is in use.
607
 *
608
 * @param root_grp pointer to the group info for the root group of the
609
 * @param is_classic store 1 if this is a classic file.
610
 * file.
611
 *
612
 * @return NC_NOERR No error.
613
 * @author Ed Hartnett
614
 */
615
static int
616
check_for_classic_model(NC_GRP_INFO_T *root_grp, int *is_classic)
617
5.94k
{
618
5.94k
    htri_t attr_exists;
619
5.94k
    hid_t grpid;
620
621
    /* Check inputs. */
622
5.94k
    assert(root_grp && root_grp->format_grp_info && !root_grp->parent
623
5.94k
           && is_classic);
624
625
    /* Get the HDF5 group id. */
626
5.94k
    grpid = ((NC_HDF5_GRP_INFO_T *)(root_grp->format_grp_info))->hdf_grpid;
627
628
    /* If this attribute exists in the root group, then classic model
629
     * is in effect. */
630
5.94k
    if ((attr_exists = H5Aexists(grpid, NC3_STRICT_ATT_NAME)) < 0)
631
0
        return NC_EHDFERR;
632
5.94k
    *is_classic = attr_exists ? 1 : 0;
633
634
5.94k
    return NC_NOERR;
635
5.94k
}
636
637
/**
638
 * @internal Open a netcdf-4 file. Things have already been kicked off
639
 * in ncfunc.c in nc_open, but here the netCDF-4 part of opening a
640
 * file is handled.
641
 *
642
 * @param path The file name of the new file.
643
 * @param mode The open mode flag.
644
 * @param parameters File parameters.
645
 * @param ncid The ncid that has been assigned to this file.
646
 *
647
 * @return ::NC_NOERR No error.
648
 * @return ::NC_ENOMEM Out of memory.
649
 * @return ::NC_EINTERNAL Internal list error.
650
 * @return ::NC_EHDFERR HDF error.
651
 * @return ::NC_EMPI MPI error for parallel.
652
 * @return ::NC_EPARINIT Parallel I/O initialization error.
653
 * @return ::NC_EINMEMMORY Memory file error.
654
 * @author Ed Hartnett, Dennis Heimbigner
655
 */
656
static int
657
nc4_open_file(const char *path, int mode, void* parameters, int ncid)
658
5.94k
{
659
5.94k
    NC_FILE_INFO_T *nc4_info = NULL;
660
5.94k
    NC_HDF5_FILE_INFO_T *h5 = NULL;
661
5.94k
    NC *nc;
662
5.94k
    hid_t fapl_id = H5P_DEFAULT;
663
5.94k
    unsigned flags;
664
5.94k
    int is_classic;
665
#ifdef USE_PARALLEL4
666
    NC_MPI_INFO *mpiinfo = NULL;
667
    int comm_duped = 0; /* Whether the MPI Communicator was duplicated */
668
    int info_duped = 0; /* Whether the MPI Info object was duplicated */
669
#endif
670
5.94k
    int retval;
671
672
5.94k
    LOG((3, "%s: path %s mode %d", __func__, path, mode));
673
5.94k
    assert(path);
674
675
    /* Find pointer to NC. */
676
5.94k
    if ((retval = NC_check_id(ncid, &nc)))
677
0
        return retval;
678
5.94k
    assert(nc);
679
680
    /* Determine the HDF5 open flag to use. */
681
5.94k
    flags = (mode & NC_WRITE) ? H5F_ACC_RDWR : H5F_ACC_RDONLY;
682
683
    /* Add necessary structs to hold netcdf-4 file data. */
684
5.94k
    if ((retval = nc4_nc4f_list_add(nc, path, mode)))
685
0
        BAIL(retval);
686
5.94k
    nc4_info = (NC_FILE_INFO_T *)nc->dispatchdata;
687
5.94k
    assert(nc4_info && nc4_info->root_grp);
688
689
    /* Add struct to hold HDF5-specific file metadata. */
690
5.94k
    if (!(nc4_info->format_file_info = calloc(1, sizeof(NC_HDF5_FILE_INFO_T))))
691
0
        BAIL(NC_ENOMEM);
692
693
    /* Add struct to hold HDF5-specific group info. */
694
5.94k
    if (!(nc4_info->root_grp->format_grp_info = calloc(1, sizeof(NC_HDF5_GRP_INFO_T))))
695
0
        BAIL(NC_ENOMEM);
696
697
5.94k
    h5 = (NC_HDF5_FILE_INFO_T*)nc4_info->format_file_info;
698
699
#ifdef ENABLE_BYTERANGE
700
    /* See if we want the byte range protocol */
701
    if(NC_testmode(path,"bytes")) {
702
        h5->http.iosp = 1;
703
        /* Kill off any conflicting modes flags */
704
        mode &= ~(NC_WRITE|NC_DISKLESS|NC_PERSIST|NC_INMEMORY);
705
        parameters = NULL; /* kill off parallel */
706
    } else
707
        h5->http.iosp = 0;
708
#endif /*ENABLE_BYTERANGE*/
709
710
5.94k
    nc4_info->mem.inmemory = ((mode & NC_INMEMORY) == NC_INMEMORY);
711
5.94k
    nc4_info->mem.diskless = ((mode & NC_DISKLESS) == NC_DISKLESS);
712
5.94k
    nc4_info->mem.persist = ((mode & NC_PERSIST) == NC_PERSIST);
713
714
    /* Does the mode specify that this file is read-only? */
715
5.94k
    if ((mode & NC_WRITE) == 0)
716
5.94k
        nc4_info->no_write = NC_TRUE;
717
718
5.94k
    if(nc4_info->mem.inmemory && nc4_info->mem.diskless)
719
0
        BAIL(NC_EINTERNAL);
720
721
#ifdef USE_PARALLEL4
722
    mpiinfo = (NC_MPI_INFO*)parameters; /* assume, may be changed if inmemory is true */
723
#endif /* !USE_PARALLEL4 */
724
725
    /* Need this access plist to control how HDF5 handles open objects
726
     * on file close. (Setting H5F_CLOSE_SEMI will cause H5Fclose to
727
     * fail if there are any open objects in the file). */
728
5.94k
    if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0)
729
0
        BAIL(NC_EHDFERR);
730
731
5.94k
    if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_SEMI) < 0)
732
0
        BAIL(NC_EHDFERR);
733
734
#ifdef USE_PARALLEL4
735
    if (!(mode & (NC_INMEMORY | NC_DISKLESS)) && mpiinfo != NULL) {
736
        /* If this is a parallel file create, set up the file creation
737
         * property list.
738
         */
739
        nc4_info->parallel = NC_TRUE;
740
        LOG((4, "opening parallel file with MPI/IO"));
741
        if (H5Pset_fapl_mpio(fapl_id, mpiinfo->comm, mpiinfo->info) < 0)
742
            BAIL(NC_EPARINIT);
743
744
        /* Keep copies of the MPI Comm & Info objects */
745
        if (MPI_SUCCESS != MPI_Comm_dup(mpiinfo->comm, &nc4_info->comm))
746
            BAIL(NC_EMPI);
747
        comm_duped++;
748
        if (MPI_INFO_NULL != mpiinfo->info)
749
        {
750
            if (MPI_SUCCESS != MPI_Info_dup(mpiinfo->info, &nc4_info->info))
751
                BAIL(NC_EMPI);
752
            info_duped++;
753
        }
754
        else
755
        {
756
            /* No dup, just copy it. */
757
            nc4_info->info = mpiinfo->info;
758
        }
759
    }
760
761
#ifdef HDF5_HAS_COLL_METADATA_OPS
762
    if (H5Pset_all_coll_metadata_ops(fapl_id, 1) < 0)
763
        BAIL(NC_EPARINIT);
764
#endif
765
766
#else /* only set cache for non-parallel. */
767
5.94k
    if (H5Pset_cache(fapl_id, 0, nc4_chunk_cache_nelems, nc4_chunk_cache_size,
768
5.94k
                     nc4_chunk_cache_preemption) < 0)
769
0
        BAIL(NC_EHDFERR);
770
5.94k
    LOG((4, "%s: set HDF raw chunk cache to size %d nelems %d preemption %f",
771
5.94k
         __func__, nc4_chunk_cache_size, nc4_chunk_cache_nelems,
772
5.94k
         nc4_chunk_cache_preemption));
773
5.94k
#endif /* USE_PARALLEL4 */
774
775
    /* Process  NC_INMEMORY */
776
5.94k
    if(nc4_info->mem.inmemory) {
777
0
        NC_memio* memio;
778
        /* validate */
779
0
        if(parameters == NULL)
780
0
            BAIL(NC_EINMEMORY);
781
0
        memio = (NC_memio*)parameters;
782
0
        if(memio->memory == NULL || memio->size == 0)
783
0
            BAIL(NC_EINMEMORY);
784
        /* initialize h5->mem */
785
0
        nc4_info->mem.memio = *memio;
786
        /* Is the incoming memory locked? */
787
0
        nc4_info->mem.locked = (nc4_info->mem.memio.flags & NC_MEMIO_LOCKED) == NC_MEMIO_LOCKED;
788
        /* As a safeguard, if not locked and not read-only,
789
           then we must take control of the incoming memory */
790
0
        if(!nc4_info->mem.locked && !nc4_info->no_write) {
791
0
            memio->memory = NULL; /* take control */
792
0
            memio->size = 0;
793
0
        }
794
0
        retval = NC4_open_image_file(nc4_info);
795
0
        if(retval)
796
0
            BAIL(NC_EHDFERR);
797
0
    }
798
5.94k
    else
799
5.94k
        if(nc4_info->mem.diskless) {   /* Process  NC_DISKLESS */
800
0
            size_t min_incr = 65536; /* Minimum buffer increment */
801
            /* Configure FAPL to use the core file driver */
802
0
            if (H5Pset_fapl_core(fapl_id, min_incr, (nc4_info->mem.persist?1:0)) < 0)
803
0
                BAIL(NC_EHDFERR);
804
            /* Open the HDF5 file. */
805
0
            if ((h5->hdfid = H5Fopen(path, flags, fapl_id)) < 0)
806
0
                BAIL(NC_EHDFERR);
807
0
        }
808
#ifdef ENABLE_BYTERANGE
809
        else
810
            if(h5->http.iosp) {   /* Arrange to use the byte-range driver */
811
                /* Configure FAPL to use the byte-range file driver */
812
                if (H5Pset_fapl_http(fapl_id) < 0)
813
                    BAIL(NC_EHDFERR);
814
                /* Open the HDF5 file. */
815
                if ((h5->hdfid = H5Fopen(path, flags, fapl_id)) < 0)
816
                    BAIL(NC_EHDFERR);
817
            }
818
#endif
819
5.94k
            else
820
5.94k
            {
821
                /* Open the HDF5 file. */
822
5.94k
                if ((h5->hdfid = H5Fopen(path, flags, fapl_id)) < 0)
823
0
                    BAIL(NC_EHDFERR);
824
5.94k
            }
825
826
    /* Now read in all the metadata. Some types and dimscale
827
     * information may be difficult to resolve here, if, for example, a
828
     * dataset of user-defined type is encountered before the
829
     * definition of that type. */
830
5.94k
    if ((retval = rec_read_metadata(nc4_info->root_grp)))
831
0
        BAIL(retval);
832
833
    /* Check for classic model attribute. */
834
5.94k
    if ((retval = check_for_classic_model(nc4_info->root_grp, &is_classic)))
835
0
        BAIL(retval);
836
5.94k
    if (is_classic)
837
2.12k
        nc4_info->cmode |= NC_CLASSIC_MODEL;
838
839
    /* Set the provenance info for this file */
840
5.94k
    if ((retval = NC4_read_provenance(nc4_info)))
841
0
        BAIL(retval);
842
843
    /* Now figure out which netCDF dims are indicated by the dimscale
844
     * information. */
845
5.94k
    if ((retval = rec_match_dimscales(nc4_info->root_grp)))
846
0
        BAIL(retval);
847
848
#ifdef LOGGING
849
    /* This will print out the names, types, lens, etc of the vars and
850
       atts in the file, if the logging level is 2 or greater. */
851
    log_metadata_nc(nc4_info);
852
#endif
853
854
    /* Close the property list. */
855
5.94k
    if (H5Pclose(fapl_id) < 0)
856
0
        BAIL(NC_EHDFERR);
857
858
5.94k
    return NC_NOERR;
859
860
0
exit:
861
#ifdef USE_PARALLEL4
862
    if (comm_duped) MPI_Comm_free(&nc4_info->comm);
863
    if (info_duped) MPI_Info_free(&nc4_info->info);
864
#endif
865
866
0
    if (fapl_id > 0 && fapl_id != H5P_DEFAULT)
867
0
        H5Pclose(fapl_id);
868
0
    if (nc4_info)
869
0
        nc4_close_hdf5_file(nc4_info, 1, 0); /*  treat like abort*/
870
0
    return retval;
871
5.94k
}
872
873
/**
874
 * @internal Open a netCDF-4 file.
875
 *
876
 * @param path The file name of the new file.
877
 * @param mode The open mode flag.
878
 * @param basepe Ignored by this function.
879
 * @param chunksizehintp Ignored by this function.
880
 * @param parameters pointer to struct holding extra data (e.g. for parallel I/O)
881
 * layer. Ignored if NULL.
882
 * @param dispatch Pointer to the dispatch table for this file.
883
 * @param nc_file Pointer to an instance of NC.
884
 *
885
 * @return ::NC_NOERR No error.
886
 * @return ::NC_EINVAL Invalid inputs.
887
 * @author Ed Hartnett
888
 */
889
int
890
NC4_open(const char *path, int mode, int basepe, size_t *chunksizehintp,
891
         void *parameters, const NC_Dispatch *dispatch, int ncid)
892
5.94k
{
893
5.94k
    assert(path && dispatch);
894
895
5.94k
    LOG((1, "%s: path %s mode %d params %x",
896
5.94k
         __func__, path, mode, parameters));
897
898
    /* Check the mode for validity */
899
5.94k
    if (mode & ILLEGAL_OPEN_FLAGS)
900
0
        return NC_EINVAL;
901
902
5.94k
    if((mode & NC_DISKLESS) && (mode & NC_INMEMORY))
903
0
        return NC_EINVAL;
904
905
    /* If this is our first file, initialize HDF5. */
906
5.94k
    if (!nc4_hdf5_initialized)
907
0
        nc4_hdf5_initialize();
908
909
#ifdef LOGGING
910
    /* If nc logging level has changed, see if we need to turn on
911
     * HDF5's error messages. */
912
    hdf5_set_log_level();
913
#endif /* LOGGING */
914
915
    /* Open the file. */
916
5.94k
    return nc4_open_file(path, mode, parameters, ncid);
917
5.94k
}
918
919
/**
920
 * @internal Find out what filters are applied to this HDF5 dataset,
921
 * fletcher32, deflate, and/or shuffle. All other filters are
922
 * captured.
923
 *
924
 * @param propid ID of HDF5 var creation properties list.
925
 * @param var Pointer to NC_VAR_INFO_T for this variable.
926
 *
927
 * @return ::NC_NOERR No error.
928
 * @return ::NC_ENOMEM Out of memory.
929
 * @return ::NC_EHDFERR HDF5 returned error.
930
 * @author Dennis Heimbigner, Ed Hartnett
931
 */
932
static int get_filter_info(hid_t propid, NC_VAR_INFO_T *var)
933
67.7k
{
934
67.7k
    H5Z_filter_t filter;
935
67.7k
    int num_filters;
936
67.7k
    unsigned int cd_values_zip[CD_NELEMS_ZLIB];
937
67.7k
    size_t cd_nelems = CD_NELEMS_ZLIB;
938
67.7k
    int f;
939
67.7k
    int stat = NC_NOERR;
940
941
67.7k
    assert(var);
942
943
67.7k
    if ((num_filters = H5Pget_nfilters(propid)) < 0)
944
0
        return NC_EHDFERR;
945
946
67.7k
    for (f = 0; f < num_filters; f++)
947
0
    {
948
0
        if ((filter = H5Pget_filter2(propid, f, NULL, &cd_nelems, cd_values_zip,
949
0
                                     0, NULL, NULL)) < 0)
950
0
            return NC_EHDFERR;
951
0
        switch (filter)
952
0
        {
953
0
        case H5Z_FILTER_SHUFFLE:
954
0
            var->shuffle = NC_TRUE;
955
0
            break;
956
957
0
        case H5Z_FILTER_FLETCHER32:
958
0
            var->fletcher32 = NC_TRUE;
959
0
            break;
960
961
0
        case H5Z_FILTER_DEFLATE:
962
0
            if (cd_nelems != CD_NELEMS_ZLIB ||
963
0
                cd_values_zip[0] > NC_MAX_DEFLATE_LEVEL)
964
0
                return NC_EHDFERR;
965
0
      if((stat = NC4_hdf5_addfilter(var,FILTERACTIVE,filter,cd_nelems,cd_values_zip)))
966
0
    return stat;
967
0
            break;
968
969
0
        case H5Z_FILTER_SZIP: {
970
            /* Szip is tricky because the filter code expands the set of parameters from 2 to 4
971
               and changes some of the parameter values; try to compensate */
972
0
            if(cd_nelems == 0) {
973
0
    if((stat = NC4_hdf5_addfilter(var,FILTERACTIVE,filter,0,NULL)))
974
0
       return stat;
975
0
            } else {
976
                /* We have to re-read the parameters based on actual nparams,
977
                   which in the case of szip, differs from users original nparams */
978
0
                unsigned int* realparams = (unsigned int*)calloc(1,sizeof(unsigned int)*cd_nelems);
979
0
                if(realparams == NULL)
980
0
                    return NC_ENOMEM;
981
0
                if((filter = H5Pget_filter2(propid, f, NULL, &cd_nelems,
982
0
                                            realparams, 0, NULL, NULL)) < 0) 
983
0
                    return NC_EHDFERR;
984
                /* fix up the parameters and the #params */
985
0
    if(cd_nelems != 4)
986
0
        return NC_EHDFERR;
987
0
    cd_nelems = 2; /* ignore last two */    
988
    /* Fix up changed params */
989
0
    realparams[0] &= (H5_SZIP_ALL_MASKS);
990
    /* Save info */
991
0
    stat = NC4_hdf5_addfilter(var,FILTERACTIVE,filter,cd_nelems,realparams);
992
0
    nullfree(realparams);
993
0
    if(stat) return stat;
994
995
0
            }
996
0
            } break;
997
998
0
        default:
999
0
            if(cd_nelems == 0) {
1000
0
            if((stat = NC4_hdf5_addfilter(var,FILTERACTIVE,filter,0,NULL))) return stat;
1001
0
            } else {
1002
                /* We have to re-read the parameters based on actual nparams */
1003
0
                unsigned int* realparams = (unsigned int*)calloc(1,sizeof(unsigned int)*cd_nelems);
1004
0
                if(realparams == NULL)
1005
0
                    return NC_ENOMEM;
1006
0
                if((filter = H5Pget_filter2(propid, f, NULL, &cd_nelems,
1007
0
                                            realparams, 0, NULL, NULL)) < 0)
1008
0
                    return NC_EHDFERR;
1009
0
            stat = NC4_hdf5_addfilter(var,FILTERACTIVE,filter,cd_nelems,realparams);
1010
0
    nullfree(realparams);
1011
0
    if(stat) return stat;
1012
0
            }
1013
0
            break;
1014
0
        }
1015
0
    }
1016
67.7k
    return NC_NOERR;
1017
67.7k
}
1018
1019
/**
1020
 * @internal Learn if there is a fill value defined for a variable,
1021
 * and, if so, its value.
1022
 *
1023
 * @param propid ID of HDF5 var creation properties list.
1024
 * @param var Pointer to NC_VAR_INFO_T for this variable.
1025
 *
1026
 * @return ::NC_NOERR No error.
1027
 * @return ::NC_ENOMEM Out of memory.
1028
 * @return ::NC_EHDFERR HDF5 returned error.
1029
 * @author Dennis Heimbigner, Ed Hartnett
1030
 */
1031
static int get_fill_info(hid_t propid, NC_VAR_INFO_T *var)
1032
67.7k
{
1033
67.7k
    H5D_fill_value_t fill_status;
1034
1035
    /* Is there a fill value associated with this dataset? */
1036
67.7k
    if (H5Pfill_value_defined(propid, &fill_status) < 0)
1037
0
        return NC_EHDFERR;
1038
1039
    /* Get the fill value, if there is one defined. */
1040
67.7k
    if (fill_status == H5D_FILL_VALUE_USER_DEFINED)
1041
67.7k
    {
1042
        /* Allocate space to hold the fill value. */
1043
67.7k
        if (!var->fill_value)
1044
67.7k
        {
1045
67.7k
            if (var->type_info->nc_type_class == NC_VLEN)
1046
0
            {
1047
0
                if (!(var->fill_value = malloc(sizeof(nc_vlen_t))))
1048
0
                    return NC_ENOMEM;
1049
0
            }
1050
67.7k
            else if (var->type_info->nc_type_class == NC_STRING)
1051
0
            {
1052
0
                if (!(var->fill_value = malloc(sizeof(char *))))
1053
0
                    return NC_ENOMEM;
1054
0
            }
1055
67.7k
            else
1056
67.7k
            {
1057
67.7k
                assert(var->type_info->size);
1058
67.7k
                if (!(var->fill_value = malloc(var->type_info->size)))
1059
0
                    return NC_ENOMEM;
1060
67.7k
            }
1061
67.7k
        }
1062
1063
        /* Get the fill value from the HDF5 property lust. */
1064
67.7k
        if (H5Pget_fill_value(propid, ((NC_HDF5_TYPE_INFO_T *)var->type_info->format_type_info)->native_hdf_typeid,
1065
67.7k
                              var->fill_value) < 0)
1066
0
            return NC_EHDFERR;
1067
67.7k
    }
1068
0
    else
1069
0
        var->no_fill = NC_TRUE;
1070
1071
67.7k
    return NC_NOERR;
1072
67.7k
}
1073
1074
/**
1075
 * @internal Learn the storage and (if chunked) chunksizes of a var.
1076
 *
1077
 * @param propid ID of HDF5 var creation properties list.
1078
 * @param var Pointer to NC_VAR_INFO_T for this variable.
1079
 *
1080
 * @return ::NC_NOERR No error.
1081
 * @return ::NC_ENOMEM Out of memory.
1082
 * @return ::NC_EHDFERR HDF5 returned error.
1083
 * @author Dennis Heimbigner, Ed Hartnett
1084
 */
1085
static int
1086
get_chunking_info(hid_t propid, NC_VAR_INFO_T *var)
1087
67.7k
{
1088
67.7k
    H5D_layout_t layout;
1089
67.7k
    hsize_t chunksize[H5S_MAX_RANK] = {0};
1090
67.7k
    int d;
1091
1092
    /* Get the chunking info the var. */
1093
67.7k
    if ((layout = H5Pget_layout(propid)) < -1)
1094
0
        return NC_EHDFERR;
1095
1096
    /* Remember the layout and, if chunked, the chunksizes. */
1097
67.7k
    if (layout == H5D_CHUNKED)
1098
12.4k
    {
1099
12.4k
  var->storage = NC_CHUNKED;
1100
12.4k
        if (H5Pget_chunk(propid, H5S_MAX_RANK, chunksize) < 0)
1101
0
            return NC_EHDFERR;
1102
12.4k
        if (!(var->chunksizes = malloc(var->ndims * sizeof(size_t))))
1103
0
            return NC_ENOMEM;
1104
35.3k
        for (d = 0; d < var->ndims; d++)
1105
22.9k
            var->chunksizes[d] = chunksize[d];
1106
12.4k
    }
1107
55.3k
    else if (layout == H5D_CONTIGUOUS)
1108
55.3k
    {
1109
55.3k
  var->storage = NC_CONTIGUOUS;
1110
55.3k
    }
1111
0
    else if (layout == H5D_COMPACT)
1112
0
    {
1113
0
  var->storage = NC_COMPACT;
1114
0
    }
1115
1116
67.7k
    return NC_NOERR;
1117
67.7k
}
1118
1119
/**
1120
 * @internal This function gets info about the dimscales attached to a
1121
 * dataset. The info is used later for dimscale matching.
1122
 *
1123
 * @param var Pointer to var info struct.
1124
 * @param hdf5_var Pointer to HDF5 var info struct.
1125
 * @param ndims Number of dims for this var.
1126
 * @param datasetid HDF5 datasetid.
1127
 *
1128
 * @return ::NC_NOERR No error.
1129
 * @return ::NC_EBADID Bad ncid.
1130
 * @return ::NC_ENOMEM Out of memory.
1131
 * @return ::NC_EHDFERR HDF5 returned error.
1132
 * @return ::NC_EVARMETA Error with var metadata.
1133
 * @author Ed Hartnett, Dennis Heimbigner
1134
 */
1135
static int
1136
get_attached_info(NC_VAR_INFO_T *var, NC_HDF5_VAR_INFO_T *hdf5_var, int ndims,
1137
                  hid_t datasetid)
1138
66.3k
{
1139
66.3k
    int d;
1140
66.3k
    int num_scales = 0;
1141
1142
66.3k
    LOG((4, "%s ndims %d datasetid %ld", __func__, ndims, datasetid));
1143
1144
    /* Find out how many scales are attached to this
1145
     * dataset. H5DSget_num_scales returns an error if there are no
1146
     * scales, so convert a negative return value to zero. */
1147
66.3k
    num_scales = H5DSget_num_scales(datasetid, 0);
1148
66.3k
    if (num_scales < 0)
1149
40.8k
        num_scales = 0;
1150
66.3k
    LOG((4, "num_scales %d", num_scales));
1151
1152
    /* If an enddef has already been called, the dimscales will already
1153
     * be taken care of. */
1154
66.3k
    if (num_scales && ndims && !var->dimscale_attached)
1155
25.4k
    {
1156
        /* Allocate space to remember whether the dimscale has been
1157
         * attached for each dimension, and the HDF5 object IDs of the
1158
         * scale(s). */
1159
25.4k
        assert(!hdf5_var->dimscale_hdf5_objids);
1160
25.4k
        if (!(var->dimscale_attached = calloc(ndims, sizeof(nc_bool_t))))
1161
0
            return NC_ENOMEM;
1162
25.4k
        if (!(hdf5_var->dimscale_hdf5_objids = malloc(ndims *
1163
25.4k
                                                      sizeof(struct hdf5_objid))))
1164
0
            return NC_ENOMEM;
1165
1166
        /* Store id information allowing us to match hdf5 dimscales to
1167
         * netcdf dimensions. */
1168
65.7k
        for (d = 0; d < var->ndims; d++)
1169
40.2k
        {
1170
40.2k
            LOG((4, "about to iterate scales for dim %d", d));
1171
40.2k
            if (H5DSiterate_scales(hdf5_var->hdf_datasetid, d, NULL, dimscale_visitor,
1172
40.2k
                                   &(hdf5_var->dimscale_hdf5_objids[d])) < 0)
1173
0
                return NC_EHDFERR;
1174
40.2k
            var->dimscale_attached[d] = NC_TRUE;
1175
40.2k
            LOG((4, "dimscale attached"));
1176
40.2k
        }
1177
25.4k
    }
1178
1179
66.3k
    return NC_NOERR;
1180
66.3k
}
1181
1182
/**
1183
 * @internal This function reads scale info for vars, whether they
1184
 * are scales or not.
1185
 *
1186
 * @param grp Pointer to group info struct.
1187
 * @param dim Pointer to dim info struct if this is a scale, NULL
1188
 * otherwise.
1189
 * @param var Pointer to var info struct.
1190
 * @param hdf5_var Pointer to HDF5 var info struct.
1191
 * @param ndims Number of dims for this var.
1192
 * @param datasetid HDF5 datasetid.
1193
 *
1194
 * @return ::NC_NOERR No error.
1195
 * @return ::NC_EBADID Bad ncid.
1196
 * @return ::NC_ENOMEM Out of memory.
1197
 * @return ::NC_EHDFERR HDF5 returned error.
1198
 * @return ::NC_EVARMETA Error with var metadata.
1199
 * @author Ed Hartnett, Dennis Heimbigner
1200
 */
1201
static int
1202
get_scale_info(NC_GRP_INFO_T *grp, NC_DIM_INFO_T *dim, NC_VAR_INFO_T *var,
1203
               NC_HDF5_VAR_INFO_T *hdf5_var, int ndims, hid_t datasetid)
1204
67.7k
{
1205
67.7k
    int retval;
1206
1207
    /* If it's a scale, mark it as such. */
1208
67.7k
    if (dim)
1209
1.47k
    {
1210
1.47k
        assert(ndims);
1211
1.47k
        var->dimscale = NC_TRUE;
1212
1213
        /* If this is a multi-dimensional coordinate var, then the
1214
         * dimids must be stored in the hidden coordinates attribute. */
1215
1.47k
        if (var->ndims > 1)
1216
369
        {
1217
369
            if ((retval = read_coord_dimids(grp, var)))
1218
0
                return retval;
1219
369
        }
1220
1.10k
        else
1221
1.10k
        {
1222
            /* This is a 1-dimensional coordinate var. */
1223
1.10k
            assert(!strcmp(var->hdr.name, dim->hdr.name));
1224
1.10k
            var->dimids[0] = dim->hdr.id;
1225
1.10k
            var->dim[0] = dim;
1226
1.10k
        }
1227
1.47k
        dim->coord_var = var;
1228
1.47k
    }
1229
66.3k
    else /* Not a scale. */
1230
66.3k
    {
1231
66.3k
        if (!var->coords_read)
1232
56.1k
            if ((retval = get_attached_info(var, hdf5_var, ndims, datasetid)))
1233
0
                return retval;
1234
66.3k
    }
1235
1236
67.7k
    return NC_NOERR;
1237
67.7k
}
1238
1239
/**
1240
 * @internal Get the metadata for a variable.
1241
 *
1242
 * @param var Pointer to var info struct.
1243
 *
1244
 * @return ::NC_NOERR No error.
1245
 * @return ::NC_EBADID Bad ncid.
1246
 * @return ::NC_ENOMEM Out of memory.
1247
 * @return ::NC_EHDFERR HDF5 returned error.
1248
 * @return ::NC_EVARMETA Error with var metadata.
1249
 * @author Ed Hartnett
1250
 */
1251
int
1252
nc4_get_var_meta(NC_VAR_INFO_T *var)
1253
67.7k
{
1254
67.7k
    NC_HDF5_VAR_INFO_T *hdf5_var;
1255
67.7k
    hid_t access_pid = 0;
1256
67.7k
    hid_t propid = 0;
1257
67.7k
    double rdcc_w0;
1258
67.7k
    int retval = NC_NOERR;
1259
1260
67.7k
    assert(var && var->format_var_info);
1261
67.7k
    LOG((3, "%s: var %s", __func__, var->hdr.name));
1262
1263
    /* Have we already read the var metadata? */
1264
67.7k
    if (var->meta_read)
1265
0
        return NC_NOERR;
1266
1267
    /* Get pointer to the HDF5-specific var info struct. */
1268
67.7k
    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
1269
1270
    /* Get the current chunk cache settings. */
1271
67.7k
    if ((access_pid = H5Dget_access_plist(hdf5_var->hdf_datasetid)) < 0)
1272
0
        BAIL(NC_EVARMETA);
1273
1274
    /* Learn about current chunk cache settings. */
1275
67.7k
    if ((H5Pget_chunk_cache(access_pid, &(var->chunk_cache_nelems),
1276
67.7k
                            &(var->chunk_cache_size), &rdcc_w0)) < 0)
1277
0
        BAIL(NC_EHDFERR);
1278
67.7k
    var->chunk_cache_preemption = rdcc_w0;
1279
1280
    /* Get the dataset creation properties. */
1281
67.7k
    if ((propid = H5Dget_create_plist(hdf5_var->hdf_datasetid)) < 0)
1282
0
        BAIL(NC_EHDFERR);
1283
1284
    /* Get var chunking info. */
1285
67.7k
    if ((retval = get_chunking_info(propid, var)))
1286
0
        BAIL(retval);
1287
1288
    /* Get filter info for a var. */
1289
67.7k
    if ((retval = get_filter_info(propid, var)))
1290
0
        BAIL(retval);
1291
1292
    /* Get fill value, if defined. */
1293
67.7k
    if ((retval = get_fill_info(propid, var)))
1294
0
        BAIL(retval);
1295
1296
    /* Is this a deflated variable with a chunksize greater than the
1297
     * current cache size? */
1298
67.7k
    if ((retval = nc4_adjust_var_cache(var->container, var)))
1299
0
        BAIL(retval);
1300
1301
67.7k
    if (var->coords_read && !var->dimscale)
1302
10.1k
        if ((retval = get_attached_info(var, hdf5_var, var->ndims, hdf5_var->hdf_datasetid)))
1303
0
            return retval;
1304
1305
    /* Remember that we have read the metadata for this var. */
1306
67.7k
    var->meta_read = NC_TRUE;
1307
1308
67.7k
exit:
1309
67.7k
    if (access_pid && H5Pclose(access_pid) < 0)
1310
0
        BAIL2(NC_EHDFERR);
1311
67.7k
    if (propid > 0 && H5Pclose(propid) < 0)
1312
0
        BAIL2(NC_EHDFERR);
1313
67.7k
    return retval;
1314
67.7k
}
1315
1316
/**
1317
 * @internal This function is called by read_dataset(), (which is
1318
 * called by rec_read_metadata()) when a netCDF variable is found in
1319
 * the file. This function reads in all the metadata about the
1320
 * var. Attributes are not read until the user asks for information
1321
 * about one of them.
1322
 *
1323
 * @param grp Pointer to group info struct.
1324
 * @param datasetid HDF5 dataset ID.
1325
 * @param obj_name Name of the HDF5 object to read.
1326
 * @param ndims Number of dimensions.
1327
 * @param dim If non-NULL, then this var is a coordinate var for a
1328
 * dimension, and this points to the info for that dimension.
1329
 *
1330
 * @return ::NC_NOERR No error.
1331
 * @return ::NC_EBADID Bad ncid.
1332
 * @return ::NC_ENOMEM Out of memory.
1333
 * @return ::NC_EHDFERR HDF5 returned error.
1334
 * @return ::NC_EVARMETA Error with var metadata.
1335
 * @author Ed Hartnett, Dennis Heimbigner
1336
 */
1337
static int
1338
read_var(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name,
1339
         size_t ndims, NC_DIM_INFO_T *dim)
1340
67.7k
{
1341
67.7k
    NC_VAR_INFO_T *var = NULL;
1342
67.7k
    NC_HDF5_VAR_INFO_T *hdf5_var;
1343
67.7k
    int incr_id_rc = 0; /* Whether dataset ID's ref count has been incremented */
1344
67.7k
    char *finalname = NULL;
1345
67.7k
    int retval = NC_NOERR;
1346
1347
67.7k
    assert(obj_name && grp);
1348
67.7k
    LOG((4, "%s: obj_name %s", __func__, obj_name));
1349
1350
    /* Check for a weird case: a non-coordinate variable that has the
1351
     * same name as a dimension. It's legal in netcdf, and requires
1352
     * that the HDF5 dataset name be changed. */
1353
67.7k
    if (strlen(obj_name) > strlen(NON_COORD_PREPEND) &&
1354
13.3k
        !strncmp(obj_name, NON_COORD_PREPEND, strlen(NON_COORD_PREPEND)))
1355
2.55k
    {
1356
        /* Allocate space for the name. */
1357
2.55k
        if (!(finalname = malloc(((strlen(obj_name) -
1358
2.55k
                                   strlen(NON_COORD_PREPEND))+ 1) * sizeof(char))))
1359
0
            BAIL(NC_ENOMEM);
1360
2.55k
        strcpy(finalname, &obj_name[strlen(NON_COORD_PREPEND)]);
1361
2.55k
    } else
1362
65.2k
        finalname = strdup(obj_name);
1363
1364
    /* Add a variable to the end of the group's var list. */
1365
67.7k
    if ((retval = nc4_var_list_add(grp, finalname, ndims, &var)))
1366
0
        BAIL(retval);
1367
1368
    /* Add storage for HDF5-specific var info. */
1369
67.7k
    if (!(var->format_var_info = calloc(1, sizeof(NC_HDF5_VAR_INFO_T))))
1370
0
        BAIL(NC_ENOMEM);
1371
67.7k
    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
1372
1373
    /* Fill in what we already know. */
1374
67.7k
    hdf5_var->hdf_datasetid = datasetid;
1375
67.7k
    H5Iinc_ref(hdf5_var->hdf_datasetid); /* Increment number of objects using ID */
1376
67.7k
    incr_id_rc++; /* Indicate that we've incremented the ref. count (for errors) */
1377
67.7k
    var->created = NC_TRUE;
1378
67.7k
    var->atts_read = 0;
1379
1380
    /* Try and read the dimids from the COORDINATES attribute. If it's
1381
     * not present, we will have to do dimsscale matching to locate the
1382
     * dims for this var. */
1383
67.7k
    retval = read_coord_dimids(grp, var);
1384
67.7k
    if (retval && retval != NC_ENOTATT)
1385
0
        BAIL(retval);
1386
67.7k
    retval = NC_NOERR;
1387
1388
    /* Handle scale info. */
1389
67.7k
    if ((retval = get_scale_info(grp, dim, var, hdf5_var, ndims, datasetid)))
1390
0
        BAIL(retval);
1391
1392
    /* Learn all about the type of this variable. This will fail for
1393
     * HDF5 reference types, and then the var we just created will be
1394
     * deleted, thus ignoring HDF5 reference type objects. */
1395
67.7k
    if ((retval = get_type_info2(var->container->nc4_info, hdf5_var->hdf_datasetid,
1396
67.7k
                                 &var->type_info)))
1397
0
        BAIL(retval);
1398
1399
    /* Indicate that the variable has a pointer to the type */
1400
67.7k
    var->type_info->rc++;
1401
1402
67.7k
exit:
1403
67.7k
    if (finalname)
1404
67.7k
        free(finalname);
1405
67.7k
    if (retval)
1406
0
    {
1407
        /* If there was an error, decrement the dataset ref counter, and
1408
         * delete the var info struct we just created. */
1409
0
        if (incr_id_rc && H5Idec_ref(datasetid) < 0)
1410
0
            BAIL2(NC_EHDFERR);
1411
0
        if (var)
1412
0
            nc4_var_list_del(grp, var);
1413
0
    }
1414
1415
67.7k
    return retval;
1416
67.7k
}
1417
1418
/**
1419
 * @internal Given an HDF5 type, set a pointer to netcdf type.
1420
 *
1421
 * @param h5 Pointer to HDF5 file info struct.
1422
 * @param native_typeid HDF5 type ID.
1423
 * @param xtype Pointer that gets netCDF type.
1424
 *
1425
 * @return ::NC_NOERR No error.
1426
 * @return ::NC_EBADID Bad ncid.
1427
 * @return ::NC_EHDFERR HDF5 returned error.
1428
 * @return ::NC_EBADTYPID Type not found.
1429
 * @author Ed Hartnett
1430
 */
1431
static int
1432
get_netcdf_type(NC_FILE_INFO_T *h5, hid_t native_typeid,
1433
                nc_type *xtype)
1434
60.0k
{
1435
60.0k
    NC_TYPE_INFO_T *type;
1436
60.0k
    H5T_class_t class;
1437
60.0k
    htri_t is_str, equal = 0;
1438
1439
60.0k
    assert(h5 && xtype);
1440
1441
60.0k
    if ((class = H5Tget_class(native_typeid)) < 0)
1442
0
        return NC_EHDFERR;
1443
1444
    /* H5Tequal doesn't work with H5T_C_S1 for some reason. But
1445
     * H5Tget_class will return H5T_STRING if this is a string. */
1446
60.0k
    if (class == H5T_STRING)
1447
23.6k
    {
1448
23.6k
        if ((is_str = H5Tis_variable_str(native_typeid)) < 0)
1449
0
            return NC_EHDFERR;
1450
23.6k
        if (is_str)
1451
0
            *xtype = NC_STRING;
1452
23.6k
        else
1453
23.6k
            *xtype = NC_CHAR;
1454
23.6k
        return NC_NOERR;
1455
23.6k
    }
1456
36.4k
    else if (class == H5T_INTEGER || class == H5T_FLOAT)
1457
36.4k
    {
1458
        /* For integers and floats, we don't have to worry about
1459
         * endianness if we compare native types. */
1460
36.4k
        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_SCHAR)) < 0)
1461
0
            return NC_EHDFERR;
1462
36.4k
        if (equal)
1463
5.01k
        {
1464
5.01k
            *xtype = NC_BYTE;
1465
5.01k
            return NC_NOERR;
1466
5.01k
        }
1467
31.4k
        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_SHORT)) < 0)
1468
0
            return NC_EHDFERR;
1469
31.4k
        if (equal)
1470
3.19k
        {
1471
3.19k
            *xtype = NC_SHORT;
1472
3.19k
            return NC_NOERR;
1473
3.19k
        }
1474
28.2k
        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_INT)) < 0)
1475
0
            return NC_EHDFERR;
1476
28.2k
        if (equal)
1477
23.5k
        {
1478
23.5k
            *xtype = NC_INT;
1479
23.5k
            return NC_NOERR;
1480
23.5k
        }
1481
4.63k
        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_FLOAT)) < 0)
1482
0
            return NC_EHDFERR;
1483
4.63k
        if (equal)
1484
0
        {
1485
0
            *xtype = NC_FLOAT;
1486
0
            return NC_NOERR;
1487
0
        }
1488
4.63k
        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_DOUBLE)) < 0)
1489
0
            return NC_EHDFERR;
1490
4.63k
        if (equal)
1491
4.63k
        {
1492
4.63k
            *xtype = NC_DOUBLE;
1493
4.63k
            return NC_NOERR;
1494
4.63k
        }
1495
0
        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_UCHAR)) < 0)
1496
0
            return NC_EHDFERR;
1497
0
        if (equal)
1498
0
        {
1499
0
            *xtype = NC_UBYTE;
1500
0
            return NC_NOERR;
1501
0
        }
1502
0
        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_USHORT)) < 0)
1503
0
            return NC_EHDFERR;
1504
0
        if (equal)
1505
0
        {
1506
0
            *xtype = NC_USHORT;
1507
0
            return NC_NOERR;
1508
0
        }
1509
0
        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_UINT)) < 0)
1510
0
            return NC_EHDFERR;
1511
0
        if (equal)
1512
0
        {
1513
0
            *xtype = NC_UINT;
1514
0
            return NC_NOERR;
1515
0
        }
1516
0
        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_LLONG)) < 0)
1517
0
            return NC_EHDFERR;
1518
0
        if (equal)
1519
0
        {
1520
0
            *xtype = NC_INT64;
1521
0
            return NC_NOERR;
1522
0
        }
1523
0
        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_ULLONG)) < 0)
1524
0
            return NC_EHDFERR;
1525
0
        if (equal)
1526
0
        {
1527
0
            *xtype = NC_UINT64;
1528
0
            return NC_NOERR;
1529
0
        }
1530
0
    }
1531
1532
    /* Maybe we already know about this type. */
1533
0
    if (!equal)
1534
0
        if((type = nc4_rec_find_hdf_type(h5, native_typeid)))
1535
0
        {
1536
0
            *xtype = type->hdr.id;
1537
0
            return NC_NOERR;
1538
0
        }
1539
1540
0
    *xtype = NC_NAT;
1541
0
    return NC_EBADTYPID;
1542
0
}
1543
1544
/**
1545
 * @internal Read an attribute. This is called by
1546
 * att_read_callbk().
1547
 *
1548
 * @param grp Pointer to group info struct.
1549
 * @param attid Attribute ID.
1550
 * @param att Pointer that gets att info struct.
1551
 *
1552
 * @return ::NC_NOERR No error.
1553
 * @return ::NC_EHDFERR HDF5 returned error.
1554
 * @return ::NC_EATTMETA Att metadata error.
1555
 * @return ::NC_ENOMEM Out of memory.
1556
 * @author Ed Hartnett
1557
 */
1558
static int
1559
read_hdf5_att(NC_GRP_INFO_T *grp, hid_t attid, NC_ATT_INFO_T *att)
1560
60.0k
{
1561
60.0k
    NC_HDF5_ATT_INFO_T *hdf5_att;
1562
60.0k
    hid_t spaceid = 0, file_typeid = 0;
1563
60.0k
    hsize_t dims[1] = {0}; /* netcdf attributes always 1-D. */
1564
60.0k
    size_t type_size;
1565
60.0k
    int att_ndims;
1566
60.0k
    hssize_t att_npoints;
1567
60.0k
    H5T_class_t att_class;
1568
60.0k
    int fixed_len_string = 0;
1569
60.0k
    size_t fixed_size = 0;
1570
60.0k
    int retval = NC_NOERR;
1571
1572
60.0k
    assert(att && att->hdr.name && att->format_att_info);
1573
60.0k
    LOG((5, "%s: att->hdr.id %d att->hdr.name %s att->nc_typeid %d att->len %d",
1574
60.0k
         __func__, att->hdr.id, att->hdr.name, (int)att->nc_typeid, att->len));
1575
1576
    /* Get HDF5-sepecific info struct for this attribute. */
1577
60.0k
    hdf5_att = (NC_HDF5_ATT_INFO_T *)att->format_att_info;
1578
1579
    /* Get type of attribute in file. */
1580
60.0k
    if ((file_typeid = H5Aget_type(attid)) < 0)
1581
0
        return NC_EATTMETA;
1582
60.0k
    if ((hdf5_att->native_hdf_typeid = H5Tget_native_type(file_typeid,
1583
60.0k
                                                          H5T_DIR_DEFAULT)) < 0)
1584
0
        BAIL(NC_EHDFERR);
1585
60.0k
    if ((att_class = H5Tget_class(hdf5_att->native_hdf_typeid)) < 0)
1586
0
        BAIL(NC_EATTMETA);
1587
60.0k
    if (att_class == H5T_STRING &&
1588
23.6k
        !H5Tis_variable_str(hdf5_att->native_hdf_typeid))
1589
23.6k
    {
1590
23.6k
        fixed_len_string++;
1591
23.6k
        if (!(fixed_size = H5Tget_size(hdf5_att->native_hdf_typeid)))
1592
0
            BAIL(NC_EATTMETA);
1593
23.6k
    }
1594
60.0k
    if ((retval = get_netcdf_type(grp->nc4_info, hdf5_att->native_hdf_typeid,
1595
60.0k
                                  &(att->nc_typeid))))
1596
0
        BAIL(retval);
1597
1598
1599
    /* Get len. */
1600
60.0k
    if ((spaceid = H5Aget_space(attid)) < 0)
1601
0
        BAIL(NC_EATTMETA);
1602
60.0k
    if ((att_ndims = H5Sget_simple_extent_ndims(spaceid)) < 0)
1603
0
        BAIL(NC_EATTMETA);
1604
60.0k
    if ((att_npoints = H5Sget_simple_extent_npoints(spaceid)) < 0)
1605
0
        BAIL(NC_EATTMETA);
1606
1607
    /* If both att_ndims and att_npoints are zero, then this is a
1608
     * zero length att. */
1609
60.0k
    if (att_ndims == 0 && att_npoints == 0)
1610
8.51k
        dims[0] = 0;
1611
51.5k
    else if (att->nc_typeid == NC_STRING)
1612
0
        dims[0] = att_npoints;
1613
51.5k
    else if (att->nc_typeid == NC_CHAR)
1614
15.1k
    {
1615
        /* NC_CHAR attributes are written as a scalar in HDF5, of type
1616
         * H5T_C_S1, of variable length. */
1617
15.1k
        if (att_ndims == 0)
1618
15.1k
        {
1619
15.1k
            if (!(dims[0] = H5Tget_size(file_typeid)))
1620
0
                BAIL(NC_EATTMETA);
1621
15.1k
        }
1622
0
        else
1623
0
        {
1624
            /* This is really a string type! */
1625
0
            att->nc_typeid = NC_STRING;
1626
0
            dims[0] = att_npoints;
1627
0
        }
1628
15.1k
    }
1629
36.4k
    else
1630
36.4k
    {
1631
36.4k
        H5S_class_t space_class;
1632
1633
        /* All netcdf attributes are scalar or 1-D only. */
1634
36.4k
        if (att_ndims > 1)
1635
0
            BAIL(NC_EATTMETA);
1636
1637
        /* Check class of HDF5 dataspace */
1638
36.4k
        if ((space_class = H5Sget_simple_extent_type(spaceid)) < 0)
1639
0
            BAIL(NC_EATTMETA);
1640
1641
        /* Check for NULL HDF5 dataspace class (should be weeded out
1642
         * earlier) */
1643
36.4k
        if (H5S_NULL == space_class)
1644
0
            BAIL(NC_EATTMETA);
1645
1646
        /* check for SCALAR HDF5 dataspace class */
1647
36.4k
        if (H5S_SCALAR == space_class)
1648
0
            dims[0] = 1;
1649
36.4k
        else /* Must be "simple" dataspace */
1650
36.4k
        {
1651
            /* Read the size of this attribute. */
1652
36.4k
            if (H5Sget_simple_extent_dims(spaceid, dims, NULL) < 0)
1653
0
                BAIL(NC_EATTMETA);
1654
36.4k
        }
1655
36.4k
    }
1656
1657
    /* Tell the user what the length if this attribute is. */
1658
60.0k
    att->len = dims[0];
1659
1660
    /* Allocate some memory if the len is not zero, and read the
1661
       attribute. */
1662
60.0k
    if (dims[0])
1663
51.5k
    {
1664
51.5k
        if ((retval = nc4_get_typelen_mem(grp->nc4_info, att->nc_typeid,
1665
51.5k
                                          &type_size)))
1666
0
            return retval;
1667
51.5k
        if (att_class == H5T_VLEN)
1668
0
        {
1669
0
            if (!(att->vldata = malloc((unsigned int)(att->len * sizeof(hvl_t)))))
1670
0
                BAIL(NC_ENOMEM);
1671
0
            if (H5Aread(attid, hdf5_att->native_hdf_typeid, att->vldata) < 0)
1672
0
                BAIL(NC_EATTMETA);
1673
0
        }
1674
51.5k
        else if (att->nc_typeid == NC_STRING)
1675
0
        {
1676
0
            if (!(att->stdata = calloc(att->len, sizeof(char *))))
1677
0
                BAIL(NC_ENOMEM);
1678
            /* For a fixed length HDF5 string, the read requires
1679
             * contiguous memory. Meanwhile, the netCDF API requires that
1680
             * nc_free_string be called on string arrays, which would not
1681
             * work if one contiguous memory block were used. So here I
1682
             * convert the contiguous block of strings into an array of
1683
             * malloced strings (each string with its own malloc). Then I
1684
             * copy the data and free the contiguous memory. This
1685
             * involves copying the data, which is bad, but this only
1686
             * occurs for fixed length string attributes, and presumably
1687
             * these are small. (And netCDF-4 does not create them - it
1688
             * always uses variable length strings. */
1689
0
            if (fixed_len_string)
1690
0
            {
1691
0
                int i;
1692
0
                char *contig_buf, *cur;
1693
1694
                /* Alloc space for the contiguous memory read. */
1695
0
                if (!(contig_buf = malloc(att->len * fixed_size * sizeof(char))))
1696
0
                    BAIL(NC_ENOMEM);
1697
1698
                /* Read the fixed-len strings as one big block. */
1699
0
                if (H5Aread(attid, hdf5_att->native_hdf_typeid, contig_buf) < 0) {
1700
0
                    free(contig_buf);
1701
0
                    BAIL(NC_EATTMETA);
1702
0
                }
1703
1704
                /* Copy strings, one at a time, into their new home. Alloc
1705
                   space for each string. The user will later free this
1706
                   space with nc_free_string. */
1707
0
                cur = contig_buf;
1708
0
                for (i = 0; i < att->len; i++)
1709
0
                {
1710
0
                    if (!(att->stdata[i] = malloc(fixed_size))) {
1711
0
                        free(contig_buf);
1712
0
                        BAIL(NC_ENOMEM);
1713
0
                    }
1714
0
                    strncpy(att->stdata[i], cur, fixed_size);
1715
0
                    cur += fixed_size;
1716
0
                }
1717
1718
                /* Free contiguous memory buffer. */
1719
0
                free(contig_buf);
1720
0
            }
1721
0
            else
1722
0
            {
1723
                /* Read variable-length string atts. */
1724
0
                if (H5Aread(attid, hdf5_att->native_hdf_typeid, att->stdata) < 0)
1725
0
                    BAIL(NC_EATTMETA);
1726
0
            }
1727
0
        }
1728
51.5k
        else
1729
51.5k
        {
1730
51.5k
            if (!(att->data = malloc((unsigned int)(att->len * type_size))))
1731
0
                BAIL(NC_ENOMEM);
1732
51.5k
            if (H5Aread(attid, hdf5_att->native_hdf_typeid, att->data) < 0)
1733
0
                BAIL(NC_EATTMETA);
1734
51.5k
        }
1735
51.5k
    }
1736
1737
60.0k
    if (H5Tclose(file_typeid) < 0)
1738
0
        BAIL(NC_EHDFERR);
1739
60.0k
    if (H5Sclose(spaceid) < 0)
1740
0
        return NC_EHDFERR;
1741
1742
60.0k
    return NC_NOERR;
1743
1744
0
exit:
1745
0
    if (H5Tclose(file_typeid) < 0)
1746
0
        BAIL2(NC_EHDFERR);
1747
0
    if (spaceid > 0 && H5Sclose(spaceid) < 0)
1748
0
        BAIL2(NC_EHDFERR);
1749
0
    return retval;
1750
60.0k
}
1751
1752
/**
1753
 * @internal Wrap HDF5 allocated memory free operations
1754
 *
1755
 * @param memory Pointer to memory to be freed.
1756
 *
1757
 * @return ::NC_NOERR No error.
1758
 * @author Dennis Heimbigner
1759
 */
1760
static void
1761
hdf5free(void* memory)
1762
0
{
1763
0
#ifndef JNA
1764
    /* On Windows using the microsoft runtime, it is an error
1765
       for one library to free memory allocated by a different library.*/
1766
#ifdef HAVE_H5FREE_MEMORY
1767
    if(memory != NULL) H5free_memory(memory);
1768
#else
1769
0
#ifndef _MSC_VER
1770
0
    if(memory != NULL) free(memory);
1771
0
#endif
1772
0
#endif
1773
0
#endif
1774
0
}
1775
1776
/**
1777
 * @internal Read information about a user defined type from the HDF5
1778
 * file, and stash it in the group's list of types.
1779
 *
1780
 * @param grp Pointer to group info struct.
1781
 * @param hdf_typeid HDF5 type ID.
1782
 * @param type_name Pointer that gets the type name.
1783
 *
1784
 * @return ::NC_NOERR No error.
1785
 * @return ::NC_EBADID Bad ncid.
1786
 * @return ::NC_EHDFERR HDF5 returned error.
1787
 * @return ::NC_EBADTYPID Type not found.
1788
 * @return ::NC_ENOMEM Out of memory.
1789
 * @author Ed Hartnett
1790
 */
1791
static int
1792
read_type(NC_GRP_INFO_T *grp, hid_t hdf_typeid, char *type_name)
1793
0
{
1794
0
    NC_TYPE_INFO_T *type;
1795
0
    NC_HDF5_TYPE_INFO_T *hdf5_type;
1796
0
    H5T_class_t class;
1797
0
    hid_t native_typeid;
1798
0
    size_t type_size;
1799
0
    int nmembers;
1800
0
    int retval;
1801
1802
0
    assert(grp && type_name);
1803
1804
0
    LOG((4, "%s: type_name %s grp->hdr.name %s", __func__, type_name,
1805
0
         grp->hdr.name));
1806
1807
    /* What is the native type for this platform? */
1808
0
    if ((native_typeid = H5Tget_native_type(hdf_typeid, H5T_DIR_DEFAULT)) < 0)
1809
0
        return NC_EHDFERR;
1810
1811
    /* What is the size of this type on this platform. */
1812
0
    if (!(type_size = H5Tget_size(native_typeid)))
1813
0
        return NC_EHDFERR;
1814
0
    LOG((5, "type_size %d", type_size));
1815
1816
    /* Add to the list for this new type, and get a local pointer to it. */
1817
0
    if ((retval = nc4_type_list_add(grp, type_size, type_name, &type)))
1818
0
        return retval;
1819
1820
    /* Allocate storage for HDF5-specific type info. */
1821
0
    if (!(hdf5_type = calloc(1, sizeof(NC_HDF5_TYPE_INFO_T))))
1822
0
        return NC_ENOMEM;
1823
0
    type->format_type_info = hdf5_type;
1824
1825
    /* Remember HDF5-specific type info. */
1826
0
    hdf5_type->hdf_typeid = hdf_typeid;
1827
0
    hdf5_type->native_hdf_typeid = native_typeid;
1828
1829
    /* Remember we have committed this type. */
1830
0
    type->committed = NC_TRUE;
1831
1832
    /* Increment number of objects using ID. */
1833
0
    if (H5Iinc_ref(hdf5_type->hdf_typeid) < 0)
1834
0
        return NC_EHDFERR;
1835
1836
    /* What is the class of this type, compound, vlen, etc. */
1837
0
    if ((class = H5Tget_class(hdf_typeid)) < 0)
1838
0
        return NC_EHDFERR;
1839
0
    switch (class)
1840
0
    {
1841
0
    case H5T_STRING:
1842
0
        type->nc_type_class = NC_STRING;
1843
0
        break;
1844
1845
0
    case H5T_COMPOUND:
1846
0
    {
1847
0
        int nmembers;
1848
0
        unsigned int m;
1849
0
        char* member_name = NULL;
1850
#ifdef JNA
1851
        char jna[1001];
1852
#endif
1853
1854
0
        type->nc_type_class = NC_COMPOUND;
1855
1856
0
        if ((nmembers = H5Tget_nmembers(hdf_typeid)) < 0)
1857
0
            return NC_EHDFERR;
1858
0
        LOG((5, "compound type has %d members", nmembers));
1859
0
        type->u.c.field = nclistnew();
1860
0
        nclistsetalloc(type->u.c.field,nmembers);
1861
1862
0
        for (m = 0; m < nmembers; m++)
1863
0
        {
1864
0
            hid_t member_hdf_typeid;
1865
0
            hid_t member_native_typeid;
1866
0
            size_t member_offset;
1867
0
            H5T_class_t mem_class;
1868
0
            nc_type member_xtype;
1869
1870
            /* Get the typeid and native typeid of this member of the
1871
             * compound type. */
1872
0
            if ((member_hdf_typeid = H5Tget_member_type(native_typeid, m)) < 0)
1873
0
                return NC_EHDFERR;
1874
1875
0
            if ((member_native_typeid = H5Tget_native_type(member_hdf_typeid,
1876
0
                                                           H5T_DIR_DEFAULT)) < 0)
1877
0
                return NC_EHDFERR;
1878
1879
            /* Get the name of the member.*/
1880
0
            member_name = H5Tget_member_name(native_typeid, m);
1881
0
            if (!member_name || strlen(member_name) > NC_MAX_NAME) {
1882
0
                retval = NC_EBADNAME;
1883
0
                break;
1884
0
            }
1885
#ifdef JNA
1886
            else {
1887
                strncpy(jna,member_name,1000);
1888
                member_name = jna;
1889
            }
1890
#endif
1891
1892
            /* Offset in bytes on *this* platform. */
1893
0
            member_offset = H5Tget_member_offset(native_typeid, m);
1894
1895
            /* Get dimensional data if this member is an array of something. */
1896
0
            if ((mem_class = H5Tget_class(member_hdf_typeid)) < 0)
1897
0
                return NC_EHDFERR;
1898
0
            if (mem_class == H5T_ARRAY)
1899
0
            {
1900
0
                int ndims, dim_size[NC_MAX_VAR_DIMS];
1901
0
                hsize_t dims[NC_MAX_VAR_DIMS];
1902
0
                int d;
1903
1904
0
                if ((ndims = H5Tget_array_ndims(member_hdf_typeid)) < 0)
1905
0
                    return NC_EHDFERR;
1906
1907
0
                if (H5Tget_array_dims(member_hdf_typeid, dims, NULL) != ndims)
1908
0
                    return NC_EHDFERR;
1909
1910
0
                for (d = 0; d < ndims; d++)
1911
0
                    dim_size[d] = dims[d];
1912
1913
                /* What is the netCDF typeid of this member? */
1914
0
                if ((retval = get_netcdf_type(grp->nc4_info, H5Tget_super(member_hdf_typeid),
1915
0
                                              &member_xtype)))
1916
0
                    return retval;
1917
1918
                /* Add this member to our list of fields in this compound type. */
1919
0
                if ((retval = nc4_field_list_add(type, member_name, member_offset,
1920
0
                                                 member_xtype, ndims, dim_size)))
1921
0
                    return retval;
1922
0
            }
1923
0
            else
1924
0
            {
1925
                /* What is the netCDF typeid of this member? */
1926
0
                if ((retval = get_netcdf_type(grp->nc4_info, member_native_typeid,
1927
0
                                              &member_xtype)))
1928
0
                    return retval;
1929
1930
                /* Add this member to our list of fields in this compound type. */
1931
0
                if ((retval = nc4_field_list_add(type, member_name, member_offset,
1932
0
                                                 member_xtype, 0, NULL)))
1933
0
                    return retval;
1934
0
            }
1935
1936
0
            hdf5free(member_name);
1937
0
        }
1938
0
    }
1939
0
    break;
1940
1941
0
    case H5T_VLEN:
1942
0
    {
1943
0
        htri_t ret;
1944
1945
        /* For conveninence we allow user to pass vlens of strings
1946
         * with null terminated strings. This means strings are
1947
         * treated slightly differently by the API, although they are
1948
         * really just VLENs of characters. */
1949
0
        if ((ret = H5Tis_variable_str(hdf_typeid)) < 0)
1950
0
            return NC_EHDFERR;
1951
0
        if (ret)
1952
0
            type->nc_type_class = NC_STRING;
1953
0
        else
1954
0
        {
1955
0
            hid_t base_hdf_typeid;
1956
0
            nc_type base_nc_type = NC_NAT;
1957
1958
0
            type->nc_type_class = NC_VLEN;
1959
1960
            /* Find the base type of this vlen (i.e. what is this a
1961
             * vlen of?) */
1962
0
            if (!(base_hdf_typeid = H5Tget_super(native_typeid)))
1963
0
                return NC_EHDFERR;
1964
1965
            /* What size is this type? */
1966
0
            if (!(type_size = H5Tget_size(base_hdf_typeid)))
1967
0
                return NC_EHDFERR;
1968
1969
            /* What is the netcdf corresponding type. */
1970
0
            if ((retval = get_netcdf_type(grp->nc4_info, base_hdf_typeid,
1971
0
                                          &base_nc_type)))
1972
0
                return retval;
1973
0
            LOG((5, "base_hdf_typeid 0x%x type_size %d base_nc_type %d",
1974
0
                 base_hdf_typeid, type_size, base_nc_type));
1975
1976
            /* Remember the base type for this vlen. */
1977
0
            type->u.v.base_nc_typeid = base_nc_type;
1978
0
        }
1979
0
    }
1980
0
    break;
1981
1982
0
    case H5T_OPAQUE:
1983
0
        type->nc_type_class = NC_OPAQUE;
1984
0
        break;
1985
1986
0
    case H5T_ENUM:
1987
0
    {
1988
0
        hid_t base_hdf_typeid;
1989
0
        nc_type base_nc_type = NC_NAT;
1990
0
        void *value;
1991
0
        int i;
1992
0
        char *member_name = NULL;
1993
#ifdef JNA
1994
        char jna[1001];
1995
#endif
1996
1997
0
        type->nc_type_class = NC_ENUM;
1998
1999
        /* Find the base type of this enum (i.e. what is this a
2000
         * enum of?) */
2001
0
        if (!(base_hdf_typeid = H5Tget_super(hdf_typeid)))
2002
0
            return NC_EHDFERR;
2003
        /* What size is this type? */
2004
0
        if (!(type_size = H5Tget_size(base_hdf_typeid)))
2005
0
            return NC_EHDFERR;
2006
        /* What is the netcdf corresponding type. */
2007
0
        if ((retval = get_netcdf_type(grp->nc4_info, base_hdf_typeid,
2008
0
                                      &base_nc_type)))
2009
0
            return retval;
2010
0
        LOG((5, "base_hdf_typeid 0x%x type_size %d base_nc_type %d",
2011
0
             base_hdf_typeid, type_size, base_nc_type));
2012
2013
        /* Remember the base type for this enum. */
2014
0
        type->u.e.base_nc_typeid = base_nc_type;
2015
2016
        /* Find out how many member are in the enum. */
2017
0
        if ((nmembers = H5Tget_nmembers(hdf_typeid)) < 0)
2018
0
            return NC_EHDFERR;
2019
0
        type->u.e.enum_member = nclistnew();
2020
0
        nclistsetalloc(type->u.e.enum_member,nmembers);
2021
2022
        /* Allocate space for one value. */
2023
0
        if (!(value = calloc(1, type_size)))
2024
0
            return NC_ENOMEM;
2025
2026
        /* Read each name and value defined in the enum. */
2027
0
        for (i = 0; i < nmembers; i++)
2028
0
        {
2029
            /* Get the name and value from HDF5. */
2030
0
            if (!(member_name = H5Tget_member_name(hdf_typeid, i)))
2031
0
                return NC_EHDFERR;
2032
2033
#ifdef JNA
2034
            strncpy(jna,member_name,1000);
2035
            member_name = jna;
2036
#endif
2037
2038
0
            if (strlen(member_name) > NC_MAX_NAME)
2039
0
                return NC_EBADNAME;
2040
2041
0
            if (H5Tget_member_value(hdf_typeid, i, value) < 0)
2042
0
                return NC_EHDFERR;
2043
2044
            /* Insert new field into this type's list of fields. */
2045
0
            if ((retval = nc4_enum_member_add(type, type->size,
2046
0
                                              member_name, value)))
2047
0
                return retval;
2048
2049
0
            hdf5free(member_name);
2050
0
        }
2051
0
        free(value);
2052
0
    }
2053
0
    break;
2054
2055
0
    default:
2056
0
        LOG((0, "unknown class"));
2057
0
        return NC_EBADCLASS;
2058
0
    }
2059
0
    return retval;
2060
0
}
2061
2062
/**
2063
 * @internal Callback function for reading attributes. This is used
2064
 * for both global and variable attributes.
2065
 *
2066
 * @param loc_id HDF5 attribute ID.
2067
 * @param att_name Name of the attrigute.
2068
 * @param ainfo HDF5 info struct for attribute.
2069
 * @param att_data Pointer to an att_iter_info struct, which contains
2070
 * pointers to the NC_GRP_INFO_T and (for variable attributes) the
2071
 * NC_VAR_INFO_T. For global atts the var pointer is NULL.
2072
 *
2073
 * @return ::NC_NOERR No error. Iteration continues.
2074
 * @return ::-1 Error. Stop iteration.
2075
 * @author Ed Hartnett
2076
 */
2077
static herr_t
2078
att_read_callbk(hid_t loc_id, const char *att_name, const H5A_info_t *ainfo,
2079
                void *att_data)
2080
111k
{
2081
2082
111k
    hid_t attid = 0;
2083
111k
    NC_ATT_INFO_T *att;
2084
111k
    NCindex *list;
2085
111k
    att_iter_info *att_info = (att_iter_info *)att_data;
2086
111k
    int retval = NC_NOERR;
2087
2088
    /* Determine what list is being added to. */
2089
111k
    list = att_info->var ? att_info->var->att : att_info->grp->att;
2090
2091
    /* This may be an attribute telling us that strict netcdf-3 rules
2092
     * are in effect. If so, we will make note of the fact, but not add
2093
     * this attribute to the metadata. It's not a user attribute, but
2094
     * an internal netcdf-4 one. */
2095
111k
    if (!strcmp(att_name, NC3_STRICT_ATT_NAME))
2096
2.12k
    {
2097
        /* Only relevant for groups, not vars. */
2098
2.12k
        if (!att_info->var)
2099
2.12k
            att_info->grp->nc4_info->cmode |= NC_CLASSIC_MODEL;
2100
2.12k
        return NC_NOERR;
2101
2.12k
    }
2102
2103
    /* Should we ignore this attribute? */
2104
109k
    if (NC_findreserved(att_name))
2105
49.6k
        return NC_NOERR;
2106
2107
    /* Add to the end of the list of atts for this var. */
2108
60.0k
    if ((retval = nc4_att_list_add(list, att_name, &att)))
2109
0
        BAIL(-1);
2110
2111
    /* Allocate storage for the HDF5 specific att info. */
2112
60.0k
    if (!(att->format_att_info = calloc(1, sizeof(NC_HDF5_ATT_INFO_T))))
2113
0
        BAIL(-1);
2114
2115
    /* Open the att by name. */
2116
60.0k
    if ((attid = H5Aopen(loc_id, att_name, H5P_DEFAULT)) < 0)
2117
0
        BAIL(-1);
2118
60.0k
    LOG((4, "%s::  att_name %s", __func__, att_name));
2119
2120
    /* Read the rest of the info about the att,
2121
     * including its values. */
2122
60.0k
    if ((retval = read_hdf5_att(att_info->grp, attid, att)))
2123
0
        BAIL(retval);
2124
2125
60.0k
    if (att)
2126
60.0k
        att->created = NC_TRUE;
2127
2128
60.0k
exit:
2129
60.0k
    if (retval == NC_EBADTYPID)
2130
0
    {
2131
        /* NC_EBADTYPID will be normally converted to NC_NOERR so that
2132
           the parent iterator does not fail. */
2133
0
        retval = nc4_att_list_del(list, att);
2134
0
        att = NULL;
2135
0
    }
2136
60.0k
    if (attid > 0 && H5Aclose(attid) < 0)
2137
0
        retval = -1;
2138
2139
    /* Since this is a HDF5 iterator callback, return -1 for any error
2140
     * to stop iteration. */
2141
60.0k
    if (retval)
2142
0
        retval = -1;
2143
60.0k
    return retval;
2144
60.0k
}
2145
2146
/**
2147
 * @internal This function reads all the attributes of a variable or
2148
 * the global attributes of a group.
2149
 *
2150
 * @param grp Pointer to the group info.
2151
 * @param var Pointer to the var info. NULL for global att reads.
2152
 *
2153
 * @return ::NC_NOERR No error.
2154
 * @return ::NC_EATTMETA Some error occurred reading attributes.
2155
 * @author Ed Hartnett
2156
 */
2157
int
2158
nc4_read_atts(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var)
2159
73.7k
{
2160
73.7k
    att_iter_info att_info;         /* Custom iteration information */
2161
73.7k
    hid_t locid; /* HDF5 location to read atts from. */
2162
2163
    /* Check inputs. */
2164
73.7k
    assert(grp);
2165
2166
    /* Assign var and grp in struct. (var may be NULL). */
2167
73.7k
    att_info.var = var;
2168
73.7k
    att_info.grp = grp;
2169
2170
    /* Determine where to read from in the HDF5 file. */
2171
73.7k
    locid = var ? ((NC_HDF5_VAR_INFO_T *)(var->format_var_info))->hdf_datasetid :
2172
73.7k
        ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid;
2173
2174
    /* Now read all the attributes at this location, ignoring special
2175
     * netCDF hidden attributes. */
2176
73.7k
    if ((H5Aiterate2(locid, H5_INDEX_CRT_ORDER, H5_ITER_INC, NULL,
2177
73.7k
                     att_read_callbk, &att_info)) < 0)
2178
0
        return NC_EATTMETA;
2179
2180
    /* Remember that we have read the atts for this var or group. */
2181
73.7k
    if (var)
2182
67.7k
        var->atts_read = 1;
2183
5.94k
    else
2184
5.94k
        grp->atts_read = 1;
2185
2186
73.7k
    return NC_NOERR;
2187
73.7k
}
2188
2189
/**
2190
 * @internal This function is called by read_dataset() when a
2191
 * dimension scale dataset is encountered. It reads in the dimension
2192
 * data (creating a new NC_DIM_INFO_T object), and also checks to see
2193
 * if this is a dimension without a variable - that is, a coordinate
2194
 * dimension which does not have any coordinate data.
2195
 *
2196
 * @param grp Pointer to group info struct.
2197
 * @param datasetid The HDF5 dataset ID.
2198
 * @param obj_name The HDF5 object name.
2199
 * @param statbuf HDF5 status buffer.
2200
 * @param scale_size Size of dimension scale.
2201
 * @param max_scale_size Maximum size of dim scale.
2202
 * @param dim Pointer to pointer that gets new dim info struct.
2203
 *
2204
 * @returns ::NC_NOERR No error.
2205
 * @return ::NC_EHDFERR HDF5 returned error.
2206
 * @author Ed Hartnett
2207
 */
2208
static int
2209
read_scale(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name,
2210
           const H5G_stat_t *statbuf, hsize_t scale_size,
2211
           hsize_t max_scale_size, NC_DIM_INFO_T **dim)
2212
29.7k
{
2213
29.7k
    NC_DIM_INFO_T *new_dim; /* Dimension added to group */
2214
29.7k
    NC_HDF5_DIM_INFO_T *new_hdf5_dim; /* HDF5-specific dim info. */
2215
29.7k
    char dimscale_name_att[NC_MAX_NAME + 1] = "";    /* Dimscale name, for checking if dim without var */
2216
29.7k
    htri_t attr_exists = -1; /* Flag indicating hidden attribute exists */
2217
29.7k
    hid_t attid = -1; /* ID of hidden attribute (to store dim ID) */
2218
29.7k
    int dimscale_created = 0; /* Remember if a dimension was created (for error recovery) */
2219
29.7k
    short initial_next_dimid = grp->nc4_info->next_dimid;/* Retain for error recovery */
2220
29.7k
    size_t len = 0;
2221
29.7k
    int too_long = NC_FALSE;
2222
29.7k
    int assigned_id = -1;
2223
29.7k
    int retval = NC_NOERR;
2224
2225
29.7k
    assert(grp && dim);
2226
2227
    /* Does this dataset have a hidden attribute that tells us its
2228
     * dimid? If so, read it. */
2229
29.7k
    if ((attr_exists = H5Aexists(datasetid, NC_DIMID_ATT_NAME)) < 0)
2230
0
        BAIL(NC_EHDFERR);
2231
29.7k
    if (attr_exists)
2232
29.7k
    {
2233
29.7k
        if ((attid = H5Aopen_name(datasetid, NC_DIMID_ATT_NAME)) < 0)
2234
0
            BAIL(NC_EHDFERR);
2235
2236
29.7k
        if (H5Aread(attid, H5T_NATIVE_INT, &assigned_id) < 0)
2237
0
            BAIL(NC_EHDFERR);
2238
2239
        /* Check if scale's dimid should impact the group's next dimid */
2240
29.7k
        if (assigned_id >= grp->nc4_info->next_dimid)
2241
29.0k
            grp->nc4_info->next_dimid = assigned_id + 1;
2242
29.7k
    }
2243
2244
    /* Get dim size. On machines with a size_t of less than 8 bytes, it
2245
     * is possible for a dimension to be too long. */
2246
29.7k
    if (SIZEOF_SIZE_T < 8 && scale_size > NC_MAX_UINT)
2247
0
    {
2248
0
        len = NC_MAX_UINT;
2249
0
        too_long = NC_TRUE;
2250
0
    }
2251
29.7k
    else
2252
29.7k
        len = scale_size;
2253
2254
    /* Create the dimension for this scale. */
2255
29.7k
    if ((retval = nc4_dim_list_add(grp, obj_name, len, assigned_id, &new_dim)))
2256
0
        BAIL(retval);
2257
29.7k
    new_dim->too_long = too_long;
2258
2259
    /* Create struct for HDF5-specific dim info. */
2260
29.7k
    if (!(new_dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T))))
2261
0
        BAIL(NC_ENOMEM);
2262
29.7k
    new_hdf5_dim = (NC_HDF5_DIM_INFO_T *)new_dim->format_dim_info;
2263
2264
29.7k
    dimscale_created++;
2265
2266
    /* Remember these 4 values to uniquely identify this dataset in the
2267
     * HDF5 file. */
2268
29.7k
    new_hdf5_dim->hdf5_objid.fileno[0] = statbuf->fileno[0];
2269
29.7k
    new_hdf5_dim->hdf5_objid.fileno[1] = statbuf->fileno[1];
2270
29.7k
    new_hdf5_dim->hdf5_objid.objno[0] = statbuf->objno[0];
2271
29.7k
    new_hdf5_dim->hdf5_objid.objno[1] = statbuf->objno[1];
2272
2273
    /* If the dimscale has an unlimited dimension, then this dimension
2274
     * is unlimited. */
2275
29.7k
    if (max_scale_size == H5S_UNLIMITED)
2276
17.1k
        new_dim->unlimited = NC_TRUE;
2277
2278
    /* If the scale name is set to DIM_WITHOUT_VARIABLE, then this is a
2279
     * dimension, but not a variable. (If get_scale_name returns an
2280
     * error, just move on, there's no NAME.) */
2281
29.7k
    if (H5DSget_scale_name(datasetid, dimscale_name_att, NC_MAX_NAME) >= 0)
2282
29.7k
    {
2283
29.7k
        if (!strncmp(dimscale_name_att, DIM_WITHOUT_VARIABLE,
2284
29.7k
                     strlen(DIM_WITHOUT_VARIABLE)))
2285
28.3k
        {
2286
28.3k
            if (new_dim->unlimited)
2287
16.6k
            {
2288
16.6k
                size_t len = 0, *lenp = &len;
2289
2290
                /* Find actual length by checking all datasets that use
2291
                 * this dim. */
2292
16.6k
                if ((retval = nc4_find_dim_len(grp, new_dim->hdr.id, &lenp)))
2293
0
                    BAIL(retval);
2294
16.6k
                new_dim->len = *lenp;
2295
16.6k
            }
2296
2297
            /* Hold open the dataset, since the dimension doesn't have a
2298
             * coordinate variable */
2299
28.3k
            new_hdf5_dim->hdf_dimscaleid = datasetid;
2300
28.3k
            H5Iinc_ref(new_hdf5_dim->hdf_dimscaleid);        /* Increment number of objects using ID */
2301
28.3k
        }
2302
29.7k
    }
2303
2304
    /* Set the dimension created. */
2305
29.7k
    *dim = new_dim;
2306
2307
29.7k
exit:
2308
    /* Close the hidden attribute, if it was opened. */
2309
29.7k
    if (attid > 0 && H5Aclose(attid) < 0)
2310
0
        BAIL2(NC_EHDFERR);
2311
2312
    /* On error, undo any dimscale creation */
2313
29.7k
    if (retval && dimscale_created)
2314
0
    {
2315
        /* free the dimension */
2316
0
        if ((retval = nc4_dim_list_del(grp, new_dim)))
2317
0
            BAIL2(retval);
2318
2319
        /* Reset the group's information */
2320
0
        grp->nc4_info->next_dimid = initial_next_dimid;
2321
0
    }
2322
2323
29.7k
    return retval;
2324
29.7k
}
2325
2326
/**
2327
 * @internal Read a HDF5 dataset. This function is called when
2328
 * read_hdf5_obj() encounters an HDF5 dataset when opening a file.
2329
 *
2330
 * @param grp Pointer to group info struct.
2331
 * @param datasetid HDF5 dataset ID.
2332
 * @param obj_name Object name.
2333
 * @param statbuf HDF5 status buffer.
2334
 *
2335
 * @return ::NC_NOERR No error.
2336
 * @return ::NC_EBADID Bad ncid.
2337
 * @return ::NC_EHDFERR HDF5 returned error.
2338
 * @author Ed Hartnett
2339
 */
2340
static int
2341
read_dataset(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name,
2342
             const H5G_stat_t *statbuf)
2343
96.0k
{
2344
96.0k
    NC_DIM_INFO_T *dim = NULL;   /* Dimension created for scales */
2345
96.0k
    NC_HDF5_DIM_INFO_T *hdf5_dim;
2346
96.0k
    hid_t spaceid = 0;
2347
96.0k
    int ndims;
2348
96.0k
    htri_t is_scale;
2349
96.0k
    int retval = NC_NOERR;
2350
2351
    /* Get the dimension information for this dataset. */
2352
96.0k
    if ((spaceid = H5Dget_space(datasetid)) < 0)
2353
0
        BAIL(NC_EHDFERR);
2354
96.0k
    if ((ndims = H5Sget_simple_extent_ndims(spaceid)) < 0)
2355
0
        BAIL(NC_EHDFERR);
2356
2357
    /* Is this a dimscale? */
2358
96.0k
    if ((is_scale = H5DSis_scale(datasetid)) < 0)
2359
0
        BAIL(NC_EHDFERR);
2360
96.0k
    if (is_scale)
2361
29.7k
    {
2362
29.7k
        hsize_t dims[H5S_MAX_RANK];
2363
29.7k
        hsize_t max_dims[H5S_MAX_RANK];
2364
2365
        /* Query the scale's size & max. size */
2366
29.7k
        if (H5Sget_simple_extent_dims(spaceid, dims, max_dims) < 0)
2367
0
            BAIL(NC_EHDFERR);
2368
2369
        /* Read the scale information. */
2370
29.7k
        if ((retval = read_scale(grp, datasetid, obj_name, statbuf, dims[0],
2371
29.7k
                                 max_dims[0], &dim)))
2372
0
            BAIL(retval);
2373
29.7k
        hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
2374
29.7k
    }
2375
2376
    /* Add a var to the linked list, and get its metadata,
2377
     * unless this is one of those funny dimscales that are a
2378
     * dimension in netCDF but not a variable. (Spooky!) */
2379
96.0k
    if (!dim || (dim && !hdf5_dim->hdf_dimscaleid))
2380
67.7k
        if ((retval = read_var(grp, datasetid, obj_name, ndims, dim)))
2381
0
            BAIL(retval);
2382
2383
96.0k
exit:
2384
96.0k
    if (spaceid && H5Sclose(spaceid) <0)
2385
0
        BAIL2(retval);
2386
2387
96.0k
    return retval;
2388
96.0k
}
2389
2390
/**
2391
 * @internal Add HDF5 object info for a group to a list for later
2392
 * processing. We do this when we encounter groups, so that the parent
2393
 * group can be fully processed before the child groups.
2394
 *
2395
 * @param udata Pointer to the user data, in this case a
2396
 * user_data_t.
2397
 * @param oinfo The HDF5 object info.
2398
 *
2399
 * @return ::NC_NOERR No error.
2400
 * @return ::NC_ENOMEM Out of memory.
2401
 * @author Ed Hartnett, Dennis Heimbigner
2402
 */
2403
static int
2404
oinfo_list_add(user_data_t *udata, const hdf5_obj_info_t *oinfo)
2405
0
{
2406
0
    hdf5_obj_info_t *new_oinfo;    /* Pointer to info for object */
2407
2408
    /* Allocate memory for the object's info. */
2409
0
    if (!(new_oinfo = calloc(1, sizeof(hdf5_obj_info_t))))
2410
0
        return NC_ENOMEM;
2411
2412
    /* Make a copy of the object's info. */
2413
0
    memcpy(new_oinfo, oinfo, sizeof(hdf5_obj_info_t));
2414
2415
    /* Add it to the list for future processing. */
2416
0
    nclistpush(udata->grps, new_oinfo);
2417
2418
0
    return NC_NOERR;
2419
0
}
2420
2421
/**
2422
 * @internal Callback function called by H5Literate() for every HDF5
2423
 * object in the file.
2424
 *
2425
 * @note This function is called by HDF5 so does not return a netCDF
2426
 * error code.
2427
 *
2428
 * @param grpid HDF5 group ID.
2429
 * @param name Name of object.
2430
 * @param info Info struct for object.
2431
 * @param _op_data Pointer to user data, a user_data_t. It will
2432
 * contain a pointer to the current group and a list of
2433
 * hdf5_obj_info_t. Any child groups will get their hdf5_obj_info
2434
 * added to this list.
2435
 *
2436
 * @return H5_ITER_CONT No error, continue iteration.
2437
 * @return H5_ITER_ERROR HDF5 error, stop iteration.
2438
 * @author Ed Hartnett
2439
 */
2440
static int
2441
read_hdf5_obj(hid_t grpid, const char *name, const H5L_info_t *info,
2442
              void *_op_data)
2443
96.0k
{
2444
    /* Pointer to user data for callback */
2445
96.0k
    user_data_t *udata = (user_data_t *)_op_data;
2446
96.0k
    hdf5_obj_info_t oinfo;    /* Pointer to info for object */
2447
96.0k
    int retval = H5_ITER_CONT;
2448
2449
    /* Open this critter. */
2450
96.0k
    if ((oinfo.oid = H5Oopen(grpid, name, H5P_DEFAULT)) < 0)
2451
0
        BAIL(H5_ITER_ERROR);
2452
2453
    /* Get info about the object.*/
2454
96.0k
    if (H5Gget_objinfo(oinfo.oid, ".", 1, &oinfo.statbuf) < 0)
2455
0
        BAIL(H5_ITER_ERROR);
2456
2457
96.0k
    strncpy(oinfo.oname, name, NC_MAX_NAME);
2458
2459
    /* Add object to list, for later */
2460
96.0k
    switch(oinfo.statbuf.type)
2461
96.0k
    {
2462
0
    case H5G_GROUP:
2463
0
        LOG((3, "found group %s", oinfo.oname));
2464
2465
        /* Defer descending into child group immediately, so that the
2466
         * types in the current group can be processed and be ready for
2467
         * use by vars in the child group(s). */
2468
0
        if (oinfo_list_add(udata, &oinfo))
2469
0
            BAIL(H5_ITER_ERROR);
2470
0
        break;
2471
2472
96.0k
    case H5G_DATASET:
2473
96.0k
        LOG((3, "found dataset %s", oinfo.oname));
2474
2475
        /* Learn all about this dataset, which may be a dimscale
2476
         * (i.e. dimension metadata), or real data. */
2477
96.0k
        if ((retval = read_dataset(udata->grp, oinfo.oid, oinfo.oname,
2478
96.0k
                                   &oinfo.statbuf)))
2479
0
        {
2480
            /* Allow NC_EBADTYPID to transparently skip over datasets
2481
             * which have a datatype that netCDF-4 doesn't understand
2482
             * (currently), but break out of iteration for other
2483
             * errors. */
2484
0
            if (retval != NC_EBADTYPID)
2485
0
                BAIL(H5_ITER_ERROR);
2486
0
            else
2487
0
                retval = H5_ITER_CONT;
2488
0
        }
2489
2490
        /* Close the object */
2491
96.0k
        if (H5Oclose(oinfo.oid) < 0)
2492
0
            BAIL(H5_ITER_ERROR);
2493
96.0k
        break;
2494
2495
96.0k
    case H5G_TYPE:
2496
0
        LOG((3, "found datatype %s", oinfo.oname));
2497
2498
        /* Process the named datatype */
2499
0
        if (read_type(udata->grp, oinfo.oid, oinfo.oname))
2500
0
            BAIL(H5_ITER_ERROR);
2501
2502
        /* Close the object */
2503
0
        if (H5Oclose(oinfo.oid) < 0)
2504
0
            BAIL(H5_ITER_ERROR);
2505
0
        break;
2506
2507
0
    default:
2508
0
        LOG((0, "Unknown object class %d in %s!", oinfo.statbuf.type, __func__));
2509
0
        BAIL(H5_ITER_ERROR);
2510
96.0k
    }
2511
2512
96.0k
exit:
2513
96.0k
    if (retval)
2514
0
    {
2515
0
        if (oinfo.oid > 0 && H5Oclose(oinfo.oid) < 0)
2516
0
            BAIL2(H5_ITER_ERROR);
2517
0
    }
2518
2519
96.0k
    return (retval);
2520
96.0k
}
2521
2522
/**
2523
 * @internal This is the main function to recursively read all the
2524
 * metadata for the file. The links in the 'grp' are iterated over and
2525
 * added to the file's metadata information. Note that child groups
2526
 * are not immediately processed, but are deferred until all the other
2527
 * links in the group are handled (so that vars in the child groups
2528
 * are guaranteed to have types that they use in a parent group in
2529
 * place).
2530
 *
2531
 * @param grp Pointer to a group.
2532
 *
2533
 * @return ::NC_NOERR No error.
2534
 * @return ::NC_EHDFERR HDF5 error.
2535
 * @return ::NC_ENOMEM Out of memory.
2536
 * @return ::NC_ECANTWRITE File must be opened read-only.
2537
 * @author Ed Hartnett, Dennis Heimbigner
2538
 */
2539
static int
2540
rec_read_metadata(NC_GRP_INFO_T *grp)
2541
5.94k
{
2542
5.94k
    NC_HDF5_GRP_INFO_T *hdf5_grp;
2543
5.94k
    user_data_t udata;         /* User data for iteration */
2544
5.94k
    hdf5_obj_info_t *oinfo;    /* Pointer to info for object */
2545
5.94k
    hsize_t idx = 0;
2546
5.94k
    hid_t pid = -1;
2547
5.94k
    unsigned crt_order_flags = 0;
2548
5.94k
    H5_index_t iter_index;
2549
5.94k
    int i, retval = NC_NOERR;
2550
2551
5.94k
    assert(grp && grp->hdr.name && grp->format_grp_info);
2552
5.94k
    LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
2553
2554
    /* Get HDF5-specific group info. */
2555
5.94k
    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
2556
2557
    /* Set user data for iteration over any child groups. */
2558
5.94k
    udata.grp = grp;
2559
5.94k
    udata.grps = nclistnew();
2560
2561
    /* Open this HDF5 group and retain its grpid. It will remain open
2562
     * with HDF5 until this file is nc_closed. */
2563
5.94k
    if (!hdf5_grp->hdf_grpid)
2564
5.94k
    {
2565
5.94k
        if (grp->parent)
2566
0
        {
2567
            /* This is a child group. */
2568
0
            NC_HDF5_GRP_INFO_T *parent_hdf5_grp;
2569
0
            parent_hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->parent->format_grp_info;
2570
2571
0
            if ((hdf5_grp->hdf_grpid = H5Gopen2(parent_hdf5_grp->hdf_grpid,
2572
0
                                                grp->hdr.name, H5P_DEFAULT)) < 0)
2573
0
                BAIL(NC_EHDFERR);
2574
0
        }
2575
5.94k
        else
2576
5.94k
        {
2577
            /* This is the root group. */
2578
5.94k
            NC_HDF5_FILE_INFO_T *hdf5_info;
2579
5.94k
            hdf5_info = (NC_HDF5_FILE_INFO_T *)grp->nc4_info->format_file_info;
2580
2581
5.94k
            if ((hdf5_grp->hdf_grpid = H5Gopen2(hdf5_info->hdfid, "/",
2582
5.94k
                                                H5P_DEFAULT)) < 0)
2583
0
                BAIL(NC_EHDFERR);
2584
5.94k
        }
2585
5.94k
    }
2586
5.94k
    assert(hdf5_grp->hdf_grpid > 0);
2587
2588
    /* Get the group creation flags, to check for creation ordering. */
2589
5.94k
    if ((pid = H5Gget_create_plist(hdf5_grp->hdf_grpid)) < 0)
2590
0
        BAIL(NC_EHDFERR);
2591
5.94k
    if (H5Pget_link_creation_order(pid, &crt_order_flags) < 0)
2592
0
        BAIL(NC_EHDFERR);
2593
2594
    /* Set the iteration index to use. */
2595
5.94k
    if (crt_order_flags & H5P_CRT_ORDER_TRACKED)
2596
5.94k
        iter_index = H5_INDEX_CRT_ORDER;
2597
0
    else
2598
0
    {
2599
0
        NC_FILE_INFO_T *h5 = grp->nc4_info;
2600
2601
        /* Without creation ordering, file must be read-only. */
2602
0
        if (!h5->no_write)
2603
0
            BAIL(NC_ECANTWRITE);
2604
2605
0
        iter_index = H5_INDEX_NAME;
2606
0
    }
2607
2608
    /* Iterate over links in this group, building lists for the types,
2609
     * datasets and groups encountered. A pointer to udata will be
2610
     * passed as a parameter to the callback function
2611
     * read_hdf5_obj(). (I have also tried H5Oiterate(), but it is much
2612
     * slower iterating over the same file - Ed.) */
2613
5.94k
    if (H5Literate(hdf5_grp->hdf_grpid, iter_index, H5_ITER_INC, &idx,
2614
5.94k
                   read_hdf5_obj, (void *)&udata) < 0)
2615
0
        BAIL(NC_EHDFERR);
2616
2617
    /* Process the child groups found. (Deferred until now, so that the
2618
     * types in the current group get processed and are available for
2619
     * vars in the child group(s).) */
2620
5.94k
    for (i = 0; i < nclistlength(udata.grps); i++)
2621
0
    {
2622
0
        NC_GRP_INFO_T *child_grp;
2623
0
        oinfo = (hdf5_obj_info_t*)nclistget(udata.grps, i);
2624
2625
        /* Add group to file's hierarchy. */
2626
0
        if ((retval = nc4_grp_list_add(grp->nc4_info, grp, oinfo->oname,
2627
0
                                       &child_grp)))
2628
0
            BAIL(retval);
2629
2630
        /* Allocate storage for HDF5-specific group info. */
2631
0
        if (!(child_grp->format_grp_info = calloc(1, sizeof(NC_HDF5_GRP_INFO_T))))
2632
0
            return NC_ENOMEM;
2633
2634
        /* Recursively read the child group's metadata. */
2635
0
        if ((retval = rec_read_metadata(child_grp)))
2636
0
            BAIL(retval);
2637
0
    }
2638
2639
    /* When reading existing file, mark all variables as written. */
2640
73.7k
    for (i = 0; i < ncindexsize(grp->vars); i++)
2641
67.7k
        ((NC_VAR_INFO_T *)ncindexith(grp->vars, i))->written_to = NC_TRUE;
2642
2643
5.94k
exit:
2644
5.94k
    if (pid > 0 && H5Pclose(pid) < 0)
2645
0
        BAIL2(NC_EHDFERR);
2646
2647
    /* Clean up list of child groups. */
2648
5.94k
    for (i = 0; i < nclistlength(udata.grps); i++)
2649
0
    {
2650
0
        oinfo = (hdf5_obj_info_t *)nclistget(udata.grps, i);
2651
        /* Close the open HDF5 object. */
2652
0
        if (H5Oclose(oinfo->oid) < 0)
2653
0
            BAIL2(NC_EHDFERR);
2654
0
        free(oinfo);
2655
0
    }
2656
5.94k
    nclistfree(udata.grps);
2657
2658
5.94k
    return retval;
2659
5.94k
}