Coverage Report

Created: 2025-12-03 08:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/netcdf-c-4.7.4/libhdf5/hdf5file.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 The netCDF-4 file functions.
7
 *
8
 * This file is part of netcdf-4, a netCDF-like interface for HDF5, or
9
 * a HDF5 backend for netCDF, depending on your point of view.
10
 *
11
 * @author Ed Hartnett
12
 */
13
14
#include "config.h"
15
#include "hdf5internal.h"
16
#include "ncrc.h"
17
18
extern int NC4_extract_file_image(NC_FILE_INFO_T* h5); /* In nc4memcb.c */
19
20
static void dumpopenobjects(NC_FILE_INFO_T* h5);
21
22
/** @internal When we have open objects at file close, should
23
    we log them or print to stdout. Default is to log. */
24
#define LOGOPEN 1
25
26
/** @internal Number of reserved attributes. These attributes are
27
 * hidden from the netcdf user, but exist in the HDF5 file to help
28
 * netcdf read the file. */
29
340k
#define NRESERVED 11 /*|NC_reservedatt|*/
30
31
/** @internal List of reserved attributes. This list must be in sorted
32
 * order for binary search. */
33
static const NC_reservedatt NC_reserved[NRESERVED] = {
34
    {NC_ATT_CLASS, READONLYFLAG|DIMSCALEFLAG},            /*CLASS*/
35
    {NC_ATT_DIMENSION_LIST, READONLYFLAG|DIMSCALEFLAG},   /*DIMENSION_LIST*/
36
    {NC_ATT_NAME, READONLYFLAG|DIMSCALEFLAG},             /*NAME*/
37
    {NC_ATT_REFERENCE_LIST, READONLYFLAG|DIMSCALEFLAG},   /*REFERENCE_LIST*/
38
    {NC_ATT_FORMAT, READONLYFLAG},                        /*_Format*/
39
    {ISNETCDF4ATT, READONLYFLAG|NAMEONLYFLAG},            /*_IsNetcdf4*/
40
    {NCPROPS, READONLYFLAG|NAMEONLYFLAG|MATERIALIZEDFLAG},/*_NCProperties*/
41
    {NC_ATT_COORDINATES, READONLYFLAG|DIMSCALEFLAG|MATERIALIZEDFLAG},/*_Netcdf4Coordinates*/
42
    {NC_DIMID_ATT_NAME, READONLYFLAG|DIMSCALEFLAG|MATERIALIZEDFLAG},/*_Netcdf4Dimid*/
43
    {SUPERBLOCKATT, READONLYFLAG|NAMEONLYFLAG},/*_SuperblockVersion*/
44
    {NC3_STRICT_ATT_NAME, READONLYFLAG|MATERIALIZEDFLAG},  /*_nc3_strict*/
45
};
46
47
/* Forward */
48
static int NC4_enddef(int ncid);
49
static void dumpopenobjects(NC_FILE_INFO_T* h5);
50
51
/**
52
 * @internal Define a binary searcher for reserved attributes
53
 * @param name for which to search
54
 * @return pointer to the matchig NC_reservedatt structure.
55
 * @return NULL if not found.
56
 * @author Dennis Heimbigner
57
 */
58
const NC_reservedatt*
59
NC_findreserved(const char* name)
60
340k
{
61
340k
    int n = NRESERVED;
62
340k
    int L = 0;
63
340k
    int R = (n - 1);
64
1.55M
    for(;;) {
65
1.55M
        if(L > R) break;
66
1.27M
        int m = (L + R) / 2;
67
1.27M
        const NC_reservedatt* p = &NC_reserved[m];
68
1.27M
        int cmp = strcmp(p->name,name);
69
1.27M
        if(cmp == 0) return p;
70
1.21M
        if(cmp < 0)
71
650k
            L = (m + 1);
72
560k
        else /*cmp > 0*/
73
560k
            R = (m - 1);
74
1.21M
    }
75
279k
    return NULL;
76
340k
}
77
78
/**
79
 * @internal Recursively determine if there is a mismatch between
80
 * order of coordinate creation and associated dimensions in this
81
 * group or any subgroups, to find out if we have to handle that
82
 * situation.  Also check if there are any multidimensional coordinate
83
 * variables defined, which require the same treatment to fix a
84
 * potential bug when such variables occur in subgroups.
85
 *
86
 * @param grp Pointer to group info struct.
87
 * @param bad_coord_orderp Pointer that gets 1 if there is a bad
88
 * coordinate order.
89
 *
90
 * @returns NC_NOERR No error.
91
 * @returns NC_EHDFERR HDF5 returned an error.
92
 * @author Ed Hartnett
93
 */
94
static int
95
detect_preserve_dimids(NC_GRP_INFO_T *grp, nc_bool_t *bad_coord_orderp)
96
7.56k
{
97
7.56k
    NC_VAR_INFO_T *var;
98
7.56k
    NC_GRP_INFO_T *child_grp;
99
7.56k
    int last_dimid = -1;
100
7.56k
    int retval;
101
7.56k
    int i;
102
103
    /* Iterate over variables in this group */
104
67.4k
    for (i=0; i < ncindexsize(grp->vars); i++)
105
61.3k
    {
106
61.3k
        var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
107
61.3k
        if (var == NULL) continue;
108
        /* Only matters for dimension scale variables, with non-scalar dimensionality */
109
61.3k
        if (var->dimscale && var->ndims)
110
1.91k
        {
111
            /* If the user writes coord vars in a different order then he
112
             * defined their dimensions, then, when the file is reopened, the
113
             * order of the dimids will change to match the order of the coord
114
             * vars. Detect if this is about to happen. */
115
1.91k
            if (var->dimids[0] < last_dimid)
116
37
            {
117
37
                LOG((5, "%s: %s is out of order coord var", __func__, var->hdr.name));
118
37
                *bad_coord_orderp = NC_TRUE;
119
37
                return NC_NOERR;
120
37
            }
121
1.87k
            last_dimid = var->dimids[0];
122
123
            /* If there are multidimensional coordinate variables defined, then
124
             * it's also necessary to preserve dimension IDs when the file is
125
             * reopened ... */
126
1.87k
            if (var->ndims > 1)
127
701
            {
128
701
                LOG((5, "%s: %s is multidimensional coord var", __func__, var->hdr.name));
129
701
                *bad_coord_orderp = NC_TRUE;
130
701
                return NC_NOERR;
131
701
            }
132
133
            /* Did the user define a dimension, end define mode, reenter define
134
             * mode, and then define a coordinate variable for that dimension?
135
             * If so, dimensions will be out of order. */
136
1.17k
            if (var->is_new_var || var->became_coord_var)
137
747
            {
138
747
                LOG((5, "%s: coord var defined after enddef/redef", __func__));
139
747
                *bad_coord_orderp = NC_TRUE;
140
747
                return NC_NOERR;
141
747
            }
142
1.17k
        }
143
61.3k
    }
144
145
    /* If there are any child groups, check them also for this condition. */
146
6.07k
    for (i = 0; i < ncindexsize(grp->children); i++)
147
0
    {
148
0
        if (!(child_grp = (NC_GRP_INFO_T *)ncindexith(grp->children, i)))
149
0
            continue;
150
0
        if ((retval = detect_preserve_dimids(child_grp, bad_coord_orderp)))
151
0
            return retval;
152
0
    }
153
6.07k
    return NC_NOERR;
154
6.07k
}
155
156
/**
157
 * @internal This function will write all changed metadata and flush
158
 * HDF5 file to disk.
159
 *
160
 * @param h5 Pointer to HDF5 file info struct.
161
 *
162
 * @return ::NC_NOERR No error.
163
 * @return ::NC_EINDEFINE Classic model file in define mode.
164
 * @return ::NC_EHDFERR HDF5 error.
165
 * @author Ed Hartnett
166
 */
167
static int
168
sync_netcdf4_file(NC_FILE_INFO_T *h5)
169
7.56k
{
170
7.56k
    NC_HDF5_FILE_INFO_T *hdf5_info;
171
7.56k
    int retval;
172
173
7.56k
    assert(h5 && h5->format_file_info);
174
7.56k
    LOG((3, "%s", __func__));
175
176
    /* If we're in define mode, that's an error, for strict nc3 rules,
177
     * otherwise, end define mode. */
178
7.56k
    if (h5->flags & NC_INDEF)
179
0
    {
180
0
        if (h5->cmode & NC_CLASSIC_MODEL)
181
0
            return NC_EINDEFINE;
182
183
        /* Turn define mode off. */
184
0
        h5->flags ^= NC_INDEF;
185
186
        /* Redef mode needs to be tracked separately for nc_abort. */
187
0
        h5->redef = NC_FALSE;
188
0
    }
189
190
#ifdef LOGGING
191
    /* This will print out the names, types, lens, etc of the vars and
192
       atts in the file, if the logging level is 2 or greater. */
193
    log_metadata_nc(h5);
194
#endif
195
196
    /* Write any metadata that has changed. */
197
7.56k
    if (!h5->no_write)
198
7.56k
    {
199
7.56k
        nc_bool_t bad_coord_order = NC_FALSE;
200
201
        /* Write any user-defined types. */
202
7.56k
        if ((retval = nc4_rec_write_groups_types(h5->root_grp)))
203
0
            return retval;
204
205
        /* Check to see if the coordinate order is messed up. If
206
         * detected, propagate to all groups to consistently store
207
         * dimids. */
208
7.56k
        if ((retval = detect_preserve_dimids(h5->root_grp, &bad_coord_order)))
209
0
            return retval;
210
211
        /* Write all the metadata. */
212
7.56k
        if ((retval = nc4_rec_write_metadata(h5->root_grp, bad_coord_order)))
213
0
            return retval;
214
215
        /* Write out provenance*/
216
7.56k
        if((retval = NC4_write_provenance(h5)))
217
0
            return retval;
218
7.56k
    }
219
220
    /* Tell HDF5 to flush all changes to the file. */
221
7.56k
    hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
222
7.56k
    if (H5Fflush(hdf5_info->hdfid, H5F_SCOPE_GLOBAL) < 0)
223
0
        return NC_EHDFERR;
224
225
7.56k
    return NC_NOERR;
226
7.56k
}
227
228
/**
229
 * @internal This function will free all allocated metadata memory,
230
 * and close the HDF5 file. The group that is passed in must be the
231
 * root group of the file. If inmemory is used, then save
232
 * the final memory in mem.memio.
233
 *
234
 * @param h5 Pointer to HDF5 file info struct.
235
 * @param abort True if this is an abort.
236
 * @param memio the place to return a core image if not NULL
237
 *
238
 * @return ::NC_NOERR No error.
239
 * @return ::NC_EHDFERR HDF5 could not close the file.
240
 * @return ::NC_EINDEFINE Classic model file is in define mode.
241
 * @author Ed Hartnett, Dennis Heimbigner
242
 */
243
int
244
nc4_close_netcdf4_file(NC_FILE_INFO_T *h5, int abort, NC_memio *memio)
245
12.5k
{
246
12.5k
    NC_HDF5_FILE_INFO_T *hdf5_info;
247
12.5k
    int retval;
248
249
12.5k
    assert(h5 && h5->root_grp && h5->format_file_info);
250
12.5k
    LOG((3, "%s: h5->path %s abort %d", __func__, h5->controller->path, abort));
251
252
    /* Get HDF5 specific info. */
253
12.5k
    hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
254
255
#ifdef USE_PARALLEL4
256
    /* Free the MPI Comm & Info objects, if we opened the file in
257
     * parallel. */
258
    if (h5->parallel)
259
    {
260
        if (h5->comm != MPI_COMM_NULL)
261
            MPI_Comm_free(&h5->comm);
262
        if (h5->info != MPI_INFO_NULL)
263
            MPI_Info_free(&h5->info);
264
    }
265
#endif
266
267
    /* Free the fileinfo struct, which holds info from the fileinfo
268
     * hidden attribute. */
269
12.5k
    NC4_clear_provenance(&h5->provenance);
270
271
    /* Close hdf file. It may not be open, since this function is also
272
     * called by NC_create() when a file opening is aborted. */
273
12.5k
    if (hdf5_info->hdfid > 0 && H5Fclose(hdf5_info->hdfid) < 0)
274
0
    {
275
0
        dumpopenobjects(h5);
276
0
        return NC_EHDFERR;
277
0
    }
278
279
    /* If inmemory is used and user wants the final memory block,
280
       then capture and return the final memory block else free it */
281
12.5k
    if (h5->mem.inmemory)
282
0
    {
283
        /* Pull out the final memory */
284
0
        (void)NC4_extract_file_image(h5);
285
0
        if (!abort && memio != NULL)
286
0
        {
287
0
            *memio = h5->mem.memio; /* capture it */
288
0
            h5->mem.memio.memory = NULL; /* avoid duplicate free */
289
0
        }
290
        /* If needed, reclaim extraneous memory */
291
0
        if (h5->mem.memio.memory != NULL)
292
0
        {
293
            /* If the original block of memory is not resizeable, then
294
               it belongs to the caller and we should not free it. */
295
0
            if(!h5->mem.locked)
296
0
                free(h5->mem.memio.memory);
297
0
        }
298
0
        h5->mem.memio.memory = NULL;
299
0
        h5->mem.memio.size = 0;
300
0
        NC4_image_finalize(h5->mem.udata);
301
0
    }
302
303
    /* Free the HDF5-specific info. */
304
12.5k
    if (h5->format_file_info)
305
12.5k
        free(h5->format_file_info);
306
307
    /* Free the NC_FILE_INFO_T struct. */
308
12.5k
    if ((retval = nc4_nc4f_list_del(h5)))
309
0
        return retval;
310
311
12.5k
    return NC_NOERR;
312
12.5k
}
313
314
/**
315
 * @internal This function will recurse through an open HDF5 file and
316
 * release resources. All open HDF5 objects in the file will be
317
 * closed.
318
 *
319
 * @param h5 Pointer to HDF5 file info struct.
320
 * @param abort True if this is an abort.
321
 * @param memio the place to return a core image if not NULL
322
 *
323
 * @return ::NC_NOERR No error.
324
 * @return ::NC_EHDFERR HDF5 could not close the file.
325
 * @author Ed Hartnett
326
 */
327
int
328
nc4_close_hdf5_file(NC_FILE_INFO_T *h5, int abort,  NC_memio *memio)
329
12.5k
{
330
12.5k
    int retval;
331
332
12.5k
    assert(h5 && h5->root_grp && h5->format_file_info);
333
12.5k
    LOG((3, "%s: h5->path %s abort %d", __func__, h5->controller->path, abort));
334
335
    /* According to the docs, always end define mode on close. */
336
12.5k
    if (h5->flags & NC_INDEF)
337
5.01k
        h5->flags ^= NC_INDEF;
338
339
    /* Sync the file, unless we're aborting, or this is a read-only
340
     * file. */
341
12.5k
    if (!h5->no_write && !abort)
342
6.28k
        if ((retval = sync_netcdf4_file(h5)))
343
0
            return retval;
344
345
    /* Close all open HDF5 objects within the file. */
346
12.5k
    if ((retval = nc4_rec_grp_HDF5_del(h5->root_grp)))
347
0
        return retval;
348
349
    /* Release all intarnal lists and metadata associated with this
350
     * file. All HDF5 objects have already been released. */
351
12.5k
    if ((retval = nc4_close_netcdf4_file(h5, abort, memio)))
352
0
        return retval;
353
354
12.5k
    return NC_NOERR;
355
12.5k
}
356
357
/**
358
 * @internal Output a list of still-open objects in the HDF5
359
 * file. This is only called if the file fails to close cleanly.
360
 *
361
 * @param h5 Pointer to file info.
362
 *
363
 * @author Dennis Heimbigner
364
 */
365
static void
366
dumpopenobjects(NC_FILE_INFO_T* h5)
367
0
{
368
0
    NC_HDF5_FILE_INFO_T *hdf5_info;
369
0
    int nobjs;
370
371
0
    assert(h5 && h5->format_file_info);
372
0
    hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
373
374
0
    if(hdf5_info->hdfid <= 0)
375
0
        return; /* File was never opened */
376
377
0
    nobjs = H5Fget_obj_count(hdf5_info->hdfid, H5F_OBJ_ALL);
378
379
    /* Apparently we can get an error even when nobjs == 0 */
380
0
    if(nobjs < 0) {
381
0
        return;
382
0
    } else if(nobjs > 0) {
383
0
        char msg[1024];
384
0
        int logit = 0;
385
        /* If the close doesn't work, probably there are still some HDF5
386
         * objects open, which means there's a bug in the library. So
387
         * print out some info on to help the poor programmer figure it
388
         * out. */
389
0
        snprintf(msg,sizeof(msg),"There are %d HDF5 objects open!", nobjs);
390
#ifdef LOGGING
391
#ifdef LOGOPEN
392
        LOG((0, msg));
393
        logit = 1;
394
#endif
395
#else
396
0
        fprintf(stdout,"%s\n",msg);
397
0
        logit = 0;
398
0
#endif
399
0
        reportopenobjects(logit,hdf5_info->hdfid);
400
0
        fflush(stderr);
401
0
    }
402
403
0
    return;
404
0
}
405
406
/**
407
 * @internal Unfortunately HDF only allows specification of fill value
408
 * only when a dataset is created. Whereas in netcdf, you first create
409
 * the variable and then (optionally) specify the fill value. To
410
 * accomplish this in HDF5 I have to delete the dataset, and recreate
411
 * it, with the fill value specified.
412
 *
413
 * @param ncid File and group ID.
414
 * @param fillmode File mode.
415
 * @param old_modep Pointer that gets old mode. Ignored if NULL.
416
 *
417
 * @return ::NC_NOERR No error.
418
 * @author Ed Hartnett
419
 */
420
int
421
NC4_set_fill(int ncid, int fillmode, int *old_modep)
422
0
{
423
0
    NC_FILE_INFO_T *nc4_info;
424
0
    int retval;
425
426
0
    LOG((2, "%s: ncid 0x%x fillmode %d", __func__, ncid, fillmode));
427
428
    /* Get pointer to file info. */
429
0
    if ((retval = nc4_find_grp_h5(ncid, NULL, &nc4_info)))
430
0
        return retval;
431
0
    assert(nc4_info);
432
433
    /* Trying to set fill on a read-only file? You sicken me! */
434
0
    if (nc4_info->no_write)
435
0
        return NC_EPERM;
436
437
    /* Did you pass me some weird fillmode? */
438
0
    if (fillmode != NC_FILL && fillmode != NC_NOFILL)
439
0
        return NC_EINVAL;
440
441
    /* If the user wants to know, tell him what the old mode was. */
442
0
    if (old_modep)
443
0
        *old_modep = nc4_info->fill_mode;
444
445
0
    nc4_info->fill_mode = fillmode;
446
447
0
    return NC_NOERR;
448
0
}
449
450
/**
451
 * @internal Put the file back in redef mode. This is done
452
 * automatically for netcdf-4 files, if the user forgets.
453
 *
454
 * @param ncid File and group ID.
455
 *
456
 * @return ::NC_NOERR No error.
457
 * @author Ed Hartnett
458
 */
459
int
460
NC4_redef(int ncid)
461
0
{
462
0
    NC_FILE_INFO_T *nc4_info;
463
0
    int retval;
464
465
0
    LOG((1, "%s: ncid 0x%x", __func__, ncid));
466
467
    /* Find this file's metadata. */
468
0
    if ((retval = nc4_find_grp_h5(ncid, NULL, &nc4_info)))
469
0
        return retval;
470
0
    assert(nc4_info);
471
472
    /* If we're already in define mode, return an error. */
473
0
    if (nc4_info->flags & NC_INDEF)
474
0
        return NC_EINDEFINE;
475
476
    /* If the file is read-only, return an error. */
477
0
    if (nc4_info->no_write)
478
0
        return NC_EPERM;
479
480
    /* Set define mode. */
481
0
    nc4_info->flags |= NC_INDEF;
482
483
    /* For nc_abort, we need to remember if we're in define mode as a
484
       redef. */
485
0
    nc4_info->redef = NC_TRUE;
486
487
0
    return NC_NOERR;
488
0
}
489
490
/**
491
 * @internal For netcdf-4 files, this just calls nc_enddef, ignoring
492
 * the extra parameters.
493
 *
494
 * @param ncid File and group ID.
495
 * @param h_minfree Ignored for netCDF-4 files.
496
 * @param v_align Ignored for netCDF-4 files.
497
 * @param v_minfree Ignored for netCDF-4 files.
498
 * @param r_align Ignored for netCDF-4 files.
499
 *
500
 * @return ::NC_NOERR No error.
501
 * @author Ed Hartnett
502
 */
503
int
504
NC4__enddef(int ncid, size_t h_minfree, size_t v_align,
505
            size_t v_minfree, size_t r_align)
506
1.27k
{
507
1.27k
    return NC4_enddef(ncid);
508
1.27k
}
509
510
/**
511
 * @internal Take the file out of define mode. This is called
512
 * automatically for netcdf-4 files, if the user forgets.
513
 *
514
 * @param ncid File and group ID.
515
 *
516
 * @return ::NC_NOERR No error.
517
 * @return ::NC_EBADID Bad ncid.
518
 * @return ::NC_EBADGRPID Bad group ID.
519
 * @author Ed Hartnett
520
 */
521
static int
522
NC4_enddef(int ncid)
523
1.27k
{
524
1.27k
    NC_FILE_INFO_T *nc4_info;
525
1.27k
    NC_GRP_INFO_T *grp;
526
1.27k
    NC_VAR_INFO_T *var;
527
1.27k
    int i;
528
1.27k
    int retval;
529
530
1.27k
    LOG((1, "%s: ncid 0x%x", __func__, ncid));
531
532
    /* Find pointer to group and nc4_info. */
533
1.27k
    if ((retval = nc4_find_grp_h5(ncid, &grp, &nc4_info)))
534
0
        return retval;
535
536
    /* When exiting define mode, mark all variable written. */
537
13.8k
    for (i = 0; i < ncindexsize(grp->vars); i++)
538
12.5k
    {
539
12.5k
        var = (NC_VAR_INFO_T *)ncindexith(grp->vars, i);
540
12.5k
        assert(var);
541
12.5k
        var->written_to = NC_TRUE;
542
12.5k
    }
543
544
1.27k
    return nc4_enddef_netcdf4_file(nc4_info);
545
1.27k
}
546
547
/**
548
 * @internal Flushes all buffers associated with the file, after
549
 * writing all changed metadata. This may only be called in data mode.
550
 *
551
 * @param ncid File and group ID.
552
 *
553
 * @return ::NC_NOERR No error.
554
 * @return ::NC_EBADID Bad ncid.
555
 * @return ::NC_EINDEFINE Classic model file is in define mode.
556
 * @author Ed Hartnett
557
 */
558
int
559
NC4_sync(int ncid)
560
0
{
561
0
    NC_FILE_INFO_T *nc4_info;
562
0
    int retval;
563
564
0
    LOG((2, "%s: ncid 0x%x", __func__, ncid));
565
566
0
    if ((retval = nc4_find_grp_h5(ncid, NULL, &nc4_info)))
567
0
        return retval;
568
0
    assert(nc4_info);
569
570
    /* If we're in define mode, we can't sync. */
571
0
    if (nc4_info->flags & NC_INDEF)
572
0
    {
573
0
        if (nc4_info->cmode & NC_CLASSIC_MODEL)
574
0
            return NC_EINDEFINE;
575
0
        if ((retval = NC4_enddef(ncid)))
576
0
            return retval;
577
0
    }
578
579
0
    return sync_netcdf4_file(nc4_info);
580
0
}
581
582
/**
583
 * @internal From the netcdf-3 docs: The function nc_abort just closes
584
 * the netCDF dataset, if not in define mode. If the dataset is being
585
 * created and is still in define mode, the dataset is deleted. If
586
 * define mode was entered by a call to nc_redef, the netCDF dataset
587
 * is restored to its state before definition mode was entered and the
588
 * dataset is closed.
589
 *
590
 * @param ncid File and group ID.
591
 *
592
 * @return ::NC_NOERR No error.
593
 * @author Ed Hartnett
594
 */
595
int
596
NC4_abort(int ncid)
597
0
{
598
0
    NC *nc;
599
0
    NC_FILE_INFO_T *nc4_info;
600
0
    int delete_file = 0;
601
0
    char path[NC_MAX_NAME + 1];
602
0
    int retval;
603
604
0
    LOG((2, "%s: ncid 0x%x", __func__, ncid));
605
606
    /* Find metadata for this file. */
607
0
    if ((retval = nc4_find_nc_grp_h5(ncid, &nc, NULL, &nc4_info)))
608
0
        return retval;
609
0
    assert(nc4_info);
610
611
    /* If we're in define mode, but not redefing the file, delete it. */
612
0
    if (nc4_info->flags & NC_INDEF && !nc4_info->redef)
613
0
    {
614
0
        delete_file++;
615
0
        strncpy(path, nc->path, NC_MAX_NAME);
616
0
    }
617
618
    /* Free any resources the netcdf-4 library has for this file's
619
     * metadata. */
620
0
    if ((retval = nc4_close_hdf5_file(nc4_info, 1, NULL)))
621
0
        return retval;
622
623
    /* Delete the file, if we should. */
624
0
    if (delete_file)
625
0
        if (remove(path) < 0)
626
0
            return NC_ECANTREMOVE;
627
628
0
    return NC_NOERR;
629
0
}
630
631
/**
632
 * @internal Close the netcdf file, writing any changes first.
633
 *
634
 * @param ncid File and group ID.
635
 * @param params any extra parameters in/out of close
636
 *
637
 * @return ::NC_NOERR No error.
638
 * @author Ed Hartnett
639
 */
640
int
641
NC4_close(int ncid, void* params)
642
12.5k
{
643
12.5k
    NC_GRP_INFO_T *grp;
644
12.5k
    NC_FILE_INFO_T *h5;
645
12.5k
    int retval;
646
12.5k
    int inmemory;
647
12.5k
    NC_memio* memio = NULL;
648
649
12.5k
    LOG((1, "%s: ncid 0x%x", __func__, ncid));
650
651
    /* Find our metadata for this file. */
652
12.5k
    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
653
0
        return retval;
654
655
12.5k
    assert(h5 && grp);
656
657
    /* This must be the root group. */
658
12.5k
    if (grp->parent)
659
0
        return NC_EBADGRPID;
660
661
12.5k
    inmemory = ((h5->cmode & NC_INMEMORY) == NC_INMEMORY);
662
663
12.5k
    if(inmemory && params != NULL) {
664
0
        memio = (NC_memio*)params;
665
0
    }
666
667
    /* Call the nc4 close. */
668
12.5k
    if ((retval = nc4_close_hdf5_file(grp->nc4_info, 0, memio)))
669
0
        return retval;
670
671
12.5k
    return NC_NOERR;
672
12.5k
}
673
674
/**
675
 * @internal Learn number of dimensions, variables, global attributes,
676
 * and the ID of the first unlimited dimension (if any).
677
 *
678
 * @note It's possible for any of these pointers to be NULL, in which
679
 * case don't try to figure out that value.
680
 *
681
 * @param ncid File and group ID.
682
 * @param ndimsp Pointer that gets number of dimensions.
683
 * @param nvarsp Pointer that gets number of variables.
684
 * @param nattsp Pointer that gets number of global attributes.
685
 * @param unlimdimidp Pointer that gets first unlimited dimension ID,
686
 * or -1 if there are no unlimied dimensions.
687
 *
688
 * @return ::NC_NOERR No error.
689
 * @author Ed Hartnett
690
 */
691
int
692
NC4_inq(int ncid, int *ndimsp, int *nvarsp, int *nattsp, int *unlimdimidp)
693
31.2k
{
694
31.2k
    NC *nc;
695
31.2k
    NC_FILE_INFO_T *h5;
696
31.2k
    NC_GRP_INFO_T *grp;
697
31.2k
    int retval;
698
31.2k
    int i;
699
700
31.2k
    LOG((2, "%s: ncid 0x%x", __func__, ncid));
701
702
    /* Find file metadata. */
703
31.2k
    if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
704
0
        return retval;
705
706
31.2k
    assert(h5 && grp && nc);
707
708
    /* Count the number of dims, vars, and global atts; need to iterate
709
     * because of possible nulls. */
710
31.2k
    if (ndimsp)
711
8.62k
    {
712
8.62k
        *ndimsp = ncindexcount(grp->dim);
713
8.62k
    }
714
31.2k
    if (nvarsp)
715
22.5k
    {
716
22.5k
        *nvarsp = ncindexcount(grp->vars);
717
22.5k
    }
718
31.2k
    if (nattsp)
719
12.5k
    {
720
        /* Do we need to read the atts? */
721
12.5k
        if (!grp->atts_read)
722
6.28k
            if ((retval = nc4_read_atts(grp, NULL)))
723
0
                return retval;
724
725
12.5k
        *nattsp = ncindexcount(grp->att);
726
12.5k
    }
727
728
31.2k
    if (unlimdimidp)
729
6.28k
    {
730
        /* Default, no unlimited dimension */
731
6.28k
        *unlimdimidp = -1;
732
733
        /* If there's more than one unlimited dim, which was not possible
734
           with netcdf-3, then only the last unlimited one will be reported
735
           back in xtendimp. */
736
        /* Note that this code is inconsistent with nc_inq_unlimid() */
737
10.3k
        for(i=0;i<ncindexsize(grp->dim);i++) {
738
8.26k
            NC_DIM_INFO_T* d = (NC_DIM_INFO_T*)ncindexith(grp->dim,i);
739
8.26k
            if(d == NULL) continue;
740
8.26k
            if(d->unlimited) {
741
4.16k
                *unlimdimidp = d->hdr.id;
742
4.16k
                break;
743
4.16k
            }
744
8.26k
        }
745
6.28k
    }
746
747
31.2k
    return NC_NOERR;
748
31.2k
}
749
750
/**
751
 * @internal This function will do the enddef stuff for a netcdf-4 file.
752
 *
753
 * @param h5 Pointer to HDF5 file info struct.
754
 *
755
 * @return ::NC_NOERR No error.
756
 * @return ::NC_ENOTINDEFINE Not in define mode.
757
 * @author Ed Hartnett
758
 */
759
int
760
nc4_enddef_netcdf4_file(NC_FILE_INFO_T *h5)
761
1.27k
{
762
1.27k
    assert(h5);
763
1.27k
    LOG((3, "%s", __func__));
764
765
    /* If we're not in define mode, return an error. */
766
1.27k
    if (!(h5->flags & NC_INDEF))
767
0
        return NC_ENOTINDEFINE;
768
769
    /* Turn define mode off. */
770
1.27k
    h5->flags ^= NC_INDEF;
771
772
    /* Redef mode needs to be tracked separately for nc_abort. */
773
1.27k
    h5->redef = NC_FALSE;
774
775
1.27k
    return sync_netcdf4_file(h5);
776
1.27k
}